8f7a0f400b
Change-Id: I83963771db301089317823f6bf14eb4a2728dbfb Reviewed-on: https://dart-review.googlesource.com/c/81740 Auto-Submit: Alexander Markov <alexmarkov@google.com> Commit-Queue: Régis Crelier <regis@google.com> Reviewed-by: Régis Crelier <regis@google.com>
267 lines
9.3 KiB
Dart
267 lines
9.3 KiB
Dart
// Copyright (c) 2017, 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.
|
|
|
|
/// Defines the VM-specific translation of Dart source code to kernel binaries.
|
|
library vm.kernel_front_end;
|
|
|
|
import 'dart:async';
|
|
|
|
import 'package:front_end/src/api_unstable/vm.dart'
|
|
show
|
|
CompilerContext,
|
|
CompilerOptions,
|
|
DiagnosticMessage,
|
|
DiagnosticMessageHandler,
|
|
ProcessedOptions,
|
|
Severity,
|
|
getMessageUri,
|
|
kernelForProgram,
|
|
printDiagnosticMessage;
|
|
|
|
import 'package:kernel/type_environment.dart' show TypeEnvironment;
|
|
import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
|
|
import 'package:kernel/ast.dart' show Component, Field, StaticGet;
|
|
import 'package:kernel/core_types.dart' show CoreTypes;
|
|
import 'package:kernel/transformations/constants.dart' as constants;
|
|
import 'package:kernel/vm/constants_native_effects.dart' as vm_constants;
|
|
|
|
import 'bytecode/gen_bytecode.dart' show generateBytecode;
|
|
|
|
import 'constants_error_reporter.dart' show ForwardConstantEvaluationErrors;
|
|
import 'transformations/devirtualization.dart' as devirtualization
|
|
show transformComponent;
|
|
import 'transformations/mixin_deduplication.dart' as mixin_deduplication
|
|
show transformComponent;
|
|
import 'transformations/no_dynamic_invocations_annotator.dart'
|
|
as no_dynamic_invocations_annotator show transformComponent;
|
|
import 'transformations/type_flow/transformer.dart' as globalTypeFlow
|
|
show transformComponent;
|
|
import 'transformations/obfuscation_prohibitions_annotator.dart'
|
|
as obfuscationProhibitions;
|
|
|
|
/// Generates a kernel representation of the program whose main library is in
|
|
/// the given [source]. Intended for whole program (non-modular) compilation.
|
|
///
|
|
/// VM-specific replacement of [kernelForProgram].
|
|
///
|
|
Future<Component> compileToKernel(Uri source, CompilerOptions options,
|
|
{bool aot: false,
|
|
bool useGlobalTypeFlowAnalysis: false,
|
|
Map<String, String> environmentDefines,
|
|
bool genBytecode: false,
|
|
bool emitBytecodeSourcePositions: false,
|
|
bool dropAST: false,
|
|
bool useFutureBytecodeFormat: false,
|
|
bool enableAsserts: false,
|
|
bool enableConstantEvaluation: true}) async {
|
|
// Replace error handler to detect if there are compilation errors.
|
|
final errorDetector =
|
|
new ErrorDetector(previousErrorHandler: options.onDiagnostic);
|
|
options.onDiagnostic = errorDetector;
|
|
|
|
final component = await kernelForProgram(source, options);
|
|
|
|
// If we don't default back to the current VM we'll add environment defines
|
|
// for the core libraries.
|
|
if (component != null && environmentDefines != null) {
|
|
if (environmentDefines['dart.vm.product'] == 'true') {
|
|
environmentDefines['dart.developer.causal_async_stacks'] = 'false';
|
|
}
|
|
environmentDefines['dart.isVM'] = 'true';
|
|
for (final library in component.libraries) {
|
|
if (library.importUri.scheme == 'dart') {
|
|
final path = library.importUri.path;
|
|
if (!path.startsWith('_')) {
|
|
environmentDefines['dart.library.${path}'] = 'true';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Run global transformations only if component is correct.
|
|
if (aot && component != null) {
|
|
await _runGlobalTransformations(
|
|
source,
|
|
options,
|
|
component,
|
|
useGlobalTypeFlowAnalysis,
|
|
environmentDefines,
|
|
enableAsserts,
|
|
enableConstantEvaluation,
|
|
errorDetector);
|
|
}
|
|
|
|
if (genBytecode && !errorDetector.hasCompilationErrors && component != null) {
|
|
await runWithFrontEndCompilerContext(source, options, component, () {
|
|
generateBytecode(component,
|
|
dropAST: dropAST,
|
|
emitSourcePositions: emitBytecodeSourcePositions,
|
|
useFutureBytecodeFormat: useFutureBytecodeFormat,
|
|
environmentDefines: environmentDefines);
|
|
});
|
|
}
|
|
|
|
// Restore error handler (in case 'options' are reused).
|
|
options.onDiagnostic = errorDetector.previousErrorHandler;
|
|
|
|
return component;
|
|
}
|
|
|
|
Future _runGlobalTransformations(
|
|
Uri source,
|
|
CompilerOptions compilerOptions,
|
|
Component component,
|
|
bool useGlobalTypeFlowAnalysis,
|
|
Map<String, String> environmentDefines,
|
|
bool enableAsserts,
|
|
bool enableConstantEvaluation,
|
|
ErrorDetector errorDetector) async {
|
|
if (errorDetector.hasCompilationErrors) return;
|
|
|
|
final coreTypes = new CoreTypes(component);
|
|
_patchVmConstants(coreTypes);
|
|
|
|
// TODO(alexmarkov, dmitryas): Consider doing canonicalization of identical
|
|
// mixin applications when creating mixin applications in frontend,
|
|
// so all backends (and all transformation passes from the very beginning)
|
|
// can benefit from mixin de-duplication.
|
|
// At least, in addition to VM/AOT case we should run this transformation
|
|
// when building a platform dill file for VM/JIT case.
|
|
mixin_deduplication.transformComponent(component);
|
|
|
|
if (enableConstantEvaluation) {
|
|
await _performConstantEvaluation(source, compilerOptions, component,
|
|
coreTypes, environmentDefines, enableAsserts);
|
|
|
|
if (errorDetector.hasCompilationErrors) return;
|
|
}
|
|
|
|
if (useGlobalTypeFlowAnalysis) {
|
|
globalTypeFlow.transformComponent(
|
|
compilerOptions.target, coreTypes, component);
|
|
} else {
|
|
devirtualization.transformComponent(coreTypes, component);
|
|
no_dynamic_invocations_annotator.transformComponent(component);
|
|
}
|
|
|
|
// We don't know yet whether gen_snapshot will want to do obfuscation, but if
|
|
// it does it will need the obfuscation prohibitions.
|
|
obfuscationProhibitions.transformComponent(component, coreTypes);
|
|
}
|
|
|
|
/// Runs given [action] with [CompilerContext]. This is needed to
|
|
/// be able to report compile-time errors.
|
|
Future<T> runWithFrontEndCompilerContext<T>(Uri source,
|
|
CompilerOptions compilerOptions, Component component, T action()) async {
|
|
final processedOptions =
|
|
new ProcessedOptions(options: compilerOptions, inputs: [source]);
|
|
|
|
// Run within the context, so we have uri source tokens...
|
|
return await CompilerContext.runWithOptions(processedOptions,
|
|
(CompilerContext context) async {
|
|
// To make the fileUri/fileOffset -> line/column mapping, we need to
|
|
// pre-fill the map.
|
|
context.uriToSource.addAll(component.uriToSource);
|
|
|
|
return action();
|
|
});
|
|
}
|
|
|
|
Future _performConstantEvaluation(
|
|
Uri source,
|
|
CompilerOptions compilerOptions,
|
|
Component component,
|
|
CoreTypes coreTypes,
|
|
Map<String, String> environmentDefines,
|
|
bool enableAsserts) async {
|
|
final vmConstants =
|
|
new vm_constants.VmConstantsBackend(environmentDefines, coreTypes);
|
|
|
|
await runWithFrontEndCompilerContext(source, compilerOptions, component, () {
|
|
final hierarchy = new ClassHierarchy(component);
|
|
final typeEnvironment =
|
|
new TypeEnvironment(coreTypes, hierarchy, strongMode: true);
|
|
|
|
// TFA will remove constants fields which are unused (and respects the
|
|
// vm/embedder entrypoints).
|
|
constants.transformComponent(component, vmConstants,
|
|
keepFields: true,
|
|
evaluateAnnotations: true,
|
|
enableAsserts: enableAsserts,
|
|
errorReporter: new ForwardConstantEvaluationErrors(typeEnvironment));
|
|
});
|
|
}
|
|
|
|
void _patchVmConstants(CoreTypes coreTypes) {
|
|
// Fix Endian.host to be a const field equal to Endial.little instead of
|
|
// a final field. VM does not support big-endian architectures at the
|
|
// moment.
|
|
// Can't use normal patching process for this because CFE does not
|
|
// support patching fields.
|
|
// See http://dartbug.com/32836 for the background.
|
|
final Field host =
|
|
coreTypes.index.getMember('dart:typed_data', 'Endian', 'host');
|
|
host.isConst = true;
|
|
host.initializer = new StaticGet(
|
|
coreTypes.index.getMember('dart:typed_data', 'Endian', 'little'))
|
|
..parent = host;
|
|
}
|
|
|
|
class ErrorDetector {
|
|
final DiagnosticMessageHandler previousErrorHandler;
|
|
bool hasCompilationErrors = false;
|
|
|
|
ErrorDetector({this.previousErrorHandler});
|
|
|
|
void call(DiagnosticMessage message) {
|
|
if (message.severity == Severity.error) {
|
|
hasCompilationErrors = true;
|
|
}
|
|
|
|
previousErrorHandler?.call(message);
|
|
}
|
|
}
|
|
|
|
class ErrorPrinter {
|
|
final DiagnosticMessageHandler previousErrorHandler;
|
|
final compilationMessages = <Uri, List<DiagnosticMessage>>{};
|
|
|
|
ErrorPrinter({this.previousErrorHandler});
|
|
|
|
void call(DiagnosticMessage message) {
|
|
final sourceUri = getMessageUri(message);
|
|
(compilationMessages[sourceUri] ??= <DiagnosticMessage>[]).add(message);
|
|
previousErrorHandler?.call(message);
|
|
}
|
|
|
|
void printCompilationMessages(Uri baseUri) {
|
|
final sortedUris = compilationMessages.keys.toList()
|
|
..sort((a, b) => '$a'.compareTo('$b'));
|
|
for (final Uri sourceUri in sortedUris) {
|
|
for (final DiagnosticMessage message in compilationMessages[sourceUri]) {
|
|
printDiagnosticMessage(message, print);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool parseCommandLineDefines(
|
|
List<String> dFlags, Map<String, String> environmentDefines, String usage) {
|
|
for (final String dflag in dFlags) {
|
|
final equalsSignIndex = dflag.indexOf('=');
|
|
if (equalsSignIndex < 0) {
|
|
// Ignored.
|
|
} else if (equalsSignIndex > 0) {
|
|
final key = dflag.substring(0, equalsSignIndex);
|
|
final value = dflag.substring(equalsSignIndex + 1);
|
|
environmentDefines[key] = value;
|
|
} else {
|
|
print('The environment constant options must have a key (was: "$dflag")');
|
|
print(usage);
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|