blob: 17c84a43751f14722f28de25311656e8847b1504 [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 'dart:convert' show utf8;
import 'package:front_end/src/api_unstable/dart2js.dart'
show getSupportedLibraryNames;
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 'io/source_file.dart';
import 'options.dart' show CompilerOptions;
/// Implements the [Compiler] using a [api.CompilerInput] for supplying the
/// sources.
class CompilerImpl extends Compiler {
@override
final Measurer measurer;
@override
api.CompilerInput provider;
api.CompilerDiagnostics handler;
GenericTask userHandlerTask;
GenericTask userProviderTask;
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),
super(
options: options,
outputProvider: outputProvider,
environment: new _Environment(options.environment),
makeReporter: makeReporter) {
tasks.addAll([
userHandlerTask = new GenericTask('Diagnostic handler', measurer),
userProviderTask = new GenericTask('Input provider', measurer),
]);
}
void log(message) {
callUserHandler(
null, null, null, null, message, api.Diagnostic.VERBOSE_INFO);
}
Future setupSdk() {
var future = new Future.value(null);
_Environment env = environment;
if (env.supportedLibraries == null) {
future = future.then((_) {
Uri specificationUri = options.librariesSpecificationUri;
return provider.readFromUri(specificationUri).then((api.Input spec) {
String json = null;
// TODO(sigmund): simplify this, we have some API inconsistencies when
// our internal input adds a terminating zero.
if (spec is SourceFile) {
json = spec.slowText();
} else if (spec is Binary) {
json = utf8.decode(spec.data);
}
// TODO(sigmund): would be nice to front-load some of the CFE option
// processing and parse this .json file only once.
env.supportedLibraries = getSupportedLibraryNames(specificationUri,
json, options.compileForServer ? "dart2js_server" : "dart2js")
.toSet();
});
});
}
// 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;
}
@override
Future<bool> run(Uri uri) {
Duration setupDuration = measurer.elapsedWallClock;
return selfTask.measureSubtask("impl.run", () {
return setupSdk().then((_) {
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.elapsedWallClock;
Duration asyncDuration = measurer.elapsedAsyncWallClock;
Duration cumulatedDuration = Duration.zero;
List<_TimingData> timingData = [];
for (final task in tasks) {
String running = task.isRunning ? "*" : " ";
Duration duration = task.duration;
if (duration != Duration.zero) {
cumulatedDuration += duration;
int milliseconds = duration.inMilliseconds;
timingData.add(_TimingData(' $running${task.name}:', milliseconds,
milliseconds * 100 / totalDuration.inMilliseconds));
for (String subtask in task.subtasks) {
int subtime = task.getSubtaskTime(subtask);
String running = task.getSubtaskIsRunning(subtask) ? "*" : " ";
timingData.add(_TimingData(' $running${task.name} > $subtask:',
subtime, subtime * 100 / totalDuration.inMilliseconds));
}
}
}
int longestDescription = timingData
.map((d) => d.description.length)
.fold(0, (a, b) => a < b ? b : a);
for (var data in timingData) {
var ms = _formatMs(data.milliseconds);
var padding =
" " * (longestDescription + 10 - data.description.length - ms.length);
var percentPadding = data.percent < 10 ? " " : "";
timings.writeln('${data.description}$padding $ms '
'$percentPadding(${data.percent.toStringAsFixed(1)}%)');
}
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)}%)');
}
@override
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;
}
}
}
class _Environment implements Environment {
final Map<String, String> definitions;
Set<String> supportedLibraries;
_Environment(this.definitions);
@override
String valueOf(String name) {
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;
if (supportedLibraries.contains(libraryName)) return "true";
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.';
class _TimingData {
final String description;
final int milliseconds;
final double percent;
_TimingData(this.description, this.milliseconds, this.percent);
}