未验证 提交 97b22348 编写于 作者: A Alexander Aprelev 提交者: GitHub

Leave only widget creation tracker kernel pass in flutter frontend server. (#4727)

* Leave only widget creation tracker kernel pass in flutter frontend server.

This is follow-up to https://dart-review.googlesource.com/c/sdk/+/43888.

* Adapt to updated interface

* Tyop

* Fix imports. Compiler interface extension

* Add trivial test back. Rename library to flutter_frontend_server.
上级 fb16d6bb
......@@ -12,6 +12,7 @@ library track_widget_constructor_locations;
import 'package:kernel/ast.dart';
import 'package:kernel/class_hierarchy.dart';
import 'package:meta/meta.dart';
import 'package:vm/frontend_server.dart' show ProgramTransformer;
// Parameter name used to track were widget constructor calls were made from.
//
......@@ -285,7 +286,7 @@ class _WidgetCallSiteTransformer extends Transformer {
/// The creation location is stored as a private field named `_location`
/// on the base widget class and flowed through the constructors using a named
/// parameter.
class WidgetCreatorTracker {
class WidgetCreatorTracker implements ProgramTransformer {
Class _widgetClass;
Class _locationClass;
......@@ -440,6 +441,7 @@ class WidgetCreatorTracker {
///
/// It is safe to call this method on a delta program generated as part of
/// performing a hot reload.
@override
void transform(Program program) {
final List<Library> libraries = program.libraries;
......
......@@ -2,364 +2,52 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
library frontend_server;
library flutter_frontend_server;
import 'dart:async';
import 'dart:convert';
import 'dart:io' hide FileSystemEntity;
import 'package:args/args.dart';
// front_end/src imports below that require lint `ignore_for_file`
// are a temporary state of things until frontend team builds better api
// that would replace api used below. This api was made private in
// an effort to discourage further use.
// ignore_for_file: implementation_imports
import 'package:front_end/src/api_prototype/compiler_options.dart';
import 'package:front_end/src/api_prototype/file_system.dart' show FileSystemEntity;
import 'package:kernel/ast.dart';
import 'package:kernel/binary/ast_to_binary.dart';
import 'package:kernel/binary/limited_ast_to_binary.dart';
import 'package:kernel/kernel.dart' show Program, loadProgramFromBytes;
import 'package:kernel/target/flutter.dart';
import 'package:kernel/target/targets.dart';
import 'package:path/path.dart' as path;
import 'package:usage/uuid/uuid.dart';
import 'package:vm/incremental_compiler.dart' show IncrementalCompiler;
import 'package:vm/kernel_front_end.dart' show compileToKernel;
import 'package:flutter_kernel_transformers/track_widget_constructor_locations.dart';
import 'package:vm/incremental_compiler.dart';
import 'package:vm/frontend_server.dart' as frontend show FrontendCompiler,
CompilerInterface, listenAndCompile, argParser, usage;
ArgParser _argParser = new ArgParser(allowTrailingOptions: true)
..addFlag('train',
help: 'Run through sample command line to produce snapshot',
negatable: false)
..addFlag('incremental',
help: 'Run compiler in incremental mode', defaultsTo: false)
..addOption('sdk-root',
help: 'Path to sdk root',
defaultsTo: '../../out/android_debug/flutter_patched_sdk')
..addFlag('aot',
help: 'Run compiler in AOT mode (enables whole-program transformations)',
defaultsTo: false)
..addFlag('strong',
help: 'Run compiler in strong mode (uses strong mode semantics)',
defaultsTo: false)
..addFlag('tfa',
help:
'Enable global type flow analysis and related transformations in AOT mode.',
defaultsTo: false)
..addOption('entry-points',
help: 'Path to JSON file with the list of entry points',
allowMultiple: true)
..addFlag('link-platform',
help:
'When in batch mode, link platform kernel file into result kernel file.'
' Intended use is to satisfy different loading strategies implemented'
' by gen_snapshot(which needs platform embedded) vs'
' Flutter engine(which does not)',
defaultsTo: true)
..addOption('output-dill',
help: 'Output path for the generated dill',
defaultsTo: null)
..addOption('output-incremental-dill',
help: 'Output path for the generated incremental dill',
defaultsTo: null)
..addOption('depfile',
help: 'Path to output Ninja depfile. Only used in batch mode.')
..addOption('packages',
help: '.packages file to use for compilation',
defaultsTo: null)
..addFlag('track-widget-creation',
help: 'Run a kernel transformer to track creation locations for widgets.',
defaultsTo: false);
String _usage = '''
Usage: server [options] [input.dart]
If input dart source code is provided on the command line, then the server
compiles it, generates dill file and exits.
If no input dart source is provided on the command line, server waits for
instructions from stdin.
Instructions:
- compile <input.dart>
- recompile [<input.dart>] <boundary-key>
<path/to/updated/file1.dart>
<path/to/updated/file2.dart>
...
<boundary-key>
- accept
- quit
Output:
- result <boundary-key>
<compiler output>
<boundary-key> [<output.dill>]
Options:
${_argParser.usage}
''';
enum _State { READY_FOR_INSTRUCTION, RECOMPILE_LIST }
/// Actions that every compiler should implement.
abstract class CompilerInterface {
/// Compile given Dart program identified by `filename` with given list of
/// `options`. When `generator` parameter is omitted, new instance of
/// `IncrementalKernelGenerator` is created by this method. Main use for this
/// parameter is for mocking in tests.
Future<Null> compile(
String filename,
ArgResults options, {
IncrementalCompiler generator,
});
/// Assuming some Dart program was previously compiled, recompile it again
/// taking into account some changed(invalidated) sources.
Future<Null> recompileDelta({String filename});
/// Accept results of previous compilation so that next recompilation cycle
/// won't recompile sources that were previously reported as changed.
void acceptLastDelta();
/// This let's compiler know that source file identifed by `uri` was changed.
void invalidate(Uri uri);
/// Resets incremental compiler accept/reject status so that next time
/// recompile is requested, complete kernel file is produced.
void resetIncrementalCompiler();
}
/// Class that for test mocking purposes encapsulates creation of [BinaryPrinter].
class BinaryPrinterFactory {
/// Creates new [BinaryPrinter] to write to [targetSink].
BinaryPrinter newBinaryPrinter(IOSink targetSink) {
return new LimitedBinaryPrinter(targetSink, (_) => true /* predicate */,
false /* excludeUriToSource */);
}
}
class _FrontendCompiler implements CompilerInterface {
_FrontendCompiler(this._outputStream, {this.printerFactory, this.trackWidgetCreation}) {
_outputStream ??= stdout;
printerFactory ??= new BinaryPrinterFactory();
if (trackWidgetCreation) {
widgetCreatorTracker = new WidgetCreatorTracker();
}
}
StringSink _outputStream;
BinaryPrinterFactory printerFactory;
/// Wrapper around [FrontendCompiler] that adds [widgetCreatorTracker] kernel
/// transformation to the compilation.
class _FlutterFrontendCompiler implements frontend.CompilerInterface{
final frontend.CompilerInterface _compiler;
CompilerOptions _compilerOptions;
Uri _mainSource;
ArgResults _options;
IncrementalCompiler _generator;
String _kernelBinaryFilename;
String _kernelBinaryFilenameIncremental;
String _kernelBinaryFilenameFull;
final bool trackWidgetCreation;
WidgetCreatorTracker widgetCreatorTracker;
void setMainSourceFilename(String filename) {
final Uri filenameUri = Uri.base.resolveUri(new Uri.file(filename));
_kernelBinaryFilenameFull = _options['output-dill'] ?? '$filename.dill';
_kernelBinaryFilenameIncremental =
_options['output-incremental-dill'] ??
_options['output-dill'] != null
? '${_options["output-dill"]}.incremental.dill'
: '$filename.incremental.dill';
_kernelBinaryFilename = _kernelBinaryFilenameFull;
_mainSource = filenameUri;
}
_FlutterFrontendCompiler(StringSink output, {bool trackWidgetCreation: false}):
_compiler = new frontend.FrontendCompiler(output,
transformer: (trackWidgetCreation ? new WidgetCreatorTracker() : null));
@override
Future<Null> compile(
String filename,
ArgResults options, {
IncrementalCompiler generator,
}) async {
_options = options;
setMainSourceFilename(filename);
final String boundaryKey = new Uuid().generateV4();
_outputStream.writeln('result $boundaryKey');
final Uri sdkRoot = _ensureFolderPath(options['sdk-root']);
final String platformKernelDill =
options['strong'] ? 'platform_strong.dill' : 'platform.dill';
final CompilerOptions compilerOptions = new CompilerOptions()
..sdkRoot = sdkRoot
..packagesFileUri = options['packages'] != null ? Uri.base.resolveUri(new Uri.file(options['packages'])) : null
..strongMode = options['strong']
..sdkSummary = sdkRoot.resolve(platformKernelDill)
..target = new FlutterTarget(new TargetFlags(strongMode: options['strong']))
..reportMessages = true;
Program program;
if (options['incremental']) {
_compilerOptions = compilerOptions;
_generator = generator ?? _createGenerator(new Uri.file(_kernelBinaryFilenameFull));
await invalidateIfBootstrapping();
program = await _runWithPrintRedirection(() => _generator.compile());
} else {
if (options['link-platform']) {
// TODO(aam): Remove linkedDependencies once platform is directly embedded
// into VM snapshot and http://dartbug.com/30111 is fixed.
compilerOptions.linkedDependencies = <Uri>[
sdkRoot.resolve(platformKernelDill)
];
}
program = await _runWithPrintRedirection(() => compileToKernel(
_mainSource, compilerOptions,
aot: options['aot'],
useGlobalTypeFlowAnalysis: options['tfa'],
entryPoints: options['entry-points']));
}
runFlutterSpecificKernelTransforms(program);
if (program != null) {
final IOSink sink = new File(_kernelBinaryFilename).openWrite();
final BinaryPrinter printer = printerFactory.newBinaryPrinter(sink);
printer.writeProgramFile(program);
await sink.close();
_outputStream.writeln('$boundaryKey $_kernelBinaryFilename');
final String depfile = options['depfile'];
if (depfile != null) {
await _writeDepfile(program, _kernelBinaryFilename, depfile);
}
_kernelBinaryFilename = _kernelBinaryFilenameIncremental;
} else
_outputStream.writeln(boundaryKey);
return null;
}
Future<Null> invalidateIfBootstrapping() async {
if (_kernelBinaryFilename != _kernelBinaryFilenameFull)
return null;
try {
final File f = new File(_kernelBinaryFilenameFull);
if (!f.existsSync())
return null;
final Program program = loadProgramFromBytes(f.readAsBytesSync());
for (Uri uri in program.uriToSource.keys) {
if ('$uri' == '')
continue;
final List<int> oldBytes = program.uriToSource[uri].source;
final FileSystemEntity entity = _compilerOptions.fileSystem.entityForUri(uri);
if (!await entity.exists()) {
_generator.invalidate(uri);
continue;
}
final List<int> newBytes = await entity.readAsBytes();
if (oldBytes.length != newBytes.length) {
_generator.invalidate(uri);
continue;
}
for (int i = 0; i < oldBytes.length; ++i) {
if (oldBytes[i] != newBytes[i]) {
_generator.invalidate(uri);
continue;
}
}
}
} catch(e) {
// If there's a failure in the above block we might not have invalidated
// correctly. Create a new generator that doesn't bootstrap to avoid missing
// any changes.
_generator = _createGenerator(null);
}
}
void runFlutterSpecificKernelTransforms(Program program) {
if (program == null) {
return;
}
if (trackWidgetCreation) {
widgetCreatorTracker.transform(program);
}
Future<Null> compile(String filename, ArgResults options, {IncrementalCompiler generator}) async {
return _compiler.compile(filename, options, generator: generator);
}
@override
Future<Null> recompileDelta({String filename}) async {
final String boundaryKey = new Uuid().generateV4();
_outputStream.writeln('result $boundaryKey');
await invalidateIfBootstrapping();
if (filename != null) {
setMainSourceFilename(filename);
}
final Program deltaProgram =
await _generator.compile(entryPoint: _mainSource);
runFlutterSpecificKernelTransforms(deltaProgram);
final IOSink sink = new File(_kernelBinaryFilename).openWrite();
final BinaryPrinter printer = printerFactory.newBinaryPrinter(sink);
printer.writeProgramFile(deltaProgram);
await sink.close();
_outputStream.writeln('$boundaryKey $_kernelBinaryFilename');
_kernelBinaryFilename = _kernelBinaryFilenameIncremental;
return null;
return _compiler.recompileDelta(filename: filename);
}
@override
void acceptLastDelta() {
_generator.accept();
_compiler.acceptLastDelta();
}
@override
void invalidate(Uri uri) {
_generator.invalidate(uri);
_compiler.invalidate(uri);
}
@override
void resetIncrementalCompiler() {
_generator = _createGenerator(new Uri.file(_kernelBinaryFilenameFull));
_kernelBinaryFilename = _kernelBinaryFilenameFull;
_compiler.resetIncrementalCompiler();
}
IncrementalCompiler _createGenerator(Uri bootstrapDill) {
return new IncrementalCompiler(_compilerOptions, _mainSource,
bootstrapDill: bootstrapDill);
}
Uri _ensureFolderPath(String path) {
String uriPath = new Uri.file(path).toString();
if (!uriPath.endsWith('/')) {
uriPath = '$uriPath/';
}
return Uri.base.resolve(uriPath);
}
/// Runs the given function [f] in a Zone that redirects all prints into
/// [_outputStream].
Future<T> _runWithPrintRedirection<T>(Future<T> f()) {
return runZoned(() => new Future<T>(f),
zoneSpecification: new ZoneSpecification(
print: (Zone self, ZoneDelegate parent, Zone zone, String line) =>
_outputStream.writeln(line)));
}
}
String _escapePath(String path) {
return path.replaceAll(r'\', r'\\').replaceAll(r' ', r'\ ');
}
// https://ninja-build.org/manual.html#_depfile
// TODO(dartbug.com/32320) Clean up analyzer directive below once that is fixed.
Future<void> _writeDepfile(Program program, String output, String depfile) async { // ignore: missing_return
final IOSink file = new File(depfile).openWrite();
file.write(_escapePath(output));
file.write(':');
for (Uri dep in program.uriToSource.keys) {
file.write(' ');
file.write(_escapePath(dep.toFilePath()));
}
file.write('\n');
await file.close();
}
/// Entry point for this module, that creates `_FrontendCompiler` instance and
......@@ -367,19 +55,22 @@ Future<void> _writeDepfile(Program program, String output, String depfile) async
/// `compiler` is an optional parameter so it can be replaced with mocked
/// version for testing.
Future<int> starter(
List<String> args, {
CompilerInterface compiler,
Stream<List<int>> input,
StringSink output,
IncrementalCompiler generator,
BinaryPrinterFactory binaryPrinterFactory,
}) async {
List<String> args, {
_FlutterFrontendCompiler compiler,
Stream<List<int>> input,
StringSink output,
}) async {
ArgResults options;
frontend.argParser
..addFlag('track-widget-creation',
help: 'Run a kernel transformer to track creation locations for widgets.',
defaultsTo: false);
try {
options = _argParser.parse(args);
options = frontend.argParser.parse(args);
} catch (error) {
print('ERROR: $error\n');
print(_usage);
print(frontend.usage);
return 1;
}
......@@ -388,15 +79,14 @@ Future<int> starter(
final Directory temp = Directory.systemTemp.createTempSync('train_frontend_server');
try {
final String outputTrainingDill = path.join(temp.path, 'app.dill');
options =
_argParser.parse(<String>['--incremental', '--sdk-root=$sdkRoot',
'--output-dill=$outputTrainingDill'
]);
compiler ??=
new _FrontendCompiler(output, printerFactory: binaryPrinterFactory);
await compiler.compile(
Platform.script.toFilePath(), options, generator: generator);
options = frontend.argParser.parse(<String>[
'--incremental',
'--sdk-root=$sdkRoot',
'--output-dill=$outputTrainingDill',
'--target=flutter']);
compiler ??= new _FlutterFrontendCompiler(output, trackWidgetCreation: true);
await compiler.compile(Platform.script.toFilePath(), options);
compiler.acceptLastDelta();
await compiler.recompileDelta();
compiler.acceptLastDelta();
......@@ -411,61 +101,13 @@ Future<int> starter(
}
}
compiler ??= new _FrontendCompiler(
output,
printerFactory: binaryPrinterFactory,
trackWidgetCreation: options['track-widget-creation'],
);
input ??= stdin;
compiler ??= new _FlutterFrontendCompiler(output, trackWidgetCreation: options['track-widget-creation']);
if (options.rest.isNotEmpty) {
await compiler.compile(options.rest[0], options, generator: generator);
await compiler.compile(options.rest[0], options);
return 0;
}
_State state = _State.READY_FOR_INSTRUCTION;
String boundaryKey;
String recompileFilename;
input
.transform(UTF8.decoder)
.transform(const LineSplitter())
.listen((String string) async {
switch (state) {
case _State.READY_FOR_INSTRUCTION:
const String COMPILE_INSTRUCTION_SPACE = 'compile ';
const String RECOMPILE_INSTRUCTION_SPACE = 'recompile ';
if (string.startsWith(COMPILE_INSTRUCTION_SPACE)) {
final String filename =
string.substring(COMPILE_INSTRUCTION_SPACE.length);
await compiler.compile(filename, options, generator: generator);
} else if (string.startsWith(RECOMPILE_INSTRUCTION_SPACE)) {
// 'recompile [<filename>] <boundarykey>'
// where <boundarykey> can't have spaces
final String remainder = string.substring(RECOMPILE_INSTRUCTION_SPACE.length);
final int spaceDelim = remainder.lastIndexOf(' ');
if (spaceDelim > -1) {
recompileFilename = remainder.substring(0, spaceDelim);
boundaryKey = remainder.substring(spaceDelim + 1);
} else {
boundaryKey = remainder;
}
state = _State.RECOMPILE_LIST;
} else if (string == 'accept') {
compiler.acceptLastDelta();
} else if (string == 'reset') {
compiler.resetIncrementalCompiler();
} else if (string == 'quit') {
exit(0);
}
break;
case _State.RECOMPILE_LIST:
if (string == boundaryKey) {
compiler.recompileDelta(filename: recompileFilename);
state = _State.READY_FOR_INSTRUCTION;
} else
compiler.invalidate(Uri.base.resolve(string));
break;
}
});
frontend.listenAndCompile(compiler, input ?? stdin, options, () { exit(0); } );
return 0;
}
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:isolate';
import 'package:args/src/arg_results.dart';
import 'package:frontend_server/server.dart';
// front_end/src imports below that require lint `ignore_for_file`
// are a temporary state of things until frontend team builds better api
// that would replace api used below. This api was made private in
// an effort to discourage further use.
// ignore_for_file: implementation_imports
import 'package:kernel/binary/ast_to_binary.dart';
import 'package:kernel/ast.dart' show Program;
import 'package:mockito/mockito.dart';
import 'package:test/test.dart';
import 'package:vm/incremental_compiler.dart';
import 'package:vm/frontend_server.dart' as frontend show CompilerInterface;
class _MockedCompiler extends Mock implements CompilerInterface {}
import '../lib/server.dart';
class _MockedIncrementalCompiler extends Mock
implements IncrementalCompiler {}
class _MockedBinaryPrinterFactory extends Mock implements BinaryPrinterFactory {}
class _MockedBinaryPrinter extends Mock implements BinaryPrinter {}
class _MockedCompiler extends Mock implements frontend.CompilerInterface {}
Future<int> main() async {
group('basic', () {
final CompilerInterface compiler = new _MockedCompiler();
final frontend.CompilerInterface compiler = new _MockedCompiler();
test('train with mocked compiler completes', () async {
expect(await starter(<String>['--train'], compiler: compiler), equals(0));
});
});
group('batch compile with mocked compiler', () {
final CompilerInterface compiler = new _MockedCompiler();
test('compile from command line', () async {
final List<String> args = <String>[
'server.dart',
'--sdk-root',
'sdkroot',
];
final int exitcode = await starter(args, compiler: compiler);
expect(exitcode, equals(0));
final List<ArgResults> capturedArgs =
verify(
compiler.compile(
argThat(equals('server.dart')),
captureAny,
generator: any,
)
).captured;
expect(capturedArgs.single['sdk-root'], equals('sdkroot'));
expect(capturedArgs.single['strong'], equals(false));
});
test('compile from command line (strong mode)', () async {
final List<String> args = <String>[
'server.dart',
'--sdk-root',
'sdkroot',
'--strong',
];
final int exitcode = await starter(args, compiler: compiler);
expect(exitcode, equals(0));
final List<ArgResults> capturedArgs =
verify(
compiler.compile(
argThat(equals('server.dart')),
captureAny,
generator: any,
)
).captured;
expect(capturedArgs.single['sdk-root'], equals('sdkroot'));
expect(capturedArgs.single['strong'], equals(true));
});
test('compile from command line with link platform', () async {
final List<String> args = <String>[
'server.dart',
'--sdk-root',
'sdkroot',
'--link-platform',
];
final int exitcode = await starter(args, compiler: compiler);
expect(exitcode, equals(0));
final List<ArgResults> capturedArgs =
verify(
compiler.compile(
argThat(equals('server.dart')),
captureAny,
generator: any,
)
).captured;
expect(capturedArgs.single['sdk-root'], equals('sdkroot'));
expect(capturedArgs.single['link-platform'], equals(true));
expect(capturedArgs.single['strong'], equals(false));
});
});
group('interactive compile with mocked compiler', () {
final CompilerInterface compiler = new _MockedCompiler();
final List<String> args = <String>[
'--sdk-root',
'sdkroot',
];
test('compile one file', () async {
final StreamController<List<int>> inputStreamController =
new StreamController<List<int>>();
final ReceivePort compileCalled = new ReceivePort();
when(compiler.compile(any, any, generator: any)).thenAnswer(
(Invocation invocation) {
expect(invocation.positionalArguments[0], equals('server.dart'));
expect(invocation.positionalArguments[1]['sdk-root'],
equals('sdkroot'));
expect(invocation.positionalArguments[1]['strong'], equals(false));
compileCalled.sendPort.send(true);
}
);
final int exitcode = await starter(args, compiler: compiler,
input: inputStreamController.stream,
);
expect(exitcode, equals(0));
inputStreamController.add('compile server.dart\n'.codeUnits);
await compileCalled.first;
inputStreamController.close();
});
});
group('interactive compile with mocked compiler', () {
final CompilerInterface compiler = new _MockedCompiler();
final List<String> args = <String>[
'--sdk-root',
'sdkroot',
];
final List<String> strongArgs = <String>[
'--sdk-root',
'sdkroot',
'--strong',
];
test('compile one file', () async {
final StreamController<List<int>> inputStreamController =
new StreamController<List<int>>();
final ReceivePort compileCalled = new ReceivePort();
when(compiler.compile(any, any, generator: any)).thenAnswer(
(Invocation invocation) {
expect(invocation.positionalArguments[0], equals('server.dart'));
expect(invocation.positionalArguments[1]['sdk-root'], equals('sdkroot'));
expect(invocation.positionalArguments[1]['strong'], equals(false));
compileCalled.sendPort.send(true);
}
);
final int exitcode = await starter(args, compiler: compiler,
input: inputStreamController.stream,
);
expect(exitcode, equals(0));
inputStreamController.add('compile server.dart\n'.codeUnits);
await compileCalled.first;
inputStreamController.close();
});
test('compile one file (strong mode)', () async {
final StreamController<List<int>> inputStreamController =
new StreamController<List<int>>();
final ReceivePort compileCalled = new ReceivePort();
when(compiler.compile(any, any, generator: any)).thenAnswer(
(Invocation invocation) {
expect(invocation.positionalArguments[0], equals('server.dart'));
expect(invocation.positionalArguments[1]['sdk-root'], equals('sdkroot'));
expect(invocation.positionalArguments[1]['strong'], equals(true));
compileCalled.sendPort.send(true);
}
);
final int exitcode = await starter(strongArgs, compiler: compiler,
input: inputStreamController.stream,
);
expect(exitcode, equals(0));
inputStreamController.add('compile server.dart\n'.codeUnits);
await compileCalled.first;
inputStreamController.close();
});
test('compile few files', () async {
final StreamController<List<int>> streamController =
new StreamController<List<int>>();
final ReceivePort compileCalled = new ReceivePort();
int counter = 1;
when(compiler.compile(any, any, generator: any)).thenAnswer(
(Invocation invocation) {
expect(invocation.positionalArguments[0], equals('server${counter++}.dart'));
expect(invocation.positionalArguments[1]['sdk-root'], equals('sdkroot'));
expect(invocation.positionalArguments[1]['strong'], equals(false));
compileCalled.sendPort.send(true);
}
);
final int exitcode = await starter(args, compiler: compiler,
input: streamController.stream,
);
expect(exitcode, equals(0));
streamController.add('compile server1.dart\n'.codeUnits);
streamController.add('compile server2.dart\n'.codeUnits);
await compileCalled.first;
streamController.close();
});
});
group('interactive incremental compile with mocked compiler', () {
final CompilerInterface compiler = new _MockedCompiler();
final List<String> args = <String>[
'--sdk-root',
'sdkroot',
'--incremental'
];
test('recompile few files', () async {
final StreamController<List<int>> streamController =
new StreamController<List<int>>();
final ReceivePort recompileCalled = new ReceivePort();
when(compiler.recompileDelta(filename: null)).thenAnswer((Invocation invocation) {
recompileCalled.sendPort.send(true);
});
final int exitcode = await starter(args, compiler: compiler,
input: streamController.stream,
);
expect(exitcode, equals(0));
streamController.add('recompile abc\nfile1.dart\nfile2.dart\nabc\n'.codeUnits);
await recompileCalled.first;
verifyInOrder(
<void>[
compiler.invalidate(Uri.base.resolve('file1.dart')),
compiler.invalidate(Uri.base.resolve('file2.dart')),
await compiler.recompileDelta(filename: null),
]
);
streamController.close();
});
test('recompile few files with new entrypoint', () async {
final StreamController<List<int>> streamController =
new StreamController<List<int>>();
final ReceivePort recompileCalled = new ReceivePort();
when(compiler.recompileDelta(filename: 'file2.dart')).thenAnswer((Invocation invocation) {
recompileCalled.sendPort.send(true);
});
final int exitcode = await starter(args, compiler: compiler,
input: streamController.stream,
);
expect(exitcode, equals(0));
streamController.add('recompile file2.dart abc\nfile1.dart\nfile2.dart\nabc\n'.codeUnits);
await recompileCalled.first;
verifyInOrder(
<void>[
compiler.invalidate(Uri.base.resolve('file1.dart')),
compiler.invalidate(Uri.base.resolve('file2.dart')),
await compiler.recompileDelta(filename: 'file2.dart'),
]
);
streamController.close();
});
test('accept', () async {
final StreamController<List<int>> inputStreamController =
new StreamController<List<int>>();
final ReceivePort acceptCalled = new ReceivePort();
when(compiler.acceptLastDelta()).thenAnswer((Invocation invocation) {
acceptCalled.sendPort.send(true);
});
final int exitcode = await starter(args, compiler: compiler,
input: inputStreamController.stream,
);
expect(exitcode, equals(0));
inputStreamController.add('accept\n'.codeUnits);
await acceptCalled.first;
inputStreamController.close();
});
test('reset', () async {
final StreamController<List<int>> inputStreamController =
new StreamController<List<int>>();
final ReceivePort resetCalled = new ReceivePort();
when(compiler.resetIncrementalCompiler()).thenAnswer((Invocation invocation) {
resetCalled.sendPort.send(true);
});
final int exitcode = await starter(args, compiler: compiler,
input: inputStreamController.stream,
);
expect(exitcode, equals(0));
inputStreamController.add('reset\n'.codeUnits);
await resetCalled.first;
inputStreamController.close();
});
test('compile then recompile', () async {
final StreamController<List<int>> streamController =
new StreamController<List<int>>();
final ReceivePort recompileCalled = new ReceivePort();
when(compiler.recompileDelta(filename: null)).thenAnswer((Invocation invocation) {
recompileCalled.sendPort.send(true);
});
final int exitcode = await starter(args, compiler: compiler,
input: streamController.stream,
);
expect(exitcode, equals(0));
streamController.add('compile file1.dart\n'.codeUnits);
streamController.add('accept\n'.codeUnits);
streamController.add('recompile def\nfile2.dart\nfile3.dart\ndef\n'.codeUnits);
await recompileCalled.first;
verifyInOrder(<void>[
await compiler.compile('file1.dart', any, generator: any),
compiler.acceptLastDelta(),
compiler.invalidate(Uri.base.resolve('file2.dart')),
compiler.invalidate(Uri.base.resolve('file3.dart')),
await compiler.recompileDelta(filename: null),
]);
streamController.close();
});
});
group('interactive incremental compile with mocked IKG', () {
final List<String> args = <String>[
'--sdk-root',
'sdkroot',
'--incremental',
];
test('compile then accept', () async {
final StreamController<List<int>> streamController =
new StreamController<List<int>>();
final StreamController<List<int>> stdoutStreamController =
new StreamController<List<int>>();
final IOSink ioSink = new IOSink(stdoutStreamController.sink);
ReceivePort receivedResult = new ReceivePort();
String boundaryKey;
stdoutStreamController.stream
.transform(UTF8.decoder)
.transform(const LineSplitter())
.listen((String s) {
const String RESULT_OUTPUT_SPACE = 'result ';
if (boundaryKey == null) {
if (s.startsWith(RESULT_OUTPUT_SPACE)) {
boundaryKey = s.substring(RESULT_OUTPUT_SPACE.length);
}
} else {
if (s.startsWith(boundaryKey)) {
boundaryKey = null;
receivedResult.sendPort.send(true);
}
}
});
final _MockedIncrementalCompiler generator =
new _MockedIncrementalCompiler();
when(generator.compile())
.thenAnswer((_) => new Future<Program>.value(new Program()));
final _MockedBinaryPrinterFactory printerFactory =
new _MockedBinaryPrinterFactory();
when(printerFactory.newBinaryPrinter(any))
.thenReturn(new _MockedBinaryPrinter());
final int exitcode = await starter(args, compiler: null,
input: streamController.stream,
output: ioSink,
generator: generator,
binaryPrinterFactory: printerFactory,
);
expect(exitcode, equals(0));
streamController.add('compile file1.dart\n'.codeUnits);
await receivedResult.first;
streamController.add('accept\n'.codeUnits);
receivedResult = new ReceivePort();
streamController.add('recompile def\nfile1.dart\ndef\n'.codeUnits);
await receivedResult.first;
streamController.close();
});
group('compile with output path', ()
{
final CompilerInterface compiler = new _MockedCompiler();
test('compile from command line', () async {
final List<String> args = <String>[
'server.dart',
'--sdk-root',
'sdkroot',
'--output-dill',
'/foo/bar/server.dart.dill',
'--output-incremental-dill',
'/foo/bar/server.incremental.dart.dill',
];
final int exitcode = await starter(args, compiler: compiler);
expect(exitcode, equals(0));
final List<ArgResults> capturedArgs =
verify(
compiler.compile(
argThat(equals('server.dart')),
captureAny,
generator: any,
)
).captured;
expect(capturedArgs.single['sdk-root'], equals('sdkroot'));
expect(capturedArgs.single['strong'], equals(false));
});
});
});
return 0;
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册