jscript: Properly handle NULL bstr in str_to_number.
[wine/multimedia.git] / dlls / jscript / jsutils.c
blob040ec58c38de18667a5815d6f1259a48ee40e579
1 /*
2 * Copyright 2008 Jacek Caban for CodeWeavers
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.
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.
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
19 #include "config.h"
20 #include "wine/port.h"
22 #include <math.h>
23 #include <assert.h>
25 #include "jscript.h"
26 #include "engine.h"
28 #include "wine/debug.h"
30 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
31 WINE_DECLARE_DEBUG_CHANNEL(heap);
33 const char *debugstr_variant(const VARIANT *v)
35 if(!v)
36 return "(null)";
38 switch(V_VT(v)) {
39 case VT_EMPTY:
40 return "{VT_EMPTY}";
41 case VT_NULL:
42 return "{VT_NULL}";
43 case VT_I4:
44 return wine_dbg_sprintf("{VT_I4: %d}", V_I4(v));
45 case VT_UI4:
46 return wine_dbg_sprintf("{VT_UI4: %u}", V_UI4(v));
47 case VT_R8:
48 return wine_dbg_sprintf("{VT_R8: %lf}", V_R8(v));
49 case VT_BSTR:
50 return wine_dbg_sprintf("{VT_BSTR: %s}", debugstr_w(V_BSTR(v)));
51 case VT_DISPATCH:
52 return wine_dbg_sprintf("{VT_DISPATCH: %p}", V_DISPATCH(v));
53 case VT_BOOL:
54 return wine_dbg_sprintf("{VT_BOOL: %x}", V_BOOL(v));
55 default:
56 return wine_dbg_sprintf("{vt %d}", V_VT(v));
60 #define MIN_BLOCK_SIZE 128
61 #define ARENA_FREE_FILLER 0xaa
63 static inline DWORD block_size(DWORD block)
65 return MIN_BLOCK_SIZE << block;
68 void jsheap_init(jsheap_t *heap)
70 memset(heap, 0, sizeof(*heap));
71 list_init(&heap->custom_blocks);
74 void *jsheap_alloc(jsheap_t *heap, DWORD size)
76 struct list *list;
77 void *tmp;
79 if(!heap->block_cnt) {
80 if(!heap->blocks) {
81 heap->blocks = heap_alloc(sizeof(void*));
82 if(!heap->blocks)
83 return NULL;
86 tmp = heap_alloc(block_size(0));
87 if(!tmp)
88 return NULL;
90 heap->blocks[0] = tmp;
91 heap->block_cnt = 1;
94 if(heap->offset + size <= block_size(heap->last_block)) {
95 tmp = ((BYTE*)heap->blocks[heap->last_block])+heap->offset;
96 heap->offset += size;
97 return tmp;
100 if(size <= block_size(heap->last_block+1)) {
101 if(heap->last_block+1 == heap->block_cnt) {
102 tmp = heap_realloc(heap->blocks, (heap->block_cnt+1)*sizeof(void*));
103 if(!tmp)
104 return NULL;
106 heap->blocks = tmp;
107 heap->blocks[heap->block_cnt] = heap_alloc(block_size(heap->block_cnt));
108 if(!heap->blocks[heap->block_cnt])
109 return NULL;
111 heap->block_cnt++;
114 heap->last_block++;
115 heap->offset = size;
116 return heap->blocks[heap->last_block];
119 list = heap_alloc(size + sizeof(struct list));
120 if(!list)
121 return NULL;
123 list_add_head(&heap->custom_blocks, list);
124 return list+1;
127 void *jsheap_grow(jsheap_t *heap, void *mem, DWORD size, DWORD inc)
129 if(mem == (BYTE*)heap->blocks[heap->last_block] + heap->offset-size
130 && heap->offset+inc < block_size(heap->last_block)) {
131 heap->offset += inc;
132 return mem;
135 return jsheap_alloc(heap, size+inc);
138 void jsheap_clear(jsheap_t *heap)
140 struct list *tmp;
142 if(!heap)
143 return;
145 while((tmp = list_next(&heap->custom_blocks, &heap->custom_blocks))) {
146 list_remove(tmp);
147 heap_free(tmp);
150 if(WARN_ON(heap)) {
151 DWORD i;
153 for(i=0; i < heap->block_cnt; i++)
154 memset(heap->blocks[i], ARENA_FREE_FILLER, block_size(i));
157 heap->last_block = heap->offset = 0;
158 heap->mark = FALSE;
161 void jsheap_free(jsheap_t *heap)
163 DWORD i;
165 jsheap_clear(heap);
167 for(i=0; i < heap->block_cnt; i++)
168 heap_free(heap->blocks[i]);
169 heap_free(heap->blocks);
171 jsheap_init(heap);
174 jsheap_t *jsheap_mark(jsheap_t *heap)
176 if(heap->mark)
177 return NULL;
179 heap->mark = TRUE;
180 return heap;
183 /* ECMA-262 3rd Edition 9.1 */
184 HRESULT to_primitive(script_ctx_t *ctx, VARIANT *v, jsexcept_t *ei, VARIANT *ret, hint_t hint)
186 switch(V_VT(v)) {
187 case VT_EMPTY:
188 case VT_NULL:
189 case VT_BOOL:
190 case VT_I4:
191 case VT_R8:
192 *ret = *v;
193 break;
194 case VT_BSTR:
195 V_VT(ret) = VT_BSTR;
196 V_BSTR(ret) = SysAllocString(V_BSTR(v));
197 break;
198 case VT_DISPATCH: {
199 jsdisp_t *jsdisp;
200 DISPID id;
201 DISPPARAMS dp = {NULL, NULL, 0, 0};
202 HRESULT hres;
204 static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0};
205 static const WCHAR valueOfW[] = {'v','a','l','u','e','O','f',0};
207 if(!V_DISPATCH(v)) {
208 V_VT(ret) = VT_NULL;
209 break;
212 jsdisp = iface_to_jsdisp((IUnknown*)V_DISPATCH(v));
213 if(!jsdisp) {
214 V_VT(ret) = VT_EMPTY;
215 return disp_propget(ctx, V_DISPATCH(v), DISPID_VALUE, ret, ei);
218 if(hint == NO_HINT)
219 hint = is_class(jsdisp, JSCLASS_DATE) ? HINT_STRING : HINT_NUMBER;
221 /* Native implementation doesn't throw TypeErrors, returns strange values */
223 hres = jsdisp_get_id(jsdisp, hint == HINT_STRING ? toStringW : valueOfW, 0, &id);
224 if(SUCCEEDED(hres)) {
225 hres = jsdisp_call(jsdisp, id, DISPATCH_METHOD, &dp, ret, ei);
226 if(FAILED(hres)) {
227 WARN("call error - forwarding exception\n");
228 jsdisp_release(jsdisp);
229 return hres;
231 else if(V_VT(ret) != VT_DISPATCH) {
232 jsdisp_release(jsdisp);
233 return S_OK;
235 else
236 IDispatch_Release(V_DISPATCH(ret));
239 hres = jsdisp_get_id(jsdisp, hint == HINT_STRING ? valueOfW : toStringW, 0, &id);
240 if(SUCCEEDED(hres)) {
241 hres = jsdisp_call(jsdisp, id, DISPATCH_METHOD, &dp, ret, ei);
242 if(FAILED(hres)) {
243 WARN("call error - forwarding exception\n");
244 jsdisp_release(jsdisp);
245 return hres;
247 else if(V_VT(ret) != VT_DISPATCH) {
248 jsdisp_release(jsdisp);
249 return S_OK;
251 else
252 IDispatch_Release(V_DISPATCH(ret));
255 jsdisp_release(jsdisp);
257 WARN("failed\n");
258 return throw_type_error(ctx, ei, JS_E_TO_PRIMITIVE, NULL);
260 default:
261 FIXME("Unimplemented for vt %d\n", V_VT(v));
262 return E_NOTIMPL;
265 return S_OK;
268 /* ECMA-262 3rd Edition 9.2 */
269 HRESULT to_boolean(VARIANT *v, VARIANT_BOOL *b)
271 switch(V_VT(v)) {
272 case VT_EMPTY:
273 case VT_NULL:
274 *b = VARIANT_FALSE;
275 break;
276 case VT_I4:
277 *b = V_I4(v) ? VARIANT_TRUE : VARIANT_FALSE;
278 break;
279 case VT_R8:
280 if(isnan(V_R8(v))) *b = VARIANT_FALSE;
281 else *b = V_R8(v) ? VARIANT_TRUE : VARIANT_FALSE;
282 break;
283 case VT_BSTR:
284 *b = V_BSTR(v) && *V_BSTR(v) ? VARIANT_TRUE : VARIANT_FALSE;
285 break;
286 case VT_DISPATCH:
287 *b = V_DISPATCH(v) ? VARIANT_TRUE : VARIANT_FALSE;
288 break;
289 case VT_BOOL:
290 *b = V_BOOL(v);
291 break;
292 default:
293 FIXME("unimplemented for vt %d\n", V_VT(v));
294 return E_NOTIMPL;
297 return S_OK;
300 static int hex_to_int(WCHAR c)
302 if('0' <= c && c <= '9')
303 return c-'0';
305 if('a' <= c && c <= 'f')
306 return c-'a'+10;
308 if('A' <= c && c <= 'F')
309 return c-'A'+10;
311 return -1;
314 /* ECMA-262 3rd Edition 9.3.1 */
315 static HRESULT str_to_number(BSTR str, double *ret)
317 const WCHAR *ptr = str;
318 BOOL neg = FALSE;
319 DOUBLE d = 0.0;
321 static const WCHAR infinityW[] = {'I','n','f','i','n','i','t','y'};
323 if(!ptr) {
324 *ret = 0;
325 return S_OK;
328 while(isspaceW(*ptr))
329 ptr++;
331 if(*ptr == '-') {
332 neg = TRUE;
333 ptr++;
334 }else if(*ptr == '+') {
335 ptr++;
338 if(!strncmpW(ptr, infinityW, sizeof(infinityW)/sizeof(WCHAR))) {
339 ptr += sizeof(infinityW)/sizeof(WCHAR);
340 while(*ptr && isspaceW(*ptr))
341 ptr++;
343 if(*ptr)
344 *ret = ret_nan();
345 else
346 *ret = neg ? -ret_inf() : ret_inf();
347 return S_OK;
350 if(*ptr == '0' && ptr[1] == 'x') {
351 DWORD l = 0;
353 ptr += 2;
354 while((l = hex_to_int(*ptr)) != -1) {
355 d = d*16 + l;
356 ptr++;
359 *ret = d;
360 return S_OK;
363 while(isdigitW(*ptr))
364 d = d*10 + (*ptr++ - '0');
366 if(*ptr == 'e' || *ptr == 'E') {
367 BOOL eneg = FALSE;
368 LONG l = 0;
370 ptr++;
371 if(*ptr == '-') {
372 ptr++;
373 eneg = TRUE;
374 }else if(*ptr == '+') {
375 ptr++;
378 while(isdigitW(*ptr))
379 l = l*10 + (*ptr++ - '0');
380 if(eneg)
381 l = -l;
383 d *= pow(10, l);
384 }else if(*ptr == '.') {
385 DOUBLE dec = 0.1;
387 ptr++;
388 while(isdigitW(*ptr)) {
389 d += dec * (*ptr++ - '0');
390 dec *= 0.1;
394 while(isspaceW(*ptr))
395 ptr++;
397 if(*ptr) {
398 *ret = ret_nan();
399 return S_OK;
402 if(neg)
403 d = -d;
405 *ret = d;
406 return S_OK;
409 /* ECMA-262 3rd Edition 9.3 */
410 HRESULT to_number(script_ctx_t *ctx, VARIANT *v, jsexcept_t *ei, double *ret)
412 switch(V_VT(v)) {
413 case VT_EMPTY:
414 *ret = ret_nan();
415 break;
416 case VT_NULL:
417 *ret = 0;
418 break;
419 case VT_I4:
420 *ret = V_I4(v);
421 break;
422 case VT_R8:
423 *ret = V_R8(v);
424 break;
425 case VT_BSTR:
426 return str_to_number(V_BSTR(v), ret);
427 case VT_DISPATCH: {
428 VARIANT prim;
429 HRESULT hres;
431 hres = to_primitive(ctx, v, ei, &prim, HINT_NUMBER);
432 if(FAILED(hres))
433 return hres;
435 hres = to_number(ctx, &prim, ei, ret);
436 VariantClear(&prim);
437 return hres;
439 case VT_BOOL:
440 *ret = V_BOOL(v) ? 1 : 0;
441 break;
442 default:
443 FIXME("unimplemented for vt %d\n", V_VT(v));
444 return E_NOTIMPL;
447 return S_OK;
450 /* ECMA-262 3rd Edition 9.4 */
451 HRESULT to_integer(script_ctx_t *ctx, VARIANT *v, jsexcept_t *ei, VARIANT *ret)
453 double n;
454 HRESULT hres;
456 if(V_VT(v) == VT_I4) {
457 *ret = *v;
458 return S_OK;
461 hres = to_number(ctx, v, ei, &n);
462 if(FAILED(hres))
463 return hres;
465 if(isnan(n)) {
466 V_VT(ret) = VT_I4;
467 V_I4(ret) = 0;
468 }else {
469 num_set_val(ret, n >= 0.0 ? floor(n) : -floor(-n));
472 return S_OK;
475 /* ECMA-262 3rd Edition 9.5 */
476 HRESULT to_int32(script_ctx_t *ctx, VARIANT *v, jsexcept_t *ei, INT *ret)
478 double n;
479 HRESULT hres;
481 if(V_VT(v) == VT_I4) {
482 *ret = V_I4(v);
483 return S_OK;
486 hres = to_number(ctx, v, ei, &n);
487 if(FAILED(hres))
488 return hres;
490 *ret = isnan(n) || isinf(n) ? 0 : n;
491 return S_OK;
494 /* ECMA-262 3rd Edition 9.6 */
495 HRESULT to_uint32(script_ctx_t *ctx, VARIANT *v, jsexcept_t *ei, DWORD *ret)
497 double n;
498 HRESULT hres;
500 if(V_VT(v) == VT_I4) {
501 *ret = V_I4(v);
502 return S_OK;
505 hres = to_number(ctx, v, ei, &n);
506 if(FAILED(hres))
507 return hres;
509 *ret = isnan(n) || isinf(n) ? 0 : n;
510 return S_OK;
513 BSTR int_to_bstr(int i)
515 WCHAR buf[12], *p;
516 BOOL neg = FALSE;
518 if(!i) {
519 static const WCHAR zeroW[] = {'0',0};
520 return SysAllocString(zeroW);
523 if(i < 0) {
524 neg = TRUE;
525 i = -i;
528 p = buf + sizeof(buf)/sizeof(*buf)-1;
529 *p-- = 0;
530 while(i) {
531 *p-- = i%10 + '0';
532 i /= 10;
535 if(neg)
536 *p = '-';
537 else
538 p++;
540 return SysAllocString(p);
543 HRESULT double_to_bstr(double n, BSTR *str)
545 const WCHAR NaNW[] = {'N','a','N',0};
546 const WCHAR InfinityW[] = {'-','I','n','f','i','n','i','t','y',0};
548 if(isnan(n)) {
549 *str = SysAllocString(NaNW);
550 }else if(isinf(n)) {
551 *str = SysAllocString(n<0 ? InfinityW : InfinityW+1);
552 }else {
553 VARIANT strv, v;
554 HRESULT hres;
556 V_VT(&v) = VT_R8;
557 V_R8(&v) = n;
558 V_VT(&strv) = VT_EMPTY;
559 hres = VariantChangeTypeEx(&strv, &v, MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT), 0, VT_BSTR);
560 if(FAILED(hres))
561 return hres;
563 *str = V_BSTR(&strv);
566 return *str ? S_OK : E_OUTOFMEMORY;
569 /* ECMA-262 3rd Edition 9.8 */
570 HRESULT to_string(script_ctx_t *ctx, VARIANT *v, jsexcept_t *ei, BSTR *str)
572 const WCHAR undefinedW[] = {'u','n','d','e','f','i','n','e','d',0};
573 const WCHAR nullW[] = {'n','u','l','l',0};
574 const WCHAR trueW[] = {'t','r','u','e',0};
575 const WCHAR falseW[] = {'f','a','l','s','e',0};
577 switch(V_VT(v)) {
578 case VT_EMPTY:
579 *str = SysAllocString(undefinedW);
580 break;
581 case VT_NULL:
582 *str = SysAllocString(nullW);
583 break;
584 case VT_I4:
585 *str = int_to_bstr(V_I4(v));
586 break;
587 case VT_R8:
588 return double_to_bstr(V_R8(v), str);
589 case VT_BSTR:
590 *str = SysAllocString(V_BSTR(v));
591 break;
592 case VT_DISPATCH: {
593 VARIANT prim;
594 HRESULT hres;
596 hres = to_primitive(ctx, v, ei, &prim, HINT_STRING);
597 if(FAILED(hres))
598 return hres;
600 hres = to_string(ctx, &prim, ei, str);
601 VariantClear(&prim);
602 return hres;
604 case VT_BOOL:
605 *str = SysAllocString(V_BOOL(v) ? trueW : falseW);
606 break;
607 default:
608 FIXME("unsupported vt %d\n", V_VT(v));
609 return E_NOTIMPL;
612 return *str ? S_OK : E_OUTOFMEMORY;
615 /* ECMA-262 3rd Edition 9.9 */
616 HRESULT to_object(script_ctx_t *ctx, VARIANT *v, IDispatch **disp)
618 jsdisp_t *dispex;
619 HRESULT hres;
621 switch(V_VT(v)) {
622 case VT_BSTR:
623 hres = create_string(ctx, V_BSTR(v), SysStringLen(V_BSTR(v)), &dispex);
624 if(FAILED(hres))
625 return hres;
627 *disp = to_disp(dispex);
628 break;
629 case VT_I4:
630 case VT_R8:
631 hres = create_number(ctx, num_val(v), &dispex);
632 if(FAILED(hres))
633 return hres;
635 *disp = to_disp(dispex);
636 break;
637 case VT_DISPATCH:
638 if(V_DISPATCH(v)) {
639 IDispatch_AddRef(V_DISPATCH(v));
640 *disp = V_DISPATCH(v);
641 }else {
642 jsdisp_t *obj;
644 hres = create_object(ctx, NULL, &obj);
645 if(FAILED(hres))
646 return hres;
648 *disp = to_disp(obj);
650 break;
651 case VT_BOOL:
652 hres = create_bool(ctx, V_BOOL(v), &dispex);
653 if(FAILED(hres))
654 return hres;
656 *disp = to_disp(dispex);
657 break;
658 case VT_ARRAY|VT_VARIANT:
659 hres = create_vbarray(ctx, V_ARRAY(v), &dispex);
660 if(FAILED(hres))
661 return hres;
663 *disp = to_disp(dispex);
664 break;
665 default:
666 FIXME("unsupported vt %d\n", V_VT(v));
667 return E_NOTIMPL;
670 return S_OK;
673 HRESULT variant_change_type(script_ctx_t *ctx, VARIANT *dst, VARIANT *src, VARTYPE vt)
675 jsexcept_t ei;
676 HRESULT hres;
678 memset(&ei, 0, sizeof(ei));
680 switch(vt) {
681 case VT_I2:
682 case VT_I4: {
683 INT i;
685 hres = to_int32(ctx, src, &ei, &i);
686 if(SUCCEEDED(hres)) {
687 if(vt == VT_I4)
688 V_I4(dst) = i;
689 else
690 V_I2(dst) = i;
692 break;
694 case VT_R8: {
695 double n;
696 hres = to_number(ctx, src, &ei, &n);
697 if(SUCCEEDED(hres))
698 V_R8(dst) = n;
699 break;
701 case VT_R4: {
702 double n;
704 hres = to_number(ctx, src, &ei, &n);
705 if(SUCCEEDED(hres))
706 V_R4(dst) = n;
707 break;
709 case VT_BOOL: {
710 VARIANT_BOOL b;
712 hres = to_boolean(src, &b);
713 if(SUCCEEDED(hres))
714 V_BOOL(dst) = b;
715 break;
717 case VT_BSTR: {
718 BSTR str;
720 hres = to_string(ctx, src, &ei, &str);
721 if(SUCCEEDED(hres))
722 V_BSTR(dst) = str;
723 break;
725 case VT_EMPTY:
726 hres = V_VT(src) == VT_EMPTY ? S_OK : E_NOTIMPL;
727 break;
728 case VT_NULL:
729 hres = V_VT(src) == VT_NULL ? S_OK : E_NOTIMPL;
730 break;
731 default:
732 FIXME("vt %d not implemented\n", vt);
733 hres = E_NOTIMPL;
736 if(FAILED(hres)) {
737 VariantClear(&ei.var);
738 return hres;
741 V_VT(dst) = vt;
742 return S_OK;
745 static inline JSCaller *impl_from_IServiceProvider(IServiceProvider *iface)
747 return CONTAINING_RECORD(iface, JSCaller, IServiceProvider_iface);
750 static HRESULT WINAPI JSCaller_QueryInterface(IServiceProvider *iface, REFIID riid, void **ppv)
752 JSCaller *This = impl_from_IServiceProvider(iface);
754 if(IsEqualGUID(&IID_IUnknown, riid)) {
755 TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
756 *ppv = &This->IServiceProvider_iface;
757 }else if(IsEqualGUID(&IID_IServiceProvider, riid)) {
758 TRACE("(%p)->(IID_IServiceProvider %p)\n", This, ppv);
759 *ppv = &This->IServiceProvider_iface;
760 }else {
761 WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
762 *ppv = NULL;
763 return E_NOINTERFACE;
766 IUnknown_AddRef((IUnknown*)*ppv);
767 return S_OK;
770 static ULONG WINAPI JSCaller_AddRef(IServiceProvider *iface)
772 JSCaller *This = impl_from_IServiceProvider(iface);
773 LONG ref = InterlockedIncrement(&This->ref);
775 TRACE("(%p) ref=%d\n", This, ref);
777 return ref;
780 static ULONG WINAPI JSCaller_Release(IServiceProvider *iface)
782 JSCaller *This = impl_from_IServiceProvider(iface);
783 LONG ref = InterlockedIncrement(&This->ref);
785 TRACE("(%p) ref=%d\n", This, ref);
787 if(!ref) {
788 assert(!This->ctx);
789 heap_free(This);
792 return ref;
795 static HRESULT WINAPI JSCaller_QueryService(IServiceProvider *iface, REFGUID guidService,
796 REFIID riid, void **ppv)
798 JSCaller *This = impl_from_IServiceProvider(iface);
800 if(IsEqualGUID(guidService, &SID_VariantConversion) && This->ctx && This->ctx->active_script) {
801 TRACE("(%p)->(SID_VariantConversion)\n", This);
802 return IActiveScript_QueryInterface(This->ctx->active_script, riid, ppv);
805 FIXME("(%p)->(%s %s %p)\n", This, debugstr_guid(guidService), debugstr_guid(riid), ppv);
807 *ppv = NULL;
808 return E_NOINTERFACE;
811 static const IServiceProviderVtbl ServiceProviderVtbl = {
812 JSCaller_QueryInterface,
813 JSCaller_AddRef,
814 JSCaller_Release,
815 JSCaller_QueryService
818 HRESULT create_jscaller(script_ctx_t *ctx)
820 JSCaller *ret;
822 ret = heap_alloc(sizeof(*ret));
823 if(!ret)
824 return E_OUTOFMEMORY;
826 ret->IServiceProvider_iface.lpVtbl = &ServiceProviderVtbl;
827 ret->ref = 1;
828 ret->ctx = ctx;
830 ctx->jscaller = ret;
831 return S_OK;