jscript: Added Object_toString and Object_toLocaleString 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)
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         return disp_propget(V_DISPATCH(v), DISPID_VALUE, ctx->lcid, ret, ei, NULL /*FIXME*/);
194     default:
195         FIXME("Unimplemented for vt %d\n", V_VT(v));
196         return E_NOTIMPL;
197     }
198
199     return S_OK;
200 }
201
202 /* ECMA-262 3rd Edition    9.2 */
203 HRESULT to_boolean(VARIANT *v, VARIANT_BOOL *b)
204 {
205     switch(V_VT(v)) {
206     case VT_EMPTY:
207     case VT_NULL:
208         *b = VARIANT_FALSE;
209         break;
210     case VT_I4:
211         *b = V_I4(v) ? VARIANT_TRUE : VARIANT_FALSE;
212         break;
213     case VT_R8:
214         if(isnan(V_R8(v))) *b = VARIANT_FALSE;
215         else *b = V_R8(v) ? VARIANT_TRUE : VARIANT_FALSE;
216         break;
217     case VT_BSTR:
218         *b = V_BSTR(v) && *V_BSTR(v) ? VARIANT_TRUE : VARIANT_FALSE;
219         break;
220     case VT_DISPATCH:
221         *b = V_DISPATCH(v) ? VARIANT_TRUE : VARIANT_FALSE;
222         break;
223     case VT_BOOL:
224         *b = V_BOOL(v);
225         break;
226     default:
227         FIXME("unimplemented for vt %d\n", V_VT(v));
228         return E_NOTIMPL;
229     }
230
231     return S_OK;
232 }
233
234 static int hex_to_int(WCHAR c)
235 {
236     if('0' <= c && c <= '9')
237         return c-'0';
238
239     if('a' <= c && c <= 'f')
240         return c-'a'+10;
241
242     if('A' <= c && c <= 'F')
243         return c-'A'+10;
244
245     return -1;
246 }
247
248 /* ECMA-262 3rd Edition    9.3.1 */
249 static HRESULT str_to_number(BSTR str, VARIANT *ret)
250 {
251     const WCHAR *ptr = str;
252     BOOL neg = FALSE;
253     DOUBLE d = 0.0;
254
255     static const WCHAR infinityW[] = {'I','n','f','i','n','i','t','y'};
256
257     while(isspaceW(*ptr))
258         ptr++;
259
260     if(*ptr == '-') {
261         neg = TRUE;
262         ptr++;
263     }else if(*ptr == '+') {
264         ptr++;
265     }
266
267     if(!strncmpW(ptr, infinityW, sizeof(infinityW)/sizeof(WCHAR))) {
268         ptr += sizeof(infinityW)/sizeof(WCHAR);
269         while(*ptr && isspaceW(*ptr))
270             ptr++;
271
272         if(*ptr)
273             num_set_nan(ret);
274         else
275             num_set_inf(ret, !neg);
276         return S_OK;
277     }
278
279     if(*ptr == '0' && ptr[1] == 'x') {
280         DWORD l = 0;
281
282         ptr += 2;
283         while((l = hex_to_int(*ptr)) != -1) {
284             d = d*16 + l;
285             ptr++;
286         }
287
288         num_set_val(ret, d);
289         return S_OK;
290     }
291
292     while(isdigitW(*ptr))
293         d = d*10 + (*ptr++ - '0');
294
295     if(*ptr == 'e' || *ptr == 'E') {
296         BOOL eneg = FALSE;
297         LONG l = 0;
298
299         ptr++;
300         if(*ptr == '-') {
301             ptr++;
302             eneg = TRUE;
303         }else if(*ptr == '+') {
304             ptr++;
305         }
306
307         while(isdigitW(*ptr))
308             l = l*10 + (*ptr++ - '0');
309         if(eneg)
310             l = -l;
311
312         d *= pow(10, l);
313     }else if(*ptr == '.') {
314         DOUBLE dec = 0.1;
315
316         ptr++;
317         while(isdigitW(*ptr)) {
318             d += dec * (*ptr++ - '0');
319             dec *= 0.1;
320         }
321     }
322
323     while(isspaceW(*ptr))
324         ptr++;
325
326     if(*ptr) {
327         num_set_nan(ret);
328         return S_OK;
329     }
330
331     if(neg)
332         d = -d;
333
334     num_set_val(ret, d);
335     return S_OK;
336 }
337
338 /* ECMA-262 3rd Edition    9.3 */
339 HRESULT to_number(script_ctx_t *ctx, VARIANT *v, jsexcept_t *ei, VARIANT *ret)
340 {
341     switch(V_VT(v)) {
342     case VT_EMPTY:
343         num_set_nan(ret);
344         break;
345     case VT_NULL:
346         V_VT(ret) = VT_I4;
347         V_I4(ret) = 0;
348         break;
349     case VT_I4:
350     case VT_R8:
351         *ret = *v;
352         break;
353     case VT_BSTR:
354         return str_to_number(V_BSTR(v), ret);
355     case VT_DISPATCH: {
356         VARIANT prim;
357         HRESULT hres;
358
359         hres = to_primitive(ctx, v, ei, &prim);
360         if(FAILED(hres))
361             return hres;
362
363         hres = to_number(ctx, &prim, ei, ret);
364         VariantClear(&prim);
365         return hres;
366     }
367     case VT_BOOL:
368         V_VT(ret) = VT_I4;
369         V_I4(ret) = V_BOOL(v) ? 1 : 0;
370         break;
371     default:
372         FIXME("unimplemented for vt %d\n", V_VT(v));
373         return E_NOTIMPL;
374     }
375
376     return S_OK;
377 }
378
379 /* ECMA-262 3rd Edition    9.4 */
380 HRESULT to_integer(script_ctx_t *ctx, VARIANT *v, jsexcept_t *ei, VARIANT *ret)
381 {
382     VARIANT num;
383     HRESULT hres;
384
385     hres = to_number(ctx, v, ei, &num);
386     if(FAILED(hres))
387         return hres;
388
389     if(V_VT(&num) == VT_I4)
390         *ret = num;
391     else
392         num_set_val(ret, V_R8(&num) >= 0.0 ? floor(V_R8(&num)) : -floor(-V_R8(&num)));
393
394     return S_OK;
395 }
396
397 /* ECMA-262 3rd Edition    9.5 */
398 HRESULT to_int32(script_ctx_t *ctx, VARIANT *v, jsexcept_t *ei, INT *ret)
399 {
400     VARIANT num;
401     HRESULT hres;
402
403     hres = to_number(ctx, v, ei, &num);
404     if(FAILED(hres))
405         return hres;
406
407     *ret = V_VT(&num) == VT_I4 ? V_I4(&num) : (INT)V_R8(&num);
408     return S_OK;
409 }
410
411 /* ECMA-262 3rd Edition    9.6 */
412 HRESULT to_uint32(script_ctx_t *ctx, VARIANT *v, jsexcept_t *ei, DWORD *ret)
413 {
414     VARIANT num;
415     HRESULT hres;
416
417     hres = to_number(ctx, v, ei, &num);
418     if(FAILED(hres))
419         return hres;
420
421     *ret = V_VT(&num) == VT_I4 ? V_I4(&num) : (DWORD)V_R8(&num);
422     return S_OK;
423 }
424
425 static BSTR int_to_bstr(INT i)
426 {
427     WCHAR buf[12], *p;
428     BOOL neg = FALSE;
429
430     if(!i) {
431         static const WCHAR zeroW[] = {'0',0};
432         return SysAllocString(zeroW);
433     }
434
435     if(i < 0) {
436         neg = TRUE;
437         i = -i;
438     }
439
440     p = buf + sizeof(buf)/sizeof(*buf)-1;
441     *p-- = 0;
442     while(i) {
443         *p-- = i%10 + '0';
444         i /= 10;
445     }
446
447     if(neg)
448         *p = '-';
449     else
450         p++;
451
452     return SysAllocString(p);
453 }
454
455 /* ECMA-262 3rd Edition    9.8 */
456 HRESULT to_string(script_ctx_t *ctx, VARIANT *v, jsexcept_t *ei, BSTR *str)
457 {
458     const WCHAR undefinedW[] = {'u','n','d','e','f','i','n','e','d',0};
459     const WCHAR nullW[] = {'n','u','l','l',0};
460     const WCHAR trueW[] = {'t','r','u','e',0};
461     const WCHAR falseW[] = {'f','a','l','s','e',0};
462
463     switch(V_VT(v)) {
464     case VT_EMPTY:
465         *str = SysAllocString(undefinedW);
466         break;
467     case VT_NULL:
468         *str = SysAllocString(nullW);
469         break;
470     case VT_I4:
471         *str = int_to_bstr(V_I4(v));
472         break;
473     case VT_R8: {
474         VARIANT strv;
475         HRESULT hres;
476
477         V_VT(&strv) = VT_EMPTY;
478         hres = VariantChangeTypeEx(&strv, v, MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT), 0, VT_BSTR);
479         if(FAILED(hres))
480             return hres;
481
482         *str = V_BSTR(&strv);
483         return S_OK;
484     }
485     case VT_BSTR:
486         *str = SysAllocString(V_BSTR(v));
487         break;
488     case VT_DISPATCH: {
489         VARIANT prim;
490         HRESULT hres;
491
492         hres = to_primitive(ctx, v, ei, &prim);
493         if(FAILED(hres))
494             return hres;
495
496         hres = to_string(ctx, &prim, ei, str);
497         VariantClear(&prim);
498         return hres;
499     }
500     case VT_BOOL:
501         *str = SysAllocString(V_BOOL(v) ? trueW : falseW);
502         break;
503     default:
504         FIXME("unsupported vt %d\n", V_VT(v));
505         return E_NOTIMPL;
506     }
507
508     return *str ? S_OK : E_OUTOFMEMORY;
509 }
510
511 /* ECMA-262 3rd Edition    9.9 */
512 HRESULT to_object(exec_ctx_t *ctx, VARIANT *v, IDispatch **disp)
513 {
514     DispatchEx *dispex;
515     HRESULT hres;
516
517     switch(V_VT(v)) {
518     case VT_BSTR:
519         hres = create_string(ctx->parser->script, V_BSTR(v), SysStringLen(V_BSTR(v)), &dispex);
520         if(FAILED(hres))
521             return hres;
522
523         *disp = (IDispatch*)_IDispatchEx_(dispex);
524         break;
525     case VT_I4:
526     case VT_R8:
527         hres = create_number(ctx->parser->script, v, &dispex);
528         if(FAILED(hres))
529             return hres;
530
531         *disp = (IDispatch*)_IDispatchEx_(dispex);
532         break;
533     case VT_DISPATCH:
534         IDispatch_AddRef(V_DISPATCH(v));
535         *disp = V_DISPATCH(v);
536         break;
537     case VT_BOOL:
538         hres = create_bool(ctx->parser->script, V_BOOL(v), &dispex);
539         if(FAILED(hres))
540             return hres;
541
542         *disp = (IDispatch*)_IDispatchEx_(dispex);
543         break;
544     default:
545         FIXME("unsupported vt %d\n", V_VT(v));
546         return E_NOTIMPL;
547     }
548
549     return S_OK;
550 }