Skip to content

Commit

Permalink
Use AST-based dependency finder when a file is already parsed.
Browse files Browse the repository at this point in the history
Also, add support for ES6 modules/goog.module to the AST-based dependency
parser.

This is a rollforward of [] with fix to ensure that moduleLoader is
always initialized, even if parseInputs is not called.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=163498759
  • Loading branch information
blickly committed Jul 31, 2017
1 parent 9fc3fe4 commit c36fe0f
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 22 deletions.
2 changes: 2 additions & 0 deletions src/com/google/javascript/jscomp/Compiler.java
Expand Up @@ -356,6 +356,8 @@ public void initOptions(CompilerOptions options) {
}
}

moduleLoader = ModuleLoader.EMPTY;

reconcileOptionsWithGuards();

// TODO(johnlenz): generally, the compiler should not be changing the options object
Expand Down
117 changes: 96 additions & 21 deletions src/com/google/javascript/jscomp/CompilerInput.java
Expand Up @@ -26,6 +26,7 @@
import com.google.common.collect.ImmutableSet;
import com.google.javascript.jscomp.deps.DependencyInfo;
import com.google.javascript.jscomp.deps.JsFileParser;
import com.google.javascript.jscomp.deps.ModuleLoader;
import com.google.javascript.jscomp.deps.ModuleLoader.ModulePath;
import com.google.javascript.jscomp.deps.SimpleDependencyInfo;
import com.google.javascript.jscomp.parsing.parser.FeatureSet;
Expand Down Expand Up @@ -231,7 +232,7 @@ private DependencyInfo generateDependencyInfo() {

// If the code is a JsAst, then it was originally JS code, and is compatible with the
// regex-based parsing of JsFileParser.
if (ast instanceof JsAst && JsFileParser.isSupported()) {
if (ast instanceof JsAst && !((JsAst) ast).isParsed() && JsFileParser.isSupported()) {
// Look at the source code.
// Note: it's OK to use getName() instead of
// getPathRelativeToClosureBase() here because we're not using
Expand All @@ -251,7 +252,7 @@ private DependencyInfo generateDependencyInfo() {
} else {
// Otherwise, just look at the AST.

DepsFinder finder = new DepsFinder(compiler.getCodingConvention());
DepsFinder finder = new DepsFinder(getPath());
Node root = getAstRoot(compiler);
if (root == null) {
return SimpleDependencyInfo.EMPTY;
Expand All @@ -277,10 +278,10 @@ private static class DepsFinder {
private final Map<String, String> loadFlags = new TreeMap<>();
private final List<String> provides = new ArrayList<>();
private final List<String> requires = new ArrayList<>();
private final CodingConvention codingConvention;
private final ModulePath modulePath;

DepsFinder(CodingConvention codingConvention) {
this.codingConvention = codingConvention;
DepsFinder(ModulePath modulePath) {
this.modulePath = modulePath;
}

void visitTree(Node n) {
Expand All @@ -300,34 +301,109 @@ void visitTree(Node n) {
void visitSubtree(Node n, Node parent) {
switch (n.getToken()) {
case CALL:
boolean isModuleDetected = codingConvention.extractIsModuleFile(n, parent);

if (isModuleDetected) {
loadFlags.put("module", "goog");
if (n.hasTwoChildren()
&& n.getFirstChild().isGetProp()
&& n.getFirstFirstChild().matchesQualifiedName("goog")) {

if (!requires.contains("goog")) {
requires.add("goog");
}

Node callee = n.getFirstChild();
Node argument = n.getLastChild();
switch (callee.getLastChild().getString()) {

case "module":
loadFlags.put("module", "goog");
// Fall-through
case "provide":
if (!argument.isString()) {
return;
}
provides.add(argument.getString());
return;

case "require":
if (!argument.isString()) {
return;
}
requires.add(argument.getString());
return;

case "loadModule":
// Process the block of the loadModule argument
n = argument.getLastChild();
break;

default:
return;
}
}
break;

String require = codingConvention.extractClassNameIfRequire(n, parent);
if (require != null) {
requires.add(require);
case MODULE_BODY:
if (!parent.getBooleanProp(Node.GOOG_MODULE)) {
provides.add(modulePath.toModuleName());
loadFlags.put("module", "es6");
}
break;

case IMPORT:
visitEs6ModuleName(n.getLastChild(), n);
return;

String provide = codingConvention.extractClassNameIfProvide(n, parent);
if (provide != null) {
provides.add(provide);
case EXPORT:
if (NodeUtil.isExportFrom(n)) {
visitEs6ModuleName(n.getLastChild(), n);
}
return;
default:
if (parent != null && !parent.isExprResult() && !NodeUtil.isTopLevel(parent)) {
return;

case VAR:
if (n.getFirstChild().matchesQualifiedName("goog")
&& NodeUtil.isNamespaceDecl(n.getFirstChild())) {
provides.add("goog");
}
break;

case EXPR_RESULT:
case CONST:
case BLOCK:
case SCRIPT:
case NAME:
case DESTRUCTURING_LHS:
break;

default:
return;
}

for (Node child = n.getFirstChild();
child != null; child = child.getNext()) {
visitSubtree(child, n);
}
}

void visitEs6ModuleName(Node n, Node parent) {
checkArgument(n.isString());
checkArgument(parent.isExport() || parent.isImport());

// TODO(blickly): Move this (and the duplicated logic in JsFileParser/Es6RewriteModules)
// into ModuleLoader.
String moduleName = n.getString();
if (moduleName.startsWith("goog:")) {
requires.add(moduleName.substring(5)); // cut off the "goog:" prefix
return;
}
ModulePath importedModule =
modulePath.resolveJsModule(
moduleName, modulePath.toString(), n.getLineno(), n.getCharno());

if (importedModule == null) {
importedModule = modulePath.resolveModuleAsPath(moduleName);
}

requires.add(importedModule.toModuleName());
}
}

public String getCode() throws IOException {
Expand Down Expand Up @@ -395,9 +471,8 @@ private static <T> Set<T> concat(Iterable<T> first, Iterable<T> second) {

ModulePath getPath() {
if (modulePath == null) {
// Note: this method will not be called until Es6RewriteModules
// (and similar), after Compiler.moduleLoader is already set.
this.modulePath = compiler.getModuleLoader().resolve(getName());
ModuleLoader moduleLoader = compiler.getModuleLoader();
this.modulePath = moduleLoader.resolve(getName());
}
return modulePath;
}
Expand Down
6 changes: 5 additions & 1 deletion src/com/google/javascript/jscomp/JsAst.java
Expand Up @@ -50,7 +50,7 @@ public JsAst(SourceFile sourceFile) {

@Override
public Node getAstRoot(AbstractCompiler compiler) {
if (root == null) {
if (!isParsed()) {
parse(compiler);
root.setInputId(inputId);
}
Expand Down Expand Up @@ -139,6 +139,10 @@ public void error(String message, String sourceName, int line, int lineOffset) {
}
}

boolean isParsed() {
return root != null;
}

private void parse(AbstractCompiler compiler) {
RecordingReporterProxy reporter = new RecordingReporterProxy(
compiler.getDefaultErrorReporter());
Expand Down
8 changes: 8 additions & 0 deletions src/com/google/javascript/jscomp/NodeUtil.java
Expand Up @@ -4160,6 +4160,14 @@ public static void visitPostOrder(
visitor.visit(node);
}

/**
* @return Whether an EXPORT node has a from clause.
*/
static boolean isExportFrom(Node n) {
checkArgument(n.isExport());
return n.hasTwoChildren();
}

/**
* @return Whether a TRY node has a finally block.
*/
Expand Down

0 comments on commit c36fe0f

Please sign in to comment.