Skip to content

Commit

Permalink
Rework hoisting and goog.require/provide insertion order so nodes can…
Browse files Browse the repository at this point in the history
… blindly be inserted at the top of a scope.

Closes #2350

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=151178910
  • Loading branch information
ChadKillingsworth authored and Tyler Breisacher committed Mar 27, 2017
1 parent d414fc4 commit e68fcaa
Showing 1 changed file with 62 additions and 70 deletions.
132 changes: 62 additions & 70 deletions src/com/google/javascript/jscomp/ProcessCommonJSModules.java
Expand Up @@ -95,7 +95,6 @@ public void process(Node externs, Node root) {
NodeTraversal.traverseEs6(compiler, root, finder); NodeTraversal.traverseEs6(compiler, root, finder);


ImmutableList.Builder<ExportInfo> exports = ImmutableList.builder(); ImmutableList.Builder<ExportInfo> exports = ImmutableList.builder();
Node hoistInsertionReference = null;
if (finder.isCommonJsModule()) { if (finder.isCommonJsModule()) {
finder.reportModuleErrors(); finder.reportModuleErrors();


Expand All @@ -122,14 +121,14 @@ public void process(Node externs, Node root) {
} }
} }


hoistInsertionReference = finder.addGoogProvide(); finder.initializeModule();
compiler.reportCodeChange(); compiler.reportCodeChange();
} }


NodeTraversal.traverseEs6( NodeTraversal.traverseEs6(
compiler, compiler, root, new RewriteModule(finder.isCommonJsModule(), exports.build()));
root,
new RewriteModule(finder.isCommonJsModule(), exports.build(), hoistInsertionReference)); finder.addGoogProvideAndRequires();
} }


/** /**
Expand Down Expand Up @@ -231,28 +230,9 @@ private boolean removeIIFEWrapper(Node root) {
return true; return true;
} }


/**
* Given a scope root, return an insertion reference after any goog.require or goot.provide
* functions.
*/
private static Node getScopeInsertionPoint(Node scopeRoot, Node startPoint) {
Node insertionPoint = startPoint;
for (Node next = startPoint != null ? startPoint.getNext() : scopeRoot.getFirstChild();
next != null
&& next.getFirstChild().isCall()
&& (next.getFirstFirstChild().matchesQualifiedName("goog.require")
|| next.getFirstFirstChild().matchesQualifiedName("goog.provide"));
next = next.getNext()) {
insertionPoint = next;
}

return insertionPoint;
}

/** /**
* Traverse the script. Find all references to CommonJS require (import) and module.exports or * Traverse the script. Find all references to CommonJS require (import) and module.exports or
* export statements. Add goog.require statements for any require statements. Rewrites any require * export statements. Rewrites any require calls to reference the rewritten module name.
* calls to reference the rewritten module name.
*/ */
class FindImportsAndExports implements NodeTraversal.Callback { class FindImportsAndExports implements NodeTraversal.Callback {
private boolean hasGoogProvideOrModule = false; private boolean hasGoogProvideOrModule = false;
Expand Down Expand Up @@ -399,17 +379,7 @@ private void visitRequireCall(NodeTraversal t, Node require, Node parent) {
parent.getParent().removeChild(parent); parent.getParent().removeChild(parent);
} }


if (imports.add(moduleName)) { imports.add(moduleName);
if (reportDependencies) {
t.getInput().addRequire(moduleName);
}
this.script.addChildToFront(
IR.exprResult(
IR.call(
IR.getprop(IR.name("goog"), IR.string("require")), IR.string(moduleName)))
.useSourceInfoIfMissingFromForTree(require));
compiler.reportCodeChange();
}
} }


/** /**
Expand Down Expand Up @@ -481,33 +451,21 @@ void reportModuleErrors() {
} }


/** /**
* Add a goog.provide statement for the module. If the export is directly assigned more than * If the export is directly assigned more than once, or the assignments are not global, declare
* once, or the assignments are not global, declare the module name variable. * the module name variable.
* *
* <p>If all of the assignments are simply property assignments, initialize the module name * <p>If all of the assignments are simply property assignments, initialize the module name
* variable as a namespace. * variable as a namespace.
*
* <p>Returns a node reference after which hoisted functions within the module should be
* inserted.
*/ */
Node addGoogProvide() { void initializeModule() {
CompilerInput ci = compiler.getInput(this.script.getInputId()); CompilerInput ci = compiler.getInput(this.script.getInputId());
ModulePath modulePath = ci.getPath(); ModulePath modulePath = ci.getPath();
if (modulePath == null) { if (modulePath == null) {
return null; return;
} }


String moduleName = modulePath.toModuleName(); String moduleName = modulePath.toModuleName();


// Add goog.provide calls.
if (reportDependencies) {
ci.addProvide(moduleName);
}
this.script.addChildToFront(
IR.exprResult(
IR.call(IR.getprop(IR.name("goog"), IR.string("provide")), IR.string(moduleName)))
.useSourceInfoIfMissingFromForTree(this.script));

// The default declaration for the goog.provide is a constant so // The default declaration for the goog.provide is a constant so
// we need to declare the variable if we have more than one // we need to declare the variable if we have more than one
// assignment to module.exports or those assignments are not // assignment to module.exports or those assignments are not
Expand Down Expand Up @@ -553,16 +511,50 @@ Node addGoogProvide() {
} }
initModule.useSourceInfoIfMissingFromForTree(this.script); initModule.useSourceInfoIfMissingFromForTree(this.script);


Node refChild = getScopeInsertionPoint(this.script, null); this.script.addChildToFront(initModule);
if (refChild == null) { }
this.script.addChildToFront(initModule); }
} else {
this.script.addChildAfter(initModule, refChild); /**
* Add goog.require statements for any require statements and a goog.provide statement for the
* module
*/
void addGoogProvideAndRequires() {
CompilerInput ci = compiler.getInput(this.script.getInputId());
ModulePath modulePath = ci.getPath();
if (modulePath == null) {
return;
}

String moduleName = modulePath.toModuleName();

for (String importName : imports) {
// Add goog.provide calls.
if (reportDependencies) {
ci.addRequire(importName);
} }
return initModule;
this.script.addChildToFront(
IR.exprResult(
IR.call(
IR.getprop(IR.name("goog"), IR.string("require")), IR.string(importName)))
.useSourceInfoIfMissingFromForTree(this.script));
} }


return script.getFirstChild(); if (isCommonJsModule()) {
// Add goog.provide calls.
if (reportDependencies) {
ci.addProvide(moduleName);
}
this.script.addChildToFront(
IR.exprResult(
IR.call(
IR.getprop(IR.name("goog"), IR.string("provide")), IR.string(moduleName)))
.useSourceInfoIfMissingFromForTree(this.script));
compiler.reportCodeChange();
} else if (imports.size() > 0) {
compiler.reportCodeChange();
}
} }


/** Find the outermost if node ancestor for a node without leaving the function scope */ /** Find the outermost if node ancestor for a node without leaving the function scope */
Expand Down Expand Up @@ -638,15 +630,10 @@ private class RewriteModule extends AbstractPostOrderCallback {
private final List<Node> imports = new ArrayList<>(); private final List<Node> imports = new ArrayList<>();
private final List<Node> rewrittenClassExpressions = new ArrayList<>(); private final List<Node> rewrittenClassExpressions = new ArrayList<>();
private final List<Node> functionsToHoist = new ArrayList<>(); private final List<Node> functionsToHoist = new ArrayList<>();
private final Node hoistInsertionPoint;


public RewriteModule( public RewriteModule(boolean allowFullRewrite, ImmutableCollection<ExportInfo> exports) {
boolean allowFullRewrite,
ImmutableCollection<ExportInfo> exports,
Node hoistInsertionPoint) {
this.allowFullRewrite = allowFullRewrite; this.allowFullRewrite = allowFullRewrite;
this.exports = exports; this.exports = exports;
this.hoistInsertionPoint = hoistInsertionPoint;
} }


@Override @Override
Expand All @@ -661,17 +648,22 @@ public void visit(NodeTraversal t, Node n, Node parent) {
compiler.reportCodeChange(); compiler.reportCodeChange();
} }


CompilerInput ci = compiler.getInput(n.getInputId());
ModulePath modulePath = ci.getPath();
String moduleName = modulePath.toModuleName();

// Hoist functions in reverse order so that they maintain the same relative // Hoist functions in reverse order so that they maintain the same relative
// order after hoisting. // order after hoisting.
for (int i = functionsToHoist.size() - 1; i >= 0; i--) { for (int i = functionsToHoist.size() - 1; i >= 0; i--) {
Node functionExpr = functionsToHoist.get(i); Node functionExpr = functionsToHoist.get(i);
Node scopeRoot = t.getClosestHoistScope().getRootNode(); Node scopeRoot = t.getClosestHoistScope().getRootNode();
Node insertionRef = null; Node insertionPoint = scopeRoot.getFirstChild();
if (hoistInsertionPoint != null if (insertionPoint == null
&& t.getEnclosingFunction() == NodeUtil.getEnclosingFunction(hoistInsertionPoint)) { || !(insertionPoint.isVar()
insertionRef = hoistInsertionPoint; && insertionPoint.getFirstChild().getString().equals(moduleName))) {
insertionPoint = null;
} }
Node insertionPoint = getScopeInsertionPoint(scopeRoot, insertionRef);
if (insertionPoint == null) { if (insertionPoint == null) {
if (scopeRoot.getFirstChild() != functionExpr) { if (scopeRoot.getFirstChild() != functionExpr) {
scopeRoot.addChildToFront(functionExpr.detach()); scopeRoot.addChildToFront(functionExpr.detach());
Expand Down

0 comments on commit e68fcaa

Please sign in to comment.