blob: 07d8e1fe7d78aa45f0aa3aeecf94dd0d2f349caa [file] [log] [blame]
// Copyright (c) 2012, 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;
import 'dart:async';
import 'dart:collection' show Queue, LinkedHashMap;
import 'dart:io';
import 'dart:uri';
import 'dart:utf';
import '../compiler.dart' as api;
import 'source_file.dart';
import 'source_file_provider.dart';
import 'filenames.dart';
import 'util/uri_extras.dart';
import '../../libraries.dart';
const String LIBRARY_ROOT = '../../../../..';
const String OUTPUT_LANGUAGE_DART = 'Dart';
typedef void HandleOption(String option);
class OptionHandler {
String pattern;
HandleOption handle;
OptionHandler(this.pattern, this.handle);
* 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) {
// 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) helpAndFail('Error: Unknown option "$argument".');
return m[2];
String extractPath(String argument) {
String path = nativeToUriPath(extractParameter(argument));
return path.endsWith("/") ? 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) {
var pattern = new RegExp('^(${patterns.join(")\$|(")})\$');
OUTER: for (String argument in argv) {
Match match = pattern.firstMatch(argument);
assert(match.groupCount == handlers.length);
for (int i = 0; i < handlers.length; i++) {
if (match[i + 1] != null) {
continue OUTER;
throw 'Internal error: "$argument" did not match';
void compile(List<String> argv) {
bool isWindows = (Platform.operatingSystem == 'windows');
Uri cwd = getCurrentDirectory();
Uri libraryRoot = cwd;
Uri out = cwd.resolve('out.js');
Uri sourceMapOut = cwd.resolve('');
Uri packageRoot = null;
List<String> options = new List<String>();
bool explicitOut = false;
bool wantHelp = false;
String outputLanguage = 'JavaScript';
bool stripArgumentSet = false;
bool analyzeOnly = false;
SourceFileProvider inputProvider = new SourceFileProvider();
FormattingDiagnosticHandler diagnosticHandler =
new FormattingDiagnosticHandler(inputProvider);
passThrough(String argument) => options.add(argument);
setLibraryRoot(String argument) {
libraryRoot = cwd.resolve(extractPath(argument));
setPackageRoot(String argument) {
packageRoot = cwd.resolve(extractPath(argument));
setOutput(String argument) {
explicitOut = true;
out = cwd.resolve(nativeToUriPath(extractParameter(argument)));
sourceMapOut = Uri.parse('$');
setOutputType(String argument) {
if (argument == '--output-type=dart') {
outputLanguage = OUTPUT_LANGUAGE_DART;
if (!explicitOut) {
out = cwd.resolve('out.dart');
sourceMapOut = cwd.resolve('');
String getDepsOutput(Map<String, SourceFile> sourceFiles) {
var filenames = new List.from(sourceFiles.keys);
return filenames.join("\n");
setStrip(String argument) {
stripArgumentSet = true;
setAnalyzeOnly(String argument) {
analyzeOnly = true;
setCategories(String argument) {
List<String> categories = extractParameter(argument).split(',');
Set<String> allowedCategories = => x.category).toSet();
List<String> allowedCategoriesList =
new List<String>.from(allowedCategories);
if (categories.contains('all')) {
categories = allowedCategoriesList;
} else {
String allowedCategoriesString = allowedCategoriesList.join(', ');
for (String category in categories) {
if (!allowedCategories.contains(category)) {
fail('Error: unsupported library category "$category", '
'supported categories are: $allowedCategoriesString');
return passThrough('--categories=${categories.join(",")}');
// TODO(8522): Remove this method once option is restored.
complainAboutDisallowUnsafeEval(String argument) {
fail('Error: $argument is currently not supported, '
handleShortOptions(String argument) {
var shortOptions = argument.substring(1).split("");
for (var shortOption in shortOptions) {
switch (shortOption) {
case 'v':
diagnosticHandler.verbose = true;
case 'h':
case '?':
wantHelp = true;
case 'c':
throw 'Internal error: "$shortOption" did not match';
List<String> arguments = <String>[];
List<OptionHandler> handlers = <OptionHandler>[
new OptionHandler('-[chv?]+', handleShortOptions),
new OptionHandler('--throw-on-error',
(_) => diagnosticHandler.throwOnError = true),
new OptionHandler('--suppress-warnings',
(_) => diagnosticHandler.showWarnings = false),
new OptionHandler('--output-type=dart|--output-type=js', setOutputType),
new OptionHandler('--verbose', (_) => diagnosticHandler.verbose = true),
new OptionHandler('--library-root=.+', setLibraryRoot),
new OptionHandler('--out=.+|-o.+', setOutput),
new OptionHandler('--allow-mock-compilation', passThrough),
new OptionHandler('--minify', passThrough),
new OptionHandler('--force-strip=.*', setStrip),
// TODO(ahe): Remove the --no-colors option.
new OptionHandler('--disable-diagnostic-colors',
(_) => diagnosticHandler.enableColors = false),
new OptionHandler('--enable-diagnostic-colors',
(_) => diagnosticHandler.enableColors = true),
new OptionHandler('--enable[_-]checked[_-]mode|--checked',
(_) => passThrough('--enable-checked-mode')),
new OptionHandler('--enable-concrete-type-inference',
(_) => passThrough('--enable-concrete-type-inference')),
new OptionHandler(r'--help|/\?|/h', (_) => wantHelp = true),
new OptionHandler('--package-root=.+|-p.+', setPackageRoot),
new OptionHandler('--disallow-unsafe-eval',
new OptionHandler('--analyze-all', passThrough),
new OptionHandler('--analyze-only', setAnalyzeOnly),
new OptionHandler('--disable-native-live-type-analysis', passThrough),
new OptionHandler('--reject-deprecated-language-features', passThrough),
new OptionHandler('--report-sdk-use-of-deprecated-language-features',
new OptionHandler('--categories=.*', setCategories),
// The following two options must come last.
new OptionHandler('-.*', (String argument) {
helpAndFail('Error: Unknown option "$argument".');
new OptionHandler('.*', (String argument) {
parseCommandLine(handlers, argv);
if (wantHelp) helpAndExit(diagnosticHandler.verbose);
if (outputLanguage != OUTPUT_LANGUAGE_DART && stripArgumentSet) {
helpAndFail('Error: --force-strip may only be used with '
if (arguments.isEmpty) {
helpAndFail('Error: No Dart file specified.');
if (arguments.length > 1) {
var extra = arguments.getRange(1, arguments.length - 1);
helpAndFail('Error: Extra arguments: ${extra.join(" ")}');
void handler(Uri uri, int begin, int end, String message,
api.Diagnostic kind) {
diagnosticHandler.diagnosticHandler(uri, begin, end, message, kind);
Uri uri = cwd.resolve(arguments[0]);
if (packageRoot == null) {
packageRoot = uri.resolve('./packages/');
}'package root is $packageRoot');
int charactersWritten = 0;
compilationDone(String code) {
if (analyzeOnly) return;
if (code == null) {
fail('Error: Compilation failed.');
'compiled ${inputProvider.dartCharactersRead} characters Dart '
'-> $charactersWritten characters $outputLanguage '
'in ${relativize(cwd, out, isWindows)}');
if (!explicitOut) {
String input = uriPathToNative(arguments[0]);
String output = relativize(cwd, out, isWindows);
print('Dart file $input compiled to $outputLanguage: $output');
StreamSink<String> outputProvider(String name, String extension) {
Uri uri;
String sourceMapFileName;
bool isPrimaryOutput = false;
if (name == '') {
if (extension == 'js' || extension == 'dart') {
isPrimaryOutput = true;
uri = out;
sourceMapFileName =
sourceMapOut.path.substring(sourceMapOut.path.lastIndexOf('/') + 1);
} else if (extension == '' || extension == '') {
uri = sourceMapOut;
} else {
fail('Error: Unknown extension: $extension');
} else {
uri = out.resolve('$name.$extension');
if (uri.scheme != 'file') {
fail('Error: Unhandled scheme ${uri.scheme} in $uri.');
var outputStream = new File(uriPathToNative(uri.path)).openOutputStream();
CountingSink sink;
onDone() {
if (sourceMapFileName != null) {
String sourceMapTag = '//@ sourceMappingURL=$sourceMapFileName\n';
sink.count += sourceMapTag.length;
if (isPrimaryOutput) {
charactersWritten += sink.count;
var controller = new StreamController<String>();, onDone: onDone);
sink = new CountingSink(controller);
return sink;
api.compile(uri, libraryRoot, packageRoot,
inputProvider.readStringFromUri, handler,
options, outputProvider)
// TODO(ahe): Get rid of this class if is fixed.
class CountingSink implements StreamSink<String> {
final StreamSink<String> sink;
int count = 0;
add(String value) {
count += value.length;
signalError(AsyncError error) => sink.signalError(error);
close() => sink.close();
class AbortLeg {
final message;
toString() => 'Aborted due to --throw-on-error: $message';
void writeString(Uri uri, String text) {
if (uri.scheme != 'file') {
fail('Error: Unhandled scheme ${uri.scheme}.');
var file = new File(uriPathToNative(uri.path)).openSync(FileMode.WRITE);
void fail(String message) {
void compilerMain(Options options) {
var root = uriPathToNative("/$LIBRARY_ROOT");
List<String> argv = ['--library-root=${options.script}$root'];
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.
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).
-h Display this message (add -v for information about all options).''');
void verboseHelp() {
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).
-h, /h, /?, --help
Display this message (add -v for information about all options).
-v, --verbose
Display verbose information.
-p<path>, --package-root=<path>
Where to find packages, that is, "package:..." imports.
Analyze all code. Without this option, the compiler only analyzes
code that is reachable from [main]. This option is useful for
finding errors in libraries, but using it can result in bigger and
slower output.
Analyze but do not generate code.
Generate minified output.
Do not display any warnings.
Add colors to diagnostic messages.
The following options are only used for compiler development and may
be removed in a future version:
Output Dart code instead of JavaScript.
Throw an exception if a compile-time error is detected.
Where to find the Dart platform libraries.
Do not generate a call to main if either of the following
libraries are used: dart:dom, dart:html dart:io.
Enable experimental concrete type inference.
Disable the optimization that removes unused native types from dart:html
and related libraries.
/* TODO(8522): Restore this comment once option is restored.
Disable dynamic generation of code in the generated output. This is
necessary to satisfy CSP restrictions (see
This flag is not continuously tested. Please report breakages and we
will fix them as soon as possible.
Reject deprecated language features. Without this option, the
compiler will accept language features that are no longer valid
according to The Dart Programming Language Specification, version
0.12, M1.
Report use of deprecated features in Dart platform libraries.
Without this option, the compiler will silently accept use of
deprecated language features from these libraries. The option
--reject-deprecated-language-features controls if these usages are
reported as errors or warnings.
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.
void helpAndExit(bool verbose) {
if (verbose) {
} else {
void helpAndFail(String message) {
void main() {
try {
compilerMain(new Options());
} catch (exception, trace) {
try {
print('Internal error: $exception');
} catch (ignored) {
print('Internal error: error while printing exception');
try {
} finally {
exit(253); // 253 is recognized as a crash by our test scripts.