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