dart-sdk/pkg/front_end/test/incremental_load_from_invalid_dill_test.dart
Johnni Winther 27068ba96b [cfe] Extract _ComponentProblems and InitializationStrategy from IncrementalCompiler
This CL extracts part of the IncrementalCompiler into subcomponents.
This is part of an effort to make the state and state changes of the
incremental compiler more explicit.

Change-Id: I59cbb30558082f4db392d925bb704d03c31ccb90
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/221940
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
2021-12-02 15:58:47 +00:00

336 lines
12 KiB
Dart

// Copyright (c) 2018, 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.
import 'dart:io' show File;
import 'package:_fe_analyzer_shared/src/messages/diagnostic_message.dart'
show DiagnosticMessage, getMessageCodeObject;
import 'package:_fe_analyzer_shared/src/messages/severity.dart' show Severity;
import 'package:expect/expect.dart' show Expect;
import 'package:front_end/src/api_prototype/compiler_options.dart'
show CompilerOptions;
import 'package:front_end/src/api_prototype/experimental_flags.dart'
show ExperimentalFlag;
import 'package:front_end/src/api_prototype/incremental_kernel_generator.dart'
show IncrementalCompilerResult;
import "package:front_end/src/api_prototype/memory_file_system.dart"
show MemoryFileSystem;
import 'package:front_end/src/base/nnbd_mode.dart' show NnbdMode;
import 'package:front_end/src/base/processed_options.dart'
show ProcessedOptions;
import 'package:front_end/src/compute_platform_binaries_location.dart'
show computePlatformBinariesLocation;
import 'package:front_end/src/fasta/compiler_context.dart' show CompilerContext;
import 'package:front_end/src/fasta/fasta_codes.dart'
show
Code,
codeInitializeFromDillNotSelfContained,
codeInitializeFromDillNotSelfContainedNoDump,
codeInitializeFromDillUnknownProblem,
codeInitializeFromDillUnknownProblemNoDump;
import 'package:front_end/src/fasta/incremental_compiler.dart'
show IncrementalCompiler, RecorderForTesting;
import 'package:front_end/src/fasta/kernel/utils.dart' show serializeComponent;
import 'package:kernel/kernel.dart'
show Component, Library, NonNullableByDefaultCompiledMode;
import 'incremental_suite.dart' show getOptions;
Future<Null> main() async {
Tester tester = new Tester();
await tester.initialize();
await tester.test();
}
class Tester {
late Uri sdkRoot;
late Uri base;
late Uri sdkSummary;
late Uri initializeFrom;
late Uri helperFile;
late Uri helper2File;
late Uri entryPoint;
late Uri entryPointImportDartFoo;
late Uri platformUri;
late List<int> sdkSummaryData;
late List<DiagnosticMessage> errorMessages;
late List<DiagnosticMessage> warningMessages;
late MemoryFileSystem fs;
late CompilerOptions options;
late IncrementalCompiler compiler;
Future<void> compileExpectInitializeFailAndSpecificWarning(
Code expectedWarningCode, bool writeFileOnCrashReport) async {
errorMessages.clear();
warningMessages.clear();
options.writeFileOnCrashReport = writeFileOnCrashReport;
compiler = new DeleteTempFilesIncrementalCompiler(
new CompilerContext(
new ProcessedOptions(options: options, inputs: [entryPoint])),
initializeFrom);
await compiler.computeDelta();
if (compiler.initializedFromDillForTesting) {
Expect.fail("Expected to not be able to initialized from dill, but did.");
}
if (errorMessages.isNotEmpty) {
Expect.fail("Got unexpected errors: " + joinMessages(errorMessages));
}
if (warningMessages.length != 1) {
Expect.fail("Got unexpected errors: Expected one, got this: " +
joinMessages(warningMessages));
}
if (getMessageCodeObject(warningMessages[0]) != expectedWarningCode) {
Expect.fail("Expected ${expectedWarningCode.name} but got " +
joinMessages(warningMessages));
}
}
Future<Component> compileExpectOk(
bool initializedFromDill, Uri compileThis) async {
errorMessages.clear();
warningMessages.clear();
options.writeFileOnCrashReport = false;
compiler = new DeleteTempFilesIncrementalCompiler(
new CompilerContext(
new ProcessedOptions(options: options, inputs: [compileThis])),
initializeFrom);
IncrementalCompilerResult compilerResult = await compiler.computeDelta();
Component component = compilerResult.component;
if (compiler.initializedFromDillForTesting != initializedFromDill) {
Expect.fail("Expected initializedFromDill to be $initializedFromDill "
"but was ${compiler.initializedFromDillForTesting}");
}
if (errorMessages.isNotEmpty) {
Expect.fail("Got unexpected errors: " + joinMessages(errorMessages));
}
if (warningMessages.isNotEmpty) {
Expect.fail("Got unexpected warnings: " + joinMessages(warningMessages));
}
return component;
}
Future<void> initialize() async {
sdkRoot = computePlatformBinariesLocation(forceBuildDir: true);
base = Uri.parse("org-dartlang-test:///");
sdkSummary = base.resolve("vm_platform.dill");
initializeFrom = base.resolve("initializeFrom.dill");
helperFile = base.resolve("helper.dart");
helper2File = base.resolve("helper2.dart");
entryPoint = base.resolve("small.dart");
entryPointImportDartFoo = base.resolve("small_foo.dart");
platformUri = sdkRoot.resolve("vm_platform_strong.dill");
sdkSummaryData = await new File.fromUri(platformUri).readAsBytes();
errorMessages = <DiagnosticMessage>[];
warningMessages = <DiagnosticMessage>[];
fs = new MemoryFileSystem(base);
options = getOptions();
options.fileSystem = fs;
options.sdkRoot = null;
options.sdkSummary = sdkSummary;
options.omitPlatform = true;
options.onDiagnostic = (DiagnosticMessage message) {
if (message.severity == Severity.error) {
errorMessages.add(message);
} else if (message.severity == Severity.warning) {
warningMessages.add(message);
}
};
fs.entityForUri(sdkSummary).writeAsBytesSync(sdkSummaryData);
fs.entityForUri(helperFile).writeAsStringSync("""
foo() {
print("hello from foo");
}
""");
fs.entityForUri(helper2File).writeAsStringSync("""
foo2() {
print("hello from foo2");
}
""");
fs.entityForUri(entryPoint).writeAsStringSync("""
import "helper.dart" as helper;
main() {
helper.foo();
}
""");
fs.entityForUri(entryPointImportDartFoo).writeAsStringSync("""
import "dart:foo" as helper;
main() {
helper.foo2();
}
""");
}
Future<Null> test() async {
compiler = new IncrementalCompiler(
new CompilerContext(
new ProcessedOptions(options: options, inputs: [entryPoint])),
initializeFrom);
IncrementalCompilerResult compilerGoodResult =
await compiler.computeDelta();
Component componentGood = compilerGoodResult.component;
List<int> dataGood = serializeComponent(componentGood);
fs.entityForUri(initializeFrom).writeAsBytesSync(dataGood);
// Create fake "dart:foo" library.
options.omitPlatform = false;
compiler = new IncrementalCompiler(
new CompilerContext(
new ProcessedOptions(options: options, inputs: [helper2File])),
initializeFrom);
IncrementalCompilerResult compilerHelperResult =
await compiler.computeDelta();
Component componentHelper = compilerHelperResult.component;
Library helper2Lib = componentHelper.libraries
.firstWhere((lib) => lib.importUri == helper2File);
helper2Lib.importUri = new Uri(scheme: "dart", path: "foo");
List<int> sdkWithDartFoo = serializeComponent(componentHelper);
options.omitPlatform = true;
// Compile with our fake sdk with dart:foo should be ok.
List<int> orgSdkBytes = await fs.entityForUri(sdkSummary).readAsBytes();
fs.entityForUri(sdkSummary).writeAsBytesSync(sdkWithDartFoo);
Component component = await compileExpectOk(true, entryPointImportDartFoo);
fs.entityForUri(sdkSummary).writeAsBytesSync(orgSdkBytes);
if (component.libraries.length != 1) {
Expect.fail("Expected 1 library, got ${component.libraries.length}: "
"${component.libraries}");
}
List<int> dataLinkedToSdkWithFoo = serializeComponent(component);
// Initialize from good dill file should be ok.
await compileExpectOk(true, entryPoint);
// Create a partial dill file.
compiler.invalidate(entryPoint);
IncrementalCompilerResult compilerResult = await compiler.computeDelta();
component = compilerResult.component;
if (component.libraries.length != 1) {
Expect.fail("Expected 1 library, got ${component.libraries.length}: "
"${component.libraries}");
}
List<int> data = serializeComponent(component);
fs.entityForUri(initializeFrom).writeAsBytesSync(data);
// Initializing from partial dill should not be ok.
await compileExpectInitializeFailAndSpecificWarning(
codeInitializeFromDillNotSelfContained, true);
await compileExpectInitializeFailAndSpecificWarning(
codeInitializeFromDillNotSelfContainedNoDump, false);
// Create a invalid dill file to load from: Should not be ok.
data = new List<int>.filled(42, 42);
fs.entityForUri(initializeFrom).writeAsBytesSync(data);
await compileExpectInitializeFailAndSpecificWarning(
codeInitializeFromDillUnknownProblem, true);
await compileExpectInitializeFailAndSpecificWarning(
codeInitializeFromDillUnknownProblemNoDump, false);
// Create a dill with a reference to a non-existing sdk thing:
// Should be ok (for now), but we shouldn't actually initialize from dill.
fs.entityForUri(initializeFrom).writeAsBytesSync(dataLinkedToSdkWithFoo);
await compileExpectOk(false, entryPoint);
// Try to initialize from a dill which contains mixed compilation modes:
// Should be ok, but we shouldn't actually initialize from dill.
List<int> mixedPart1;
{
// Create a component that is compiled without NNBD.
Map<ExperimentalFlag, bool>? prevTesting =
options.defaultExperimentFlagsForTesting;
options.defaultExperimentFlagsForTesting = {
ExperimentalFlag.nonNullable: false
};
NnbdMode prevNnbd = options.nnbdMode;
options.nnbdMode = NnbdMode.Weak;
compiler = new IncrementalCompiler(
new CompilerContext(
new ProcessedOptions(options: options, inputs: [helper2File])),
null);
IncrementalCompilerResult result = await compiler.computeDelta();
Component c = result.component;
c.setMainMethodAndMode(
null, false, NonNullableByDefaultCompiledMode.Weak);
mixedPart1 = serializeComponent(c);
options.defaultExperimentFlagsForTesting = prevTesting;
options.nnbdMode = prevNnbd;
}
List<int> mixedPart2;
{
// Create a component that is compiled with strong NNBD.
Map<ExperimentalFlag, bool>? prevTesting =
options.defaultExperimentFlagsForTesting;
options.defaultExperimentFlagsForTesting = {
ExperimentalFlag.nonNullable: true
};
NnbdMode prevNnbd = options.nnbdMode;
options.nnbdMode = NnbdMode.Strong;
compiler = new IncrementalCompiler(
new CompilerContext(
new ProcessedOptions(options: options, inputs: [helperFile])),
null);
IncrementalCompilerResult result = await compiler.computeDelta();
Component c = result.component;
c.setMainMethodAndMode(
null, false, NonNullableByDefaultCompiledMode.Strong);
mixedPart2 = serializeComponent(c);
options.defaultExperimentFlagsForTesting = prevTesting;
options.nnbdMode = prevNnbd;
}
// Now mix the two components together and try to initialize from them.
// We expect the compilation to be OK but that the dill is not actually
// used to initialize from (as it's invalid because of the mixed mode).
List<int> mixed = [];
mixed.addAll(mixedPart1);
mixed.addAll(mixedPart2);
fs.entityForUri(initializeFrom).writeAsBytesSync(mixed);
await compileExpectOk(false, entryPoint);
}
}
class DeleteTempFilesIncrementalCompiler extends IncrementalCompiler {
DeleteTempFilesIncrementalCompiler(CompilerContext context,
[Uri? initializeFromDillUri])
: super(context, initializeFromDillUri);
@override
final RecorderForTesting recorderForTesting =
const DeleteTempFilesRecorderForTesting();
}
class DeleteTempFilesRecorderForTesting extends RecorderForTesting {
const DeleteTempFilesRecorderForTesting();
@override
void recordTemporaryFile(Uri uri) {
File f = new File.fromUri(uri);
if (f.existsSync()) f.deleteSync();
}
}
String joinMessages(List<DiagnosticMessage> messages) {
return messages.map((m) => m.plainTextFormatted.join("\n")).join("\n");
}