blob: 4ee2430beed8f0bf5e868f1ca9e0d17ebf5052d8 [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.
import 'dart:async';
import 'package:front_end/memory_file_system.dart';
import 'package:front_end/src/base/performace_logger.dart';
import 'package:front_end/src/fasta/kernel/utils.dart';
import 'package:front_end/src/fasta/uri_translator_impl.dart';
import 'package:front_end/src/incremental/byte_store.dart';
import 'package:front_end/src/incremental/kernel_driver.dart';
import 'package:kernel/ast.dart';
import 'package:kernel/binary/ast_from_binary.dart';
import 'package:kernel/target/targets.dart';
import 'package:kernel/text/ast_to_text.dart';
import 'package:kernel/verifier.dart';
import 'package:package_config/src/packages_impl.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'mock_sdk.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(KernelDriverTest);
});
}
@reflectiveTest
class KernelDriverTest {
/// Virtual filesystem for testing.
final fileSystem = new MemoryFileSystem(Uri.parse('file:///'));
/// The object under test.
KernelDriver driver;
void setUp() {
_createDriver();
}
test_compile_chain() async {
writeFile('/test/.packages', 'test:lib/');
String aPath = '/test/lib/a.dart';
String bPath = '/test/lib/b.dart';
String cPath = '/test/lib/c.dart';
Uri aUri = writeFile(aPath, 'var a = 1;');
Uri bUri = writeFile(bPath, r'''
import 'a.dart';
var b = a;
''');
Uri cUri = writeFile(cPath, r'''
import 'a.dart';
import 'b.dart';
var c1 = a;
var c2 = b;
void main() {}
''');
{
KernelResult result = await driver.getKernel(cUri);
_assertLibraryUris(result,
includes: [aUri, bUri, cUri, Uri.parse('dart:core')]);
Library library = _getLibrary(result, cUri);
expect(_getLibraryText(library), r'''
library;
import self as self;
import "dart:core" as core;
import "./a.dart" as a;
import "./b.dart" as b;
static field core::int c1 = a::a;
static field core::int c2 = b::b;
static method main() → void {}
''');
}
// Update b.dart and recompile c.dart
writeFile(bPath, r'''
import 'a.dart';
var b = 1.2;
''');
driver.invalidate(bUri);
{
KernelResult result = await driver.getKernel(cUri);
_assertLibraryUris(result,
includes: [aUri, bUri, cUri, Uri.parse('dart:core')]);
Library library = _getLibrary(result, cUri);
expect(_getLibraryText(library), r'''
library;
import self as self;
import "dart:core" as core;
import "./a.dart" as a;
import "./b.dart" as b;
static field core::int c1 = a::a;
static field core::double c2 = b::b;
static method main() → void {}
''');
}
}
test_compile_export() async {
writeFile('/test/.packages', 'test:lib/');
String aPath = '/test/lib/a.dart';
String bPath = '/test/lib/b.dart';
String cPath = '/test/lib/c.dart';
writeFile(aPath, 'class A {}');
writeFile(bPath, 'export "a.dart";');
Uri cUri = writeFile(cPath, r'''
import 'b.dart';
A a;
''');
KernelResult result = await driver.getKernel(cUri);
Library library = _getLibrary(result, cUri);
expect(_getLibraryText(library), r'''
library;
import self as self;
import "./a.dart" as a;
static field a::A a;
''');
}
test_compile_export_cycle() async {
writeFile('/test/.packages', 'test:lib/');
String aPath = '/test/lib/a.dart';
String bPath = '/test/lib/b.dart';
String cPath = '/test/lib/c.dart';
writeFile(aPath, 'export "b.dart"; class A {}');
writeFile(bPath, 'export "a.dart"; class B {}');
Uri cUri = writeFile(cPath, r'''
import 'b.dart';
A a;
B b;
''');
{
KernelResult result = await driver.getKernel(cUri);
Library library = _getLibrary(result, cUri);
expect(_getLibraryText(library), r'''
library;
import self as self;
import "./a.dart" as a;
import "./b.dart" as b;
static field a::A a;
static field b::B b;
''');
}
// Update c.dart and compile.
// We should load the cycle [a.dart, b.dart] from the byte store.
// This tests that we compute export scopes after loading.
writeFile(cPath, r'''
import 'b.dart';
A a;
B b;
int c;
''');
driver.invalidate(cUri);
{
KernelResult result = await driver.getKernel(cUri);
Library library = _getLibrary(result, cUri);
expect(_getLibraryText(library), r'''
library;
import self as self;
import "./a.dart" as a;
import "./b.dart" as b;
import "dart:core" as core;
static field a::A a;
static field b::B b;
static field core::int c;
''');
}
}
test_compile_export_hideWithLocal() async {
writeFile('/test/.packages', 'test:lib/');
String aPath = '/test/lib/a.dart';
String bPath = '/test/lib/b.dart';
String cPath = '/test/lib/c.dart';
writeFile(aPath, 'class A {} class B {}');
writeFile(bPath, 'export "a.dart"; class B {}');
Uri cUri = writeFile(cPath, r'''
import 'b.dart';
A a;
B b;
''');
KernelResult result = await driver.getKernel(cUri);
Library library = _getLibrary(result, cUri);
expect(_getLibraryText(library), r'''
library;
import self as self;
import "./a.dart" as a;
import "./b.dart" as b;
static field a::A a;
static field b::B b;
''');
}
test_compile_recompileMixin() async {
writeFile('/test/.packages', 'test:lib/');
String aPath = '/test/lib/a.dart';
String bPath = '/test/lib/b.dart';
String cPath = '/test/lib/c.dart';
Uri aUri = writeFile(aPath, r'''
import 'b.dart';
main() {
new B().foo();
}
''');
Uri bUri = writeFile(bPath, r'''
import 'c.dart';
class B extends Object with C {}
''');
Uri cUri = writeFile(cPath, r'''
class C {
void foo() {
print(0);
}
}
''');
{
KernelResult result = await driver.getKernel(aUri);
_assertLibraryUris(result,
includes: [aUri, bUri, cUri, Uri.parse('dart:core')]);
}
// Update c.dart and compute the delta.
// Includes: c.dart, b.dart and a.dart files.
// Compiled: c.dart (changed) and b.dart (has mixin), but not a.dart file.
writeFile(cPath, r'''
class C {
void foo() {
print(1);
}
}
''');
driver.invalidate(cUri);
{
KernelResult result = await driver.getKernel(aUri);
_assertLibraryUris(result,
includes: [aUri, bUri, cUri, Uri.parse('dart:core')]);
// Compiled: c.dart (changed), and b.dart (has mixin).
_assertCompiledUris([cUri, bUri]);
}
}
test_compile_typedef() async {
writeFile('/test/.packages', 'test:lib/');
String aPath = '/test/lib/a.dart';
String bPath = '/test/lib/b.dart';
writeFile(aPath, 'typedef int F<T>(T x);');
Uri bUri = writeFile(bPath, r'''
import 'a.dart';
F<String> f;
''');
KernelResult result = await driver.getKernel(bUri);
Library library = _getLibrary(result, bUri);
expect(_getLibraryText(library), r'''
library;
import self as self;
import "dart:core" as core;
static field (core::String) → core::int f;
''');
}
test_limited_ast_to_binary() async {
writeFile('/test/.packages', 'test:lib/');
String aPath = '/test/lib/a.dart';
String bPath = '/test/lib/b.dart';
writeFile(aPath, r'''
int topField = 0;
int get topGetter => 0;
int topFunction({p}) => 0;
abstract class I {
int interfaceField;
int get interfaceGetter;
int interfaceMethod();
}
class A implements I {
static int staticField;
static int get staticGetter => 0;
static int staticMethod() => 0;
int instanceField;
int get instanceGetter => 0;
int instanceMethod() => 0;
int interfaceField;
int get interfaceGetter => 0;
int interfaceMethod() => 0;
A();
A.named();
}
''');
Uri bUri = writeFile(bPath, r'''
import 'a.dart';
class B extends A {
B() : super();
B.named() : super.named();
void foo() {
super.instanceMethod();
instanceMethod();
super.interfaceField;
super.interfaceField = 0;
super.interfaceGetter;
super.interfaceMethod();
}
int instanceMethod() => 0;
int interfaceField;
int get interfaceGetter => 0;
int interfaceMethod() => 0;
}
main() {
topField;
topField = 0;
var v1 = topGetter;
var v2 = topFunction(p: 0);
A.staticField;
A.staticField = 0;
var v3 = A.staticGetter;
var v4 = A.staticMethod();
var a = new A();
a.instanceField;
a.instanceField = 0;
var v5 = a.instanceGetter;
var v6 = a.instanceMethod();
a.interfaceField;
a.interfaceField = 0;
var v7 = a.interfaceGetter;
var v8 = a.interfaceMethod();
}
''');
KernelResult result = await driver.getKernel(bUri);
Program program = new Program(
nameRoot: result.nameRoot, libraries: _allLibraries(result));
String initialKernelText;
List<int> bytes;
{
Library initialLibrary = _getLibraryFromProgram(program, bUri);
initialKernelText = _getLibraryText(initialLibrary);
bytes = serializeProgram(program,
filter: (library) => library.importUri == bUri);
// Remove b.dart from the program.
// So, the program is now ready for re-adding the library.
program.mainMethod = null;
program.libraries.remove(initialLibrary);
program.root.removeChild(initialLibrary.importUri.toString());
}
// Load b.dart from bytes using the initial name root, so that
// serialized canonical names can be linked to corresponding nodes.
Library loadedLibrary;
{
var programForLoading = new Program(nameRoot: program.root);
var reader = new BinaryBuilder(bytes);
reader.readProgram(programForLoading);
loadedLibrary = _getLibraryFromProgram(programForLoading, bUri);
}
// Add the library into the program.
program.libraries.add(loadedLibrary);
loadedLibrary.parent = program;
program.mainMethod = loadedLibrary.procedures
.firstWhere((procedure) => procedure.name.name == 'main');
expect(_getLibraryText(loadedLibrary), initialKernelText);
verifyProgram(program);
}
test_updatePackageSourceUsingFileUri() async {
_createDriver(packages: {'test': _folderUri('/test/lib')});
writeFile('/test/.packages', 'test:lib/');
Uri aFileUri = writeFile('/test/bin/a.dart', r'''
import 'package:test/b.dart';
var a = b;
''');
Uri bFileUri = writeFile('/test/lib/b.dart', 'var b = 1;');
Uri bPackageUri = Uri.parse('package:test/b.dart');
// Compute the initial state.
{
KernelResult result = await driver.getKernel(aFileUri);
Library library = _getLibrary(result, aFileUri);
expect(_getLibraryText(library), r'''
library;
import self as self;
import "dart:core" as core;
import "package:test/b.dart" as b;
static field core::int a = b::b;
''');
}
// Update b.dart and use file URI to invalidate it.
// The delta is recomputed even though b.dart is used with the package URI.
writeFile('/test/lib/b.dart', 'var b = 1.2;');
driver.invalidate(bFileUri);
{
KernelResult result = await driver.getKernel(aFileUri);
_assertLibraryUris(result, includes: [aFileUri, bPackageUri]);
Library library = _getLibrary(result, aFileUri);
expect(_getLibraryText(library), r'''
library;
import self as self;
import "dart:core" as core;
import "package:test/b.dart" as b;
static field core::double a = b::b;
''');
}
}
test_updatePart() async {
writeFile('/test/.packages', 'test:lib/');
String libPath = '/test/lib/test.dart';
String partPath = '/test/lib/bar.dart';
Uri libUri = writeFile(libPath, r'''
library foo;
part 'bar.dart';
var a = 1;
var c = b;
void main() {}
''');
Uri partUri = writeFile(partPath, r'''
part of foo;
var b = 2;
var d = a;
''');
// Check the initial state - types flow between the part and the library.
KernelResult result = await driver.getKernel(libUri);
Library library = _getLibrary(result, libUri);
expect(_getLibraryText(library), r'''
library foo;
import self as self;
import "dart:core" as core;
static field core::int a = 1;
static field core::int c = self::b;
static field core::int b = 2 /* from file:///test/lib/bar.dart */;
static field core::int d = self::a /* from file:///test/lib/bar.dart */;
static method main() → void {}
''');
// Update [b] in the part, the type is changed in the part and library.
{
writeFile(partPath, r'''
part of foo;
var b = 2.3;
var d = a;
''');
driver.invalidate(partUri);
KernelResult result = await driver.getKernel(libUri);
Library library = _getLibrary(result, libUri);
expect(_getLibraryText(library), r'''
library foo;
import self as self;
import "dart:core" as core;
static field core::int a = 1;
static field core::double c = self::b;
static field core::double b = 2.3 /* from file:///test/lib/bar.dart */;
static field core::int d = self::a /* from file:///test/lib/bar.dart */;
static method main() → void {}
''');
}
// Update [a] in the library, the type is changed in the part and library.
{
writeFile(libPath, r'''
library foo;
part 'bar.dart';
var a = 'aaa';
var c = b;
void main() {}
''');
driver.invalidate(libUri);
KernelResult result = await driver.getKernel(libUri);
Library library = _getLibrary(result, libUri);
expect(_getLibraryText(library), r'''
library foo;
import self as self;
import "dart:core" as core;
static field core::String a = "aaa";
static field core::double c = self::b;
static field core::double b = 2.3 /* from file:///test/lib/bar.dart */;
static field core::String d = self::a /* from file:///test/lib/bar.dart */;
static method main() → void {}
''');
}
}
test_watch() async {
writeFile('/test/.packages', 'test:lib/');
String aPath = '/test/lib/a.dart';
String bPath = '/test/lib/b.dart';
String cPath = '/test/lib/c.dart';
Uri aUri = writeFile(aPath, '');
Uri bUri = writeFile(bPath, '');
Uri cUri = writeFile(cPath, r'''
import 'a.dart';
''');
var usedFiles = <Uri>[];
_createDriver(fileAddedFn: (Uri uri) {
usedFiles.add(uri);
return new Future.value();
});
{
await driver.getKernel(cUri);
// We use at least c.dart and a.dart now.
expect(usedFiles, contains(cUri));
expect(usedFiles, contains(aUri));
usedFiles.clear();
}
// Update c.dart to reference also b.dart file.
writeFile(cPath, r'''
import 'a.dart';
import 'b.dart';
''');
driver.invalidate(cUri);
{
await driver.getKernel(cUri);
// The only new file is b.dart now.
expect(usedFiles, [bUri]);
usedFiles.clear();
}
}
/// Write the given [text] of the file with the given [path] into the
/// virtual filesystem. Return the URI of the file.
Uri writeFile(String path, String text) {
Uri uri = Uri.parse('file://$path');
fileSystem.entityForUri(uri).writeAsStringSync(text);
return uri;
}
List<Library> _allLibraries(KernelResult result) {
return result.results
.map((cycle) => cycle.kernelLibraries)
.expand((libraries) => libraries)
.toList();
}
void _assertCompiledUris(Iterable<Uri> expected) {
var compiledCycles = driver.test.compiledCycles;
Set<Uri> compiledUris = compiledCycles
.map((cycle) => cycle.libraries.map((file) => file.uri))
.expand((uris) => uris)
.toSet();
expect(compiledUris, unorderedEquals(expected));
}
void _assertLibraryUris(KernelResult result,
{List<Uri> includes: const [], List<Uri> excludes: const []}) {
List<Uri> libraryUris = result.results
.map((cycle) => cycle.kernelLibraries.map((lib) => lib.importUri))
.expand((uris) => uris)
.toList();
for (var shouldInclude in includes) {
expect(libraryUris, contains(shouldInclude));
}
for (var shouldExclude in excludes) {
expect(libraryUris, isNot(contains(shouldExclude)));
}
}
/// Create new [KernelDriver] instance and put it into the [driver] field.
void _createDriver(
{Map<String, Uri> packages, KernelDriverFileAddedFn fileAddedFn}) {
Map<String, Uri> dartLibraries = createSdkFiles(fileSystem);
var uriTranslator =
new UriTranslatorImpl(dartLibraries, {}, new MapPackages(packages));
driver = new KernelDriver(
new PerformanceLog(null),
fileSystem,
new MemoryByteStore(),
uriTranslator,
new NoneTarget(new TargetFlags(strongMode: true)),
fileAddedFn: fileAddedFn);
}
Library _getLibrary(KernelResult result, Uri uri) {
for (var cycleResult in result.results) {
for (var library in cycleResult.kernelLibraries) {
if (library.importUri == uri) return library;
}
}
throw fail('No library found with URI "$uri"');
}
Library _getLibraryFromProgram(Program program, Uri uri) {
for (var library in program.libraries) {
if (library.importUri == uri) return library;
}
throw fail('No library found with URI "$uri"');
}
String _getLibraryText(Library library) {
StringBuffer buffer = new StringBuffer();
new Printer(buffer, syntheticNames: new NameSystem())
.writeLibraryFile(library);
return buffer.toString();
}
/// Return the [Uri] for the given Posix [path].
static Uri _folderUri(String path) {
if (!path.endsWith('/')) path += '/';
return Uri.parse('file://$path');
}
}