/** * Replaces a GETPROP a.b.c with a NAME a$b$c. * * @param alias A flattened prefix name (e.g. "a$b") * @param n The GETPROP node corresponding to the original name (e.g. "a.b") * @param parent {@code n}'s parent * @param originalName String version of the property name. */ private void flattenNameRef(String alias, Node n, Node parent, String originalName) { Preconditions.checkArgument( n.isGetProp(), "Expected GETPROP, found %s. Node: %s", Token.name(n.getType()), n); // BEFORE: // getprop // getprop // name a // string b // string c // AFTER: // name a$b$c Node ref = NodeUtil.newName(compiler, alias, n, originalName); NodeUtil.copyNameAnnotations(n.getLastChild(), ref); if (parent.isCall() && n == parent.getFirstChild()) { // The node was a call target, we are deliberately flatten these as // we node the "this" isn't provided by the namespace. Mark it as such: parent.putBooleanProp(Node.FREE_CALL, true); } TypeI type = n.getTypeI(); if (type != null) { ref.setTypeI(type); } parent.replaceChild(n, ref); compiler.reportCodeChange(); }
@Override protected void aliasNode(Node throwNode, Node parent) { Node name = NodeUtil.newName( compiler.getCodingConvention(), getAliasName(), throwNode, getAliasName()); Node aliasCall = IR.call(name, throwNode.removeFirstChild()); aliasCall.putBooleanProp(Node.FREE_CALL, true); Node exprResult = IR.exprResult(aliasCall); parent.replaceChild(throwNode, exprResult); }
@Override /** Create the alias declaration (e.g. var $$ALIAS_VOID=void 0;). */ protected void insertAliasDeclaration(Node codeRoot) { Node varNode = new Node(Token.VAR); Node value = IR.voidNode(IR.number(0)); Node name = NodeUtil.newName(compiler.getCodingConvention(), getAliasName(), varNode, getAliasName()); name.addChildToBack(value); varNode.addChildToBack(name); codeRoot.addChildrenToFront(varNode); }
/** Flattens a stub declaration. This is mostly a hack to support legacy users. */ private void flattenSimpleStubDeclaration(Name name, String alias) { Ref ref = Iterables.getOnlyElement(name.getRefs()); Node nameNode = NodeUtil.newName(compiler, alias, ref.node, name.getFullName()); Node varNode = IR.var(nameNode).useSourceInfoIfMissingFrom(nameNode); Preconditions.checkState(ref.node.getParent().isExprResult()); Node parent = ref.node.getParent(); Node grandparent = parent.getParent(); grandparent.replaceChild(parent, varNode); compiler.reportCodeChange(); }
@Override protected void aliasNode(Node n, Node parent) { Node aliasNode = NodeUtil.newName(compiler.getCodingConvention(), getAliasName(), n, getAliasName()); parent.replaceChild(n, aliasNode); }
/** * Updates the first initialization (a.k.a "declaration") of a global name that occurs at an * ASSIGN node. See comment for {@link #updateObjLitOrFunctionDeclaration}. * * @param n An object representing a global name (e.g. "a", "a.b.c") * @param alias The flattened name for {@code n} (e.g. "a", "a$b$c") */ private void updateObjLitOrFunctionDeclarationAtAssignNode( Name n, String alias, boolean canCollapseChildNames) { // NOTE: It's important that we don't add additional nodes // (e.g. a var node before the exprstmt) because the exprstmt might be // the child of an if statement that's not inside a block). Ref ref = n.getDeclaration(); Node rvalue = ref.node.getNext(); Node varNode = new Node(Token.VAR); Node varParent = ref.node.getAncestor(3); Node grandparent = ref.node.getAncestor(2); boolean isObjLit = rvalue.isObjectLit(); boolean insertedVarNode = false; if (isObjLit && n.canEliminate()) { // Eliminate the object literal altogether. varParent.replaceChild(grandparent, varNode); ref.node = null; insertedVarNode = true; } else if (!n.isSimpleName()) { // Create a VAR node to declare the name. if (rvalue.isFunction()) { checkForHosedThisReferences(rvalue, n.docInfo, n); } ref.node.getParent().removeChild(rvalue); Node nameNode = NodeUtil.newName(compiler, alias, ref.node.getAncestor(2), n.getFullName()); JSDocInfo info = NodeUtil.getBestJSDocInfo(ref.node.getParent()); if (ref.node.getLastChild().getBooleanProp(Node.IS_CONSTANT_NAME) || (info != null && info.isConstant())) { nameNode.putBooleanProp(Node.IS_CONSTANT_NAME, true); } if (info != null) { varNode.setJSDocInfo(info); } varNode.addChildToBack(nameNode); nameNode.addChildToFront(rvalue); varParent.replaceChild(grandparent, varNode); // Update the node ancestry stored in the reference. ref.node = nameNode; insertedVarNode = true; } if (canCollapseChildNames) { if (isObjLit) { declareVarsForObjLitValues( n, alias, rvalue, varNode, varParent.getChildBefore(varNode), varParent); } addStubsForUndeclaredProperties(n, alias, varParent, varNode); } if (insertedVarNode) { if (!varNode.hasChildren()) { varParent.removeChild(varNode); } compiler.reportCodeChange(); } }
/** * Updates the initial assignment to a collapsible property at global scope by changing it to a * variable declaration (e.g. a.b = 1 -> var a$b = 1). The property's value may either be a * primitive or an object literal or function whose properties aren't collapsible. * * @param alias The flattened property name (e.g. "a$b") * @param refName The name for the reference being updated. * @param ref An object containing information about the assignment getting updated */ private void updateSimpleDeclaration(String alias, Name refName, Ref ref) { Node rvalue = ref.node.getNext(); Node parent = ref.node.getParent(); Node grandparent = parent.getParent(); Node greatGrandparent = grandparent.getParent(); if (rvalue != null && rvalue.isFunction()) { checkForHosedThisReferences(rvalue, refName.docInfo, refName); } // Create the new alias node. Node nameNode = NodeUtil.newName(compiler, alias, grandparent.getFirstChild(), refName.getFullName()); NodeUtil.copyNameAnnotations(ref.node.getLastChild(), nameNode); if (grandparent.isExprResult()) { // BEFORE: a.b.c = ...; // exprstmt // assign // getprop // getprop // name a // string b // string c // NODE // AFTER: var a$b$c = ...; // var // name a$b$c // NODE // Remove the r-value (NODE). parent.removeChild(rvalue); nameNode.addChildToFront(rvalue); Node varNode = IR.var(nameNode); greatGrandparent.replaceChild(grandparent, varNode); } else { // This must be a complex assignment. Preconditions.checkNotNull(ref.getTwin()); // BEFORE: // ... (x.y = 3); // // AFTER: // var x$y; // ... (x$y = 3); Node current = grandparent; Node currentParent = grandparent.getParent(); for (; !currentParent.isScript() && !currentParent.isBlock(); current = currentParent, currentParent = currentParent.getParent()) {} // Create a stub variable declaration right // before the current statement. Node stubVar = IR.var(nameNode.cloneTree()).useSourceInfoIfMissingFrom(nameNode); currentParent.addChildBefore(stubVar, current); parent.replaceChild(ref.node, nameNode); } compiler.reportCodeChange(); }