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