Skip to content

Commit

Permalink
Refactor RescopeGlobalSymbols to move the externs rewriting to the sa…
Browse files Browse the repository at this point in the history
…me traversal as the source-variable rewriting.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=152212804
  • Loading branch information
dimvar authored and Tyler Breisacher committed Apr 6, 2017
1 parent a618d06 commit 0b33297
Showing 1 changed file with 57 additions and 71 deletions.
128 changes: 57 additions & 71 deletions src/com/google/javascript/jscomp/RescopeGlobalSymbols.java
Expand Up @@ -130,6 +130,14 @@ private boolean isCrossModuleName(String name) {
|| compiler.getCodingConvention().isExported(name, false); || compiler.getCodingConvention().isExported(name, false);
} }


private static boolean isExternVar(String varname, NodeTraversal t) {
if (varname.isEmpty()) {
return false;
}
Var v = t.getScope().getVar(varname);
return v == null || v.isExtern();
}

private void addExternForGlobalSymbolNamespace() { private void addExternForGlobalSymbolNamespace() {
Node varNode = IR.var(IR.name(globalSymbolNamespace)); Node varNode = IR.var(IR.name(globalSymbolNamespace));
CompilerInput input = compiler.getSynthesizedExternsInput(); CompilerInput input = compiler.getSynthesizedExternsInput();
Expand All @@ -143,36 +151,32 @@ public void process(Node externs, Node root) {
if (addExtern) { if (addExtern) {
addExternForGlobalSymbolNamespace(); addExternForGlobalSymbolNamespace();
} }
// Rewrite all references to global symbols to properties of a
// single symbol by: // Rewrite all references to global symbols to properties of a single symbol:
// (If necessary the 4 traversals could be combined. They are left
// separate for readability reasons.) // Turn global named function statements into var assignments.
// 1. turning global named function statements into var assignments.
NodeTraversal.traverseEs6( NodeTraversal.traverseEs6(
compiler, compiler,
root, root,
new RewriteGlobalFunctionStatementsToVarAssignmentsCallback()); new RewriteGlobalFunctionStatementsToVarAssignmentsCallback());
// 2. find global names that are used in more than one module. Those that
// are have to be rewritten. // Find global names that are used in more than one module. Those that
// are have to be rewritten.
List<Callback> nonMutatingPasses = new ArrayList<>(); List<Callback> nonMutatingPasses = new ArrayList<>();
nonMutatingPasses.add(new FindCrossModuleNamesCallback()); nonMutatingPasses.add(new FindCrossModuleNamesCallback());
// and find names that may refering functions that reference this.
// And find names that may refer to functions that reference this.
nonMutatingPasses.add(new FindNamesReferencingThis()); nonMutatingPasses.add(new FindNamesReferencingThis());
CombinedCompilerPass.traverse(compiler, root, nonMutatingPasses); CombinedCompilerPass.traverse(compiler, root, nonMutatingPasses);
// 3. rewriting all references to be property accesses of the single symbol.
// Rewrite all references to be property accesses of the single symbol.
RewriteScopeCallback rewriteScope = new RewriteScopeCallback(); RewriteScopeCallback rewriteScope = new RewriteScopeCallback();
NodeTraversal.traverseEs6(compiler, root, rewriteScope); NodeTraversal.traverseEs6(compiler, root, rewriteScope);
// 4. removing the var from statements in global scope if the declared names
// have been rewritten in the previous pass. // Remove the var from statements in global scope if the declared names have been rewritten
// in the previous pass.
NodeTraversal.traverseEs6(compiler, root, new RemoveGlobalVarCallback()); NodeTraversal.traverseEs6(compiler, root, new RemoveGlobalVarCallback());
rewriteScope.declareModuleGlobals(); rewriteScope.declareModuleGlobals();

// Extra pass which makes all extern global symbols reference window
// explicitly.
NodeTraversal.traverseEs6(
compiler,
root,
new MakeExternsReferenceWindowExplicitly());
} }


/** /**
Expand Down Expand Up @@ -287,8 +291,8 @@ public void visit(NodeTraversal t, Node n, Node parent) {


/** /**
* Visits each NAME token and checks whether it refers to a global variable. * Visits each NAME token and checks whether it refers to a global variable.
* If yes, rewrites the name to be a property access on the * If yes, rewrites the name to be a property access on the "globalSymbolNamespace".
* "globalSymbolNamespace". * If the NAME is an extern variable, it becomes a property access on window.
* *
* <pre>var a = 1, b = 2, c = 3;</pre> * <pre>var a = 1, b = 2, c = 3;</pre>
* becomes * becomes
Expand All @@ -302,11 +306,9 @@ public void visit(NodeTraversal t, Node n, Node parent) {
* <pre>a()</pre> * <pre>a()</pre>
* becomes * becomes
* <pre>(0,NS.a)()</pre> * <pre>(0,NS.a)()</pre>
* Notice the special syntax here to preserve the *this* semantics in the * Notice the special syntax here to preserve the *this* semantics in the function call.
* function call.
*/ */
private class RewriteScopeCallback extends AbstractPostOrderCallback { private class RewriteScopeCallback extends AbstractPostOrderCallback {

List<ModuleGlobal> preDeclarations = new ArrayList<>(); List<ModuleGlobal> preDeclarations = new ArrayList<>();


@Override @Override
Expand All @@ -319,22 +321,18 @@ public void visit(NodeTraversal t, Node n, Node parent) {
if (parent.isFunction() && name.isEmpty()) { if (parent.isFunction() && name.isEmpty()) {
return; return;
} }
Var var = t.getScope().getVar(name); if (isExternVar(name, t)) {
if (var == null) { visitExtern(n, parent);
return;
}
// Don't touch externs.
if (var.isExtern()) {
return; return;
} }
// When the globalSymbolNamespace is used as a local variable name // When the globalSymbolNamespace is used as a local variable name
// add suffix to avoid shadowing the namespace. Also add a suffix // add suffix to avoid shadowing the namespace. Also add a suffix
// if a name starts with the name of the globalSymbolNamespace and // if a name starts with the name of the globalSymbolNamespace and
// the suffix. // the suffix.
if (!var.isExtern() && !var.isGlobal() Var var = t.getScope().getVar(name);
if (!var.isGlobal()
&& (name.equals(globalSymbolNamespace) && (name.equals(globalSymbolNamespace)
|| name.startsWith( || name.startsWith(globalSymbolNamespace + DISAMBIGUATION_SUFFIX))) {
globalSymbolNamespace + DISAMBIGUATION_SUFFIX))) {
n.setString(name + DISAMBIGUATION_SUFFIX); n.setString(name + DISAMBIGUATION_SUFFIX);
compiler.reportCodeChange(); compiler.reportCodeChange();
} }
Expand All @@ -344,8 +342,7 @@ public void visit(NodeTraversal t, Node n, Node parent) {
} }
Node nameNode = var.getNameNode(); Node nameNode = var.getNameNode();
// The exception variable (e in try{}catch(e){}) should not be rewritten. // The exception variable (e in try{}catch(e){}) should not be rewritten.
if (nameNode != null && nameNode.getParent() != null if (nameNode != null && nameNode.getParent() != null && nameNode.getParent().isCatch()) {
&& nameNode.getParent().isCatch()) {
return; return;
} }
replaceSymbol(n, name, t.getInput()); replaceSymbol(n, name, t.getInput());
Expand All @@ -360,18 +357,15 @@ private void replaceSymbol(Node node, String name, CompilerInput input) {
if (!parent.isVar()) { if (!parent.isVar()) {
return; return;
} }
// If it is a var declaration, but no cross module names are declared boolean hasInterestingChildren = false;
// we also don't have to do anything.
boolean hasCrossModuleChildren = false;
for (Node c : parent.children()) { for (Node c : parent.children()) {
// Var child is no longer a name means it was transformed already // VAR child is no longer a name means it was transformed already.
// which means there was a cross module name.
if (!c.isName() || isCrossModuleName(c.getString())) { if (!c.isName() || isCrossModuleName(c.getString())) {
hasCrossModuleChildren = true; hasInterestingChildren = true;
break; break;
} }
} }
if (!hasCrossModuleChildren) { if (!hasInterestingChildren) {
return; return;
} }
} }
Expand Down Expand Up @@ -411,6 +405,27 @@ private void replaceSymbol(Node node, String name, CompilerInput input) {
compiler.reportCodeChange(); compiler.reportCodeChange();
} }


/**
* Rewrites extern names to be explicit children of window instead of only implicitly
* referencing it. This enables injecting window into a scope and make all global symbols
* depend on the injected object.
*/
private void visitExtern(Node nameNode, Node parent) {
String name = nameNode.getString();
if (globalSymbolNamespace.equals(name) || SPECIAL_EXTERNS.contains(name)) {
return;
}
Node windowPropAccess = IR.getprop(IR.name(WINDOW), IR.string(name));
if (parent.isVar() && nameNode.hasOneChild()) {
Node assign = IR.assign(windowPropAccess, nameNode.getFirstChild().detachFromParent());
assign.setJSDocInfo(parent.getJSDocInfo());
parent.replaceChild(nameNode, assign.srcrefTree(parent));
} else {
parent.replaceChild(nameNode, windowPropAccess.srcrefTree(nameNode));
}
compiler.reportCodeChange();
}

/** /**
* Adds back declarations for variables that do not cross module boundaries. * Adds back declarations for variables that do not cross module boundaries.
* Must be called after RemoveGlobalVarCallback. * Must be called after RemoveGlobalVarCallback.
Expand All @@ -421,8 +436,7 @@ void declareModuleGlobals() {
&& global.root.getFirstChild().isVar()) { && global.root.getFirstChild().isVar()) {
global.root.getFirstChild().addChildToBack(global.name); global.root.getFirstChild().addChildToBack(global.name);
} else { } else {
global.root.addChildToFront( global.root.addChildToFront(IR.var(global.name).srcref(global.name));
IR.var(global.name).srcref(global.name));
} }
compiler.reportCodeChange(); compiler.reportCodeChange();
} }
Expand Down Expand Up @@ -515,32 +529,4 @@ private Node joinOnComma(List<Node> commas, Node source) {
return comma; return comma;
} }
} }

/**
* Rewrites extern names to be explicit children of window instead of only
* implicitly referencing it.
* This enables injecting window into a scope and make all global symbol
* depend on the injected object.
*/
private class MakeExternsReferenceWindowExplicitly extends
AbstractPostOrderCallback {

@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (!n.isName()) {
return;
}
String name = n.getString();
if (globalSymbolNamespace.equals(name)
|| SPECIAL_EXTERNS.contains(name)) {
return;
}
Var var = t.getScope().getVar(name);
if (name.length() > 0 && (var == null || var.isExtern())) {
parent.replaceChild(n, IR.getprop(IR.name(WINDOW), IR.string(name))
.srcrefTree(n));
compiler.reportCodeChange();
}
}
}
} }

0 comments on commit 0b33297

Please sign in to comment.