Search in sources :

Example 1 with AstChange

use of com.google.javascript.jscomp.GlobalNamespace.AstChange in project closure-compiler by google.

the class AggressiveInlineAliases method inlineGlobalAliasIfPossible.

/**
 * Attempt to inline an global alias of a global name. This requires that the name is well
 * defined: assigned unconditionally, assigned exactly once. It is assumed that, the name for
 * which it is an alias must already meet these same requirements.
 *
 * @param alias The alias to inline
 * @return Whether the alias was inlined.
 */
private boolean inlineGlobalAliasIfPossible(Name name, Ref alias, GlobalNamespace namespace) {
    // Ensure that the alias is assigned to global name at that the
    // declaration.
    Node aliasParent = alias.node.getParent();
    if (((aliasParent.isAssign() || aliasParent.isName()) && NodeUtil.isExecutedExactlyOnce(aliasParent)) || // TODO(tbreisacher): Do we still need this special case?
    (aliasParent.isName() && name.isConstructor())) {
        Node lvalue = aliasParent.isName() ? aliasParent : aliasParent.getFirstChild();
        if (!lvalue.isQualifiedName()) {
            return false;
        }
        if (lvalue.isName() && compiler.getCodingConvention().isExported(lvalue.getString(), /* local */
        false)) {
            return false;
        }
        name = namespace.getSlot(lvalue.getQualifiedName());
        if (name != null && name.isInlinableGlobalAlias()) {
            Set<AstChange> newNodes = new LinkedHashSet<>();
            List<Ref> refs = new ArrayList<>(name.getRefs());
            for (Ref ref : refs) {
                switch(ref.type) {
                    case SET_FROM_GLOBAL:
                        continue;
                    case DIRECT_GET:
                    case ALIASING_GET:
                    case PROTOTYPE_GET:
                    case CALL_GET:
                        Node newNode = alias.node.cloneTree();
                        Node node = ref.node;
                        node.getParent().replaceChild(node, newNode);
                        compiler.reportChangeToEnclosingScope(newNode);
                        newNodes.add(new AstChange(ref.module, ref.scope, newNode));
                        name.removeRef(ref);
                        break;
                    default:
                        throw new IllegalStateException();
                }
            }
            rewriteAliasProps(name, alias.node, 0, newNodes);
            // just set the original alias to null.
            aliasParent.replaceChild(alias.node, IR.nullNode());
            codeChanged = true;
            compiler.reportChangeToEnclosingScope(aliasParent);
            // Inlining the variable may have introduced new references
            // to descendants of {@code name}. So those need to be collected now.
            namespace.scanNewNodes(newNodes);
            return true;
        }
    }
    return false;
}
Also used : LinkedHashSet(java.util.LinkedHashSet) Ref(com.google.javascript.jscomp.GlobalNamespace.Ref) AstChange(com.google.javascript.jscomp.GlobalNamespace.AstChange) Node(com.google.javascript.rhino.Node) ArrayList(java.util.ArrayList)

Example 2 with AstChange

use of com.google.javascript.jscomp.GlobalNamespace.AstChange in project closure-compiler by google.

the class AggressiveInlineAliases method replaceAliasReference.

/**
 * @param alias A GlobalNamespace.Ref of the variable being aliased
 * @param aliasRef One particular usage of an alias that we want to replace with the aliased var.
 * @return an AstChange representing the new node(s) added to the AST *
 */
private AstChange replaceAliasReference(Ref alias, Reference aliasRef) {
    Node newNode = alias.node.cloneTree();
    aliasRef.getParent().replaceChild(aliasRef.getNode(), newNode);
    compiler.reportChangeToEnclosingScope(newNode);
    return new AstChange(getRefModule(aliasRef), aliasRef.getScope(), newNode);
}
Also used : AstChange(com.google.javascript.jscomp.GlobalNamespace.AstChange) Node(com.google.javascript.rhino.Node)

Example 3 with AstChange

use of com.google.javascript.jscomp.GlobalNamespace.AstChange in project closure-compiler by google.

the class AggressiveInlineAliases method rewriteAliasProp.

/**
 * @param value The value to use when rewriting.
 * @param depth The chain depth.
 * @param newNodes Expression nodes that have been updated.
 * @param prop The property to rewrite with value.
 */
private void rewriteAliasProp(Node value, int depth, Set<AstChange> newNodes, Name prop) {
    rewriteAliasProps(prop, value, depth + 1, newNodes);
    List<Ref> refs = new ArrayList<>(prop.getRefs());
    for (Ref ref : refs) {
        Node target = ref.node;
        for (int i = 0; i <= depth; i++) {
            if (target.isGetProp()) {
                target = target.getFirstChild();
            } else if (NodeUtil.isObjectLitKey(target)) {
                // Object literal key definitions are a little trickier, as we
                // need to find the assignment target
                Node gparent = target.getGrandparent();
                if (gparent.isAssign()) {
                    target = gparent.getFirstChild();
                } else {
                    checkState(NodeUtil.isObjectLitKey(gparent));
                    target = gparent;
                }
            } else {
                throw new IllegalStateException("unexpected: " + target);
            }
        }
        checkState(target.isGetProp() || target.isName());
        Node newValue = value.cloneTree();
        target.replaceWith(newValue);
        compiler.reportChangeToEnclosingScope(newValue);
        prop.removeRef(ref);
        // Rescan the expression root.
        newNodes.add(new AstChange(ref.module, ref.scope, ref.node));
        codeChanged = true;
    }
}
Also used : Ref(com.google.javascript.jscomp.GlobalNamespace.Ref) AstChange(com.google.javascript.jscomp.GlobalNamespace.AstChange) Node(com.google.javascript.rhino.Node) ArrayList(java.util.ArrayList)

Example 4 with AstChange

use of com.google.javascript.jscomp.GlobalNamespace.AstChange in project closure-compiler by google.

the class DestructuringGlobalNameExtractor method reassignDestructringLvalue.

/**
 * Given an lvalue in a destructuring pattern, and a detached subtree, rewrites the AST to assign
 * the lvalue to the subtree instead of its previous value, while preserving the rest of the
 * destructuring pattern
 *
 * <p>For example: given stringKey: 'y' and newName: 'new.name', where 'y' is contained in the
 * declaration `const {x, y, z} = original;`, this method will produce `const {x} = original;
 * const y = new.name; const {z} = original;`
 *
 * <p>This method only handles a limited subset of destructuring patterns and is intended only for
 * CollapseProperties and AggressiveInlineAliases. Preconditions are:
 *
 * <ul>
 *   <li>the pattern must be the lhs of an assignment or declaration (e.g. not a nested pattern)
 *   <li>the original rvalue for the pattern must be an effectively constant qualified name. it is
 *       safe to evaluate the rvalue multiple times.
 * </ul>
 *
 * @param stringKey the STRING_KEY node representing the ref to rewrite, e.g. `y`
 * @param newName what the lvalue in the STRING_KEY should now be assigned to, e.g. foo.bar
 * @param newNodes optionally a set to add new AstChanges to, if you need to keep the
 *     GlobalNamespace up-to-date. Otherwise null.
 * @param ref the Ref corresponding to the `stringKey`
 */
static void reassignDestructringLvalue(Node stringKey, Node newName, @Nullable Set<AstChange> newNodes, Ref ref, AbstractCompiler compiler) {
    Node pattern = stringKey.getParent();
    Node assignmentType = pattern.getParent();
    checkState(assignmentType.isAssign() || assignmentType.isDestructuringLhs(), assignmentType);
    // e.g. `original`
    Node originalRvalue = pattern.getNext();
    // don't handle rvalues with side effects
    checkState(originalRvalue.isQualifiedName());
    // Create a new assignment using the provided qualified name, e.g. `const y = new.name;`
    Node lvalueToReassign = stringKey.getOnlyChild().isDefaultValue() ? stringKey.getOnlyChild().getFirstChild() : stringKey.getOnlyChild();
    if (newNodes != null) {
        newNodes.add(new AstChange(ref.scope, newName));
    }
    Node rvalue = makeNewRvalueForDestructuringKey(stringKey, newName, newNodes, ref);
    // Add that new assignment to the AST after the original destructuring pattern
    if (stringKey.getPrevious() == null && stringKey.getNext() != null) {
        // Remove the original item completely if the given string key doesn't have any preceding
        // string keys, /and/ it has succeeding string keys.
        replaceDestructuringAssignment(pattern, lvalueToReassign.detach(), rvalue);
    } else {
        addAfter(pattern, lvalueToReassign.detach(), rvalue);
    }
    // assigning `lvalueToReassign`. e.g. create `const {z} = original;`
    if (stringKey.getNext() != null) {
        Node newPattern = createNewObjectPatternFromSuccessiveKeys(stringKey).srcref(pattern);
        // reuse the original rvalue if we don't need it for earlier keys, otherwise make a copy.
        final Node newRvalue;
        if (stringKey.getPrevious() == null) {
            newRvalue = originalRvalue.detach();
        } else {
            newRvalue = originalRvalue.cloneTree();
            if (newNodes != null) {
                newNodes.add(new AstChange(ref.scope, newRvalue));
            }
        }
        addAfter(lvalueToReassign, newPattern, newRvalue);
    }
    stringKey.detach();
    compiler.reportChangeToEnclosingScope(lvalueToReassign);
}
Also used : AstChange(com.google.javascript.jscomp.GlobalNamespace.AstChange) Node(com.google.javascript.rhino.Node)

Example 5 with AstChange

use of com.google.javascript.jscomp.GlobalNamespace.AstChange in project closure-compiler by google.

the class AggressiveInlineAliases method inlineAliasIfPossible.

private boolean inlineAliasIfPossible(Name name, Ref alias, GlobalNamespace namespace) {
    // Ensure that the alias is assigned to a local variable at that
    // variable's declaration. If the alias's parent is a NAME,
    // then the NAME must be the child of a VAR, LET, or CONST node, and we must
    // be in a VAR, LET, or CONST assignment.
    // Otherwise if the parent is an assign, we are in a "a = alias" case.
    Node aliasParent = alias.node.getParent();
    if (aliasParent.isName() || aliasParent.isAssign()) {
        Node aliasLhsNode = aliasParent.isName() ? aliasParent : aliasParent.getFirstChild();
        String aliasVarName = aliasLhsNode.getString();
        Var aliasVar = alias.scope.getVar(aliasVarName);
        checkState(aliasVar != null, "Expected variable to be defined in scope", aliasVarName);
        ReferenceCollectingCallback collector = new ReferenceCollectingCallback(compiler, ReferenceCollectingCallback.DO_NOTHING_BEHAVIOR, new Es6SyntacticScopeCreator(compiler), Predicates.equalTo(aliasVar));
        Scope aliasScope = aliasVar.getScope();
        collector.processScope(aliasScope);
        ReferenceCollection aliasRefs = collector.getReferences(aliasVar);
        Set<AstChange> newNodes = new LinkedHashSet<>();
        if (aliasRefs.isWellDefined() && aliasRefs.isAssignedOnceInLifetime()) {
            // The alias is well-formed, so do the inlining now.
            int size = aliasRefs.references.size();
            // It's initialized on either the first or second reference.
            int firstRead = aliasRefs.references.get(0).isInitializingDeclaration() ? 1 : 2;
            for (int i = firstRead; i < size; i++) {
                Reference aliasRef = aliasRefs.references.get(i);
                newNodes.add(replaceAliasReference(alias, aliasRef));
            }
            // just set the original alias to null.
            replaceAliasAssignment(alias, aliasLhsNode);
            // Inlining the variable may have introduced new references
            // to descendants of {@code name}. So those need to be collected now.
            namespace.scanNewNodes(newNodes);
            return true;
        }
        if (name.isConstructor()) {
            // removing this.
            if (partiallyInlineAlias(alias, namespace, aliasRefs, aliasLhsNode)) {
                return true;
            } else {
                // accesses.
                if (referencesCollapsibleProperty(aliasRefs, name, namespace)) {
                    compiler.report(JSError.make(aliasParent, UNSAFE_CTOR_ALIASING, aliasVarName));
                }
            }
        }
    }
    return false;
}
Also used : LinkedHashSet(java.util.LinkedHashSet) AstChange(com.google.javascript.jscomp.GlobalNamespace.AstChange) Node(com.google.javascript.rhino.Node)

Aggregations

AstChange (com.google.javascript.jscomp.GlobalNamespace.AstChange)9 Node (com.google.javascript.rhino.Node)9 Ref (com.google.javascript.jscomp.GlobalNamespace.Ref)4 LinkedHashSet (java.util.LinkedHashSet)4 Name (com.google.javascript.jscomp.GlobalNamespace.Name)2 ArrayList (java.util.ArrayList)2 ImmutableSet (com.google.common.collect.ImmutableSet)1 NodeSubject.assertNode (com.google.javascript.rhino.testing.NodeSubject.assertNode)1 Test (org.junit.Test)1