blob: 3c68a615dfd5968f284f3c045b4c20f0efaa0d82 [file] [log] [blame]
// 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:async' show Future;
import 'dart:io' show Directory, File;
import 'package:expect/expect.dart' show Expect;
import 'package:front_end/src/base/processed_options.dart'
show ProcessedOptions;
import 'package:front_end/src/fasta/compiler_context.dart' show CompilerContext;
import 'package:front_end/src/api_prototype/compiler_options.dart'
show CompilerOptions;
import 'package:front_end/src/api_prototype/diagnostic_message.dart'
show DiagnosticMessage, getMessageCodeObject;
import "package:front_end/src/api_prototype/memory_file_system.dart"
show MemoryFileSystem;
import 'package:front_end/src/compute_platform_binaries_location.dart'
show computePlatformBinariesLocation;
import 'package:front_end/src/fasta/incremental_compiler.dart'
show IncrementalCompiler;
import 'package:front_end/src/fasta/severity.dart' show Severity;
import 'package:kernel/binary/ast_from_binary.dart' show BinaryBuilder;
import 'package:kernel/kernel.dart'
show
Class,
Component,
EmptyStatement,
Field,
Library,
LibraryDependency,
Name,
Procedure;
import 'package:kernel/target/targets.dart' show TargetFlags;
import 'package:kernel/text/ast_to_text.dart' show componentToString;
import "package:testing/testing.dart"
show Chain, ChainContext, Result, Step, TestDescription, runMe;
import "package:vm/target/vm.dart" show VmTarget;
import "package:yaml/yaml.dart" show YamlList, YamlMap, loadYamlNode;
import "incremental_utils.dart" as util;
import 'package:front_end/src/fasta/fasta_codes.dart'
show DiagnosticMessageFromJson, FormattedMessage;
main([List<String> arguments = const []]) =>
runMe(arguments, createContext, "../testing.json");
Future<Context> createContext(
Chain suite, Map<String, String> environment) async {
return new Context();
}
class Context extends ChainContext {
final List<Step> steps = const <Step>[
const ReadTest(),
const RunCompilations(),
];
@override
Future<void> cleanUp(TestDescription description, Result result) async {
await cleanupHelper?.outDir?.delete(recursive: true);
}
TestData cleanupHelper;
}
class TestData {
YamlMap map;
Directory outDir;
}
class ReadTest extends Step<TestDescription, TestData, Context> {
const ReadTest();
String get name => "read test";
Future<Result<TestData>> run(
TestDescription description, Context context) async {
Uri uri = description.uri;
String contents = await new File.fromUri(uri).readAsString();
TestData data = new TestData();
data.map = loadYamlNode(contents, sourceUrl: uri);
data.outDir =
Directory.systemTemp.createTempSync("incremental_load_from_dill_test");
context.cleanupHelper = data;
return pass(data);
}
}
class RunCompilations extends Step<TestData, TestData, Context> {
const RunCompilations();
String get name => "run compilations";
Future<Result<TestData>> run(TestData data, Context context) async {
YamlMap map = data.map;
switch (map["type"]) {
case "basic":
await basicTest(
map["sources"],
map["entry"],
map["invalidate"],
data.outDir,
);
break;
case "newworld":
await newWorldTest(
map["worlds"],
map["modules"],
map["omitPlatform"],
);
break;
default:
throw "Unexpected type: ${map['type']}";
}
return pass(data);
}
}
Future<Null> basicTest(YamlMap sourceFiles, String entryPoint,
YamlList invalidate, Directory outDir) async {
Uri entryPointUri = outDir.uri.resolve(entryPoint);
Set<String> invalidateFilenames =
invalidate == null ? new Set<String>() : new Set<String>.from(invalidate);
List<Uri> invalidateUris = <Uri>[];
Uri packagesUri;
for (String filename in sourceFiles.keys) {
Uri uri = outDir.uri.resolve(filename);
if (invalidateFilenames.contains(filename)) {
invalidateUris.add(uri);
invalidateFilenames.remove(filename);
}
String source = sourceFiles[filename];
if (filename == ".packages") {
packagesUri = uri;
}
File file = new File.fromUri(uri);
await file.parent.create(recursive: true);
await file.writeAsString(source);
}
for (String invalidateFilename in invalidateFilenames) {
if (invalidateFilename.startsWith('package:')) {
invalidateUris.add(Uri.parse(invalidateFilename));
} else {
throw "Error in test yaml: $invalidateFilename was not recognized.";
}
}
Uri output = outDir.uri.resolve("full.dill");
Uri initializedOutput = outDir.uri.resolve("full_from_initialized.dill");
Stopwatch stopwatch = new Stopwatch()..start();
CompilerOptions options = getOptions();
if (packagesUri != null) {
options.packagesFileUri = packagesUri;
}
await normalCompile(entryPointUri, output, options: options);
print("Normal compile took ${stopwatch.elapsedMilliseconds} ms");
stopwatch.reset();
options = getOptions();
if (packagesUri != null) {
options.packagesFileUri = packagesUri;
}
bool initializedResult = await initializedCompile(
entryPointUri, initializedOutput, output, invalidateUris,
options: options);
print("Initialized compile(s) from ${output.pathSegments.last} "
"took ${stopwatch.elapsedMilliseconds} ms");
Expect.isTrue(initializedResult);
// Compare the two files.
List<int> normalDillData = new File.fromUri(output).readAsBytesSync();
List<int> initializedDillData =
new File.fromUri(initializedOutput).readAsBytesSync();
checkIsEqual(normalDillData, initializedDillData);
}
Future<Map<String, List<int>>> createModules(
Map module, final List<int> sdkSummaryData) async {
final Uri base = Uri.parse("org-dartlang-test:///");
final Uri sdkSummary = base.resolve("vm_platform.dill");
MemoryFileSystem fs = new MemoryFileSystem(base);
fs.entityForUri(sdkSummary).writeAsBytesSync(sdkSummaryData);
// Setup all sources
for (Map moduleSources in module.values) {
for (String filename in moduleSources.keys) {
String data = moduleSources[filename];
Uri uri = base.resolve(filename);
if (await fs.entityForUri(uri).exists())
throw "More than one entry for $filename";
fs.entityForUri(uri).writeAsStringSync(data);
}
}
Map<String, List<int>> moduleResult = new Map<String, List<int>>();
for (String moduleName in module.keys) {
List<Uri> moduleSources = new List<Uri>();
Uri packagesUri;
for (String filename in module[moduleName].keys) {
Uri uri = base.resolve(filename);
if (uri.pathSegments.last == ".packages") {
packagesUri = uri;
} else {
moduleSources.add(uri);
}
}
CompilerOptions options = getOptions();
options.fileSystem = fs;
options.sdkRoot = null;
options.sdkSummary = sdkSummary;
options.omitPlatform = true;
options.onDiagnostic = (DiagnosticMessage message) {
if (getMessageCodeObject(message)?.name == "InferredPackageUri") return;
throw message.ansiFormatted;
};
if (packagesUri != null) {
options.packagesFileUri = packagesUri;
}
TestIncrementalCompiler compiler =
new TestIncrementalCompiler(options, moduleSources.first, null);
Component c = await compiler.computeDelta(entryPoints: moduleSources);
c.computeCanonicalNames();
List<Library> wantedLibs = new List<Library>();
for (Library lib in c.libraries) {
if (moduleSources.contains(lib.importUri) ||
moduleSources.contains(lib.fileUri)) {
wantedLibs.add(lib);
}
}
if (wantedLibs.length != moduleSources.length) {
throw "Module probably not setup right.";
}
Component result = new Component(libraries: wantedLibs);
List<int> resultBytes = util.postProcess(result);
moduleResult[moduleName] = resultBytes;
}
return moduleResult;
}
Future<Null> newWorldTest(List worlds, Map modules, bool omitPlatform) async {
final Uri sdkRoot = computePlatformBinariesLocation(forceBuildDir: true);
final Uri base = Uri.parse("org-dartlang-test:///");
final Uri sdkSummary = base.resolve("vm_platform.dill");
final Uri initializeFrom = base.resolve("initializeFrom.dill");
Uri platformUri = sdkRoot.resolve("vm_platform_strong.dill");
final List<int> sdkSummaryData =
await new File.fromUri(platformUri).readAsBytes();
List<int> newestWholeComponentData;
Component newestWholeComponent;
MemoryFileSystem fs;
Map<String, String> sourceFiles;
CompilerOptions options;
TestIncrementalCompiler compiler;
Map<String, List<int>> moduleData;
Map<String, Component> moduleComponents;
Component sdk;
if (modules != null) {
moduleData = await createModules(modules, sdkSummaryData);
sdk = newestWholeComponent = new Component();
new BinaryBuilder(sdkSummaryData, filename: null, disableLazyReading: false)
.readComponent(newestWholeComponent);
}
for (YamlMap world in worlds) {
List<Component> modulesToUse;
if (world["modules"] != null) {
moduleComponents ??= new Map<String, Component>();
sdk.adoptChildren();
for (Component c in moduleComponents.values) {
c.adoptChildren();
}
modulesToUse = new List<Component>();
for (String moduleName in world["modules"]) {
Component moduleComponent = moduleComponents[moduleName];
if (moduleComponent != null) {
modulesToUse.add(moduleComponent);
}
}
for (String moduleName in world["modules"]) {
Component moduleComponent = moduleComponents[moduleName];
if (moduleComponent == null) {
moduleComponent = new Component(nameRoot: sdk.root);
new BinaryBuilder(moduleData[moduleName],
filename: null,
disableLazyReading: false,
alwaysCreateNewNamedNodes: true)
.readComponent(moduleComponent);
moduleComponents[moduleName] = moduleComponent;
modulesToUse.add(moduleComponent);
}
}
}
bool brandNewWorld = true;
if (world["worldType"] == "updated") {
brandNewWorld = false;
}
bool noFullComponent = false;
if (world["noFullComponent"] == true) {
noFullComponent = true;
}
if (brandNewWorld) {
fs = new MemoryFileSystem(base);
}
fs.entityForUri(sdkSummary).writeAsBytesSync(sdkSummaryData);
bool expectInitializeFromDill = false;
if (newestWholeComponentData != null &&
newestWholeComponentData.isNotEmpty) {
fs
.entityForUri(initializeFrom)
.writeAsBytesSync(newestWholeComponentData);
expectInitializeFromDill = true;
}
if (world["expectInitializeFromDill"] != null) {
expectInitializeFromDill = world["expectInitializeFromDill"];
}
if (brandNewWorld) {
sourceFiles = new Map<String, String>.from(world["sources"]);
} else {
sourceFiles.addAll(
new Map<String, String>.from(world["sources"] ?? <String, String>{}));
}
Uri packagesUri;
for (String filename in sourceFiles.keys) {
String data = sourceFiles[filename] ?? "";
Uri uri = base.resolve(filename);
if (filename == ".packages") {
packagesUri = uri;
}
fs.entityForUri(uri).writeAsStringSync(data);
}
if (world["dotPackagesFile"] != null) {
packagesUri = base.resolve(world["dotPackagesFile"]);
}
if (brandNewWorld) {
options = getOptions();
options.fileSystem = fs;
options.sdkRoot = null;
options.sdkSummary = sdkSummary;
options.omitPlatform = omitPlatform != false;
}
if (packagesUri != null) {
options.packagesFileUri = packagesUri;
}
bool gotError = false;
final Set<String> formattedErrors = Set<String>();
bool gotWarning = false;
final Set<String> formattedWarnings = Set<String>();
options.onDiagnostic = (DiagnosticMessage message) {
String stringId = message.ansiFormatted.join("\n");
if (message is FormattedMessage) {
stringId = message.toJsonString();
} else if (message is DiagnosticMessageFromJson) {
stringId = message.toJsonString();
}
if (message.severity == Severity.error) {
gotError = true;
if (!formattedErrors.add(stringId)) {
Expect.fail("Got the same message twice: ${stringId}");
}
} else if (message.severity == Severity.warning) {
gotWarning = true;
if (!formattedWarnings.add(stringId)) {
Expect.fail("Got the same message twice: ${stringId}");
}
}
};
List<Uri> entries;
if (world["entry"] is String) {
entries = [base.resolve(world["entry"])];
} else {
entries = new List<Uri>();
List<dynamic> entryList = world["entry"];
for (String entry in entryList) {
entries.add(base.resolve(entry));
}
}
bool outlineOnly = world["outlineOnly"] == true;
if (brandNewWorld) {
if (world["fromComponent"] == true) {
compiler = new TestIncrementalCompiler.fromComponent(
options, entries.first, newestWholeComponent, outlineOnly);
} else {
compiler = new TestIncrementalCompiler(
options, entries.first, initializeFrom, outlineOnly);
}
}
List<Uri> invalidated = new List<Uri>();
if (world["invalidate"] != null) {
for (String filename in world["invalidate"]) {
Uri uri = base.resolve(filename);
invalidated.add(uri);
compiler.invalidate(uri);
}
}
if (modulesToUse != null) {
compiler.setModulesToLoadOnNextComputeDelta(modulesToUse);
compiler.invalidateAllSources();
}
Stopwatch stopwatch = new Stopwatch()..start();
Component component = await compiler.computeDelta(
entryPoints: entries,
fullComponent: brandNewWorld ? false : (noFullComponent ? false : true),
simulateTransformer: world["simulateTransformer"]);
if (outlineOnly) {
for (Library lib in component.libraries) {
for (Class c in lib.classes) {
for (Procedure p in c.procedures) {
if (p.function.body != null && p.function.body is! EmptyStatement) {
throw "Got body (${p.function.body.runtimeType})";
}
}
}
for (Procedure p in lib.procedures) {
if (p.function.body != null && p.function.body is! EmptyStatement) {
throw "Got body (${p.function.body.runtimeType})";
}
}
}
}
performErrorAndWarningCheck(
world, gotError, formattedErrors, gotWarning, formattedWarnings);
util.throwOnEmptyMixinBodies(component);
util.throwOnInsufficientUriToSource(component);
print("Compile took ${stopwatch.elapsedMilliseconds} ms");
checkExpectedContent(world, component);
if (!noFullComponent) {
Set<Library> allLibraries = new Set<Library>();
for (Library lib in component.libraries) {
computeAllReachableLibrariesFor(lib, allLibraries);
}
if (allLibraries.length != component.libraries.length) {
Expect.fail("Expected for the reachable stuff to be equal to "
"${component.libraries} but it was $allLibraries");
}
Set<Library> tooMany = allLibraries.toSet()
..removeAll(component.libraries);
if (tooMany.isNotEmpty) {
Expect.fail("Expected for the reachable stuff to be equal to "
"${component.libraries} but these were there too: $tooMany "
"(and others were missing)");
}
}
newestWholeComponentData = util.postProcess(component);
newestWholeComponent = component;
print("*****\n\ncomponent:\n"
"${componentToStringSdkFiltered(component)}\n\n\n");
if (world["uriToSourcesDoesntInclude"] != null) {
for (String filename in world["uriToSourcesDoesntInclude"]) {
Uri uri = base.resolve(filename);
if (component.uriToSource[uri] != null) {
throw "Expected no uriToSource for $uri but found "
"${component.uriToSource[uri]}";
}
}
}
int nonSyntheticLibraries = countNonSyntheticLibraries(component);
int nonSyntheticPlatformLibraries =
countNonSyntheticPlatformLibraries(component);
int syntheticLibraries = countSyntheticLibraries(component);
if (world["expectsPlatform"] == true) {
if (nonSyntheticPlatformLibraries < 5)
throw "Expected to have at least 5 platform libraries "
"(actually, the entire sdk), "
"but got $nonSyntheticPlatformLibraries.";
} else {
if (nonSyntheticPlatformLibraries != 0)
throw "Expected to have 0 platform libraries "
"but got $nonSyntheticPlatformLibraries.";
}
if (world["expectedLibraryCount"] != null) {
if (nonSyntheticLibraries - nonSyntheticPlatformLibraries !=
world["expectedLibraryCount"]) {
throw "Expected ${world["expectedLibraryCount"]} non-synthetic "
"libraries, got "
"${nonSyntheticLibraries - nonSyntheticPlatformLibraries} "
"(not counting platform libraries)";
}
}
if (world["expectedSyntheticLibraryCount"] != null) {
if (syntheticLibraries != world["expectedSyntheticLibraryCount"]) {
throw "Expected ${world["expectedSyntheticLibraryCount"]} synthetic "
"libraries, got ${syntheticLibraries}";
}
}
if (!noFullComponent) {
List<Library> entryLib = component.libraries
.where((Library lib) =>
entries.contains(lib.importUri) || entries.contains(lib.fileUri))
.toList();
if (entryLib.length != entries.length) {
throw "Expected the entries to become libraries. Got ${entryLib.length} "
"libraries for the expected ${entries.length} entries.";
}
}
if (compiler.initializedFromDill != expectInitializeFromDill) {
throw "Expected that initializedFromDill would be "
"$expectInitializeFromDill but was ${compiler.initializedFromDill}";
}
if (world["checkInvalidatedFiles"] != false) {
Set<Uri> filteredInvalidated =
compiler.getFilteredInvalidatedImportUrisForTesting(invalidated);
if (world["invalidate"] != null) {
Expect.equals(
world["invalidate"].length, filteredInvalidated?.length ?? 0);
List expectedInvalidatedUri = world["expectedInvalidatedUri"];
if (expectedInvalidatedUri != null) {
Expect.setEquals(expectedInvalidatedUri.map((s) => base.resolve(s)),
filteredInvalidated);
}
} else {
Expect.isNull(filteredInvalidated);
Expect.isNull(world["expectedInvalidatedUri"]);
}
}
if (!noFullComponent) {
Set<String> prevFormattedErrors = formattedErrors.toSet();
Set<String> prevFormattedWarnings = formattedWarnings.toSet();
gotError = false;
formattedErrors.clear();
gotWarning = false;
formattedWarnings.clear();
Component component2 = await compiler.computeDelta(
entryPoints: entries,
fullComponent: true,
simulateTransformer: world["simulateTransformer"]);
performErrorAndWarningCheck(
world, gotError, formattedErrors, gotWarning, formattedWarnings);
List<int> thisWholeComponent = util.postProcess(component2);
print("*****\n\ncomponent2:\n"
"${componentToStringSdkFiltered(component2)}\n\n\n");
checkIsEqual(newestWholeComponentData, thisWholeComponent);
if (prevFormattedErrors.length != formattedErrors.length) {
Expect.fail("Previously had ${prevFormattedErrors.length} errors, "
"now had ${formattedErrors.length}.\n\n"
"Before:\n"
"${prevFormattedErrors.join("\n")}"
"\n\n"
"Now:\n"
"${formattedErrors.join("\n")}");
}
if ((prevFormattedErrors.toSet()..removeAll(formattedErrors))
.isNotEmpty) {
Expect.fail("Previously got error messages $prevFormattedErrors, "
"now had ${formattedErrors}.");
}
if (prevFormattedWarnings.length != formattedWarnings.length) {
Expect.fail("Previously had ${prevFormattedWarnings.length} errors, "
"now had ${formattedWarnings.length}.");
}
if ((prevFormattedWarnings.toSet()..removeAll(formattedWarnings))
.isNotEmpty) {
Expect.fail("Previously got error messages $prevFormattedWarnings, "
"now had ${formattedWarnings}.");
}
}
if (world["expressionCompilation"] != null) {
Uri uri = base.resolve(world["expressionCompilation"]["uri"]);
String expression = world["expressionCompilation"]["expression"];
await compiler.compileExpression(expression, {}, [], "debugExpr", uri);
}
}
}
void computeAllReachableLibrariesFor(Library lib, Set<Library> allLibraries) {
Set<Library> libraries = new Set<Library>();
List<Library> workList = <Library>[];
allLibraries.add(lib);
libraries.add(lib);
workList.add(lib);
while (workList.isNotEmpty) {
Library library = workList.removeLast();
for (LibraryDependency dependency in library.dependencies) {
if (libraries.add(dependency.targetLibrary)) {
workList.add(dependency.targetLibrary);
allLibraries.add(dependency.targetLibrary);
}
}
}
}
void checkExpectedContent(YamlMap world, Component component) {
if (world["expectedContent"] != null) {
Map<String, Set<String>> actualContent = new Map<String, Set<String>>();
for (Library lib in component.libraries) {
Set<String> libContent =
actualContent[lib.importUri.toString()] = new Set<String>();
for (Class c in lib.classes) {
libContent.add("Class ${c.name}");
}
for (Procedure p in lib.procedures) {
libContent.add("Procedure ${p.name}");
}
for (Field f in lib.fields) {
libContent.add("Field ${f.name}");
}
}
Map expectedContent = world["expectedContent"];
doThrow() {
throw "Expected and actual content not the same.\n"
"Expected $expectedContent.\n"
"Got $actualContent";
}
if (actualContent.length != expectedContent.length) doThrow();
Set<String> missingKeys = actualContent.keys.toSet()
..removeAll(expectedContent.keys);
if (missingKeys.isNotEmpty) doThrow();
for (String key in expectedContent.keys) {
Set<String> expected = new Set<String>.from(expectedContent[key]);
Set<String> actual = actualContent[key].toSet();
if (expected.length != actual.length) doThrow();
actual.removeAll(expected);
if (actual.isNotEmpty) doThrow();
}
}
}
String componentToStringSdkFiltered(Component node) {
Component c = new Component();
List<Uri> dartUris = new List<Uri>();
for (Library lib in node.libraries) {
if (lib.importUri.scheme == "dart") {
dartUris.add(lib.importUri);
} else {
c.libraries.add(lib);
}
}
StringBuffer s = new StringBuffer();
s.write(componentToString(c));
if (dartUris.isNotEmpty) {
s.writeln("");
s.writeln("And ${dartUris.length} platform libraries:");
for (Uri uri in dartUris) {
s.writeln(" - $uri");
}
}
return s.toString();
}
int countNonSyntheticLibraries(Component c) {
int result = 0;
for (Library lib in c.libraries) {
if (!lib.isSynthetic) result++;
}
return result;
}
int countNonSyntheticPlatformLibraries(Component c) {
int result = 0;
for (Library lib in c.libraries) {
if (!lib.isSynthetic && lib.importUri.scheme == "dart") result++;
}
return result;
}
int countSyntheticLibraries(Component c) {
int result = 0;
for (Library lib in c.libraries) {
if (lib.isSynthetic) result++;
}
return result;
}
void performErrorAndWarningCheck(
YamlMap world,
bool gotError,
Set<String> formattedErrors,
bool gotWarning,
Set<String> formattedWarnings) {
if (world["errors"] == true && !gotError) {
throw "Expected error, but didn't get any.";
} else if (world["errors"] != true && gotError) {
throw "Got unexpected error(s): $formattedErrors.";
}
if (world["warnings"] == true && !gotWarning) {
throw "Expected warning, but didn't get any.";
} else if (world["warnings"] != true && gotWarning) {
throw "Got unexpected warnings(s): $formattedWarnings.";
}
}
void checkIsEqual(List<int> a, List<int> b) {
int length = a.length;
if (b.length < length) {
length = b.length;
}
for (int i = 0; i < length; ++i) {
if (a[i] != b[i]) {
Expect.fail("Data differs at byte ${i + 1}.");
}
}
Expect.equals(a.length, b.length);
}
CompilerOptions getOptions() {
final Uri sdkRoot = computePlatformBinariesLocation(forceBuildDir: true);
CompilerOptions options = new CompilerOptions()
..sdkRoot = sdkRoot
..target = new VmTarget(new TargetFlags())
..librariesSpecificationUri = Uri.base.resolve("sdk/lib/libraries.json")
..omitPlatform = true
..onDiagnostic = (DiagnosticMessage message) {
if (message.severity == Severity.error ||
message.severity == Severity.warning) {
Expect.fail(
"Unexpected error: ${message.plainTextFormatted.join('\n')}");
}
};
options.sdkSummary = sdkRoot.resolve("vm_platform_strong.dill");
return options;
}
Future<bool> normalCompile(Uri input, Uri output,
{CompilerOptions options}) async {
options ??= getOptions();
TestIncrementalCompiler compiler =
new TestIncrementalCompiler(options, input);
Component component = await compiler.computeDelta();
util.throwOnEmptyMixinBodies(component);
util.throwOnInsufficientUriToSource(component);
new File.fromUri(output).writeAsBytesSync(util.postProcess(component));
return compiler.initializedFromDill;
}
Future<bool> initializedCompile(
Uri input, Uri output, Uri initializeWith, List<Uri> invalidateUris,
{CompilerOptions options}) async {
options ??= getOptions();
TestIncrementalCompiler compiler =
new TestIncrementalCompiler(options, input, initializeWith);
for (Uri invalidateUri in invalidateUris) {
compiler.invalidate(invalidateUri);
}
Component initializedComponent = await compiler.computeDelta();
util.throwOnEmptyMixinBodies(initializedComponent);
util.throwOnInsufficientUriToSource(initializedComponent);
bool result = compiler.initializedFromDill;
new File.fromUri(output)
.writeAsBytesSync(util.postProcess(initializedComponent));
int actuallyInvalidatedCount = compiler
.getFilteredInvalidatedImportUrisForTesting(invalidateUris)
?.length ??
0;
if (result && actuallyInvalidatedCount < invalidateUris.length) {
Expect.fail("Expected at least ${invalidateUris.length} invalidated uris, "
"got $actuallyInvalidatedCount");
}
Component initializedFullComponent =
await compiler.computeDelta(fullComponent: true);
util.throwOnEmptyMixinBodies(initializedFullComponent);
util.throwOnInsufficientUriToSource(initializedFullComponent);
Expect.equals(initializedComponent.libraries.length,
initializedFullComponent.libraries.length);
Expect.equals(initializedComponent.uriToSource.length,
initializedFullComponent.uriToSource.length);
for (Uri invalidateUri in invalidateUris) {
compiler.invalidate(invalidateUri);
}
Component partialComponent = await compiler.computeDelta();
util.throwOnEmptyMixinBodies(partialComponent);
util.throwOnInsufficientUriToSource(partialComponent);
actuallyInvalidatedCount = (compiler
.getFilteredInvalidatedImportUrisForTesting(invalidateUris)
?.length ??
0);
if (actuallyInvalidatedCount < invalidateUris.length) {
Expect.fail("Expected at least ${invalidateUris.length} invalidated uris, "
"got $actuallyInvalidatedCount");
}
Component emptyComponent = await compiler.computeDelta();
util.throwOnEmptyMixinBodies(emptyComponent);
util.throwOnInsufficientUriToSource(emptyComponent);
List<Uri> fullLibUris =
initializedComponent.libraries.map((lib) => lib.importUri).toList();
List<Uri> partialLibUris =
partialComponent.libraries.map((lib) => lib.importUri).toList();
List<Uri> emptyLibUris =
emptyComponent.libraries.map((lib) => lib.importUri).toList();
Expect.isTrue(fullLibUris.length > partialLibUris.length ||
partialLibUris.length == invalidateUris.length);
Expect.isTrue(partialLibUris.isNotEmpty || invalidateUris.isEmpty);
Expect.isTrue(emptyLibUris.isEmpty);
return result;
}
class TestIncrementalCompiler extends IncrementalCompiler {
Set<Uri> invalidatedImportUrisForTesting;
final Uri entryPoint;
/// Filter out the automatically added entryPoint, unless it's explicitly
/// specified as being invalidated.
/// Also filter out uris with "nonexisting.dart" in the name as synthetic
/// libraries are invalidated automatically too.
/// This is not perfect, but works for what it's currently used for.
Set<Uri> getFilteredInvalidatedImportUrisForTesting(
List<Uri> invalidatedUris) {
if (invalidatedImportUrisForTesting == null) return null;
Set<String> invalidatedFilenames =
invalidatedUris.map((uri) => uri.pathSegments.last).toSet();
Set<Uri> result = new Set<Uri>();
for (Uri uri in invalidatedImportUrisForTesting) {
if (uri.pathSegments.last == "nonexisting.dart") continue;
if (invalidatedFilenames.contains(entryPoint.pathSegments.last) ||
invalidatedFilenames.contains(uri.pathSegments.last)) result.add(uri);
}
return result.isEmpty ? null : result;
}
TestIncrementalCompiler(CompilerOptions options, this.entryPoint,
[Uri initializeFrom, bool outlineOnly])
: super(
new CompilerContext(
new ProcessedOptions(options: options, inputs: [entryPoint])),
initializeFrom,
outlineOnly);
TestIncrementalCompiler.fromComponent(CompilerOptions options,
this.entryPoint, Component componentToInitializeFrom, [bool outlineOnly])
: super.fromComponent(
new CompilerContext(
new ProcessedOptions(options: options, inputs: [entryPoint])),
componentToInitializeFrom,
outlineOnly);
@override
void recordInvalidatedImportUrisForTesting(List<Uri> uris) {
invalidatedImportUrisForTesting = uris.isEmpty ? null : uris.toSet();
}
@override
void recordNonFullComponentForTesting(Component component) {
// It should at least contain the sdk. Slight smoke test.
if (!component.libraries
.map((lib) => lib.importUri.toString())
.contains("dart:core")) {
throw "Loaders builder should contain the sdk, "
"but didn't even contain dart:core.";
}
}
@override
Future<Component> computeDelta(
{List<Uri> entryPoints,
bool fullComponent = false,
bool simulateTransformer}) async {
Component result = await super
.computeDelta(entryPoints: entryPoints, fullComponent: fullComponent);
// We should at least have the SDK builders available. Slight smoke test.
if (!dillLoadedData.loader.builders.keys
.map((uri) => uri.toString())
.contains("dart:core")) {
throw "Loaders builder should contain the sdk, "
"but didn't even contain dart:core.";
}
if (simulateTransformer == true) {
doSimulateTransformer(result);
}
return result;
}
}
void doSimulateTransformer(Component c) {
for (Library lib in c.libraries) {
if (lib.fields
.where((f) => f.name.name == "lalala_SimulateTransformer")
.toList()
.isNotEmpty) continue;
Name fieldName = new Name("lalala_SimulateTransformer");
Field field = new Field(fieldName,
isFinal: true,
reference: lib.reference.canonicalName
?.getChildFromFieldWithName(fieldName)
?.reference);
lib.addMember(field);
for (Class c in lib.classes) {
if (c.fields
.where((f) => f.name.name == "lalala_SimulateTransformer")
.toList()
.isNotEmpty) continue;
fieldName = new Name("lalala_SimulateTransformer");
field = new Field(fieldName,
isFinal: true,
reference: c.reference.canonicalName
?.getChildFromFieldWithName(fieldName)
?.reference);
c.addMember(field);
}
}
}