5d8a9340ae
Change-Id: Iabcd0c79fb8f09c9de99856e50518eb7ace38464 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/163860 Commit-Queue: Johnni Winther <johnniwinther@google.com> Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
420 lines
16 KiB
Dart
420 lines
16 KiB
Dart
// Copyright (c) 2016, 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 kernel.target.targets;
|
|
|
|
import '../ast.dart';
|
|
import '../class_hierarchy.dart';
|
|
import '../core_types.dart';
|
|
import '../reference_from_index.dart';
|
|
import 'changed_structure_notifier.dart';
|
|
|
|
final List<String> targetNames = targets.keys.toList();
|
|
|
|
class TargetFlags {
|
|
final bool trackWidgetCreation;
|
|
final bool forceLateLoweringForTesting;
|
|
final bool forceLateLoweringSentinelForTesting;
|
|
final bool forceStaticFieldLoweringForTesting;
|
|
final bool forceNoExplicitGetterCallsForTesting;
|
|
final bool enableNullSafety;
|
|
|
|
const TargetFlags(
|
|
{this.trackWidgetCreation = false,
|
|
this.forceLateLoweringForTesting = false,
|
|
this.forceLateLoweringSentinelForTesting = false,
|
|
this.forceStaticFieldLoweringForTesting = false,
|
|
this.forceNoExplicitGetterCallsForTesting = false,
|
|
this.enableNullSafety = false});
|
|
|
|
bool operator ==(other) {
|
|
if (identical(this, other)) return true;
|
|
return other is TargetFlags &&
|
|
trackWidgetCreation == other.trackWidgetCreation &&
|
|
forceLateLoweringForTesting == other.forceLateLoweringForTesting &&
|
|
forceLateLoweringSentinelForTesting ==
|
|
other.forceLateLoweringSentinelForTesting &&
|
|
forceStaticFieldLoweringForTesting ==
|
|
other.forceStaticFieldLoweringForTesting &&
|
|
forceNoExplicitGetterCallsForTesting ==
|
|
other.forceNoExplicitGetterCallsForTesting &&
|
|
enableNullSafety == other.enableNullSafety;
|
|
}
|
|
|
|
int get hashCode {
|
|
int hash = 485786;
|
|
hash = 0x3fffffff & (hash * 31 + (hash ^ trackWidgetCreation.hashCode));
|
|
hash = 0x3fffffff &
|
|
(hash * 31 + (hash ^ forceLateLoweringForTesting.hashCode));
|
|
hash = 0x3fffffff &
|
|
(hash * 31 + (hash ^ forceLateLoweringSentinelForTesting.hashCode));
|
|
hash = 0x3fffffff &
|
|
(hash * 31 + (hash ^ forceStaticFieldLoweringForTesting.hashCode));
|
|
hash = 0x3fffffff &
|
|
(hash * 31 + (hash ^ forceNoExplicitGetterCallsForTesting.hashCode));
|
|
hash = 0x3fffffff & (hash * 31 + (hash ^ enableNullSafety.hashCode));
|
|
return hash;
|
|
}
|
|
}
|
|
|
|
typedef Target _TargetBuilder(TargetFlags flags);
|
|
|
|
final Map<String, _TargetBuilder> targets = <String, _TargetBuilder>{
|
|
'none': (TargetFlags flags) => new NoneTarget(flags),
|
|
};
|
|
|
|
Target getTarget(String name, TargetFlags flags) {
|
|
_TargetBuilder builder = targets[name];
|
|
if (builder == null) return null;
|
|
return builder(flags);
|
|
}
|
|
|
|
abstract class DiagnosticReporter<M, C> {
|
|
void report(M message, int charOffset, int length, Uri fileUri,
|
|
{List<C> context});
|
|
}
|
|
|
|
/// The different kinds of number semantics supported by the constant evaluator.
|
|
enum NumberSemantics {
|
|
/// Dart VM number semantics.
|
|
vm,
|
|
|
|
/// JavaScript (Dart2js and DDC) number semantics.
|
|
js,
|
|
}
|
|
|
|
// Backend specific constant evaluation behavior
|
|
class ConstantsBackend {
|
|
const ConstantsBackend();
|
|
|
|
/// Lowering of a list constant to a backend-specific representation.
|
|
Constant lowerListConstant(ListConstant constant) => constant;
|
|
|
|
/// Lowering of a set constant to a backend-specific representation.
|
|
Constant lowerSetConstant(SetConstant constant) => constant;
|
|
|
|
/// Lowering of a map constant to a backend-specific representation.
|
|
Constant lowerMapConstant(MapConstant constant) => constant;
|
|
|
|
/// Number semantics to use for this backend.
|
|
NumberSemantics get numberSemantics => NumberSemantics.vm;
|
|
|
|
/// Inline control of constant variables. The given constant expression
|
|
/// is the initializer of a [Field] or [VariableDeclaration] node.
|
|
/// If this method returns `true`, the variable will be inlined at all
|
|
/// points of reference and the variable itself removed (unless overridden
|
|
/// by the `keepFields` or `keepLocals` properties).
|
|
/// This method must be deterministic, i.e. it must always return the same
|
|
/// value for the same constant value and place in the AST.
|
|
bool shouldInlineConstant(ConstantExpression initializer) => true;
|
|
|
|
/// Whether this target supports unevaluated constants.
|
|
///
|
|
/// If not, then trying to perform constant evaluation without an environment
|
|
/// raises an exception.
|
|
///
|
|
/// This defaults to `false` since it requires additional work for a backend
|
|
/// to support unevaluated constants.
|
|
bool get supportsUnevaluatedConstants => false;
|
|
|
|
/// If `true` constant [Field] declarations are not removed from the AST even
|
|
/// when use-sites are inlined.
|
|
///
|
|
/// All use-sites will be rewritten based on [shouldInlineConstant].
|
|
bool get keepFields => true;
|
|
|
|
/// If `true` constant [VariableDeclaration]s are not removed from the AST
|
|
/// even when use-sites are inlined.
|
|
///
|
|
/// All use-sites will be rewritten based on [shouldInlineConstant].
|
|
bool get keepLocals => false;
|
|
}
|
|
|
|
/// A target provides backend-specific options for generating kernel IR.
|
|
abstract class Target {
|
|
TargetFlags get flags;
|
|
String get name;
|
|
|
|
/// A list of URIs of required libraries, not including dart:core.
|
|
///
|
|
/// Libraries will be loaded in order.
|
|
List<String> get extraRequiredLibraries => const <String>[];
|
|
|
|
/// A list of URIs of extra required libraries when compiling the platform.
|
|
///
|
|
/// Libraries will be loaded in order after the [extraRequiredLibraries]
|
|
/// above.
|
|
///
|
|
/// Normally not needed, but can be useful if removing libraries from the
|
|
/// [extraRequiredLibraries] list so libraries will still be available in the
|
|
/// platform if having a weird mix of current and not-quite-current as can
|
|
/// sometimes be the case.
|
|
List<String> get extraRequiredLibrariesPlatform => const <String>[];
|
|
|
|
/// A list of URIs of libraries to be indexed in the CoreTypes index, not
|
|
/// including dart:_internal, dart:async, dart:core and dart:mirrors.
|
|
List<String> get extraIndexedLibraries => const <String>[];
|
|
|
|
/// Additional declared variables implied by this target.
|
|
///
|
|
/// These can also be passed on the command-line of form `-D<name>=<value>`,
|
|
/// and those provided on the command-line take precedence over those defined
|
|
/// by the target.
|
|
Map<String, String> get extraDeclaredVariables => const <String, String>{};
|
|
|
|
/// Classes from the SDK whose interface is required for the modular
|
|
/// transformations.
|
|
Map<String, List<String>> get requiredSdkClasses => CoreTypes.requiredClasses;
|
|
|
|
/// A derived class may change this to `true` to enable forwarders to
|
|
/// user-defined `noSuchMethod` that are generated for each abstract member
|
|
/// if such `noSuchMethod` is present.
|
|
///
|
|
/// The forwarders are abstract [Procedure]s with [isNoSuchMethodForwarder]
|
|
/// bit set. The implementation of the behavior of such forwarders is up
|
|
/// for the target backend.
|
|
bool get enableNoSuchMethodForwarders => false;
|
|
|
|
/// A derived class may change this to `true` to enable Flutter specific
|
|
/// "super-mixins" semantics.
|
|
///
|
|
/// This semantics relaxes a number of constraint previously imposed on
|
|
/// mixins. Importantly it imposes the following change:
|
|
///
|
|
/// An abstract class may contain a member with a super-invocation that
|
|
/// corresponds to a member of the superclass interface, but where the
|
|
/// actual superclass does not declare or inherit a matching method.
|
|
/// Since no amount of overriding can change this property, such a class
|
|
/// cannot be extended to a class that is not abstract, it can only be
|
|
/// used to derive a mixin from.
|
|
///
|
|
/// See dartbug.com/31542 for details of the semantics.
|
|
bool get enableSuperMixins => false;
|
|
|
|
/// Perform target-specific transformations on the outlines stored in
|
|
/// [Component] when generating summaries.
|
|
///
|
|
/// This transformation is used to add metadata on outlines and to filter
|
|
/// unnecessary information before generating program summaries. This
|
|
/// transformation is not applied when compiling full kernel programs to
|
|
/// prevent affecting the internal invariants of the compiler and accidentally
|
|
/// slowing down compilation.
|
|
void performOutlineTransformations(Component component) {}
|
|
|
|
/// Perform target-specific transformations on the given libraries that must
|
|
/// run before constant evaluation.
|
|
void performPreConstantEvaluationTransformations(
|
|
Component component,
|
|
CoreTypes coreTypes,
|
|
List<Library> libraries,
|
|
DiagnosticReporter diagnosticReporter,
|
|
{void logger(String msg),
|
|
ChangedStructureNotifier changedStructureNotifier}) {}
|
|
|
|
/// Perform target-specific modular transformations on the given libraries.
|
|
void performModularTransformationsOnLibraries(
|
|
Component component,
|
|
CoreTypes coreTypes,
|
|
ClassHierarchy hierarchy,
|
|
List<Library> libraries,
|
|
// TODO(askesc): Consider how to generally pass compiler options to
|
|
// transformations.
|
|
Map<String, String> environmentDefines,
|
|
DiagnosticReporter diagnosticReporter,
|
|
ReferenceFromIndex referenceFromIndex,
|
|
{void logger(String msg),
|
|
ChangedStructureNotifier changedStructureNotifier});
|
|
|
|
/// Perform target-specific modular transformations on the given program.
|
|
///
|
|
/// This is used when an individual expression is compiled, e.g. for debugging
|
|
/// purposes. It is illegal to modify any of the enclosing nodes of the
|
|
/// procedure.
|
|
void performTransformationsOnProcedure(
|
|
CoreTypes coreTypes, ClassHierarchy hierarchy, Procedure procedure,
|
|
{void logger(String msg)}) {}
|
|
|
|
/// Whether a platform library may define a restricted type, such as `bool`,
|
|
/// `int`, `double`, `num`, and `String`.
|
|
///
|
|
/// By default only `dart:core` may define restricted types, but some target
|
|
/// implementations override this.
|
|
bool mayDefineRestrictedType(Uri uri) =>
|
|
uri.scheme == 'dart' && uri.path == 'core';
|
|
|
|
/// Whether a library is allowed to import a platform private library.
|
|
///
|
|
/// By default only `dart:*` libraries are allowed. May be overridden for
|
|
/// testing purposes.
|
|
bool allowPlatformPrivateLibraryAccess(Uri importer, Uri imported) =>
|
|
imported.scheme != "dart" ||
|
|
!imported.path.startsWith("_") ||
|
|
importer.scheme == "dart" ||
|
|
(importer.scheme == "package" &&
|
|
importer.path.startsWith("dart_internal/"));
|
|
|
|
/// Whether the `native` language extension is supported within [library].
|
|
///
|
|
/// The `native` language extension is not part of the language specification,
|
|
/// it means something else to each target, and it is enabled under different
|
|
/// circumstances for each target implementation. For example, the VM target
|
|
/// enables it everywhere because of existing support for "dart-ext:" native
|
|
/// extensions, but targets like dart2js only enable it on the core libraries.
|
|
bool enableNative(Uri uri) => false;
|
|
|
|
/// There are two variants of the `native` language extension. The VM expects
|
|
/// the native token to be followed by string, whereas dart2js and DDC do not.
|
|
// TODO(sigmund, ahe): ideally we should remove the `native` syntax, if not,
|
|
// we should at least unify the VM and non-VM variants.
|
|
bool get nativeExtensionExpectsString => false;
|
|
|
|
/// Whether integer literals that cannot be represented exactly on the web
|
|
/// (i.e. in Javascript) should cause an error to be issued.
|
|
/// An example of such a number is `2^53 + 1` where in Javascript - because
|
|
/// integers are represented as doubles
|
|
/// `Math.pow(2, 53) = Math.pow(2, 53) + 1`.
|
|
bool get errorOnUnexactWebIntLiterals => false;
|
|
|
|
/// Whether set literals are natively supported by this target. If set
|
|
/// literals are not supported by the target, they will be desugared into
|
|
/// explicit `Set` creation (for non-const set literals) or wrapped map
|
|
/// literals (for const set literals).
|
|
bool get supportsSetLiterals => true;
|
|
|
|
/// Whether late fields and variable are support by this target.
|
|
///
|
|
/// If `false`, late fields and variables are lowered fields, getter, setters
|
|
/// etc. that provide equivalent semantics. See `pkg/kernel/nnbd_api.md` for
|
|
/// details.
|
|
bool get supportsLateFields;
|
|
|
|
/// If `true`, the backend supports creation and checking of a sentinel value
|
|
/// for uninitialized late fields and variables through the `createSentinel`
|
|
/// and `isSentinel` methods in `dart:_internal`.
|
|
///
|
|
/// If `true` this is used when [supportsLateFields] is `false`.
|
|
bool get supportsLateLoweringSentinel;
|
|
|
|
/// Whether static fields with initializers in nnbd libraries should be
|
|
/// encoded using the late field lowering.
|
|
bool get useStaticFieldLowering;
|
|
|
|
/// Whether calls to getters and fields should be encoded as a .call
|
|
/// invocation on a property get.
|
|
///
|
|
/// If `false`, calls to getters and fields are encoded as method invocations
|
|
/// with the accessed getter or field as the interface target.
|
|
bool get supportsExplicitGetterCalls;
|
|
|
|
/// Builds an expression that instantiates an [Invocation] that can be passed
|
|
/// to [noSuchMethod].
|
|
Expression instantiateInvocation(CoreTypes coreTypes, Expression receiver,
|
|
String name, Arguments arguments, int offset, bool isSuper);
|
|
|
|
Expression instantiateNoSuchMethodError(CoreTypes coreTypes,
|
|
Expression receiver, String name, Arguments arguments, int offset,
|
|
{bool isMethod: false,
|
|
bool isGetter: false,
|
|
bool isSetter: false,
|
|
bool isField: false,
|
|
bool isLocalVariable: false,
|
|
bool isDynamic: false,
|
|
bool isSuper: false,
|
|
bool isStatic: false,
|
|
bool isConstructor: false,
|
|
bool isTopLevel: false});
|
|
|
|
/// Configure the given [Component] in a target specific way.
|
|
/// Returns the configured component.
|
|
Component configureComponent(Component component) => component;
|
|
|
|
// Configure environment defines in a target-specific way.
|
|
Map<String, String> updateEnvironmentDefines(Map<String, String> map) => map;
|
|
|
|
String toString() => 'Target($name)';
|
|
|
|
Class concreteListLiteralClass(CoreTypes coreTypes) => null;
|
|
Class concreteConstListLiteralClass(CoreTypes coreTypes) => null;
|
|
|
|
Class concreteMapLiteralClass(CoreTypes coreTypes) => null;
|
|
Class concreteConstMapLiteralClass(CoreTypes coreTypes) => null;
|
|
|
|
Class concreteIntLiteralClass(CoreTypes coreTypes, int value) => null;
|
|
Class concreteDoubleLiteralClass(CoreTypes coreTypes, double value) => null;
|
|
Class concreteStringLiteralClass(CoreTypes coreTypes, String value) => null;
|
|
|
|
ConstantsBackend constantsBackend(CoreTypes coreTypes);
|
|
}
|
|
|
|
class NoneConstantsBackend extends ConstantsBackend {
|
|
@override
|
|
final bool supportsUnevaluatedConstants;
|
|
|
|
const NoneConstantsBackend({this.supportsUnevaluatedConstants});
|
|
}
|
|
|
|
class NoneTarget extends Target {
|
|
final TargetFlags flags;
|
|
|
|
NoneTarget(this.flags);
|
|
|
|
@override
|
|
bool get supportsLateFields => !flags.forceLateLoweringForTesting;
|
|
|
|
@override
|
|
bool get supportsLateLoweringSentinel =>
|
|
flags.forceLateLoweringSentinelForTesting;
|
|
|
|
@override
|
|
bool get useStaticFieldLowering => flags.forceStaticFieldLoweringForTesting;
|
|
|
|
@override
|
|
bool get supportsExplicitGetterCalls =>
|
|
!flags.forceNoExplicitGetterCallsForTesting;
|
|
|
|
@override
|
|
String get name => 'none';
|
|
|
|
@override
|
|
List<String> get extraRequiredLibraries => <String>[];
|
|
|
|
@override
|
|
void performModularTransformationsOnLibraries(
|
|
Component component,
|
|
CoreTypes coreTypes,
|
|
ClassHierarchy hierarchy,
|
|
List<Library> libraries,
|
|
Map<String, String> environmentDefines,
|
|
DiagnosticReporter diagnosticReporter,
|
|
ReferenceFromIndex referenceFromIndex,
|
|
{void logger(String msg),
|
|
ChangedStructureNotifier changedStructureNotifier}) {}
|
|
|
|
@override
|
|
Expression instantiateInvocation(CoreTypes coreTypes, Expression receiver,
|
|
String name, Arguments arguments, int offset, bool isSuper) {
|
|
return new InvalidExpression(null);
|
|
}
|
|
|
|
@override
|
|
Expression instantiateNoSuchMethodError(CoreTypes coreTypes,
|
|
Expression receiver, String name, Arguments arguments, int offset,
|
|
{bool isMethod: false,
|
|
bool isGetter: false,
|
|
bool isSetter: false,
|
|
bool isField: false,
|
|
bool isLocalVariable: false,
|
|
bool isDynamic: false,
|
|
bool isSuper: false,
|
|
bool isStatic: false,
|
|
bool isConstructor: false,
|
|
bool isTopLevel: false}) {
|
|
return new InvalidExpression(null);
|
|
}
|
|
|
|
@override
|
|
ConstantsBackend constantsBackend(CoreTypes coreTypes) =>
|
|
// TODO(johnniwinther): Should this vary with the use case?
|
|
const NoneConstantsBackend(supportsUnevaluatedConstants: true);
|
|
}
|