jscript: Added String.charAt implementation.
[wine] / dlls / jscript / jsutils.c
1 /*
2  * Copyright 2008 Jacek Caban for CodeWeavers
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #include <math.h>
20
21 #include "jscript.h"
22 #include "engine.h"
23
24 #include "wine/debug.h"
25
26 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
27
28 const char *debugstr_variant(const VARIANT *v)
29 {
30     switch(V_VT(v)) {
31     case VT_EMPTY:
32         return wine_dbg_sprintf("{VT_EMPTY}");
33     case VT_NULL:
34         return wine_dbg_sprintf("{VT_NULL}");
35     case VT_I4:
36         return wine_dbg_sprintf("{VT_I4: %d}", V_I4(v));
37     case VT_R8:
38         return wine_dbg_sprintf("{VT_R8: %lf}", V_R8(v));
39     case VT_BSTR:
40         return wine_dbg_sprintf("{VT_BSTR: %s}", debugstr_w(V_BSTR(v)));
41     case VT_DISPATCH:
42         return wine_dbg_sprintf("{VT_DISPATCH: %p}", V_DISPATCH(v));
43     case VT_BOOL:
44         return wine_dbg_sprintf("{VT_BOOL: %x}", V_BOOL(v));
45     default:
46         return wine_dbg_sprintf("{vt %d}", V_VT(v));
47     }
48 }
49
50 #define MIN_BLOCK_SIZE  128
51
52 static inline DWORD block_size(DWORD block)
53 {
54     return MIN_BLOCK_SIZE << block;
55 }
56
57 void jsheap_init(jsheap_t *heap)
58 {
59     memset(heap, 0, sizeof(*heap));
60     list_init(&heap->custom_blocks);
61 }
62
63 void *jsheap_alloc(jsheap_t *heap, DWORD size)
64 {
65     struct list *list;
66     void *tmp;
67
68     if(!heap->block_cnt) {
69         if(!heap->blocks) {
70             heap->blocks = heap_alloc(sizeof(void*));
71             if(!heap->blocks)
72                 return NULL;
73         }
74
75         tmp = heap_alloc(block_size(0));
76         if(!tmp)
77             return NULL;
78
79         heap->blocks[0] = tmp;
80         heap->block_cnt = 1;
81     }
82
83     if(heap->offset + size < block_size(heap->last_block)) {
84         tmp = ((BYTE*)heap->blocks[heap->last_block])+heap->offset;
85         heap->offset += size;
86         return tmp;
87     }
88
89     if(size < block_size(heap->last_block+1)) {
90         if(heap->last_block+1 == heap->block_cnt) {
91             tmp = heap_realloc(heap->blocks, (heap->block_cnt+1)*sizeof(void*));
92             if(!tmp)
93                 return NULL;
94             heap->blocks = tmp;
95         }
96
97         tmp = heap_alloc(block_size(heap->block_cnt+1));
98         if(!tmp)
99             return NULL;
100
101         heap->blocks[heap->block_cnt++] = tmp;
102
103         heap->last_block++;
104         heap->offset = size;
105         return heap->blocks[heap->last_block];
106     }
107
108     list = heap_alloc(size + sizeof(struct list));
109     if(!list)
110         return NULL;
111
112     list_add_head(&heap->custom_blocks, list);
113     return list+1;
114 }
115
116 void *jsheap_grow(jsheap_t *heap, void *mem, DWORD size, DWORD inc)
117 {
118     if(mem == (BYTE*)heap->blocks[heap->last_block] + heap->offset-size
119        && heap->offset+inc < block_size(heap->last_block)) {
120         heap->offset += inc;
121         return mem;
122     }
123
124     return jsheap_alloc(heap, size+inc);
125 }
126
127 void jsheap_clear(jsheap_t *heap)
128 {
129     struct list *tmp;
130
131     if(!heap)
132         return;
133
134     while((tmp = list_next(&heap->custom_blocks, &heap->custom_blocks))) {
135         list_remove(tmp);
136         heap_free(tmp);
137     }
138
139     heap->last_block = heap->offset = 0;
140 }
141
142 void jsheap_free(jsheap_t *heap)
143 {
144     DWORD i;
145
146     jsheap_clear(heap);
147
148     for(i=0; i < heap->block_cnt; i++)
149         heap_free(heap->blocks[i]);
150     heap_free(heap->blocks);
151
152     jsheap_init(heap);
153 }
154
155 jsheap_t *jsheap_mark(jsheap_t *heap)
156 {
157     if(heap->mark)
158         return NULL;
159
160     heap->mark = TRUE;
161     return heap;
162 }
163
164 /* ECMA-262 3rd Edition    9.1 */
165 HRESULT to_primitive(script_ctx_t *ctx, VARIANT *v, jsexcept_t *ei, VARIANT *ret)
166 {
167     switch(V_VT(v)) {
168     case VT_EMPTY:
169     case VT_NULL:
170     case VT_BOOL:
171     case VT_I4:
172     case VT_R8:
173         *ret = *v;
174         break;
175     case VT_BSTR:
176         V_VT(ret) = VT_BSTR;
177         V_BSTR(ret) = SysAllocString(V_BSTR(v));
178         break;
179     case VT_DISPATCH:
180         return disp_propget(V_DISPATCH(v), DISPID_VALUE, ctx->lcid, ret, ei, NULL /*FIXME*/);
181     default:
182         FIXME("Unimplemented for vt %d\n", V_VT(v));
183         return E_NOTIMPL;
184     }
185
186     return S_OK;
187 }
188
189 /* ECMA-262 3rd Edition    9.2 */
190 HRESULT to_boolean(VARIANT *v, VARIANT_BOOL *b)
191 {
192     switch(V_VT(v)) {
193     case VT_EMPTY:
194     case VT_NULL:
195         *b = VARIANT_FALSE;
196         break;
197     case VT_I4:
198         *b = V_I4(v) ? VARIANT_TRUE : VARIANT_FALSE;
199         break;
200     case VT_R8:
201         *b = V_R8(v) ? VARIANT_TRUE : VARIANT_FALSE;
202         break;
203     case VT_BSTR:
204         *b = V_BSTR(v) && *V_BSTR(v) ? VARIANT_TRUE : VARIANT_FALSE;
205         break;
206     case VT_DISPATCH:
207         *b = V_DISPATCH(v) ? VARIANT_TRUE : VARIANT_FALSE;
208         break;
209     case VT_BOOL:
210         *b = V_BOOL(v);
211         break;
212     default:
213         FIXME("unimplemented for vt %d\n", V_VT(v));
214         return E_NOTIMPL;
215     }
216
217     return S_OK;
218 }
219
220 /* ECMA-262 3rd Edition    9.3 */
221 HRESULT to_number(script_ctx_t *ctx, VARIANT *v, jsexcept_t *ei, VARIANT *ret)
222 {
223     switch(V_VT(v)) {
224     case VT_NULL:
225         V_VT(ret) = VT_I4;
226         V_I4(ret) = 0;
227         break;
228     case VT_I4:
229     case VT_R8:
230         *ret = *v;
231         break;
232     case VT_BOOL:
233         V_VT(ret) = VT_I4;
234         V_I4(ret) = V_BOOL(v) ? 1 : 0;
235         break;
236     default:
237         FIXME("unimplemented for vt %d\n", V_VT(v));
238         return E_NOTIMPL;
239     }
240
241     return S_OK;
242 }
243
244 /* ECMA-262 3rd Edition    9.4 */
245 HRESULT to_integer(script_ctx_t *ctx, VARIANT *v, jsexcept_t *ei, VARIANT *ret)
246 {
247     VARIANT num;
248     HRESULT hres;
249
250     hres = to_number(ctx, v, ei, &num);
251     if(FAILED(hres))
252         return hres;
253
254     if(V_VT(&num) == VT_I4)
255         *ret = *v;
256     else
257         num_set_val(ret, V_R8(&num) >= 0.0 ? floor(V_R8(&num)) : -floor(-V_R8(&num)));
258
259     return S_OK;
260 }
261
262 /* ECMA-262 3rd Edition    9.5 */
263 HRESULT to_int32(script_ctx_t *ctx, VARIANT *v, jsexcept_t *ei, INT *ret)
264 {
265     VARIANT num;
266     HRESULT hres;
267
268     hres = to_number(ctx, v, ei, &num);
269     if(FAILED(hres))
270         return hres;
271
272     *ret = V_VT(&num) == VT_I4 ? V_I4(&num) : (INT)V_R8(&num);
273     return S_OK;
274 }
275
276 static BSTR int_to_bstr(INT i)
277 {
278     WCHAR buf[12], *p;
279     BOOL neg = FALSE;
280
281     if(!i) {
282         static const WCHAR zeroW[] = {'0',0};
283         return SysAllocString(zeroW);
284     }
285
286     if(i < 0) {
287         neg = TRUE;
288         i = -i;
289     }
290
291     p = buf + sizeof(buf)/sizeof(*buf)-1;
292     *p-- = 0;
293     while(i) {
294         *p-- = i%10 + '0';
295         i /= 10;
296     }
297
298     if(neg)
299         *p = '-';
300     else
301         p++;
302
303     return SysAllocString(p);
304 }
305
306 /* ECMA-262 3rd Edition    9.8 */
307 HRESULT to_string(script_ctx_t *ctx, VARIANT *v, jsexcept_t *ei, BSTR *str)
308 {
309     switch(V_VT(v)) {
310     case VT_I4:
311         *str = int_to_bstr(V_I4(v));
312         break;
313
314     case VT_BSTR:
315         *str = SysAllocString(V_BSTR(v));
316         break;
317
318     default:
319         FIXME("unsupported vt %d\n", V_VT(v));
320         return E_NOTIMPL;
321     }
322
323     return *str ? S_OK : E_OUTOFMEMORY;
324 }
325
326 /* ECMA-262 3rd Edition    9.9 */
327 HRESULT to_object(exec_ctx_t *ctx, VARIANT *v, IDispatch **disp)
328 {
329     DispatchEx *dispex;
330     HRESULT hres;
331
332     switch(V_VT(v)) {
333     case VT_BSTR:
334         hres = create_string(ctx->parser->script, V_BSTR(v), SysStringLen(V_BSTR(v)), &dispex);
335         if(FAILED(hres))
336             return hres;
337
338         *disp = (IDispatch*)_IDispatchEx_(dispex);
339         break;
340     case VT_I4:
341     case VT_R8:
342         hres = create_number(ctx->parser->script, v, &dispex);
343         if(FAILED(hres))
344             return hres;
345
346         *disp = (IDispatch*)_IDispatchEx_(dispex);
347         break;
348     case VT_DISPATCH:
349         IDispatch_AddRef(V_DISPATCH(v));
350         *disp = V_DISPATCH(v);
351         break;
352     case VT_BOOL:
353         hres = create_bool(ctx->parser->script, V_BOOL(v), &dispex);
354         if(FAILED(hres))
355             return hres;
356
357         *disp = (IDispatch*)_IDispatchEx_(dispex);
358         break;
359     default:
360         FIXME("unsupported vt %d\n", V_VT(v));
361         return E_NOTIMPL;
362     }
363
364     return S_OK;
365 }