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 <limits.h> 23 24 #include "jscript.h" 25 #include "activscp.h" 26 #include "objsafe.h" 27 #include "engine.h" 28 #include "parser.h" 29 30 #include "parser.tab.h" 31 32 #include "wine/debug.h" 33 #include "wine/unicode.h" 34 35 #ifdef __REACTOS__ 36 /* FIXME: Inspect - For some reason these exist in the generated header but are not picked up */ 37 #define kGET (270) 38 #define kSET (272) 39 #endif 40 41 WINE_DEFAULT_DEBUG_CHANNEL(jscript); 42 43 static const WCHAR breakW[] = {'b','r','e','a','k',0}; 44 static const WCHAR caseW[] = {'c','a','s','e',0}; 45 static const WCHAR catchW[] = {'c','a','t','c','h',0}; 46 static const WCHAR continueW[] = {'c','o','n','t','i','n','u','e',0}; 47 static const WCHAR defaultW[] = {'d','e','f','a','u','l','t',0}; 48 static const WCHAR deleteW[] = {'d','e','l','e','t','e',0}; 49 static const WCHAR doW[] = {'d','o',0}; 50 static const WCHAR elseW[] = {'e','l','s','e',0}; 51 static const WCHAR falseW[] = {'f','a','l','s','e',0}; 52 static const WCHAR finallyW[] = {'f','i','n','a','l','l','y',0}; 53 static const WCHAR forW[] = {'f','o','r',0}; 54 static const WCHAR functionW[] = {'f','u','n','c','t','i','o','n',0}; 55 static const WCHAR getW[] = {'g','e','t',0}; 56 static const WCHAR ifW[] = {'i','f',0}; 57 static const WCHAR inW[] = {'i','n',0}; 58 static const WCHAR instanceofW[] = {'i','n','s','t','a','n','c','e','o','f',0}; 59 static const WCHAR newW[] = {'n','e','w',0}; 60 static const WCHAR nullW[] = {'n','u','l','l',0}; 61 static const WCHAR returnW[] = {'r','e','t','u','r','n',0}; 62 static const WCHAR setW[] = {'s','e','t',0}; 63 static const WCHAR switchW[] = {'s','w','i','t','c','h',0}; 64 static const WCHAR thisW[] = {'t','h','i','s',0}; 65 static const WCHAR throwW[] = {'t','h','r','o','w',0}; 66 static const WCHAR trueW[] = {'t','r','u','e',0}; 67 static const WCHAR tryW[] = {'t','r','y',0}; 68 static const WCHAR typeofW[] = {'t','y','p','e','o','f',0}; 69 static const WCHAR varW[] = {'v','a','r',0}; 70 static const WCHAR voidW[] = {'v','o','i','d',0}; 71 static const WCHAR whileW[] = {'w','h','i','l','e',0}; 72 static const WCHAR withW[] = {'w','i','t','h',0}; 73 74 static const WCHAR elifW[] = {'e','l','i','f',0}; 75 static const WCHAR endW[] = {'e','n','d',0}; 76 77 static const struct { 78 const WCHAR *word; 79 int token; 80 BOOL no_nl; 81 unsigned min_version; 82 } keywords[] = { 83 {breakW, kBREAK, TRUE}, 84 {caseW, kCASE}, 85 {catchW, kCATCH}, 86 {continueW, kCONTINUE, TRUE}, 87 {defaultW, kDEFAULT}, 88 {deleteW, kDELETE}, 89 {doW, kDO}, 90 {elseW, kELSE}, 91 {falseW, kFALSE}, 92 {finallyW, kFINALLY}, 93 {forW, kFOR}, 94 {functionW, kFUNCTION}, 95 {getW, kGET, FALSE, SCRIPTLANGUAGEVERSION_ES5}, 96 {ifW, kIF}, 97 {inW, kIN}, 98 {instanceofW, kINSTANCEOF}, 99 {newW, kNEW}, 100 {nullW, kNULL}, 101 {returnW, kRETURN, TRUE}, 102 {setW, kSET, FALSE, SCRIPTLANGUAGEVERSION_ES5}, 103 {switchW, kSWITCH}, 104 {thisW, kTHIS}, 105 {throwW, kTHROW}, 106 {trueW, kTRUE}, 107 {tryW, kTRY}, 108 {typeofW, kTYPEOF}, 109 {varW, kVAR}, 110 {voidW, kVOID}, 111 {whileW, kWHILE}, 112 {withW, kWITH} 113 }; 114 115 static int lex_error(parser_ctx_t *ctx, HRESULT hres) 116 { 117 ctx->hres = hres; 118 ctx->lexer_error = TRUE; 119 return -1; 120 } 121 122 /* ECMA-262 3rd Edition 7.6 */ 123 BOOL is_identifier_char(WCHAR c) 124 { 125 return isalnumW(c) || c == '$' || c == '_' || c == '\\'; 126 } 127 128 static BOOL is_identifier_first_char(WCHAR c) 129 { 130 return isalphaW(c) || c == '$' || c == '_' || c == '\\'; 131 } 132 133 static int check_keyword(parser_ctx_t *ctx, const WCHAR *word, const WCHAR **lval) 134 { 135 const WCHAR *p1 = ctx->ptr; 136 const WCHAR *p2 = word; 137 138 while(p1 < ctx->end && *p2) { 139 if(*p1 != *p2) 140 return *p1 - *p2; 141 p1++; 142 p2++; 143 } 144 145 if(*p2 || (p1 < ctx->end && is_identifier_char(*p1))) 146 return 1; 147 148 if(lval) 149 *lval = word; 150 ctx->ptr = p1; 151 return 0; 152 } 153 154 /* ECMA-262 3rd Edition 7.3 */ 155 static BOOL is_endline(WCHAR c) 156 { 157 return c == '\n' || c == '\r' || c == 0x2028 || c == 0x2029; 158 } 159 160 static int hex_to_int(WCHAR c) 161 { 162 if('0' <= c && c <= '9') 163 return c-'0'; 164 165 if('a' <= c && c <= 'f') 166 return c-'a'+10; 167 168 if('A' <= c && c <= 'F') 169 return c-'A'+10; 170 171 return -1; 172 } 173 174 static int check_keywords(parser_ctx_t *ctx, const WCHAR **lval) 175 { 176 int min = 0, max = ARRAY_SIZE(keywords)-1, r, i; 177 178 while(min <= max) { 179 i = (min+max)/2; 180 181 r = check_keyword(ctx, keywords[i].word, lval); 182 if(!r) { 183 if(ctx->script->version < keywords[i].min_version) { 184 TRACE("ignoring keyword %s in incompatible mode\n", 185 debugstr_w(keywords[i].word)); 186 ctx->ptr -= strlenW(keywords[i].word); 187 return 0; 188 } 189 ctx->implicit_nl_semicolon = keywords[i].no_nl; 190 return keywords[i].token; 191 } 192 193 if(r > 0) 194 min = i+1; 195 else 196 max = i-1; 197 } 198 199 return 0; 200 } 201 202 static BOOL skip_html_comment(parser_ctx_t *ctx) 203 { 204 const WCHAR html_commentW[] = {'<','!','-','-',0}; 205 206 if(!ctx->is_html || ctx->ptr+3 >= ctx->end || 207 memcmp(ctx->ptr, html_commentW, sizeof(WCHAR)*4)) 208 return FALSE; 209 210 ctx->nl = TRUE; 211 while(ctx->ptr < ctx->end && !is_endline(*ctx->ptr++)); 212 213 return TRUE; 214 } 215 216 static BOOL skip_comment(parser_ctx_t *ctx) 217 { 218 if(ctx->ptr+1 >= ctx->end) 219 return FALSE; 220 221 if(*ctx->ptr != '/') { 222 if(*ctx->ptr == '@' && ctx->ptr+2 < ctx->end && ctx->ptr[1] == '*' && ctx->ptr[2] == '/') { 223 ctx->ptr += 3; 224 return TRUE; 225 } 226 227 return FALSE; 228 } 229 230 switch(ctx->ptr[1]) { 231 case '*': 232 ctx->ptr += 2; 233 if(ctx->ptr+2 < ctx->end && *ctx->ptr == '@' && is_identifier_char(ctx->ptr[1])) 234 return FALSE; 235 while(ctx->ptr+1 < ctx->end && (ctx->ptr[0] != '*' || ctx->ptr[1] != '/')) 236 ctx->ptr++; 237 238 if(ctx->ptr[0] == '*' && ctx->ptr[1] == '/') { 239 ctx->ptr += 2; 240 }else { 241 WARN("unexpected end of file (missing end of comment)\n"); 242 ctx->ptr = ctx->end; 243 } 244 break; 245 case '/': 246 ctx->ptr += 2; 247 if(ctx->ptr+2 < ctx->end && *ctx->ptr == '@' && is_identifier_char(ctx->ptr[1])) 248 return FALSE; 249 while(ctx->ptr < ctx->end && !is_endline(*ctx->ptr)) 250 ctx->ptr++; 251 break; 252 default: 253 return FALSE; 254 } 255 256 return TRUE; 257 } 258 259 static BOOL skip_spaces(parser_ctx_t *ctx) 260 { 261 while(ctx->ptr < ctx->end && (isspaceW(*ctx->ptr) || *ctx->ptr == 0xFEFF /* UTF16 BOM */)) { 262 if(is_endline(*ctx->ptr++)) 263 ctx->nl = TRUE; 264 } 265 266 return ctx->ptr != ctx->end; 267 } 268 269 BOOL unescape(WCHAR *str) 270 { 271 WCHAR *pd, *p, c; 272 int i; 273 274 pd = p = str; 275 while(*p) { 276 if(*p != '\\') { 277 *pd++ = *p++; 278 continue; 279 } 280 281 p++; 282 283 switch(*p) { 284 case '\'': 285 case '\"': 286 case '\\': 287 c = *p; 288 break; 289 case 'b': 290 c = '\b'; 291 break; 292 case 't': 293 c = '\t'; 294 break; 295 case 'n': 296 c = '\n'; 297 break; 298 case 'f': 299 c = '\f'; 300 break; 301 case 'r': 302 c = '\r'; 303 break; 304 case 'x': 305 i = hex_to_int(*++p); 306 if(i == -1) 307 return FALSE; 308 c = i << 4; 309 310 i = hex_to_int(*++p); 311 if(i == -1) 312 return FALSE; 313 c += i; 314 break; 315 case 'u': 316 i = hex_to_int(*++p); 317 if(i == -1) 318 return FALSE; 319 c = i << 12; 320 321 i = hex_to_int(*++p); 322 if(i == -1) 323 return FALSE; 324 c += i << 8; 325 326 i = hex_to_int(*++p); 327 if(i == -1) 328 return FALSE; 329 c += i << 4; 330 331 i = hex_to_int(*++p); 332 if(i == -1) 333 return FALSE; 334 c += i; 335 break; 336 default: 337 if(isdigitW(*p)) { 338 c = *p++ - '0'; 339 if(isdigitW(*p)) { 340 c = c*8 + (*p++ - '0'); 341 if(isdigitW(*p)) 342 c = c*8 + (*p++ - '0'); 343 } 344 p--; 345 } 346 else 347 c = *p; 348 } 349 350 *pd++ = c; 351 p++; 352 } 353 354 *pd = 0; 355 return TRUE; 356 } 357 358 static int parse_identifier(parser_ctx_t *ctx, const WCHAR **ret) 359 { 360 const WCHAR *ptr = ctx->ptr++; 361 WCHAR *wstr; 362 int len; 363 364 while(ctx->ptr < ctx->end && is_identifier_char(*ctx->ptr)) 365 ctx->ptr++; 366 367 len = ctx->ptr-ptr; 368 369 *ret = wstr = parser_alloc(ctx, (len+1)*sizeof(WCHAR)); 370 memcpy(wstr, ptr, len*sizeof(WCHAR)); 371 wstr[len] = 0; 372 373 /* FIXME: unescape */ 374 return tIdentifier; 375 } 376 377 static int parse_string_literal(parser_ctx_t *ctx, const WCHAR **ret, WCHAR endch) 378 { 379 const WCHAR *ptr = ++ctx->ptr; 380 WCHAR *wstr; 381 int len; 382 383 while(ctx->ptr < ctx->end && *ctx->ptr != endch) { 384 if(*ctx->ptr++ == '\\') 385 ctx->ptr++; 386 } 387 388 if(ctx->ptr == ctx->end) 389 return lex_error(ctx, JS_E_UNTERMINATED_STRING); 390 391 len = ctx->ptr-ptr; 392 393 *ret = wstr = parser_alloc(ctx, (len+1)*sizeof(WCHAR)); 394 memcpy(wstr, ptr, len*sizeof(WCHAR)); 395 wstr[len] = 0; 396 397 ctx->ptr++; 398 399 if(!unescape(wstr)) { 400 WARN("unescape failed\n"); 401 return lex_error(ctx, E_FAIL); 402 } 403 404 return tStringLiteral; 405 } 406 407 static literal_t *new_double_literal(parser_ctx_t *ctx, DOUBLE d) 408 { 409 literal_t *ret = parser_alloc(ctx, sizeof(literal_t)); 410 411 ret->type = LT_DOUBLE; 412 ret->u.dval = d; 413 return ret; 414 } 415 416 literal_t *new_boolean_literal(parser_ctx_t *ctx, BOOL bval) 417 { 418 literal_t *ret = parser_alloc(ctx, sizeof(literal_t)); 419 420 ret->type = LT_BOOL; 421 ret->u.bval = bval; 422 423 return ret; 424 } 425 426 HRESULT parse_decimal(const WCHAR **iter, const WCHAR *end, double *ret) 427 { 428 const WCHAR *ptr = *iter; 429 LONGLONG d = 0, hlp; 430 int exp = 0; 431 432 while(ptr < end && isdigitW(*ptr)) { 433 hlp = d*10 + *(ptr++) - '0'; 434 if(d>MAXLONGLONG/10 || hlp<0) { 435 exp++; 436 break; 437 } 438 else 439 d = hlp; 440 } 441 while(ptr < end && isdigitW(*ptr)) { 442 exp++; 443 ptr++; 444 } 445 446 if(*ptr == '.') { 447 ptr++; 448 449 while(ptr < end && isdigitW(*ptr)) { 450 hlp = d*10 + *(ptr++) - '0'; 451 if(d>MAXLONGLONG/10 || hlp<0) 452 break; 453 454 d = hlp; 455 exp--; 456 } 457 while(ptr < end && isdigitW(*ptr)) 458 ptr++; 459 } 460 461 if(ptr < end && (*ptr == 'e' || *ptr == 'E')) { 462 int sign = 1, e = 0; 463 464 if(++ptr < end) { 465 if(*ptr == '+') { 466 ptr++; 467 }else if(*ptr == '-') { 468 sign = -1; 469 ptr++; 470 }else if(!isdigitW(*ptr)) { 471 WARN("Expected exponent part\n"); 472 return E_FAIL; 473 } 474 } 475 476 if(ptr == end) { 477 WARN("unexpected end of file\n"); 478 return E_FAIL; 479 } 480 481 while(ptr < end && isdigitW(*ptr)) { 482 if(e > INT_MAX/10 || (e = e*10 + *ptr++ - '0')<0) 483 e = INT_MAX; 484 } 485 e *= sign; 486 487 if(exp<0 && e<0 && e+exp>0) exp = INT_MIN; 488 else if(exp>0 && e>0 && e+exp<0) exp = INT_MAX; 489 else exp += e; 490 } 491 492 if(is_identifier_char(*ptr)) { 493 WARN("wrong char after zero\n"); 494 return JS_E_MISSING_SEMICOLON; 495 } 496 497 *ret = exp>=0 ? d*pow(10, exp) : d/pow(10, -exp); 498 *iter = ptr; 499 return S_OK; 500 } 501 502 static BOOL parse_numeric_literal(parser_ctx_t *ctx, double *ret) 503 { 504 HRESULT hres; 505 506 if(*ctx->ptr == '0') { 507 ctx->ptr++; 508 509 if(*ctx->ptr == 'x' || *ctx->ptr == 'X') { 510 double r = 0; 511 int d; 512 if(++ctx->ptr == ctx->end) { 513 ERR("unexpected end of file\n"); 514 return FALSE; 515 } 516 517 while(ctx->ptr < ctx->end && (d = hex_to_int(*ctx->ptr)) != -1) { 518 r = r*16 + d; 519 ctx->ptr++; 520 } 521 522 if(ctx->ptr < ctx->end && is_identifier_char(*ctx->ptr)) { 523 WARN("unexpected identifier char\n"); 524 lex_error(ctx, JS_E_MISSING_SEMICOLON); 525 return FALSE; 526 } 527 528 *ret = r; 529 return TRUE; 530 } 531 532 if(isdigitW(*ctx->ptr)) { 533 unsigned base = 8; 534 const WCHAR *ptr; 535 double val = 0; 536 537 for(ptr = ctx->ptr; ptr < ctx->end && isdigitW(*ptr); ptr++) { 538 if(*ptr > '7') { 539 base = 10; 540 break; 541 } 542 } 543 544 do { 545 val = val*base + *ctx->ptr-'0'; 546 }while(++ctx->ptr < ctx->end && isdigitW(*ctx->ptr)); 547 548 /* FIXME: Do we need it here? */ 549 if(ctx->ptr < ctx->end && (is_identifier_char(*ctx->ptr) || *ctx->ptr == '.')) { 550 WARN("wrong char after octal literal: '%c'\n", *ctx->ptr); 551 lex_error(ctx, JS_E_MISSING_SEMICOLON); 552 return FALSE; 553 } 554 555 *ret = val; 556 return TRUE; 557 } 558 559 if(is_identifier_char(*ctx->ptr)) { 560 WARN("wrong char after zero\n"); 561 lex_error(ctx, JS_E_MISSING_SEMICOLON); 562 return FALSE; 563 } 564 } 565 566 hres = parse_decimal(&ctx->ptr, ctx->end, ret); 567 if(FAILED(hres)) { 568 lex_error(ctx, hres); 569 return FALSE; 570 } 571 572 return TRUE; 573 } 574 575 static int next_token(parser_ctx_t *ctx, void *lval) 576 { 577 do { 578 if(!skip_spaces(ctx)) 579 return tEOF; 580 }while(skip_comment(ctx) || skip_html_comment(ctx)); 581 582 if(ctx->implicit_nl_semicolon) { 583 if(ctx->nl) 584 return ';'; 585 ctx->implicit_nl_semicolon = FALSE; 586 } 587 588 if(isalphaW(*ctx->ptr)) { 589 int ret = check_keywords(ctx, lval); 590 if(ret) 591 return ret; 592 593 return parse_identifier(ctx, lval); 594 } 595 596 if(isdigitW(*ctx->ptr)) { 597 double n; 598 599 if(!parse_numeric_literal(ctx, &n)) 600 return -1; 601 602 *(literal_t**)lval = new_double_literal(ctx, n); 603 return tNumericLiteral; 604 } 605 606 switch(*ctx->ptr) { 607 case '{': 608 case '(': 609 case ')': 610 case '[': 611 case ']': 612 case ';': 613 case ',': 614 case '~': 615 case '?': 616 return *ctx->ptr++; 617 618 case '}': 619 *(const WCHAR**)lval = ctx->ptr++; 620 return '}'; 621 622 case '.': 623 if(ctx->ptr+1 < ctx->end && isdigitW(ctx->ptr[1])) { 624 double n; 625 HRESULT hres; 626 hres = parse_decimal(&ctx->ptr, ctx->end, &n); 627 if(FAILED(hres)) { 628 lex_error(ctx, hres); 629 return -1; 630 } 631 *(literal_t**)lval = new_double_literal(ctx, n); 632 return tNumericLiteral; 633 } 634 ctx->ptr++; 635 return '.'; 636 637 case '<': 638 if(++ctx->ptr == ctx->end) { 639 *(int*)lval = EXPR_LESS; 640 return tRelOper; 641 } 642 643 switch(*ctx->ptr) { 644 case '=': /* <= */ 645 ctx->ptr++; 646 *(int*)lval = EXPR_LESSEQ; 647 return tRelOper; 648 case '<': /* << */ 649 if(++ctx->ptr < ctx->end && *ctx->ptr == '=') { /* <<= */ 650 ctx->ptr++; 651 *(int*)lval = EXPR_ASSIGNLSHIFT; 652 return tAssignOper; 653 } 654 *(int*)lval = EXPR_LSHIFT; 655 return tShiftOper; 656 default: /* < */ 657 *(int*)lval = EXPR_LESS; 658 return tRelOper; 659 } 660 661 case '>': 662 if(++ctx->ptr == ctx->end) { /* > */ 663 *(int*)lval = EXPR_GREATER; 664 return tRelOper; 665 } 666 667 switch(*ctx->ptr) { 668 case '=': /* >= */ 669 ctx->ptr++; 670 *(int*)lval = EXPR_GREATEREQ; 671 return tRelOper; 672 case '>': /* >> */ 673 if(++ctx->ptr < ctx->end) { 674 if(*ctx->ptr == '=') { /* >>= */ 675 ctx->ptr++; 676 *(int*)lval = EXPR_ASSIGNRSHIFT; 677 return tAssignOper; 678 } 679 if(*ctx->ptr == '>') { /* >>> */ 680 if(++ctx->ptr < ctx->end && *ctx->ptr == '=') { /* >>>= */ 681 ctx->ptr++; 682 *(int*)lval = EXPR_ASSIGNRRSHIFT; 683 return tAssignOper; 684 } 685 *(int*)lval = EXPR_RRSHIFT; 686 return tRelOper; 687 } 688 } 689 *(int*)lval = EXPR_RSHIFT; 690 return tShiftOper; 691 default: 692 *(int*)lval = EXPR_GREATER; 693 return tRelOper; 694 } 695 696 case '+': 697 ctx->ptr++; 698 if(ctx->ptr < ctx->end) { 699 switch(*ctx->ptr) { 700 case '+': /* ++ */ 701 ctx->ptr++; 702 return tINC; 703 case '=': /* += */ 704 ctx->ptr++; 705 *(int*)lval = EXPR_ASSIGNADD; 706 return tAssignOper; 707 } 708 } 709 return '+'; 710 711 case '-': 712 ctx->ptr++; 713 if(ctx->ptr < ctx->end) { 714 switch(*ctx->ptr) { 715 case '-': /* -- or --> */ 716 ctx->ptr++; 717 if(ctx->is_html && ctx->nl && ctx->ptr < ctx->end && *ctx->ptr == '>') { 718 ctx->ptr++; 719 return tHTMLCOMMENT; 720 } 721 return tDEC; 722 case '=': /* -= */ 723 ctx->ptr++; 724 *(int*)lval = EXPR_ASSIGNSUB; 725 return tAssignOper; 726 } 727 } 728 return '-'; 729 730 case '*': 731 if(++ctx->ptr < ctx->end && *ctx->ptr == '=') { /* *= */ 732 ctx->ptr++; 733 *(int*)lval = EXPR_ASSIGNMUL; 734 return tAssignOper; 735 } 736 return '*'; 737 738 case '%': 739 if(++ctx->ptr < ctx->end && *ctx->ptr == '=') { /* %= */ 740 ctx->ptr++; 741 *(int*)lval = EXPR_ASSIGNMOD; 742 return tAssignOper; 743 } 744 return '%'; 745 746 case '&': 747 if(++ctx->ptr < ctx->end) { 748 switch(*ctx->ptr) { 749 case '=': /* &= */ 750 ctx->ptr++; 751 *(int*)lval = EXPR_ASSIGNAND; 752 return tAssignOper; 753 case '&': /* && */ 754 ctx->ptr++; 755 return tANDAND; 756 } 757 } 758 return '&'; 759 760 case '|': 761 if(++ctx->ptr < ctx->end) { 762 switch(*ctx->ptr) { 763 case '=': /* |= */ 764 ctx->ptr++; 765 *(int*)lval = EXPR_ASSIGNOR; 766 return tAssignOper; 767 case '|': /* || */ 768 ctx->ptr++; 769 return tOROR; 770 } 771 } 772 return '|'; 773 774 case '^': 775 if(++ctx->ptr < ctx->end && *ctx->ptr == '=') { /* ^= */ 776 ctx->ptr++; 777 *(int*)lval = EXPR_ASSIGNXOR; 778 return tAssignOper; 779 } 780 return '^'; 781 782 case '!': 783 if(++ctx->ptr < ctx->end && *ctx->ptr == '=') { /* != */ 784 if(++ctx->ptr < ctx->end && *ctx->ptr == '=') { /* !== */ 785 ctx->ptr++; 786 *(int*)lval = EXPR_NOTEQEQ; 787 return tEqOper; 788 } 789 *(int*)lval = EXPR_NOTEQ; 790 return tEqOper; 791 } 792 return '!'; 793 794 case '=': 795 if(++ctx->ptr < ctx->end && *ctx->ptr == '=') { /* == */ 796 if(++ctx->ptr < ctx->end && *ctx->ptr == '=') { /* === */ 797 ctx->ptr++; 798 *(int*)lval = EXPR_EQEQ; 799 return tEqOper; 800 } 801 *(int*)lval = EXPR_EQ; 802 return tEqOper; 803 } 804 return '='; 805 806 case '/': 807 if(++ctx->ptr < ctx->end) { 808 if(*ctx->ptr == '=') { /* /= */ 809 ctx->ptr++; 810 *(int*)lval = EXPR_ASSIGNDIV; 811 return kDIVEQ; 812 } 813 } 814 return '/'; 815 816 case ':': 817 if(++ctx->ptr < ctx->end && *ctx->ptr == ':') { 818 ctx->ptr++; 819 return kDCOL; 820 } 821 return ':'; 822 823 case '\"': 824 case '\'': 825 return parse_string_literal(ctx, lval, *ctx->ptr); 826 827 case '_': 828 case '$': 829 return parse_identifier(ctx, lval); 830 831 case '@': 832 return '@'; 833 } 834 835 WARN("unexpected char '%c' %d\n", *ctx->ptr, *ctx->ptr); 836 return 0; 837 } 838 839 struct _cc_var_t { 840 ccval_t val; 841 struct _cc_var_t *next; 842 unsigned name_len; 843 WCHAR name[0]; 844 }; 845 846 void release_cc(cc_ctx_t *cc) 847 { 848 cc_var_t *iter, *next; 849 850 for(iter = cc->vars; iter; iter = next) { 851 next = iter->next; 852 heap_free(iter); 853 } 854 855 heap_free(cc); 856 } 857 858 static BOOL new_cc_var(cc_ctx_t *cc, const WCHAR *name, int len, ccval_t v) 859 { 860 cc_var_t *new_v; 861 862 if(len == -1) 863 len = strlenW(name); 864 865 new_v = heap_alloc(sizeof(cc_var_t) + (len+1)*sizeof(WCHAR)); 866 if(!new_v) 867 return FALSE; 868 869 new_v->val = v; 870 memcpy(new_v->name, name, (len+1)*sizeof(WCHAR)); 871 new_v->name_len = len; 872 new_v->next = cc->vars; 873 cc->vars = new_v; 874 return TRUE; 875 } 876 877 static cc_var_t *find_cc_var(cc_ctx_t *cc, const WCHAR *name, unsigned name_len) 878 { 879 cc_var_t *iter; 880 881 for(iter = cc->vars; iter; iter = iter->next) { 882 if(iter->name_len == name_len && !memcmp(iter->name, name, name_len*sizeof(WCHAR))) 883 return iter; 884 } 885 886 return NULL; 887 } 888 889 static BOOL init_cc(parser_ctx_t *ctx) 890 { 891 cc_ctx_t *cc; 892 893 static const WCHAR _win32W[] = {'_','w','i','n','3','2',0}; 894 static const WCHAR _win64W[] = {'_','w','i','n','6','4',0}; 895 static const WCHAR _x86W[] = {'_','x','8','6',0}; 896 static const WCHAR _amd64W[] = {'_','a','m','d','6','4',0}; 897 static const WCHAR _jscriptW[] = {'_','j','s','c','r','i','p','t',0}; 898 static const WCHAR _jscript_buildW[] = {'_','j','s','c','r','i','p','t','_','b','u','i','l','d',0}; 899 static const WCHAR _jscript_versionW[] = {'_','j','s','c','r','i','p','t','_','v','e','r','s','i','o','n',0}; 900 901 if(ctx->script->cc) 902 return TRUE; 903 904 cc = heap_alloc(sizeof(cc_ctx_t)); 905 if(!cc) { 906 lex_error(ctx, E_OUTOFMEMORY); 907 return FALSE; 908 } 909 910 cc->vars = NULL; 911 912 if(!new_cc_var(cc, _jscriptW, -1, ccval_bool(TRUE)) 913 || !new_cc_var(cc, sizeof(void*) == 8 ? _win64W : _win32W, -1, ccval_bool(TRUE)) 914 || !new_cc_var(cc, sizeof(void*) == 8 ? _amd64W : _x86W, -1, ccval_bool(TRUE)) 915 || !new_cc_var(cc, _jscript_versionW, -1, ccval_num(JSCRIPT_MAJOR_VERSION + (DOUBLE)JSCRIPT_MINOR_VERSION/10.0)) 916 || !new_cc_var(cc, _jscript_buildW, -1, ccval_num(JSCRIPT_BUILD_VERSION))) { 917 release_cc(cc); 918 lex_error(ctx, E_OUTOFMEMORY); 919 return FALSE; 920 } 921 922 ctx->script->cc = cc; 923 return TRUE; 924 } 925 926 static BOOL parse_cc_identifier(parser_ctx_t *ctx, const WCHAR **ret, unsigned *ret_len) 927 { 928 if(*ctx->ptr != '@') { 929 lex_error(ctx, JS_E_EXPECTED_AT); 930 return FALSE; 931 } 932 933 if(!is_identifier_first_char(*++ctx->ptr)) { 934 lex_error(ctx, JS_E_EXPECTED_IDENTIFIER); 935 return FALSE; 936 } 937 938 *ret = ctx->ptr; 939 while(++ctx->ptr < ctx->end && is_identifier_char(*ctx->ptr)); 940 *ret_len = ctx->ptr - *ret; 941 return TRUE; 942 } 943 944 int try_parse_ccval(parser_ctx_t *ctx, ccval_t *r) 945 { 946 if(!skip_spaces(ctx)) 947 return -1; 948 949 if(isdigitW(*ctx->ptr)) { 950 double n; 951 952 if(!parse_numeric_literal(ctx, &n)) 953 return -1; 954 955 *r = ccval_num(n); 956 return 1; 957 } 958 959 if(*ctx->ptr == '@') { 960 const WCHAR *ident; 961 unsigned ident_len; 962 cc_var_t *cc_var; 963 964 if(!parse_cc_identifier(ctx, &ident, &ident_len)) 965 return -1; 966 967 cc_var = find_cc_var(ctx->script->cc, ident, ident_len); 968 *r = cc_var ? cc_var->val : ccval_num(NAN); 969 return 1; 970 } 971 972 if(!check_keyword(ctx, trueW, NULL)) { 973 *r = ccval_bool(TRUE); 974 return 1; 975 } 976 977 if(!check_keyword(ctx, falseW, NULL)) { 978 *r = ccval_bool(FALSE); 979 return 1; 980 } 981 982 return 0; 983 } 984 985 static int skip_code(parser_ctx_t *ctx, BOOL exec_else) 986 { 987 int if_depth = 1; 988 const WCHAR *ptr; 989 990 while(1) { 991 ptr = strchrW(ctx->ptr, '@'); 992 if(!ptr) { 993 WARN("No @end\n"); 994 return lex_error(ctx, JS_E_EXPECTED_CCEND); 995 } 996 ctx->ptr = ptr+1; 997 998 if(!check_keyword(ctx, endW, NULL)) { 999 if(--if_depth) 1000 continue; 1001 return 0; 1002 } 1003 1004 if(exec_else && !check_keyword(ctx, elifW, NULL)) { 1005 if(if_depth > 1) 1006 continue; 1007 1008 if(!skip_spaces(ctx) || *ctx->ptr != '(') 1009 return lex_error(ctx, JS_E_MISSING_LBRACKET); 1010 1011 if(!parse_cc_expr(ctx)) 1012 return -1; 1013 1014 if(!get_ccbool(ctx->ccval)) 1015 continue; /* skip block of code */ 1016 1017 /* continue parsing */ 1018 ctx->cc_if_depth++; 1019 return 0; 1020 } 1021 1022 if(exec_else && !check_keyword(ctx, elseW, NULL)) { 1023 if(if_depth > 1) 1024 continue; 1025 1026 /* parse else block */ 1027 ctx->cc_if_depth++; 1028 return 0; 1029 } 1030 1031 if(!check_keyword(ctx, ifW, NULL)) { 1032 if_depth++; 1033 continue; 1034 } 1035 1036 ctx->ptr++; 1037 } 1038 } 1039 1040 static int cc_token(parser_ctx_t *ctx, void *lval) 1041 { 1042 unsigned id_len = 0; 1043 cc_var_t *var; 1044 1045 static const WCHAR cc_onW[] = {'c','c','_','o','n',0}; 1046 static const WCHAR setW[] = {'s','e','t',0}; 1047 1048 ctx->ptr++; 1049 1050 if(!check_keyword(ctx, cc_onW, NULL)) 1051 return init_cc(ctx) ? 0 : -1; 1052 1053 if(!check_keyword(ctx, setW, NULL)) { 1054 const WCHAR *ident; 1055 unsigned ident_len; 1056 cc_var_t *var; 1057 1058 if(!init_cc(ctx)) 1059 return -1; 1060 1061 if(!skip_spaces(ctx)) 1062 return lex_error(ctx, JS_E_EXPECTED_AT); 1063 1064 if(!parse_cc_identifier(ctx, &ident, &ident_len)) 1065 return -1; 1066 1067 if(!skip_spaces(ctx) || *ctx->ptr != '=') 1068 return lex_error(ctx, JS_E_EXPECTED_ASSIGN); 1069 ctx->ptr++; 1070 1071 if(!parse_cc_expr(ctx)) { 1072 WARN("parsing CC expression failed\n"); 1073 return -1; 1074 } 1075 1076 var = find_cc_var(ctx->script->cc, ident, ident_len); 1077 if(var) { 1078 var->val = ctx->ccval; 1079 }else { 1080 if(!new_cc_var(ctx->script->cc, ident, ident_len, ctx->ccval)) 1081 return lex_error(ctx, E_OUTOFMEMORY); 1082 } 1083 1084 return 0; 1085 } 1086 1087 if(!check_keyword(ctx, ifW, NULL)) { 1088 if(!init_cc(ctx)) 1089 return -1; 1090 1091 if(!skip_spaces(ctx) || *ctx->ptr != '(') 1092 return lex_error(ctx, JS_E_MISSING_LBRACKET); 1093 1094 if(!parse_cc_expr(ctx)) 1095 return -1; 1096 1097 if(get_ccbool(ctx->ccval)) { 1098 /* continue parsing block inside if */ 1099 ctx->cc_if_depth++; 1100 return 0; 1101 } 1102 1103 return skip_code(ctx, TRUE); 1104 } 1105 1106 if(!check_keyword(ctx, elifW, NULL) || !check_keyword(ctx, elseW, NULL)) { 1107 if(!ctx->cc_if_depth) 1108 return lex_error(ctx, JS_E_SYNTAX); 1109 1110 return skip_code(ctx, FALSE); 1111 } 1112 1113 if(!check_keyword(ctx, endW, NULL)) { 1114 if(!ctx->cc_if_depth) 1115 return lex_error(ctx, JS_E_SYNTAX); 1116 1117 ctx->cc_if_depth--; 1118 return 0; 1119 } 1120 1121 if(!ctx->script->cc) 1122 return lex_error(ctx, JS_E_DISABLED_CC); 1123 1124 while(ctx->ptr+id_len < ctx->end && is_identifier_char(ctx->ptr[id_len])) 1125 id_len++; 1126 if(!id_len) 1127 return '@'; 1128 1129 TRACE("var %s\n", debugstr_wn(ctx->ptr, id_len)); 1130 1131 var = find_cc_var(ctx->script->cc, ctx->ptr, id_len); 1132 ctx->ptr += id_len; 1133 if(!var || var->val.is_num) { 1134 *(literal_t**)lval = new_double_literal(ctx, var ? var->val.u.n : NAN); 1135 return tNumericLiteral; 1136 } 1137 1138 *(literal_t**)lval = new_boolean_literal(ctx, var->val.u.b); 1139 return tBooleanLiteral; 1140 } 1141 1142 int parser_lex(void *lval, parser_ctx_t *ctx) 1143 { 1144 int ret; 1145 1146 ctx->nl = ctx->ptr == ctx->begin; 1147 1148 do { 1149 ret = next_token(ctx, lval); 1150 } while(ret == '@' && !(ret = cc_token(ctx, lval))); 1151 1152 return ret; 1153 } 1154 1155 literal_t *parse_regexp(parser_ctx_t *ctx) 1156 { 1157 const WCHAR *re, *flags_ptr; 1158 BOOL in_class = FALSE; 1159 DWORD re_len, flags; 1160 literal_t *ret; 1161 HRESULT hres; 1162 1163 TRACE("\n"); 1164 1165 while(*--ctx->ptr != '/'); 1166 1167 /* Simple regexp pre-parser; '/' if used in char class does not terminate regexp literal */ 1168 re = ++ctx->ptr; 1169 while(ctx->ptr < ctx->end) { 1170 if(*ctx->ptr == '\\') { 1171 if(++ctx->ptr == ctx->end) 1172 break; 1173 }else if(in_class) { 1174 if(*ctx->ptr == '\n') 1175 break; 1176 if(*ctx->ptr == ']') 1177 in_class = FALSE; 1178 }else { 1179 if(*ctx->ptr == '/') 1180 break; 1181 1182 if(*ctx->ptr == '[') 1183 in_class = TRUE; 1184 } 1185 ctx->ptr++; 1186 } 1187 1188 if(ctx->ptr == ctx->end || *ctx->ptr != '/') { 1189 WARN("pre-parsing failed\n"); 1190 return NULL; 1191 } 1192 1193 re_len = ctx->ptr-re; 1194 1195 flags_ptr = ++ctx->ptr; 1196 while(ctx->ptr < ctx->end && isalnumW(*ctx->ptr)) 1197 ctx->ptr++; 1198 1199 hres = parse_regexp_flags(flags_ptr, ctx->ptr-flags_ptr, &flags); 1200 if(FAILED(hres)) 1201 return NULL; 1202 1203 ret = parser_alloc(ctx, sizeof(literal_t)); 1204 ret->type = LT_REGEXP; 1205 ret->u.regexp.str = re; 1206 ret->u.regexp.str_len = re_len; 1207 ret->u.regexp.flags = flags; 1208 return ret; 1209 } 1210