private void addStatement(GenerateNodeContext context, Node stmt) { CodingConvention convention = compiler.getCodingConvention(); Node n = context.getNode(); Node exprRoot = n; while (!NodeUtil.isStatementBlock(exprRoot.getParent())) { exprRoot = exprRoot.getParent(); } // It's important that any class-building calls (goog.inherits) // come right after the class definition, so move the export after that. while (true) { Node next = exprRoot.getNext(); if (next != null && NodeUtil.isExprCall(next) && convention.getClassesDefinedByCall(next.getFirstChild()) != null) { exprRoot = next; } else { break; } } Node block = exprRoot.getParent(); block.addChildAfter(stmt, exprRoot); }
/** 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; }
@Override public void process(Node externs, Node root) { FindExportableNodes findExportableNodes = new FindExportableNodes(compiler); NodeTraversal.traverse(compiler, root, findExportableNodes); Map<String, GenerateNodeContext> exports = findExportableNodes.getExports(); for (Map.Entry<String, GenerateNodeContext> entry : exports.entrySet()) { String export = entry.getKey(); GenerateNodeContext context = entry.getValue(); // Emit the proper CALL expression. // This is an optimization to avoid exporting everything as a symbol // because exporting a property is significantly simpler/faster. // Only export the property if the parent is being exported or // if the parent is "prototype" and the grandparent is being exported. String parent = null; String grandparent = null; Node node = context.getNode().getFirstChild(); if (node.getType() == Token.GETPROP) { parent = node.getFirstChild().getQualifiedName(); if (node.getFirstChild().getType() == Token.GETPROP && getPropertyName(node.getFirstChild()).equals(PROTOTYPE_PROPERTY)) { grandparent = node.getFirstChild().getFirstChild().getQualifiedName(); } } boolean useExportSymbol = true; if (grandparent != null && exports.containsKey(grandparent)) { useExportSymbol = false; } else if (parent != null && exports.containsKey(parent)) { useExportSymbol = false; } Node call; if (useExportSymbol) { // exportSymbol(publicPath, object); call = new Node( Token.CALL, NodeUtil.newQualifiedNameNode(exportSymbolFunction, context.getNode(), export)); call.addChildToBack(Node.newString(export)); call.addChildToBack(NodeUtil.newQualifiedNameNode(export, context.getNode(), export)); } else { // exportProperty(object, publicName, symbol); String property = getPropertyName(node); call = new Node( Token.CALL, new Node[] { NodeUtil.newQualifiedNameNode( exportPropertyFunction, context.getNode(), exportPropertyFunction), NodeUtil.newQualifiedNameNode(parent, context.getNode(), exportPropertyFunction), Node.newString(property), NodeUtil.newQualifiedNameNode(export, context.getNode(), exportPropertyFunction) }); } Node expression = new Node(Token.EXPR_RESULT, call); annotate(expression); // It's important that any class-building calls (goog.inherits) // come right after the class definition, so move the export after that. Node insertionPoint = context.getContextNode().getNext(); CodingConvention convention = compiler.getCodingConvention(); while (insertionPoint != null && NodeUtil.isExprCall(insertionPoint) && convention.getClassesDefinedByCall(insertionPoint.getFirstChild()) != null) { insertionPoint = insertionPoint.getNext(); } if (insertionPoint == null) { context.getScriptNode().addChildToBack(expression); } else { context.getScriptNode().addChildBefore(expression, insertionPoint); } compiler.reportCodeChange(); } }