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