jscript: Reset mark in jsheap_clear.
[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 "config.h"
20 #include "wine/port.h"
21
22 #include <math.h>
23
24 #include "jscript.h"
25 #include "engine.h"
26
27 #include "wine/debug.h"
28
29 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
30
31 const char *debugstr_variant(const VARIANT *v)
32 {
33     switch(V_VT(v)) {
34     case VT_EMPTY:
35         return wine_dbg_sprintf("{VT_EMPTY}");
36     case VT_NULL:
37         return wine_dbg_sprintf("{VT_NULL}");
38     case VT_I4:
39         return wine_dbg_sprintf("{VT_I4: %d}", V_I4(v));
40     case VT_R8:
41         return wine_dbg_sprintf("{VT_R8: %lf}", V_R8(v));
42     case VT_BSTR:
43         return wine_dbg_sprintf("{VT_BSTR: %s}", debugstr_w(V_BSTR(v)));
44     case VT_DISPATCH:
45         return wine_dbg_sprintf("{VT_DISPATCH: %p}", V_DISPATCH(v));
46     case VT_BOOL:
47         return wine_dbg_sprintf("{VT_BOOL: %x}", V_BOOL(v));
48     default:
49         return wine_dbg_sprintf("{vt %d}", V_VT(v));
50     }
51 }
52
53 #define MIN_BLOCK_SIZE  128
54
55 static inline DWORD block_size(DWORD block)
56 {
57     return MIN_BLOCK_SIZE << block;
58 }
59
60 void jsheap_init(jsheap_t *heap)
61 {
62     memset(heap, 0, sizeof(*heap));
63     list_init(&heap->custom_blocks);
64 }
65
66 void *jsheap_alloc(jsheap_t *heap, DWORD size)
67 {
68     struct list *list;
69     void *tmp;
70
71     if(!heap->block_cnt) {
72         if(!heap->blocks) {
73             heap->blocks = heap_alloc(sizeof(void*));
74             if(!heap->blocks)
75                 return NULL;
76         }
77
78         tmp = heap_alloc(block_size(0));
79         if(!tmp)
80             return NULL;
81
82         heap->blocks[0] = tmp;
83         heap->block_cnt = 1;
84     }
85
86     if(heap->offset + size <= block_size(heap->last_block)) {
87         tmp = ((BYTE*)heap->blocks[heap->last_block])+heap->offset;
88         heap->offset += size;
89         return tmp;
90     }
91
92     if(size <= block_size(heap->last_block+1)) {
93         if(heap->last_block+1 == heap->block_cnt) {
94             tmp = heap_realloc(heap->blocks, (heap->block_cnt+1)*sizeof(void*));
95             if(!tmp)
96                 return NULL;
97
98             heap->blocks = tmp;
99             heap->blocks[heap->block_cnt] = heap_alloc(block_size(heap->block_cnt));
100             if(!heap->blocks[heap->block_cnt])
101                 return NULL;
102
103             heap->block_cnt++;
104         }
105
106         heap->last_block++;
107         heap->offset = size;
108         return heap->blocks[heap->last_block];
109     }
110
111     list = heap_alloc(size + sizeof(struct list));
112     if(!list)
113         return NULL;
114
115     list_add_head(&heap->custom_blocks, list);
116     return list+1;
117 }
118
119 void *jsheap_grow(jsheap_t *heap, void *mem, DWORD size, DWORD inc)
120 {
121     if(mem == (BYTE*)heap->blocks[heap->last_block] + heap->offset-size
122        && heap->offset+inc < block_size(heap->last_block)) {
123         heap->offset += inc;
124         return mem;
125     }
126
127     return jsheap_alloc(heap, size+inc);
128 }
129
130 void jsheap_clear(jsheap_t *heap)
131 {
132     struct list *tmp;
133
134     if(!heap)
135         return;
136
137     while((tmp = list_next(&heap->custom_blocks, &heap->custom_blocks))) {
138         list_remove(tmp);
139         heap_free(tmp);
140     }
141
142     heap->last_block = heap->offset = 0;
143     heap->mark = FALSE;
144 }
145
146 void jsheap_free(jsheap_t *heap)
147 {
148     DWORD i;
149
150     jsheap_clear(heap);
151
152     for(i=0; i < heap->block_cnt; i++)
153         heap_free(heap->blocks[i]);
154     heap_free(heap->blocks);
155
156     jsheap_init(heap);
157 }
158
159 jsheap_t *jsheap_mark(jsheap_t *heap)
160 {
161     if(heap->mark)
162         return NULL;
163
164     heap->mark = TRUE;
165     return heap;
166 }
167
168 /* ECMA-262 3rd Edition    9.1 */
169 HRESULT to_primitive(script_ctx_t *ctx, VARIANT *v, jsexcept_t *ei, VARIANT *ret)
170 {
171     switch(V_VT(v)) {
172     case VT_EMPTY:
173     case VT_NULL:
174     case VT_BOOL:
175     case VT_I4:
176     case VT_R8:
177         *ret = *v;
178         break;
179     case VT_BSTR:
180         V_VT(ret) = VT_BSTR;
181         V_BSTR(ret) = SysAllocString(V_BSTR(v));
182         break;
183     case VT_DISPATCH:
184         return disp_propget(V_DISPATCH(v), DISPID_VALUE, ctx->lcid, ret, ei, NULL /*FIXME*/);
185     default:
186         FIXME("Unimplemented for vt %d\n", V_VT(v));
187         return E_NOTIMPL;
188     }
189
190     return S_OK;
191 }
192
193 /* ECMA-262 3rd Edition    9.2 */
194 HRESULT to_boolean(VARIANT *v, VARIANT_BOOL *b)
195 {
196     switch(V_VT(v)) {
197     case VT_EMPTY:
198     case VT_NULL:
199         *b = VARIANT_FALSE;
200         break;
201     case VT_I4:
202         *b = V_I4(v) ? VARIANT_TRUE : VARIANT_FALSE;
203         break;
204     case VT_R8:
205         *b = V_R8(v) ? VARIANT_TRUE : VARIANT_FALSE;
206         break;
207     case VT_BSTR:
208         *b = V_BSTR(v) && *V_BSTR(v) ? VARIANT_TRUE : VARIANT_FALSE;
209         break;
210     case VT_DISPATCH:
211         *b = V_DISPATCH(v) ? VARIANT_TRUE : VARIANT_FALSE;
212         break;
213     case VT_BOOL:
214         *b = V_BOOL(v);
215         break;
216     default:
217         FIXME("unimplemented for vt %d\n", V_VT(v));
218         return E_NOTIMPL;
219     }
220
221     return S_OK;
222 }
223
224 static int hex_to_int(WCHAR c)
225 {
226     if('0' <= c && c <= '9')
227         return c-'0';
228
229     if('a' <= c && c <= 'f')
230         return c-'a'+10;
231
232     if('A' <= c && c <= 'F')
233         return c-'A'+10;
234
235     return -1;
236 }
237
238 /* ECMA-262 3rd Edition    9.3.1 */
239 static HRESULT str_to_number(BSTR str, VARIANT *ret)
240 {
241     const WCHAR *ptr = str;
242     BOOL neg = FALSE;
243     DOUBLE d = 0.0;
244
245     static const WCHAR infinityW[] = {'I','n','f','i','n','i','t','y'};
246
247     while(isspaceW(*ptr))
248         ptr++;
249
250     if(*ptr == '-') {
251         neg = TRUE;
252         ptr++;
253     }else if(*ptr == '+') {
254         ptr++;
255     }
256
257     if(!strncmpW(ptr, infinityW, sizeof(infinityW)/sizeof(WCHAR))) {
258         ptr += sizeof(infinityW)/sizeof(WCHAR);
259         while(*ptr && isspaceW(*ptr))
260             ptr++;
261
262         if(*ptr)
263             num_set_nan(ret);
264         else
265             num_set_inf(ret, !neg);
266         return S_OK;
267     }
268
269     if(*ptr == '0' && ptr[1] == 'x') {
270         DWORD l = 0;
271
272         ptr += 2;
273         while((l = hex_to_int(*ptr)) != -1) {
274             d = d*16 + l;
275             ptr++;
276         }
277
278         num_set_val(ret, d);
279         return S_OK;
280     }
281
282     while(isdigitW(*ptr))
283         d = d*10 + (*ptr++ - '0');
284
285     if(*ptr == 'e' || *ptr == 'E') {
286         BOOL eneg = FALSE;
287         LONG l = 0;
288
289         ptr++;
290         if(*ptr == '-') {
291             ptr++;
292             eneg = TRUE;
293         }else if(*ptr == '+') {
294             ptr++;
295         }
296
297         while(isdigitW(*ptr))
298             l = l*10 + (*ptr++ - '0');
299         if(eneg)
300             l = -l;
301
302         d *= pow(10, l);
303     }else if(*ptr == '.') {
304         DOUBLE dec = 0.1;
305
306         ptr++;
307         while(isdigitW(*ptr)) {
308             d += dec * (*ptr++ - '0');
309             dec *= 0.1;
310         }
311     }
312
313     while(isspaceW(*ptr))
314         ptr++;
315
316     if(*ptr) {
317         num_set_nan(ret);
318         return S_OK;
319     }
320
321     if(neg)
322         d = -d;
323
324     num_set_val(ret, d);
325     return S_OK;
326 }
327
328 /* ECMA-262 3rd Edition    9.3 */
329 HRESULT to_number(script_ctx_t *ctx, VARIANT *v, jsexcept_t *ei, VARIANT *ret)
330 {
331     switch(V_VT(v)) {
332     case VT_EMPTY:
333         num_set_nan(ret);
334         break;
335     case VT_NULL:
336         V_VT(ret) = VT_I4;
337         V_I4(ret) = 0;
338         break;
339     case VT_I4:
340     case VT_R8:
341         *ret = *v;
342         break;
343     case VT_BSTR:
344         return str_to_number(V_BSTR(v), ret);
345     case VT_DISPATCH: {
346         VARIANT prim;
347         HRESULT hres;
348
349         hres = to_primitive(ctx, v, ei, &prim);
350         if(FAILED(hres))
351             return hres;
352
353         hres = to_number(ctx, &prim, ei, ret);
354         VariantClear(&prim);
355         return hres;
356     }
357     case VT_BOOL:
358         V_VT(ret) = VT_I4;
359         V_I4(ret) = V_BOOL(v) ? 1 : 0;
360         break;
361     default:
362         FIXME("unimplemented for vt %d\n", V_VT(v));
363         return E_NOTIMPL;
364     }
365
366     return S_OK;
367 }
368
369 /* ECMA-262 3rd Edition    9.4 */
370 HRESULT to_integer(script_ctx_t *ctx, VARIANT *v, jsexcept_t *ei, VARIANT *ret)
371 {
372     VARIANT num;
373     HRESULT hres;
374
375     hres = to_number(ctx, v, ei, &num);
376     if(FAILED(hres))
377         return hres;
378
379     if(V_VT(&num) == VT_I4)
380         *ret = num;
381     else
382         num_set_val(ret, V_R8(&num) >= 0.0 ? floor(V_R8(&num)) : -floor(-V_R8(&num)));
383
384     return S_OK;
385 }
386
387 /* ECMA-262 3rd Edition    9.5 */
388 HRESULT to_int32(script_ctx_t *ctx, VARIANT *v, jsexcept_t *ei, INT *ret)
389 {
390     VARIANT num;
391     HRESULT hres;
392
393     hres = to_number(ctx, v, ei, &num);
394     if(FAILED(hres))
395         return hres;
396
397     *ret = V_VT(&num) == VT_I4 ? V_I4(&num) : (INT)V_R8(&num);
398     return S_OK;
399 }
400
401 /* ECMA-262 3rd Edition    9.6 */
402 HRESULT to_uint32(script_ctx_t *ctx, VARIANT *v, jsexcept_t *ei, DWORD *ret)
403 {
404     VARIANT num;
405     HRESULT hres;
406
407     hres = to_number(ctx, v, ei, &num);
408     if(FAILED(hres))
409         return hres;
410
411     *ret = V_VT(&num) == VT_I4 ? V_I4(&num) : (DWORD)V_R8(&num);
412     return S_OK;
413 }
414
415 static BSTR int_to_bstr(INT i)
416 {
417     WCHAR buf[12], *p;
418     BOOL neg = FALSE;
419
420     if(!i) {
421         static const WCHAR zeroW[] = {'0',0};
422         return SysAllocString(zeroW);
423     }
424
425     if(i < 0) {
426         neg = TRUE;
427         i = -i;
428     }
429
430     p = buf + sizeof(buf)/sizeof(*buf)-1;
431     *p-- = 0;
432     while(i) {
433         *p-- = i%10 + '0';
434         i /= 10;
435     }
436
437     if(neg)
438         *p = '-';
439     else
440         p++;
441
442     return SysAllocString(p);
443 }
444
445 /* ECMA-262 3rd Edition    9.8 */
446 HRESULT to_string(script_ctx_t *ctx, VARIANT *v, jsexcept_t *ei, BSTR *str)
447 {
448     const WCHAR undefinedW[] = {'u','n','d','e','f','i','n','e','d',0};
449     const WCHAR nullW[] = {'n','u','l','l',0};
450     const WCHAR trueW[] = {'t','r','u','e',0};
451     const WCHAR falseW[] = {'f','a','l','s','e',0};
452
453     switch(V_VT(v)) {
454     case VT_EMPTY:
455         *str = SysAllocString(undefinedW);
456         break;
457     case VT_NULL:
458         *str = SysAllocString(nullW);
459         break;
460     case VT_I4:
461         *str = int_to_bstr(V_I4(v));
462         break;
463     case VT_R8: {
464         VARIANT strv;
465         HRESULT hres;
466
467         V_VT(&strv) = VT_EMPTY;
468         hres = VariantChangeTypeEx(&strv, v, MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT), 0, VT_BSTR);
469         if(FAILED(hres))
470             return hres;
471
472         *str = V_BSTR(&strv);
473         return S_OK;
474     }
475     case VT_BSTR:
476         *str = SysAllocString(V_BSTR(v));
477         break;
478     case VT_DISPATCH: {
479         VARIANT prim;
480         HRESULT hres;
481
482         hres = to_primitive(ctx, v, ei, &prim);
483         if(FAILED(hres))
484             return hres;
485
486         hres = to_string(ctx, &prim, ei, str);
487         VariantClear(&prim);
488         return hres;
489     }
490     case VT_BOOL:
491         *str = SysAllocString(V_BOOL(v) ? trueW : falseW);
492         break;
493     default:
494         FIXME("unsupported vt %d\n", V_VT(v));
495         return E_NOTIMPL;
496     }
497
498     return *str ? S_OK : E_OUTOFMEMORY;
499 }
500
501 /* ECMA-262 3rd Edition    9.9 */
502 HRESULT to_object(exec_ctx_t *ctx, VARIANT *v, IDispatch **disp)
503 {
504     DispatchEx *dispex;
505     HRESULT hres;
506
507     switch(V_VT(v)) {
508     case VT_BSTR:
509         hres = create_string(ctx->parser->script, V_BSTR(v), SysStringLen(V_BSTR(v)), &dispex);
510         if(FAILED(hres))
511             return hres;
512
513         *disp = (IDispatch*)_IDispatchEx_(dispex);
514         break;
515     case VT_I4:
516     case VT_R8:
517         hres = create_number(ctx->parser->script, v, &dispex);
518         if(FAILED(hres))
519             return hres;
520
521         *disp = (IDispatch*)_IDispatchEx_(dispex);
522         break;
523     case VT_DISPATCH:
524         IDispatch_AddRef(V_DISPATCH(v));
525         *disp = V_DISPATCH(v);
526         break;
527     case VT_BOOL:
528         hres = create_bool(ctx->parser->script, V_BOOL(v), &dispex);
529         if(FAILED(hres))
530             return hres;
531
532         *disp = (IDispatch*)_IDispatchEx_(dispex);
533         break;
534     default:
535         FIXME("unsupported vt %d\n", V_VT(v));
536         return E_NOTIMPL;
537     }
538
539     return S_OK;
540 }