[CFE] Allow crash test minimizer to work in more scenarios
This change allows to reproduce and minimize the bug in https://dart-review.googlesource.com/c/sdk/+/385441/2 via this (mouthful) of a command: ``` out/ReleaseX64/dart pkg/front_end/test/crashing_test_case_minimizer.dart --platform=out/ReleaseX64/vm_platform_strong.dill --invalidate=package:_fe_analyzer_shared/src/messages/codes.dart --invalidate=package:front_end/src/type_inference/assignable_errors.dart --initial-only-outline --load-from-component-before-invalidate --invalidate-all-at-once --packages=.dart_tool/package_config.json pkg/front_end/lib/src/type_inference/assignable_errors.dart ``` Which - after renames - creates a reproduction like this: ``` # Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file # for details. All rights reserved. Use of this source code is governed by a # BSD-style license that can be found in the LICENSE file. # Reproduce a crash. type: newworld worlds: - entry: - a.dart sources: a.dart: | import 'b.dart'; b.dart: | export 'c.dart'; c.dart: | class Message {} typedef SummaryTemplate = Message Function(int, int, num, num, num); expectedLibraryCount: 3 - entry: - a.dart worldType: updated expectInitializeFromDill: false invalidate: - c.dart - a.dart expectedLibraryCount: 3 advancedInvalidation: bodiesOnly ``` (this was already reproduced and fixed in https://dart-review.googlesource.com/c/sdk/+/385720 but still) Change-Id: I63d5510b1b848309cc4c74a15cdc1f33400d4861 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/385740 Reviewed-by: Johnni Winther <johnniwinther@google.com> Commit-Queue: Jens Johansen <jensj@google.com>
This commit is contained in:
committed by
Commit Queue
parent
f24162c8bc
commit
ab85479ce4
@@ -17,7 +17,7 @@ import 'crashing_test_case_minimizer_impl.dart';
|
||||
// parser on it and verifies that no syntax errors have been introduced.
|
||||
|
||||
Future<void> main(List<String> arguments) async {
|
||||
String? filename;
|
||||
List<String> filenames = [];
|
||||
Uri? loadJson;
|
||||
for (String arg in arguments) {
|
||||
if (arg.startsWith("--json=")) {
|
||||
@@ -41,8 +41,17 @@ Future<void> main(List<String> arguments) async {
|
||||
} else if (arg.startsWith("--platform=")) {
|
||||
String platform = arg.substring("--platform=".length);
|
||||
settings.platformUri = Uri.base.resolve(platform);
|
||||
} else if (arg.startsWith("--packages=")) {
|
||||
String packages = arg.substring("--packages=".length);
|
||||
settings.packagesFileUri = Uri.base.resolve(packages);
|
||||
} else if (arg == "--no-platform") {
|
||||
settings.noPlatform = true;
|
||||
} else if (arg == "--initial-only-outline") {
|
||||
settings.initialOnlyOutline = true;
|
||||
} else if (arg == "--load-from-component-before-invalidate") {
|
||||
settings.loadFromComponentBeforeInvalidate = true;
|
||||
} else if (arg == "--invalidate-all-at-once") {
|
||||
settings.invalidateAllAtOnce = true;
|
||||
} else if (arg.startsWith("--invalidate=")) {
|
||||
for (String s in arg.substring("--invalidate=".length).split(",")) {
|
||||
settings.invalidate.add(Uri.base.resolve(s));
|
||||
@@ -79,11 +88,8 @@ Future<void> main(List<String> arguments) async {
|
||||
} else {
|
||||
throw "Unknown option $arg";
|
||||
}
|
||||
} else if (filename != null) {
|
||||
throw "Already got '$filename', '$arg' is also a filename; "
|
||||
"can only get one";
|
||||
} else {
|
||||
filename = arg;
|
||||
filenames.add(arg);
|
||||
}
|
||||
}
|
||||
if (settings.noPlatform) {
|
||||
@@ -101,12 +107,14 @@ Future<void> main(List<String> arguments) async {
|
||||
throw "The platform file '${settings.platformUri}' doesn't exist";
|
||||
}
|
||||
}
|
||||
if (filename == null) {
|
||||
throw "Need file to operate on";
|
||||
if (filenames.isEmpty) {
|
||||
throw "Need file(s) to operate on";
|
||||
}
|
||||
for (String filename in filenames) {
|
||||
File file = new File(filename);
|
||||
if (!file.existsSync()) throw "File $filename doesn't exist.";
|
||||
settings.entryUris.add(file.absolute.uri);
|
||||
}
|
||||
File file = new File(filename);
|
||||
if (!file.existsSync()) throw "File $filename doesn't exist.";
|
||||
settings.mainUri = file.absolute.uri;
|
||||
}
|
||||
|
||||
TestMinimizer testMinimizer = new TestMinimizer(settings);
|
||||
|
||||
@@ -26,7 +26,7 @@ import 'package:front_end/src/api_prototype/incremental_kernel_generator.dart'
|
||||
show IncrementalCompilerResult;
|
||||
import 'package:front_end/src/base/compiler_context.dart' show CompilerContext;
|
||||
import 'package:front_end/src/base/incremental_compiler.dart'
|
||||
show IncrementalCompiler;
|
||||
show IncrementalCompiler, IncrementalKernelTarget;
|
||||
import 'package:front_end/src/base/messages.dart' show Message;
|
||||
import 'package:front_end/src/base/processed_options.dart'
|
||||
show ProcessedOptions;
|
||||
@@ -58,9 +58,13 @@ class TestMinimizerSettings {
|
||||
}
|
||||
|
||||
bool _useInitialFs = true;
|
||||
Uri? mainUri;
|
||||
final List<Uri> entryUris = [];
|
||||
Uri? platformUri;
|
||||
Uri? packagesFileUri;
|
||||
bool noPlatform = false;
|
||||
bool initialOnlyOutline = false;
|
||||
bool loadFromComponentBeforeInvalidate = false;
|
||||
bool invalidateAllAtOnce = false;
|
||||
bool experimentalInvalidation = false;
|
||||
bool serialize = false;
|
||||
bool widgetTransformation = false;
|
||||
@@ -89,9 +93,13 @@ class TestMinimizerSettings {
|
||||
new List<Map<String, dynamic>>.from(this.fileSystems);
|
||||
fileSystems.add(_fsNotInitial.toJson());
|
||||
return {
|
||||
'mainUri': mainUri.toString(),
|
||||
'entryUris': entryUris.map((uri) => uri.toString()).toList(),
|
||||
'platformUri': platformUri.toString(),
|
||||
'packagesFileUri': packagesFileUri.toString(),
|
||||
'noPlatform': noPlatform,
|
||||
'loadFromComponentBeforeInvalidate': loadFromComponentBeforeInvalidate,
|
||||
'invalidateAllAtOnce': invalidateAllAtOnce,
|
||||
'initialOnlyOutline': initialOnlyOutline,
|
||||
'experimentalInvalidation': experimentalInvalidation,
|
||||
'serialize': serialize,
|
||||
'widgetTransformation': widgetTransformation,
|
||||
@@ -112,9 +120,16 @@ class TestMinimizerSettings {
|
||||
}
|
||||
|
||||
void initializeFromJson(Map<String, dynamic> json) {
|
||||
mainUri = Uri.parse(json["mainUri"]);
|
||||
entryUris.clear();
|
||||
entryUris.addAll(
|
||||
(json["entryUris"] as List).map((uriString) => Uri.parse(uriString)));
|
||||
platformUri = Uri.parse(json["platformUri"]);
|
||||
packagesFileUri = Uri.parse(json["packagesFileUri"]);
|
||||
noPlatform = json["noPlatform"];
|
||||
loadFromComponentBeforeInvalidate =
|
||||
json["loadFromComponentBeforeInvalidate"];
|
||||
invalidateAllAtOnce = json["invalidateAllAtOnce"];
|
||||
initialOnlyOutline = json["initialOnlyOutline"];
|
||||
experimentalInvalidation = json["experimentalInvalidation"];
|
||||
serialize = json["serialize"];
|
||||
widgetTransformation = json["widgetTransformation"];
|
||||
@@ -151,7 +166,7 @@ class TestMinimizerSettings {
|
||||
class TestMinimizer {
|
||||
final TestMinimizerSettings _settings;
|
||||
_FakeFileSystem get _fs => _settings._fs;
|
||||
Uri get _mainUri => _settings.mainUri!;
|
||||
List<Uri> get _entryUris => _settings.entryUris;
|
||||
String? _expectedCrashLine;
|
||||
bool _quit = false;
|
||||
bool _skip = false;
|
||||
@@ -162,6 +177,7 @@ class TestMinimizer {
|
||||
Component? _latestComponent;
|
||||
IncrementalCompiler? _latestCrashingIncrementalCompiler;
|
||||
Map<Uri, LibraryBuilder>? _latestCrashingKnownInitialBuilders;
|
||||
IncrementalKernelTarget? _latestKernelTarget;
|
||||
StreamSubscription<List<int>>? _stdinSubscription;
|
||||
|
||||
static const int _$LF = 10;
|
||||
@@ -269,11 +285,16 @@ class TestMinimizer {
|
||||
Future _tryToMinimizeImpl() async {
|
||||
// Set main to be basically empty up front.
|
||||
_settings._useInitialFs = true;
|
||||
_fs.data[_mainUri] = utf8.encode("main() {}");
|
||||
for (Uri entry in _entryUris) {
|
||||
_fs.data[entry] = utf8.encode("main() {}");
|
||||
}
|
||||
Component initialComponent = await _getInitialComponent();
|
||||
print("Compiled initially (without data)");
|
||||
// Remove fake cache.
|
||||
_fs.data.remove(_mainUri);
|
||||
for (Uri entry in _entryUris) {
|
||||
_fs.data.remove(entry);
|
||||
}
|
||||
|
||||
_settings._useInitialFs = false;
|
||||
|
||||
// First assure it actually crash on the input.
|
||||
@@ -470,6 +491,7 @@ class TestMinimizer {
|
||||
|
||||
for (Uri uri in uris) {
|
||||
if (_fs.data[uri] == null || _fs.data[uri]!.isEmpty) continue;
|
||||
if (uri.path.endsWith(".dill")) continue;
|
||||
print("Uri $uri has this content:");
|
||||
|
||||
try {
|
||||
@@ -511,7 +533,7 @@ class TestMinimizer {
|
||||
/// success.
|
||||
Future<bool> _attemptInline(Uri uri, Component initialComponent) async {
|
||||
// Don't attempt to inline the main uri --- that's our entry!
|
||||
if (uri == _mainUri) return false;
|
||||
if (_entryUris.contains(uri)) return false;
|
||||
|
||||
Uint8List inlineData = _fs.data[uri]!;
|
||||
bool hasMultipleLines = false;
|
||||
@@ -721,7 +743,7 @@ class TestMinimizer {
|
||||
|
||||
// TODO(jensj): don't use full uris.
|
||||
print("""
|
||||
# Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
|
||||
# Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
|
||||
# for details. All rights reserved. Use of this source code is governed by a
|
||||
# BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
@@ -734,13 +756,18 @@ type: newworld""");
|
||||
}
|
||||
print("""
|
||||
worlds:
|
||||
- entry: $_mainUri""");
|
||||
- entry:""");
|
||||
for (Uri entry in _settings.entryUris) {
|
||||
print(" - $entry");
|
||||
}
|
||||
if (_settings.experimentalInvalidation) {
|
||||
print(" experiments: alternative-invalidation-strategy");
|
||||
}
|
||||
print(" sources:");
|
||||
for (MapEntry<Uri?, Uint8List?> entry in _fs.data.entries) {
|
||||
if (entry.value == null) continue;
|
||||
if (entry.key == null) continue;
|
||||
if (entry.key!.path.endsWith(".dill")) continue;
|
||||
print(" ${entry.key}: |");
|
||||
String string = utf8.decode(entry.value!);
|
||||
List<String> lines = string.split("\n");
|
||||
@@ -752,19 +779,42 @@ worlds:
|
||||
"# with parts this is not right");
|
||||
print("");
|
||||
|
||||
for (Uri uri in _settings.invalidate) {
|
||||
print(" - entry: $_mainUri");
|
||||
if (_settings.invalidateAllAtOnce) {
|
||||
print(" - entry:");
|
||||
for (Uri entry in _settings.entryUris) {
|
||||
print(" - $entry");
|
||||
}
|
||||
if (_settings.experimentalInvalidation) {
|
||||
print(" experiments: alternative-invalidation-strategy");
|
||||
}
|
||||
print(" worldType: updated");
|
||||
print(" expectInitializeFromDill: false # or true?");
|
||||
print(" invalidate:");
|
||||
print(" - $uri");
|
||||
for (Uri uri in _settings.invalidate) {
|
||||
print(" - $uri");
|
||||
}
|
||||
print(" expectedLibraryCount: $dartFiles "
|
||||
"# with parts this is not right");
|
||||
print(" advancedInvalidation: bodiesOnly # or something else?");
|
||||
print("");
|
||||
} else {
|
||||
for (Uri uri in _settings.invalidate) {
|
||||
print(" - entry:");
|
||||
for (Uri entry in _settings.entryUris) {
|
||||
print(" - $entry");
|
||||
}
|
||||
if (_settings.experimentalInvalidation) {
|
||||
print(" experiments: alternative-invalidation-strategy");
|
||||
}
|
||||
print(" worldType: updated");
|
||||
print(" expectInitializeFromDill: false # or true?");
|
||||
print(" invalidate:");
|
||||
print(" - $uri");
|
||||
print(" expectedLibraryCount: $dartFiles "
|
||||
"# with parts this is not right");
|
||||
print(" advancedInvalidation: bodiesOnly # or something else?");
|
||||
print("");
|
||||
}
|
||||
}
|
||||
|
||||
print("------------------------------------------------------------------");
|
||||
@@ -808,8 +858,7 @@ worlds:
|
||||
}
|
||||
|
||||
Uri _getImportUri(Uri uri) {
|
||||
return _latestCrashingIncrementalCompiler!.kernelTargetForTesting!
|
||||
.getEntryPointUri(uri);
|
||||
return _latestKernelTarget!.getEntryPointUri(uri);
|
||||
}
|
||||
|
||||
Uint8List _sublist(Uint8List data, int start, int end) {
|
||||
@@ -1349,7 +1398,7 @@ worlds:
|
||||
shouldCompile = true;
|
||||
what = "part of";
|
||||
} else if (child.isScript()) {
|
||||
var decl = child.asScript();
|
||||
ScriptHandle decl = child.asScript();
|
||||
helper.replacements.add(new _Replacement(
|
||||
decl.token.offset - 1, decl.token.offset + decl.token.length));
|
||||
shouldCompile = true;
|
||||
@@ -1595,7 +1644,8 @@ worlds:
|
||||
}
|
||||
Uint8List candidate = _replaceRange(data.replacements, data.originalData);
|
||||
|
||||
if (!_parsesWithoutError(candidate, _isUriNnbd(uri))) {
|
||||
if (!_parsesWithoutError(candidate, _isUriNnbd(uri)) &&
|
||||
_parsesWithoutError(data.originalData, _isUriNnbd(uri))) {
|
||||
print("WARNING: Parser error after stuff at ${StackTrace.current}");
|
||||
_parsesWithoutError(candidate, _isUriNnbd(uri));
|
||||
_parsesWithoutError(data.originalData, _isUriNnbd(uri));
|
||||
@@ -1750,8 +1800,7 @@ worlds:
|
||||
}
|
||||
|
||||
bool _knownByCompiler(Uri uri) {
|
||||
LibraryBuilder? libraryBuilder = _latestCrashingIncrementalCompiler!
|
||||
.kernelTargetForTesting!.loader
|
||||
LibraryBuilder? libraryBuilder = _latestKernelTarget!.loader
|
||||
.lookupLoadedLibraryBuilder(_getImportUri(uri));
|
||||
if (libraryBuilder != null) {
|
||||
return true;
|
||||
@@ -1777,13 +1826,17 @@ worlds:
|
||||
if (libraryBuilder != null) {
|
||||
return libraryBuilder.languageVersion;
|
||||
}
|
||||
print("Couldn't lookup $uri");
|
||||
print("Couldn't lookup $uri ($asImportUri)");
|
||||
for (LibraryBuilder libraryBuilder
|
||||
in _latestCrashingKnownInitialBuilders!.values) {
|
||||
if (libraryBuilder.importUri == uri) {
|
||||
print("Found $uri as ${libraryBuilder.importUri} (!= ${asImportUri})");
|
||||
return libraryBuilder.languageVersion;
|
||||
}
|
||||
if (libraryBuilder.fileUri == uri) {
|
||||
print("Found $uri as file uri ${libraryBuilder.fileUri}");
|
||||
return libraryBuilder.languageVersion;
|
||||
}
|
||||
// Check parts too.
|
||||
for (LibraryPart part in libraryBuilder.library.parts) {
|
||||
Uri thisPartUri = libraryBuilder.importUri.resolve(part.partUri);
|
||||
@@ -1791,10 +1844,19 @@ worlds:
|
||||
print("Found $uri as part of ${libraryBuilder.importUri}");
|
||||
return libraryBuilder.languageVersion;
|
||||
}
|
||||
thisPartUri = libraryBuilder.fileUri.resolve(part.partUri);
|
||||
if (thisPartUri == uri || thisPartUri == asImportUri) {
|
||||
print("Found $uri as part of ${libraryBuilder.importUri}");
|
||||
return libraryBuilder.languageVersion;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (crashOnFail) {
|
||||
throw "Couldn't lookup $uri at all!";
|
||||
throw "Couldn't lookup $uri ($asImportUri) at all!"
|
||||
"\n\n"
|
||||
"$_latestCrashingKnownInitialBuilders"
|
||||
"\n\n"
|
||||
"";
|
||||
} else {
|
||||
return defaultLanguageVersion;
|
||||
}
|
||||
@@ -1809,17 +1871,23 @@ worlds:
|
||||
IncrementalCompiler incrementalCompiler;
|
||||
_gotWantedError = false;
|
||||
bool didNotGetWantedErrorAfterFirstCompile = true;
|
||||
CompilerContext context = _setupCompilerContext();
|
||||
if (_settings.noPlatform) {
|
||||
incrementalCompiler = new IncrementalCompiler(_setupCompilerContext());
|
||||
incrementalCompiler = new IncrementalCompiler(context);
|
||||
} else if (_settings.initialOnlyOutline) {
|
||||
incrementalCompiler = new IncrementalCompiler(context, null, true);
|
||||
} else {
|
||||
incrementalCompiler = new IncrementalCompiler.fromComponent(
|
||||
_setupCompilerContext(), initialComponent);
|
||||
context, initialComponent, true);
|
||||
}
|
||||
for (Uri entry in _entryUris) {
|
||||
incrementalCompiler.invalidate(entry);
|
||||
}
|
||||
incrementalCompiler.invalidate(_mainUri);
|
||||
Map<Uri, LibraryBuilder>? knownInitialBuilders;
|
||||
try {
|
||||
IncrementalCompilerResult incrementalCompilerResult =
|
||||
await incrementalCompiler.computeDelta();
|
||||
_latestKernelTarget = incrementalCompiler.kernelTargetForTesting!;
|
||||
_latestComponent = incrementalCompilerResult.component;
|
||||
if (_settings.serialize) {
|
||||
// We're asked to serialize, probably because it crashes in
|
||||
@@ -1830,17 +1898,26 @@ worlds:
|
||||
}
|
||||
|
||||
knownInitialBuilders = <Uri, LibraryBuilder>{
|
||||
for (var v in incrementalCompiler
|
||||
.kernelTargetForTesting!.loader.loadedLibraryBuilders)
|
||||
for (LibraryBuilder v
|
||||
in _latestKernelTarget!.loader.loadedLibraryBuilders)
|
||||
v.importUri: v
|
||||
};
|
||||
|
||||
if (_gotWantedError) didNotGetWantedErrorAfterFirstCompile = false;
|
||||
|
||||
for (Uri uri in _settings.invalidate) {
|
||||
incrementalCompiler.invalidate(uri);
|
||||
if (_settings.loadFromComponentBeforeInvalidate) {
|
||||
incrementalCompiler =
|
||||
new IncrementalCompiler.fromComponent(context, _latestComponent!);
|
||||
}
|
||||
|
||||
if (_settings.invalidateAllAtOnce && _settings.invalidate.isNotEmpty) {
|
||||
for (Uri uri in _settings.invalidate) {
|
||||
incrementalCompiler.invalidate(uri);
|
||||
}
|
||||
|
||||
IncrementalCompilerResult deltaResult =
|
||||
await incrementalCompiler.computeDelta();
|
||||
_latestKernelTarget = incrementalCompiler.kernelTargetForTesting!;
|
||||
Component delta = deltaResult.component;
|
||||
if (_settings.serialize) {
|
||||
// We're asked to serialize, probably because it crashes in
|
||||
@@ -1850,6 +1927,22 @@ worlds:
|
||||
printer.writeComponentFile(delta);
|
||||
sink.builder.takeBytes();
|
||||
}
|
||||
} else {
|
||||
for (Uri uri in _settings.invalidate) {
|
||||
incrementalCompiler.invalidate(uri);
|
||||
IncrementalCompilerResult deltaResult =
|
||||
await incrementalCompiler.computeDelta();
|
||||
_latestKernelTarget = incrementalCompiler.kernelTargetForTesting!;
|
||||
Component delta = deltaResult.component;
|
||||
if (_settings.serialize) {
|
||||
// We're asked to serialize, probably because it crashes in
|
||||
// serialization.
|
||||
ByteSink sink = new ByteSink();
|
||||
BinaryPrinter printer = new BinaryPrinter(sink);
|
||||
printer.writeComponentFile(delta);
|
||||
sink.builder.takeBytes();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_settings.lookForErrorErrorOnReload != null &&
|
||||
_gotWantedError &&
|
||||
@@ -1968,6 +2061,7 @@ worlds:
|
||||
options.fileSystem = _fs;
|
||||
options.sdkRoot = null;
|
||||
options.sdkSummary = _settings.platformUri;
|
||||
options.packagesFileUri = _settings.packagesFileUri;
|
||||
options.omitPlatform = false;
|
||||
options.onDiagnostic = (DiagnosticMessage message) {
|
||||
// don't care.
|
||||
@@ -1983,7 +2077,7 @@ worlds:
|
||||
}
|
||||
|
||||
CompilerContext compilerContext = new CompilerContext(
|
||||
new ProcessedOptions(options: options, inputs: [_mainUri]));
|
||||
new ProcessedOptions(options: options, inputs: _entryUris));
|
||||
return compilerContext;
|
||||
}
|
||||
|
||||
@@ -2126,7 +2220,7 @@ class _FakeFileSystem extends FileSystem {
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
List tmp = [];
|
||||
for (var entry in data.entries) {
|
||||
for (MapEntry<Uri?, Uint8List?> entry in data.entries) {
|
||||
if (entry.value == null) continue;
|
||||
tmp.add(entry.key == null ? null : entry.key.toString());
|
||||
dynamic out = entry.value;
|
||||
|
||||
Reference in New Issue
Block a user