blob: 0a7b306963dd94d550d1c6185aa65669b1c1ea5e [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.
// @dart = 2.7
import 'dart:io';
import 'package:compiler/compiler_new.dart';
import 'package:compiler/src/commandline_options.dart';
import 'package:compiler/src/compiler.dart';
import 'package:compiler/src/js_model/js_world.dart';
import 'package:compiler/src/inferrer/types.dart';
import 'package:compiler/src/serialization/serialization.dart';
import 'package:compiler/src/serialization/strategies.dart';
import 'package:compiler/src/serialization/task.dart';
import 'package:expect/expect.dart';
import 'package:kernel/ast.dart' as ir;
import '../helpers/memory_compiler.dart';
import '../helpers/text_helpers.dart';
/// Entries in dump info that naturally differ between compilations.
const List<String> dumpInfoExceptions = [
'"compilationMoment":',
'"compilationDuration":',
'"toJsonDuration":'
];
void finishCompileAndCompare(
Map<OutputType, Map<String, String>> expectedOutput,
OutputCollector actualOutputCollector,
Compiler compiler,
SerializationStrategy strategy,
{bool stoppedAfterClosedWorld = false,
bool stoppedAfterTypeInference = false}) {
if (stoppedAfterClosedWorld) {
JsClosedWorld closedWorld = compiler.backendClosedWorldForTesting;
var newClosedWorldAndIndices =
cloneClosedWorld(compiler, closedWorld, strategy);
compiler.performGlobalTypeInference(newClosedWorldAndIndices.closedWorld);
}
if (stoppedAfterClosedWorld || stoppedAfterTypeInference) {
GlobalTypeInferenceResults globalInferenceResults =
compiler.globalInference.resultsForTesting;
var indices = compiler.closedWorldIndicesForTesting;
GlobalTypeInferenceResults newGlobalInferenceResults =
cloneInferenceResults(
indices, compiler, globalInferenceResults, strategy);
compiler.generateJavaScriptCode(newGlobalInferenceResults);
}
var actualOutput = actualOutputCollector.clear();
Expect.setEquals(
expectedOutput.keys, actualOutput.keys, "Output type mismatch.");
void check(OutputType outputType, Map<String, String> fileMap) {
Map<String, String> newFileMap = actualOutput[outputType];
Expect.setEquals(fileMap.keys, newFileMap.keys,
"File mismatch for output type $outputType.");
fileMap.forEach((String fileName, String code) {
String newCode = newFileMap[fileName];
bool Function(int, List<String>, List<String>) filter;
if (outputType == OutputType.dumpInfo) {
filter = (int index, List<String> lines1, List<String> lines2) {
if (index <= lines1.length && index <= lines2.length) {
String line1 = lines1[index];
String line2 = lines2[index];
for (String exception in dumpInfoExceptions) {
if (line1.trim().startsWith(exception) &&
line2.trim().startsWith(exception)) {
return true;
}
}
}
return false;
};
}
int failureLine =
checkEqualContentAndShowDiff(code, newCode, filter: filter);
Expect.isNull(
failureLine,
"Output mismatch at line $failureLine in "
"file '${fileName}' of type ${outputType}.");
});
}
expectedOutput.forEach(check);
}
runTest(
{Uri entryPoint,
Map<String, String> memorySourceFiles: const <String, String>{},
Uri packageConfig,
Uri librariesSpecificationUri,
List<String> options,
SerializationStrategy strategy: const BytesInMemorySerializationStrategy(),
bool useDataKinds: false}) async {
var commonOptions = options + ['--out=out.js'];
OutputCollector collector = new OutputCollector();
CompilationResult result = await runCompiler(
entryPoint: entryPoint,
memorySourceFiles: memorySourceFiles,
packageConfig: packageConfig,
librariesSpecificationUri: librariesSpecificationUri,
options: commonOptions,
outputProvider: collector,
beforeRun: (Compiler compiler) {
compiler.kernelLoader.forceSerialization = true;
});
Expect.isTrue(result.isSuccess);
Map<OutputType, Map<String, String>> expectedOutput = collector.clear();
OutputCollector collector2 = new OutputCollector();
CompilationResult result2 = await runCompiler(
entryPoint: entryPoint,
memorySourceFiles: memorySourceFiles,
packageConfig: packageConfig,
librariesSpecificationUri: librariesSpecificationUri,
options: commonOptions,
outputProvider: collector2,
beforeRun: (Compiler compiler) {
compiler.kernelLoader.forceSerialization = true;
compiler.stopAfterClosedWorld = true;
});
Expect.isTrue(result2.isSuccess);
var dillUri = Uri.parse('out.dill');
var closedWorldUri = Uri.parse('world.data');
OutputCollector collector3a = new OutputCollector();
CompilationResult result3a = await runCompiler(
entryPoint: entryPoint,
memorySourceFiles: memorySourceFiles,
packageConfig: packageConfig,
librariesSpecificationUri: librariesSpecificationUri,
options: options +
['--out=$dillUri', '${Flags.writeClosedWorld}=$closedWorldUri'],
outputProvider: collector3a,
beforeRun: (Compiler compiler) {
compiler.kernelLoader.forceSerialization = true;
});
Expect.isTrue(result3a.isSuccess);
Expect.isTrue(collector3a.binaryOutputMap.containsKey(dillUri));
Expect.isTrue(collector3a.binaryOutputMap.containsKey(closedWorldUri));
Directory dir =
await Directory.systemTemp.createTemp('serialization_test_helper');
var dillFileUri = dir.uri.resolve('out.dill');
var closedWorldFileUri = dir.uri.resolve('world.data');
var dillBytes = collector3a.binaryOutputMap[dillUri].list;
var closedWorldBytes = collector3a.binaryOutputMap[closedWorldUri].list;
File(dillFileUri.path).writeAsBytesSync(dillBytes);
File(closedWorldFileUri.path).writeAsBytesSync(closedWorldBytes);
OutputCollector collector3b = new OutputCollector();
CompilationResult result3b = await runCompiler(
entryPoint: entryPoint,
memorySourceFiles: memorySourceFiles,
packageConfig: packageConfig,
librariesSpecificationUri: librariesSpecificationUri,
options: commonOptions +
[
'${Flags.inputDill}=$dillFileUri',
'${Flags.readClosedWorld}=$closedWorldFileUri',
'${Flags.writeData}=global.data'
],
outputProvider: collector3b,
beforeRun: (Compiler compiler) {
compiler.kernelLoader.forceSerialization = true;
compiler.stopAfterTypeInference = true;
});
Expect.isTrue(result3b.isSuccess);
finishCompileAndCompare(
expectedOutput, collector2, result2.compiler, strategy,
stoppedAfterClosedWorld: true);
finishCompileAndCompare(
expectedOutput, collector3b, result3b.compiler, strategy,
stoppedAfterTypeInference: true);
await dir.delete(recursive: true);
}
void checkData(List<int> data, List<int> newData) {
Expect.equals(
data.length, newData.length, "Reserialization data length mismatch.");
for (int i = 0; i < data.length; i++) {
if (data[i] != newData[i]) {
print('Reserialization data mismatch at offset $i:');
for (int j = i - 50; j < i + 50; j++) {
if (0 <= j && j <= data.length) {
String text;
if (data[j] == newData[j]) {
text = '${data[j]}';
} else {
text = '${data[j]} <> ${newData[j]}';
}
print('${j == i ? '> ' : ' '}$j: $text');
}
}
break;
}
}
Expect.listEquals(data, newData);
}
ClosedWorldAndIndices cloneClosedWorld(Compiler compiler,
JsClosedWorld closedWorld, SerializationStrategy strategy) {
ir.Component component = closedWorld.elementMap.programEnv.mainComponent;
List<int> irData = strategy.serializeComponent(component);
List<int> closedWorldData = strategy.serializeClosedWorld(closedWorld);
print('data size: ${closedWorldData.length}');
ir.Component newComponent = strategy.deserializeComponent(irData);
var newClosedWorldAndIndices = strategy.deserializeClosedWorld(
compiler.options,
compiler.reporter,
compiler.environment,
compiler.abstractValueStrategy,
newComponent,
closedWorldData);
List<int> newClosedWorldData =
strategy.serializeClosedWorld(newClosedWorldAndIndices.closedWorld);
checkData(closedWorldData, newClosedWorldData);
return newClosedWorldAndIndices;
}
GlobalTypeInferenceResults cloneInferenceResults(
DataSourceIndices indices,
Compiler compiler,
GlobalTypeInferenceResults results,
SerializationStrategy strategy) {
List<int> irData = strategy.unpackAndSerializeComponent(results);
List<int> closedWorldData =
strategy.serializeClosedWorld(results.closedWorld);
List<int> worldData =
strategy.serializeGlobalTypeInferenceResults(indices, results);
print('data size: ${worldData.length}');
ir.Component newComponent = strategy.deserializeComponent(irData);
var newClosedWorldAndIndices = strategy.deserializeClosedWorld(
compiler.options,
compiler.reporter,
compiler.environment,
compiler.abstractValueStrategy,
newComponent,
closedWorldData);
var newIndices = indices == null ? null : newClosedWorldAndIndices.indices;
GlobalTypeInferenceResults newResults =
strategy.deserializeGlobalTypeInferenceResults(
compiler.options,
compiler.reporter,
compiler.environment,
compiler.abstractValueStrategy,
newComponent,
newClosedWorldAndIndices.closedWorld,
newIndices,
worldData);
List<int> newWorldData =
strategy.serializeGlobalTypeInferenceResults(newIndices, newResults);
checkData(worldData, newWorldData);
return newResults;
}