7580e77ef2
Change-Id: Id5511296eb0b6acf812bc464d71efa4019f211d7 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/103523 Reviewed-by: Sigmund Cherem <sigmund@google.com>
1169 lines
41 KiB
Dart
1169 lines
41 KiB
Dart
// Copyright (c) 2013, 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.
|
|
|
|
library dart2js.cmdline;
|
|
|
|
import 'dart:async' show Future;
|
|
import 'dart:convert' show utf8, LineSplitter;
|
|
import 'dart:io' show exit, File, FileMode, Platform, stdin, stderr;
|
|
import 'dart:isolate' show Isolate;
|
|
|
|
import 'package:front_end/src/api_unstable/dart2js.dart' as fe;
|
|
|
|
import '../compiler_new.dart' as api;
|
|
import 'commandline_options.dart';
|
|
import 'filenames.dart';
|
|
import 'options.dart' show CompilerOptions;
|
|
import 'source_file_provider.dart';
|
|
import 'util/command_line.dart';
|
|
import 'util/uri_extras.dart';
|
|
import 'util/util.dart' show stackTraceFilePrefix;
|
|
|
|
const String _defaultSpecificationUri = '../../../../sdk/lib/libraries.json';
|
|
const String OUTPUT_LANGUAGE_DART = 'Dart';
|
|
|
|
/// A string to identify the revision or build.
|
|
///
|
|
/// This ID is displayed if the compiler crashes and in verbose mode, and is
|
|
/// an aid in reproducing bug reports.
|
|
///
|
|
/// The actual string is rewritten by a wrapper script when included in the sdk.
|
|
String BUILD_ID = null;
|
|
|
|
/// The data passed to the [HandleOption] callback is either a single
|
|
/// string argument, or the arguments iterator for multiple arguments
|
|
/// handlers.
|
|
typedef void HandleOption(Null data);
|
|
|
|
class OptionHandler {
|
|
final String pattern;
|
|
final HandleOption _handle;
|
|
final bool multipleArguments;
|
|
|
|
void handle(argument) {
|
|
(_handle as dynamic)(argument);
|
|
}
|
|
|
|
OptionHandler(this.pattern, this._handle, {this.multipleArguments: false});
|
|
}
|
|
|
|
/// Extract the parameter of an option.
|
|
///
|
|
/// For example, in ['--out=fisk.js'] and ['-ohest.js'], the parameters
|
|
/// are ['fisk.js'] and ['hest.js'], respectively.
|
|
String extractParameter(String argument, {bool isOptionalArgument: false}) {
|
|
// m[0] is the entire match (which will be equal to argument). m[1]
|
|
// is something like "-o" or "--out=", and m[2] is the parameter.
|
|
Match m = new RegExp('^(-[a-zA-Z]|--.+=)(.*)').firstMatch(argument);
|
|
if (m == null) {
|
|
if (isOptionalArgument) return null;
|
|
helpAndFail('Unknown option "$argument".');
|
|
}
|
|
return m[2];
|
|
}
|
|
|
|
String extractPath(String argument, {bool isDirectory: true}) {
|
|
String path = nativeToUriPath(extractParameter(argument));
|
|
return !path.endsWith("/") && isDirectory ? "$path/" : path;
|
|
}
|
|
|
|
void parseCommandLine(List<OptionHandler> handlers, List<String> argv) {
|
|
// TODO(ahe): Use ../../args/args.dart for parsing options instead.
|
|
var patterns = <String>[];
|
|
for (OptionHandler handler in handlers) {
|
|
patterns.add(handler.pattern);
|
|
}
|
|
var pattern = new RegExp('^(${patterns.join(")\$|^(")})\$');
|
|
|
|
Iterator<String> arguments = argv.iterator;
|
|
OUTER:
|
|
while (arguments.moveNext()) {
|
|
String argument = arguments.current;
|
|
Match match = pattern.firstMatch(argument);
|
|
assert(match.groupCount == handlers.length);
|
|
for (int i = 0; i < handlers.length; i++) {
|
|
if (match[i + 1] != null) {
|
|
OptionHandler handler = handlers[i];
|
|
if (handler.multipleArguments) {
|
|
handler.handle(arguments);
|
|
} else {
|
|
handler.handle(argument);
|
|
}
|
|
continue OUTER;
|
|
}
|
|
}
|
|
throw 'Internal error: "$argument" did not match';
|
|
}
|
|
}
|
|
|
|
FormattingDiagnosticHandler diagnosticHandler;
|
|
|
|
Future<api.CompilationResult> compile(List<String> argv,
|
|
{fe.InitializedCompilerState kernelInitializedCompilerState}) {
|
|
Stopwatch wallclock = new Stopwatch()..start();
|
|
stackTraceFilePrefix = '$currentDirectory';
|
|
Uri librariesSpecificationUri =
|
|
currentDirectory.resolve('lib/libraries.json');
|
|
bool outputSpecified = false;
|
|
Uri out;
|
|
Uri sourceMapOut;
|
|
Uri readDataUri;
|
|
Uri writeDataUri;
|
|
Uri readCodegenUri;
|
|
Uri writeCodegenUri;
|
|
int codegenShard;
|
|
int codegenShards;
|
|
List<String> bazelPaths;
|
|
Uri packageConfig = null;
|
|
Uri packageRoot = null;
|
|
List<String> options = new List<String>();
|
|
bool wantHelp = false;
|
|
bool wantVersion = false;
|
|
bool trustTypeAnnotations = false;
|
|
bool checkedMode = false;
|
|
bool strongMode = true;
|
|
List<String> hints = <String>[];
|
|
bool verbose;
|
|
bool throwOnError;
|
|
int throwOnErrorCount;
|
|
bool showWarnings;
|
|
bool showHints;
|
|
bool enableColors;
|
|
int optimizationLevel = null;
|
|
Uri platformBinaries;
|
|
Map<String, String> environment = new Map<String, String>();
|
|
ReadStrategy readStrategy = ReadStrategy.fromDart;
|
|
WriteStrategy writeStrategy = WriteStrategy.toJs;
|
|
|
|
void passThrough(String argument) => options.add(argument);
|
|
void ignoreOption(String argument) {}
|
|
|
|
if (BUILD_ID != null) {
|
|
passThrough("--build-id=$BUILD_ID");
|
|
}
|
|
|
|
void setLibrarySpecificationUri(String argument) {
|
|
librariesSpecificationUri =
|
|
currentDirectory.resolve(extractPath(argument, isDirectory: false));
|
|
}
|
|
|
|
void setPackageRoot(String argument) {
|
|
packageRoot = currentDirectory.resolve(extractPath(argument));
|
|
}
|
|
|
|
void setPackageConfig(String argument) {
|
|
packageConfig =
|
|
currentDirectory.resolve(extractPath(argument, isDirectory: false));
|
|
}
|
|
|
|
void setOutput(Iterator<String> arguments) {
|
|
outputSpecified = true;
|
|
String path;
|
|
if (arguments.current == '-o') {
|
|
if (!arguments.moveNext()) {
|
|
helpAndFail('Error: Missing file after -o option.');
|
|
}
|
|
path = arguments.current;
|
|
} else {
|
|
path = extractParameter(arguments.current);
|
|
}
|
|
out = currentDirectory.resolve(nativeToUriPath(path));
|
|
}
|
|
|
|
void setOptimizationLevel(String argument) {
|
|
int value = int.tryParse(extractParameter(argument));
|
|
if (value == null || value < 0 || value > 4) {
|
|
helpAndFail("Error: Unsupported optimization level '$argument', "
|
|
"supported levels are: 0, 1, 2, 3, 4");
|
|
return;
|
|
}
|
|
optimizationLevel = value;
|
|
}
|
|
|
|
void setOutputType(String argument) {
|
|
if (argument == '--output-type=dart' ||
|
|
argument == '--output-type=dart-multi') {
|
|
helpAndFail(
|
|
"--output-type=dart is no longer supported. It was deprecated "
|
|
"since Dart 1.11 and removed in Dart 1.19.");
|
|
}
|
|
}
|
|
|
|
setStrip(String argument) {
|
|
helpAndFail("Option '--force-strip' is not in use now that"
|
|
"--output-type=dart is no longer supported.");
|
|
}
|
|
|
|
void setBazelPaths(String argument) {
|
|
String paths = extractParameter(argument);
|
|
bazelPaths = <String>[]..addAll(paths.split(','));
|
|
}
|
|
|
|
String getDepsOutput(Iterable<Uri> sourceFiles) {
|
|
var filenames = sourceFiles.map((uri) => '$uri').toList();
|
|
filenames.sort();
|
|
return filenames.join("\n");
|
|
}
|
|
|
|
void setAllowNativeExtensions(String argument) {
|
|
helpAndFail("Option '${Flags.allowNativeExtensions}' is not supported.");
|
|
}
|
|
|
|
void setVerbose(_) {
|
|
verbose = true;
|
|
passThrough('--verbose');
|
|
}
|
|
|
|
void setTrustTypeAnnotations(String argument) {
|
|
trustTypeAnnotations = true;
|
|
}
|
|
|
|
void setCheckedMode(String argument) {
|
|
checkedMode = true;
|
|
passThrough(argument);
|
|
}
|
|
|
|
void addInEnvironment(String argument) {
|
|
int eqIndex = argument.indexOf('=');
|
|
String name = argument.substring(2, eqIndex);
|
|
String value = argument.substring(eqIndex + 1);
|
|
environment[name] = value;
|
|
}
|
|
|
|
void setCategories(String argument) {
|
|
List<String> categories = extractParameter(argument).split(',');
|
|
bool isServerMode = categories.length == 1 && categories.single == "Server";
|
|
if (isServerMode) {
|
|
hints.add("The --categories flag is deprecated and will be deleted in a "
|
|
"future release, please use '${Flags.serverMode}' instead of "
|
|
"'--categories=Server'.");
|
|
passThrough(Flags.serverMode);
|
|
} else {
|
|
hints.add(
|
|
"The --categories flag is deprecated, see the usage for details.");
|
|
}
|
|
}
|
|
|
|
void setPlatformBinaries(String argument) {
|
|
platformBinaries =
|
|
currentDirectory.resolve(extractPath(argument, isDirectory: true));
|
|
}
|
|
|
|
void setReadData(String argument) {
|
|
if (argument != Flags.readData) {
|
|
readDataUri = nativeToUri(extractPath(argument, isDirectory: false));
|
|
}
|
|
if (readStrategy != ReadStrategy.fromCodegen) {
|
|
readStrategy = ReadStrategy.fromData;
|
|
}
|
|
}
|
|
|
|
void setDillDependencies(String argument) {
|
|
String dependencies = extractParameter(argument);
|
|
String uriDependencies = dependencies.splitMapJoin(',',
|
|
onMatch: (_) => ',', onNonMatch: (p) => '${nativeToUri(p)}');
|
|
options.add('${Flags.dillDependencies}=${uriDependencies}');
|
|
}
|
|
|
|
void setCfeOnly(String argument) {
|
|
if (writeStrategy == WriteStrategy.toData) {
|
|
fail("Cannot use ${Flags.cfeOnly} "
|
|
"and write serialized data simultaneously.");
|
|
}
|
|
if (writeStrategy == WriteStrategy.toCodegen) {
|
|
fail("Cannot use ${Flags.cfeOnly} "
|
|
"and write serialized codegen simultaneously.");
|
|
}
|
|
writeStrategy = WriteStrategy.toKernel;
|
|
}
|
|
|
|
void setReadCodegen(String argument) {
|
|
if (argument != Flags.readCodegen) {
|
|
readCodegenUri = nativeToUri(extractPath(argument, isDirectory: false));
|
|
}
|
|
readStrategy = ReadStrategy.fromCodegen;
|
|
}
|
|
|
|
void setWriteData(String argument) {
|
|
if (writeStrategy == WriteStrategy.toKernel) {
|
|
fail("Cannot use ${Flags.cfeOnly} "
|
|
"and write serialized data simultaneously.");
|
|
}
|
|
if (writeStrategy == WriteStrategy.toCodegen) {
|
|
fail("Cannot write serialized data and codegen simultaneously.");
|
|
}
|
|
if (argument != Flags.writeData) {
|
|
writeDataUri = nativeToUri(extractPath(argument, isDirectory: false));
|
|
}
|
|
writeStrategy = WriteStrategy.toData;
|
|
}
|
|
|
|
void setWriteCodegen(String argument) {
|
|
if (writeStrategy == WriteStrategy.toKernel) {
|
|
fail("Cannot use ${Flags.cfeOnly} "
|
|
"and write serialized codegen simultaneously.");
|
|
}
|
|
if (writeStrategy == WriteStrategy.toData) {
|
|
fail("Cannot write serialized data and codegen data simultaneously.");
|
|
}
|
|
if (argument != Flags.writeCodegen) {
|
|
writeCodegenUri = nativeToUri(extractPath(argument, isDirectory: false));
|
|
}
|
|
writeStrategy = WriteStrategy.toCodegen;
|
|
}
|
|
|
|
void setCodegenShard(String argument) {
|
|
codegenShard = int.parse(extractParameter(argument));
|
|
}
|
|
|
|
void setCodegenShards(String argument) {
|
|
codegenShards = int.parse(extractParameter(argument));
|
|
}
|
|
|
|
void setDumpInfo(String argument) {
|
|
passThrough(Flags.dumpInfo);
|
|
if (argument == Flags.dumpInfo || argument == "${Flags.dumpInfo}=json") {
|
|
return;
|
|
}
|
|
if (argument == "${Flags.dumpInfo}=binary") {
|
|
passThrough(argument);
|
|
return;
|
|
}
|
|
helpAndFail("Error: Unsupported dump-info format '$argument', "
|
|
"supported formats are: json or binary");
|
|
}
|
|
|
|
void handleThrowOnError(String argument) {
|
|
throwOnError = true;
|
|
String parameter = extractParameter(argument, isOptionalArgument: true);
|
|
if (parameter != null) {
|
|
var count = int.parse(parameter);
|
|
throwOnErrorCount = count;
|
|
}
|
|
}
|
|
|
|
void handleShortOptions(String argument) {
|
|
var shortOptions = argument.substring(1).split("");
|
|
for (var shortOption in shortOptions) {
|
|
switch (shortOption) {
|
|
case 'v':
|
|
setVerbose(null);
|
|
break;
|
|
case 'h':
|
|
case '?':
|
|
wantHelp = true;
|
|
break;
|
|
case 'c':
|
|
setCheckedMode(Flags.enableCheckedMode);
|
|
break;
|
|
case 'm':
|
|
passThrough(Flags.minify);
|
|
break;
|
|
default:
|
|
throw 'Internal error: "$shortOption" did not match';
|
|
}
|
|
}
|
|
}
|
|
|
|
List<String> arguments = <String>[];
|
|
List<OptionHandler> handlers = <OptionHandler>[
|
|
new OptionHandler('-[chvm?]+', handleShortOptions),
|
|
new OptionHandler('--throw-on-error(?:=[0-9]+)?', handleThrowOnError),
|
|
new OptionHandler(Flags.suppressWarnings, (String argument) {
|
|
showWarnings = false;
|
|
passThrough(argument);
|
|
}),
|
|
new OptionHandler(Flags.fatalWarnings, passThrough),
|
|
new OptionHandler(Flags.suppressHints, (String argument) {
|
|
showHints = false;
|
|
passThrough(argument);
|
|
}),
|
|
// TODO(sigmund): remove entirely after Dart 1.20
|
|
new OptionHandler(
|
|
'--output-type=dart|--output-type=dart-multi|--output-type=js',
|
|
setOutputType),
|
|
new OptionHandler('--use-kernel', ignoreOption),
|
|
new OptionHandler(Flags.platformBinaries, setPlatformBinaries),
|
|
new OptionHandler(Flags.noFrequencyBasedMinification, passThrough),
|
|
new OptionHandler(Flags.verbose, setVerbose),
|
|
new OptionHandler(Flags.progress, passThrough),
|
|
new OptionHandler(Flags.version, (_) => wantVersion = true),
|
|
new OptionHandler('--library-root=.+', ignoreOption),
|
|
new OptionHandler('--libraries-spec=.+', setLibrarySpecificationUri),
|
|
new OptionHandler('${Flags.dillDependencies}=.+', setDillDependencies),
|
|
new OptionHandler('${Flags.readData}|${Flags.readData}=.+', setReadData),
|
|
new OptionHandler('${Flags.writeData}|${Flags.writeData}=.+', setWriteData),
|
|
new OptionHandler(
|
|
'${Flags.readCodegen}|${Flags.readCodegen}=.+', setReadCodegen),
|
|
new OptionHandler(
|
|
'${Flags.writeCodegen}|${Flags.writeCodegen}=.+', setWriteCodegen),
|
|
new OptionHandler('${Flags.codegenShard}=.+', setCodegenShard),
|
|
new OptionHandler('${Flags.codegenShards}=.+', setCodegenShards),
|
|
new OptionHandler(Flags.cfeOnly, setCfeOnly),
|
|
new OptionHandler(Flags.debugGlobalInference, passThrough),
|
|
new OptionHandler('--out=.+|-o.*', setOutput, multipleArguments: true),
|
|
new OptionHandler('-O.*', setOptimizationLevel),
|
|
new OptionHandler(Flags.allowMockCompilation, ignoreOption),
|
|
new OptionHandler(Flags.fastStartup, ignoreOption),
|
|
new OptionHandler(Flags.genericMethodSyntax, ignoreOption),
|
|
new OptionHandler(Flags.initializingFormalAccess, ignoreOption),
|
|
new OptionHandler(Flags.minify, passThrough),
|
|
new OptionHandler(Flags.noMinify, passThrough),
|
|
new OptionHandler(Flags.preserveUris, ignoreOption),
|
|
new OptionHandler('--force-strip=.*', setStrip),
|
|
new OptionHandler(Flags.disableDiagnosticColors, (_) {
|
|
enableColors = false;
|
|
}),
|
|
new OptionHandler(Flags.enableDiagnosticColors, (_) {
|
|
enableColors = true;
|
|
}),
|
|
new OptionHandler('--enable[_-]checked[_-]mode|--checked',
|
|
(_) => setCheckedMode(Flags.enableCheckedMode)),
|
|
new OptionHandler(Flags.enableAsserts, passThrough),
|
|
new OptionHandler(Flags.trustTypeAnnotations, setTrustTypeAnnotations),
|
|
new OptionHandler(Flags.trustPrimitives, passThrough),
|
|
new OptionHandler(Flags.trustJSInteropTypeAnnotations, passThrough),
|
|
new OptionHandler(r'--help|/\?|/h', (_) => wantHelp = true),
|
|
new OptionHandler('--packages=.+', setPackageConfig),
|
|
new OptionHandler('--package-root=.+|-p.+', setPackageRoot),
|
|
new OptionHandler(Flags.noSourceMaps, passThrough),
|
|
new OptionHandler(Option.resolutionInput, ignoreOption),
|
|
new OptionHandler(Option.bazelPaths, setBazelPaths),
|
|
new OptionHandler(Flags.resolveOnly, ignoreOption),
|
|
new OptionHandler(Flags.disableNativeLiveTypeAnalysis, passThrough),
|
|
new OptionHandler('--categories=.*', setCategories),
|
|
new OptionHandler(Flags.serverMode, passThrough),
|
|
new OptionHandler(Flags.disableInlining, passThrough),
|
|
new OptionHandler(Flags.disableProgramSplit, passThrough),
|
|
new OptionHandler(Flags.disableTypeInference, passThrough),
|
|
new OptionHandler(Flags.useTrivialAbstractValueDomain, passThrough),
|
|
new OptionHandler(Flags.disableRtiOptimization, passThrough),
|
|
new OptionHandler(Flags.terse, passThrough),
|
|
new OptionHandler('--deferred-map=.+', passThrough),
|
|
new OptionHandler(Flags.newDeferredSplit, passThrough),
|
|
new OptionHandler(Flags.reportInvalidInferredDeferredTypes, passThrough),
|
|
new OptionHandler('${Flags.dumpInfo}|${Flags.dumpInfo}=.+', setDumpInfo),
|
|
new OptionHandler('--disallow-unsafe-eval', ignoreOption),
|
|
new OptionHandler(Option.showPackageWarnings, passThrough),
|
|
new OptionHandler(Option.enableLanguageExperiments, passThrough),
|
|
new OptionHandler(Flags.useContentSecurityPolicy, passThrough),
|
|
new OptionHandler('--enable-experimental-mirrors', ignoreOption),
|
|
new OptionHandler(Flags.enableAssertMessage, passThrough),
|
|
new OptionHandler('--strong', ignoreOption),
|
|
new OptionHandler(Flags.previewDart2, ignoreOption),
|
|
new OptionHandler(Flags.omitImplicitChecks, passThrough),
|
|
new OptionHandler(Flags.omitAsCasts, passThrough),
|
|
new OptionHandler(Flags.laxRuntimeTypeToString, passThrough),
|
|
new OptionHandler(Flags.benchmarkingProduction, passThrough),
|
|
|
|
// TODO(floitsch): remove conditional directives flag.
|
|
// We don't provide the info-message yet, since we haven't publicly
|
|
// launched the feature yet.
|
|
new OptionHandler(Flags.conditionalDirectives, ignoreOption),
|
|
new OptionHandler('--enable-async', ignoreOption),
|
|
new OptionHandler('--enable-null-aware-operators', ignoreOption),
|
|
new OptionHandler('--enable-enum', ignoreOption),
|
|
new OptionHandler(Flags.allowNativeExtensions, setAllowNativeExtensions),
|
|
new OptionHandler(Flags.generateCodeWithCompileTimeErrors, ignoreOption),
|
|
new OptionHandler(Flags.useMultiSourceInfo, passThrough),
|
|
new OptionHandler(Flags.useNewSourceInfo, passThrough),
|
|
new OptionHandler(Flags.testMode, passThrough),
|
|
|
|
// Experimental features.
|
|
// We don't provide documentation for these yet.
|
|
// TODO(29574): provide documentation when this feature is supported.
|
|
// TODO(29574): provide a warning/hint/error, when profile-based data is
|
|
// used without `--fast-startup`.
|
|
new OptionHandler(Flags.experimentalTrackAllocations, passThrough),
|
|
new OptionHandler("${Flags.experimentalAllocationsPath}=.+", passThrough),
|
|
|
|
new OptionHandler(Flags.experimentLocalNames, ignoreOption),
|
|
new OptionHandler(Flags.experimentStartupFunctions, passThrough),
|
|
new OptionHandler(Flags.experimentToBoolean, passThrough),
|
|
new OptionHandler(Flags.experimentCallInstrumentation, passThrough),
|
|
new OptionHandler(Flags.experimentNewRti, passThrough),
|
|
|
|
// The following three options must come last.
|
|
new OptionHandler('-D.+=.*', addInEnvironment),
|
|
new OptionHandler('-.*', (String argument) {
|
|
helpAndFail("Unknown option '$argument'.");
|
|
}),
|
|
new OptionHandler('.*', (String argument) {
|
|
arguments.add(nativeToUriPath(argument));
|
|
})
|
|
];
|
|
|
|
parseCommandLine(handlers, argv);
|
|
|
|
// TODO(johnniwinther): Measure time for reading files.
|
|
SourceFileProvider inputProvider;
|
|
if (bazelPaths != null) {
|
|
inputProvider = new BazelInputProvider(bazelPaths);
|
|
} else {
|
|
inputProvider = new CompilerSourceFileProvider();
|
|
}
|
|
|
|
diagnosticHandler = new FormattingDiagnosticHandler(inputProvider);
|
|
if (verbose != null) {
|
|
diagnosticHandler.verbose = verbose;
|
|
}
|
|
if (throwOnError != null) {
|
|
diagnosticHandler.throwOnError = throwOnError;
|
|
}
|
|
if (throwOnErrorCount != null) {
|
|
diagnosticHandler.throwOnErrorCount = throwOnErrorCount;
|
|
}
|
|
if (showWarnings != null) {
|
|
diagnosticHandler.showWarnings = showWarnings;
|
|
}
|
|
if (showHints != null) {
|
|
diagnosticHandler.showHints = showHints;
|
|
}
|
|
if (enableColors != null) {
|
|
diagnosticHandler.enableColors = enableColors;
|
|
}
|
|
|
|
if (checkedMode && strongMode) {
|
|
checkedMode = false;
|
|
hints.add("Option '${Flags.enableCheckedMode}' is not needed in Dart 2.0. "
|
|
"To enable assertions use '${Flags.enableAsserts}' instead.");
|
|
}
|
|
|
|
if (trustTypeAnnotations && strongMode) {
|
|
hints.add("Option '${Flags.trustTypeAnnotations}' is not available "
|
|
"in Dart 2.0. Try using '${Flags.omitImplicitChecks}' instead.");
|
|
}
|
|
|
|
for (String hint in hints) {
|
|
diagnosticHandler.info(hint, api.Diagnostic.HINT);
|
|
}
|
|
|
|
if (wantHelp || wantVersion) {
|
|
helpAndExit(wantHelp, wantVersion, diagnosticHandler.verbose);
|
|
}
|
|
|
|
if (arguments.isEmpty) {
|
|
helpAndFail('No Dart file specified.');
|
|
}
|
|
if (arguments.length > 1) {
|
|
var extra = arguments.sublist(1);
|
|
helpAndFail('Extra arguments: ${extra.join(" ")}');
|
|
}
|
|
|
|
if (trustTypeAnnotations && checkedMode) {
|
|
helpAndFail("Option '${Flags.trustTypeAnnotations}' may not be used in "
|
|
"checked mode.");
|
|
}
|
|
|
|
if (packageRoot != null && packageConfig != null) {
|
|
helpAndFail("Cannot specify both '--package-root' and '--packages.");
|
|
}
|
|
|
|
String scriptName = arguments[0];
|
|
|
|
switch (writeStrategy) {
|
|
case WriteStrategy.toJs:
|
|
out ??= currentDirectory.resolve('out.js');
|
|
break;
|
|
case WriteStrategy.toKernel:
|
|
out ??= currentDirectory.resolve('out.dill');
|
|
options.add(Flags.cfeOnly);
|
|
if (readStrategy == ReadStrategy.fromData) {
|
|
fail("Cannot use ${Flags.cfeOnly} "
|
|
"and read serialized data simultaneously.");
|
|
} else if (readStrategy == ReadStrategy.fromCodegen) {
|
|
fail("Cannot use ${Flags.cfeOnly} "
|
|
"and read serialized codegen simultaneously.");
|
|
}
|
|
break;
|
|
case WriteStrategy.toData:
|
|
out ??= currentDirectory.resolve('out.dill');
|
|
writeDataUri ??= currentDirectory.resolve('$out.data');
|
|
options.add('${Flags.writeData}=${writeDataUri}');
|
|
if (readStrategy == ReadStrategy.fromData) {
|
|
fail("Cannot read and write serialized data simultaneously.");
|
|
} else if (readStrategy == ReadStrategy.fromCodegen) {
|
|
fail("Cannot read serialized codegen and "
|
|
"write serialized data simultaneously.");
|
|
}
|
|
break;
|
|
case WriteStrategy.toCodegen:
|
|
// TODO(johnniwinther): Avoid the need for an [out] value in this case or
|
|
// use [out] to pass [writeCodegenUri].
|
|
out ??= currentDirectory.resolve('out');
|
|
writeCodegenUri ??= currentDirectory.resolve('$out.code');
|
|
options.add('${Flags.writeCodegen}=${writeCodegenUri}');
|
|
if (readStrategy == ReadStrategy.fromCodegen) {
|
|
fail("Cannot read and write serialized codegen simultaneously.");
|
|
}
|
|
if (readStrategy != ReadStrategy.fromData) {
|
|
fail("Can only write serialized codegen from serialized data.");
|
|
}
|
|
if (codegenShards == null) {
|
|
fail("Cannot write serialized codegen without setting "
|
|
"${Flags.codegenShards}.");
|
|
} else if (codegenShards <= 0) {
|
|
fail("${Flags.codegenShards} must be a positive integer.");
|
|
}
|
|
if (codegenShard == null) {
|
|
fail("Cannot write serialized codegen without setting "
|
|
"${Flags.codegenShard}.");
|
|
} else if (codegenShard < 0 || codegenShard >= codegenShards) {
|
|
fail("${Flags.codegenShard} must be between 0 and "
|
|
"${Flags.codegenShards}.");
|
|
}
|
|
options.add('${Flags.codegenShard}=$codegenShard');
|
|
options.add('${Flags.codegenShards}=$codegenShards');
|
|
break;
|
|
}
|
|
switch (readStrategy) {
|
|
case ReadStrategy.fromDart:
|
|
break;
|
|
case ReadStrategy.fromData:
|
|
readDataUri ??= currentDirectory.resolve('$scriptName.data');
|
|
options.add('${Flags.readData}=${readDataUri}');
|
|
break;
|
|
case ReadStrategy.fromCodegen:
|
|
readDataUri ??= currentDirectory.resolve('$scriptName.data');
|
|
options.add('${Flags.readData}=${readDataUri}');
|
|
readCodegenUri ??= currentDirectory.resolve('$scriptName.code');
|
|
options.add('${Flags.readCodegen}=${readCodegenUri}');
|
|
if (codegenShards == null) {
|
|
fail("Cannot write serialized codegen without setting "
|
|
"${Flags.codegenShards}.");
|
|
} else if (codegenShards <= 0) {
|
|
fail("${Flags.codegenShards} must be a positive integer.");
|
|
}
|
|
options.add('${Flags.codegenShards}=$codegenShards');
|
|
}
|
|
options.add('--out=$out');
|
|
if (writeStrategy == WriteStrategy.toJs) {
|
|
sourceMapOut = Uri.parse('$out.map');
|
|
options.add('--source-map=${sourceMapOut}');
|
|
}
|
|
|
|
RandomAccessFileOutputProvider outputProvider =
|
|
new RandomAccessFileOutputProvider(out, sourceMapOut,
|
|
onInfo: diagnosticHandler.info, onFailure: fail);
|
|
|
|
api.CompilationResult compilationDone(api.CompilationResult result) {
|
|
if (!result.isSuccess) {
|
|
fail('Compilation failed.');
|
|
}
|
|
writeString(
|
|
Uri.parse('$out.deps'), getDepsOutput(inputProvider.getSourceUris()));
|
|
|
|
String input = uriPathToNative(scriptName);
|
|
int inputSize;
|
|
String processName;
|
|
String inputName;
|
|
|
|
int outputSize;
|
|
int primaryOutputSize;
|
|
String outputName;
|
|
|
|
String summary;
|
|
switch (readStrategy) {
|
|
case ReadStrategy.fromDart:
|
|
inputName = 'characters Dart';
|
|
inputSize = inputProvider.dartCharactersRead;
|
|
summary = 'Dart file $input ';
|
|
break;
|
|
case ReadStrategy.fromData:
|
|
inputName = 'bytes data';
|
|
inputSize = inputProvider.dartCharactersRead;
|
|
String dataInput =
|
|
relativize(currentDirectory, readDataUri, Platform.isWindows);
|
|
summary = 'Data files $input and $dataInput ';
|
|
break;
|
|
case ReadStrategy.fromCodegen:
|
|
inputName = 'bytes data';
|
|
inputSize = inputProvider.dartCharactersRead;
|
|
String dataInput =
|
|
relativize(currentDirectory, readDataUri, Platform.isWindows);
|
|
String codeInput =
|
|
relativize(currentDirectory, readCodegenUri, Platform.isWindows);
|
|
summary = 'Data files $input, $dataInput and '
|
|
'${codeInput}[0-${codegenShards - 1}] ';
|
|
break;
|
|
}
|
|
|
|
switch (writeStrategy) {
|
|
case WriteStrategy.toJs:
|
|
processName = 'Compiled';
|
|
outputName = 'characters JavaScript';
|
|
outputSize = outputProvider.totalCharactersWrittenJavaScript;
|
|
primaryOutputSize = outputProvider.totalCharactersWrittenPrimary;
|
|
String output = relativize(currentDirectory, out, Platform.isWindows);
|
|
summary += 'compiled to JavaScript: ${output}';
|
|
break;
|
|
case WriteStrategy.toKernel:
|
|
processName = 'Compiled';
|
|
outputName = 'kernel bytes';
|
|
outputSize = outputProvider.totalDataWritten;
|
|
String output = relativize(currentDirectory, out, Platform.isWindows);
|
|
summary += 'compiled to dill: ${output}.';
|
|
break;
|
|
case WriteStrategy.toData:
|
|
processName = 'Serialized';
|
|
outputName = 'bytes data';
|
|
outputSize = outputProvider.totalDataWritten;
|
|
String output = relativize(currentDirectory, out, Platform.isWindows);
|
|
String dataOutput =
|
|
relativize(currentDirectory, writeDataUri, Platform.isWindows);
|
|
summary += 'serialized to dill and data: ${output} and ${dataOutput}.';
|
|
break;
|
|
case WriteStrategy.toCodegen:
|
|
processName = 'Serialized';
|
|
outputName = 'bytes data';
|
|
outputSize = outputProvider.totalDataWritten;
|
|
String codeOutput =
|
|
relativize(currentDirectory, writeCodegenUri, Platform.isWindows);
|
|
summary += 'serialized to codegen data: '
|
|
'${codeOutput}${codegenShard}.';
|
|
break;
|
|
}
|
|
|
|
print('$processName '
|
|
'${_formatCharacterCount(inputSize)} $inputName to '
|
|
'${_formatCharacterCount(outputSize)} $outputName in '
|
|
'${_formatDurationAsSeconds(wallclock.elapsed)} seconds');
|
|
if (primaryOutputSize != null) {
|
|
diagnosticHandler
|
|
.info('${_formatCharacterCount(primaryOutputSize)} $outputName '
|
|
'in ${relativize(currentDirectory, out, Platform.isWindows)}');
|
|
}
|
|
if (writeStrategy == WriteStrategy.toJs) {
|
|
if (outputSpecified || diagnosticHandler.verbose) {
|
|
print(summary);
|
|
if (diagnosticHandler.verbose) {
|
|
var files = outputProvider.allOutputFiles;
|
|
int jsCount = files.where((f) => f.endsWith('.js')).length;
|
|
print('Emitted file $jsCount JavaScript files.');
|
|
}
|
|
}
|
|
} else {
|
|
print(summary);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
Uri script = currentDirectory.resolve(scriptName);
|
|
|
|
diagnosticHandler.autoReadFileUri = true;
|
|
CompilerOptions compilerOptions = CompilerOptions.parse(options,
|
|
librariesSpecificationUri: librariesSpecificationUri,
|
|
platformBinaries: platformBinaries)
|
|
..entryPoint = script
|
|
..packageRoot = packageRoot
|
|
..packageConfig = packageConfig
|
|
..environment = environment
|
|
..kernelInitializedCompilerState = kernelInitializedCompilerState
|
|
..optimizationLevel = optimizationLevel;
|
|
return compileFunc(
|
|
compilerOptions, inputProvider, diagnosticHandler, outputProvider)
|
|
.then(compilationDone);
|
|
}
|
|
|
|
/// Returns the non-negative integer formatted with a thousands separator.
|
|
String _formatCharacterCount(int value, [String separator = ',']) {
|
|
String text = '$value';
|
|
// 'Insert' separators right-to-left. Inefficient, but used just a few times.
|
|
for (int position = text.length - 3; position > 0; position -= 3) {
|
|
text = text.substring(0, position) + separator + text.substring(position);
|
|
}
|
|
return text;
|
|
}
|
|
|
|
/// Formats [duration] in seconds in fixed-point format, preferring to keep the
|
|
/// result at to below [width] characters.
|
|
String _formatDurationAsSeconds(Duration duration, [int width = 4]) {
|
|
num seconds = duration.inMilliseconds / 1000.0;
|
|
String text;
|
|
for (int digits = 3; digits >= 0; digits--) {
|
|
text = seconds.toStringAsFixed(digits);
|
|
if (text.length <= width) return text;
|
|
}
|
|
return text;
|
|
}
|
|
|
|
class AbortLeg {
|
|
final message;
|
|
AbortLeg(this.message);
|
|
@override
|
|
toString() => 'Aborted due to --throw-on-error: $message';
|
|
}
|
|
|
|
void writeString(Uri uri, String text) {
|
|
if (!enableWriteString) return;
|
|
if (uri.scheme != 'file') {
|
|
fail('Unhandled scheme ${uri.scheme}.');
|
|
}
|
|
var file = new File(uri.toFilePath()).openSync(mode: FileMode.write);
|
|
file.writeStringSync(text);
|
|
file.closeSync();
|
|
}
|
|
|
|
void fail(String message) {
|
|
if (diagnosticHandler != null) {
|
|
diagnosticHandler.report(null, null, -1, -1, message, api.Diagnostic.ERROR);
|
|
} else {
|
|
print('Error: $message');
|
|
}
|
|
exitFunc(1);
|
|
}
|
|
|
|
Future<api.CompilationResult> compilerMain(List<String> arguments,
|
|
{fe.InitializedCompilerState kernelInitializedCompilerState}) async {
|
|
if (!arguments.any((a) => a.startsWith('--libraries-spec='))) {
|
|
Uri script = Platform.script;
|
|
if (script.isScheme("package")) {
|
|
script = await Isolate.resolvePackageUri(script);
|
|
}
|
|
Uri librariesJson = script.resolve(_defaultSpecificationUri);
|
|
arguments = <String>['--libraries-spec=${librariesJson.toFilePath()}']
|
|
..addAll(arguments);
|
|
}
|
|
return compile(arguments,
|
|
kernelInitializedCompilerState: kernelInitializedCompilerState);
|
|
}
|
|
|
|
void help() {
|
|
// This message should be no longer than 20 lines. The default
|
|
// terminal size normally 80x24. Two lines are used for the prompts
|
|
// before and after running the compiler. Another two lines may be
|
|
// used to print an error message.
|
|
print('''
|
|
Usage: dart2js [options] dartfile
|
|
|
|
Compiles Dart to JavaScript.
|
|
|
|
Common options:
|
|
-o <file> Generate the output into <file>.
|
|
-m Generate minified output.
|
|
-h Display this message (add -v for information about all options).''');
|
|
}
|
|
|
|
void verboseHelp() {
|
|
print(r'''
|
|
Usage: dart2js [options] dartfile
|
|
|
|
Compiles Dart to JavaScript.
|
|
|
|
Supported options:
|
|
-h, /h, /?, --help
|
|
Display this message (add -v for information about all options).
|
|
|
|
-o <file>, --out=<file>
|
|
Generate the output into <file>.
|
|
|
|
-m, --minify
|
|
Generate minified output.
|
|
|
|
--enable-asserts
|
|
Enable assertions.
|
|
|
|
-v, --verbose
|
|
Display verbose information.
|
|
|
|
-D<name>=<value>
|
|
Define an environment variable.
|
|
|
|
--version
|
|
Display version information.
|
|
|
|
--packages=<path>
|
|
Path to the package resolution configuration file, which supplies a mapping
|
|
of package names to paths.
|
|
|
|
--suppress-warnings
|
|
Do not display any warnings.
|
|
|
|
--fatal-warnings
|
|
Treat warnings as compilation errors.
|
|
|
|
--suppress-hints
|
|
Do not display any hints.
|
|
|
|
--enable-diagnostic-colors
|
|
Add colors to diagnostic messages.
|
|
|
|
--terse
|
|
Emit diagnostics without suggestions for how to get rid of the diagnosed
|
|
problems.
|
|
|
|
--show-package-warnings
|
|
Show warnings and hints generated from packages.
|
|
|
|
--csp
|
|
Disable dynamic generation of code in the generated output. This is
|
|
necessary to satisfy CSP restrictions (see http://www.w3.org/TR/CSP/).
|
|
|
|
--no-source-maps
|
|
Do not generate a source map file.
|
|
|
|
-O<0,1,2,3,4>
|
|
Controls optimizations that can help reduce code-size and improve
|
|
performance of the generated code for deployment.
|
|
|
|
-O0
|
|
Disables all optimizations. Equivalent to calling dart2js with these
|
|
extra flags:
|
|
--disable-inlining
|
|
--disable-type-inference
|
|
--disable-rti-optimizations
|
|
|
|
|
|
Some optimizations cannot be dissabled at this time, as we add the option
|
|
to disable them, they will be added here as well.
|
|
|
|
-O1
|
|
Enables default optimizations. Equivalent to calling dart2js with no
|
|
extra flags.
|
|
|
|
-O2
|
|
Enables optimizations that respect the language semantics and are safe
|
|
for all programs. It however changes the string representation of types,
|
|
which will no longer be consistent with the Dart VM or DDC.
|
|
|
|
Equivalent to calling dart2js with these extra flags:
|
|
--minify
|
|
--lax-runtime-type-to-string
|
|
|
|
-O3
|
|
Enables optimizations that respect the language semantics only on
|
|
programs that don't ever throw any subtype of `Error`. These
|
|
optimizations improve the generated code, but they may cause programs to
|
|
behave unexpectedly if this assumption is not met. To use this
|
|
option, we recommend that you properly test your application first
|
|
without it, and ensure that no subtype of `Error` (such as `TypeError`)
|
|
is ever thrown.
|
|
|
|
Equivalent to calling dart2js with these extra flags:
|
|
-O2
|
|
--omit-implicit-checks
|
|
|
|
-O4
|
|
Enables more aggressive optimizations than -O3, but with the same
|
|
assumptions. These optimizations are on a separate group because they
|
|
are more susceptible to variations in input data. To use this option we
|
|
recommend to pay special attention to test edge cases in user input.
|
|
|
|
Equivalent to calling dart2js with these extra flags:
|
|
-O3
|
|
--trust-primitives
|
|
|
|
While some of the individual optimizations and flags may change with time,
|
|
we intend to keep the -O* flags stable. New safe optimizations may be added
|
|
on any level, and optimizations that only work on some programs may move up
|
|
from one level to the next (for instance, once alternative safe
|
|
optimizations are implemented, `omit-implicit-checks` may be removed or may
|
|
move to the O4 level).
|
|
|
|
The following individual options are included in some of the -O optimization
|
|
levels above. They help reduce the size of the generated code, but they may
|
|
cause programs to behave unexpectedly if assumptions are not met. Only turn on
|
|
these flags if you have enough test coverage to ensure they are safe to use:
|
|
|
|
--omit-implicit-checks
|
|
Omit implicit runtime checks, such as parameter checks and implicit
|
|
downcasts. These checks are included by default in Dart 2.0. By
|
|
using this flag the checks are removed, however the compiler will assume
|
|
that all such checks were valid and may use this information for
|
|
optimizations. Use this option only if you have enough testing to ensure
|
|
that your program works with the checks.
|
|
|
|
--trust-primitives
|
|
Assume that operations on numbers, strings, and lists have valid inputs.
|
|
This option allows the compiler to drop runtime checks for those operations.
|
|
Note: a well-typed program is not guaranteed to have valid inputs. For
|
|
example, an int index argument may be null or out of range.
|
|
|
|
--lax-runtime-type-to-string
|
|
Omits reified class type arguments when these are only needed for `toString`
|
|
on `runtimeType`. This is useful if `runtimeType.toString()` is only used
|
|
for debugging. Note that semantics of other uses of `.runtimeType`, for
|
|
instance `a.runtimeType == b.runtimeType`, is not affected by this flag.
|
|
|
|
The following options are only used for compiler development and may
|
|
be removed in a future version:
|
|
|
|
--throw-on-error
|
|
Throw an exception if a compile-time error is detected.
|
|
|
|
--libraries-spec=<file>
|
|
A .json file containing the libraries specification for dart2js.
|
|
|
|
--allow-mock-compilation
|
|
Do not generate a call to main if either of the following
|
|
libraries are used: dart:dom, dart:html dart:io.
|
|
|
|
--disable-native-live-type-analysis
|
|
Disable the optimization that removes unused native types from dart:html
|
|
and related libraries.
|
|
|
|
--server-mode
|
|
Compile with server support. The compiler will use a library specification
|
|
that disables dart:html but supports dart:js in conditional imports.
|
|
|
|
--categories=<categories>
|
|
(deprecated)
|
|
Use '--server-mode' instead of '--categories=Server'. All other category
|
|
values have no effect on the compiler behavior.
|
|
|
|
--deferred-map=<file>
|
|
Generates a json file with a mapping from each deferred import to a list of
|
|
the part.js files that will be loaded.
|
|
|
|
--dump-info[=<format>]
|
|
Generates information about the generated code. 'format' can be either
|
|
'json' or 'binary'.
|
|
You can inspect the generated data using tools from 'package:dart2js_info'.
|
|
|
|
--generate-code-with-compile-time-errors
|
|
Generates output even if the program contains compile-time errors. Use the
|
|
exit code to determine if compilation failed.
|
|
|
|
--no-frequency-based-minification
|
|
Experimental. Disabled the new frequency based minifying namer and use the
|
|
old namer instead.
|
|
'''
|
|
.trim());
|
|
}
|
|
|
|
void helpAndExit(bool wantHelp, bool wantVersion, bool verbose) {
|
|
if (wantVersion) {
|
|
var version = (BUILD_ID == null) ? '<non-SDK build>' : BUILD_ID;
|
|
print('Dart-to-JavaScript compiler (dart2js) version: $version');
|
|
}
|
|
if (wantHelp) {
|
|
if (verbose) {
|
|
verboseHelp();
|
|
} else {
|
|
help();
|
|
}
|
|
}
|
|
exitFunc(0);
|
|
}
|
|
|
|
void helpAndFail(String message) {
|
|
help();
|
|
print('');
|
|
fail(message);
|
|
}
|
|
|
|
void main(List<String> arguments) {
|
|
// Since the sdk/bin/dart2js script adds its own arguments in front of
|
|
// user-supplied arguments we search for '--batch' at the end of the list.
|
|
if (arguments.length > 0 && arguments.last == "--batch") {
|
|
batchMain(arguments.sublist(0, arguments.length - 1));
|
|
return;
|
|
}
|
|
internalMain(arguments);
|
|
}
|
|
|
|
typedef void ExitFunc(int exitCode);
|
|
typedef Future<api.CompilationResult> CompileFunc(
|
|
CompilerOptions compilerOptions,
|
|
api.CompilerInput compilerInput,
|
|
api.CompilerDiagnostics compilerDiagnostics,
|
|
api.CompilerOutput compilerOutput);
|
|
|
|
ExitFunc exitFunc = exit;
|
|
CompileFunc compileFunc = api.compile;
|
|
|
|
/// If `true` a '.deps' file will be generated after compilation.
|
|
///
|
|
/// Set this to `false` in end-to-end tests to avoid generating '.deps' files.
|
|
bool enableWriteString = true;
|
|
|
|
Future<api.CompilationResult> internalMain(List<String> arguments,
|
|
{fe.InitializedCompilerState kernelInitializedCompilerState}) {
|
|
Future<api.CompilationResult> onError(exception, trace) {
|
|
// If we are already trying to exit, just continue exiting.
|
|
if (exception == _EXIT_SIGNAL) throw exception;
|
|
|
|
try {
|
|
print('The compiler crashed: $exception');
|
|
} catch (ignored) {
|
|
print('The compiler crashed: error while printing exception');
|
|
}
|
|
|
|
try {
|
|
if (trace != null) {
|
|
print(trace);
|
|
}
|
|
} finally {
|
|
exitFunc(253); // 253 is recognized as a crash by our test scripts.
|
|
}
|
|
return new Future.error(exception, trace);
|
|
}
|
|
|
|
try {
|
|
return compilerMain(arguments,
|
|
kernelInitializedCompilerState: kernelInitializedCompilerState)
|
|
.catchError(onError);
|
|
} catch (exception, trace) {
|
|
return onError(exception, trace);
|
|
}
|
|
}
|
|
|
|
class _ExitSignal {
|
|
const _ExitSignal();
|
|
}
|
|
|
|
const _EXIT_SIGNAL = const _ExitSignal();
|
|
|
|
void batchMain(List<String> batchArguments) {
|
|
int exitCode;
|
|
exitFunc = (errorCode) {
|
|
// Since we only throw another part of the compiler might intercept our
|
|
// exception and try to exit with a different code.
|
|
if (exitCode == 0) {
|
|
exitCode = errorCode;
|
|
}
|
|
throw _EXIT_SIGNAL;
|
|
};
|
|
|
|
var stream = stdin.transform(utf8.decoder).transform(new LineSplitter());
|
|
var subscription;
|
|
fe.InitializedCompilerState kernelInitializedCompilerState;
|
|
subscription = stream.listen((line) {
|
|
new Future.sync(() {
|
|
subscription.pause();
|
|
exitCode = 0;
|
|
if (line == null) exit(0);
|
|
List<String> args = <String>[];
|
|
args.addAll(batchArguments);
|
|
args.addAll(splitLine(line, windows: Platform.isWindows));
|
|
return internalMain(args,
|
|
kernelInitializedCompilerState: kernelInitializedCompilerState);
|
|
}).catchError((exception, trace) {
|
|
if (!identical(exception, _EXIT_SIGNAL)) {
|
|
exitCode = 253;
|
|
}
|
|
}).then((api.CompilationResult result) {
|
|
if (result != null) {
|
|
kernelInitializedCompilerState = result.kernelInitializedCompilerState;
|
|
}
|
|
}).whenComplete(() {
|
|
// The testing framework waits for a status line on stdout and
|
|
// stderr before moving to the next test.
|
|
if (exitCode == 0) {
|
|
print(">>> TEST OK");
|
|
} else if (exitCode == 253) {
|
|
print(">>> TEST CRASH");
|
|
} else {
|
|
print(">>> TEST FAIL");
|
|
}
|
|
stderr.writeln(">>> EOF STDERR");
|
|
subscription.resume();
|
|
});
|
|
});
|
|
}
|
|
|
|
enum ReadStrategy { fromDart, fromData, fromCodegen }
|
|
enum WriteStrategy { toKernel, toData, toCodegen, toJs }
|