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