blob: 2fa6522a98f0432e014bf6cbb8bd0d37f59a1dfb [file] [log] [blame]
// 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 'package:front_end/src/api_unstable/dart2js.dart' as fe;
import 'package:front_end/src/compute_platform_binaries_location.dart'
show computePlatformBinariesLocation;
import 'package:package_config/discovery.dart' show findPackages;
import '../compiler_new.dart' as api;
import 'commandline_options.dart';
import 'common/names.dart' show Uris;
import 'filenames.dart';
import 'io/source_file.dart';
import 'null_compiler_output.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 LIBRARY_ROOT = '../../../../../sdk';
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-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 libraryRoot = currentDirectory;
Uri out = currentDirectory.resolve('out.js');
Uri sourceMapOut = currentDirectory.resolve('out.js.map');
List<Uri> resolutionInputs;
List<String> bazelPaths;
Uri packageConfig = null;
Uri packageRoot = null;
List<String> options = new List<String>();
List<String> explicitOutputArguments = <String>[];
bool wantHelp = false;
bool wantVersion = false;
bool analyzeOnly = false;
bool analyzeAll = false;
bool resolveOnly = false;
Uri resolutionOutput = currentDirectory.resolve('out.data');
bool allowNativeExtensions = false;
bool trustTypeAnnotations = false;
bool checkedMode = false;
List<String> hints = <String>[];
bool verbose;
bool throwOnError;
int throwOnErrorCount;
bool showWarnings;
bool showHints;
bool enableColors;
bool useKernel = false;
Uri platformBinaries = computePlatformBinariesLocation();
// List of provided options that imply that output is expected.
List<String> optionsImplyCompilation = <String>[];
bool hasDisallowUnsafeEval = false;
Map<String, dynamic> environment = new Map<String, dynamic>();
void passThrough(String argument) => options.add(argument);
void ignoreOption(String argument) {}
if (BUILD_ID != null) {
passThrough("--build-id=$BUILD_ID");
}
void setLibraryRoot(String argument) {
libraryRoot = currentDirectory.resolve(extractPath(argument));
}
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) {
explicitOutputArguments.add(arguments.current);
String path;
if (arguments.current == '-o') {
if (!arguments.moveNext()) {
helpAndFail('Error: Missing file after -o option.');
}
explicitOutputArguments.add(arguments.current);
path = arguments.current;
} else {
path = extractParameter(arguments.current);
}
resolutionOutput = out = currentDirectory.resolve(nativeToUriPath(path));
sourceMapOut = Uri.parse('$out.map');
}
void setOutputType(String argument) {
optionsImplyCompilation.add(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.");
}
}
void setResolutionInput(String argument) {
resolutionInputs = <Uri>[];
String parts = extractParameter(argument);
for (String part in parts.split(',')) {
resolutionInputs.add(currentDirectory.resolve(nativeToUriPath(part)));
}
}
void setBazelPaths(String argument) {
String paths = extractParameter(argument);
bazelPaths = <String>[]..addAll(paths.split(','));
}
void setResolveOnly(String argument) {
resolveOnly = true;
passThrough(argument);
}
String getDepsOutput(Iterable<Uri> sourceFiles) {
var filenames = sourceFiles.map((uri) => '$uri').toList();
filenames.sort();
return filenames.join("\n");
}
implyCompilation(String argument) {
optionsImplyCompilation.add(argument);
passThrough(argument);
}
setStrip(String argument) {
helpAndFail("Option '--force-strip' is not in use now that"
"--output-type=dart is no longer supported.");
}
void setAnalyzeOnly(String argument) {
analyzeOnly = true;
passThrough(argument);
}
void setAnalyzeAll(String argument) {
analyzeAll = true;
passThrough(argument);
}
void setAllowNativeExtensions(String argument) {
allowNativeExtensions = true;
passThrough(argument);
}
void setVerbose(_) {
verbose = true;
passThrough('--verbose');
}
void setTrustTypeAnnotations(String argument) {
trustTypeAnnotations = true;
implyCompilation(argument);
}
void setTrustJSInteropTypeAnnotations(String argument) {
implyCompilation(argument);
}
void setTrustPrimitives(String argument) {
implyCompilation(argument);
}
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(',');
if (categories.contains('all')) {
categories = ["Client", "Server"];
} else {
for (String category in categories) {
if (!["Client", "Server"].contains(category)) {
fail('Unsupported library category "$category", '
'supported categories are: Client, Server, all');
}
}
}
passThrough('--categories=${categories.join(",")}');
}
void setUseKernel(String argument) {
useKernel = true;
passThrough(argument);
}
void setPlatformBinaries(String argument) {
platformBinaries =
currentDirectory.resolve(extractPath(argument, isDirectory: true));
}
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':
implyCompilation(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, (_) {
showWarnings = false;
passThrough(Flags.suppressWarnings);
}),
new OptionHandler(Flags.fatalWarnings, passThrough),
new OptionHandler(Flags.suppressHints, (_) {
showHints = false;
}),
// TODO(sigmund): remove entirely after Dart 1.20
new OptionHandler(
'--output-type=dart|--output-type=dart-multi|--output-type=js',
setOutputType),
new OptionHandler(Flags.useKernel, setUseKernel),
new OptionHandler(Flags.platformBinaries, setPlatformBinaries),
new OptionHandler(Flags.noFrequencyBasedMinification, passThrough),
new OptionHandler(Flags.verbose, setVerbose),
new OptionHandler(Flags.version, (_) => wantVersion = true),
new OptionHandler('--library-root=.+', setLibraryRoot),
new OptionHandler('--out=.+|-o.*', setOutput, multipleArguments: true),
new OptionHandler(Flags.allowMockCompilation, passThrough),
new OptionHandler(Flags.fastStartup, passThrough),
new OptionHandler(Flags.genericMethodSyntax, ignoreOption),
new OptionHandler(Flags.initializingFormalAccess, ignoreOption),
new OptionHandler('${Flags.minify}|-m', implyCompilation),
new OptionHandler(Flags.preserveUris, passThrough),
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(Flags.trustTypeAnnotations)),
new OptionHandler(Flags.trustPrimitives,
(_) => setTrustPrimitives(Flags.trustPrimitives)),
new OptionHandler(
Flags.trustJSInteropTypeAnnotations,
(_) => setTrustJSInteropTypeAnnotations(
Flags.trustJSInteropTypeAnnotations)),
new OptionHandler(r'--help|/\?|/h', (_) => wantHelp = true),
new OptionHandler('--packages=.+', setPackageConfig),
new OptionHandler('--package-root=.+|-p.+', setPackageRoot),
new OptionHandler(Flags.analyzeAll, setAnalyzeAll),
new OptionHandler(Flags.analyzeOnly, setAnalyzeOnly),
new OptionHandler(Flags.noSourceMaps, passThrough),
new OptionHandler(Option.resolutionInput, setResolutionInput),
new OptionHandler(Option.bazelPaths, setBazelPaths),
new OptionHandler(Flags.resolveOnly, setResolveOnly),
new OptionHandler(Flags.analyzeSignaturesOnly, setAnalyzeOnly),
new OptionHandler(Flags.disableNativeLiveTypeAnalysis, passThrough),
new OptionHandler('--categories=.*', setCategories),
new OptionHandler(Flags.disableInlining, implyCompilation),
new OptionHandler(Flags.disableTypeInference, implyCompilation),
new OptionHandler(Flags.terse, passThrough),
new OptionHandler('--deferred-map=.+', implyCompilation),
new OptionHandler(Flags.dumpInfo, implyCompilation),
new OptionHandler(
'--disallow-unsafe-eval', (_) => hasDisallowUnsafeEval = true),
new OptionHandler(Option.showPackageWarnings, passThrough),
new OptionHandler(Flags.useContentSecurityPolicy, passThrough),
new OptionHandler(Flags.enableExperimentalMirrors, passThrough),
new OptionHandler(Flags.enableAssertMessage, 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, (_) {}),
new OptionHandler('--enable-async', (_) {
hints.add("Option '--enable-async' is no longer needed. "
"Async-await is supported by default.");
}),
new OptionHandler('--enable-null-aware-operators', (_) {
hints.add("Option '--enable-null-aware-operators' is no longer needed. "
"Null aware operators are supported by default.");
}),
new OptionHandler('--enable-enum', (_) {
hints.add("Option '--enable-enum' is no longer needed. "
"Enums are supported by default.");
}),
new OptionHandler(Flags.allowNativeExtensions, setAllowNativeExtensions),
new OptionHandler(Flags.generateCodeWithCompileTimeErrors, passThrough),
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),
// 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;
}
for (String hint in hints) {
diagnosticHandler.info(hint, api.Diagnostic.HINT);
}
if (wantHelp || wantVersion) {
helpAndExit(wantHelp, wantVersion, diagnosticHandler.verbose);
}
if (hasDisallowUnsafeEval) {
String precompiledName = relativize(
currentDirectory,
RandomAccessFileOutputProvider.computePrecompiledUri(out),
Platform.isWindows);
helpAndFail("Option '--disallow-unsafe-eval' has been removed."
" Instead, the compiler generates a file named"
" '$precompiledName'.");
}
if (arguments.isEmpty) {
helpAndFail('No Dart file specified.');
}
if (arguments.length > 1) {
var extra = arguments.sublist(1);
helpAndFail('Extra arguments: ${extra.join(" ")}');
}
if (checkedMode && trustTypeAnnotations) {
helpAndFail("Option '${Flags.trustTypeAnnotations}' may not be used in "
"checked mode.");
}
if (packageRoot != null && packageConfig != null) {
helpAndFail("Cannot specify both '--package-root' and '--packages.");
}
List<String> optionsImplyOutput = <String>[]
..addAll(optionsImplyCompilation)
..addAll(explicitOutputArguments);
if (resolveOnly && !optionsImplyCompilation.isEmpty) {
diagnosticHandler.info(
"Options $optionsImplyCompilation indicate that compilation is "
"expected, but compilation is turned off by the option "
"'${Flags.resolveOnly}'.",
api.Diagnostic.INFO);
} else if ((analyzeOnly || analyzeAll) && !optionsImplyOutput.isEmpty) {
if (analyzeAll && !analyzeOnly) {
diagnosticHandler.info(
"Option '${Flags.analyzeAll}' implies '${Flags.analyzeOnly}'.",
api.Diagnostic.INFO);
}
diagnosticHandler.info(
"Options $optionsImplyOutput indicate that output is expected, "
"but compilation is turned off by the option '${Flags.analyzeOnly}'.",
api.Diagnostic.INFO);
}
if (resolveOnly) {
if (resolutionInputs != null &&
resolutionInputs.contains(resolutionOutput)) {
helpAndFail("Resolution input '${resolutionOutput}' can't be used as "
"resolution output. Use the '--out' option to specify another "
"resolution output.");
}
analyzeOnly = analyzeAll = true;
} else if (analyzeAll) {
analyzeOnly = true;
}
if (!analyzeOnly) {
if (allowNativeExtensions) {
helpAndFail("Option '${Flags.allowNativeExtensions}' is only supported "
"in combination with the '${Flags.analyzeOnly}' option.");
}
}
options.add('--out=$out');
options.add('--source-map=$sourceMapOut');
RandomAccessFileOutputProvider outputProvider =
new RandomAccessFileOutputProvider(out, sourceMapOut,
onInfo: diagnosticHandler.info,
onFailure: fail,
resolutionOutput: resolveOnly ? resolutionOutput : null);
api.CompilationResult compilationDone(api.CompilationResult result) {
if (analyzeOnly) return result;
if (!result.isSuccess) {
fail('Compilation failed.');
}
writeString(
Uri.parse('$out.deps'), getDepsOutput(inputProvider.getSourceUris()));
int dartCharactersRead = inputProvider.dartCharactersRead;
int jsCharactersWritten = outputProvider.totalCharactersWrittenJavaScript;
int jsCharactersPrimary = outputProvider.totalCharactersWrittenPrimary;
print('Compiled '
'${_formatCharacterCount(dartCharactersRead)} characters Dart'
' to '
'${_formatCharacterCount(jsCharactersWritten)} characters JavaScript'
' in '
'${_formatDurationAsSeconds(wallclock.elapsed)} seconds');
diagnosticHandler.info(
'${_formatCharacterCount(jsCharactersPrimary)} characters JavaScript'
' in '
'${relativize(currentDirectory, out, Platform.isWindows)}');
if (diagnosticHandler.verbose) {
String input = uriPathToNative(arguments[0]);
print('Dart file ($input) compiled to JavaScript.');
print('Wrote the following files:');
for (String filename in outputProvider.allOutputFiles) {
print(" $filename");
}
} else if (explicitOutputArguments.isNotEmpty) {
String input = uriPathToNative(arguments[0]);
String output = relativize(currentDirectory, out, Platform.isWindows);
print('Dart file ($input) compiled to JavaScript: $output');
}
return result;
}
Uri script = currentDirectory.resolve(arguments[0]);
if (useKernel) {
diagnosticHandler.autoReadFileUri = true;
}
CompilerOptions compilerOptions = new CompilerOptions.parse(
entryPoint: script,
libraryRoot: libraryRoot,
packageRoot: packageRoot,
packageConfig: packageConfig,
platformBinaries: platformBinaries,
packagesDiscoveryProvider: findPackages,
resolutionInputs: resolutionInputs,
resolutionOutput: resolveOnly ? resolutionOutput : null,
options: options,
environment: environment)
..kernelInitializedCompilerState = kernelInitializedCompilerState;
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);
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}) {
var root = uriPathToNative("/$LIBRARY_ROOT");
arguments = <String>['--library-root=${Platform.script.toFilePath()}$root']
..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>.
-c Insert runtime type checks and enable assertions (checked mode).
-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:
-o <file>, --out=<file>
Generate the output into <file>.
-c, --enable-checked-mode, --checked
Insert runtime type checks and enable assertions (checked mode).
-m, --minify
Generate minified output.
-h, /h, /?, --help
Display this message (add -v for information about all options).
-v, --verbose
Display verbose information.
-D<name>=<value>
Define an environment variable.
--version
Display version information.
-p<path>, --package-root=<path>
Where to find packages, that is, "package:..." imports. This option cannot
be used with --packages.
--packages=<path>
Path to the package resolution configuration file, which supplies a mapping
of package names to paths. This option cannot be used with --package-root.
--analyze-all
Analyze all code. Without this option, the compiler only analyzes
code that is reachable from [main]. This option implies --analyze-only.
--analyze-only
Analyze but do not generate code.
--analyze-signatures-only
Skip analysis of method bodies and field initializers. This option implies
--analyze-only.
--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.
--preserve-uris
Preserve the source URIs in the reflection data. Without this flag the
`uri` getter for `LibraryMirror`s is mangled in minified mode.
--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.
--fast-startup
Produce JavaScript that can be parsed more quickly by VMs. This option
usually results in larger JavaScript files with faster startup.
Note: the dart:mirrors library is not supported with this option.
The following advanced options can 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:
--trust-type-annotations
Assume that all types are correct. This option allows the compiler to drop
type checks and to rely on local type information for optimizations. Use
this option only if you have enough testing to ensure that your program
works in strong mode or checked mode.
--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.
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.
--library-root=<directory>
Where to find the Dart platform libraries.
--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.
--categories=<categories>
A comma separated list of allowed library categories. The default
is "Client". Possible categories can be seen by providing an
unsupported category, for example, --categories=help. To enable
all categories, use --categories=all.
--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
Generates an out.info.json file with information about the generated code.
You can inspect the generated file with the viewer at:
https://dart-lang.github.io/dump-info-visualizer/
--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 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;
};
if (USE_SERIALIZED_DART_CORE) {
_useSerializedDataForDartCore(compileFunc);
}
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();
});
});
}
// TODO(johnniwinther): Add corresponding options to the test script and change
// these to use 'bool.fromEnvironment'.
final bool USE_SERIALIZED_DART_CORE =
Platform.environment['USE_SERIALIZED_DART_CORE'] == 'true';
final bool SERIALIZED_COMPILATION =
Platform.environment['SERIALIZED_COMPILATION'] == 'true';
/// Mock URI used only in testing when [USE_SERIALIZED_DART_CORE] or
/// [SERIALIZED_COMPILATION] is enabled.
final Uri _SERIALIZED_DART_CORE_URI = Uri.parse('file:core.data');
final Uri _SERIALIZED_TEST_URI = Uri.parse('file:test.data');
void _useSerializedDataForDartCore(CompileFunc oldCompileFunc) {
/// Run the [oldCompileFunc] with [serializedData] added as resolution input.
Future<api.CompilationResult> compileWithSerializedData(
CompilerOptions compilerOptions,
api.CompilerInput compilerInput,
api.CompilerDiagnostics compilerDiagnostics,
api.CompilerOutput compilerOutput,
List<_SerializedData> serializedData,
{bool compileOnly: false}) {
api.CompilerInput input = compilerInput;
CompilerOptions options = compilerOptions;
if (serializedData != null && serializedData.isNotEmpty) {
Map<Uri, String> dataMap = <Uri, String>{};
for (_SerializedData data in serializedData) {
dataMap[data.uri] = data.data;
}
input = new _CompilerInput(input, dataMap);
List<Uri> resolutionInputs = dataMap.keys.toList();
if (compilerOptions.resolutionInputs != null) {
for (Uri uri in compilerOptions.resolutionInputs) {
if (!dataMap.containsKey(uri)) {
resolutionInputs.add(uri);
}
}
}
options = CompilerOptions.copy(options,
resolutionInputs: resolutionInputs, compileOnly: compileOnly);
}
return oldCompileFunc(options, input, compilerDiagnostics, compilerOutput);
}
/// Serialize [entryPoint] using [serializedData] if provided.
Future<api.CompilationResult> serialize(
Uri entryPoint,
Uri serializedUri,
CompilerOptions compilerOptions,
api.CompilerInput compilerInput,
api.CompilerDiagnostics compilerDiagnostics,
api.CompilerOutput compilerOutput,
[List<_SerializedData> serializedData]) {
CompilerOptions options = CompilerOptions.copy(compilerOptions,
entryPoint: entryPoint,
resolutionOutput: serializedUri,
analyzeAll: true,
analyzeOnly: true,
resolveOnly: true);
return compileWithSerializedData(options, compilerInput,
compilerDiagnostics, compilerOutput, serializedData);
}
// Local cache for the serialized data for dart:core.
_SerializedData serializedDartCore;
/// Serialize the entry point using serialized data from dart:core and run
/// [oldCompileFunc] using serialized data for whole program.
Future<api.CompilationResult> compileFromSerializedData(
CompilerOptions compilerOptions,
api.CompilerInput compilerInput,
api.CompilerDiagnostics compilerDiagnostics,
api.CompilerOutput compilerOutput) async {
_CompilerOutput output = new _CompilerOutput(_SERIALIZED_TEST_URI);
api.CompilationResult result = await serialize(
compilerOptions.entryPoint,
output.uri,
compilerOptions,
compilerInput,
compilerDiagnostics,
output,
[serializedDartCore]);
if (!result.isSuccess) {
return result;
}
return compileWithSerializedData(
compilerOptions,
compilerInput,
compilerDiagnostics,
compilerOutput,
[serializedDartCore, output.serializedData],
compileOnly: true);
}
/// Compiles the entry point using the serialized data from dart:core.
Future<api.CompilationResult> compileWithSerializedDartCoreData(
CompilerOptions compilerOptions,
api.CompilerInput compilerInput,
api.CompilerDiagnostics compilerDiagnostics,
api.CompilerOutput compilerOutput) async {
return compileWithSerializedData(compilerOptions, compilerInput,
compilerDiagnostics, compilerOutput, [serializedDartCore]);
}
/// Serialize dart:core data into [serializedDartCore] and setup the
/// [compileFunc] to run the compiler using this data.
Future<api.CompilationResult> generateSerializedDataForDartCore(
CompilerOptions compilerOptions,
api.CompilerInput compilerInput,
api.CompilerDiagnostics compilerDiagnostics,
api.CompilerOutput compilerOutput) async {
_CompilerOutput output = new _CompilerOutput(_SERIALIZED_DART_CORE_URI);
await serialize(Uris.dart_core, output.uri, compilerOptions, compilerInput,
compilerDiagnostics, output);
serializedDartCore = output.serializedData;
if (SERIALIZED_COMPILATION) {
compileFunc = compileFromSerializedData;
} else {
compileFunc = compileWithSerializedDartCoreData;
}
return compileFunc(
compilerOptions, compilerInput, compilerDiagnostics, compilerOutput);
}
compileFunc = generateSerializedDataForDartCore;
}
class _CompilerInput implements api.CompilerInput {
final api.CompilerInput _input;
final Map<Uri, String> _data;
_CompilerInput(this._input, this._data);
@override
Future<api.Input> readFromUri(Uri uri,
{api.InputKind inputKind: api.InputKind.utf8}) {
String data = _data[uri];
if (data != null) {
return new Future.value(new StringSourceFile.fromUri(uri, data));
}
return _input.readFromUri(uri, inputKind: inputKind);
}
}
class _SerializedData {
final Uri uri;
final String data;
_SerializedData(this.uri, this.data);
}
class _CompilerOutput extends NullCompilerOutput {
final Uri uri;
_BufferedOutputSink sink;
_CompilerOutput(this.uri);
@override
api.OutputSink createOutputSink(
String name, String extension, api.OutputType type) {
if (name == '' && extension == 'data') {
return sink = new _BufferedOutputSink();
}
return super.createOutputSink(name, extension, type);
}
_SerializedData get serializedData {
return new _SerializedData(uri, sink.sb.toString());
}
}
class _BufferedOutputSink implements api.OutputSink {
StringBuffer sb = new StringBuffer();
@override
void add(String event) {
sb.write(event);
}
@override
void close() {
// Do nothing.
}
}