| // 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; |
| |
| /// 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 dropAST: 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, 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); |
| } |
| } |
| |
| /// 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; |
| } |