Skip to content

Commit 710c0b3

Browse files
sigmundchCommit Bot
authored and
Commit Bot
committed
[dartj2s] (tech-debt) better error locations for sources from dill files.
This change addresses a technical debt issue in dart2js, where errors sometimes get reported as a binary offset location, instead of a line/column position with contextual data from the source file. The reason for this behavior is that dartj2s reports binary offsets when it can't recover line/column data and show the contents of the file. This typically happens if the file cannot be read directly from disk. The fix consits of adding an API to record the contents of files that are not read directly by the compiler. We then use this API to provide all sources collected by the CFE when parsing components from .dill files. Note: this CL also deletes the `autoread` feature in source-file-provider. That feature was added for the same purpose. The way it worked was that instead of registering source content, it tried to read the sources from disk on-demand as errors got reported. This doesn't always work for three reasons: * First, we often use custom schemes (like the multi-root scheme) in dill files to make the .dill deterministic in distributed build systems, the autoload feature had no understanding of how to translate those custom URIs to file URIs (that translation is only define at the time the .dill is being built). * Second, sometimes the files are simply not available, for example, in hermetic build systems like bazel we have no access to those files. * Third, when files were found, there was no guarantee that the contents were consistent. That is, the current version of the file on disk could have been modified and have different contents than those used when the .dill file was built. I tested this manually by adding a crash in SSA and observing the location in multiple scenarios, including running from source, running from a .dill with file URIs, running from a .dill with multi-root URIs, crashing in SDK locations in regular files and patch files. Change-Id: Ief09be577f4c9c4b345b4e2641918cafbe93c3fc Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/251700 Reviewed-by: Mayank Patke <[email protected]> Commit-Queue: Sigmund Cherem <[email protected]>
1 parent 77b9f7d commit 710c0b3

File tree

7 files changed

+88
-72
lines changed

7 files changed

+88
-72
lines changed

pkg/compiler/lib/compiler_api.dart

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,20 @@ abstract class CompilerInput {
108108
/// is expected to hold a zero element at the last position. If this is not
109109
/// the case, the entire data structure is copied before scanning.
110110
Future<Input> readFromUri(Uri uri, {InputKind inputKind = InputKind.UTF8});
111+
112+
/// Register that [uri] should be an `InputKind.UTF8` input with the
113+
/// given [source] as its zero-terminated list of contents.
114+
///
115+
/// If [uri] was read prior to this call, this registration has no effect,
116+
/// otherwise it is expected that a future [readFromUri] will return the
117+
/// contents provided here.
118+
///
119+
/// The main purpose of this API is to assist in error reporting when
120+
/// compiling from kernel binary files. Binary files embed the contents
121+
/// of source files that may not be available on disk. By using these
122+
/// registered contents, dart2js will be able to provide accurate line/column
123+
/// information on an error.
124+
void registerUtf8ContentsForDiagnostics(Uri uri, List<int> source);
111125
}
112126

113127
/// Output types used in `CompilerOutput.createOutputSink`.

pkg/compiler/lib/src/dart2js.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1085,7 +1085,6 @@ Future<api.CompilationResult> compile(List<String> argv,
10851085
return result;
10861086
}
10871087

1088-
diagnosticHandler.autoReadFileUri = true;
10891088
CompilerOptions compilerOptions = CompilerOptions.parse(options,
10901089
featureOptions: features,
10911090
librariesSpecificationUri: librariesSpecificationUri,

pkg/compiler/lib/src/phase/load_kernel.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,8 @@ Future<_LoadFromKernelResult> _loadFromKernel(CompilerOptions options,
196196
if (options.cfeOnly) {
197197
_doGlobalTransforms(component);
198198
}
199+
200+
registerSources(component, compilerInput);
199201
return _LoadFromKernelResult(component, entryLibrary, moduleLibraries);
200202
}
201203

@@ -287,6 +289,7 @@ Future<_LoadFromSourceResult> _loadFromSource(
287289
if (isModularCompile) {
288290
component?.computeCanonicalNames();
289291
}
292+
registerSources(component, compilerInput);
290293
return _LoadFromSourceResult(
291294
component, initializedCompilerState, isModularCompile ? sources : []);
292295
}
@@ -394,3 +397,16 @@ Future<Output?> run(Input input) async {
394397
return _createOutput(options, reporter, entryLibrary, component,
395398
moduleLibraries, initializedCompilerState);
396399
}
400+
401+
/// Registers with the dart2js compiler all sources embeded in a kernel
402+
/// component. This may include sources that were read from disk directly as
403+
/// files, but also sources that were embedded in binary `.dill` files (like the
404+
/// platform kernel file and kernel files from modular compilation pipelines).
405+
///
406+
/// This registration improves how locations are presented when errors
407+
/// or crashes are reported by the dart2js compiler.
408+
void registerSources(ir.Component? component, api.CompilerInput compilerInput) {
409+
component?.uriToSource.forEach((uri, source) {
410+
compilerInput.registerUtf8ContentsForDiagnostics(uri, source.source);
411+
});
412+
}

pkg/compiler/lib/src/source_file_provider.dart

Lines changed: 54 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,7 @@ abstract class SourceFileProvider implements api.CompilerInput {
2929
if (!resourceUri.isAbsolute) {
3030
resourceUri = cwd.resolveUri(resourceUri);
3131
}
32-
api.Input<List<int>> input;
33-
switch (inputKind) {
34-
case api.InputKind.UTF8:
35-
input = utf8SourceFiles[resourceUri];
36-
break;
37-
case api.InputKind.binary:
38-
input = binarySourceFiles[resourceUri];
39-
break;
40-
}
32+
api.Input<List<int>> input = _loadInputFromCache(resourceUri, inputKind);
4133
if (input != null) return Future.value(input);
4234

4335
if (resourceUri.isScheme('file')) {
@@ -49,6 +41,52 @@ abstract class SourceFileProvider implements api.CompilerInput {
4941
}
5042
}
5143

44+
/// Fetches any existing value of [resourceUri] in a cache.
45+
///
46+
/// For `api.InputKind.UTF8` inputs, this looks up both the cache of
47+
/// utf8 source files and binary source files. This is done today because of
48+
/// how dart2js binds to the CFE's file system. While dart2js reads sources as
49+
/// utf8, the CFE file system may read them as binary inputs. In case the CFE
50+
/// needs to report errors, dart2js will only find the location data if it
51+
/// checks both caches.
52+
api.Input<List<int>> _loadInputFromCache(
53+
Uri resourceUri, api.InputKind inputKind) {
54+
switch (inputKind) {
55+
case api.InputKind.UTF8:
56+
var input = utf8SourceFiles[resourceUri];
57+
if (input != null) return input;
58+
input = binarySourceFiles[resourceUri];
59+
if (input == null) return null;
60+
return _storeSourceInCache(resourceUri, input.data, api.InputKind.UTF8);
61+
case api.InputKind.binary:
62+
return binarySourceFiles[resourceUri];
63+
}
64+
return null;
65+
}
66+
67+
/// Adds [source] to the cache under the [resourceUri] key.
68+
api.Input _storeSourceInCache(
69+
Uri resourceUri, List<int> source, api.InputKind inputKind) {
70+
switch (inputKind) {
71+
case api.InputKind.UTF8:
72+
return utf8SourceFiles[resourceUri] = CachingUtf8BytesSourceFile(
73+
resourceUri, relativizeUri(resourceUri), source);
74+
case api.InputKind.binary:
75+
return binarySourceFiles[resourceUri] = Binary(resourceUri, source);
76+
}
77+
return null;
78+
}
79+
80+
@override
81+
void registerUtf8ContentsForDiagnostics(Uri resourceUri, List<int> source) {
82+
if (!resourceUri.isAbsolute) {
83+
resourceUri = cwd.resolveUri(resourceUri);
84+
}
85+
if (!utf8SourceFiles.containsKey(resourceUri)) {
86+
_storeSourceInCache(resourceUri, source, api.InputKind.UTF8);
87+
}
88+
}
89+
5290
api.Input _readFromFileSync(Uri resourceUri, api.InputKind inputKind) {
5391
assert(resourceUri.isScheme('file'));
5492
List<int> source;
@@ -61,29 +99,19 @@ abstract class SourceFileProvider implements api.CompilerInput {
6199
throw "Error reading '${relativizeUri(resourceUri)}' $detail";
62100
}
63101
dartCharactersRead += source.length;
64-
api.Input input;
65-
switch (inputKind) {
66-
case api.InputKind.UTF8:
67-
input = utf8SourceFiles[resourceUri] = CachingUtf8BytesSourceFile(
68-
resourceUri, relativizeUri(resourceUri), source);
69-
break;
70-
case api.InputKind.binary:
71-
input = binarySourceFiles[resourceUri] = Binary(resourceUri, source);
72-
break;
73-
}
74-
return input;
102+
return _storeSourceInCache(resourceUri, source, inputKind);
75103
}
76104

77105
/// Read [resourceUri] directly as a UTF-8 file. If reading fails, `null` is
78106
/// returned.
79-
api.Input autoReadFromFile(Uri resourceUri) {
107+
api.Input readUtf8FromFileSyncForTesting(Uri resourceUri) {
80108
try {
81109
return _readFromFileSync(resourceUri, api.InputKind.UTF8);
82110
} catch (e) {
83111
// Silence the error. The [resourceUri] was not requested by the user and
84112
// was only needed to give better error messages.
113+
return null;
85114
}
86-
return null;
87115
}
88116

89117
Future<api.Input<List<int>>> _readFromFile(
@@ -139,11 +167,14 @@ abstract class SourceFileProvider implements api.CompilerInput {
139167
relativizeUri(Uri uri) => fe.relativizeUri(cwd, uri, isWindows);
140168

141169
SourceFile<List<int>> getUtf8SourceFile(Uri resourceUri) {
142-
return utf8SourceFiles[resourceUri];
170+
return _loadInputFromCache(resourceUri, api.InputKind.UTF8);
143171
}
144172

145173
Iterable<Uri> getSourceUris() {
146174
Set<Uri> uris = Set<Uri>();
175+
// Note: this includes also indirect sources that were used to create
176+
// `.dill` inputs to the compiler. This is OK, since this API is only
177+
// used to calculate DEPS for gn build systems.
147178
uris.addAll(utf8SourceFiles.keys);
148179
uris.addAll(binarySourceFiles.keys);
149180
return uris;
@@ -179,7 +210,6 @@ class FormattingDiagnosticHandler implements api.CompilerDiagnostics {
179210
bool isAborting = false;
180211
bool enableColors = false;
181212
bool throwOnError = false;
182-
bool autoReadFileUri = false;
183213
int throwOnErrorCount = 0;
184214
api.Diagnostic lastKind = null;
185215
int fatalCount = 0;
@@ -265,17 +295,6 @@ class FormattingDiagnosticHandler implements api.CompilerDiagnostics {
265295
print('${color(message)}');
266296
} else {
267297
api.Input file = provider.getUtf8SourceFile(uri);
268-
if (file == null &&
269-
autoReadFileUri &&
270-
(uri.isScheme('file') || !uri.isAbsolute) &&
271-
uri.path.endsWith('.dart')) {
272-
if (!uri.isAbsolute) {
273-
uri = provider.cwd.resolveUri(uri);
274-
}
275-
// When reading from .dill files, the original source files haven't been
276-
// loaded. Load the file if possible to provide a better error message.
277-
file = provider.autoReadFromFile(uri);
278-
}
279298
if (file is SourceFile) {
280299
print(file.getLocationMessage(color(message), begin, end,
281300
colorize: color));
@@ -574,21 +593,6 @@ class BazelInputProvider extends SourceFileProvider {
574593
}
575594
return result;
576595
}
577-
578-
@override
579-
api.Input autoReadFromFile(Uri resourceUri) {
580-
var path = resourceUri.path;
581-
if (path.startsWith('/bazel-root')) {
582-
path = path.substring('/bazel-root/'.length);
583-
for (var dir in dirs) {
584-
var file = dir.resolve(path);
585-
if (File.fromUri(file).existsSync()) {
586-
return super.autoReadFromFile(file);
587-
}
588-
}
589-
}
590-
return null;
591-
}
592596
}
593597

594598
/// Adapter to support one or more synthetic uri schemes.
@@ -635,18 +639,4 @@ class MultiRootInputProvider extends SourceFileProvider {
635639
}
636640
return result;
637641
}
638-
639-
@override
640-
api.Input autoReadFromFile(Uri resourceUri) {
641-
if (resourceUri.isScheme(markerScheme)) {
642-
var path = resourceUri.path;
643-
for (var dir in roots) {
644-
var file = dir.resolve(path);
645-
if (File.fromUri(file).existsSync()) {
646-
return super.autoReadFromFile(file);
647-
}
648-
}
649-
}
650-
return null;
651-
}
652642
}

pkg/compiler/test/equivalence/show_helper.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ show<T>(ArgResults argResults, DataComputer<T> dataComputer,
7171
if (show != null && !show.any((f) => '$fileUri'.endsWith(f))) {
7272
continue;
7373
}
74-
SourceFile sourceFile = await provider.autoReadFromFile(fileUri);
74+
SourceFile sourceFile = provider.readUtf8FromFileSyncForTesting(fileUri);
7575
String sourceCode = sourceFile?.slowText();
7676
if (sourceCode == null) {
7777
sourceCode = new File.fromUri(fileUri).readAsStringSync();

pkg/compiler/test/helpers/memory_compiler.dart

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,10 @@ api.CompilerDiagnostics createCompilerDiagnostics(
6060
api.CompilerDiagnostics handler = diagnostics;
6161
if (showDiagnostics) {
6262
if (diagnostics == null) {
63-
handler = new FormattingDiagnosticHandler(provider)
64-
..verbose = verbose
65-
..autoReadFileUri = true;
63+
handler = new FormattingDiagnosticHandler(provider)..verbose = verbose;
6664
} else {
6765
var formattingHandler = new FormattingDiagnosticHandler(provider)
68-
..verbose = verbose
69-
..autoReadFileUri = true;
66+
..verbose = verbose;
7067
handler = new MultiDiagnostics([diagnostics, formattingHandler]);
7168
}
7269
} else if (diagnostics == null) {

pkg/compiler/test/sourcemaps/helpers/sourcemap_helper.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ class ProviderSourceFileManager implements SourceFileManager {
105105
@override
106106
SourceFile getSourceFile(uri) {
107107
SourceFile sourceFile = sourceFileProvider.getUtf8SourceFile(uri);
108-
sourceFile ??= sourceFileProvider.autoReadFromFile(uri);
108+
sourceFile ??= sourceFileProvider.readUtf8FromFileSyncForTesting(uri);
109109
if (sourceFile == null) {
110110
sourceFile = outputProvider.getSourceFile(uri);
111111
}

0 commit comments

Comments
 (0)