Search in sources :

Example 6 with AstChange

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

the class AggressiveInlineAliases method rewriteAllSubclassInheritedAccesses.

/**
 * Inline all references to inherited static superclass properties from the subclass or any
 * descendant of the given subclass. Avoids inlining references to inherited methods when
 * possible, since they may use this or super().
 *
 * @param superclassNameObj The Name of the superclass
 * @param subclassNameObj The Name of the subclass
 * @param prop The property on the superclass to rewrite, if any descendant accesses it.
 * @param namespace The GlobalNamespace containing superclassNameObj
 */
private boolean rewriteAllSubclassInheritedAccesses(Name superclassNameObj, Name subclassNameObj, Name prop, GlobalNamespace namespace) {
    Ref propDeclRef = prop.getDeclaration();
    if (propDeclRef == null || propDeclRef.node == null || !propDeclRef.node.getParent().isAssign()) {
        return false;
    }
    Node propRhs = propDeclRef.node.getParent().getLastChild();
    if (propRhs.isFunction()) {
        return false;
    }
    String subclassQualifiedPropName = subclassNameObj.getFullName() + "." + prop.getBaseName();
    Name subclassPropNameObj = namespace.getOwnSlot(subclassQualifiedPropName);
    // shadows it.
    if (subclassPropNameObj != null && (subclassPropNameObj.localSets > 0 || subclassPropNameObj.globalSets > 0)) {
        return false;
    }
    // Recurse to find potential sub-subclass accesses of the superclass property.
    if (subclassNameObj.subclasses != null) {
        for (Name name : subclassNameObj.subclasses) {
            rewriteAllSubclassInheritedAccesses(superclassNameObj, name, prop, namespace);
        }
    }
    if (subclassPropNameObj != null) {
        Set<AstChange> newNodes = new LinkedHashSet<>();
        // Use this node as a template for rewriteAliasProp.
        Node superclassNameNode = superclassNameObj.getDeclaration().node;
        if (superclassNameNode.isName()) {
            superclassNameNode = superclassNameNode.cloneNode();
        } else if (superclassNameNode.isGetProp()) {
            superclassNameNode = superclassNameNode.cloneTree();
        } else {
            return false;
        }
        rewriteAliasProp(superclassNameNode, 0, newNodes, subclassPropNameObj);
        namespace.scanNewNodes(newNodes);
    }
    return true;
}
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) Name(com.google.javascript.jscomp.GlobalNamespace.Name)

Example 7 with AstChange

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

the class AggressiveInlineAliases method partiallyInlineAlias.

/**
 * Inlines some references to an alias with its value. This handles cases where the alias is not
 * declared at initialization. It does nothing if the alias is reassigned after being initialized,
 * unless the reassignment occurs because of an enclosing function or a loop.
 *
 * @param alias An alias of some variable, which may not be well-defined.
 * @param namespace The GlobalNamespace, which will be updated with all new nodes created.
 * @param aliasRefs All references to the alias in its scope.
 * @param aliasLhsNode The lhs name of the alias when it is first initialized.
 * @return Whether all references to the alias were inlined
 */
private boolean partiallyInlineAlias(Ref alias, GlobalNamespace namespace, ReferenceCollection aliasRefs, Node aliasLhsNode) {
    BasicBlock aliasBlock = null;
    // This is more aggressive than what "inlineAliasIfPossible" does.
    for (Reference aliasRef : aliasRefs) {
        Node aliasRefNode = aliasRef.getNode();
        if (aliasRefNode == aliasLhsNode) {
            aliasBlock = aliasRef.getBasicBlock();
            continue;
        } else if (aliasRef.isLvalue()) {
            // Don't replace any references if the alias is reassigned
            return false;
        }
    }
    Set<AstChange> newNodes = new LinkedHashSet<>();
    boolean alreadySeenInitialAlias = false;
    boolean foundNonReplaceableAlias = false;
    // Do a second iteration through all the alias references, and replace any inlinable references.
    for (Reference aliasRef : aliasRefs) {
        Node aliasRefNode = aliasRef.getNode();
        if (aliasRefNode == aliasLhsNode) {
            alreadySeenInitialAlias = true;
            continue;
        } else if (aliasRef.isDeclaration()) {
            // Ignore any alias declarations, e.g. "var alias;", since there's nothing to inline.
            continue;
        }
        BasicBlock refBlock = aliasRef.getBasicBlock();
        if ((refBlock != aliasBlock && aliasBlock.provablyExecutesBefore(refBlock)) || (refBlock == aliasBlock && alreadySeenInitialAlias)) {
            // We replace the alias only if the alias and reference are in the same BasicBlock,
            // the aliasing assignment takes place before the reference, and the alias is
            // never reassigned.
            codeChanged = true;
            newNodes.add(replaceAliasReference(alias, aliasRef));
        } else {
            foundNonReplaceableAlias = true;
        }
    }
    // We removed all references to the alias, so remove the original aliasing assignment.
    if (!foundNonReplaceableAlias) {
        replaceAliasAssignment(alias, aliasLhsNode);
    }
    if (codeChanged) {
        // Inlining the variable may have introduced new references
        // to descendants of {@code name}. So those need to be collected now.
        namespace.scanNewNodes(newNodes);
    }
    return !foundNonReplaceableAlias;
}
Also used : LinkedHashSet(java.util.LinkedHashSet) AstChange(com.google.javascript.jscomp.GlobalNamespace.AstChange) Node(com.google.javascript.rhino.Node)

Example 8 with AstChange

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

the class DestructuringGlobalNameExtractor method makeNewRvalueForDestructuringKey.

/**
 * Makes a default value expression from the rvalue, or otherwise just returns it
 *
 * <p>e.g. for `const {x = defaultValue} = y;`, and the new rvalue `rvalue`, returns `void 0 ===
 * rvalue ? defaultValue : rvalue`
 */
private static Node makeNewRvalueForDestructuringKey(Node stringKey, Node rvalue, Set<AstChange> newNodes, Ref ref) {
    if (stringKey.getOnlyChild().isDefaultValue()) {
        Node defaultValue = stringKey.getFirstChild().getSecondChild().detach();
        // Assume `rvalue` has no side effects since it's a qname, and we can create multiple
        // references to it. This ignores getters/setters.
        Node rvalueForSheq = rvalue.cloneTree();
        if (newNodes != null) {
            newNodes.add(new AstChange(ref.scope, rvalueForSheq));
        }
        // `void 0 === rvalue ? defaultValue : rvalue`
        rvalue = IR.hook(IR.sheq(NodeUtil.newUndefinedNode(rvalue), rvalueForSheq), defaultValue, rvalue).srcrefTree(defaultValue);
    }
    return rvalue;
}
Also used : AstChange(com.google.javascript.jscomp.GlobalNamespace.AstChange) Node(com.google.javascript.rhino.Node)

Example 9 with AstChange

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

the class GlobalNamespaceTest method rescanningExistingNodesDoesNotCreateDuplicateRefs.

@Test
public void rescanningExistingNodesDoesNotCreateDuplicateRefs() {
    GlobalNamespace namespace = parse("class Foo {} const Bar = Foo; const Baz = Bar;");
    Name foo = namespace.getOwnSlot("Foo");
    Name bar = namespace.getOwnSlot("Bar");
    Name baz = namespace.getOwnSlot("Baz");
    Collection<Ref> originalFooRefs = ImmutableList.copyOf(foo.getRefs());
    Collection<Ref> originalBarRefs = ImmutableList.copyOf(bar.getRefs());
    Collection<Ref> originalBazRefs = ImmutableList.copyOf(baz.getRefs());
    // Rescan all of the nodes for which we got refs as if they were newly added
    Node root = lastCompiler.getJsRoot();
    ImmutableSet.Builder<AstChange> astChangeSetBuilder = ImmutableSet.builder();
    for (Name name : ImmutableList.of(foo, bar, baz)) {
        for (Ref ref : name.getRefs()) {
            astChangeSetBuilder.add(createGlobalAstChangeForNode(root, ref.getNode()));
        }
    }
    namespace.scanNewNodes(astChangeSetBuilder.build());
    // We should get the same Name objects
    assertThat(namespace.getOwnSlot("Foo")).isEqualTo(foo);
    assertThat(namespace.getOwnSlot("Bar")).isEqualTo(bar);
    assertThat(namespace.getOwnSlot("Baz")).isEqualTo(baz);
    // ...and they should contain the same refs with no duplicates added
    assertThat(foo.getRefs()).containsExactlyElementsIn(originalFooRefs).inOrder();
    assertThat(bar.getRefs()).containsExactlyElementsIn(originalBarRefs).inOrder();
    assertThat(baz.getRefs()).containsExactlyElementsIn(originalBazRefs).inOrder();
}
Also used : Ref(com.google.javascript.jscomp.GlobalNamespace.Ref) ImmutableSet(com.google.common.collect.ImmutableSet) AstChange(com.google.javascript.jscomp.GlobalNamespace.AstChange) Node(com.google.javascript.rhino.Node) NodeSubject.assertNode(com.google.javascript.rhino.testing.NodeSubject.assertNode) Name(com.google.javascript.jscomp.GlobalNamespace.Name) Test(org.junit.Test)

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