| Index: lib/src/compiler.dart
|
| diff --git a/lib/src/compiler.dart b/lib/src/compiler.dart
|
| deleted file mode 100644
|
| index f1747df08e702c23daa7118b3a73d58af025ddde..0000000000000000000000000000000000000000
|
| --- a/lib/src/compiler.dart
|
| +++ /dev/null
|
| @@ -1,512 +0,0 @@
|
| -// Copyright (c) 2015, 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.
|
| -
|
| -/// Command line tool to run the checker on a Dart program.
|
| -
|
| -import 'dart:async';
|
| -import 'dart:collection';
|
| -import 'dart:math' as math;
|
| -import 'dart:io';
|
| -
|
| -import 'package:analyzer/dart/ast/ast.dart' show CompilationUnit;
|
| -import 'package:analyzer/dart/element/element.dart';
|
| -import 'package:analyzer/src/generated/engine.dart'
|
| - show AnalysisEngine, AnalysisContext, ChangeSet, ParseDartTask;
|
| -import 'package:analyzer/src/generated/error.dart'
|
| - show AnalysisError, ErrorSeverity, ErrorType;
|
| -import 'package:analyzer/src/generated/error.dart';
|
| -import 'package:analyzer/src/generated/source.dart' show Source;
|
| -import 'package:analyzer/src/task/html.dart';
|
| -import 'package:html/dom.dart' as html;
|
| -import 'package:html/parser.dart' as html;
|
| -import 'package:logging/logging.dart' show Level, Logger, LogRecord;
|
| -import 'package:path/path.dart' as path;
|
| -
|
| -import 'analysis_context.dart';
|
| -import 'codegen/html_codegen.dart' as html_codegen;
|
| -import 'codegen/js_codegen.dart';
|
| -import 'options.dart';
|
| -import 'report.dart';
|
| -import 'utils.dart' show FileSystem, isStrongModeError;
|
| -
|
| -/// Sets up the type checker logger to print a span that highlights error
|
| -/// messages.
|
| -StreamSubscription setupLogger(Level level, printFn) {
|
| - Logger.root.level = level;
|
| - return Logger.root.onRecord.listen((LogRecord rec) {
|
| - printFn('${rec.level.name.toLowerCase()}: ${rec.message}');
|
| - });
|
| -}
|
| -
|
| -CompilerOptions validateOptions(List<String> args, {bool forceOutDir: false}) {
|
| - var options = parseOptions(args, forceOutDir: forceOutDir);
|
| - if (!options.help && !options.version) {
|
| - var srcOpts = options.sourceOptions;
|
| - if (!srcOpts.useMockSdk && srcOpts.dartSdkPath == null) {
|
| - print('Could not automatically find dart sdk path.');
|
| - print('Please pass in explicitly: --dart-sdk <path>');
|
| - exit(1);
|
| - }
|
| - if (options.inputs.length == 0) {
|
| - print('Expected filename.');
|
| - return null;
|
| - }
|
| - }
|
| - return options;
|
| -}
|
| -
|
| -/// Compile with the given options and return success or failure.
|
| -bool compile(CompilerOptions options) {
|
| - var context = createAnalysisContextWithSources(options.sourceOptions);
|
| - var reporter = new LogReporter(context, useColors: options.useColors);
|
| - return new BatchCompiler(context, options, reporter: reporter).run();
|
| -}
|
| -
|
| -// Callback on each individual compiled library
|
| -typedef void CompilationNotifier(String path);
|
| -
|
| -class BatchCompiler extends AbstractCompiler {
|
| - JSGenerator _jsGen;
|
| - LibraryElement _dartCore;
|
| - String _runtimeOutputDir;
|
| -
|
| - /// Already compiled sources, so we don't check or compile them again.
|
| - final _compilationRecord = <LibraryElement, bool>{};
|
| - bool _sdkCopied = false;
|
| -
|
| - bool _failure = false;
|
| - bool get failure => _failure;
|
| -
|
| - final _pendingLibraries = <List<CompilationUnit>>[];
|
| -
|
| - BatchCompiler(AnalysisContext context, CompilerOptions options,
|
| - {AnalysisErrorListener reporter,
|
| - FileSystem fileSystem: const FileSystem()})
|
| - : super(
|
| - context,
|
| - options,
|
| - new ErrorCollector(
|
| - context, reporter ?? AnalysisErrorListener.NULL_LISTENER),
|
| - fileSystem) {
|
| - _inputBaseDir = options.inputBaseDir;
|
| - if (outputDir != null) {
|
| - _jsGen = new JSGenerator(this);
|
| - _runtimeOutputDir = path.join(outputDir, 'dev_compiler', 'runtime');
|
| - }
|
| - _dartCore = context.typeProvider.objectType.element.library;
|
| - }
|
| -
|
| - ErrorCollector get reporter => super.reporter;
|
| -
|
| - /// Compiles every file in [options.inputs].
|
| - /// Returns true on successful compile.
|
| - bool run() {
|
| - var clock = new Stopwatch()..start();
|
| - options.inputs.forEach(compileFromUriString);
|
| - clock.stop();
|
| - var time = (clock.elapsedMilliseconds / 1000).toStringAsFixed(2);
|
| - _log.fine('Compiled ${_compilationRecord.length} libraries in ${time} s\n');
|
| -
|
| - return !_failure;
|
| - }
|
| -
|
| - void compileFromUriString(String uriString, [CompilationNotifier notifier]) {
|
| - _compileFromUri(stringToUri(uriString), notifier);
|
| - }
|
| -
|
| - void _compileFromUri(Uri uri, CompilationNotifier notifier) {
|
| - _failure = false;
|
| - if (!uri.isAbsolute) {
|
| - throw new ArgumentError.value('$uri', 'uri', 'must be absolute');
|
| - }
|
| - var source = context.sourceFactory.forUri(Uri.encodeFull('$uri'));
|
| - if (source == null) {
|
| - throw new ArgumentError.value('$uri', 'uri', 'could not find source for');
|
| - }
|
| - _compileSource(source, notifier);
|
| - }
|
| -
|
| - void _compileSource(Source source, CompilationNotifier notifier) {
|
| - if (AnalysisEngine.isHtmlFileName(source.uri.path)) {
|
| - _compileHtml(source, notifier);
|
| - } else {
|
| - _compileLibrary(context.computeLibraryElement(source), notifier);
|
| - }
|
| - _processPending();
|
| - reporter.flush();
|
| - }
|
| -
|
| - void _processPending() {
|
| - // _pendingLibraries was recorded in post-order. Process from the end
|
| - // to ensure reverse post-order. This will ensure that we handle back
|
| - // edges from the original depth-first search correctly.
|
| -
|
| - while (_pendingLibraries.isNotEmpty) {
|
| - var unit = _pendingLibraries.removeLast();
|
| - var library = unit.first.element.library;
|
| - assert(_compilationRecord[library] == true ||
|
| - options.codegenOptions.forceCompile);
|
| -
|
| - // Process dependencies one more time to propagate failure from cycles
|
| - for (var import in library.imports) {
|
| - if (!_compilationRecord[import.importedLibrary]) {
|
| - _compilationRecord[library] = false;
|
| - }
|
| - }
|
| - for (var export in library.exports) {
|
| - if (!_compilationRecord[export.exportedLibrary]) {
|
| - _compilationRecord[library] = false;
|
| - }
|
| - }
|
| -
|
| - // Generate code if still valid
|
| - if (_jsGen != null &&
|
| - (_compilationRecord[library] ||
|
| - options.codegenOptions.forceCompile)) {
|
| - _jsGen.generateLibrary(unit);
|
| - }
|
| - }
|
| - }
|
| -
|
| - bool _compileLibrary(LibraryElement library, CompilationNotifier notifier) {
|
| - var success = _compilationRecord[library];
|
| - if (success != null) {
|
| - if (!success) _failure = true;
|
| - return success;
|
| - }
|
| -
|
| - // Optimistically mark a library valid until proven otherwise
|
| - _compilationRecord[library] = true;
|
| -
|
| - if (!options.checkSdk && library.source.isInSystemLibrary) {
|
| - // We assume the Dart SDK is always valid
|
| - if (_jsGen != null) _copyDartRuntime();
|
| - return true;
|
| - }
|
| -
|
| - // Check dependences to determine if this library type checks
|
| - // TODO(jmesserly): in incremental mode, we can skip the transitive
|
| - // compile of imports/exports.
|
| - _compileLibrary(_dartCore, notifier); // implicit dart:core dependency
|
| - for (var import in library.imports) {
|
| - if (!_compileLibrary(import.importedLibrary, notifier)) {
|
| - _compilationRecord[library] = false;
|
| - }
|
| - }
|
| - for (var export in library.exports) {
|
| - if (!_compileLibrary(export.exportedLibrary, notifier)) {
|
| - _compilationRecord[library] = false;
|
| - }
|
| - }
|
| -
|
| - // Check this library's own code
|
| - var unitElements = new List.from(library.parts)
|
| - ..add(library.definingCompilationUnit);
|
| - var units = <CompilationUnit>[];
|
| -
|
| - bool failureInLib = false;
|
| - for (var element in unitElements) {
|
| - var unit = context.resolveCompilationUnit(element.source, library);
|
| - units.add(unit);
|
| - failureInLib = computeErrors(element.source) || failureInLib;
|
| - }
|
| - if (failureInLib) _compilationRecord[library] = false;
|
| -
|
| - // Notifier framework if requested
|
| - if (notifier != null) {
|
| - reporter.flush();
|
| - notifier(getOutputPath(library.source.uri));
|
| - }
|
| -
|
| - // Record valid libraries for further dependence checking (cycles) and
|
| - // codegen.
|
| -
|
| - // TODO(vsm): Restructure this to not delay code generation more than
|
| - // necessary. We'd like to process the AST before there is any chance
|
| - // it's cached out. We should refactor common logic in
|
| - // server/dependency_graph and perhaps the analyzer itself.
|
| - success = _compilationRecord[library];
|
| - if (success || options.codegenOptions.forceCompile) {
|
| - _pendingLibraries.add(units);
|
| - }
|
| -
|
| - // Return tentative success status.
|
| - if (!success) _failure = true;
|
| - return success;
|
| - }
|
| -
|
| - void _copyDartRuntime() {
|
| - if (_sdkCopied) return;
|
| - _sdkCopied = true;
|
| - for (var file in defaultRuntimeFiles) {
|
| - var input = new File(path.join(options.runtimeDir, file));
|
| - var output = new File(path.join(_runtimeOutputDir, file));
|
| - if (output.existsSync() &&
|
| - output.lastModifiedSync() == input.lastModifiedSync()) {
|
| - continue;
|
| - }
|
| - fileSystem.copySync(input.path, output.path);
|
| - }
|
| - }
|
| -
|
| - void _compileHtml(Source source, CompilationNotifier notifier) {
|
| - // TODO(jmesserly): reuse DartScriptsTask instead of copy/paste.
|
| - var contents = context.getContents(source);
|
| - var document = html.parse(contents.data, generateSpans: true);
|
| - var scripts = document.querySelectorAll('script[type="application/dart"]');
|
| -
|
| - var loadedLibs = new LinkedHashSet<Uri>();
|
| -
|
| - // If we're generating code, convert the HTML file as well.
|
| - // Otherwise, just search for Dart sources to analyze.
|
| - var htmlOutDir =
|
| - _jsGen != null ? path.dirname(getOutputPath(source.uri)) : null;
|
| - for (var script in scripts) {
|
| - Source scriptSource = null;
|
| - var srcAttr = script.attributes['src'];
|
| - if (srcAttr == null) {
|
| - if (script.hasContent()) {
|
| - var fragments = <ScriptFragment>[];
|
| - for (var node in script.nodes) {
|
| - if (node is html.Text) {
|
| - var start = node.sourceSpan.start;
|
| - fragments.add(new ScriptFragment(
|
| - start.offset, start.line, start.column, node.data));
|
| - }
|
| - }
|
| - scriptSource = new DartScript(source, fragments);
|
| - }
|
| - } else if (AnalysisEngine.isDartFileName(srcAttr)) {
|
| - scriptSource = context.sourceFactory.resolveUri(source, srcAttr);
|
| - }
|
| -
|
| - if (scriptSource != null) {
|
| - var lib = context.computeLibraryElement(scriptSource);
|
| - _compileLibrary(lib, notifier);
|
| - if (htmlOutDir != null) {
|
| - script.replaceWith(_linkLibraries(lib, loadedLibs, from: htmlOutDir));
|
| - }
|
| - }
|
| - }
|
| -
|
| - if (htmlOutDir != null) {
|
| - fileSystem.writeAsStringSync(
|
| - getOutputPath(source.uri), document.outerHtml + '\n');
|
| - }
|
| - }
|
| -
|
| - html.DocumentFragment _linkLibraries(
|
| - LibraryElement mainLib, LinkedHashSet<Uri> loaded,
|
| - {String from}) {
|
| - assert(from != null);
|
| - var alreadyLoaded = loaded.length;
|
| - _collectLibraries(mainLib, loaded);
|
| -
|
| - var newLibs = loaded.skip(alreadyLoaded);
|
| - var df = new html.DocumentFragment();
|
| -
|
| - for (var uri in newLibs) {
|
| - if (uri.scheme == 'dart') {
|
| - if (uri.path == 'core') {
|
| - // TODO(jmesserly): it would be nice to not special case these.
|
| - for (var file in defaultRuntimeFiles) {
|
| - file = path.join(_runtimeOutputDir, file);
|
| - df.append(
|
| - html_codegen.libraryInclude(path.relative(file, from: from)));
|
| - }
|
| - }
|
| - } else {
|
| - var file = path.join(outputDir, getModulePath(uri));
|
| - df.append(html_codegen.libraryInclude(path.relative(file, from: from)));
|
| - }
|
| - }
|
| -
|
| - df.append(html_codegen.invokeMain(getModuleName(mainLib.source.uri)));
|
| - return df;
|
| - }
|
| -
|
| - void _collectLibraries(LibraryElement lib, LinkedHashSet<Uri> loaded) {
|
| - var uri = lib.source.uri;
|
| - if (!loaded.add(uri)) return;
|
| - _collectLibraries(_dartCore, loaded);
|
| -
|
| - for (var l in lib.imports) _collectLibraries(l.importedLibrary, loaded);
|
| - for (var l in lib.exports) _collectLibraries(l.exportedLibrary, loaded);
|
| - // Move the item to the end of the list.
|
| - loaded.remove(uri);
|
| - loaded.add(uri);
|
| - }
|
| -}
|
| -
|
| -abstract class AbstractCompiler {
|
| - final CompilerOptions options;
|
| - final AnalysisContext context;
|
| - final AnalysisErrorListener reporter;
|
| - final FileSystem fileSystem;
|
| -
|
| - AbstractCompiler(this.context, this.options,
|
| - [AnalysisErrorListener listener, this.fileSystem = const FileSystem()])
|
| - : reporter = listener ?? AnalysisErrorListener.NULL_LISTENER;
|
| -
|
| - String get outputDir => options.codegenOptions.outputDir;
|
| -
|
| - Uri stringToUri(String uriString) {
|
| - var uri = uriString.startsWith('dart:') || uriString.startsWith('package:')
|
| - ? Uri.parse(uriString)
|
| - : new Uri.file(path.absolute(uriString));
|
| - return uri;
|
| - }
|
| -
|
| - /// Directory presumed to be the common prefix for all input file:// URIs.
|
| - /// Used when computing output paths.
|
| - ///
|
| - /// For example:
|
| - /// dartdevc -o out foo/a.dart bar/b.dart
|
| - ///
|
| - /// Will produce:
|
| - /// out/foo/a.dart
|
| - /// out/bar/b.dart
|
| - ///
|
| - /// This is only used if at least one of [options.codegenOptions.inputs] is
|
| - /// a file URI.
|
| - // TODO(jmesserly): do we need an option for this?
|
| - // Other ideas: we could look up and see what package the file is in, treat
|
| - // that as a base path. We could also use the current working directory as
|
| - // the base.
|
| - String get inputBaseDir {
|
| - if (_inputBaseDir == null) {
|
| - List<String> common = null;
|
| - for (var uri in options.inputs.map(stringToUri)) {
|
| - if (uri.scheme != 'file') continue;
|
| -
|
| - var segments = path.split(path.dirname(uri.path));
|
| - if (common == null) {
|
| - common = segments;
|
| - } else {
|
| - int len = math.min(common.length, segments.length);
|
| - while (len > 0 && common[len - 1] != segments[len - 1]) {
|
| - len--;
|
| - }
|
| - common.length = len;
|
| - }
|
| - }
|
| - _inputBaseDir = common == null ? '' : path.joinAll(common);
|
| - }
|
| - return _inputBaseDir;
|
| - }
|
| -
|
| - String _inputBaseDir;
|
| -
|
| - String getOutputPath(Uri uri) => path.join(outputDir, getModulePath(uri));
|
| -
|
| - /// Like [getModuleName] but includes the file extension, either .js or .html.
|
| - String getModulePath(Uri uri) {
|
| - var ext = path.extension(uri.path);
|
| - if (ext == '.dart' || ext == '' && uri.scheme == 'dart') ext = '.js';
|
| - return getModuleName(uri) + ext;
|
| - }
|
| -
|
| - /// Gets the module name, without extension. For example:
|
| - ///
|
| - /// * dart:core -> dart/core
|
| - /// * file:foo/bar/baz.dart -> foo/bar/baz
|
| - /// * package:qux/qux.dart -> qux/qux
|
| - ///
|
| - /// For file: URLs this will also make them relative to [inputBaseDir].
|
| - // TODO(jmesserly): we need to figure out a way to keep package and file URLs
|
| - // from conflicting.
|
| - String getModuleName(Uri uri) {
|
| - var filepath = path.withoutExtension(uri.path);
|
| - if (uri.scheme == 'dart') {
|
| - return 'dart/$filepath';
|
| - } else if (uri.scheme == 'file') {
|
| - return path.relative(filepath, from: inputBaseDir);
|
| - } else {
|
| - assert(uri.scheme == 'package');
|
| - // filepath is good here, we want the output to start with a directory
|
| - // matching the package name.
|
| - return filepath;
|
| - }
|
| - }
|
| -
|
| - /// Log any errors encountered when resolving [source] and return whether any
|
| - /// errors were found.
|
| - bool computeErrors(Source source) {
|
| - AnalysisContext errorContext = context;
|
| - // TODO(jmesserly): should this be a fix somewhere in analyzer?
|
| - // otherwise we fail to find the parts.
|
| - if (source.isInSystemLibrary) {
|
| - errorContext = context.sourceFactory.dartSdk.context;
|
| - }
|
| - List<AnalysisError> errors = errorContext.computeErrors(source);
|
| - bool failure = false;
|
| - for (var error in errors) {
|
| - // TODO(jmesserly): this is a very expensive lookup, and it has to be
|
| - // repeated every time we want to query error severity.
|
| - var severity = errorSeverity(errorContext, error);
|
| - if (severity == ErrorSeverity.ERROR) {
|
| - reporter.onError(error);
|
| - failure = true;
|
| - } else if (severity == ErrorSeverity.WARNING) {
|
| - reporter.onError(error);
|
| - }
|
| - }
|
| - return failure;
|
| - }
|
| -}
|
| -
|
| -// TODO(jmesserly): find a better home for these.
|
| -/// Curated order to minimize lazy classes needed by dart:core and its
|
| -/// transitive SDK imports.
|
| -final corelibOrder = [
|
| - 'dart:core',
|
| - 'dart:collection',
|
| - 'dart:_internal',
|
| - 'dart:math',
|
| - 'dart:_interceptors',
|
| - 'dart:async',
|
| - 'dart:_foreign_helper',
|
| - 'dart:_js_embedded_names',
|
| - 'dart:_js_helper',
|
| - 'dart:isolate',
|
| - 'dart:typed_data',
|
| - 'dart:_native_typed_data',
|
| - 'dart:_isolate_helper',
|
| - 'dart:_js_primitives',
|
| - 'dart:convert',
|
| - // TODO(jmesserly): these are not part of corelib library cycle, and shouldn't
|
| - // be listed here. Instead, their source should be copied on demand if they
|
| - // are actually used by the application.
|
| - 'dart:mirrors',
|
| - 'dart:_js_mirrors',
|
| - 'dart:js',
|
| - 'dart:_metadata',
|
| - 'dart:html',
|
| - 'dart:html_common',
|
| - 'dart:indexed_db',
|
| - 'dart:svg',
|
| - 'dart:web_audio',
|
| - 'dart:web_gl',
|
| - 'dart:web_sql',
|
| - 'dart:_debugger'
|
| -
|
| - // _foreign_helper is not included, as it only defines the JS builtin that
|
| - // the compiler handles at compile time.
|
| -].map(Uri.parse).toList();
|
| -
|
| -/// Runtime files added to all applications when running the compiler in the
|
| -/// command line.
|
| -final defaultRuntimeFiles = () {
|
| - String coreToFile(Uri uri) => uri.toString().replaceAll(':', '/') + '.js';
|
| -
|
| - var files = [
|
| - 'harmony_feature_check.js',
|
| - 'dart_library.js',
|
| - 'dart/_runtime.js',
|
| - ];
|
| - files.addAll(corelibOrder.map(coreToFile));
|
| - return files;
|
| -}();
|
| -
|
| -final _log = new Logger('dev_compiler.src.compiler');
|
|
|