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