xref: /reactos/dll/win32/jscript/jsutils.c (revision da5f10af)
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 "jscript.h"
20 
21 WINE_DECLARE_DEBUG_CHANNEL(heap);
22 
23 const char *debugstr_jsval(const jsval_t v)
24 {
25     switch(jsval_type(v)) {
26     case JSV_UNDEFINED:
27         return "undefined";
28     case JSV_NULL:
29         return "null";
30     case JSV_OBJECT:
31         return wine_dbg_sprintf("obj(%p)", get_object(v));
32     case JSV_STRING:
33         return wine_dbg_sprintf("str(%s)", debugstr_jsstr(get_string(v)));
34     case JSV_NUMBER:
35         return wine_dbg_sprintf("%lf", get_number(v));
36     case JSV_BOOL:
37         return get_bool(v) ? "true" : "false";
38     case JSV_VARIANT:
39         return debugstr_variant(get_variant(v));
40     }
41 
42     assert(0);
43     return NULL;
44 }
45 
46 BOOL is_finite(double n)
47 {
48     return !isnan(n) && !isinf(n);
49 }
50 
51 #define MIN_BLOCK_SIZE  128
52 #define ARENA_FREE_FILLER  0xaa
53 
54 static inline DWORD block_size(DWORD block)
55 {
56     return MIN_BLOCK_SIZE << block;
57 }
58 
59 void heap_pool_init(heap_pool_t *heap)
60 {
61     memset(heap, 0, sizeof(*heap));
62     list_init(&heap->custom_blocks);
63 }
64 
65 void *heap_pool_alloc(heap_pool_t *heap, DWORD size)
66 {
67     struct list *list;
68     void *tmp;
69 
70     if(!heap->block_cnt) {
71         if(!heap->blocks) {
72             heap->blocks = heap_alloc(sizeof(void*));
73             if(!heap->blocks)
74                 return NULL;
75         }
76 
77         tmp = heap_alloc(block_size(0));
78         if(!tmp)
79             return NULL;
80 
81         heap->blocks[0] = tmp;
82         heap->block_cnt = 1;
83     }
84 
85     if(heap->offset + size <= block_size(heap->last_block)) {
86         tmp = ((BYTE*)heap->blocks[heap->last_block])+heap->offset;
87         heap->offset += size;
88         return tmp;
89     }
90 
91     if(size <= block_size(heap->last_block+1)) {
92         if(heap->last_block+1 == heap->block_cnt) {
93             tmp = heap_realloc(heap->blocks, (heap->block_cnt+1)*sizeof(void*));
94             if(!tmp)
95                 return NULL;
96 
97             heap->blocks = tmp;
98             heap->blocks[heap->block_cnt] = heap_alloc(block_size(heap->block_cnt));
99             if(!heap->blocks[heap->block_cnt])
100                 return NULL;
101 
102             heap->block_cnt++;
103         }
104 
105         heap->last_block++;
106         heap->offset = size;
107         return heap->blocks[heap->last_block];
108     }
109 
110     list = heap_alloc(size + sizeof(struct list));
111     if(!list)
112         return NULL;
113 
114     list_add_head(&heap->custom_blocks, list);
115     return list+1;
116 }
117 
118 void *heap_pool_grow(heap_pool_t *heap, void *mem, DWORD size, DWORD inc)
119 {
120     void *ret;
121 
122     if(mem == (BYTE*)heap->blocks[heap->last_block] + heap->offset-size
123        && heap->offset+inc < block_size(heap->last_block)) {
124         heap->offset += inc;
125         return mem;
126     }
127 
128     ret = heap_pool_alloc(heap, size+inc);
129     if(ret) /* FIXME: avoid copying for custom blocks */
130         memcpy(ret, mem, size);
131     return ret;
132 }
133 
134 void heap_pool_clear(heap_pool_t *heap)
135 {
136     struct list *tmp;
137 
138     if(!heap)
139         return;
140 
141     while((tmp = list_head(&heap->custom_blocks))) {
142         list_remove(tmp);
143         heap_free(tmp);
144     }
145 
146     if(WARN_ON(heap)) {
147         DWORD i;
148 
149         for(i=0; i < heap->block_cnt; i++)
150             memset(heap->blocks[i], ARENA_FREE_FILLER, block_size(i));
151     }
152 
153     heap->last_block = heap->offset = 0;
154     heap->mark = FALSE;
155 }
156 
157 void heap_pool_free(heap_pool_t *heap)
158 {
159     DWORD i;
160 
161     heap_pool_clear(heap);
162 
163     for(i=0; i < heap->block_cnt; i++)
164         heap_free(heap->blocks[i]);
165     heap_free(heap->blocks);
166 
167     heap_pool_init(heap);
168 }
169 
170 heap_pool_t *heap_pool_mark(heap_pool_t *heap)
171 {
172     if(heap->mark)
173         return NULL;
174 
175     heap->mark = TRUE;
176     return heap;
177 }
178 
179 void jsval_release(jsval_t val)
180 {
181     switch(jsval_type(val)) {
182     case JSV_OBJECT:
183         if(get_object(val))
184             IDispatch_Release(get_object(val));
185         break;
186     case JSV_STRING:
187         jsstr_release(get_string(val));
188         break;
189     case JSV_VARIANT:
190         VariantClear(get_variant(val));
191         heap_free(get_variant(val));
192         break;
193     default:
194         break;
195     }
196 }
197 
198 static HRESULT jsval_variant(jsval_t *val, VARIANT *var)
199 {
200     VARIANT *v;
201     HRESULT hres;
202 
203     __JSVAL_TYPE(*val) = JSV_VARIANT;
204     __JSVAL_VAR(*val) = v = heap_alloc(sizeof(VARIANT));
205     if(!v) {
206         *val = jsval_undefined();
207         return E_OUTOFMEMORY;
208     }
209 
210     V_VT(v) = VT_EMPTY;
211     hres = VariantCopy(v, var);
212     if(FAILED(hres)) {
213         *val = jsval_undefined();
214         heap_free(v);
215     }
216     return hres;
217 }
218 
219 HRESULT jsval_copy(jsval_t v, jsval_t *r)
220 {
221     switch(jsval_type(v)) {
222     case JSV_UNDEFINED:
223     case JSV_NULL:
224     case JSV_NUMBER:
225     case JSV_BOOL:
226         *r = v;
227         return S_OK;
228     case JSV_OBJECT:
229         if(get_object(v))
230             IDispatch_AddRef(get_object(v));
231         *r = v;
232         return S_OK;
233     case JSV_STRING: {
234         jsstr_addref(get_string(v));
235         *r = v;
236         return S_OK;
237     }
238     case JSV_VARIANT:
239         return jsval_variant(r, get_variant(v));
240     }
241 
242     assert(0);
243     return E_FAIL;
244 }
245 
246 HRESULT variant_to_jsval(VARIANT *var, jsval_t *r)
247 {
248     if(V_VT(var) == (VT_VARIANT|VT_BYREF))
249         var = V_VARIANTREF(var);
250 
251     switch(V_VT(var)) {
252     case VT_EMPTY:
253         *r = jsval_undefined();
254         return S_OK;
255     case VT_NULL:
256         *r = jsval_null();
257         return S_OK;
258     case VT_BOOL:
259         *r = jsval_bool(V_BOOL(var));
260         return S_OK;
261     case VT_I4:
262         *r = jsval_number(V_I4(var));
263         return S_OK;
264     case VT_R8:
265         *r = jsval_number(V_R8(var));
266         return S_OK;
267     case VT_BSTR: {
268         jsstr_t *str;
269 
270         if(V_BSTR(var)) {
271             str = jsstr_alloc_len(V_BSTR(var), SysStringLen(V_BSTR(var)));
272             if(!str)
273                 return E_OUTOFMEMORY;
274         }else {
275             str = jsstr_null_bstr();
276         }
277 
278         *r = jsval_string(str);
279         return S_OK;
280     }
281     case VT_DISPATCH: {
282         if(V_DISPATCH(var))
283             IDispatch_AddRef(V_DISPATCH(var));
284         *r = jsval_disp(V_DISPATCH(var));
285         return S_OK;
286     }
287     case VT_I2:
288         *r = jsval_number(V_I2(var));
289         return S_OK;
290     case VT_UI2:
291         *r = jsval_number(V_UI2(var));
292         return S_OK;
293     case VT_INT:
294         *r = jsval_number(V_INT(var));
295         return S_OK;
296     case VT_UI4:
297         *r = jsval_number(V_UI4(var));
298         return S_OK;
299     case VT_UI8:
300         /*
301          * Native doesn't support VT_UI8 here, but it's needed for IE9+ APIs
302          * (native IE9 doesn't use jscript.dll for JavaScript).
303          */
304         *r = jsval_number(V_UI8(var));
305         return S_OK;
306     case VT_R4:
307         *r = jsval_number(V_R4(var));
308         return S_OK;
309     case VT_UNKNOWN:
310         if(V_UNKNOWN(var)) {
311             IDispatch *disp;
312             HRESULT hres;
313 
314             hres = IUnknown_QueryInterface(V_UNKNOWN(var), &IID_IDispatch, (void**)&disp);
315             if(SUCCEEDED(hres)) {
316                 *r = jsval_disp(disp);
317                 return S_OK;
318             }
319         }else {
320             *r = jsval_disp(NULL);
321             return S_OK;
322         }
323         /* fall through */
324     default:
325         return jsval_variant(r, var);
326     }
327 }
328 
329 HRESULT jsval_to_variant(jsval_t val, VARIANT *retv)
330 {
331     switch(jsval_type(val)) {
332     case JSV_UNDEFINED:
333         V_VT(retv) = VT_EMPTY;
334         return S_OK;
335     case JSV_NULL:
336         V_VT(retv) = VT_NULL;
337         return S_OK;
338     case JSV_OBJECT:
339         V_VT(retv) = VT_DISPATCH;
340         if(get_object(val))
341             IDispatch_AddRef(get_object(val));
342         V_DISPATCH(retv) = get_object(val);
343         return S_OK;
344     case JSV_STRING: {
345         jsstr_t *str = get_string(val);
346 
347         V_VT(retv) = VT_BSTR;
348         if(is_null_bstr(str)) {
349             V_BSTR(retv) = NULL;
350         }else {
351             V_BSTR(retv) = SysAllocStringLen(NULL, jsstr_length(str));
352             if(V_BSTR(retv))
353                 jsstr_flush(str, V_BSTR(retv));
354             else
355                 return E_OUTOFMEMORY;
356         }
357         return S_OK;
358     }
359     case JSV_NUMBER: {
360         double n = get_number(val);
361 
362         if(is_int32(n)) {
363             V_VT(retv) = VT_I4;
364             V_I4(retv) = n;
365         }else {
366             V_VT(retv) = VT_R8;
367             V_R8(retv) = n;
368         }
369 
370         return S_OK;
371     }
372     case JSV_BOOL:
373         V_VT(retv) = VT_BOOL;
374         V_BOOL(retv) = get_bool(val) ? VARIANT_TRUE : VARIANT_FALSE;
375         return S_OK;
376     case JSV_VARIANT:
377         V_VT(retv) = VT_EMPTY;
378         return VariantCopy(retv, get_variant(val));
379     }
380 
381     assert(0);
382     return E_FAIL;
383 }
384 
385 /* ECMA-262 3rd Edition    9.1 */
386 HRESULT to_primitive(script_ctx_t *ctx, jsval_t val, jsval_t *ret, hint_t hint)
387 {
388     if(is_object_instance(val)) {
389         jsdisp_t *jsdisp;
390         jsval_t prim;
391         DISPID id;
392         HRESULT hres;
393 
394         static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0};
395         static const WCHAR valueOfW[] = {'v','a','l','u','e','O','f',0};
396 
397         if(!get_object(val)) {
398             *ret = jsval_null();
399             return S_OK;
400         }
401 
402         jsdisp = iface_to_jsdisp(get_object(val));
403         if(!jsdisp)
404             return disp_propget(ctx, get_object(val), DISPID_VALUE, ret);
405 
406         if(hint == NO_HINT)
407             hint = is_class(jsdisp, JSCLASS_DATE) ? HINT_STRING : HINT_NUMBER;
408 
409         /* Native implementation doesn't throw TypeErrors, returns strange values */
410 
411         hres = jsdisp_get_id(jsdisp, hint == HINT_STRING ? toStringW : valueOfW, 0, &id);
412         if(SUCCEEDED(hres)) {
413             hres = jsdisp_call(jsdisp, id, DISPATCH_METHOD, 0, NULL, &prim);
414             if(FAILED(hres)) {
415                 WARN("call error - forwarding exception\n");
416                 jsdisp_release(jsdisp);
417                 return hres;
418             }else if(!is_object_instance(prim)) {
419                 jsdisp_release(jsdisp);
420                 *ret = prim;
421                 return S_OK;
422             }else {
423                 IDispatch_Release(get_object(prim));
424             }
425         }
426 
427         hres = jsdisp_get_id(jsdisp, hint == HINT_STRING ? valueOfW : toStringW, 0, &id);
428         if(SUCCEEDED(hres)) {
429             hres = jsdisp_call(jsdisp, id, DISPATCH_METHOD, 0, NULL, &prim);
430             if(FAILED(hres)) {
431                 WARN("call error - forwarding exception\n");
432                 jsdisp_release(jsdisp);
433                 return hres;
434             }else if(!is_object_instance(prim)) {
435                 jsdisp_release(jsdisp);
436                 *ret = prim;
437                 return S_OK;
438             }else {
439                 IDispatch_Release(get_object(prim));
440             }
441         }
442 
443         jsdisp_release(jsdisp);
444 
445         WARN("failed\n");
446         return throw_type_error(ctx, JS_E_TO_PRIMITIVE, NULL);
447     }
448 
449     return jsval_copy(val, ret);
450 
451 }
452 
453 /* ECMA-262 3rd Edition    9.2 */
454 HRESULT to_boolean(jsval_t val, BOOL *ret)
455 {
456     switch(jsval_type(val)) {
457     case JSV_UNDEFINED:
458     case JSV_NULL:
459         *ret = FALSE;
460         return S_OK;
461     case JSV_OBJECT:
462         *ret = get_object(val) != NULL;
463         return S_OK;
464     case JSV_STRING:
465         *ret = jsstr_length(get_string(val)) != 0;
466         return S_OK;
467     case JSV_NUMBER:
468         *ret = !isnan(get_number(val)) && get_number(val);
469         return S_OK;
470     case JSV_BOOL:
471         *ret = get_bool(val);
472         return S_OK;
473     case JSV_VARIANT:
474         FIXME("unimplemented for variant %s\n", debugstr_variant(get_variant(val)));
475         return E_NOTIMPL;
476     }
477 
478     assert(0);
479     return E_FAIL;
480 }
481 
482 static int hex_to_int(WCHAR c)
483 {
484     if('0' <= c && c <= '9')
485         return c-'0';
486 
487     if('a' <= c && c <= 'f')
488         return c-'a'+10;
489 
490     if('A' <= c && c <= 'F')
491         return c-'A'+10;
492 
493     return -1;
494 }
495 
496 /* ECMA-262 3rd Edition    9.3.1 */
497 static HRESULT str_to_number(jsstr_t *str, double *ret)
498 {
499     const WCHAR *ptr;
500     BOOL neg = FALSE;
501     DOUBLE d = 0.0;
502 
503     static const WCHAR infinityW[] = {'I','n','f','i','n','i','t','y'};
504 
505     ptr = jsstr_flatten(str);
506     if(!ptr)
507         return E_OUTOFMEMORY;
508 
509     while(isspaceW(*ptr))
510         ptr++;
511 
512     if(*ptr == '-') {
513         neg = TRUE;
514         ptr++;
515     }else if(*ptr == '+') {
516         ptr++;
517     }
518 
519     if(!strncmpW(ptr, infinityW, sizeof(infinityW)/sizeof(WCHAR))) {
520         ptr += sizeof(infinityW)/sizeof(WCHAR);
521         while(*ptr && isspaceW(*ptr))
522             ptr++;
523 
524         if(*ptr)
525             *ret = NAN;
526         else
527             *ret = neg ? -INFINITY : INFINITY;
528         return S_OK;
529     }
530 
531     if(*ptr == '0' && ptr[1] == 'x') {
532         DWORD l = 0;
533 
534         ptr += 2;
535         while((l = hex_to_int(*ptr)) != -1) {
536             d = d*16 + l;
537             ptr++;
538         }
539 
540         *ret = d;
541         return S_OK;
542     }
543 
544     while(isdigitW(*ptr))
545         d = d*10 + (*ptr++ - '0');
546 
547     if(*ptr == 'e' || *ptr == 'E') {
548         BOOL eneg = FALSE;
549         LONG l = 0;
550 
551         ptr++;
552         if(*ptr == '-') {
553             ptr++;
554             eneg = TRUE;
555         }else if(*ptr == '+') {
556             ptr++;
557         }
558 
559         while(isdigitW(*ptr))
560             l = l*10 + (*ptr++ - '0');
561         if(eneg)
562             l = -l;
563 
564         d *= pow(10, l);
565     }else if(*ptr == '.') {
566         DOUBLE dec = 0.1;
567 
568         ptr++;
569         while(isdigitW(*ptr)) {
570             d += dec * (*ptr++ - '0');
571             dec *= 0.1;
572         }
573     }
574 
575     while(isspaceW(*ptr))
576         ptr++;
577 
578     if(*ptr) {
579         *ret = NAN;
580         return S_OK;
581     }
582 
583     if(neg)
584         d = -d;
585 
586     *ret = d;
587     return S_OK;
588 }
589 
590 /* ECMA-262 3rd Edition    9.3 */
591 HRESULT to_number(script_ctx_t *ctx, jsval_t val, double *ret)
592 {
593     switch(jsval_type(val)) {
594     case JSV_UNDEFINED:
595         *ret = NAN;
596         return S_OK;
597     case JSV_NULL:
598         *ret = 0;
599         return S_OK;
600     case JSV_NUMBER:
601         *ret = get_number(val);
602         return S_OK;
603     case JSV_STRING:
604         return str_to_number(get_string(val), ret);
605     case JSV_OBJECT: {
606         jsval_t prim;
607         HRESULT hres;
608 
609         hres = to_primitive(ctx, val, &prim, HINT_NUMBER);
610         if(FAILED(hres))
611             return hres;
612 
613         hres = to_number(ctx, prim, ret);
614         jsval_release(prim);
615         return hres;
616     }
617     case JSV_BOOL:
618         *ret = get_bool(val) ? 1 : 0;
619         return S_OK;
620     case JSV_VARIANT:
621         FIXME("unimplemented for variant %s\n", debugstr_variant(get_variant(val)));
622         return E_NOTIMPL;
623     };
624 
625     assert(0);
626     return E_FAIL;
627 }
628 
629 /* ECMA-262 3rd Edition    9.4 */
630 HRESULT to_integer(script_ctx_t *ctx, jsval_t v, double *ret)
631 {
632     double n;
633     HRESULT hres;
634 
635     hres = to_number(ctx, v, &n);
636     if(FAILED(hres))
637         return hres;
638 
639     if(isnan(n))
640         *ret = 0;
641     else
642         *ret = n >= 0.0 ? floor(n) : -floor(-n);
643     return S_OK;
644 }
645 
646 /* ECMA-262 3rd Edition    9.5 */
647 HRESULT to_int32(script_ctx_t *ctx, jsval_t v, INT *ret)
648 {
649     double n;
650     HRESULT hres;
651 
652     hres = to_number(ctx, v, &n);
653     if(FAILED(hres))
654         return hres;
655 
656     *ret = is_finite(n) ? n : 0;
657     return S_OK;
658 }
659 
660 /* ECMA-262 3rd Edition    9.6 */
661 HRESULT to_uint32(script_ctx_t *ctx, jsval_t val, DWORD *ret)
662 {
663     INT32 n;
664     HRESULT hres;
665 
666     hres = to_int32(ctx, val, &n);
667     if(SUCCEEDED(hres))
668         *ret = n;
669     return hres;
670 }
671 
672 static jsstr_t *int_to_string(int i)
673 {
674     WCHAR buf[12], *p;
675     BOOL neg = FALSE;
676 
677     if(!i) {
678         static const WCHAR zeroW[] = {'0',0};
679         return jsstr_alloc(zeroW);
680     }
681 
682     if(i < 0) {
683         neg = TRUE;
684         i = -i;
685     }
686 
687     p = buf + sizeof(buf)/sizeof(*buf)-1;
688     *p-- = 0;
689     while(i) {
690         *p-- = i%10 + '0';
691         i /= 10;
692     }
693 
694     if(neg)
695         *p = '-';
696     else
697         p++;
698 
699     return jsstr_alloc(p);
700 }
701 
702 HRESULT double_to_string(double n, jsstr_t **str)
703 {
704     const WCHAR InfinityW[] = {'-','I','n','f','i','n','i','t','y',0};
705 
706     if(isnan(n)) {
707         *str = jsstr_nan();
708     }else if(isinf(n)) {
709         *str = jsstr_alloc(n<0 ? InfinityW : InfinityW+1);
710     }else if(is_int32(n)) {
711         *str = int_to_string(n);
712     }else {
713         VARIANT strv, v;
714         HRESULT hres;
715 
716         /* FIXME: Don't use VariantChangeTypeEx */
717         V_VT(&v) = VT_R8;
718         V_R8(&v) = n;
719         V_VT(&strv) = VT_EMPTY;
720         hres = VariantChangeTypeEx(&strv, &v, MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT), 0, VT_BSTR);
721         if(FAILED(hres))
722             return hres;
723 
724         *str = jsstr_alloc(V_BSTR(&strv));
725         SysFreeString(V_BSTR(&strv));
726     }
727 
728     return *str ? S_OK : E_OUTOFMEMORY;
729 }
730 
731 /* ECMA-262 3rd Edition    9.8 */
732 HRESULT to_string(script_ctx_t *ctx, jsval_t val, jsstr_t **str)
733 {
734     const WCHAR nullW[] = {'n','u','l','l',0};
735     const WCHAR trueW[] = {'t','r','u','e',0};
736     const WCHAR falseW[] = {'f','a','l','s','e',0};
737 
738     switch(jsval_type(val)) {
739     case JSV_UNDEFINED:
740         *str = jsstr_undefined();
741         return S_OK;
742     case JSV_NULL:
743         *str = jsstr_alloc(nullW);
744         break;
745     case JSV_NUMBER:
746         return double_to_string(get_number(val), str);
747     case JSV_STRING:
748         *str = jsstr_addref(get_string(val));
749         break;
750     case JSV_OBJECT: {
751         jsval_t prim;
752         HRESULT hres;
753 
754         hres = to_primitive(ctx, val, &prim, HINT_STRING);
755         if(FAILED(hres))
756             return hres;
757 
758         hres = to_string(ctx, prim, str);
759         jsval_release(prim);
760         return hres;
761     }
762     case JSV_BOOL:
763         *str = jsstr_alloc(get_bool(val) ? trueW : falseW);
764         break;
765     default:
766         FIXME("unsupported %s\n", debugstr_jsval(val));
767         return E_NOTIMPL;
768     }
769 
770     return *str ? S_OK : E_OUTOFMEMORY;
771 }
772 
773 HRESULT to_flat_string(script_ctx_t *ctx, jsval_t val, jsstr_t **str, const WCHAR **ret_str)
774 {
775     HRESULT hres;
776 
777     hres = to_string(ctx, val, str);
778     if(FAILED(hres))
779         return hres;
780 
781     *ret_str = jsstr_flatten(*str);
782     if(!*ret_str) {
783         jsstr_release(*str);
784         return E_OUTOFMEMORY;
785     }
786 
787     return S_OK;
788 }
789 
790 /* ECMA-262 3rd Edition    9.9 */
791 HRESULT to_object(script_ctx_t *ctx, jsval_t val, IDispatch **disp)
792 {
793     jsdisp_t *dispex;
794     HRESULT hres;
795 
796     switch(jsval_type(val)) {
797     case JSV_STRING:
798         hres = create_string(ctx, get_string(val), &dispex);
799         if(FAILED(hres))
800             return hres;
801 
802         *disp = to_disp(dispex);
803         break;
804     case JSV_NUMBER:
805         hres = create_number(ctx, get_number(val), &dispex);
806         if(FAILED(hres))
807             return hres;
808 
809         *disp = to_disp(dispex);
810         break;
811     case JSV_OBJECT:
812         if(get_object(val)) {
813             *disp = get_object(val);
814             IDispatch_AddRef(*disp);
815         }else {
816             jsdisp_t *obj;
817 
818             hres = create_object(ctx, NULL, &obj);
819             if(FAILED(hres))
820                 return hres;
821 
822             *disp = to_disp(obj);
823         }
824         break;
825     case JSV_BOOL:
826         hres = create_bool(ctx, get_bool(val), &dispex);
827         if(FAILED(hres))
828             return hres;
829 
830         *disp = to_disp(dispex);
831         break;
832     case JSV_UNDEFINED:
833     case JSV_NULL:
834         WARN("object expected\n");
835         return throw_type_error(ctx, JS_E_OBJECT_EXPECTED, NULL);
836     case JSV_VARIANT:
837         switch(V_VT(get_variant(val))) {
838         case VT_ARRAY|VT_VARIANT:
839             hres = create_vbarray(ctx, V_ARRAY(get_variant(val)), &dispex);
840             if(FAILED(hres))
841                 return hres;
842 
843             *disp = to_disp(dispex);
844             break;
845 
846         default:
847             FIXME("Unsupported %s\n", debugstr_variant(get_variant(val)));
848             return E_NOTIMPL;
849         }
850         break;
851     }
852 
853     return S_OK;
854 }
855 
856 HRESULT variant_change_type(script_ctx_t *ctx, VARIANT *dst, VARIANT *src, VARTYPE vt)
857 {
858     jsval_t val;
859     HRESULT hres;
860 
861     clear_ei(ctx);
862     hres = variant_to_jsval(src, &val);
863     if(FAILED(hres))
864         return hres;
865 
866     switch(vt) {
867     case VT_I2:
868     case VT_I4: {
869         INT i;
870 
871         hres = to_int32(ctx, val, &i);
872         if(SUCCEEDED(hres)) {
873             if(vt == VT_I4)
874                 V_I4(dst) = i;
875             else
876                 V_I2(dst) = i;
877         }
878         break;
879     }
880     case VT_R8: {
881         double n;
882         hres = to_number(ctx, val, &n);
883         if(SUCCEEDED(hres))
884             V_R8(dst) = n;
885         break;
886     }
887     case VT_R4: {
888         double n;
889 
890         hres = to_number(ctx, val, &n);
891         if(SUCCEEDED(hres))
892             V_R4(dst) = n;
893         break;
894     }
895     case VT_BOOL: {
896         BOOL b;
897 
898         hres = to_boolean(val, &b);
899         if(SUCCEEDED(hres))
900             V_BOOL(dst) = b ? VARIANT_TRUE : VARIANT_FALSE;
901         break;
902     }
903     case VT_BSTR: {
904         jsstr_t *str;
905 
906         hres = to_string(ctx, val, &str);
907         if(FAILED(hres))
908             break;
909 
910         if(is_null_bstr(str)) {
911             V_BSTR(dst) = NULL;
912             break;
913         }
914 
915         V_BSTR(dst) = SysAllocStringLen(NULL, jsstr_length(str));
916         if(V_BSTR(dst))
917             jsstr_flush(str, V_BSTR(dst));
918         else
919             hres = E_OUTOFMEMORY;
920         break;
921     }
922     case VT_EMPTY:
923         hres = V_VT(src) == VT_EMPTY ? S_OK : E_NOTIMPL;
924         break;
925     case VT_NULL:
926         hres = V_VT(src) == VT_NULL ? S_OK : E_NOTIMPL;
927         break;
928     default:
929         FIXME("vt %d not implemented\n", vt);
930         hres = E_NOTIMPL;
931     }
932 
933     jsval_release(val);
934     if(FAILED(hres))
935         return hres;
936 
937     V_VT(dst) = vt;
938     return S_OK;
939 }
940 
941 static inline JSCaller *impl_from_IServiceProvider(IServiceProvider *iface)
942 {
943     return CONTAINING_RECORD(iface, JSCaller, IServiceProvider_iface);
944 }
945 
946 static HRESULT WINAPI JSCaller_QueryInterface(IServiceProvider *iface, REFIID riid, void **ppv)
947 {
948     JSCaller *This = impl_from_IServiceProvider(iface);
949 
950     if(IsEqualGUID(&IID_IUnknown, riid)) {
951         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
952         *ppv = &This->IServiceProvider_iface;
953     }else if(IsEqualGUID(&IID_IServiceProvider, riid)) {
954         TRACE("(%p)->(IID_IServiceProvider %p)\n", This, ppv);
955         *ppv = &This->IServiceProvider_iface;
956     }else {
957         WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
958         *ppv = NULL;
959         return E_NOINTERFACE;
960     }
961 
962     IUnknown_AddRef((IUnknown*)*ppv);
963     return S_OK;
964 }
965 
966 static ULONG WINAPI JSCaller_AddRef(IServiceProvider *iface)
967 {
968     JSCaller *This = impl_from_IServiceProvider(iface);
969     LONG ref = InterlockedIncrement(&This->ref);
970 
971     TRACE("(%p) ref=%d\n", This, ref);
972 
973     return ref;
974 }
975 
976 static ULONG WINAPI JSCaller_Release(IServiceProvider *iface)
977 {
978     JSCaller *This = impl_from_IServiceProvider(iface);
979     LONG ref = InterlockedIncrement(&This->ref);
980 
981     TRACE("(%p) ref=%d\n", This, ref);
982 
983     if(!ref) {
984         assert(!This->ctx);
985         heap_free(This);
986     }
987 
988     return ref;
989 }
990 
991 static HRESULT WINAPI JSCaller_QueryService(IServiceProvider *iface, REFGUID guidService,
992         REFIID riid, void **ppv)
993 {
994     JSCaller *This = impl_from_IServiceProvider(iface);
995 
996     if(IsEqualGUID(guidService, &SID_VariantConversion) && This->ctx && This->ctx->active_script) {
997         TRACE("(%p)->(SID_VariantConversion)\n", This);
998         return IActiveScript_QueryInterface(This->ctx->active_script, riid, ppv);
999     }
1000 
1001     FIXME("(%p)->(%s %s %p)\n", This, debugstr_guid(guidService), debugstr_guid(riid), ppv);
1002 
1003     *ppv = NULL;
1004     return E_NOINTERFACE;
1005 }
1006 
1007 static const IServiceProviderVtbl ServiceProviderVtbl = {
1008     JSCaller_QueryInterface,
1009     JSCaller_AddRef,
1010     JSCaller_Release,
1011     JSCaller_QueryService
1012 };
1013 
1014 HRESULT create_jscaller(script_ctx_t *ctx)
1015 {
1016     JSCaller *ret;
1017 
1018     ret = heap_alloc(sizeof(*ret));
1019     if(!ret)
1020         return E_OUTOFMEMORY;
1021 
1022     ret->IServiceProvider_iface.lpVtbl = &ServiceProviderVtbl;
1023     ret->ref = 1;
1024     ret->ctx = ctx;
1025 
1026     ctx->jscaller = ret;
1027     return S_OK;
1028 }
1029