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