Skip to content

Commit

Permalink
Add support for avoiding aliases in the exports.name = name export …
Browse files Browse the repository at this point in the history
…format.

This is the (hopefully) last step required before we can remove the inlineAliases pass.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=140792942
  • Loading branch information
blickly committed Dec 2, 2016
1 parent cbf6153 commit c86cf1e
Show file tree
Hide file tree
Showing 3 changed files with 297 additions and 82 deletions.
164 changes: 107 additions & 57 deletions src/com/google/javascript/jscomp/ClosureRewriteModule.java
Expand Up @@ -215,6 +215,8 @@ private static final class UnrecognizedRequire {
}

private static final class ExportDefinition {
// Null if the export is a default export (exports = expr)
@Nullable String exportName;
// Null if the export is of a @typedef
@Nullable Node rhs;
// Null if the export is of anything other than a name
Expand All @@ -223,15 +225,27 @@ private static final class ExportDefinition {
private static final Set<Token> INLINABLE_NAME_PARENTS =
ImmutableSet.of(Token.VAR, Token.CONST, Token.LET, Token.FUNCTION, Token.CLASS);

static ExportDefinition fromRhs(NodeTraversal t, Node rhs) {
static ExportDefinition newDefaultExport(NodeTraversal t, Node rhs) {
return newNamedExport(t, null, rhs);
}

static ExportDefinition newNamedExport(NodeTraversal t, String name, Node rhs) {
ExportDefinition newExport = new ExportDefinition();
newExport.exportName = name;
newExport.rhs = rhs;
if (rhs != null && (rhs.isName() || rhs.isStringKey())) {
newExport.nameDecl = t.getScope().getVar(rhs.getString());
}
return newExport;
}

String getExportPostfix() {
if (exportName == null) {
return "";
}
return "." + exportName;
}

boolean hasInlinableName() {
if (nameDecl == null
|| !INLINABLE_NAME_PARENTS.contains(nameDecl.getParentNode().getToken())) {
Expand Down Expand Up @@ -271,6 +285,7 @@ private static final class ScriptDescription {
Node defaultExportRhs;
String defaultExportLocalName;
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
Expand Down Expand Up @@ -844,22 +859,39 @@ private void maybeRecordExportDeclaration(NodeTraversal t, Node n) {
}

Preconditions.checkState(currentScript.defaultExportRhs == null);
currentScript.willCreateExportsObject = true;
Node exportRhs = n.getNext();
if (isNamedExportsLiteral(exportRhs)) {
boolean areAllExportsInlinable = true;
List<ExportDefinition> inlinableExports = new ArrayList<>();
for (Node key = exportRhs.getFirstChild(); key != null; key = key.getNext()) {
String exportName = key.getString();
Node rhs = key.hasChildren() ? key.getFirstChild() : key;
ExportDefinition namedExport = ExportDefinition.newNamedExport(t, exportName, rhs);
currentScript.namedExports.add(exportName);
if (currentScript.declareLegacyNamespace || !namedExport.hasInlinableName()) {
areAllExportsInlinable = false;
} else {
inlinableExports.add(namedExport);
}
}
if (areAllExportsInlinable) {
for (ExportDefinition export : inlinableExports) {
recordExportToInline(export);
}
NodeUtil.removeChild(n.getParent().getParent(), n.getParent());
} else {
currentScript.willCreateExportsObject = true;
}
return;
}

currentScript.defaultExportRhs = exportRhs;
ExportDefinition defaultExport = ExportDefinition.fromRhs(t, exportRhs);
currentScript.willCreateExportsObject = true;
ExportDefinition defaultExport = ExportDefinition.newDefaultExport(t, exportRhs);
if (!currentScript.declareLegacyNamespace && defaultExport.hasInlinableName()) {
String localName = defaultExport.getLocalName();
currentScript.defaultExportLocalName = localName;
recordNameToInline(localName, currentScript.getBinaryNamespace());
recordExportToInline(defaultExport);
}

return;
Expand Down Expand Up @@ -1091,6 +1123,14 @@ private void recordExportsPropertyAssignment(NodeTraversal t, Node getpropNode)
if (t.inModuleScope() || t.inGlobalScope()) {
String exportName = getpropNode.getLastChild().getString();
currentScript.namedExports.add(exportName);
Node exportRhs = getpropNode.getNext();
ExportDefinition namedExport = ExportDefinition.newNamedExport(t, exportName, exportRhs);
if (!currentScript.declareLegacyNamespace
&& currentScript.defaultExportRhs == null
&& namedExport.hasInlinableName()) {
recordExportToInline(namedExport);
parent.getParent().detach();
}
}
}

Expand Down Expand Up @@ -1145,18 +1185,14 @@ private void maybeUpdateTopLevelName(NodeTraversal t, Node nameNode) {
}
}

boolean nameIsExported = name.equals(currentScript.defaultExportLocalName);
if (nameIsExported) {
safeSetString(nameNode, currentScript.getBinaryNamespace());
currentScript.hasCreatedExportObject = true;
return;
}

// If the name is an alias for an imported namespace rewrite from
// "new Foo;" to "new module$exports$Foo;"
boolean nameIsAnAlias = currentScript.namesToInlineByAlias.containsKey(name);
if (nameIsAnAlias && var.getNode() != nameNode) {
String namespaceToInline = currentScript.namesToInlineByAlias.get(name);
if (namespaceToInline.equals(currentScript.getBinaryNamespace())) {
currentScript.hasCreatedExportObject = true;
}
safeSetMaybeQualifiedString(nameNode, namespaceToInline);

// Make sure this action won't shadow a local variable.
Expand Down Expand Up @@ -1314,6 +1350,7 @@ private void updateModuleReturn(Node returnNode) {
}

returnStatementNode.detach();
updateEndModule();
popScript();
}

Expand All @@ -1326,6 +1363,11 @@ private void updateEndScript() {
}

private void updateEndModule() {
for (ExportDefinition export : currentScript.exportsToInline.values()) {
Node nameNode = export.nameDecl.getNameNode();
safeSetMaybeQualifiedString(
nameNode, currentScript.getBinaryNamespace() + export.getExportPostfix());
}
Preconditions.checkState(currentScript.isModule);
Preconditions.checkState(
currentScript.declareLegacyNamespace || currentScript.hasCreatedExportObject);
Expand Down Expand Up @@ -1418,6 +1460,14 @@ private void markConstAndCopyJsDoc(Node from, Node target, Node value) {
target.setJSDocInfo(builder.build());
}

private void recordExportToInline(ExportDefinition exportDefinition) {
currentScript.exportsToInline.put(exportDefinition.nameDecl, exportDefinition);
String localName = exportDefinition.getLocalName();
String fullExportedName =
currentScript.getBinaryNamespace() + exportDefinition.getExportPostfix();
recordNameToInline(localName, fullExportedName);
}

private void recordNameToInline(String aliasName, String legacyNamespace) {
Preconditions.checkNotNull(aliasName);
Preconditions.checkNotNull(legacyNamespace);
Expand Down Expand Up @@ -1471,57 +1521,57 @@ private void reportUnrecognizedRequires() {
private void safeSetString(Node n, String newString) {
String originalName = n.getString();
n.setString(newString);
n.putProp(Node.ORIGINALNAME_PROP, originalName);
if (n.getOriginalName() == null) {
n.setOriginalName(originalName);
}
compiler.reportCodeChange();
}

private void safeSetMaybeQualifiedString(Node nameNode, String newString) {
if (newString.contains(".")) {
// When replacing with a dotted fully qualified name it's already better than an original
// name.
Node nameParent = nameNode.getParent();
JSDocInfo jsdoc = nameParent.getJSDocInfo();
switch (nameParent.getToken()) {
case FUNCTION:
case CLASS:
if (NodeUtil.isStatement(nameParent) && nameParent.getFirstChild() == nameNode) {
Node statementParent = nameParent.getParent();
Node placeholder = IR.empty();
statementParent.replaceChild(nameParent, placeholder);
Node newStatement =
NodeUtil.newQNameDeclaration(compiler, newString, nameParent, jsdoc);
nameParent.setJSDocInfo(null);
newStatement.useSourceInfoIfMissingFromForTree(nameParent);
statementParent.replaceChild(placeholder, newStatement);
NodeUtil.removeName(nameParent);
return;
}
break;
case VAR:
case LET:
case CONST:
{
Node rhs = nameNode.hasChildren() ? nameNode.getLastChild().detach() : null;
Node newStatement = NodeUtil.newQNameDeclaration(compiler, newString, rhs, jsdoc);
newStatement.useSourceInfoIfMissingFromForTree(nameParent);
NodeUtil.replaceDeclarationChild(nameNode, newStatement);
return;
}
case OBJECT_PATTERN:
case ARRAY_PATTERN:
case PARAM_LIST:
throw new RuntimeException("Not supported");
default:
break;
}
Node newQualifiedNameNode = NodeUtil.newQName(compiler, newString);
newQualifiedNameNode.srcrefTree(nameNode);
nameParent.replaceChild(nameNode, newQualifiedNameNode);
} else {
String originalName = nameNode.getString();
nameNode.setString(newString);
nameNode.putProp(Node.ORIGINALNAME_PROP, originalName);
if (!newString.contains(".")) {
safeSetString(nameNode, newString);
return;
}
// When replacing with a dotted fully qualified name it's already better than an original
// name.
Node nameParent = nameNode.getParent();
JSDocInfo jsdoc = nameParent.getJSDocInfo();
switch (nameParent.getToken()) {
case FUNCTION:
case CLASS:
if (NodeUtil.isStatement(nameParent) && nameParent.getFirstChild() == nameNode) {
Node statementParent = nameParent.getParent();
Node placeholder = IR.empty();
statementParent.replaceChild(nameParent, placeholder);
Node newStatement =
NodeUtil.newQNameDeclaration(compiler, newString, nameParent, jsdoc);
nameParent.setJSDocInfo(null);
newStatement.useSourceInfoIfMissingFromForTree(nameParent);
statementParent.replaceChild(placeholder, newStatement);
NodeUtil.removeName(nameParent);
return;
}
break;
case VAR:
case LET:
case CONST:
{
Node rhs = nameNode.hasChildren() ? nameNode.getLastChild().detach() : null;
Node newStatement = NodeUtil.newQNameDeclaration(compiler, newString, rhs, jsdoc);
newStatement.useSourceInfoIfMissingFromForTree(nameParent);
NodeUtil.replaceDeclarationChild(nameNode, newStatement);
return;
}
case OBJECT_PATTERN:
case ARRAY_PATTERN:
case PARAM_LIST:
throw new RuntimeException("Not supported");
default:
break;
}
Node newQualifiedNameNode = NodeUtil.newQName(compiler, newString);
newQualifiedNameNode.srcrefTree(nameNode);
nameParent.replaceChild(nameNode, newQualifiedNameNode);
compiler.reportCodeChange();
}

Expand Down

0 comments on commit c86cf1e

Please sign in to comment.