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