Skip to content

Commit 73dc7df

Browse files
rishipalcopybara-github
authored andcommitted
Backoff SourceExcerptProvider from attempting to get source code of stub files when reporting errors.
When JSCompiler generates an error report, it tries to format the error message with the relevant source code snippet. To do that, it relies on a SourceExcerptProvider to give it the relevant snippet. The implementation for this SourceExcerptProvider interface is provided by the compiler. This CL makes the SourceExcerptProvider (i.e. the compiler implementing it) backoff when the source file is a stub source file. The sequence of calls is: `ThreadSafeDelegatingErrorManager.generateReport()` --> `BasicErrorManager.generateReport()` --> .. --> `LightweightMessageFormatter.formatError()` --> `SourceExcerptProvider$SourceExcerpt$1.get()` --> `compiler.getSourceLine()` Also adds a unit test to check that compiler reports an error without crashing. Fixes the crash reported in b/379868495 synced to CL 697886232 - http://sponge2/4ea3023b-17f3-45b4-adb4-de5ce0d1e864 PiperOrigin-RevId: 698904429
1 parent e64e1c7 commit 73dc7df

File tree

2 files changed

+85
-19
lines changed

2 files changed

+85
-19
lines changed

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

+6-3
Original file line numberDiff line numberDiff line change
@@ -3394,6 +3394,9 @@ public boolean hasErrors() {
33943394

33953395
public @Nullable CharSequence getSourceFileContentByName(String sourceName) {
33963396
SourceFile file = getSourceFileByName(sourceName);
3397+
if (file.isStubSourceFileForAlreadyProvidedInput()) {
3398+
return null;
3399+
}
33973400
checkNotNull(file);
33983401
try {
33993402
return file.getCode();
@@ -3521,7 +3524,7 @@ private synchronized void addSourceMapSourceFiles(SourceMapInput inputSourceMap)
35213524
return null;
35223525
}
35233526
SourceFile input = getSourceFileByName(sourceName);
3524-
if (input != null) {
3527+
if (input != null && !input.isStubSourceFileForAlreadyProvidedInput()) {
35253528
return input.getLine(lineNumber);
35263529
}
35273530
return null;
@@ -3533,7 +3536,7 @@ private synchronized void addSourceMapSourceFiles(SourceMapInput inputSourceMap)
35333536
return null;
35343537
}
35353538
SourceFile input = getSourceFileByName(sourceName);
3536-
if (input != null) {
3539+
if (input != null && !input.isStubSourceFileForAlreadyProvidedInput()) {
35373540
return input.getLines(lineNumber, length);
35383541
}
35393542
return null;
@@ -3545,7 +3548,7 @@ private synchronized void addSourceMapSourceFiles(SourceMapInput inputSourceMap)
35453548
return null;
35463549
}
35473550
SourceFile input = getSourceFileByName(sourceName);
3548-
if (input != null) {
3551+
if (input != null && !input.isStubSourceFileForAlreadyProvidedInput()) {
35493552
return input.getRegion(lineNumber);
35503553
}
35513554
return null;

test/com/google/javascript/jscomp/integration/TypedAstIntegrationTest.java

+79-16
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import java.nio.file.Path;
5252
import java.util.ArrayList;
5353
import java.util.Collections;
54+
import java.util.Objects;
5455
import java.util.zip.GZIPInputStream;
5556
import org.junit.Before;
5657
import org.junit.Test;
@@ -62,16 +63,70 @@
6263
public final class TypedAstIntegrationTest extends IntegrationTestCase {
6364

6465
private ArrayList<Path> shards;
65-
private ArrayList<SourceFile> externFiles;
66-
private ArrayList<SourceFile> sourceFiles;
66+
private ArrayList<SourceFile> stubExternFiles;
67+
private ArrayList<SourceFile> stubSourceFiles;
6768

6869
@Override
6970
@Before
7071
public void setUp() {
7172
super.setUp();
7273
this.shards = new ArrayList<>();
73-
this.externFiles = new ArrayList<>();
74-
this.sourceFiles = new ArrayList<>();
74+
this.stubExternFiles = new ArrayList<>();
75+
this.stubSourceFiles = new ArrayList<>();
76+
}
77+
78+
@Test
79+
public void compilerGeneratesErrorReportWithoutCrashing() throws IOException {
80+
SourceFile lib1 =
81+
code("\n\n class Lib1 { m() { return 'lib1'; } n() { return 'delete me'; } }");
82+
SourceFile lib2 =
83+
code("\n\n class Lib2 { m() { return 'delete me'; } n() { return 'lib2'; } }");
84+
precompileLibrary(lib1);
85+
precompileLibrary(lib2);
86+
precompileLibrary(
87+
extern(new TestExternsBuilder().addAlert().build()),
88+
typeSummary(lib1),
89+
typeSummary(lib2),
90+
code("\n\n alert(new Lib1().m()); \n\n alert(new Lib2().n());"));
91+
// assigning an instance of Lib1 to a variable of type 'string' causes the disambiguator to
92+
// 'invalidate' the type of Lib1 and any associated properties.
93+
SourceFile invalidating =
94+
code("/** @suppress {checkTypes} @type {string} */ \n\n\n const str = new Lib1();");
95+
precompileLibrary(typeSummary(lib1), invalidating);
96+
97+
CompilerOptions options = new CompilerOptions();
98+
CompilationLevel.ADVANCED_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
99+
options.setDependencyOptions(DependencyOptions.none());
100+
options.setDisambiguateProperties(true);
101+
options.setPropertiesThatMustDisambiguate(ImmutableSet.of("m"));
102+
103+
Compiler compiler = compileTypedAstShardsWithoutErrorChecks(options);
104+
105+
assertThat(compiler.getErrors())
106+
.comparingElementsUsing(JSCompCorrespondences.DESCRIPTION_EQUALITY)
107+
.containsExactly(
108+
"Property 'm' was required to be disambiguated but was invalidated."
109+
);
110+
111+
// This code path pipes through the {@link ErrorManager} which extends the
112+
// {@link BasicErrorManager}. The {@link BasicErrorManager} uses a {@link
113+
// LightweightMessageFormatter} to format the error messages seen by the compiler. When doing
114+
// so, it tries to format the error message using the compiler which extends the {@link
115+
// SourceExceptProvider} to attach the relevant snippet of the source files. It invokes
116+
// {@link SourceExceptProvider}'s methods such as {@link getSourceLine()}, etc to get the source
117+
// code. For stage 2 and stage 3 passes, the compiler (and these tests) does not receive the
118+
// source files. So, the {@link SourceExceptProvider} backs off from reading the source files.
119+
// TODO(b/379868495): The `JSC_DISAMBIGUATE2_PROPERTY_INVALIDATION` error in this test is not a
120+
// good example to test the crashing behavior when it is reported by the compiler. It is not a
121+
// good example because it is a `JSError` that is created in the disambiguator pass without
122+
// source details (line number, column number, etc) -
123+
// https://source.corp.google.com/piper///depot/google3/third_party/java_src/jscomp/java/com/google/javascript/jscomp/disambiguate/DisambiguateProperties.java;rcl=665086807;l=235. When such an error is formatted, the {@link
124+
// LightweightMessageFormatter}
125+
// anyway backs off from extracting the source snippet and formatting it -
126+
// https://source.corp.google.com/piper///depot/google3/third_party/java_src/jscomp/java/com/google/javascript/jscomp/LightweightMessageFormatter.java;rcl=645071015;l=175.
127+
// A better example would be an error that is reported in the stage 2 or stage 3 pass and
128+
// includes the relevant source code details.
129+
compiler.generateReport();
75130
}
76131

77132
@Test
@@ -230,8 +285,10 @@ public void disambiguatesAndDeletesMethodsAcrossLibraries_ignoresInvalidationsIn
230285
// However, leave in the associated TypedAST in this.shards.
231286
// We want to verify that JSCompiler is able to disambiguate properties on Lib1 despite the
232287
// invalidation in the unused TypedAST shard.
233-
Preconditions.checkState(this.sourceFiles.get(3) == invalidating, this.sourceFiles);
234-
this.sourceFiles.remove(3);
288+
Preconditions.checkState(
289+
Objects.equals(this.stubSourceFiles.get(3).getName(), invalidating.getName()),
290+
this.stubSourceFiles);
291+
this.stubSourceFiles.remove(3);
235292

236293
Compiler compiler = compileTypedAstShards(options);
237294

@@ -298,9 +355,9 @@ public void exportAnnotationOnProperty_ignoredInUnusedTypedAstShard() throws IOE
298355
// However, leave in the associated TypedAST in this.shards.
299356
// We want to verify that JSCompiler does /not/ pay attention to the @export in
300357
// the unusedLib file, as it's not part of the compilation.
301-
Preconditions.checkState(this.sourceFiles.size() == 2, this.sourceFiles);
358+
Preconditions.checkState(this.stubSourceFiles.size() == 2, this.stubSourceFiles);
302359
Preconditions.checkState(this.shards.size() == 2, this.shards);
303-
this.sourceFiles.remove(1);
360+
this.stubSourceFiles.remove(1);
304361

305362
Compiler compiler = compileTypedAstShards(options);
306363

@@ -512,7 +569,7 @@ public void runsJ2clOptimizations() throws IOException {
512569
" InternalWidget.$clinit();",
513570
"};",
514571
"InternalWidget.$clinit();"));
515-
sourceFiles.add(f);
572+
stubSourceFiles.add(f);
516573
precompileLibrary(f);
517574

518575
CompilerOptions options = new CompilerOptions();
@@ -576,7 +633,7 @@ public void testCrossChunkMethodMotion() throws IOException {
576633
// run compilation
577634
try (InputStream inputStream = toInputStream(this.shards)) {
578635
compiler.initChunksWithTypedAstFilesystem(
579-
ImmutableList.copyOf(this.externFiles),
636+
ImmutableList.copyOf(this.stubExternFiles),
580637
ImmutableList.of(chunk1, chunk2),
581638
options,
582639
inputStream);
@@ -842,8 +899,8 @@ private Compiler compileTypedAstShardsWithoutErrorChecks(CompilerOptions options
842899
compiler.initOptions(options);
843900
try (InputStream inputStream = toInputStream(this.shards)) {
844901
compiler.initWithTypedAstFilesystem(
845-
ImmutableList.copyOf(this.externFiles),
846-
ImmutableList.copyOf(this.sourceFiles),
902+
ImmutableList.copyOf(this.stubExternFiles),
903+
ImmutableList.copyOf(this.stubSourceFiles),
847904
options,
848905
inputStream);
849906
}
@@ -852,6 +909,8 @@ private Compiler compileTypedAstShardsWithoutErrorChecks(CompilerOptions options
852909
compiler.stage3Passes();
853910
}
854911

912+
compiler.generateReport();
913+
855914
return compiler;
856915
}
857916

@@ -864,15 +923,19 @@ private Compiler compileTypedAstShards(CompilerOptions options) throws IOExcepti
864923

865924
private SourceFile code(String... code) {
866925
SourceFile sourceFile =
867-
SourceFile.fromCode("input_" + (sourceFiles.size() + 1), lines(code), SourceKind.STRONG);
868-
this.sourceFiles.add(sourceFile);
926+
SourceFile.fromCode(
927+
"input_" + (stubSourceFiles.size() + 1), lines(code), SourceKind.STRONG);
928+
SourceFile stubFile = SourceFile.stubSourceFile(sourceFile.getName(), SourceKind.STRONG);
929+
this.stubSourceFiles.add(stubFile);
869930
return sourceFile;
870931
}
871932

872933
private SourceFile extern(String... code) {
873934
SourceFile sourceFile =
874-
SourceFile.fromCode("extern_" + (externFiles.size() + 1), lines(code), SourceKind.EXTERN);
875-
this.externFiles.add(sourceFile);
935+
SourceFile.fromCode(
936+
"extern_" + (stubExternFiles.size() + 1), lines(code), SourceKind.EXTERN);
937+
SourceFile stubFile = SourceFile.stubSourceFile(sourceFile.getName(), SourceKind.STRONG);
938+
this.stubExternFiles.add(stubFile);
876939
return sourceFile;
877940
}
878941

0 commit comments

Comments
 (0)