Skip to content

Commit 6990533

Browse files
lauraharkercopybara-github
authored andcommitted
Use AST-based parser for dependency info in JSCompiler unit tests
This exposes a few small behavioral differences, like module load errors being reported in more tests. This also fixes some crashes in some parsing/deps finding code on malformed goog.modules, that were previously covered up by the regex parser throwing an error before we got to AST-based parsing. A followup change will use the AST-based parser in even more places. PiperOrigin-RevId: 506049938
1 parent 7c89590 commit 6990533

19 files changed

+115
-34
lines changed

src/com/google/javascript/jscomp/AbstractCompiler.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,15 @@ void afterPass(String passName) {}
260260
/** Prints a node to source code. */
261261
public abstract String toSource(Node root);
262262

263+
/**
264+
* Whether to prefer using {@link JSFileRegexParser} when calculating {@link DependencyInfo} as
265+
* opposed to triggering an AST-based parse.
266+
*
267+
* <p>This is primarily for performance reasons. The two should produce the same results except in
268+
* some edge cases.
269+
*/
270+
abstract boolean preferRegexParser();
271+
263272
/** Gets a default error reporter for injecting into Rhino. */
264273
abstract ErrorReporter getDefaultErrorReporter();
265274

src/com/google/javascript/jscomp/Compiler.java

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1979,6 +1979,31 @@ Node parseInputs() {
19791979
}
19801980
}
19811981

1982+
/**
1983+
* This hints to other parts of the compiler as to whether every input will eventually require a
1984+
* full AST parse.
1985+
*
1986+
* <p>NOTE: this is just intended as a hint towards improving performance. Returning true or false
1987+
* shouldn't affect any non-performance behavior.
1988+
*/
1989+
@Override
1990+
boolean preferRegexParser() {
1991+
return preferRegexParser;
1992+
}
1993+
1994+
/**
1995+
* Artificially sets the return value of `preferRegexParser`
1996+
*
1997+
* <p>Non-public and not exposed on AbstractCompiler, as for now we want to maintain control of
1998+
* when the regex vs. AST parser is used.
1999+
*/
2000+
void setPreferRegexParser(boolean preferRegexParser) {
2001+
this.preferRegexParser = preferRegexParser;
2002+
}
2003+
2004+
// TODO(b/264916633): make this default to false.
2005+
private boolean preferRegexParser = true;
2006+
19822007
void orderInputsWithLargeStack() {
19832008
runInCompilerThread(
19842009
() -> {
@@ -2035,8 +2060,8 @@ void orderInputs() {
20352060
/**
20362061
* Find modules by recursively traversing dependencies starting with the entry points.
20372062
*
2038-
* <p>Causes a regex parse of every file, and a full parse of every file reachable from the entry
2039-
* points (which would be required by later compilation passes regardless).
2063+
* <p>Triggers either a regex parse or full AST-based parse of each file in order to gather
2064+
* dependency information.
20402065
*
20412066
* <p>If the dependency mode is set to PRUNE_LEGACY, inputs which the regex parse does not
20422067
* identify as ES modules and which do not contain any provide statements are considered to be

src/com/google/javascript/jscomp/CompilerInput.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ private DependencyInfo generateDependencyInfo() {
341341

342342
// If the code is a JsAst, then it was originally JS code, and is compatible with the
343343
// regex-based parsing of JsFileRegexParser.
344-
if (ast instanceof JsAst && JsFileRegexParser.isSupported()) {
344+
if (ast instanceof JsAst && JsFileRegexParser.isSupported() && compiler.preferRegexParser()) {
345345
// Look at the source code.
346346
// Note: it's OK to use getName() instead of
347347
// getPathRelativeToClosureBase() here because we're not using

src/com/google/javascript/jscomp/GatherModuleMetadata.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,7 @@ private void visitGoogCall(NodeTraversal t, Node n) {
387387
addNamespace(currentModule, ModuleType.GOOG_PROVIDE, namespace, t, n);
388388
} else {
389389
t.report(n, ClosureRewriteModule.INVALID_PROVIDE_NAMESPACE);
390+
currentModule.metadataBuilder.usesClosure(false);
390391
}
391392
} else if (getprop.matchesQualifiedName(GOOG_MODULE)) {
392393
currentModule.moduleType(ModuleType.GOOG_MODULE, t, n);
@@ -395,6 +396,7 @@ private void visitGoogCall(NodeTraversal t, Node n) {
395396
addNamespace(currentModule, ModuleType.GOOG_MODULE, namespace, t, n);
396397
} else {
397398
t.report(n, ClosureRewriteModule.INVALID_MODULE_ID_ARG);
399+
currentModule.metadataBuilder.usesClosure(false);
398400
}
399401
} else if (getprop.matchesQualifiedName(GOOG_MODULE_DECLARELEGACYNAMESPACE)) {
400402
currentModule.recordDeclareLegacyNamespace(n);

src/com/google/javascript/jscomp/modules/ModuleMapCreator.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,13 @@ private void process(ModuleMetadata moduleMetadata) {
255255
break;
256256
case GOOG_MODULE:
257257
case LEGACY_GOOG_MODULE:
258-
processor = closureModuleProcessor;
258+
if (moduleMetadata.googNamespaces().size() == 1) {
259+
processor = closureModuleProcessor;
260+
} else {
261+
// this indicates some malformed Closure module. We should already have reported an
262+
// error.
263+
processor = nonEsModuleProcessor;
264+
}
259265
break;
260266
default:
261267
processor = nonEsModuleProcessor;

src/com/google/javascript/jscomp/parsing/IRFactory.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1309,7 +1309,9 @@ private boolean isGoogModuleFile(Node scriptNode) {
13091309
if (!call.isCall()) {
13101310
return false;
13111311
}
1312-
return GOOG_MODULE.matches(call.getFirstChild());
1312+
return GOOG_MODULE.matches(call.getFirstChild())
1313+
&& call.hasTwoChildren()
1314+
&& call.getSecondChild().isStringLit();
13131315
}
13141316

13151317
/**

test/com/google/javascript/jscomp/ClosureRewriteModuleTest.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import static com.google.javascript.jscomp.ClosureRewriteModule.LOAD_MODULE_FN_MISSING_RETURN;
2727
import static com.google.javascript.jscomp.modules.ModuleMapCreator.DOES_NOT_HAVE_EXPORT_WITH_DETAILS;
2828
import static com.google.javascript.rhino.testing.NodeSubject.assertNode;
29+
import static org.junit.Assert.assertThrows;
2930

3031
import com.google.common.base.Predicates;
3132
import com.google.javascript.jscomp.testing.TestExternsBuilder;
@@ -1092,7 +1093,9 @@ public void testGoogLoadModule_missingReturn() {
10921093

10931094
@Test
10941095
public void testGoogLoadModuleString() {
1095-
testSame("goog.loadModule(\"goog.module('a.b.c'); exports = class {};\");");
1096+
assertThrows(
1097+
IllegalArgumentException.class,
1098+
() -> testSame("goog.loadModule(\"goog.module('a.b.c'); exports = class {};\");"));
10961099
}
10971100

10981101
@Test

test/com/google/javascript/jscomp/CodePrinterTest.java

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.google.javascript.jscomp;
1818

1919
import static com.google.common.truth.Truth.assertThat;
20+
import static com.google.javascript.jscomp.DiagnosticGroups.MODULE_LOAD;
2021
import static com.google.javascript.rhino.testing.NodeSubject.assertNode;
2122
import static java.nio.charset.StandardCharsets.UTF_8;
2223

@@ -3568,19 +3569,21 @@ public void testDeclarations() {
35683569

35693570
@Test
35703571
public void testImports() {
3571-
assertPrintSame("import x from\"foo\"");
3572-
assertPrintSame("import\"foo\"");
3573-
assertPrintSame("import x,{a as b}from\"foo\"");
3574-
assertPrintSame("import{a as b,c as d}from\"foo\"");
3575-
assertPrintSame("import x,{a}from\"foo\"");
3576-
assertPrintSame("import{a,c}from\"foo\"");
3577-
assertPrintSame("import x,*as f from\"foo\"");
3578-
assertPrintSame("import*as f from\"foo\"");
3572+
diagnosticsToIgnore = ImmutableList.of(MODULE_LOAD); // allow importing nonexistent modules
3573+
assertPrintSame("import x from\"./foo\"");
3574+
assertPrintSame("import\"./foo\"");
3575+
assertPrintSame("import x,{a as b}from\"./foo\"");
3576+
assertPrintSame("import{a as b,c as d}from\"./foo\"");
3577+
assertPrintSame("import x,{a}from\"./foo\"");
3578+
assertPrintSame("import{a,c}from\"./foo\"");
3579+
assertPrintSame("import x,*as f from\"./foo\"");
3580+
assertPrintSame("import*as f from\"./foo\"");
35793581
}
35803582

35813583
@Test
35823584
public void testExports() {
35833585
// export declarations
3586+
diagnosticsToIgnore = ImmutableList.of(MODULE_LOAD); // allow importing nonexistent modules
35843587
assertPrintSame("export var x=1");
35853588
assertPrintSame("export var x;export var y");
35863589
assertPrintSame("export let x=1");
@@ -3590,13 +3593,13 @@ public void testExports() {
35903593
assertPrintSame("export class f{}export class b{}");
35913594

35923595
// export all from
3593-
assertPrint("export * from 'a.b.c'", "export*from\"a.b.c\"");
3596+
assertPrint("export * from './a.b.c'", "export*from\"./a.b.c\"");
35943597

35953598
// from
3596-
assertPrintSame("export{a}from\"a.b.c\"");
3597-
assertPrintSame("export{a as x}from\"a.b.c\"");
3598-
assertPrintSame("export{a,b}from\"a.b.c\"");
3599-
assertPrintSame("export{a as x,b as y}from\"a.b.c\"");
3599+
assertPrintSame("export{a}from\"./a.b.c\"");
3600+
assertPrintSame("export{a as x}from\"./a.b.c\"");
3601+
assertPrintSame("export{a,b}from\"./a.b.c\"");
3602+
assertPrintSame("export{a as x,b as y}from\"./a.b.c\"");
36003603
assertPrintSame("export{a}");
36013604
assertPrintSame("export{a as x}");
36023605

test/com/google/javascript/jscomp/CodePrinterTestBase.java

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public abstract class CodePrinterTestBase {
3838
protected LanguageMode languageMode = LanguageMode.ECMASCRIPT5;
3939
protected @Nullable Compiler lastCompiler = null;
4040
protected @Nullable Charset outputCharset = null;
41+
protected ImmutableList<DiagnosticGroup> diagnosticsToIgnore = ImmutableList.of();
4142

4243
@Before
4344
public void setUp() throws Exception {
@@ -48,6 +49,7 @@ public void setUp() throws Exception {
4849
lastCompiler = null;
4950
languageMode = LanguageMode.UNSUPPORTED;
5051
outputCharset = null;
52+
diagnosticsToIgnore = ImmutableList.of();
5153
}
5254

5355
Node parse(String js) {
@@ -66,6 +68,8 @@ Node parse(String js, boolean typeChecked) {
6668
options.setParseJsDocDocumentation(Config.JsDocParsing.INCLUDE_ALL_COMMENTS);
6769
options.setPreserveNonJSDocComments(true);
6870
}
71+
options.setContinueAfterErrors(true);
72+
compiler.setPreferRegexParser(false);
6973
compiler.init(
7074
ImmutableList.of(SourceFile.fromCode("externs", CompilerTestCase.MINIMAL_EXTERNS)),
7175
ImmutableList.of(SourceFile.fromCode("testcode", js)),
@@ -87,21 +91,25 @@ Node parse(String js, boolean typeChecked) {
8791

8892
private void checkUnexpectedErrorsOrWarnings(
8993
Compiler compiler, int expected) {
90-
int actual = compiler.getErrors().size();
91-
if (!allowWarnings) {
92-
actual += compiler.getWarnings().size();
93-
}
94-
95-
if (actual != expected) {
94+
int actual = 0;
9695
String msg = "";
9796
for (JSError err : compiler.getErrors()) {
97+
if (shouldIgnore(err)) {
98+
continue;
99+
}
100+
actual++;
98101
msg += "Error:" + err + "\n";
99102
}
100103
if (!allowWarnings) {
101104
for (JSError err : compiler.getWarnings()) {
105+
if (shouldIgnore(err)) {
106+
continue;
107+
}
108+
actual++;
102109
msg += "Warning:" + err + "\n";
103110
}
104111
}
112+
if (actual != expected) {
105113
assertWithMessage("Unexpected warnings or errors.\n " + msg).that(actual).isEqualTo(expected);
106114
}
107115
}
@@ -110,6 +118,15 @@ String parsePrint(String js, CompilerOptions options) {
110118
return new CodePrinter.Builder(parse(js)).setCompilerOptions(options).build();
111119
}
112120

121+
private boolean shouldIgnore(JSError error) {
122+
for (DiagnosticGroup diagnosticGroup : diagnosticsToIgnore) {
123+
if (diagnosticGroup.matches(error)) {
124+
return true;
125+
}
126+
}
127+
return false;
128+
}
129+
113130
abstract static class CompilerOptionBuilder {
114131
abstract void setOptions(CompilerOptions options);
115132
}

test/com/google/javascript/jscomp/CompilerTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2346,7 +2346,7 @@ public void testProperGoogBaseOrdering() throws Exception {
23462346
SourceFile.fromCode(
23472347
"base.js",
23482348
lines(
2349-
"/** @provideGoog */",
2349+
"/** @fileoverview @provideGoog */",
23502350
"/** @const */ var goog = goog || {};",
23512351
"var COMPILED = false;")));
23522352
sources.add(

0 commit comments

Comments
 (0)