blob: 2d4b54619ea12b03de414a33e7053487fa875f65 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
library 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_unstable/vm.dart';
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 Component, loadComponentSourceFromBytes;
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,
parseCommandLineDefines,
convertFileOrUriArgumentToUri,
createFrontEndTarget,
createFrontEndFileSystem,
writeDepfile;
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')
..addOption('platform', help: 'Platform kernel filename')
..addFlag('aot',
help: 'Run compiler in AOT mode (enables whole-program transformations)',
defaultsTo: false)
// TODO(alexmarkov): Cleanup uses in Flutter and remove these obsolete flags.
..addFlag('strong', help: 'Obsolete', defaultsTo: true)
..addFlag('tfa',
help:
'Enable global type flow analysis and related transformations in AOT mode.',
defaultsTo: false)
..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('import-dill',
help: 'Import libraries from existing dill file', defaultsTo: null)
..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)
..addOption('target',
help: 'Target model that determines what core libraries are available',
allowed: <String>['vm', 'flutter', 'flutter_runner', 'dart_runner'],
defaultsTo: 'vm')
..addMultiOption('filesystem-root',
help: 'File path that is used as a root in virtual filesystem used in'
' compiled kernel files. When used --output-dill should be provided'
' as well.',
hide: true)
..addOption('filesystem-scheme',
help:
'Scheme that is used in virtual filesystem set up via --filesystem-root'
' option',
defaultsTo: 'org-dartlang-root',
hide: true)
..addFlag('verbose', help: 'Enables verbose output from the compiler.')
..addOption('initialize-from-dill',
help: 'Normally the output dill is used to specify which dill to '
'initialize from, but it can be overwritten here.',
defaultsTo: null,
hide: true)
..addMultiOption('define',
abbr: 'D',
help: 'The values for the environment constants (e.g. -Dkey=value).')
..addFlag('embed-source-text',
help: 'Includes sources into generated dill file. Having sources'
' allows to effectively use observatory to debug produced'
' application, produces better stack traces on exceptions.',
defaultsTo: true)
..addFlag('unsafe-package-serialization',
help: 'Potentially unsafe: Does not allow for invalidating packages, '
'additionally the output dill file might include more libraries than '
'needed. The use case is test-runs, where invalidation is not really '
'used, and where dill filesize does not matter, and the gain is '
'improved speed.',
defaultsTo: false,
hide: true);
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,
COMPILE_EXPRESSION_EXPRESSION,
COMPILE_EXPRESSION_DEFS,
COMPILE_EXPRESSION_TYPEDEFS,
COMPILE_EXPRESSION_LIBRARY_URI,
COMPILE_EXPRESSION_KLASS,
COMPILE_EXPRESSION_IS_STATIC
}
/// 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.
/// Returns [true] if compilation was successful and produced no errors.
Future<bool> 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();
/// Rejects results of previous compilation and sets compiler back to last
/// accepted state.
Future<void> rejectLastDelta();
/// 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();
/// Compiles [expression] with free variables listed in [definitions],
/// free type variables listed in [typedDefinitions]. "Free" means their
/// values are through evaluation API call, rather than coming from running
/// application.
/// If [klass] is not [null], expression is compiled in context of [klass]
/// class.
/// If [klass] is [null],expression is compiled at top-level of
/// [libraryUrl] library. If [klass] is not [null], [isStatic] determines
/// whether expression can refer to [this] or not.
Future<Null> compileExpression(
String expression,
List<String> definitions,
List<String> typeDefinitions,
String libraryUri,
String klass,
bool isStatic);
/// Communicates an error [msg] to the client.
void reportError(String msg);
}
abstract class ProgramTransformer {
void transform(Component component);
}
/// Class that for test mocking purposes encapsulates creation of [BinaryPrinter].
class BinaryPrinterFactory {
/// Creates new [BinaryPrinter] to write to [targetSink].
BinaryPrinter newBinaryPrinter(Sink<List<int>> targetSink) {
return new LimitedBinaryPrinter(targetSink, (_) => true /* predicate */,
false /* excludeUriToSource */);
}
}
class FrontendCompiler implements CompilerInterface {
FrontendCompiler(this._outputStream,
{this.printerFactory,
this.transformer,
this.unsafePackageSerialization}) {
_outputStream ??= stdout;
printerFactory ??= new BinaryPrinterFactory();
}
StringSink _outputStream;
BinaryPrinterFactory printerFactory;
bool unsafePackageSerialization;
CompilerOptions _compilerOptions;
FileSystem _fileSystem;
Uri _mainSource;
ArgResults _options;
IncrementalCompiler _generator;
String _kernelBinaryFilename;
String _kernelBinaryFilenameIncremental;
String _kernelBinaryFilenameFull;
String _initializeFromDill;
final ProgramTransformer transformer;
final List<String> errors = new List<String>();
void setMainSourceFilename(String filename) {
final Uri filenameUri = _getFileOrUri(filename);
_mainSource = filenameUri;
}
@override
Future<bool> compile(
String filename,
ArgResults options, {
IncrementalCompiler generator,
}) async {
_options = options;
_fileSystem = createFrontEndFileSystem(
options['filesystem-scheme'], options['filesystem-root']);
setMainSourceFilename(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;
_initializeFromDill =
_options['initialize-from-dill'] ?? _kernelBinaryFilenameFull;
final String boundaryKey = new Uuid().generateV4();
_outputStream.writeln('result $boundaryKey');
final Uri sdkRoot = _ensureFolderPath(options['sdk-root']);
final String platformKernelDill =
options['platform'] ?? 'platform_strong.dill';
final CompilerOptions compilerOptions = new CompilerOptions()
..sdkRoot = sdkRoot
..fileSystem = _fileSystem
..packagesFileUri = _getFileOrUri(_options['packages'])
..sdkSummary = sdkRoot.resolve(platformKernelDill)
..verbose = options['verbose']
..embedSourceText = options['embed-source-text']
..onDiagnostic = (DiagnosticMessage message) {
bool printMessage;
switch (message.severity) {
case Severity.error:
case Severity.internalProblem:
printMessage = true;
errors.addAll(message.plainTextFormatted);
break;
case Severity.warning:
printMessage = true;
break;
case Severity.errorLegacyWarning:
case Severity.context:
case Severity.ignored:
throw 'Unexpected severity: ${message.severity}';
}
if (printMessage) {
printDiagnosticMessage(message, _outputStream.writeln);
}
};
if (options.wasParsed('filesystem-root')) {
if (_options['output-dill'] == null) {
print('When --filesystem-root is specified it is required to specify'
' --output-dill option that points to physical file system location'
' of a target dill file.');
return false;
}
}
final Map<String, String> environmentDefines = {};
if (!parseCommandLineDefines(
options['define'], environmentDefines, usage)) {
return false;
}
compilerOptions.target = createFrontEndTarget(options['target']);
if (compilerOptions.target == null) {
print('Failed to create front-end target ${options['target']}.');
return false;
}
final String importDill = options['import-dill'];
if (importDill != null) {
compilerOptions.inputSummaries = <Uri>[
Uri.base.resolveUri(new Uri.file(importDill))
];
}
Component component;
if (options['incremental']) {
_compilerOptions = compilerOptions;
_generator =
generator ?? _createGenerator(new Uri.file(_initializeFromDill));
await invalidateIfInitializingFromDill();
component = 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)
];
}
component = await _runWithPrintRedirection(() => compileToKernel(
_mainSource, compilerOptions,
aot: options['aot'],
useGlobalTypeFlowAnalysis: options['tfa'],
environmentDefines: environmentDefines));
}
if (component != null) {
if (transformer != null) {
transformer.transform(component);
}
await writeDillFile(component, _kernelBinaryFilename,
filterExternal: importDill != null);
_outputStream
.writeln('$boundaryKey $_kernelBinaryFilename ${errors.length}');
final String depfile = options['depfile'];
if (depfile != null) {
await writeDepfile(compilerOptions.fileSystem, component,
_kernelBinaryFilename, depfile);
}
_kernelBinaryFilename = _kernelBinaryFilenameIncremental;
} else
_outputStream.writeln(boundaryKey);
return errors.isEmpty;
}
writeDillFile(Component component, String filename,
{bool filterExternal: false}) async {
final IOSink sink = new File(filename).openWrite();
final BinaryPrinter printer = filterExternal
? new LimitedBinaryPrinter(
sink, (lib) => !lib.isExternal, true /* excludeUriToSource */)
: printerFactory.newBinaryPrinter(sink);
component.libraries.sort((Library l1, Library l2) {
return "${l1.fileUri}".compareTo("${l2.fileUri}");
});
component.computeCanonicalNames();
for (Library library in component.libraries) {
library.additionalExports.sort((Reference r1, Reference r2) {
return "${r1.canonicalName}".compareTo("${r2.canonicalName}");
});
}
if (unsafePackageSerialization == true) {
writePackagesToSinkAndTrimComponent(component, sink);
}
printer.writeComponentFile(component);
await sink.close();
}
Future<Null> invalidateIfInitializingFromDill() async {
if (_kernelBinaryFilename != _kernelBinaryFilenameFull) return null;
// If the generator is initialized, it's not going to initialize from dill
// again anyway, so there's no reason to spend time invalidating what should
// be invalidated by the normal approach anyway.
if (_generator.initialized) return null;
final File f = new File(_initializeFromDill);
if (!f.existsSync()) return null;
Component component;
try {
component = loadComponentSourceFromBytes(f.readAsBytesSync());
} catch (e) {
// If we cannot load the dill file we shouldn't initialize from it.
_generator = _createGenerator(null);
return null;
}
nextUri:
for (Uri uri in component.uriToSource.keys) {
if (uri == null || '$uri' == '') continue nextUri;
final List<int> oldBytes = component.uriToSource[uri].source;
FileSystemEntity entity;
try {
entity = _compilerOptions.fileSystem.entityForUri(uri);
} catch (_) {
// Ignore errors that might be caused by non-file uris.
continue nextUri;
}
if (!await entity.exists()) {
_generator.invalidate(uri);
continue nextUri;
}
final List<int> newBytes = await entity.readAsBytes();
if (oldBytes.length != newBytes.length) {
_generator.invalidate(uri);
continue nextUri;
}
for (int i = 0; i < oldBytes.length; ++i) {
if (oldBytes[i] != newBytes[i]) {
_generator.invalidate(uri);
continue nextUri;
}
}
}
}
@override
Future<Null> recompileDelta({String filename}) async {
final String boundaryKey = new Uuid().generateV4();
_outputStream.writeln('result $boundaryKey');
await invalidateIfInitializingFromDill();
if (filename != null) {
setMainSourceFilename(filename);
}
errors.clear();
final Component deltaProgram =
await _generator.compile(entryPoint: _mainSource);
if (deltaProgram != null && transformer != null) {
transformer.transform(deltaProgram);
}
await writeDillFile(deltaProgram, _kernelBinaryFilename);
_outputStream
.writeln('$boundaryKey $_kernelBinaryFilename ${errors.length}');
_kernelBinaryFilename = _kernelBinaryFilenameIncremental;
}
@override
Future<Null> compileExpression(
String expression,
List<String> definitions,
List<String> typeDefinitions,
String libraryUri,
String klass,
bool isStatic) async {
final String boundaryKey = new Uuid().generateV4();
_outputStream.writeln('result $boundaryKey');
Procedure procedure = await _generator.compileExpression(
expression, definitions, typeDefinitions, libraryUri, klass, isStatic);
if (procedure != null) {
final IOSink sink = new File(_kernelBinaryFilename).openWrite();
sink.add(serializeProcedure(procedure));
await sink.close();
_outputStream
.writeln('$boundaryKey $_kernelBinaryFilename ${errors.length}');
_kernelBinaryFilename = _kernelBinaryFilenameIncremental;
} else {
_outputStream.writeln(boundaryKey);
}
}
@override
void reportError(String msg) {
final String boundaryKey = new Uuid().generateV4();
_outputStream.writeln('result $boundaryKey');
_outputStream.writeln(msg);
_outputStream.writeln(boundaryKey);
}
/// Map of already serialized dill data. All uris in a serialized component
/// maps to the same blob of data. Used by
/// [writePackagesToSinkAndTrimComponent].
Map<Uri, List<int>> cachedPackageLibraries = new Map<Uri, List<int>>();
/// Map of dependencies for already serialized dill data.
/// E.g. if blob1 dependents on blob2, but only using a single file from blob1
/// that does not dependent on blob2, blob2 would not be included leaving the
/// dill file in a weird state that could cause the VM to crash if asked to
/// forcefully compile everything. Used by
/// [writePackagesToSinkAndTrimComponent].
Map<Uri, List<Uri>> cachedPackageDependencies = new Map<Uri, List<Uri>>();
writePackagesToSinkAndTrimComponent(
Component deltaProgram, Sink<List<int>> ioSink) {
if (deltaProgram == null) return;
List<Library> packageLibraries = new List<Library>();
List<Library> libraries = new List<Library>();
deltaProgram.computeCanonicalNames();
for (var lib in deltaProgram.libraries) {
Uri uri = lib.importUri;
if (uri.scheme == "package") {
packageLibraries.add(lib);
} else {
libraries.add(lib);
}
}
deltaProgram.libraries
..clear()
..addAll(libraries);
Map<String, List<Library>> newPackages = new Map<String, List<Library>>();
Set<List<int>> alreadyAdded = new Set<List<int>>();
addDataAndDependentData(List<int> data, Uri uri) {
if (alreadyAdded.add(data)) {
ioSink.add(data);
// Now also add all dependencies.
for (Uri dep in cachedPackageDependencies[uri]) {
addDataAndDependentData(cachedPackageLibraries[dep], dep);
}
}
}
for (Library lib in packageLibraries) {
List<int> data = cachedPackageLibraries[lib.fileUri];
if (data != null) {
addDataAndDependentData(data, lib.fileUri);
} else {
String package = lib.importUri.pathSegments.first;
newPackages[package] ??= <Library>[];
newPackages[package].add(lib);
}
}
for (String package in newPackages.keys) {
List<Library> libraries = newPackages[package];
Component singleLibrary = new Component(
libraries: libraries,
uriToSource: deltaProgram.uriToSource,
nameRoot: deltaProgram.root);
ByteSink byteSink = new ByteSink();
final BinaryPrinter printer = printerFactory.newBinaryPrinter(byteSink);
printer.writeComponentFile(singleLibrary);
// Record things this package blob dependent on.
Set<Uri> libraryUris = new Set<Uri>();
for (Library lib in libraries) {
libraryUris.add(lib.fileUri);
}
Set<Uri> deps = new Set<Uri>();
for (Library lib in libraries) {
for (LibraryDependency dep in lib.dependencies) {
Library dependencyLibrary = dep.importedLibraryReference.asLibrary;
if (dependencyLibrary.importUri.scheme != "package") continue;
Uri dependencyLibraryUri =
dep.importedLibraryReference.asLibrary.fileUri;
if (libraryUris.contains(dependencyLibraryUri)) continue;
deps.add(dependencyLibraryUri);
}
}
List<int> data = byteSink.builder.takeBytes();
for (Library lib in libraries) {
cachedPackageLibraries[lib.fileUri] = data;
cachedPackageDependencies[lib.fileUri] = new List<Uri>.from(deps);
}
ioSink.add(data);
}
}
@override
void acceptLastDelta() {
_generator.accept();
}
@override
Future<void> rejectLastDelta() async {
await _generator.reject();
final String boundaryKey = new Uuid().generateV4();
_outputStream.writeln('result $boundaryKey');
_outputStream.writeln(boundaryKey);
}
@override
void invalidate(Uri uri) {
_generator.invalidate(uri);
}
@override
void resetIncrementalCompiler() {
_generator.resetDeltaState();
_kernelBinaryFilename = _kernelBinaryFilenameFull;
}
Uri _getFileOrUri(String fileOrUri) =>
convertFileOrUriArgumentToUri(_fileSystem, fileOrUri);
IncrementalCompiler _createGenerator(Uri initializeFromDillUri) {
return new IncrementalCompiler(_compilerOptions, _mainSource,
initializeFromDillUri: initializeFromDillUri);
}
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)));
}
}
/// A [Sink] that directly writes data into a byte builder.
class ByteSink implements Sink<List<int>> {
final BytesBuilder builder = new BytesBuilder();
void add(List<int> data) {
builder.add(data);
}
void close() {}
}
class _CompileExpressionRequest {
String expression;
// Note that FE will reject a compileExpression command by returning a null
// procedure when defs or typeDefs include an illegal identifier.
List<String> defs = <String>[];
List<String> typeDefs = <String>[];
String library;
String klass;
bool isStatic;
}
/// Listens for the compilation commands on [input] stream.
/// This supports "interactive" recompilation mode of execution.
void listenAndCompile(CompilerInterface compiler, Stream<List<int>> input,
ArgResults options, Completer<int> completer,
{IncrementalCompiler generator}) {
_State state = _State.READY_FOR_INSTRUCTION;
_CompileExpressionRequest compileExpressionRequest;
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 ';
const String COMPILE_EXPRESSION_INSTRUCTION_SPACE =
'compile-expression ';
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.startsWith(COMPILE_EXPRESSION_INSTRUCTION_SPACE)) {
// 'compile-expression <boundarykey>
// expression
// definitions (one per line)
// ...
// <boundarykey>
// type-defintions (one per line)
// ...
// <boundarykey>
// <libraryUri: String>
// <klass: String>
// <isStatic: true|false>
compileExpressionRequest = new _CompileExpressionRequest();
boundaryKey =
string.substring(COMPILE_EXPRESSION_INSTRUCTION_SPACE.length);
state = _State.COMPILE_EXPRESSION_EXPRESSION;
} else if (string == 'accept') {
compiler.acceptLastDelta();
} else if (string == 'reject') {
await compiler.rejectLastDelta();
} else if (string == 'reset') {
compiler.resetIncrementalCompiler();
} else if (string == 'quit') {
completer.complete(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;
case _State.COMPILE_EXPRESSION_EXPRESSION:
compileExpressionRequest.expression = string;
state = _State.COMPILE_EXPRESSION_DEFS;
break;
case _State.COMPILE_EXPRESSION_DEFS:
if (string == boundaryKey) {
state = _State.COMPILE_EXPRESSION_TYPEDEFS;
} else {
compileExpressionRequest.defs.add(string);
}
break;
case _State.COMPILE_EXPRESSION_TYPEDEFS:
if (string == boundaryKey) {
state = _State.COMPILE_EXPRESSION_LIBRARY_URI;
} else {
compileExpressionRequest.typeDefs.add(string);
}
break;
case _State.COMPILE_EXPRESSION_LIBRARY_URI:
compileExpressionRequest.library = string;
state = _State.COMPILE_EXPRESSION_KLASS;
break;
case _State.COMPILE_EXPRESSION_KLASS:
compileExpressionRequest.klass = string.isEmpty ? null : string;
state = _State.COMPILE_EXPRESSION_IS_STATIC;
break;
case _State.COMPILE_EXPRESSION_IS_STATIC:
if (string == 'true' || string == 'false') {
compileExpressionRequest.isStatic = string == 'true';
compiler.compileExpression(
compileExpressionRequest.expression,
compileExpressionRequest.defs,
compileExpressionRequest.typeDefs,
compileExpressionRequest.library,
compileExpressionRequest.klass,
compileExpressionRequest.isStatic);
} else {
compiler
.reportError('Got $string. Expected either "true" or "false"');
}
state = _State.READY_FOR_INSTRUCTION;
break;
}
});
}
/// Entry point for this module, that creates `_FrontendCompiler` instance and
/// processes user input.
/// `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 {
ArgResults options;
try {
options = argParser.parse(args);
} catch (error) {
print('ERROR: $error\n');
print(usage);
return 1;
}
if (options['train']) {
final String sdkRoot = options['sdk-root'];
final String platform = options['platform'];
final Directory temp =
Directory.systemTemp.createTempSync('train_frontend_server');
try {
final String outputTrainingDill = path.join(temp.path, 'app.dill');
final List<String> args = <String>[
'--incremental',
'--sdk-root=$sdkRoot',
'--output-dill=$outputTrainingDill',
];
if (platform != null) {
args.add('--platform=${new Uri.file(platform)}');
}
options = argParser.parse(args);
compiler ??=
new FrontendCompiler(output, printerFactory: binaryPrinterFactory);
await compiler.compile(Platform.script.toFilePath(), options,
generator: generator);
compiler.acceptLastDelta();
await compiler.recompileDelta();
compiler.acceptLastDelta();
compiler.resetIncrementalCompiler();
await compiler.recompileDelta();
compiler.acceptLastDelta();
await compiler.recompileDelta();
compiler.acceptLastDelta();
return 0;
} finally {
temp.deleteSync(recursive: true);
}
}
compiler ??= new FrontendCompiler(output,
printerFactory: binaryPrinterFactory,
unsafePackageSerialization: options["unsafe-package-serialization"]);
if (options.rest.isNotEmpty) {
return await compiler.compile(options.rest[0], options,
generator: generator)
? 0
: 254;
}
Completer<int> completer = new Completer<int>();
listenAndCompile(compiler, input ?? stdin, options, completer,
generator: generator);
return completer.future;
}