Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.google.gwt.dev.js.JsAstMapper Maven / Gradle / Ivy
/*
* Copyright 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.dev.js;
import com.google.dart.compiler.backend.js.ast.*;
import com.google.dart.compiler.backend.js.ast.JsLiteral.JsBooleanLiteral;
import com.google.gwt.dev.js.parserExceptions.JsParserException;
import com.google.gwt.dev.js.rhino.*;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
public class JsAstMapper {
private final JsProgram program;
private final ScopeContext scopeContext;
public JsAstMapper(@NotNull JsScope scope) {
scopeContext = new ScopeContext(scope);
program = scope.getProgram();
}
private static JsParserException createParserException(String msg, Node offender) {
CodePosition position = new CodePosition(offender.getLineno(), 0);
return new JsParserException("Parser encountered internal error: " + msg, position);
}
private JsNode map(Node node) throws JsParserException {
switch (node.getType()) {
case TokenStream.SCRIPT: {
JsBlock block = new JsBlock();
mapStatements(block.getStatements(), node);
return block;
}
case TokenStream.DEBUGGER:
return mapDebuggerStatement(node);
case TokenStream.VOID:
// VOID = nothing was parsed for this node
return null;
case TokenStream.EXPRSTMT:
return mapExpressionStatement(node);
case TokenStream.REGEXP:
return mapRegExp(node);
case TokenStream.ADD:
return mapBinaryOperation(JsBinaryOperator.ADD, node);
case TokenStream.SUB:
return mapBinaryOperation(JsBinaryOperator.SUB, node);
case TokenStream.MUL:
return mapBinaryOperation(JsBinaryOperator.MUL, node);
case TokenStream.DIV:
return mapBinaryOperation(JsBinaryOperator.DIV, node);
case TokenStream.MOD:
return mapBinaryOperation(JsBinaryOperator.MOD, node);
case TokenStream.AND:
return mapBinaryOperation(JsBinaryOperator.AND, node);
case TokenStream.OR:
return mapBinaryOperation(JsBinaryOperator.OR, node);
case TokenStream.BITAND:
return mapBinaryOperation(JsBinaryOperator.BIT_AND, node);
case TokenStream.BITOR:
return mapBinaryOperation(JsBinaryOperator.BIT_OR, node);
case TokenStream.BITXOR:
return mapBinaryOperation(JsBinaryOperator.BIT_XOR, node);
case TokenStream.ASSIGN:
return mapAssignmentVariant(node);
case TokenStream.RELOP:
return mapRelationalVariant(node);
case TokenStream.EQOP:
return mapEqualityVariant(node);
case TokenStream.SHOP:
return mapShiftVariant(node);
case TokenStream.UNARYOP:
return mapUnaryVariant(node);
case TokenStream.INC:
return mapIncDecFixity(JsUnaryOperator.INC, node);
case TokenStream.DEC:
return mapIncDecFixity(JsUnaryOperator.DEC, node);
case TokenStream.HOOK:
return mapConditional(node);
case TokenStream.STRING:
return program.getStringLiteral(node.getString());
case TokenStream.NUMBER_INT:
return mapIntNumber(node);
case TokenStream.NUMBER:
return mapDoubleNumber(node);
case TokenStream.CALL:
return mapCall(node);
case TokenStream.GETPROP:
return mapGetProp(node);
case TokenStream.SETPROP:
return mapSetProp(node);
case TokenStream.DELPROP:
return mapDeleteProp(node);
case TokenStream.IF:
return mapIfStatement(node);
case TokenStream.WHILE:
return mapDoOrWhileStatement(true, node);
case TokenStream.DO:
return mapDoOrWhileStatement(false, node);
case TokenStream.FOR:
return mapForStatement(node);
case TokenStream.WITH:
return mapWithStatement(node);
case TokenStream.GETELEM:
return mapGetElem(node);
case TokenStream.SETELEM:
return mapSetElem(node);
case TokenStream.FUNCTION:
return mapFunction(node);
case TokenStream.BLOCK:
return mapBlock(node);
case TokenStream.SETNAME:
return mapBinaryOperation(JsBinaryOperator.ASG, node);
case TokenStream.NAME:
case TokenStream.BINDNAME:
return scopeContext.globalNameFor(node.getString()).makeRef();
case TokenStream.RETURN:
return mapReturn(node);
case TokenStream.BREAK:
return mapBreak(node);
case TokenStream.CONTINUE:
return mapContinue(node);
case TokenStream.OBJLIT:
return mapObjectLit(node);
case TokenStream.ARRAYLIT:
return mapArrayLit(node);
case TokenStream.VAR:
return mapVar(node);
case TokenStream.PRIMARY:
return mapPrimary(node);
case TokenStream.COMMA:
return mapBinaryOperation(JsBinaryOperator.COMMA, node);
case TokenStream.NEW:
return mapNew(node);
case TokenStream.THROW:
return mapThrowStatement(node);
case TokenStream.TRY:
return mapTryStatement(node);
case TokenStream.SWITCH:
return mapSwitchStatement(node);
case TokenStream.LABEL:
return mapLabel(node);
default:
int tokenType = node.getType();
throw createParserException("Unexpected top-level token type: "
+ tokenType, node);
}
}
private JsArrayLiteral mapArrayLit(Node node) throws JsParserException {
JsArrayLiteral toLit = new JsArrayLiteral();
Node from = node.getFirstChild();
while (from != null) {
toLit.getExpressions().add(mapExpression(from));
from = from.getNext();
}
return toLit;
}
/**
* Produces a {@link JsNameRef}.
*/
private JsNameRef mapAsPropertyNameRef(Node nameRefNode)
throws JsParserException {
JsNode unknown = map(nameRefNode);
// This is weird, but for "a.b", the rhino AST calls "b" a string literal.
// However, since we know it's for a PROPGET, we can unstringliteralize it.
//
if (unknown instanceof JsStringLiteral) {
JsStringLiteral lit = (JsStringLiteral) unknown;
return scopeContext.referenceFor(lit.getValue());
}
else {
throw createParserException("Expecting a name reference", nameRefNode);
}
}
private JsExpression mapAssignmentVariant(Node asgNode)
throws JsParserException {
switch (asgNode.getIntDatum()) {
case TokenStream.NOP:
return mapBinaryOperation(JsBinaryOperator.ASG, asgNode);
case TokenStream.ADD:
return mapBinaryOperation(JsBinaryOperator.ASG_ADD, asgNode);
case TokenStream.SUB:
return mapBinaryOperation(JsBinaryOperator.ASG_SUB, asgNode);
case TokenStream.MUL:
return mapBinaryOperation(JsBinaryOperator.ASG_MUL, asgNode);
case TokenStream.DIV:
return mapBinaryOperation(JsBinaryOperator.ASG_DIV, asgNode);
case TokenStream.MOD:
return mapBinaryOperation(JsBinaryOperator.ASG_MOD, asgNode);
case TokenStream.BITAND:
return mapBinaryOperation(JsBinaryOperator.ASG_BIT_AND, asgNode);
case TokenStream.BITOR:
return mapBinaryOperation(JsBinaryOperator.ASG_BIT_OR, asgNode);
case TokenStream.BITXOR:
return mapBinaryOperation(JsBinaryOperator.ASG_BIT_XOR, asgNode);
case TokenStream.LSH:
return mapBinaryOperation(JsBinaryOperator.ASG_SHL, asgNode);
case TokenStream.RSH:
return mapBinaryOperation(JsBinaryOperator.ASG_SHR, asgNode);
case TokenStream.URSH:
return mapBinaryOperation(JsBinaryOperator.ASG_SHRU, asgNode);
default:
throw createParserException("Unknown assignment operator variant: "
+ asgNode.getIntDatum(), asgNode);
}
}
private JsExpression mapBinaryOperation(JsBinaryOperator op, Node node)
throws JsParserException {
Node from1 = node.getFirstChild();
Node from2 = from1.getNext();
JsExpression to1 = mapExpression(from1);
JsExpression to2 = mapExpression(from2);
return new JsBinaryOperation(op, to1, to2);
}
private JsBlock mapBlock(Node nodeStmts) throws JsParserException {
JsBlock block = new JsBlock();
mapStatements(block.getStatements(), nodeStmts);
return block;
}
private JsBreak mapBreak(Node breakNode) {
return new JsBreak(getTargetLabel(breakNode));
}
@Nullable
private JsNameRef getTargetLabel(@NotNull Node statementWithLabel) {
int type = statementWithLabel.getType();
if (type != TokenStream.BREAK && type != TokenStream.CONTINUE) {
String tokenTypeName = TokenStream.tokenToName(statementWithLabel.getType());
throw new AssertionError("Unexpected node type with label: " + tokenTypeName);
}
Node label = statementWithLabel.getFirstChild();
if (label == null) return null;
String identifier = label.getString();
assert identifier != null: "If label exists identifier should not be null";
JsName labelName = scopeContext.labelFor(identifier);
assert labelName != null: "Unknown label name: " + identifier;
return labelName.makeRef();
}
private JsInvocation mapCall(Node callNode) throws JsParserException {
// Map the target expression.
//
Node from = callNode.getFirstChild();
JsExpression qualifier = mapExpression(from);
// Iterate over and map the arguments.
//
List arguments = new SmartList();
from = from.getNext();
while (from != null) {
arguments.add(mapExpression(from));
from = from.getNext();
}
return new JsInvocation(qualifier, arguments);
}
private JsExpression mapConditional(Node condNode) throws JsParserException {
JsConditional toCond = new JsConditional();
Node fromTest = condNode.getFirstChild();
toCond.setTestExpression(mapExpression(fromTest));
Node fromThen = fromTest.getNext();
toCond.setThenExpression(mapExpression(fromThen));
Node fromElse = fromThen.getNext();
toCond.setElseExpression(mapExpression(fromElse));
return toCond;
}
private JsContinue mapContinue(Node contNode) {
return new JsContinue(getTargetLabel(contNode));
}
private JsStatement mapDebuggerStatement(Node node) {
// Calls an optional method to invoke the debugger.
//
return new JsDebugger();
}
private JsExpression mapDeleteProp(Node node) throws JsParserException {
Node from = node.getFirstChild();
JsExpression to = mapExpression(from);
if (to instanceof JsNameRef) {
return new JsPrefixOperation(
JsUnaryOperator.DELETE, to);
}
else if (to instanceof JsArrayAccess) {
return new JsPrefixOperation(
JsUnaryOperator.DELETE, to);
}
else {
throw createParserException(
"'delete' can only operate on property names and array elements",
from);
}
}
private JsStatement mapDoOrWhileStatement(boolean isWhile, Node ifNode)
throws JsParserException {
// Pull out the pieces we want to map.
//
Node fromTestExpr;
Node fromBody;
if (isWhile) {
fromTestExpr = ifNode.getFirstChild();
fromBody = ifNode.getFirstChild().getNext();
}
else {
fromBody = ifNode.getFirstChild();
fromTestExpr = ifNode.getFirstChild().getNext();
}
// Map the test expression.
//
JsExpression toTestExpr = mapExpression(fromTestExpr);
// Map the body block.
//
JsStatement toBody = mapStatement(fromBody);
// Create and attach the "while" or "do" statement we're mapping to.
//
if (isWhile) {
return new JsWhile(toTestExpr, toBody);
}
else {
return new JsDoWhile(toTestExpr, toBody);
}
}
private JsExpression mapEqualityVariant(Node eqNode) throws JsParserException {
switch (eqNode.getIntDatum()) {
case TokenStream.EQ:
return mapBinaryOperation(JsBinaryOperator.EQ, eqNode);
case TokenStream.NE:
return mapBinaryOperation(JsBinaryOperator.NEQ, eqNode);
case TokenStream.SHEQ:
return mapBinaryOperation(JsBinaryOperator.REF_EQ, eqNode);
case TokenStream.SHNE:
return mapBinaryOperation(JsBinaryOperator.REF_NEQ, eqNode);
case TokenStream.LT:
return mapBinaryOperation(JsBinaryOperator.LT, eqNode);
case TokenStream.LE:
return mapBinaryOperation(JsBinaryOperator.LTE, eqNode);
case TokenStream.GT:
return mapBinaryOperation(JsBinaryOperator.GT, eqNode);
case TokenStream.GE:
return mapBinaryOperation(JsBinaryOperator.GTE, eqNode);
default:
throw createParserException("Unknown equality operator variant: "
+ eqNode.getIntDatum(), eqNode);
}
}
private JsExpression mapExpression(Node exprNode) throws JsParserException {
JsNode unknown = map(exprNode);
if (unknown instanceof JsExpression) {
return (JsExpression) unknown;
}
else {
throw createParserException("Expecting an expression", exprNode);
}
}
private JsStatement mapExpressionStatement(Node node) throws JsParserException {
JsExpression expr = mapExpression(node.getFirstChild());
return expr.makeStmt();
}
private JsStatement mapForStatement(Node forNode) throws JsParserException {
Node fromInit = forNode.getFirstChild();
Node fromTest = fromInit.getNext();
Node fromIncr = fromTest.getNext();
Node fromBody = fromIncr.getNext();
if (fromBody == null) {
// This could be a "for...in" structure.
// We could based on the different child layout.
//
Node fromIter = forNode.getFirstChild();
Node fromObjExpr = fromIter.getNext();
fromBody = fromObjExpr.getNext();
JsForIn toForIn;
if (fromIter.getType() == TokenStream.VAR) {
// A named iterator var.
//
Node fromIterVarName = fromIter.getFirstChild();
String fromName = fromIterVarName.getString();
JsName toName = scopeContext.localNameFor(fromName);
toForIn = new JsForIn(toName);
Node fromIterInit = fromIterVarName.getFirstChild();
if (fromIterInit != null) {
// That has an initializer expression (useful only for side effects).
//
toForIn.setIterExpression(mapOptionalExpression(fromIterInit));
}
}
else {
// An unnamed iterator var.
//
toForIn = new JsForIn();
toForIn.setIterExpression(mapExpression(fromIter));
}
toForIn.setObjectExpression(mapExpression(fromObjExpr));
// The body stmt.
//
JsStatement bodyStmt = mapStatement(fromBody);
if (bodyStmt != null) {
toForIn.setBody(bodyStmt);
}
else {
toForIn.setBody(JsEmpty.INSTANCE);
}
return toForIn;
}
else {
// Regular ol' for loop.
//
JsFor toFor;
// The first item is either an expression or a JsVars.
JsNode init = map(fromInit);
JsExpression condition = mapOptionalExpression(fromTest);
JsExpression increment = mapOptionalExpression(fromIncr);
assert (init != null);
if (init instanceof JsVars) {
toFor = new JsFor((JsVars) init, condition, increment);
}
else {
assert (init instanceof JsExpression);
toFor = new JsFor((JsExpression) init, condition, increment);
}
JsStatement bodyStmt = mapStatement(fromBody);
if (bodyStmt != null) {
toFor.setBody(bodyStmt);
}
else {
toFor.setBody(JsEmpty.INSTANCE);
}
return toFor;
}
}
public JsFunction mapFunction(Node fnNode) throws JsParserException {
int nodeType = fnNode.getType();
assert nodeType == TokenStream.FUNCTION: "Expected function node, got: " + TokenStream.tokenToName(nodeType);
Node fromFnNameNode = fnNode.getFirstChild();
Node fromParamNode = fnNode.getFirstChild().getNext().getFirstChild();
Node fromBodyNode = fnNode.getFirstChild().getNext().getNext();
JsFunction toFn = scopeContext.enterFunction();
// Decide the function's name, if any.
//
String fnNameIdent = fromFnNameNode.getString();
if (fnNameIdent != null && fnNameIdent.length() > 0) {
scopeContext.globalNameFor(fnNameIdent);
}
while (fromParamNode != null) {
String fromParamName = fromParamNode.getString();
JsName name = scopeContext.localNameFor(fromParamName);
toFn.getParameters().add(new JsParameter(name));
fromParamNode = fromParamNode.getNext();
}
// Map the function's body.
//
JsBlock toBody = mapBlock(fromBodyNode);
toFn.setBody(toBody);
scopeContext.exitFunction();
return toFn;
}
private JsArrayAccess mapGetElem(Node getElemNode) throws JsParserException {
Node from1 = getElemNode.getFirstChild();
Node from2 = from1.getNext();
JsExpression to1 = mapExpression(from1);
JsExpression to2 = mapExpression(from2);
return new JsArrayAccess(to1, to2);
}
private JsNameRef mapGetProp(Node getPropNode) throws JsParserException {
Node from1 = getPropNode.getFirstChild();
Node from2 = from1.getNext();
JsExpression toQualifier = mapExpression(from1);
JsNameRef toNameRef;
if (from2 != null) {
toNameRef = mapAsPropertyNameRef(from2);
}
else {
// Special properties don't have a second expression.
//
Object obj = getPropNode.getProp(Node.SPECIAL_PROP_PROP);
assert (obj instanceof String);
toNameRef = scopeContext.referenceFor((String) obj);
}
toNameRef.setQualifier(toQualifier);
return toNameRef;
}
private JsIf mapIfStatement(Node ifNode) throws JsParserException {
// Pull out the pieces we want to map.
//
Node fromTestExpr = ifNode.getFirstChild();
Node fromThenBlock = ifNode.getFirstChild().getNext();
Node fromElseBlock = ifNode.getFirstChild().getNext().getNext();
// Create the "if" statement we're mapping to.
//
JsIf toIf = new JsIf();
// Map the test expression.
//
JsExpression toTestExpr = mapExpression(fromTestExpr);
toIf.setIfExpression(toTestExpr);
// Map the "then" block.
//
toIf.setThenStatement(mapStatement(fromThenBlock));
// Map the "else" block.
//
if (fromElseBlock != null) {
toIf.setElseStatement(mapStatement(fromElseBlock));
}
return toIf;
}
private JsExpression mapIncDecFixity(JsUnaryOperator op, Node node)
throws JsParserException {
switch (node.getIntDatum()) {
case TokenStream.PRE:
return mapPrefixOperation(op, node);
case TokenStream.POST:
return mapPostfixOperation(op, node);
default:
throw createParserException(
"Unknown prefix/postfix variant: " + node.getIntDatum(), node);
}
}
private JsLabel mapLabel(Node labelNode) throws JsParserException {
String fromName = labelNode.getFirstChild().getString();
JsName toName = scopeContext.enterLabel(fromName);
Node fromStmt = labelNode.getFirstChild().getNext();
JsLabel toLabel = new JsLabel(toName);
toLabel.setStatement(mapStatement(fromStmt));
scopeContext.exitLabel();
return toLabel;
}
private JsNew mapNew(Node newNode) throws JsParserException {
// Map the constructor expression, which is often just the name of
// some lambda.
//
Node fromCtorExpr = newNode.getFirstChild();
JsNew newExpr = new JsNew(
mapExpression(fromCtorExpr));
// Iterate over and map the arguments.
//
List args = newExpr.getArguments();
Node fromArg = fromCtorExpr.getNext();
while (fromArg != null) {
args.add(mapExpression(fromArg));
fromArg = fromArg.getNext();
}
return newExpr;
}
private JsExpression mapIntNumber(Node numberNode) {
return program.getNumberLiteral((int) numberNode.getDouble());
}
private JsExpression mapDoubleNumber(Node numberNode) {
return program.getNumberLiteral(numberNode.getDouble());
}
private JsExpression mapObjectLit(Node objLitNode) throws JsParserException {
JsObjectLiteral toLit = new JsObjectLiteral();
Node fromPropInit = objLitNode.getFirstChild();
while (fromPropInit != null) {
Node fromLabelExpr = fromPropInit;
JsExpression toLabelExpr = mapExpression(fromLabelExpr);
// Advance to the initializer expression.
//
fromPropInit = fromPropInit.getNext();
Node fromValueExpr = fromPropInit;
if (fromValueExpr == null) {
throw createParserException("Expected an init expression for: "
+ toLabelExpr, objLitNode);
}
JsExpression toValueExpr = mapExpression(fromValueExpr);
JsPropertyInitializer toPropInit = new JsPropertyInitializer(
toLabelExpr, toValueExpr);
toLit.getPropertyInitializers().add(toPropInit);
// Begin the next property initializer, if there is one.
//
fromPropInit = fromPropInit.getNext();
}
return toLit;
}
private JsExpression mapOptionalExpression(Node exprNode)
throws JsParserException {
JsNode unknown = map(exprNode);
if (unknown != null) {
if (unknown instanceof JsExpression) {
return (JsExpression) unknown;
}
else {
throw createParserException("Expecting an expression or null", exprNode);
}
}
return null;
}
private JsExpression mapPostfixOperation(JsUnaryOperator op, Node node)
throws JsParserException {
Node from = node.getFirstChild();
JsExpression to = mapExpression(from);
return new JsPostfixOperation(op, to);
}
private JsExpression mapPrefixOperation(JsUnaryOperator op, Node node)
throws JsParserException {
Node from = node.getFirstChild();
JsExpression to = mapExpression(from);
return new JsPrefixOperation(op, to);
}
private JsExpression mapPrimary(Node node) throws JsParserException {
switch (node.getIntDatum()) {
case TokenStream.THIS:
return JsLiteral.THIS;
case TokenStream.TRUE:
return JsBooleanLiteral.TRUE;
case TokenStream.FALSE:
return JsBooleanLiteral.FALSE;
case TokenStream.NULL:
return JsNullLiteral.NULL;
case TokenStream.UNDEFINED:
return JsLiteral.UNDEFINED;
default:
throw createParserException("Unknown primary: " + node.getIntDatum(),
node);
}
}
private JsNode mapRegExp(Node regExpNode) {
JsRegExp toRegExp = new JsRegExp();
Node fromPattern = regExpNode.getFirstChild();
toRegExp.setPattern(fromPattern.getString());
Node fromFlags = fromPattern.getNext();
if (fromFlags != null) {
toRegExp.setFlags(fromFlags.getString());
}
return toRegExp;
}
private JsExpression mapRelationalVariant(Node relNode)
throws JsParserException {
switch (relNode.getIntDatum()) {
case TokenStream.LT:
return mapBinaryOperation(JsBinaryOperator.LT, relNode);
case TokenStream.LE:
return mapBinaryOperation(JsBinaryOperator.LTE, relNode);
case TokenStream.GT:
return mapBinaryOperation(JsBinaryOperator.GT, relNode);
case TokenStream.GE:
return mapBinaryOperation(JsBinaryOperator.GTE, relNode);
case TokenStream.INSTANCEOF:
return mapBinaryOperation(JsBinaryOperator.INSTANCEOF, relNode);
case TokenStream.IN:
return mapBinaryOperation(JsBinaryOperator.INOP, relNode);
default:
throw createParserException("Unknown relational operator variant: "
+ relNode.getIntDatum(), relNode);
}
}
private JsReturn mapReturn(Node returnNode) throws JsParserException {
JsReturn toReturn = new JsReturn();
Node from = returnNode.getFirstChild();
if (from != null) {
JsExpression to = mapExpression(from);
toReturn.setExpression(to);
}
return toReturn;
}
private JsExpression mapSetElem(Node setElemNode) throws JsParserException {
// Reuse the get elem code.
//
JsArrayAccess lhs = mapGetElem(setElemNode);
// Map the RHS.
//
Node fromRhs = setElemNode.getFirstChild().getNext().getNext();
JsExpression toRhs = mapExpression(fromRhs);
return new JsBinaryOperation(
JsBinaryOperator.ASG, lhs, toRhs);
}
private JsExpression mapSetProp(Node getPropNode) throws JsParserException {
// Reuse the get prop code.
//
JsNameRef lhs = mapGetProp(getPropNode);
// Map the RHS.
//
Node fromRhs = getPropNode.getFirstChild().getNext().getNext();
JsExpression toRhs = mapExpression(fromRhs);
return new JsBinaryOperation(
JsBinaryOperator.ASG, lhs, toRhs);
}
private JsExpression mapShiftVariant(Node shiftNode) throws JsParserException {
switch (shiftNode.getIntDatum()) {
case TokenStream.LSH:
return mapBinaryOperation(JsBinaryOperator.SHL, shiftNode);
case TokenStream.RSH:
return mapBinaryOperation(JsBinaryOperator.SHR, shiftNode);
case TokenStream.URSH:
return mapBinaryOperation(JsBinaryOperator.SHRU, shiftNode);
default:
throw createParserException("Unknown equality operator variant: "
+ shiftNode.getIntDatum(), shiftNode);
}
}
private JsStatement mapStatement(Node nodeStmt) throws JsParserException {
JsNode unknown = map(nodeStmt);
if (unknown != null) {
if (unknown instanceof JsStatement) {
return (JsStatement) unknown;
}
else if (unknown instanceof JsExpression) {
return ((JsExpression) unknown).makeStmt();
}
else {
throw createParserException("Expecting a statement", nodeStmt);
}
}
else {
// When map() returns null, we return an empty statement.
//
return JsEmpty.INSTANCE;
}
}
private void mapStatements(List stmts, Node nodeStmts)
throws JsParserException {
Node curr = nodeStmts.getFirstChild();
while (curr != null) {
JsStatement stmt = mapStatement(curr);
if (stmt != null) {
stmts.add(stmt);
}
else {
// When mapStatement() returns null, we just ignore it.
//
}
curr = curr.getNext();
}
}
public List mapStatements(Node nodeStmts)
throws JsParserException {
List stmts = new ArrayList();
mapStatements(stmts, nodeStmts);
return stmts;
}
private JsSwitch mapSwitchStatement(Node switchNode) throws JsParserException {
JsSwitch toSwitch = new JsSwitch();
// The switch expression.
//
Node fromSwitchExpr = switchNode.getFirstChild();
toSwitch.setExpression(mapExpression(fromSwitchExpr));
// The members.
//
Node fromMember = fromSwitchExpr.getNext();
while (fromMember != null) {
if (fromMember.getType() == TokenStream.CASE) {
JsCase toCase = new JsCase();
// Set the case expression. In JS, this can be any expression.
//
Node fromCaseExpr = fromMember.getFirstChild();
toCase.setCaseExpression(mapExpression(fromCaseExpr));
// Set the case statements.
//
Node fromCaseBlock = fromCaseExpr.getNext();
mapStatements(toCase.getStatements(), fromCaseBlock);
// Attach the case to the switch.
//
toSwitch.getCases().add(toCase);
}
else {
// This should be the only default statement.
// If more than one is present, we keep the last one.
//
assert (fromMember.getType() == TokenStream.DEFAULT);
JsDefault toDefault = new JsDefault();
// Set the default statements.
//
Node fromDefaultBlock = fromMember.getFirstChild();
mapStatements(toDefault.getStatements(), fromDefaultBlock);
// Attach the default to the switch.
//
toSwitch.getCases().add(toDefault);
}
fromMember = fromMember.getNext();
}
return toSwitch;
}
private JsThrow mapThrowStatement(Node throwNode) throws JsParserException {
// Create, map, and attach.
//
Node fromExpr = throwNode.getFirstChild();
JsThrow toThrow = new JsThrow(mapExpression(fromExpr));
return toThrow;
}
private JsTry mapTryStatement(Node tryNode) throws JsParserException {
JsTry toTry = new JsTry();
// Map the "try" body.
//
Node fromTryBody = tryNode.getFirstChild();
toTry.setTryBlock(mapBlock(fromTryBody));
// Map zero or more catch blocks.
//
Node fromCatchNodes = fromTryBody.getNext();
Node fromCatchNode = fromCatchNodes.getFirstChild();
while (fromCatchNode != null) {
assert (fromCatchNode.getType() == TokenStream.CATCH);
// Map the catch variable.
//
Node fromCatchVarName = fromCatchNode.getFirstChild();
JsCatch catchBlock = scopeContext.enterCatch(fromCatchVarName.getString());
// Pre-advance to the next catch block, if any.
// We do this here to decide whether or not this is the last one.
//
fromCatchNode = fromCatchNode.getNext();
// Map the condition, with a little fixup based on whether or not
// this is the last catch block.
//
Node fromCondition = fromCatchVarName.getNext();
JsExpression toCondition = mapExpression(fromCondition);
catchBlock.setCondition(toCondition);
if (fromCatchNode == null) {
if (toCondition instanceof JsBooleanLiteral) {
if (((JsBooleanLiteral) toCondition).getValue()) {
// Actually, this is an unconditional catch block.
// Indicate that by nulling the condition.
//
catchBlock.setCondition(null);
}
}
}
// Map the catch body.
//
Node fromCatchBody = fromCondition.getNext();
catchBlock.setBody(mapBlock(fromCatchBody));
// Attach it.
//
toTry.getCatches().add(catchBlock);
scopeContext.exitCatch();
}
Node fromFinallyNode = fromCatchNodes.getNext();
if (fromFinallyNode != null) {
toTry.setFinallyBlock(mapBlock(fromFinallyNode));
}
return toTry;
}
private JsExpression mapUnaryVariant(Node unOp) throws JsParserException {
switch (unOp.getIntDatum()) {
case TokenStream.SUB:
return mapPrefixOperation(JsUnaryOperator.NEG, unOp);
case TokenStream.NOT:
return mapPrefixOperation(JsUnaryOperator.NOT, unOp);
case TokenStream.BITNOT:
return mapPrefixOperation(JsUnaryOperator.BIT_NOT, unOp);
case TokenStream.TYPEOF:
return mapPrefixOperation(JsUnaryOperator.TYPEOF, unOp);
case TokenStream.ADD:
if (!isJsNumber(unOp.getFirstChild())) {
return mapPrefixOperation(JsUnaryOperator.POS, unOp);
}
else {
// Pretend we didn't see it.
return mapExpression(unOp.getFirstChild());
}
case TokenStream.VOID:
return mapPrefixOperation(JsUnaryOperator.VOID, unOp);
default:
throw createParserException(
"Unknown unary operator variant: " + unOp.getIntDatum(), unOp);
}
}
private JsVars mapVar(Node varNode) throws JsParserException {
JsVars toVars = new JsVars();
Node fromVar = varNode.getFirstChild();
while (fromVar != null) {
// Use a conservative name allocation strategy that allocates all names
// from the function's scope, even the names of properties in field
// literals.
//
String fromName = fromVar.getString();
JsName toName = scopeContext.localNameFor(fromName);
JsVars.JsVar toVar = new JsVars.JsVar(toName);
Node fromInit = fromVar.getFirstChild();
if (fromInit != null) {
JsExpression toInit = mapExpression(fromInit);
toVar.setInitExpression(toInit);
}
toVars.add(toVar);
fromVar = fromVar.getNext();
}
return toVars;
}
private JsNode mapWithStatement(Node withNode) throws JsParserException {
// The "with" statement is unsupported because it introduces ambiguity
// related to whether or not a name is obfuscatable that we cannot resolve
// statically. This is modified in our copy of the Rhino Parser to provide
// detailed source & line info. So, this method should never actually be
// called.
//
throw createParserException("Internal error: unexpected token 'with'",
withNode);
}
private boolean isJsNumber(Node jsNode) {
int type = jsNode.getType();
return type == TokenStream.NUMBER || type == TokenStream.NUMBER;
}
}