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