blob: aa04009f411978cc7b412cf71e19807a71649359 [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.md file.
library test.kernel.reify.suite;
import 'dart:async' show Future;
import 'dart:io' show Directory, File, Platform;
import 'package:analyzer/src/generated/sdk.dart' show DartSdk;
import 'package:kernel/analyzer/loader.dart'
show DartLoader, DartOptions, createDartSdk;
import 'package:kernel/target/targets.dart' show Target, TargetFlags, getTarget;
import 'package:kernel/target/vmcc.dart' show VmClosureConvertedTarget;
import 'kernel_chain.dart'
show MatchExpectation, Print, ReadDill, SanityCheck, WriteDill;
import 'package:testing/testing.dart'
show
Chain,
ChainContext,
Result,
StdioProcess,
Step,
TestDescription,
runMe;
import 'package:kernel/ast.dart' show Program;
import 'package:kernel/transformations/generic_types_reification.dart'
as generic_types_reification;
import 'package:package_config/discovery.dart' show loadPackagesFile;
class TestContext extends ChainContext {
final Uri vm;
final Uri packages;
final DartOptions options;
final DartSdk dartSdk;
final List<Step> steps;
TestContext(String sdk, this.vm, Uri packages, bool strongMode, this.dartSdk,
bool updateExpectations)
: packages = packages,
options = new DartOptions(
strongMode: strongMode,
sdk: sdk,
packagePath: packages.toFilePath()),
steps = <Step>[
const NotReifiedKernel(),
const Print(),
const SanityCheck(),
const GenericTypesReification(),
const Print(),
const SanityCheck(),
new MatchExpectation(".expect",
updateExpectations: updateExpectations),
const WriteDill(),
const ReadDill(),
const Run(),
];
Future<DartLoader> createLoader() async {
return new DartLoader(new Program(), options,
await loadPackagesFile(packages), dartSdk: dartSdk);
}
}
enum Environment {
directory,
file,
}
Future<String> getEnvironmentVariable(
String name, Environment kind, String undefined, notFound(String n)) async {
String result = Platform.environment[name];
if (result == null) {
throw undefined;
}
switch (kind) {
case Environment.directory:
if (!await new Directory(result).exists()) throw notFound(result);
break;
case Environment.file:
if (!await new File(result).exists()) throw notFound(result);
break;
}
return result;
}
Future<bool> fileExists(Uri base, String path) async {
return await new File.fromUri(base.resolve(path)).exists();
}
Future<TestContext> createContext(
Chain suite, Map<String, String> environment) async {
const String suggestion = """Try building the patched SDK by running
'tools/build.py patched_sdk'""";
// TODO(karlklose): The path is different on MacOS.
String sdk = "out/DebugX64/patched_sdk/";
Uri sdkUri = Uri.base.resolve(sdk);
const String asyncDart = "lib/async/async.dart";
if (!await fileExists(sdkUri, asyncDart)) {
throw "Couldn't find the patched SDK. $suggestion";
}
const String asyncSources = "lib/async/async_sources.gypi";
if (await fileExists(sdkUri, asyncSources)) {
throw "Found '$asyncSources' in '$sdk', so it isn't a patched SDK. "
"$suggestion";
}
// TODO(karlklose): select the VM based on the mode.
Uri vm = Uri.base.resolve("out/ReleaseX64/dart");
Uri packages = Uri.base.resolve(".packages");
// Strong mode is required to keep the type arguments in invocations of
// generic methods.
bool strongMode = true;
bool updateExpectations = const String.fromEnvironment("updateExpectations",
defaultValue: "false") ==
"true";
return new TestContext(sdk, vm, packages, strongMode,
createDartSdk(sdk, strongMode: strongMode), updateExpectations);
}
// [NotReifiedTarget] is intended to work as the [Target] class that
// [VmGenericTypesReifiedTarget] inherits from, but with some transformations
// disabled. Those include tree shaking and generic types information erasure
// passes.
// [NotReifiedTarget] also adds the necessary runtime libraries.
class NotReifiedTarget extends VmClosureConvertedTarget {
NotReifiedTarget(TargetFlags flags) : super(flags);
@override
String get name => "not reified target";
// Tree shaking needs to be disabled, because Generic Types Reification
// transformation relies on certain runtime libraries to be present in
// the program that is being transformed. If the tree shaker is enabled,
// it just deletes everything from those libraries, because they aren't
// used in the program being transform prior to the transformation.
@override
void performTreeShaking(Program program) {}
// Erasure needs to be disabled, because it removes the necessary information
// about type arguments for generic methods.
@override
void performErasure(Program program) {}
// Adds the necessary runtime libraries.
@override
List<String> get extraRequiredLibraries {
Target reifyTarget = getTarget("vmreify", this.flags);
var x = reifyTarget.extraRequiredLibraries;
return x;
}
}
class NotReifiedKernel extends Step<TestDescription, Program, TestContext> {
const NotReifiedKernel();
String get name => "kernel";
Future<Result<Program>> run(
TestDescription description, TestContext testContext) async {
try {
DartLoader loader = await testContext.createLoader();
// Strong mode is required to keep the type arguments in invocations of
// generic methods.
Target target = new NotReifiedTarget(new TargetFlags(
strongMode: true,
kernelRuntime: Platform.script.resolve("../../runtime/")));
String path = description.file.path;
Uri uri = Uri.base.resolve(path);
loader.loadProgram(uri, target: target);
var program = loader.program;
for (var error in loader.errors) {
return fail(program, "$error");
}
target
..performModularTransformations(program)
..performGlobalTransformations(program);
return pass(program);
} catch (e, s) {
return crash(e, s);
}
}
}
class GenericTypesReification extends Step<Program, Program, TestContext> {
const GenericTypesReification();
String get name => "generic types reification";
Future<Result<Program>> run(Program program, TestContext testContext) async {
try {
program = generic_types_reification.transformProgram(program);
return pass(program);
} catch (e, s) {
return crash(e, s);
}
}
}
class Run extends Step<Uri, int, TestContext> {
const Run();
String get name => "run";
Future<Result<int>> run(Uri uri, TestContext context) async {
File generated = new File.fromUri(uri);
StdioProcess process;
try {
process =
await StdioProcess.run(context.vm.toFilePath(), [generated.path]);
print(process.output);
} finally {
generated.parent.delete(recursive: true);
}
return process.toResult();
}
}
main(List<String> arguments) => runMe(arguments, createContext, "testing.json");