blob: b65f5b69960bd2a0dc0c106aaa89df52068ab5a7 [file] [log] [blame]
// Copyright (c) 2017, 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.
// Helper to test compilation equivalence between source and .dill based
// compilation.
library dart2js.kernel.compile_from_dill_test_helper;
import 'dart:async';
import 'package:compiler/compiler_new.dart';
import 'package:compiler/src/commandline_options.dart';
import 'package:compiler/src/common.dart';
import 'package:compiler/src/compiler.dart';
import 'package:compiler/src/elements/elements.dart';
import 'package:compiler/src/elements/types.dart';
import 'package:compiler/src/kernel/element_map.dart';
import 'package:compiler/src/kernel/kernel_backend_strategy.dart';
import 'package:compiler/src/kernel/kernel_strategy.dart';
import 'package:compiler/src/serialization/equivalence.dart';
import 'package:compiler/src/resolution/enum_creator.dart';
import 'package:compiler/src/universe/world_builder.dart';
import 'package:compiler/src/world.dart';
import 'package:expect/expect.dart';
import '../memory_compiler.dart';
import '../equivalence/check_functions.dart';
import '../equivalence/check_helpers.dart';
import '../serialization/helper.dart';
import 'test_helpers.dart';
import 'compiler_helper.dart';
class Test {
final Uri uri;
final Map<String, String> sources;
final bool expectIdenticalOutput;
const Test(this.sources, {this.expectIdenticalOutput: true}) : uri = null;
Test.fromUri(this.uri, {this.expectIdenticalOutput: true})
: sources = const {};
Uri get entryPoint => uri ?? Uri.parse('memory:main.dart');
String toString() => uri != null ? '$uri' : sources.values.first;
}
const List<Test> TESTS = const <Test>[
const Test(const {
'main.dart': '''
import 'dart:html';
import 'package:expect/expect.dart';
foo({named}) => 1;
bar(a) => !a;
class Class {
var field;
static var staticField;
Class();
Class.named(this.field) {
staticField = 42;
}
method() {}
}
class SubClass extends Class {
method() {
super.method();
}
}
class Generic<T> {
method(o) => o is T;
}
var toplevel;
typedef Typedef();
main() {
foo();
bar(true);
[];
<int>[];
{};
new Object();
new Class.named('');
new SubClass().method();
Class.staticField;
var x = null;
var y1 = x == null;
var y2 = null == x;
var z1 = x?.toString();
var z2 = x ?? y1;
var z3 = x ??= y2;
var w = x == null ? null : x.toString();
for (int i = 0; i < 10; i++) {
if (i == 5) continue;
x = i;
if (i == 5) break;
}
int i = 0;
while (i < 10) {
if (i == 5) continue;
x = i;
if (i == 7) break;
i++;
}
for (var v in [3, 5]) {
if (v == 5) continue;
x = v;
if (v == 7) break;
}
do {
x = i;
if (i == 7) break;
i++;
} while (i < 10);
switch (x) {
case 0:
x = 7;
break;
case 1:
x = 9;
break;
default:
x = 11;
break;
}
x = toplevel;
x = testIs(x);
x = new Generic<int>().method(x);
x = testAsGeneric(x);
x = testAsFunction(x);
print(x);
var f = (x) {
return 400 + x;
};
x = f(x);
x = Object;
x = Typedef;
return x;
}
typedef NoArg();
@NoInline()
testIs(o) => o is Generic<int> || o is NoArg;
@NoInline()
testAsGeneric(o) => o as Generic<int>;
@NoInline()
testAsFunction(o) => o as NoArg;
'''
}),
const Test(const {
'main.dart': '''
main() {
var x;
int i = 0;
do {
if (i == 5) continue;
x = i;
if (i == 7) break;
i++;
} while (i < 10);
print(x);
}
'''
}, expectIdenticalOutput: false),
const Test(const {
'main.dart': '''
class A<U,V> {
var a = U;
var b = V;
}
class B<Q, R> extends A<R, W<Q>> {
}
class C<Y> extends B<Y, W<Y>> {
}
class W<Z> {}
main() {
print(new C<String>().a);
}
'''
}, expectIdenticalOutput: true),
const Test(const {
'main.dart': '''
class _Marker { const _Marker(); }
const _MARKER = const _Marker();
class Thing<X> {
Thing([length = _MARKER]);
}
foo(x) {
print(new List(x).length);
}
main() {
print(new Thing<String>(100));
print(new List());
print(new List(4));
foo(3);
foo(4);
}
'''
}, expectIdenticalOutput: true),
];
enum ResultKind { crashes, errors, warnings, success, failure }
const List<String> commonOptions = const <String>[
Flags.disableTypeInference,
Flags.disableInlining,
Flags.enableAssertMessage
];
Future runTests(List<String> args,
{bool skipWarnings: false,
bool skipErrors: false,
List<String> options: const <String>[]}) async {
Arguments arguments = new Arguments.from(args);
List<Test> tests = TESTS;
if (arguments.start != null) {
int start = arguments.start;
int end = arguments.end ?? 0; // Default 'end' to single test.
if (end > tests.length) end = tests.length; // Large 'end' means all.
if (end <= start) end = start + 1; // Always at least one test (else Error).
tests = tests.sublist(start, end);
} else if (arguments.uri != null) {
tests = <Test>[new Test.fromUri(arguments.uri)];
}
for (Test test in tests) {
if (test.uri != null) {
print('--- running test uri ${test.uri} -------------------------------');
} else {
print(
'--- running test code -------------------------------------------');
print(test.sources.values.first);
print('----------------------------------------------------------------');
}
await runTest(test.entryPoint, test.sources,
verbose: arguments.verbose,
skipWarnings: skipWarnings,
skipErrors: skipErrors,
options: options,
expectIdenticalOutput: test.expectIdenticalOutput);
}
}
Future<ResultKind> runTest(
Uri entryPoint, Map<String, String> memorySourceFiles,
{bool skipWarnings: false,
bool skipErrors: false,
bool verbose: false,
List<String> options: const <String>[],
bool expectAstEquivalence: false,
bool expectIdenticalOutput: true}) async {
enableDebugMode();
EnumCreator.matchKernelRepresentationForTesting = true;
Elements.usePatchedDart2jsSdkSorting = true;
entryPoint =
await createTemp(entryPoint, memorySourceFiles, printSteps: true);
print('---- compile from ast ----------------------------------------------');
DiagnosticCollector collector = new DiagnosticCollector();
OutputCollector collector1 = new OutputCollector();
Compiler compiler1 = compilerFor(
entryPoint: entryPoint,
diagnosticHandler: collector,
outputProvider: collector1,
options: <String>[]..addAll(commonOptions)..addAll(options));
ElementResolutionWorldBuilder.useInstantiationMap = true;
compiler1.resolution.retainCachesForTesting = true;
await compiler1.run(entryPoint);
if (collector.crashes.isNotEmpty) {
print('Skipping due to crashes.');
return ResultKind.crashes;
}
if (collector.errors.isNotEmpty && skipErrors) {
print('Skipping due to errors.');
return ResultKind.errors;
}
if (collector.warnings.isNotEmpty && skipWarnings) {
print('Skipping due to warnings.');
return ResultKind.warnings;
}
Expect.isFalse(compiler1.compilationFailed);
ClosedWorld closedWorld1 =
compiler1.resolutionWorldBuilder.closedWorldForTesting;
OutputCollector collector2 = new OutputCollector();
Compiler compiler2 = await compileWithDill(
entryPoint: entryPoint,
options: <String>[]..addAll(commonOptions)..addAll(options),
printSteps: true,
compilerOutput: collector2);
KernelFrontEndStrategy frontendStrategy = compiler2.frontendStrategy;
KernelToElementMap elementMap = frontendStrategy.elementMap;
Expect.isFalse(compiler2.compilationFailed);
KernelEquivalence equivalence1 = new KernelEquivalence(elementMap);
ClosedWorld closedWorld2 =
compiler2.resolutionWorldBuilder.closedWorldForTesting;
checkBackendUsage(closedWorld1.backendUsage, closedWorld2.backendUsage,
equivalence1.defaultStrategy);
print('--- checking resolution enqueuers ----------------------------------');
checkResolutionEnqueuers(closedWorld1.backendUsage, closedWorld2.backendUsage,
compiler1.enqueuer.resolution, compiler2.enqueuer.resolution,
elementEquivalence: (a, b) => equivalence1.entityEquivalence(a, b),
typeEquivalence: (DartType a, DartType b) {
return equivalence1.typeEquivalence(unalias(a), b);
},
elementFilter: elementFilter,
verbose: verbose);
print('--- checking closed worlds -----------------------------------------');
checkClosedWorlds(closedWorld1, closedWorld2,
strategy: equivalence1.defaultStrategy,
verbose: verbose,
// TODO(johnniwinther,efortuna): Require closure class equivalence when
// these are supported.
allowMissingClosureClasses: true);
// TODO(johnniwinther): Perform equivalence tests on the model: codegen world
// impacts, program model, etc.
print('--- checking codegen enqueuers--------------------------------------');
KernelBackendStrategy backendStrategy = compiler2.backendStrategy;
KernelEquivalence equivalence2 =
new KernelEquivalence(backendStrategy.elementMap);
checkCodegenEnqueuers(compiler1.enqueuer.codegenEnqueuerForTesting,
compiler2.enqueuer.codegenEnqueuerForTesting,
elementEquivalence: (a, b) => equivalence2.entityEquivalence(a, b),
typeEquivalence: (DartType a, DartType b) {
return equivalence2.typeEquivalence(unalias(a), b);
},
elementFilter: elementFilter,
verbose: verbose);
checkEmitters(compiler1.backend.emitter, compiler2.backend.emitter,
equivalence2.defaultStrategy,
elementEquivalence: (a, b) => equivalence2.entityEquivalence(a, b),
typeEquivalence: (DartType a, DartType b) {
return equivalence2.typeEquivalence(unalias(a), b);
},
verbose: verbose);
if (expectAstEquivalence) {
checkGeneratedCode(compiler1.backend, compiler2.backend,
elementEquivalence: (a, b) => equivalence2.entityEquivalence(a, b));
}
if (expectIdenticalOutput) {
print('--- checking output------- ---------------------------------------');
collector1.outputMap
.forEach((OutputType outputType, Map<String, BufferedOutputSink> map1) {
if (outputType == OutputType.sourceMap) {
// TODO(johnniwinther): Support source map from .dill.
return;
}
Map<String, BufferedOutputSink> map2 = collector2.outputMap[outputType];
checkSets(map1.keys, map2.keys, 'output', equality);
map1.forEach((String name, BufferedOutputSink output1) {
BufferedOutputSink output2 = map2[name];
Expect.stringEquals(output1.text, output2.text);
});
});
}
return ResultKind.success;
}