From 97b22348c8d5cba471418dc94f9e384e384e796f Mon Sep 17 00:00:00 2001 From: Alexander Aprelev Date: Fri, 2 Mar 2018 07:52:19 -0800 Subject: [PATCH] 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. --- .../track_widget_constructor_locations.dart | 4 +- frontend_server/lib/server.dart | 438 ++---------------- frontend_server/test/server_test.dart | 416 +---------------- 3 files changed, 47 insertions(+), 811 deletions(-) diff --git a/flutter_kernel_transformers/lib/track_widget_constructor_locations.dart b/flutter_kernel_transformers/lib/track_widget_constructor_locations.dart index eca364c47c9e1..8ffd699ad5a12 100644 --- a/flutter_kernel_transformers/lib/track_widget_constructor_locations.dart +++ b/flutter_kernel_transformers/lib/track_widget_constructor_locations.dart @@ -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 libraries = program.libraries; diff --git a/frontend_server/lib/server.dart b/frontend_server/lib/server.dart index 610ae68741e71..7c287e2b2637c 100644 --- a/frontend_server/lib/server.dart +++ b/frontend_server/lib/server.dart @@ -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 -- recompile [] - - -... - -- accept -- quit - -Output: -- result - - [] - -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 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 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 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 = [ - 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 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 oldBytes = program.uriToSource[uri].source; - final FileSystemEntity entity = _compilerOptions.fileSystem.entityForUri(uri); - if (!await entity.exists()) { - _generator.invalidate(uri); - continue; - } - final List 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 compile(String filename, ArgResults options, {IncrementalCompiler generator}) async { + return _compiler.compile(filename, options, generator: generator); } @override Future 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 _runWithPrintRedirection(Future f()) { - return runZoned(() => new Future(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 _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 _writeDepfile(Program program, String output, String depfile) async /// `compiler` is an optional parameter so it can be replaced with mocked /// version for testing. Future starter( - List args, { - CompilerInterface compiler, - Stream> input, - StringSink output, - IncrementalCompiler generator, - BinaryPrinterFactory binaryPrinterFactory, -}) async { + List args, { + _FlutterFrontendCompiler compiler, + Stream> 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 starter( final Directory temp = Directory.systemTemp.createTempSync('train_frontend_server'); try { final String outputTrainingDill = path.join(temp.path, 'app.dill'); - options = - _argParser.parse(['--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([ + '--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 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 [] ' - // where 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; } diff --git a/frontend_server/test/server_test.dart b/frontend_server/test/server_test.dart index c05d8d48a7044..b2d8e87d13e3e 100644 --- a/frontend_server/test/server_test.dart +++ b/frontend_server/test/server_test.dart @@ -1,428 +1,20 @@ 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 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(['--train'], compiler: compiler), equals(0)); }); }); - - group('batch compile with mocked compiler', () { - final CompilerInterface compiler = new _MockedCompiler(); - - test('compile from command line', () async { - final List args = [ - 'server.dart', - '--sdk-root', - 'sdkroot', - ]; - final int exitcode = await starter(args, compiler: compiler); - expect(exitcode, equals(0)); - final List 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 args = [ - 'server.dart', - '--sdk-root', - 'sdkroot', - '--strong', - ]; - final int exitcode = await starter(args, compiler: compiler); - expect(exitcode, equals(0)); - final List 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 args = [ - 'server.dart', - '--sdk-root', - 'sdkroot', - '--link-platform', - ]; - final int exitcode = await starter(args, compiler: compiler); - expect(exitcode, equals(0)); - final List 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 args = [ - '--sdk-root', - 'sdkroot', - ]; - - test('compile one file', () async { - final StreamController> inputStreamController = - new StreamController>(); - 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 args = [ - '--sdk-root', - 'sdkroot', - ]; - final List strongArgs = [ - '--sdk-root', - 'sdkroot', - '--strong', - ]; - - test('compile one file', () async { - final StreamController> inputStreamController = - new StreamController>(); - 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> inputStreamController = - new StreamController>(); - 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> streamController = - new StreamController>(); - 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 args = [ - '--sdk-root', - 'sdkroot', - '--incremental' - ]; - - test('recompile few files', () async { - final StreamController> streamController = - new StreamController>(); - 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( - [ - 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> streamController = - new StreamController>(); - 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( - [ - 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> inputStreamController = - new StreamController>(); - 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> inputStreamController = - new StreamController>(); - 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> streamController = - new StreamController>(); - 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([ - 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 args = [ - '--sdk-root', - 'sdkroot', - '--incremental', - ]; - - test('compile then accept', () async { - final StreamController> streamController = - new StreamController>(); - final StreamController> stdoutStreamController = - new StreamController>(); - 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.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 args = [ - '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 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; }