blob: a85592506d00b64a1088fc46ade46ba57331cccf [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 leg_apiimpl;
import 'dart:async';
import 'package:package_config/packages.dart';
import 'package:package_config/packages_file.dart' as pkgs;
import 'package:package_config/src/packages_impl.dart'
show MapPackages, NonFilePackagesDirectoryPackages;
import 'package:package_config/src/util.dart' show checkValidPackageUri;
import '../compiler_new.dart' as api;
import 'common/tasks.dart' show GenericTask, Measurer;
import 'common.dart';
import 'compiler.dart';
import 'diagnostics/messages.dart' show Message;
import 'environment.dart';
import 'options.dart' show CompilerOptions;
import 'platform_configuration.dart' as platform_configuration;
import 'resolved_uri_translator.dart';
/// Implements the [Compiler] using a [api.CompilerInput] for supplying the
/// sources.
class CompilerImpl extends Compiler {
final Measurer measurer;
api.CompilerInput provider;
api.CompilerDiagnostics handler;
Packages packages;
ForwardingResolvedUriTranslator resolvedUriTranslator;
GenericTask userHandlerTask;
GenericTask userProviderTask;
GenericTask userPackagesDiscoveryTask;
Uri get libraryRoot => options.platformConfigUri.resolve(".");
CompilerImpl(this.provider, api.CompilerOutput outputProvider, this.handler,
CompilerOptions options,
{MakeReporterFunction makeReporter})
// NOTE: allocating measurer is done upfront to ensure the wallclock is
// started before other computations.
: measurer = new Measurer(enableTaskMeasurements: options.verbose),
resolvedUriTranslator = new ForwardingResolvedUriTranslator(),
super(
options: options,
outputProvider: outputProvider,
environment: new _Environment(options.environment),
makeReporter: makeReporter) {
_Environment env = environment;
env.compiler = this;
tasks.addAll([
userHandlerTask = new GenericTask('Diagnostic handler', measurer),
userProviderTask = new GenericTask('Input provider', measurer),
userPackagesDiscoveryTask =
new GenericTask('Package discovery', measurer),
]);
}
void log(message) {
callUserHandler(
null, null, null, null, message, api.Diagnostic.VERBOSE_INFO);
}
/**
* Translates a readable URI into a resource URI.
*
* See [LibraryLoader] for terminology on URIs.
*/
Uri translateUri(Spannable node, Uri uri) =>
uri.scheme == 'package' ? translatePackageUri(node, uri) : uri;
Uri translatePackageUri(Spannable node, Uri uri) {
try {
checkValidPackageUri(uri);
} on ArgumentError catch (e) {
reporter.reportErrorMessage(node, MessageKind.INVALID_PACKAGE_URI,
{'uri': uri, 'exception': e.message});
return null;
}
return packages.resolve(uri, notFound: (Uri notFound) {
reporter.reportErrorMessage(
node, MessageKind.LIBRARY_NOT_FOUND, {'resolvedUri': uri});
return null;
});
}
Future setupPackages(Uri uri) {
if (options.packageRoot != null) {
// Use "non-file" packages because the file version requires a [Directory]
// and we can't depend on 'dart:io' classes.
packages = new NonFilePackagesDirectoryPackages(options.packageRoot);
} else if (options.packageConfig != null) {
Future<api.Input<List<int>>> future =
callUserProvider(options.packageConfig, api.InputKind.binary);
return future.then((api.Input<List<int>> binary) {
packages =
new MapPackages(pkgs.parse(binary.data, options.packageConfig));
}).catchError((error) {
reporter.reportErrorMessage(
NO_LOCATION_SPANNABLE,
MessageKind.INVALID_PACKAGE_CONFIG,
{'uri': options.packageConfig, 'exception': error});
packages = Packages.noPackages;
});
} else {
if (options.packagesDiscoveryProvider == null) {
packages = Packages.noPackages;
} else {
return callUserPackagesDiscovery(uri).then((p) {
packages = p;
});
}
}
return new Future.value();
}
Future setupSdk() {
var future = new Future.value(null);
if (resolvedUriTranslator.isNotSet) {
future = future.then((_) {
return platform_configuration
.load(options.platformConfigUri, provider)
.then((Map<String, Uri> mapping) {
resolvedUriTranslator.resolvedUriTranslator =
new ResolvedUriTranslator(mapping);
});
});
}
// TODO(johnniwinther): This does not apply anymore.
// The incremental compiler sets up the sdk before run.
// Therefore this will be called a second time.
return future;
}
Future<bool> run(Uri uri) {
Duration setupDuration = measurer.wallClock.elapsed;
return selfTask.measureSubtask("CompilerImpl.run", () {
log('Using platform configuration at ${options.platformConfigUri}');
return setupSdk().then((_) => setupPackages(uri)).then((_) {
assert(resolvedUriTranslator.isSet);
assert(packages != null);
return super.run(uri);
}).then((bool success) {
if (options.verbose) {
StringBuffer timings = new StringBuffer();
computeTimings(setupDuration, timings);
log("$timings");
}
return success;
});
});
}
String _formatMs(int ms) {
return (ms / 1000).toStringAsFixed(3) + 's';
}
void computeTimings(Duration setupDuration, StringBuffer timings) {
timings.writeln("Timings:");
Duration totalDuration = measurer.wallClock.elapsed;
Duration asyncDuration = measurer.asyncWallClock.elapsed;
Duration cumulatedDuration = Duration.zero;
for (final task in tasks) {
String running = task.isRunning ? "*" : "";
Duration duration = task.duration;
if (duration != Duration.zero) {
cumulatedDuration += duration;
timings.writeln(' $running${task.name}:'
' ${_formatMs(duration.inMilliseconds)}');
for (String subtask in task.subtasks) {
int subtime = task.getSubtaskTime(subtask);
String running = task.getSubtaskIsRunning(subtask) ? "*" : "";
timings.writeln(
' $running${task.name} > $subtask: ${_formatMs(subtime)}');
}
}
}
Duration unaccountedDuration =
totalDuration - cumulatedDuration - setupDuration - asyncDuration;
double percent =
unaccountedDuration.inMilliseconds * 100 / totalDuration.inMilliseconds;
timings.write(
' Total compile-time ${_formatMs(totalDuration.inMilliseconds)};'
' setup ${_formatMs(setupDuration.inMilliseconds)};'
' async ${_formatMs(asyncDuration.inMilliseconds)};'
' unaccounted ${_formatMs(unaccountedDuration.inMilliseconds)}'
' (${percent.toStringAsFixed(2)}%)');
}
void reportDiagnostic(DiagnosticMessage message,
List<DiagnosticMessage> infos, api.Diagnostic kind) {
_reportDiagnosticMessage(message, kind);
for (DiagnosticMessage info in infos) {
_reportDiagnosticMessage(info, api.Diagnostic.INFO);
}
}
void _reportDiagnosticMessage(
DiagnosticMessage diagnosticMessage, api.Diagnostic kind) {
// [:span.uri:] might be [:null:] in case of a [Script] with no [uri]. For
// instance in the [Types] constructor in typechecker.dart.
SourceSpan span = diagnosticMessage.sourceSpan;
Message message = diagnosticMessage.message;
if (span == null || span.uri == null) {
callUserHandler(message, null, null, null, '$message', kind);
} else {
callUserHandler(
message, span.uri, span.begin, span.end, '$message', kind);
}
}
void callUserHandler(Message message, Uri uri, int begin, int end,
String text, api.Diagnostic kind) {
try {
userHandlerTask.measure(() {
handler.report(message, uri, begin, end, text, kind);
});
} catch (ex, s) {
reportCrashInUserCode('Uncaught exception in diagnostic handler', ex, s);
rethrow;
}
}
Future<api.Input> callUserProvider(Uri uri, api.InputKind inputKind) {
try {
return userProviderTask
.measureIo(() => provider.readFromUri(uri, inputKind: inputKind));
} catch (ex, s) {
reportCrashInUserCode('Uncaught exception in input provider', ex, s);
rethrow;
}
}
Future<Packages> callUserPackagesDiscovery(Uri uri) {
try {
return userPackagesDiscoveryTask
.measureIo(() => options.packagesDiscoveryProvider(uri));
} catch (ex, s) {
reportCrashInUserCode('Uncaught exception in package discovery', ex, s);
rethrow;
}
}
}
class _Environment implements Environment {
final Map<String, String> definitions;
// TODO(sigmund): break the circularity here: Compiler needs an environment to
// initialize the library loader, but the environment here needs to know about
// how the sdk is set up and about whether the backend supports mirrors.
CompilerImpl compiler;
_Environment(this.definitions);
String valueOf(String name) {
assert(compiler.resolvedUriTranslator != null,
failedAt(NO_LOCATION_SPANNABLE, "setupSdk() has not been run"));
var result = definitions[name];
if (result != null || definitions.containsKey(name)) return result;
if (!name.startsWith(_dartLibraryEnvironmentPrefix)) return null;
String libraryName = name.substring(_dartLibraryEnvironmentPrefix.length);
// Private libraries are not exposed to the users.
if (libraryName.startsWith("_")) return null;
Uri libraryUri = compiler.resolvedUriTranslator.sdkLibraries[libraryName];
// TODO(sigmund): use libraries.json instead of .platform files, then simply
// use the `supported` bit.
if (libraryUri != null && libraryUri.scheme != "unsupported") {
if (libraryName == 'mirrors') return null;
if (libraryName == 'isolate') return null;
return "true";
}
// Note: we return null on `dart:io` here, even if we allow users to
// unconditionally import it.
//
// In the past it was invalid to import `dart:io` for client apps. We just
// made it valid to import it as a stopgap measure to support packages like
// `http`. This is temporary until we support config-imports in the
// language.
//
// Because it is meant to be temporary and because the returned `dart:io`
// implementation will throw on most APIs, we still preserve that
// when compiling client apps the `dart:io` library is technically not
// supported, and so `const bool.fromEnvironment(dart.library.io)` is false.
return null;
}
}
/// For every 'dart:' library, a corresponding environment variable is set
/// to "true". The environment variable's name is the concatenation of
/// this prefix and the name (without the 'dart:'.
///
/// For example 'dart:html' has the environment variable 'dart.library.html' set
/// to "true".
const String _dartLibraryEnvironmentPrefix = 'dart.library.';