Skip to content

Commit

Permalink
Teach goog.module rewriting pass to understand MODULE_BODY nodes.
Browse files Browse the repository at this point in the history
This is a first step toward treating goog.loadModule wrapped modules more similarly
to full-file goog.modules

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=158264631
  • Loading branch information
blickly authored and brad4d committed Jun 7, 2017
1 parent ed493ed commit f60fddd
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 34 deletions.
70 changes: 37 additions & 33 deletions src/com/google/javascript/jscomp/ClosureRewriteModule.java
Expand Up @@ -24,7 +24,6 @@
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.javascript.jscomp.NodeTraversal.AbstractPreOrderCallback;
import com.google.javascript.jscomp.NodeTraversal.Callback;
import com.google.javascript.rhino.IR;
import com.google.javascript.rhino.JSDocInfo;
Expand Down Expand Up @@ -291,7 +290,7 @@ private static final class ScriptDescription {
String legacyNamespace; // "a.b.c"
String contentsPrefix; // "module$contents$a$b$c_
final Set<String> topLevelNames = new HashSet<>(); // For prefixed content renaming.
final Deque<ScriptDescription> childScripts = new LinkedList<>(); // For goog.loadModule()
final Deque<ScriptDescription> childScripts = new LinkedList<>();
final Map<String, String> namesToInlineByAlias = new HashMap<>(); // For alias inlining.

/**
Expand All @@ -304,9 +303,8 @@ private static final class ScriptDescription {
Set<String> namedExports = new HashSet<>();
Map<Var, ExportDefinition> exportsToInline = new HashMap<>();

// The root of the module. The SCRIPT node (or for goog.loadModule, the body of the
// function) that contains the module contents. For recognizing top level names. Changes when
// unwrapping a goog.loadModule() call.
// The root of the module. The MODULE_BODY node (or for goog.loadModule, the body of the
// function) that contains the module contents. For recognizing top level names.
Node rootNode;

public void addChildScript(ScriptDescription childScript) {
Expand Down Expand Up @@ -334,16 +332,17 @@ String getExportedNamespace() {
}
}

private class ScriptRecorder extends AbstractPreOrderCallback {
private class ScriptRecorder implements Callback {
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
if (NodeUtil.isGoogModuleFile(n)) {
inlineModuleIntoGlobal(n);
t.reportCodeChange();
checkAndSetStrictModeDirective(t, n);
}

switch (n.getToken()) {
case MODULE_BODY:
recordModuleBody(n);
break;
case CALL:
Node method = n.getFirstChild();
if (!method.isGetProp()) {
Expand Down Expand Up @@ -409,15 +408,26 @@ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {

return true;
}

@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (n.isModuleBody()) {
popScript();
}
}
}

private class ScriptUpdater implements Callback {
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
switch (n.getToken()) {
case MODULE_BODY:
updateModuleBodyEarly(n);
break;

case EXPR_RESULT:
if (isGoogLoadModuleStatement(n)) {
updateGoogLoadModuleEarly(n);
updateModuleBodyEarly(n.getFirstChild().getLastChild().getLastChild());
}
break;

Expand Down Expand Up @@ -472,8 +482,8 @@ public void visit(NodeTraversal t, Node n, Node parent) {
}
break;

case SCRIPT:
updateEndScript();
case MODULE_BODY:
updateModuleBody(n);
break;

case NAME:
Expand Down Expand Up @@ -699,9 +709,16 @@ public void hotSwapScript(Node scriptRoot, Node originalRoot) {
}

private void recordGoogLoadModule(Node call) {
Node moduleScopeRoot = call.getLastChild().getLastChild();
Preconditions.checkState(NodeUtil.isModuleScopeRoot(moduleScopeRoot),
"goog.loadModule called with non-module contents: %s", moduleScopeRoot);
recordModuleBody(moduleScopeRoot);
}

private void recordModuleBody(Node moduleRoot) {
pushScript(new ScriptDescription());

currentScript.rootNode = NodeUtil.getEnclosingStatement(call).getParent();
currentScript.rootNode = moduleRoot;
currentScript.isModule = true;
}

Expand All @@ -713,8 +730,6 @@ private void recordGoogModule(NodeTraversal t, Node call) {
}
String legacyNamespace = legacyNamespaceNode.getString();

currentScript.isModule = true;
currentScript.rootNode = NodeUtil.getEnclosingStatement(call).getParent();
currentScript.legacyNamespace = legacyNamespace;
currentScript.contentsPrefix = toModuleContentsPrefix(legacyNamespace);

Expand Down Expand Up @@ -939,18 +954,8 @@ private void recordModuleReturn() {
popScript();
}

static void inlineModuleIntoGlobal(Node scriptNode) {
Preconditions.checkArgument(NodeUtil.isGoogModuleFile(scriptNode));
Node moduleNode = scriptNode.getFirstChild();
scriptNode.removeChild(moduleNode);
scriptNode.addChildrenToBack(moduleNode.removeChildren());
}

private void updateGoogLoadModuleEarly(Node exprResultNode) {
private void updateModuleBodyEarly(Node moduleScopeRoot) {
pushScript(currentScript.removeFirstChildScript());
Node moduleScopeRoot = exprResultNode.getFirstChild().getLastChild().getLastChild();
Preconditions.checkState(NodeUtil.isModuleScopeRoot(moduleScopeRoot),
"goog.loadModule called with non-module contents: %s", moduleScopeRoot);
currentScript.rootNode = moduleScopeRoot;
}

Expand Down Expand Up @@ -1137,9 +1142,7 @@ private void recordExportsPropertyAssignment(NodeTraversal t, Node getpropNode)
Node exportsNameNode = getpropNode.getFirstChild();
Preconditions.checkState(exportsNameNode.getString().equals("exports"));

// Would be just t.inModuleScope() if this ran before the inlineModuleIntoGlobal() call
// that happens at the beginning of module rewriting.
if (t.inModuleScope() || t.inGlobalScope()) {
if (t.inModuleScope()) {
String exportName = getpropNode.getLastChild().getString();
currentScript.namedExports.add(exportName);
Node exportRhs = getpropNode.getNext();
Expand Down Expand Up @@ -1218,7 +1221,7 @@ private void maybeUpdateTopLevelName(NodeTraversal t, Node nameNode) {
if (namespaceToInline.indexOf('.') != -1) {
String firstQualifiedName = namespaceToInline.substring(0, namespaceToInline.indexOf('.'));
Var shadowedVar = t.getScope().getVar(firstQualifiedName);
if (shadowedVar != null && shadowedVar.isLocal()) {
if (shadowedVar != null && !shadowedVar.getScope().isModuleScope()) {
t.report(
shadowedVar.getNode(),
IMPORT_INLINING_SHADOWS_VAR,
Expand Down Expand Up @@ -1374,12 +1377,13 @@ private void updateModuleReturn(Node returnNode) {
popScript();
}

private void updateEndScript() {
if (!currentScript.isModule) {
return;
}
void updateModuleBody(Node moduleBody) {
Preconditions.checkArgument(moduleBody.isModuleBody());
moduleBody.setToken(Token.BLOCK);
NodeUtil.tryMergeBlock(moduleBody);

updateEndModule();
popScript();
}

private void updateEndModule() {
Expand Down
Expand Up @@ -18,6 +18,7 @@
import com.google.common.base.Preconditions;
import com.google.javascript.rhino.IR;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;

/**
* Replicates the effect of {@literal ClosureBundler} in whitespace-only mode and wraps goog.modules
Expand All @@ -44,7 +45,9 @@ public void hotSwapScript(Node scriptRoot, Node originalRoot) {
if (!NodeUtil.isGoogModuleFile(scriptRoot)) {
return;
}
ClosureRewriteModule.inlineModuleIntoGlobal(scriptRoot);
Node moduleBody = scriptRoot.getFirstChild();
moduleBody.setToken(Token.BLOCK);
NodeUtil.tryMergeBlock(moduleBody);
compiler.reportChangeToEnclosingScope(scriptRoot);

// As per ClosureBundler:
Expand Down

0 comments on commit f60fddd

Please sign in to comment.