/** * Prepare an template AST to use when performing matches. * * @param templateFunctionNode The template declaration function to extract the template AST from. * @return The first node of the template AST sequence to use when matching. */ private Node initTemplate(Node templateFunctionNode) { Node prepped = templateFunctionNode.cloneTree(); prepTemplatePlaceholders(prepped); Node body = prepped.getLastChild(); Node startNode; if (body.hasOneChild() && body.getFirstChild().isExprResult()) { // When matching an expression, don't require it to be a complete // statement. startNode = body.getFirstFirstChild(); } else { startNode = body.getFirstChild(); } for (int i = 0; i < templateLocals.size(); i++) { // reserve space in the locals array. this.localVarMatches.add(null); } for (int i = 0; i < templateParams.size(); i++) { // reserve space in the params array. this.paramNodeMatches.add(null); } return startNode; }
public void findNamedFunctions(NodeTraversal t, Node n, Node parent) { if (!NodeUtil.isStatement(n)) { // There aren't any interesting functions here. return; } switch (n.getType()) { // Functions expressions in the form of: // var fooFn = function(x) { return ... } case Token.VAR: Preconditions.checkState(n.hasOneChild()); Node nameNode = n.getFirstChild(); if (nameNode.isName() && nameNode.hasChildren() && nameNode.getFirstChild().isFunction()) { maybeAddFunction(new FunctionVar(n), t.getModule()); } break; // Named functions // function Foo(x) { return ... } case Token.FUNCTION: Preconditions.checkState(NodeUtil.isStatementBlock(parent) || parent.isLabel()); if (!NodeUtil.isFunctionExpression(n)) { Function fn = new NamedFunction(n); maybeAddFunction(fn, t.getModule()); } break; } }
private void validateObjectPatternStringKey(int type, Node n) { validateNodeType(Token.STRING_KEY, n); validateObjectLiteralKeyName(n); validateChildCountIn(n, 0, 1); if (n.hasOneChild()) { validateNameDeclarationChild(type, n.getFirstChild()); } }
private void validateObjectLitStringKey(Node n) { validateNodeType(Token.STRING_KEY, n); validateObjectLiteralKeyName(n); if (isEs6OrHigher()) { validateChildCountIn(n, 0, 1); } else { validateChildCount(n, 1); } if (n.hasOneChild()) { validateExpression(n.getFirstChild()); } }
private void validateObjectLitSetKey(Node n) { validateNodeType(Token.SETTER_DEF, n); validateChildCount(n); validateObjectLiteralKeyName(n); Node function = n.getFirstChild(); validateFunctionExpression(function); // objlit set functions must be nameless, and must have 1 parameter. if (!function.getFirstChild().getString().isEmpty()) { violation("Expected unnamed function expression.", n); } Node functionParams = function.getChildAtIndex(1); if (!functionParams.hasOneChild()) { violation("set methods must have exactly one parameter.", n); } }
/** Collapse VARs and EXPR_RESULT node into FOR loop initializers where possible. */ private void maybeCollapseIntoForStatements(Node n, Node parent) { // Only SCRIPT, BLOCK, and LABELs can have FORs that can be collapsed into. // LABELs are not supported here. if (parent == null || !NodeUtil.isStatementBlock(parent)) { return; } // Is the current node something that can be in a for loop initializer? if (!NodeUtil.isExpressionNode(n) && !NodeUtil.isVar(n)) { return; } // Is the next statement a valid FOR? Node nextSibling = n.getNext(); if (nextSibling != null && nextSibling.getType() == Token.FOR && !NodeUtil.isForIn(nextSibling) && nextSibling.getFirstChild().getType() == Token.EMPTY) { // Does the current node contain an in operator? If so, embedding // the expression in a for loop can cause some Javascript parsers (such // as the Playstation 3's browser based on Access's NetFront // browser) to fail to parse the code. // See bug 1778863 for details. if (NodeUtil.containsType(n, Token.IN)) { return; } // Move the current node into the FOR loop initializer. Node forNode = nextSibling; Node oldInitializer = forNode.getFirstChild(); parent.removeChild(n); Node newInitializer; if (NodeUtil.isVar(n)) { newInitializer = n; } else { // Extract the expression from EXPR_RESULT node. Preconditions.checkState(n.hasOneChild()); newInitializer = n.getFirstChild(); n.removeChild(newInitializer); } forNode.replaceChild(oldInitializer, newInitializer); compiler.reportCodeChange(); } }
/** * Checks if the given function matches the criteria for an inlinable function, and if so, adds it * to our set of inlinable functions. */ boolean isDirectCallNodeReplacementPossible(Node fnNode) { // Only inline single-statement functions Node block = NodeUtil.getFunctionBody(fnNode); // Check if this function is suitable for direct replacement of a CALL node: // a function that consists of single return that returns an expression. if (!block.hasChildren()) { // special case empty functions. return true; } else if (block.hasOneChild()) { // Only inline functions that return something. if (block.getFirstChild().getType() == Token.RETURN && block.getFirstChild().getFirstChild() != null) { return true; } } return false; }
protected void testExternChanges(String extern, String input, String expectedExtern) { Compiler compiler = createCompiler(); CompilerOptions options = getOptions(); compiler.init( ImmutableList.of(SourceFile.fromCode("extern", extern)), ImmutableList.of(SourceFile.fromCode("input", input)), options); compiler.parseInputs(); assertFalse(compiler.hasErrors()); Node externsAndJs = compiler.getRoot(); Node root = externsAndJs.getLastChild(); Node externs = externsAndJs.getFirstChild(); Node expected = compiler.parseTestCode(expectedExtern); assertFalse(compiler.hasErrors()); (getProcessor(compiler)).process(externs, root); if (compareAsTree) { // Expected output parsed without implied block. Preconditions.checkState(externs.isBlock()); Preconditions.checkState(compareJsDoc); Preconditions.checkState( externs.hasOneChild(), "Compare as tree only works when output has a single script."); externs = externs.getFirstChild(); String explanation = expected.checkTreeEqualsIncludingJsDoc(externs); assertNull( "\nExpected: " + compiler.toSource(expected) + "\nResult: " + compiler.toSource(externs) + "\n" + explanation, explanation); } else { String externsCode = compiler.toSource(externs); String expectedCode = compiler.toSource(expected); assertEquals(expectedCode, externsCode); } }
private boolean canFuseIntoOneStatement(Node block) { // If we are favoring semi-colon, we shouldn't fuse script blocks. if (!favorsCommaOverSemiColon && !block.isBlock()) { return false; } // Nothing to do here. if (!block.hasChildren() || block.hasOneChild()) { return false; } Node last = block.getLastChild(); for (Node c = block.getFirstChild(); c != null; c = c.getNext()) { if (!c.isExprResult() && c != last) { return false; } } return isFusableControlStatement(last); }
/** Determine which, if any, of the supported types the call site is. */ private CallSiteType classifyCallSite(Node callNode) { Node parent = callNode.getParent(); Node grandParent = parent.getParent(); // Verify the call site: if (NodeUtil.isExprCall(parent)) { // This is a simple call? Example: "foo();". return CallSiteType.SIMPLE_CALL; } else if (NodeUtil.isExprAssign(grandParent) && !NodeUtil.isLhs(callNode, parent) && parent.getFirstChild().getType() == Token.NAME && !NodeUtil.isConstantName(parent.getFirstChild())) { // This is a simple assignment. Example: "x = foo();" return CallSiteType.SIMPLE_ASSIGNMENT; } else if (parent.getType() == Token.NAME && !NodeUtil.isConstantName(parent) && grandParent.getType() == Token.VAR && grandParent.hasOneChild()) { // This is a var declaration. Example: "var x = foo();" // TODO(johnlenz): Should we be checking for constants on the // left-hand-side of the assignments (and handling them as EXPRESSION? return CallSiteType.VAR_DECL_SIMPLE_ASSIGNMENT; } else { Node expressionRoot = ExpressionDecomposer.findExpressionRoot(callNode); if (expressionRoot != null) { ExpressionDecomposer decomposer = new ExpressionDecomposer(compiler, safeNameIdSupplier, knownConstants); DecompositionType type = decomposer.canExposeExpression(callNode); if (type == DecompositionType.MOVABLE) { return CallSiteType.EXPRESSION; } else if (type == DecompositionType.DECOMPOSABLE) { return CallSiteType.DECOMPOSABLE_EXPRESSION; } else { Preconditions.checkState(type == DecompositionType.UNDECOMPOSABLE); } } } return CallSiteType.UNSUPPORTED; }
private Node tryFoldUnaryOperator(Node n) { Preconditions.checkState(n.hasOneChild(), n); Node left = n.getFirstChild(); Node parent = n.getParent(); if (left == null) { return n; } TernaryValue leftVal = NodeUtil.getPureBooleanValue(left); if (leftVal == TernaryValue.UNKNOWN) { return n; } switch (n.getType()) { case NOT: // Don't fold !0 and !1 back to false. if (late && left.isNumber()) { double numValue = left.getDouble(); if (numValue == 0 || numValue == 1) { return n; } } Node replacementNode = NodeUtil.booleanNode(!leftVal.toBoolean(true)); parent.replaceChild(n, replacementNode); reportCodeChange(); return replacementNode; case POS: if (NodeUtil.isNumericResult(left)) { // POS does nothing to numeric values. parent.replaceChild(n, left.detachFromParent()); reportCodeChange(); return left; } return n; case NEG: if (left.isName()) { if (left.getString().equals("Infinity")) { // "-Infinity" is valid and a literal, don't modify it. return n; } else if (left.getString().equals("NaN")) { // "-NaN" is "NaN". n.removeChild(left); parent.replaceChild(n, left); reportCodeChange(); return left; } } if (left.isNumber()) { double negNum = -left.getDouble(); Node negNumNode = IR.number(negNum); parent.replaceChild(n, negNumNode); reportCodeChange(); return negNumNode; } else { // left is not a number node, so do not replace, but warn the // user because they can't be doing anything good report(NEGATING_A_NON_NUMBER_ERROR, left); return n; } case BITNOT: try { double val = left.getDouble(); if (val >= Integer.MIN_VALUE && val <= Integer.MAX_VALUE) { int intVal = (int) val; if (intVal == val) { Node notIntValNode = IR.number(~intVal); parent.replaceChild(n, notIntValNode); reportCodeChange(); return notIntValNode; } else { report(FRACTIONAL_BITWISE_OPERAND, left); return n; } } else { report(BITWISE_OPERAND_OUT_OF_RANGE, left); return n; } } catch (UnsupportedOperationException ex) { // left is not a number node, so do not replace, but warn the // user because they can't be doing anything good report(NEGATING_A_NON_NUMBER_ERROR, left); return n; } default: return n; } }