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