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;
}
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;
}
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;
}
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();
}
Aggregations