xref: /reactos/dll/win32/jscript/global.c (revision c2c66aff)
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 
22 static const WCHAR NaNW[] = {'N','a','N',0};
23 static const WCHAR InfinityW[] = {'I','n','f','i','n','i','t','y',0};
24 static const WCHAR ArrayW[] = {'A','r','r','a','y',0};
25 static const WCHAR BooleanW[] = {'B','o','o','l','e','a','n',0};
26 static const WCHAR DateW[] = {'D','a','t','e',0};
27 static const WCHAR ErrorW[] = {'E','r','r','o','r',0};
28 static const WCHAR EvalErrorW[] = {'E','v','a','l','E','r','r','o','r',0};
29 static const WCHAR RangeErrorW[] = {'R','a','n','g','e','E','r','r','o','r',0};
30 static const WCHAR ReferenceErrorW[] = {'R','e','f','e','r','e','n','c','e','E','r','r','o','r',0};
31 static const WCHAR SyntaxErrorW[] = {'S','y','n','t','a','x','E','r','r','o','r',0};
32 static const WCHAR TypeErrorW[] = {'T','y','p','e','E','r','r','o','r',0};
33 static const WCHAR URIErrorW[] = {'U','R','I','E','r','r','o','r',0};
34 static const WCHAR FunctionW[] = {'F','u','n','c','t','i','o','n',0};
35 static const WCHAR NumberW[] = {'N','u','m','b','e','r',0};
36 static const WCHAR ObjectW[] = {'O','b','j','e','c','t',0};
37 static const WCHAR StringW[] = {'S','t','r','i','n','g',0};
38 static const WCHAR RegExpW[] = {'R','e','g','E','x','p',0};
39 static const WCHAR RegExpErrorW[] = {'R','e','g','E','x','p','E','r','r','o','r',0};
40 static const WCHAR ActiveXObjectW[] = {'A','c','t','i','v','e','X','O','b','j','e','c','t',0};
41 static const WCHAR VBArrayW[] = {'V','B','A','r','r','a','y',0};
42 static const WCHAR EnumeratorW[] = {'E','n','u','m','e','r','a','t','o','r',0};
43 static const WCHAR escapeW[] = {'e','s','c','a','p','e',0};
44 static const WCHAR evalW[] = {'e','v','a','l',0};
45 static const WCHAR isNaNW[] = {'i','s','N','a','N',0};
46 static const WCHAR isFiniteW[] = {'i','s','F','i','n','i','t','e',0};
47 static const WCHAR parseIntW[] = {'p','a','r','s','e','I','n','t',0};
48 static const WCHAR parseFloatW[] = {'p','a','r','s','e','F','l','o','a','t',0};
49 static const WCHAR unescapeW[] = {'u','n','e','s','c','a','p','e',0};
50 static const WCHAR _GetObjectW[] = {'G','e','t','O','b','j','e','c','t',0};
51 static const WCHAR ScriptEngineW[] = {'S','c','r','i','p','t','E','n','g','i','n','e',0};
52 static const WCHAR ScriptEngineMajorVersionW[] =
53     {'S','c','r','i','p','t','E','n','g','i','n','e','M','a','j','o','r','V','e','r','s','i','o','n',0};
54 static const WCHAR ScriptEngineMinorVersionW[] =
55     {'S','c','r','i','p','t','E','n','g','i','n','e','M','i','n','o','r','V','e','r','s','i','o','n',0};
56 static const WCHAR ScriptEngineBuildVersionW[] =
57     {'S','c','r','i','p','t','E','n','g','i','n','e','B','u','i','l','d','V','e','r','s','i','o','n',0};
58 static const WCHAR CollectGarbageW[] = {'C','o','l','l','e','c','t','G','a','r','b','a','g','e',0};
59 static const WCHAR MathW[] = {'M','a','t','h',0};
60 static const WCHAR JSONW[] = {'J','S','O','N',0};
61 static const WCHAR encodeURIW[] = {'e','n','c','o','d','e','U','R','I',0};
62 static const WCHAR decodeURIW[] = {'d','e','c','o','d','e','U','R','I',0};
63 static const WCHAR encodeURIComponentW[] = {'e','n','c','o','d','e','U','R','I','C','o','m','p','o','n','e','n','t',0};
64 static const WCHAR decodeURIComponentW[] = {'d','e','c','o','d','e','U','R','I','C','o','m','p','o','n','e','n','t',0};
65 
66 static const WCHAR undefinedW[] = {'u','n','d','e','f','i','n','e','d',0};
67 
68 static int uri_char_table[] = {
69     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 00-0f */
70     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 10-1f */
71     0,2,0,0,1,0,1,2,2,2,2,1,1,2,2,1, /* 20-2f */
72     2,2,2,2,2,2,2,2,2,2,1,1,0,1,0,1, /* 30-3f */
73     1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, /* 40-4f */
74     2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,2, /* 50-5f */
75     0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, /* 60-6f */
76     2,2,2,2,2,2,2,2,2,2,2,0,0,0,2,0, /* 70-7f */
77 };
78 
79 /* 1 - reserved */
80 /* 2 - unescaped */
81 
82 static inline BOOL is_uri_reserved(WCHAR c)
83 {
84     return c < 128 && uri_char_table[c] == 1;
85 }
86 
87 static inline BOOL is_uri_unescaped(WCHAR c)
88 {
89     return c < 128 && uri_char_table[c] == 2;
90 }
91 
92 /* Check that the character is one of the 69 non-blank characters as defined by ECMA-262 B.2.1 */
93 static inline BOOL is_ecma_nonblank(const WCHAR c)
94 {
95     return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
96         c == '@' || c == '*' || c == '_' || c == '+' || c == '-' || c == '.' || c == '/');
97 }
98 
99 static WCHAR int_to_char(int i)
100 {
101     if(i < 10)
102         return '0'+i;
103     return 'A'+i-10;
104 }
105 
106 static HRESULT JSGlobal_Enumerator(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
107         jsval_t *r)
108 {
109     FIXME("\n");
110     return E_NOTIMPL;
111 }
112 
113 static HRESULT JSGlobal_escape(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
114         jsval_t *r)
115 {
116     jsstr_t *ret_str, *str;
117     const WCHAR *ptr, *buf;
118     DWORD len = 0;
119     WCHAR *ret;
120     HRESULT hres;
121 
122     TRACE("\n");
123 
124     if(!argc) {
125         if(r)
126             *r = jsval_string(jsstr_undefined());
127         return S_OK;
128     }
129 
130     hres = to_flat_string(ctx, argv[0], &str, &buf);
131     if(FAILED(hres))
132         return hres;
133 
134     for(ptr = buf; *ptr; ptr++) {
135         if(*ptr > 0xff)
136             len += 6;
137         else if(is_ecma_nonblank(*ptr))
138             len++;
139         else
140             len += 3;
141     }
142 
143     ret_str = jsstr_alloc_buf(len, &ret);
144     if(!ret_str) {
145         jsstr_release(str);
146         return E_OUTOFMEMORY;
147     }
148 
149     len = 0;
150     for(ptr = buf; *ptr; ptr++) {
151         if(*ptr > 0xff) {
152             ret[len++] = '%';
153             ret[len++] = 'u';
154             ret[len++] = int_to_char(*ptr >> 12);
155             ret[len++] = int_to_char((*ptr >> 8) & 0xf);
156             ret[len++] = int_to_char((*ptr >> 4) & 0xf);
157             ret[len++] = int_to_char(*ptr & 0xf);
158         }
159         else if(is_ecma_nonblank(*ptr))
160             ret[len++] = *ptr;
161         else {
162             ret[len++] = '%';
163             ret[len++] = int_to_char(*ptr >> 4);
164             ret[len++] = int_to_char(*ptr & 0xf);
165         }
166     }
167 
168     jsstr_release(str);
169 
170     if(r)
171         *r = jsval_string(ret_str);
172     else
173         jsstr_release(ret_str);
174     return S_OK;
175 }
176 
177 /* ECMA-262 3rd Edition    15.1.2.1 */
178 HRESULT JSGlobal_eval(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
179         jsval_t *r)
180 {
181     call_frame_t *frame;
182     DWORD exec_flags = EXEC_EVAL;
183     bytecode_t *code;
184     const WCHAR *src;
185     HRESULT hres;
186 
187     TRACE("\n");
188 
189     if(!argc) {
190         if(r)
191             *r = jsval_undefined();
192         return S_OK;
193     }
194 
195     if(!is_string(argv[0])) {
196         if(r)
197             return jsval_copy(argv[0], r);
198         return S_OK;
199     }
200 
201     if(!(frame = ctx->call_ctx)) {
202         FIXME("No active exec_ctx\n");
203         return E_UNEXPECTED;
204     }
205 
206     src = jsstr_flatten(get_string(argv[0]));
207     if(!src)
208         return E_OUTOFMEMORY;
209 
210     TRACE("parsing %s\n", debugstr_jsval(argv[0]));
211     hres = compile_script(ctx, src, NULL, NULL, TRUE, FALSE, &code);
212     if(FAILED(hres)) {
213         WARN("parse (%s) failed: %08x\n", debugstr_jsval(argv[0]), hres);
214         return throw_syntax_error(ctx, hres, NULL);
215     }
216 
217     if(frame->flags & EXEC_GLOBAL)
218         exec_flags |= EXEC_GLOBAL;
219     if(flags & DISPATCH_JSCRIPT_CALLEREXECSSOURCE)
220         exec_flags |= EXEC_RETURN_TO_INTERP;
221     hres = exec_source(ctx, exec_flags, code, &code->global_code, frame->scope,
222             frame->this_obj, NULL, frame->variable_obj, 0, NULL, r);
223     release_bytecode(code);
224     return hres;
225 }
226 
227 static HRESULT JSGlobal_isNaN(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
228         jsval_t *r)
229 {
230     BOOL ret = TRUE;
231     double n;
232     HRESULT hres;
233 
234     TRACE("\n");
235 
236     if(argc) {
237         hres = to_number(ctx, argv[0], &n);
238         if(FAILED(hres))
239             return hres;
240 
241         if(!isnan(n))
242             ret = FALSE;
243     }
244 
245     if(r)
246         *r = jsval_bool(ret);
247     return S_OK;
248 }
249 
250 static HRESULT JSGlobal_isFinite(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
251         jsval_t *r)
252 {
253     BOOL ret = FALSE;
254     HRESULT hres;
255 
256     TRACE("\n");
257 
258     if(argc) {
259         double n;
260 
261         hres = to_number(ctx, argv[0], &n);
262         if(FAILED(hres))
263             return hres;
264 
265         ret = is_finite(n);
266     }
267 
268     if(r)
269         *r = jsval_bool(ret);
270     return S_OK;
271 }
272 
273 static INT char_to_int(WCHAR c)
274 {
275     if('0' <= c && c <= '9')
276         return c - '0';
277     if('a' <= c && c <= 'z')
278         return c - 'a' + 10;
279     if('A' <= c && c <= 'Z')
280         return c - 'A' + 10;
281     return 100;
282 }
283 
284 static HRESULT JSGlobal_parseInt(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
285         jsval_t *r)
286 {
287     BOOL neg = FALSE, empty = TRUE;
288     const WCHAR *ptr;
289     DOUBLE ret = 0.0;
290     INT radix=0, i;
291     jsstr_t *str;
292     HRESULT hres;
293 
294     if(!argc) {
295         if(r)
296             *r = jsval_number(NAN);
297         return S_OK;
298     }
299 
300     if(argc >= 2) {
301         hres = to_int32(ctx, argv[1], &radix);
302         if(FAILED(hres))
303             return hres;
304 
305         if(radix && (radix < 2 || radix > 36)) {
306             WARN("radix %d out of range\n", radix);
307             if(r)
308                 *r = jsval_number(NAN);
309             return S_OK;
310         }
311     }
312 
313     hres = to_flat_string(ctx, argv[0], &str, &ptr);
314     if(FAILED(hres))
315         return hres;
316 
317     while(isspaceW(*ptr))
318         ptr++;
319 
320     switch(*ptr) {
321     case '+':
322         ptr++;
323         break;
324     case '-':
325         neg = TRUE;
326         ptr++;
327         break;
328     }
329 
330     if(!radix) {
331         if(*ptr == '0') {
332             if(ptr[1] == 'x' || ptr[1] == 'X') {
333                 radix = 16;
334                 ptr += 2;
335             }else {
336                 radix = 8;
337                 ptr++;
338                 empty = FALSE;
339             }
340         }else {
341             radix = 10;
342         }
343     }
344 
345     i = char_to_int(*ptr++);
346     if(i < radix) {
347         do {
348             ret = ret*radix + i;
349             i = char_to_int(*ptr++);
350         }while(i < radix);
351     }else if(empty) {
352         ret = NAN;
353     }
354 
355     jsstr_release(str);
356 
357     if(neg)
358         ret = -ret;
359 
360     if(r)
361         *r = jsval_number(ret);
362     return S_OK;
363 }
364 
365 static HRESULT JSGlobal_parseFloat(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
366         jsval_t *r)
367 {
368     LONGLONG d = 0, hlp;
369     jsstr_t *val_str;
370     int exp = 0;
371     const WCHAR *str;
372     BOOL ret_nan = TRUE, positive = TRUE;
373     HRESULT hres;
374 
375     if(!argc) {
376         if(r)
377             *r = jsval_number(NAN);
378         return S_OK;
379     }
380 
381     hres = to_flat_string(ctx, argv[0], &val_str, &str);
382     if(FAILED(hres))
383         return hres;
384 
385     while(isspaceW(*str)) str++;
386 
387     if(*str == '+')
388         str++;
389     else if(*str == '-') {
390         positive = FALSE;
391         str++;
392     }
393 
394     if(isdigitW(*str))
395         ret_nan = FALSE;
396 
397     while(isdigitW(*str)) {
398         hlp = d*10 + *(str++) - '0';
399         if(d>MAXLONGLONG/10 || hlp<0) {
400             exp++;
401             break;
402         }
403         else
404             d = hlp;
405     }
406     while(isdigitW(*str)) {
407         exp++;
408         str++;
409     }
410 
411     if(*str == '.') str++;
412 
413     if(isdigitW(*str))
414         ret_nan = FALSE;
415 
416     while(isdigitW(*str)) {
417         hlp = d*10 + *(str++) - '0';
418         if(d>MAXLONGLONG/10 || hlp<0)
419             break;
420 
421         d = hlp;
422         exp--;
423     }
424     while(isdigitW(*str))
425         str++;
426 
427     if(*str && !ret_nan && (*str=='e' || *str=='E')) {
428         int sign = 1, e = 0;
429 
430         str++;
431         if(*str == '+')
432             str++;
433         else if(*str == '-') {
434             sign = -1;
435             str++;
436         }
437 
438         while(isdigitW(*str)) {
439             if(e>INT_MAX/10 || (e = e*10 + *str++ - '0')<0)
440                 e = INT_MAX;
441         }
442         e *= sign;
443 
444         if(exp<0 && e<0 && exp+e>0) exp = INT_MIN;
445         else if(exp>0 && e>0 && exp+e<0) exp = INT_MAX;
446         else exp += e;
447     }
448 
449     jsstr_release(val_str);
450 
451     if(ret_nan) {
452         if(r)
453             *r = jsval_number(NAN);
454         return S_OK;
455     }
456 
457     if(!positive)
458         d = -d;
459     if(r)
460         *r = jsval_number(exp>0 ? d*pow(10, exp) : d/pow(10, -exp));
461     return S_OK;
462 }
463 
464 static inline int hex_to_int(const WCHAR wch) {
465     if(toupperW(wch)>='A' && toupperW(wch)<='F') return toupperW(wch)-'A'+10;
466     if(isdigitW(wch)) return wch-'0';
467     return -1;
468 }
469 
470 static HRESULT JSGlobal_unescape(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
471         jsval_t *r)
472 {
473     jsstr_t *ret_str, *str;
474     const WCHAR *ptr, *buf;
475     DWORD len = 0;
476     WCHAR *ret;
477     HRESULT hres;
478 
479     TRACE("\n");
480 
481     if(!argc) {
482         if(r)
483             *r = jsval_string(jsstr_undefined());
484         return S_OK;
485     }
486 
487     hres = to_flat_string(ctx, argv[0], &str, &buf);
488     if(FAILED(hres))
489         return hres;
490 
491     for(ptr = buf; *ptr; ptr++) {
492         if(*ptr == '%') {
493             if(hex_to_int(*(ptr+1))!=-1 && hex_to_int(*(ptr+2))!=-1)
494                 ptr += 2;
495             else if(*(ptr+1)=='u' && hex_to_int(*(ptr+2))!=-1 && hex_to_int(*(ptr+3))!=-1
496                     && hex_to_int(*(ptr+4))!=-1 && hex_to_int(*(ptr+5))!=-1)
497                 ptr += 5;
498         }
499 
500         len++;
501     }
502 
503     ret_str = jsstr_alloc_buf(len, &ret);
504     if(!ret_str) {
505         jsstr_release(str);
506         return E_OUTOFMEMORY;
507     }
508 
509     len = 0;
510     for(ptr = buf; *ptr; ptr++) {
511         if(*ptr == '%') {
512             if(hex_to_int(*(ptr+1))!=-1 && hex_to_int(*(ptr+2))!=-1) {
513                 ret[len] = (hex_to_int(*(ptr+1))<<4) + hex_to_int(*(ptr+2));
514                 ptr += 2;
515             }
516             else if(*(ptr+1)=='u' && hex_to_int(*(ptr+2))!=-1 && hex_to_int(*(ptr+3))!=-1
517                     && hex_to_int(*(ptr+4))!=-1 && hex_to_int(*(ptr+5))!=-1) {
518                 ret[len] = (hex_to_int(*(ptr+2))<<12) + (hex_to_int(*(ptr+3))<<8)
519                     + (hex_to_int(*(ptr+4))<<4) + hex_to_int(*(ptr+5));
520                 ptr += 5;
521             }
522             else
523                 ret[len] = *ptr;
524         }
525         else
526             ret[len] = *ptr;
527 
528         len++;
529     }
530 
531     jsstr_release(str);
532 
533     if(r)
534         *r = jsval_string(ret_str);
535     else
536         jsstr_release(ret_str);
537     return S_OK;
538 }
539 
540 static HRESULT JSGlobal_GetObject(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
541         jsval_t *r)
542 {
543     FIXME("\n");
544     return E_NOTIMPL;
545 }
546 
547 static HRESULT JSGlobal_ScriptEngine(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
548         jsval_t *r)
549 {
550     static const WCHAR JScriptW[] = {'J','S','c','r','i','p','t',0};
551 
552     TRACE("\n");
553 
554     if(r) {
555         jsstr_t *ret;
556 
557         ret = jsstr_alloc(JScriptW);
558         if(!ret)
559             return E_OUTOFMEMORY;
560 
561         *r = jsval_string(ret);
562     }
563 
564     return S_OK;
565 }
566 
567 static HRESULT JSGlobal_ScriptEngineMajorVersion(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
568         jsval_t *r)
569 {
570     TRACE("\n");
571 
572     if(r)
573         *r = jsval_number(JSCRIPT_MAJOR_VERSION);
574     return S_OK;
575 }
576 
577 static HRESULT JSGlobal_ScriptEngineMinorVersion(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
578         jsval_t *r)
579 {
580     TRACE("\n");
581 
582     if(r)
583         *r = jsval_number(JSCRIPT_MINOR_VERSION);
584     return S_OK;
585 }
586 
587 static HRESULT JSGlobal_ScriptEngineBuildVersion(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
588         jsval_t *r)
589 {
590     TRACE("\n");
591 
592     if(r)
593         *r = jsval_number(JSCRIPT_BUILD_VERSION);
594     return S_OK;
595 }
596 
597 static HRESULT JSGlobal_CollectGarbage(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
598         jsval_t *r)
599 {
600     static int once = 0;
601     if (!once++)
602         FIXME(": stub\n");
603     return S_OK;
604 }
605 
606 static HRESULT JSGlobal_encodeURI(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
607         jsval_t *r)
608 {
609     const WCHAR *ptr, *uri;
610     jsstr_t *str, *ret;
611     DWORD len = 0, i;
612     char buf[4];
613     WCHAR *rptr;
614     HRESULT hres;
615 
616     TRACE("\n");
617 
618     if(!argc) {
619         if(r)
620             *r = jsval_string(jsstr_undefined());
621         return S_OK;
622     }
623 
624     hres = to_flat_string(ctx, argv[0], &str, &uri);
625     if(FAILED(hres))
626         return hres;
627 
628     for(ptr = uri; *ptr; ptr++) {
629         if(is_uri_unescaped(*ptr) || is_uri_reserved(*ptr) || *ptr == '#') {
630             len++;
631         }else {
632             i = WideCharToMultiByte(CP_UTF8, 0, ptr, 1, NULL, 0, NULL, NULL)*3;
633             if(!i) {
634                 jsstr_release(str);
635                 return throw_uri_error(ctx, JS_E_INVALID_URI_CHAR, NULL);
636             }
637 
638             len += i;
639         }
640     }
641 
642     ret = jsstr_alloc_buf(len, &rptr);
643     if(!ret) {
644         jsstr_release(str);
645         return E_OUTOFMEMORY;
646     }
647 
648     for(ptr = uri; *ptr; ptr++) {
649         if(is_uri_unescaped(*ptr) || is_uri_reserved(*ptr) || *ptr == '#') {
650             *rptr++ = *ptr;
651         }else {
652             len = WideCharToMultiByte(CP_UTF8, 0, ptr, 1, buf, sizeof(buf), NULL, NULL);
653             for(i=0; i<len; i++) {
654                 *rptr++ = '%';
655                 *rptr++ = int_to_char((BYTE)buf[i] >> 4);
656                 *rptr++ = int_to_char(buf[i] & 0x0f);
657             }
658         }
659     }
660 
661     TRACE("%s -> %s\n", debugstr_jsstr(str), debugstr_jsstr(ret));
662     jsstr_release(str);
663 
664     if(r)
665         *r = jsval_string(ret);
666     else
667         jsstr_release(ret);
668     return S_OK;
669 }
670 
671 static HRESULT JSGlobal_decodeURI(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
672         jsval_t *r)
673 {
674     const WCHAR *ptr, *uri;
675     jsstr_t *str, *ret_str;
676     unsigned len = 0;
677     int i, val, res;
678     WCHAR *ret;
679     char buf[4];
680     WCHAR out;
681     HRESULT hres;
682 
683     TRACE("\n");
684 
685     if(!argc) {
686         if(r)
687             *r = jsval_string(jsstr_undefined());
688         return S_OK;
689     }
690 
691     hres = to_flat_string(ctx, argv[0], &str, &uri);
692     if(FAILED(hres))
693         return hres;
694 
695     for(ptr = uri; *ptr; ptr++) {
696         if(*ptr != '%') {
697             len++;
698         }else {
699             res = 0;
700             for(i=0; i<4; i++) {
701                 if(ptr[i*3]!='%' || hex_to_int(ptr[i*3+1])==-1 || (val=hex_to_int(ptr[i*3+2]))==-1)
702                     break;
703                 val += hex_to_int(ptr[i*3+1])<<4;
704                 buf[i] = val;
705 
706                 res = MultiByteToWideChar(CP_UTF8, 0, buf, i+1, &out, 1);
707                 if(res)
708                     break;
709             }
710 
711             if(!res) {
712                 jsstr_release(str);
713                 return throw_uri_error(ctx, JS_E_INVALID_URI_CODING, NULL);
714             }
715 
716             ptr += i*3+2;
717             len++;
718         }
719     }
720 
721     ret_str = jsstr_alloc_buf(len, &ret);
722     if(!ret_str) {
723         jsstr_release(str);
724         return E_OUTOFMEMORY;
725     }
726 
727     for(ptr = uri; *ptr; ptr++) {
728         if(*ptr != '%') {
729             *ret++ = *ptr;
730         }else {
731             for(i=0; i<4; i++) {
732                 if(ptr[i*3]!='%' || hex_to_int(ptr[i*3+1])==-1 || (val=hex_to_int(ptr[i*3+2]))==-1)
733                     break;
734                 val += hex_to_int(ptr[i*3+1])<<4;
735                 buf[i] = val;
736 
737                 res = MultiByteToWideChar(CP_UTF8, 0, buf, i+1, ret, 1);
738                 if(res)
739                     break;
740             }
741 
742             ptr += i*3+2;
743             ret++;
744         }
745     }
746 
747     TRACE("%s -> %s\n", debugstr_jsstr(str), debugstr_jsstr(ret_str));
748     jsstr_release(str);
749 
750     if(r)
751         *r = jsval_string(ret_str);
752     else
753         jsstr_release(ret_str);
754     return S_OK;
755 }
756 
757 static HRESULT JSGlobal_encodeURIComponent(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
758         jsval_t *r)
759 {
760     jsstr_t *str, *ret_str;
761     char buf[4];
762     const WCHAR *ptr, *uri;
763     DWORD len = 0, size, i;
764     WCHAR *ret;
765     HRESULT hres;
766 
767     TRACE("\n");
768 
769     if(!argc) {
770         if(r)
771             *r = jsval_string(jsstr_undefined());
772         return S_OK;
773     }
774 
775     hres = to_flat_string(ctx, argv[0], &str, &uri);
776     if(FAILED(hres))
777         return hres;
778 
779     for(ptr = uri; *ptr; ptr++) {
780         if(is_uri_unescaped(*ptr))
781             len++;
782         else {
783             size = WideCharToMultiByte(CP_UTF8, 0, ptr, 1, NULL, 0, NULL, NULL);
784             if(!size) {
785                 jsstr_release(str);
786                 return throw_uri_error(ctx, JS_E_INVALID_URI_CHAR, NULL);
787             }
788             len += size*3;
789         }
790     }
791 
792     ret_str = jsstr_alloc_buf(len, &ret);
793     if(!ret_str) {
794         jsstr_release(str);
795         return E_OUTOFMEMORY;
796     }
797 
798     for(ptr = uri; *ptr; ptr++) {
799         if(is_uri_unescaped(*ptr)) {
800             *ret++ = *ptr;
801         }else {
802             size = WideCharToMultiByte(CP_UTF8, 0, ptr, 1, buf, sizeof(buf), NULL, NULL);
803             for(i=0; i<size; i++) {
804                 *ret++ = '%';
805                 *ret++ = int_to_char((BYTE)buf[i] >> 4);
806                 *ret++ = int_to_char(buf[i] & 0x0f);
807             }
808         }
809     }
810 
811     jsstr_release(str);
812 
813     if(r)
814         *r = jsval_string(ret_str);
815     else
816         jsstr_release(ret_str);
817     return S_OK;
818 }
819 
820 /* ECMA-262 3rd Edition    15.1.3.2 */
821 static HRESULT JSGlobal_decodeURIComponent(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
822         jsval_t *r)
823 {
824     const WCHAR *ptr, *uri;
825     jsstr_t *str, *ret;
826     WCHAR *out_ptr;
827     DWORD len = 0;
828     HRESULT hres;
829 
830     TRACE("\n");
831 
832     if(!argc) {
833         if(r)
834             *r = jsval_string(jsstr_undefined());
835         return S_OK;
836     }
837 
838     hres = to_flat_string(ctx, argv[0], &str, &uri);
839     if(FAILED(hres))
840         return hres;
841 
842     ptr = uri;
843     while(*ptr) {
844         if(*ptr == '%') {
845             char octets[4];
846             unsigned char mask = 0x80;
847             int i, size, num_bytes = 0;
848             if(hex_to_int(*(ptr+1)) < 0 || hex_to_int(*(ptr+2)) < 0) {
849                 FIXME("Throw URIError: Invalid hex sequence\n");
850                 jsstr_release(str);
851                 return E_FAIL;
852             }
853             octets[0] = (hex_to_int(*(ptr+1)) << 4) + hex_to_int(*(ptr+2));
854             ptr += 3;
855             while(octets[0] & mask) {
856                 mask = mask >> 1;
857                 ++num_bytes;
858             }
859             if(num_bytes == 1 || num_bytes > 4) {
860                 FIXME("Throw URIError: Invalid initial UTF character\n");
861                 jsstr_release(str);
862                 return E_FAIL;
863             }
864             for(i = 1; i < num_bytes; ++i) {
865                 if(*ptr != '%'){
866                     FIXME("Throw URIError: Incomplete UTF sequence\n");
867                     jsstr_release(str);
868                     return E_FAIL;
869                 }
870                 if(hex_to_int(*(ptr+1)) < 0 || hex_to_int(*(ptr+2)) < 0) {
871                     FIXME("Throw URIError: Invalid hex sequence\n");
872                     jsstr_release(str);
873                     return E_FAIL;
874                 }
875                 octets[i] = (hex_to_int(*(ptr+1)) << 4) + hex_to_int(*(ptr+2));
876                 ptr += 3;
877             }
878             size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, octets,
879                     num_bytes ? num_bytes : 1, NULL, 0);
880             if(size == 0) {
881                 FIXME("Throw URIError: Invalid UTF sequence\n");
882                 jsstr_release(str);
883                 return E_FAIL;
884             }
885             len += size;
886         }else {
887             ++ptr;
888             ++len;
889         }
890     }
891 
892     ret = jsstr_alloc_buf(len, &out_ptr);
893     if(!ret) {
894         jsstr_release(str);
895         return E_OUTOFMEMORY;
896     }
897 
898     ptr = uri;
899     while(*ptr) {
900         if(*ptr == '%') {
901             char octets[4];
902             unsigned char mask = 0x80;
903             int i, size, num_bytes = 0;
904             octets[0] = (hex_to_int(*(ptr+1)) << 4) + hex_to_int(*(ptr+2));
905             ptr += 3;
906             while(octets[0] & mask) {
907                 mask = mask >> 1;
908                 ++num_bytes;
909             }
910             for(i = 1; i < num_bytes; ++i) {
911                 octets[i] = (hex_to_int(*(ptr+1)) << 4) + hex_to_int(*(ptr+2));
912                 ptr += 3;
913             }
914             size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, octets,
915                     num_bytes ? num_bytes : 1, out_ptr, len);
916             len -= size;
917             out_ptr += size;
918         }else {
919             *out_ptr++ = *ptr++;
920             --len;
921         }
922     }
923 
924     jsstr_release(str);
925 
926     if(r)
927         *r = jsval_string(ret);
928     else
929         jsstr_release(ret);
930     return S_OK;
931 }
932 
933 static const builtin_prop_t JSGlobal_props[] = {
934     {CollectGarbageW,            JSGlobal_CollectGarbage,            PROPF_METHOD},
935     {EnumeratorW,                JSGlobal_Enumerator,                PROPF_METHOD|7},
936     {_GetObjectW,                JSGlobal_GetObject,                 PROPF_METHOD|2},
937     {ScriptEngineW,              JSGlobal_ScriptEngine,              PROPF_METHOD},
938     {ScriptEngineBuildVersionW,  JSGlobal_ScriptEngineBuildVersion,  PROPF_METHOD},
939     {ScriptEngineMajorVersionW,  JSGlobal_ScriptEngineMajorVersion,  PROPF_METHOD},
940     {ScriptEngineMinorVersionW,  JSGlobal_ScriptEngineMinorVersion,  PROPF_METHOD},
941     {decodeURIW,                 JSGlobal_decodeURI,                 PROPF_METHOD|1},
942     {decodeURIComponentW,        JSGlobal_decodeURIComponent,        PROPF_METHOD|1},
943     {encodeURIW,                 JSGlobal_encodeURI,                 PROPF_METHOD|1},
944     {encodeURIComponentW,        JSGlobal_encodeURIComponent,        PROPF_METHOD|1},
945     {escapeW,                    JSGlobal_escape,                    PROPF_METHOD|1},
946     {evalW,                      JSGlobal_eval,                      PROPF_METHOD|1},
947     {isFiniteW,                  JSGlobal_isFinite,                  PROPF_METHOD|1},
948     {isNaNW,                     JSGlobal_isNaN,                     PROPF_METHOD|1},
949     {parseFloatW,                JSGlobal_parseFloat,                PROPF_METHOD|1},
950     {parseIntW,                  JSGlobal_parseInt,                  PROPF_METHOD|2},
951     {unescapeW,                  JSGlobal_unescape,                  PROPF_METHOD|1}
952 };
953 
954 static const builtin_info_t JSGlobal_info = {
955     JSCLASS_GLOBAL,
956     {NULL, NULL, 0},
957     sizeof(JSGlobal_props)/sizeof(*JSGlobal_props),
958     JSGlobal_props,
959     NULL,
960     NULL
961 };
962 
963 static HRESULT init_constructors(script_ctx_t *ctx, jsdisp_t *object_prototype)
964 {
965     HRESULT hres;
966 
967     hres = init_function_constr(ctx, object_prototype);
968     if(FAILED(hres))
969         return hres;
970 
971     hres = jsdisp_propput_dontenum(ctx->global, FunctionW, jsval_obj(ctx->function_constr));
972     if(FAILED(hres))
973         return hres;
974 
975     hres = create_object_constr(ctx, object_prototype, &ctx->object_constr);
976     if(FAILED(hres))
977         return hres;
978 
979     hres = jsdisp_propput_dontenum(ctx->global, ObjectW, jsval_obj(ctx->object_constr));
980     if(FAILED(hres))
981         return hres;
982 
983     hres = create_array_constr(ctx, object_prototype, &ctx->array_constr);
984     if(FAILED(hres))
985         return hres;
986 
987     hres = jsdisp_propput_dontenum(ctx->global, ArrayW, jsval_obj(ctx->array_constr));
988     if(FAILED(hres))
989         return hres;
990 
991     hres = create_bool_constr(ctx, object_prototype, &ctx->bool_constr);
992     if(FAILED(hres))
993         return hres;
994 
995     hres = jsdisp_propput_dontenum(ctx->global, BooleanW, jsval_obj(ctx->bool_constr));
996     if(FAILED(hres))
997         return hres;
998 
999     hres = create_date_constr(ctx, object_prototype, &ctx->date_constr);
1000     if(FAILED(hres))
1001         return hres;
1002 
1003     hres = jsdisp_propput_dontenum(ctx->global, DateW, jsval_obj(ctx->date_constr));
1004     if(FAILED(hres))
1005         return hres;
1006 
1007     hres = init_error_constr(ctx, object_prototype);
1008     if(FAILED(hres))
1009         return hres;
1010 
1011     hres = jsdisp_propput_dontenum(ctx->global, ErrorW, jsval_obj(ctx->error_constr));
1012     if(FAILED(hres))
1013         return hres;
1014 
1015     hres = jsdisp_propput_dontenum(ctx->global, EvalErrorW, jsval_obj(ctx->eval_error_constr));
1016     if(FAILED(hres))
1017         return hres;
1018 
1019     hres = jsdisp_propput_dontenum(ctx->global, RangeErrorW, jsval_obj(ctx->range_error_constr));
1020     if(FAILED(hres))
1021         return hres;
1022 
1023     hres = jsdisp_propput_dontenum(ctx->global, ReferenceErrorW, jsval_obj(ctx->reference_error_constr));
1024     if(FAILED(hres))
1025         return hres;
1026 
1027     hres = jsdisp_propput_dontenum(ctx->global, RegExpErrorW, jsval_obj(ctx->regexp_error_constr));
1028     if(FAILED(hres))
1029         return hres;
1030 
1031     hres = jsdisp_propput_dontenum(ctx->global, SyntaxErrorW, jsval_obj(ctx->syntax_error_constr));
1032     if(FAILED(hres))
1033         return hres;
1034 
1035     hres = jsdisp_propput_dontenum(ctx->global, TypeErrorW, jsval_obj(ctx->type_error_constr));
1036     if(FAILED(hres))
1037         return hres;
1038 
1039     hres = jsdisp_propput_dontenum(ctx->global, URIErrorW, jsval_obj(ctx->uri_error_constr));
1040     if(FAILED(hres))
1041         return hres;
1042 
1043     hres = create_number_constr(ctx, object_prototype, &ctx->number_constr);
1044     if(FAILED(hres))
1045         return hres;
1046 
1047     hres = jsdisp_propput_dontenum(ctx->global, NumberW, jsval_obj(ctx->number_constr));
1048     if(FAILED(hres))
1049         return hres;
1050 
1051     hres = create_regexp_constr(ctx, object_prototype, &ctx->regexp_constr);
1052     if(FAILED(hres))
1053         return hres;
1054 
1055     hres = jsdisp_propput_dontenum(ctx->global, RegExpW, jsval_obj(ctx->regexp_constr));
1056     if(FAILED(hres))
1057         return hres;
1058 
1059     hres = create_string_constr(ctx, object_prototype, &ctx->string_constr);
1060     if(FAILED(hres))
1061         return hres;
1062 
1063     hres = jsdisp_propput_dontenum(ctx->global, StringW, jsval_obj(ctx->string_constr));
1064     if(FAILED(hres))
1065         return hres;
1066 
1067     hres = create_vbarray_constr(ctx, object_prototype, &ctx->vbarray_constr);
1068     if(FAILED(hres))
1069         return hres;
1070 
1071     hres = jsdisp_propput_dontenum(ctx->global, VBArrayW, jsval_obj(ctx->vbarray_constr));
1072     if(FAILED(hres))
1073         return hres;
1074 
1075     return S_OK;
1076 }
1077 
1078 HRESULT init_global(script_ctx_t *ctx)
1079 {
1080     jsdisp_t *math, *object_prototype, *constr;
1081     HRESULT hres;
1082 
1083     if(ctx->global)
1084         return S_OK;
1085 
1086     hres = create_dispex(ctx, &JSGlobal_info, NULL, &ctx->global);
1087     if(FAILED(hres))
1088         return hres;
1089 
1090     hres = create_object_prototype(ctx, &object_prototype);
1091     if(FAILED(hres))
1092         return hres;
1093 
1094     hres = init_constructors(ctx, object_prototype);
1095     jsdisp_release(object_prototype);
1096     if(FAILED(hres))
1097         return hres;
1098 
1099     hres = create_math(ctx, &math);
1100     if(FAILED(hres))
1101         return hres;
1102 
1103     hres = jsdisp_propput_dontenum(ctx->global, MathW, jsval_obj(math));
1104     jsdisp_release(math);
1105     if(FAILED(hres))
1106         return hres;
1107 
1108     if(ctx->version >= 2) {
1109         jsdisp_t *json;
1110 
1111         hres = create_json(ctx, &json);
1112         if(FAILED(hres))
1113             return hres;
1114 
1115         hres = jsdisp_propput_dontenum(ctx->global, JSONW, jsval_obj(json));
1116         jsdisp_release(json);
1117         if(FAILED(hres))
1118             return hres;
1119     }
1120 
1121     hres = create_activex_constr(ctx, &constr);
1122     if(FAILED(hres))
1123         return hres;
1124 
1125     hres = jsdisp_propput_dontenum(ctx->global, ActiveXObjectW, jsval_obj(constr));
1126     jsdisp_release(constr);
1127     if(FAILED(hres))
1128         return hres;
1129 
1130     hres = jsdisp_propput_dontenum(ctx->global, undefinedW, jsval_undefined());
1131     if(FAILED(hres))
1132         return hres;
1133 
1134     hres = jsdisp_propput_dontenum(ctx->global, NaNW, jsval_number(NAN));
1135     if(FAILED(hres))
1136         return hres;
1137 
1138     hres = jsdisp_propput_dontenum(ctx->global, InfinityW, jsval_number(INFINITY));
1139     return hres;
1140 }
1141