001 /* 002 * Copyright 2008 Google Inc. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 005 * use this file except in compliance with the License. You may obtain a copy of 006 * the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 013 * License for the specific language governing permissions and limitations under 014 * the License. 015 */ 016 package com.google.gwt.dev.js; 017 018 import com.google.dart.compiler.backend.js.ast.*; 019 import com.google.dart.compiler.backend.js.ast.JsLiteral.JsBooleanLiteral; 020 import com.google.dart.compiler.common.SourceInfo; 021 import com.google.gwt.dev.js.rhino.*; 022 import com.intellij.util.SmartList; 023 import org.jetbrains.annotations.NotNull; 024 import org.jetbrains.annotations.Nullable; 025 026 import java.io.IOException; 027 import java.io.Reader; 028 import java.util.ArrayList; 029 import java.util.Collections; 030 import java.util.List; 031 import java.util.Stack; 032 033 import static com.google.dart.compiler.util.AstUtil.toFunctionScope; 034 035 /** 036 * Parses JavaScript source. 037 */ 038 public class JsParser { 039 040 private JsProgram program; 041 042 public static List<JsStatement> parse( 043 SourceInfo rootSourceInfo, 044 JsScope scope, Reader r, 045 ErrorReporter errorReporter, 046 boolean insideFunction 047 ) throws IOException, JsParserException { 048 return new JsParser(scope.getProgram()).parseImpl(rootSourceInfo, scope, r, errorReporter, insideFunction); 049 } 050 051 private final Stack<JsScope> scopeStack = new Stack<JsScope>(); 052 053 /** 054 * since source maps are not mapped to kotlin source maps 055 */ 056 private static final String SOURCE_NAME_STUB = "jsCode"; 057 058 private JsParser(JsProgram program) { 059 this.program = program; 060 } 061 062 List<JsStatement> parseImpl( 063 final SourceInfo rootSourceInfo, JsScope scope, 064 Reader r, 065 ErrorReporter errorReporter, 066 boolean insideFunction 067 ) throws JsParserException, IOException { 068 // Create a custom error handler so that we can throw our own exceptions. 069 Context.enter().setErrorReporter(errorReporter); 070 try { 071 // Parse using the Rhino parser. 072 // 073 TokenStream ts = new TokenStream(r, SOURCE_NAME_STUB, rootSourceInfo.getLine()); 074 Parser parser = new Parser(new IRFactory(ts), insideFunction); 075 Node topNode = (Node) parser.parse(ts); 076 077 // Map the Rhino AST to ours. 078 pushScope(scope); 079 List<JsStatement> stmts = mapStatements(topNode); 080 popScope(); 081 return stmts; 082 } 083 finally { 084 Context.exit(); 085 } 086 } 087 088 private static JsParserException createParserException(String msg, Node offender) { 089 return new JsParserException(msg, offender.getLineno(), null, 0, SOURCE_NAME_STUB); 090 } 091 092 private JsScope getScope() { 093 return scopeStack.peek(); 094 } 095 096 private JsNode map(Node node) throws JsParserException { 097 switch (node.getType()) { 098 case TokenStream.SCRIPT: { 099 JsBlock block = new JsBlock(); 100 mapStatements(block.getStatements(), node); 101 return block; 102 } 103 104 case TokenStream.DEBUGGER: 105 return mapDebuggerStatement(node); 106 107 case TokenStream.VOID: 108 // VOID = nothing was parsed for this node 109 return null; 110 111 case TokenStream.EXPRSTMT: 112 return mapExpressionStatement(node); 113 114 case TokenStream.REGEXP: 115 return mapRegExp(node); 116 117 case TokenStream.ADD: 118 return mapBinaryOperation(JsBinaryOperator.ADD, node); 119 120 case TokenStream.SUB: 121 return mapBinaryOperation(JsBinaryOperator.SUB, node); 122 123 case TokenStream.MUL: 124 return mapBinaryOperation(JsBinaryOperator.MUL, node); 125 126 case TokenStream.DIV: 127 return mapBinaryOperation(JsBinaryOperator.DIV, node); 128 129 case TokenStream.MOD: 130 return mapBinaryOperation(JsBinaryOperator.MOD, node); 131 132 case TokenStream.AND: 133 return mapBinaryOperation(JsBinaryOperator.AND, node); 134 135 case TokenStream.OR: 136 return mapBinaryOperation(JsBinaryOperator.OR, node); 137 138 case TokenStream.BITAND: 139 return mapBinaryOperation(JsBinaryOperator.BIT_AND, node); 140 141 case TokenStream.BITOR: 142 return mapBinaryOperation(JsBinaryOperator.BIT_OR, node); 143 144 case TokenStream.BITXOR: 145 return mapBinaryOperation(JsBinaryOperator.BIT_XOR, node); 146 147 case TokenStream.ASSIGN: 148 return mapAssignmentVariant(node); 149 150 case TokenStream.RELOP: 151 return mapRelationalVariant(node); 152 153 case TokenStream.EQOP: 154 return mapEqualityVariant(node); 155 156 case TokenStream.SHOP: 157 return mapShiftVariant(node); 158 159 case TokenStream.UNARYOP: 160 return mapUnaryVariant(node); 161 162 case TokenStream.INC: 163 return mapIncDecFixity(JsUnaryOperator.INC, node); 164 165 case TokenStream.DEC: 166 return mapIncDecFixity(JsUnaryOperator.DEC, node); 167 168 case TokenStream.HOOK: 169 return mapConditional(node); 170 171 case TokenStream.STRING: 172 return program.getStringLiteral(node.getString()); 173 174 case TokenStream.NUMBER_INT: 175 return mapIntNumber(node); 176 177 case TokenStream.NUMBER: 178 return mapDoubleNumber(node); 179 180 case TokenStream.CALL: 181 return mapCall(node); 182 183 case TokenStream.GETPROP: 184 return mapGetProp(node); 185 186 case TokenStream.SETPROP: 187 return mapSetProp(node); 188 189 case TokenStream.DELPROP: 190 return mapDeleteProp(node); 191 192 case TokenStream.IF: 193 return mapIfStatement(node); 194 195 case TokenStream.WHILE: 196 return mapDoOrWhileStatement(true, node); 197 198 case TokenStream.DO: 199 return mapDoOrWhileStatement(false, node); 200 201 case TokenStream.FOR: 202 return mapForStatement(node); 203 204 case TokenStream.WITH: 205 return mapWithStatement(node); 206 207 case TokenStream.GETELEM: 208 return mapGetElem(node); 209 210 case TokenStream.SETELEM: 211 return mapSetElem(node); 212 213 case TokenStream.FUNCTION: 214 return mapFunction(node); 215 216 case TokenStream.BLOCK: 217 return mapBlock(node); 218 219 case TokenStream.SETNAME: 220 return mapBinaryOperation(JsBinaryOperator.ASG, node); 221 222 case TokenStream.NAME: 223 case TokenStream.BINDNAME: 224 return mapName(node); 225 226 case TokenStream.RETURN: 227 return mapReturn(node); 228 229 case TokenStream.BREAK: 230 return mapBreak(node); 231 232 case TokenStream.CONTINUE: 233 return mapContinue(node); 234 235 case TokenStream.OBJLIT: 236 return mapObjectLit(node); 237 238 case TokenStream.ARRAYLIT: 239 return mapArrayLit(node); 240 241 case TokenStream.VAR: 242 return mapVar(node); 243 244 case TokenStream.PRIMARY: 245 return mapPrimary(node); 246 247 case TokenStream.COMMA: 248 return mapBinaryOperation(JsBinaryOperator.COMMA, node); 249 250 case TokenStream.NEW: 251 return mapNew(node); 252 253 case TokenStream.THROW: 254 return mapThrowStatement(node); 255 256 case TokenStream.TRY: 257 return mapTryStatement(node); 258 259 case TokenStream.SWITCH: 260 return mapSwitchStatement(node); 261 262 case TokenStream.LABEL: 263 return mapLabel(node); 264 265 default: 266 int tokenType = node.getType(); 267 throw createParserException("Unexpected top-level token type: " 268 + tokenType, node); 269 } 270 } 271 272 private JsArrayLiteral mapArrayLit(Node node) throws JsParserException { 273 JsArrayLiteral toLit = new JsArrayLiteral(); 274 Node from = node.getFirstChild(); 275 while (from != null) { 276 toLit.getExpressions().add(mapExpression(from)); 277 from = from.getNext(); 278 } 279 return toLit; 280 } 281 282 /** 283 * Produces a {@link JsNameRef}. 284 */ 285 private JsNameRef mapAsPropertyNameRef(Node nameRefNode) 286 throws JsParserException { 287 JsNode unknown = map(nameRefNode); 288 // This is weird, but for "a.b", the rhino AST calls "b" a string literal. 289 // However, since we know it's for a PROPGET, we can unstringliteralize it. 290 // 291 if (unknown instanceof JsStringLiteral) { 292 JsStringLiteral lit = (JsStringLiteral) unknown; 293 String litName = lit.getValue(); 294 return new JsNameRef(litName); 295 } 296 else { 297 throw createParserException("Expecting a name reference", nameRefNode); 298 } 299 } 300 301 private JsExpression mapAssignmentVariant(Node asgNode) 302 throws JsParserException { 303 switch (asgNode.getIntDatum()) { 304 case TokenStream.NOP: 305 return mapBinaryOperation(JsBinaryOperator.ASG, asgNode); 306 307 case TokenStream.ADD: 308 return mapBinaryOperation(JsBinaryOperator.ASG_ADD, asgNode); 309 310 case TokenStream.SUB: 311 return mapBinaryOperation(JsBinaryOperator.ASG_SUB, asgNode); 312 313 case TokenStream.MUL: 314 return mapBinaryOperation(JsBinaryOperator.ASG_MUL, asgNode); 315 316 case TokenStream.DIV: 317 return mapBinaryOperation(JsBinaryOperator.ASG_DIV, asgNode); 318 319 case TokenStream.MOD: 320 return mapBinaryOperation(JsBinaryOperator.ASG_MOD, asgNode); 321 322 case TokenStream.BITAND: 323 return mapBinaryOperation(JsBinaryOperator.ASG_BIT_AND, asgNode); 324 325 case TokenStream.BITOR: 326 return mapBinaryOperation(JsBinaryOperator.ASG_BIT_OR, asgNode); 327 328 case TokenStream.BITXOR: 329 return mapBinaryOperation(JsBinaryOperator.ASG_BIT_XOR, asgNode); 330 331 case TokenStream.LSH: 332 return mapBinaryOperation(JsBinaryOperator.ASG_SHL, asgNode); 333 334 case TokenStream.RSH: 335 return mapBinaryOperation(JsBinaryOperator.ASG_SHR, asgNode); 336 337 case TokenStream.URSH: 338 return mapBinaryOperation(JsBinaryOperator.ASG_SHRU, asgNode); 339 340 default: 341 throw createParserException("Unknown assignment operator variant: " 342 + asgNode.getIntDatum(), asgNode); 343 } 344 } 345 346 private JsExpression mapBinaryOperation(JsBinaryOperator op, Node node) 347 throws JsParserException { 348 Node from1 = node.getFirstChild(); 349 Node from2 = from1.getNext(); 350 351 JsExpression to1 = mapExpression(from1); 352 JsExpression to2 = mapExpression(from2); 353 354 return new JsBinaryOperation(op, to1, to2); 355 } 356 357 private JsBlock mapBlock(Node nodeStmts) throws JsParserException { 358 JsBlock block = new JsBlock(); 359 mapStatements(block.getStatements(), nodeStmts); 360 return block; 361 } 362 363 private JsBreak mapBreak(Node breakNode) { 364 return new JsBreak(getTargetLabel(breakNode)); 365 } 366 367 @Nullable 368 private JsNameRef getTargetLabel(@NotNull Node statementWithLabel) { 369 int type = statementWithLabel.getType(); 370 if (type != TokenStream.BREAK && type != TokenStream.CONTINUE) { 371 String tokenTypeName = TokenStream.tokenToName(statementWithLabel.getType()); 372 throw new AssertionError("Unexpected node type with label: " + tokenTypeName); 373 } 374 375 Node label = statementWithLabel.getFirstChild(); 376 if (label == null) return null; 377 378 String identifier = label.getString(); 379 assert identifier != null: "If label exists identifier should not be null"; 380 381 JsFunctionScope scope = toFunctionScope(getScope()); 382 JsName labelName = scope.findLabel(identifier); 383 assert labelName != null: "Unknown label name: " + identifier; 384 385 return labelName.makeRef(); 386 } 387 388 private JsInvocation mapCall(Node callNode) throws JsParserException { 389 // Map the target expression. 390 // 391 Node from = callNode.getFirstChild(); 392 JsExpression qualifier = mapExpression(from); 393 394 // Iterate over and map the arguments. 395 // 396 List<JsExpression> arguments = new SmartList<JsExpression>(); 397 from = from.getNext(); 398 while (from != null) { 399 arguments.add(mapExpression(from)); 400 from = from.getNext(); 401 } 402 403 return new JsInvocation(qualifier, arguments); 404 } 405 406 private JsExpression mapConditional(Node condNode) throws JsParserException { 407 JsConditional toCond = new JsConditional(); 408 409 Node fromTest = condNode.getFirstChild(); 410 toCond.setTestExpression(mapExpression(fromTest)); 411 412 Node fromThen = fromTest.getNext(); 413 toCond.setThenExpression(mapExpression(fromThen)); 414 415 Node fromElse = fromThen.getNext(); 416 toCond.setElseExpression(mapExpression(fromElse)); 417 418 return toCond; 419 } 420 421 private JsContinue mapContinue(Node contNode) { 422 return new JsContinue(getTargetLabel(contNode)); 423 } 424 425 private JsStatement mapDebuggerStatement(Node node) { 426 // Calls an optional method to invoke the debugger. 427 // 428 return new JsDebugger(); 429 } 430 431 private JsExpression mapDeleteProp(Node node) throws JsParserException { 432 Node from = node.getFirstChild(); 433 JsExpression to = mapExpression(from); 434 if (to instanceof JsNameRef) { 435 return new JsPrefixOperation( 436 JsUnaryOperator.DELETE, to); 437 } 438 else if (to instanceof JsArrayAccess) { 439 return new JsPrefixOperation( 440 JsUnaryOperator.DELETE, to); 441 } 442 else { 443 throw createParserException( 444 "'delete' can only operate on property names and array elements", 445 from); 446 } 447 } 448 449 private JsStatement mapDoOrWhileStatement(boolean isWhile, Node ifNode) 450 throws JsParserException { 451 452 // Pull out the pieces we want to map. 453 // 454 Node fromTestExpr; 455 Node fromBody; 456 if (isWhile) { 457 fromTestExpr = ifNode.getFirstChild(); 458 fromBody = ifNode.getFirstChild().getNext(); 459 } 460 else { 461 fromBody = ifNode.getFirstChild(); 462 fromTestExpr = ifNode.getFirstChild().getNext(); 463 } 464 465 // Map the test expression. 466 // 467 JsExpression toTestExpr = mapExpression(fromTestExpr); 468 469 // Map the body block. 470 // 471 JsStatement toBody = mapStatement(fromBody); 472 473 // Create and attach the "while" or "do" statement we're mapping to. 474 // 475 if (isWhile) { 476 return new JsWhile(toTestExpr, toBody); 477 } 478 else { 479 return new JsDoWhile(toTestExpr, toBody); 480 } 481 } 482 483 private JsExpression mapEqualityVariant(Node eqNode) throws JsParserException { 484 switch (eqNode.getIntDatum()) { 485 case TokenStream.EQ: 486 return mapBinaryOperation(JsBinaryOperator.EQ, eqNode); 487 488 case TokenStream.NE: 489 return mapBinaryOperation(JsBinaryOperator.NEQ, eqNode); 490 491 case TokenStream.SHEQ: 492 return mapBinaryOperation(JsBinaryOperator.REF_EQ, eqNode); 493 494 case TokenStream.SHNE: 495 return mapBinaryOperation(JsBinaryOperator.REF_NEQ, eqNode); 496 497 case TokenStream.LT: 498 return mapBinaryOperation(JsBinaryOperator.LT, eqNode); 499 500 case TokenStream.LE: 501 return mapBinaryOperation(JsBinaryOperator.LTE, eqNode); 502 503 case TokenStream.GT: 504 return mapBinaryOperation(JsBinaryOperator.GT, eqNode); 505 506 case TokenStream.GE: 507 return mapBinaryOperation(JsBinaryOperator.GTE, eqNode); 508 509 default: 510 throw createParserException("Unknown equality operator variant: " 511 + eqNode.getIntDatum(), eqNode); 512 } 513 } 514 515 private JsExpression mapExpression(Node exprNode) throws JsParserException { 516 JsNode unknown = map(exprNode); 517 if (unknown instanceof JsExpression) { 518 return (JsExpression) unknown; 519 } 520 else { 521 throw createParserException("Expecting an expression", exprNode); 522 } 523 } 524 525 private JsStatement mapExpressionStatement(Node node) throws JsParserException { 526 JsExpression expr = mapExpression(node.getFirstChild()); 527 return expr.makeStmt(); 528 } 529 530 private JsStatement mapForStatement(Node forNode) throws JsParserException { 531 Node fromInit = forNode.getFirstChild(); 532 Node fromTest = fromInit.getNext(); 533 Node fromIncr = fromTest.getNext(); 534 Node fromBody = fromIncr.getNext(); 535 536 if (fromBody == null) { 537 // This could be a "for...in" structure. 538 // We could based on the different child layout. 539 // 540 Node fromIter = forNode.getFirstChild(); 541 Node fromObjExpr = fromIter.getNext(); 542 fromBody = fromObjExpr.getNext(); 543 544 JsForIn toForIn; 545 if (fromIter.getType() == TokenStream.VAR) { 546 // A named iterator var. 547 // 548 Node fromIterVarName = fromIter.getFirstChild(); 549 String fromName = fromIterVarName.getString(); 550 JsName toName = getScope().declareName(fromName); 551 toForIn = new JsForIn(toName); 552 Node fromIterInit = fromIterVarName.getFirstChild(); 553 if (fromIterInit != null) { 554 // That has an initializer expression (useful only for side effects). 555 // 556 toForIn.setIterExpression(mapOptionalExpression(fromIterInit)); 557 } 558 } 559 else { 560 // An unnamed iterator var. 561 // 562 toForIn = new JsForIn(); 563 toForIn.setIterExpression(mapExpression(fromIter)); 564 } 565 toForIn.setObjectExpression(mapExpression(fromObjExpr)); 566 567 // The body stmt. 568 // 569 JsStatement bodyStmt = mapStatement(fromBody); 570 if (bodyStmt != null) { 571 toForIn.setBody(bodyStmt); 572 } 573 else { 574 toForIn.setBody(program.getEmptyStatement()); 575 } 576 577 return toForIn; 578 } 579 else { 580 // Regular ol' for loop. 581 // 582 JsFor toFor; 583 584 // The first item is either an expression or a JsVars. 585 JsNode init = map(fromInit); 586 JsExpression condition = mapOptionalExpression(fromTest); 587 JsExpression increment = mapOptionalExpression(fromIncr); 588 assert (init != null); 589 if (init instanceof JsVars) { 590 toFor = new JsFor((JsVars) init, condition, increment); 591 } 592 else { 593 assert (init instanceof JsExpression); 594 toFor = new JsFor((JsExpression) init, condition, increment); 595 } 596 597 JsStatement bodyStmt = mapStatement(fromBody); 598 if (bodyStmt != null) { 599 toFor.setBody(bodyStmt); 600 } 601 else { 602 toFor.setBody(program.getEmptyStatement()); 603 } 604 return toFor; 605 } 606 } 607 608 private JsExpression mapFunction(Node fnNode) throws JsParserException { 609 610 Node fromFnNameNode = fnNode.getFirstChild(); 611 Node fromParamNode = fnNode.getFirstChild().getNext().getFirstChild(); 612 Node fromBodyNode = fnNode.getFirstChild().getNext().getNext(); 613 614 // Decide the function's name, if any. 615 // 616 String fromFnName = fromFnNameNode.getString(); 617 JsName toFnName = null; 618 if (fromFnName != null && fromFnName.length() > 0) { 619 toFnName = getScope().declareName(fromFnName); 620 } 621 622 // Create it, and set the params. 623 // 624 JsFunction toFn = new JsFunction(getScope(), "jsCode"); 625 626 // Creating a function also creates a new scope, which we push onto 627 // the scope stack. 628 // 629 pushScope(toFn.getScope()); 630 631 while (fromParamNode != null) { 632 String fromParamName = fromParamNode.getString(); 633 // should this be unique? I think not since you can have dup args. 634 JsName paramName = toFn.getScope().declareName(fromParamName); 635 toFn.getParameters().add(new JsParameter(paramName)); 636 fromParamNode = fromParamNode.getNext(); 637 } 638 639 // Map the function's body. 640 // 641 JsBlock toBody = mapBlock(fromBodyNode); 642 toFn.setBody(toBody); 643 644 // Pop the new function's scope off of the scope stack. 645 // 646 popScope(); 647 648 return toFn; 649 } 650 651 private JsArrayAccess mapGetElem(Node getElemNode) throws JsParserException { 652 Node from1 = getElemNode.getFirstChild(); 653 Node from2 = from1.getNext(); 654 655 JsExpression to1 = mapExpression(from1); 656 JsExpression to2 = mapExpression(from2); 657 658 return new JsArrayAccess(to1, to2); 659 } 660 661 private JsNameRef mapGetProp(Node getPropNode) throws JsParserException { 662 Node from1 = getPropNode.getFirstChild(); 663 Node from2 = from1.getNext(); 664 665 JsExpression toQualifier = mapExpression(from1); 666 JsNameRef toNameRef; 667 if (from2 != null) { 668 toNameRef = mapAsPropertyNameRef(from2); 669 } 670 else { 671 // Special properties don't have a second expression. 672 // 673 Object obj = getPropNode.getProp(Node.SPECIAL_PROP_PROP); 674 assert (obj instanceof String); 675 toNameRef = new JsNameRef((String) obj); 676 } 677 toNameRef.setQualifier(toQualifier); 678 679 return toNameRef; 680 } 681 682 private JsIf mapIfStatement(Node ifNode) throws JsParserException { 683 684 // Pull out the pieces we want to map. 685 // 686 Node fromTestExpr = ifNode.getFirstChild(); 687 Node fromThenBlock = ifNode.getFirstChild().getNext(); 688 Node fromElseBlock = ifNode.getFirstChild().getNext().getNext(); 689 690 // Create the "if" statement we're mapping to. 691 // 692 JsIf toIf = new JsIf(); 693 694 // Map the test expression. 695 // 696 JsExpression toTestExpr = mapExpression(fromTestExpr); 697 toIf.setIfExpression(toTestExpr); 698 699 // Map the "then" block. 700 // 701 toIf.setThenStatement(mapStatement(fromThenBlock)); 702 703 // Map the "else" block. 704 // 705 if (fromElseBlock != null) { 706 toIf.setElseStatement(mapStatement(fromElseBlock)); 707 } 708 709 return toIf; 710 } 711 712 private JsExpression mapIncDecFixity(JsUnaryOperator op, Node node) 713 throws JsParserException { 714 switch (node.getIntDatum()) { 715 case TokenStream.PRE: 716 return mapPrefixOperation(op, node); 717 case TokenStream.POST: 718 return mapPostfixOperation(op, node); 719 default: 720 throw createParserException( 721 "Unknown prefix/postfix variant: " + node.getIntDatum(), node); 722 } 723 } 724 725 private JsLabel mapLabel(Node labelNode) throws JsParserException { 726 String fromName = labelNode.getFirstChild().getString(); 727 JsFunctionScope scope = toFunctionScope(getScope()); 728 JsName toName = scope.enterLabel(fromName); 729 Node fromStmt = labelNode.getFirstChild().getNext(); 730 JsLabel toLabel = new JsLabel(toName); 731 toLabel.setStatement(mapStatement(fromStmt)); 732 scope.exitLabel(); 733 return toLabel; 734 } 735 736 /** 737 * Creates a reference to a name that may or may not be obfuscatable, based on 738 * whether it matches a known name in the scope. 739 */ 740 private JsNameRef mapName(Node node) { 741 String ident = node.getString(); 742 return new JsNameRef(ident); 743 } 744 745 private JsNew mapNew(Node newNode) throws JsParserException { 746 // Map the constructor expression, which is often just the name of 747 // some lambda. 748 // 749 Node fromCtorExpr = newNode.getFirstChild(); 750 JsNew newExpr = new JsNew( 751 mapExpression(fromCtorExpr)); 752 753 // Iterate over and map the arguments. 754 // 755 List<JsExpression> args = newExpr.getArguments(); 756 Node fromArg = fromCtorExpr.getNext(); 757 while (fromArg != null) { 758 args.add(mapExpression(fromArg)); 759 fromArg = fromArg.getNext(); 760 } 761 762 return newExpr; 763 } 764 765 private JsExpression mapIntNumber(Node numberNode) { 766 return program.getNumberLiteral((int) numberNode.getDouble()); 767 } 768 769 private JsExpression mapDoubleNumber(Node numberNode) { 770 return program.getNumberLiteral(numberNode.getDouble()); 771 } 772 773 private JsExpression mapObjectLit(Node objLitNode) throws JsParserException { 774 JsObjectLiteral toLit = new JsObjectLiteral(); 775 Node fromPropInit = objLitNode.getFirstChild(); 776 while (fromPropInit != null) { 777 778 Node fromLabelExpr = fromPropInit; 779 JsExpression toLabelExpr = mapExpression(fromLabelExpr); 780 781 // Advance to the initializer expression. 782 // 783 fromPropInit = fromPropInit.getNext(); 784 Node fromValueExpr = fromPropInit; 785 if (fromValueExpr == null) { 786 throw createParserException("Expected an init expression for: " 787 + toLabelExpr, objLitNode); 788 } 789 JsExpression toValueExpr = mapExpression(fromValueExpr); 790 791 JsPropertyInitializer toPropInit = new JsPropertyInitializer( 792 toLabelExpr, toValueExpr); 793 toLit.getPropertyInitializers().add(toPropInit); 794 795 // Begin the next property initializer, if there is one. 796 // 797 fromPropInit = fromPropInit.getNext(); 798 } 799 800 return toLit; 801 } 802 803 private JsExpression mapOptionalExpression(Node exprNode) 804 throws JsParserException { 805 JsNode unknown = map(exprNode); 806 if (unknown != null) { 807 if (unknown instanceof JsExpression) { 808 return (JsExpression) unknown; 809 } 810 else { 811 throw createParserException("Expecting an expression or null", exprNode); 812 } 813 } 814 return null; 815 } 816 817 private JsExpression mapPostfixOperation(JsUnaryOperator op, Node node) 818 throws JsParserException { 819 Node from = node.getFirstChild(); 820 JsExpression to = mapExpression(from); 821 return new JsPostfixOperation(op, to); 822 } 823 824 private JsExpression mapPrefixOperation(JsUnaryOperator op, Node node) 825 throws JsParserException { 826 Node from = node.getFirstChild(); 827 JsExpression to = mapExpression(from); 828 return new JsPrefixOperation(op, to); 829 } 830 831 private JsExpression mapPrimary(Node node) throws JsParserException { 832 switch (node.getIntDatum()) { 833 case TokenStream.THIS: 834 return JsLiteral.THIS; 835 836 case TokenStream.TRUE: 837 return JsBooleanLiteral.TRUE; 838 839 case TokenStream.FALSE: 840 return JsBooleanLiteral.FALSE; 841 842 case TokenStream.NULL: 843 return JsNullLiteral.NULL; 844 845 case TokenStream.UNDEFINED: 846 return JsLiteral.UNDEFINED; 847 848 default: 849 throw createParserException("Unknown primary: " + node.getIntDatum(), 850 node); 851 } 852 } 853 854 private JsNode mapRegExp(Node regExpNode) { 855 JsRegExp toRegExp = new JsRegExp(); 856 857 Node fromPattern = regExpNode.getFirstChild(); 858 toRegExp.setPattern(fromPattern.getString()); 859 860 Node fromFlags = fromPattern.getNext(); 861 if (fromFlags != null) { 862 toRegExp.setFlags(fromFlags.getString()); 863 } 864 865 return toRegExp; 866 } 867 868 private JsExpression mapRelationalVariant(Node relNode) 869 throws JsParserException { 870 switch (relNode.getIntDatum()) { 871 case TokenStream.LT: 872 return mapBinaryOperation(JsBinaryOperator.LT, relNode); 873 874 case TokenStream.LE: 875 return mapBinaryOperation(JsBinaryOperator.LTE, relNode); 876 877 case TokenStream.GT: 878 return mapBinaryOperation(JsBinaryOperator.GT, relNode); 879 880 case TokenStream.GE: 881 return mapBinaryOperation(JsBinaryOperator.GTE, relNode); 882 883 case TokenStream.INSTANCEOF: 884 return mapBinaryOperation(JsBinaryOperator.INSTANCEOF, relNode); 885 886 case TokenStream.IN: 887 return mapBinaryOperation(JsBinaryOperator.INOP, relNode); 888 889 default: 890 throw createParserException("Unknown relational operator variant: " 891 + relNode.getIntDatum(), relNode); 892 } 893 } 894 895 private JsReturn mapReturn(Node returnNode) throws JsParserException { 896 JsReturn toReturn = new JsReturn(); 897 Node from = returnNode.getFirstChild(); 898 if (from != null) { 899 JsExpression to = mapExpression(from); 900 toReturn.setExpression(to); 901 } 902 903 return toReturn; 904 } 905 906 private JsExpression mapSetElem(Node setElemNode) throws JsParserException { 907 // Reuse the get elem code. 908 // 909 JsArrayAccess lhs = mapGetElem(setElemNode); 910 911 // Map the RHS. 912 // 913 Node fromRhs = setElemNode.getFirstChild().getNext().getNext(); 914 JsExpression toRhs = mapExpression(fromRhs); 915 916 return new JsBinaryOperation( 917 JsBinaryOperator.ASG, lhs, toRhs); 918 } 919 920 private JsExpression mapSetProp(Node getPropNode) throws JsParserException { 921 // Reuse the get prop code. 922 // 923 JsNameRef lhs = mapGetProp(getPropNode); 924 925 // Map the RHS. 926 // 927 Node fromRhs = getPropNode.getFirstChild().getNext().getNext(); 928 JsExpression toRhs = mapExpression(fromRhs); 929 930 return new JsBinaryOperation( 931 JsBinaryOperator.ASG, lhs, toRhs); 932 } 933 934 private JsExpression mapShiftVariant(Node shiftNode) throws JsParserException { 935 switch (shiftNode.getIntDatum()) { 936 case TokenStream.LSH: 937 return mapBinaryOperation(JsBinaryOperator.SHL, shiftNode); 938 939 case TokenStream.RSH: 940 return mapBinaryOperation(JsBinaryOperator.SHR, shiftNode); 941 942 case TokenStream.URSH: 943 return mapBinaryOperation(JsBinaryOperator.SHRU, shiftNode); 944 945 default: 946 throw createParserException("Unknown equality operator variant: " 947 + shiftNode.getIntDatum(), shiftNode); 948 } 949 } 950 951 private JsStatement mapStatement(Node nodeStmt) throws JsParserException { 952 JsNode unknown = map(nodeStmt); 953 if (unknown != null) { 954 if (unknown instanceof JsStatement) { 955 return (JsStatement) unknown; 956 } 957 else if (unknown instanceof JsExpression) { 958 return ((JsExpression) unknown).makeStmt(); 959 } 960 else { 961 throw createParserException("Expecting a statement", nodeStmt); 962 } 963 } 964 else { 965 // When map() returns null, we return an empty statement. 966 // 967 return program.getEmptyStatement(); 968 } 969 } 970 971 private void mapStatements(List<JsStatement> stmts, Node nodeStmts) 972 throws JsParserException { 973 Node curr = nodeStmts.getFirstChild(); 974 while (curr != null) { 975 JsStatement stmt = mapStatement(curr); 976 if (stmt != null) { 977 stmts.add(stmt); 978 } 979 else { 980 // When mapStatement() returns null, we just ignore it. 981 // 982 } 983 curr = curr.getNext(); 984 } 985 } 986 987 private List<JsStatement> mapStatements(Node nodeStmts) 988 throws JsParserException { 989 List<JsStatement> stmts = new ArrayList<JsStatement>(); 990 mapStatements(stmts, nodeStmts); 991 return stmts; 992 } 993 994 private JsSwitch mapSwitchStatement(Node switchNode) throws JsParserException { 995 JsSwitch toSwitch = new JsSwitch(); 996 997 // The switch expression. 998 // 999 Node fromSwitchExpr = switchNode.getFirstChild(); 1000 toSwitch.setExpression(mapExpression(fromSwitchExpr)); 1001 1002 // The members. 1003 // 1004 Node fromMember = fromSwitchExpr.getNext(); 1005 while (fromMember != null) { 1006 if (fromMember.getType() == TokenStream.CASE) { 1007 JsCase toCase = new JsCase(); 1008 1009 // Set the case expression. In JS, this can be any expression. 1010 // 1011 Node fromCaseExpr = fromMember.getFirstChild(); 1012 toCase.setCaseExpression(mapExpression(fromCaseExpr)); 1013 1014 // Set the case statements. 1015 // 1016 Node fromCaseBlock = fromCaseExpr.getNext(); 1017 mapStatements(toCase.getStatements(), fromCaseBlock); 1018 1019 // Attach the case to the switch. 1020 // 1021 toSwitch.getCases().add(toCase); 1022 } 1023 else { 1024 // This should be the only default statement. 1025 // If more than one is present, we keep the last one. 1026 // 1027 assert (fromMember.getType() == TokenStream.DEFAULT); 1028 JsDefault toDefault = new JsDefault(); 1029 1030 // Set the default statements. 1031 // 1032 Node fromDefaultBlock = fromMember.getFirstChild(); 1033 mapStatements(toDefault.getStatements(), fromDefaultBlock); 1034 1035 // Attach the default to the switch. 1036 // 1037 toSwitch.getCases().add(toDefault); 1038 } 1039 fromMember = fromMember.getNext(); 1040 } 1041 1042 return toSwitch; 1043 } 1044 1045 private JsThrow mapThrowStatement(Node throwNode) throws JsParserException { 1046 // Create, map, and attach. 1047 // 1048 Node fromExpr = throwNode.getFirstChild(); 1049 JsThrow toThrow = new JsThrow(mapExpression(fromExpr)); 1050 1051 return toThrow; 1052 } 1053 1054 private JsTry mapTryStatement(Node tryNode) throws JsParserException { 1055 JsTry toTry = new JsTry(); 1056 1057 // Map the "try" body. 1058 // 1059 Node fromTryBody = tryNode.getFirstChild(); 1060 toTry.setTryBlock(mapBlock(fromTryBody)); 1061 1062 // Map zero or more catch blocks. 1063 // 1064 Node fromCatchNodes = fromTryBody.getNext(); 1065 Node fromCatchNode = fromCatchNodes.getFirstChild(); 1066 while (fromCatchNode != null) { 1067 assert (fromCatchNode.getType() == TokenStream.CATCH); 1068 // Map the catch variable. 1069 // 1070 Node fromCatchVarName = fromCatchNode.getFirstChild(); 1071 JsCatch catchBlock = new JsCatch( 1072 getScope(), fromCatchVarName.getString()); 1073 1074 // Pre-advance to the next catch block, if any. 1075 // We do this here to decide whether or not this is the last one. 1076 // 1077 fromCatchNode = fromCatchNode.getNext(); 1078 1079 // Map the condition, with a little fixup based on whether or not 1080 // this is the last catch block. 1081 // 1082 Node fromCondition = fromCatchVarName.getNext(); 1083 JsExpression toCondition = mapExpression(fromCondition); 1084 catchBlock.setCondition(toCondition); 1085 if (fromCatchNode == null) { 1086 if (toCondition instanceof JsBooleanLiteral) { 1087 if (((JsBooleanLiteral) toCondition).getValue()) { 1088 // Actually, this is an unconditional catch block. 1089 // Indicate that by nulling the condition. 1090 // 1091 catchBlock.setCondition(null); 1092 } 1093 } 1094 } 1095 1096 // Map the catch body. 1097 // 1098 Node fromCatchBody = fromCondition.getNext(); 1099 catchBlock.setBody(mapBlock(fromCatchBody)); 1100 1101 // Attach it. 1102 // 1103 toTry.getCatches().add(catchBlock); 1104 } 1105 1106 Node fromFinallyNode = fromCatchNodes.getNext(); 1107 if (fromFinallyNode != null) { 1108 toTry.setFinallyBlock(mapBlock(fromFinallyNode)); 1109 } 1110 1111 return toTry; 1112 } 1113 1114 private JsExpression mapUnaryVariant(Node unOp) throws JsParserException { 1115 switch (unOp.getIntDatum()) { 1116 case TokenStream.SUB: 1117 return mapPrefixOperation(JsUnaryOperator.NEG, unOp); 1118 1119 case TokenStream.NOT: 1120 return mapPrefixOperation(JsUnaryOperator.NOT, unOp); 1121 1122 case TokenStream.BITNOT: 1123 return mapPrefixOperation(JsUnaryOperator.BIT_NOT, unOp); 1124 1125 case TokenStream.TYPEOF: 1126 return mapPrefixOperation(JsUnaryOperator.TYPEOF, unOp); 1127 1128 case TokenStream.ADD: 1129 if (!isJsNumber(unOp.getFirstChild())) { 1130 return mapPrefixOperation(JsUnaryOperator.POS, unOp); 1131 } 1132 else { 1133 // Pretend we didn't see it. 1134 return mapExpression(unOp.getFirstChild()); 1135 } 1136 1137 case TokenStream.VOID: 1138 return mapPrefixOperation(JsUnaryOperator.VOID, unOp); 1139 1140 default: 1141 throw createParserException( 1142 "Unknown unary operator variant: " + unOp.getIntDatum(), unOp); 1143 } 1144 } 1145 1146 private JsVars mapVar(Node varNode) throws JsParserException { 1147 JsVars toVars = new JsVars(); 1148 Node fromVar = varNode.getFirstChild(); 1149 while (fromVar != null) { 1150 // Use a conservative name allocation strategy that allocates all names 1151 // from the function's scope, even the names of properties in field 1152 // literals. 1153 // 1154 String fromName = fromVar.getString(); 1155 JsName toName = getScope().declareName(fromName); 1156 JsVars.JsVar toVar = new JsVars.JsVar(toName); 1157 1158 Node fromInit = fromVar.getFirstChild(); 1159 if (fromInit != null) { 1160 JsExpression toInit = mapExpression(fromInit); 1161 toVar.setInitExpression(toInit); 1162 } 1163 toVars.add(toVar); 1164 1165 fromVar = fromVar.getNext(); 1166 } 1167 1168 return toVars; 1169 } 1170 1171 private JsNode mapWithStatement(Node withNode) throws JsParserException { 1172 // The "with" statement is unsupported because it introduces ambiguity 1173 // related to whether or not a name is obfuscatable that we cannot resolve 1174 // statically. This is modified in our copy of the Rhino Parser to provide 1175 // detailed source & line info. So, this method should never actually be 1176 // called. 1177 // 1178 throw createParserException("Internal error: unexpected token 'with'", 1179 withNode); 1180 } 1181 1182 private void popScope() { 1183 scopeStack.pop(); 1184 } 1185 1186 private void pushScope(JsScope scope) { 1187 scopeStack.push(scope); 1188 } 1189 1190 private boolean isJsNumber(Node jsNode) { 1191 int type = jsNode.getType(); 1192 return type == TokenStream.NUMBER || type == TokenStream.NUMBER; 1193 } 1194 }