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