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