| // 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 enableNullSafety; |
| final bool supportMirrors; |
| |
| const TargetFlags( |
| {this.trackWidgetCreation = false, |
| this.enableNullSafety = false, |
| this.supportMirrors = true}); |
| |
| @override |
| bool operator ==(other) { |
| if (identical(this, other)) return true; |
| return other is TargetFlags && |
| trackWidgetCreation == other.trackWidgetCreation && |
| enableNullSafety == other.enableNullSafety && |
| supportMirrors == other.supportMirrors; |
| } |
| |
| @override |
| int get hashCode { |
| int hash = 485786; |
| hash = 0x3fffffff & (hash * 31 + (hash ^ trackWidgetCreation.hashCode)); |
| hash = 0x3fffffff & (hash * 31 + (hash ^ enableNullSafety.hashCode)); |
| hash = 0x3fffffff & (hash * 31 + (hash ^ supportMirrors.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; |
| Target target = builder(flags); |
| if (flags is TestTargetFlags) { |
| target = new TestTargetWrapper(target, flags); |
| } |
| return target; |
| } |
| |
| 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; |
| |
| /// Returns `true` if [constant] is lowered list constant created by |
| /// [lowerListConstant]. |
| bool isLoweredListConstant(Constant constant) => false; |
| |
| /// Calls `f` for each element in the lowered list [constant]. |
| /// |
| /// This assumes that `isLoweredListConstant(constant)` is true. |
| void forEachLoweredListConstantElement( |
| Constant constant, void Function(Constant element) f) {} |
| |
| /// Lowering of a set constant to a backend-specific representation. |
| Constant lowerSetConstant(SetConstant constant) => constant; |
| |
| /// Returns `true` if [constant] is lowered set constant created by |
| /// [lowerSetConstant]. |
| bool isLoweredSetConstant(Constant constant) => false; |
| |
| /// Calls `f` for each element in the lowered set [constant]. |
| /// |
| /// This assumes that `isLoweredSetConstant(constant)` is true. |
| void forEachLoweredSetConstantElement( |
| Constant constant, void Function(Constant element) f) {} |
| |
| /// Lowering of a map constant to a backend-specific representation. |
| Constant lowerMapConstant(MapConstant constant) => constant; |
| |
| /// Returns `true` if [constant] is lowered map constant created by |
| /// [lowerMapConstant]. |
| bool isLoweredMapConstant(Constant constant) => false; |
| |
| /// Calls `f` for each key/value pair in the lowered map [constant]. |
| /// |
| /// This assumes that `lowerMapConstant(constant)` is true. |
| void forEachLoweredMapConstantEntry( |
| Constant constant, void Function(Constant key, Constant value) f) {} |
| |
| /// Number semantics to use for this backend. |
| NumberSemantics get numberSemantics => NumberSemantics.vm; |
| |
| /// If true, all constants are inlined. Otherwise [shouldInlineConstant] is |
| /// called to determine whether a constant expression should be inlined. |
| bool get alwaysInlineConstants => true; |
| |
| /// 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. |
| /// |
| /// This is only called if [alwaysInlineConstants] is `true`. |
| bool shouldInlineConstant(ConstantExpression initializer) => |
| throw new UnsupportedError( |
| 'Per-value constant inlining is not supported'); |
| |
| /// 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; |
| } |
| |
| /// Interface used for determining whether a `dart:*` is considered supported |
| /// for the current target. |
| abstract class DartLibrarySupport { |
| /// Returns `true` if the 'dart:[libraryName]' library is supported. |
| /// |
| /// [isSupportedBySpec] is `true` if the dart library was supported by the |
| /// libraries specification. |
| /// |
| /// This is used to allow AOT to consider `dart:mirrors` as unsupported |
| /// despite it being supported in the platform dill, and dart2js to consider |
| /// `dart:_dart2js_runtime_metrics` to be supported despite it being an |
| /// internal library. |
| bool computeDartLibrarySupport(String libraryName, |
| {required bool isSupportedBySpec}); |
| |
| static const String dartLibraryPrefix = "dart.library."; |
| |
| static bool isDartLibraryQualifier(String dottedName) { |
| return dottedName.startsWith(dartLibraryPrefix); |
| } |
| |
| static String getDartLibraryName(String dottedName) { |
| assert(isDartLibraryQualifier(dottedName)); |
| return dottedName.substring(dartLibraryPrefix.length); |
| } |
| |
| /// Returns `"true"` if the "dart:[libraryName]" is supported and `""` |
| /// otherwise. |
| /// |
| /// This is used to determine conditional imports and `bool.fromEnvironment` |
| /// constant values for "dart.library.[libraryName]" values. |
| static String getDartLibrarySupportValue(String libraryName, |
| {required bool libraryExists, |
| required bool isSynthetic, |
| required bool isUnsupported, |
| required DartLibrarySupport dartLibrarySupport}) { |
| // A `dart:` library can be unsupported for several reasons: |
| // * If the library doesn't exist from source or from dill, it is not |
| // supported. |
| // * If the library has been synthesized, then it doesn't exist, but has |
| // been synthetically created due to an explicit import or export of it, |
| // in which case it is also not supported. |
| // * If the library is marked as not supported in the libraries |
| // specification, it does exist, but is not supported. |
| // * If the library is marked as supported in the libraries specification, |
| // it does exist and is potentially supported. Still the [Target] can |
| // consider it unsupported. This is for instance used to consider |
| // `dart:mirrors` as unsupported in AOT. The platform dill is shared with |
| // JIT, so the library exists and is marked as supported, but for AOT |
| // compilation it is still unsupported. |
| bool isSupported = libraryExists && !isSynthetic && !isUnsupported; |
| isSupported = dartLibrarySupport.computeDartLibrarySupport(libraryName, |
| isSupportedBySpec: isSupported); |
| return isSupported ? "true" : ""; |
| } |
| } |
| |
| /// [DartLibrarySupport] that only relies on the "supported" property of |
| /// the libraries specification. |
| class DefaultDartLibrarySupport implements DartLibrarySupport { |
| const DefaultDartLibrarySupport(); |
| |
| @override |
| bool computeDartLibrarySupport(String libraryName, |
| {required bool isSupportedBySpec}) => |
| isSupportedBySpec; |
| } |
| |
| /// [DartLibrarySupport] that supports overriding `dart:*` library support |
| /// otherwise defined by the libraries specification. |
| class CustomizedDartLibrarySupport implements DartLibrarySupport { |
| final Set<String> supported; |
| final Set<String> unsupported; |
| |
| const CustomizedDartLibrarySupport( |
| {this.supported: const {}, this.unsupported: const {}}); |
| |
| @override |
| bool computeDartLibrarySupport(String libraryName, |
| {required bool isSupportedBySpec}) { |
| if (supported.contains(libraryName)) { |
| return true; |
| } else if (unsupported.contains(libraryName)) { |
| return false; |
| } |
| return isSupportedBySpec; |
| } |
| } |
| |
| /// 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 Function(String msg)? logger, |
| 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 Function(String msg)? logger, |
| 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, |
| // TODO(askesc): Consider how to generally pass compiler options to |
| // transformations. |
| Map<String, String>? environmentDefines, |
| {void Function(String msg)? logger}) {} |
| |
| /// Whether a platform library may define a restricted type, such as `bool`, |
| /// `int`, `double`, `num`, and `String`. |
| /// |
| /// By default only `dart:core` and `dart:typed_data` may define restricted |
| /// types, but some target implementations override this. |
| bool mayDefineRestrictedType(Uri uri) => |
| uri.isScheme('dart') && (uri.path == 'core' || uri.path == 'typed_data'); |
| |
| /// Whether a library is allowed to import the platform private library |
| /// [imported] from library [importer]. |
| /// |
| /// By default only `dart:*` libraries are allowed. May be overridden for |
| /// testing purposes. |
| bool allowPlatformPrivateLibraryAccess(Uri importer, Uri imported) => |
| importer.isScheme("dart") || |
| (importer.isScheme("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; |
| |
| /// Bit mask of [LateLowering] values for the late lowerings that should |
| /// be performed by the CFE. |
| /// |
| /// For the selected lowerings, late fields and variables are encoded using |
| /// fields, getter, setters etc. in a way that provide equivalent semantics. |
| /// See `pkg/kernel/nnbd_api.md` for details. |
| int get enabledLateLowerings; |
| |
| /// Returns `true` if the CFE should lower a late field given it |
| /// [hasInitializer], [isFinal], and [isStatic]. |
| /// |
| /// This is determined by the [enabledLateLowerings] mask. |
| bool isLateFieldLoweringEnabled( |
| {required bool hasInitializer, |
| required bool isFinal, |
| required bool isStatic}) { |
| // ignore: unnecessary_null_comparison |
| assert(hasInitializer != null); |
| // ignore: unnecessary_null_comparison |
| assert(isFinal != null); |
| // ignore: unnecessary_null_comparison |
| assert(isStatic != null); |
| int mask = LateLowering.getFieldLowering( |
| hasInitializer: hasInitializer, isFinal: isFinal, isStatic: isStatic); |
| return enabledLateLowerings & mask != 0; |
| } |
| |
| /// Bit mask of [ConstructorTearOffLowering] values for the constructor tear |
| /// off lowerings that should be performed by the CFE. |
| /// |
| /// For the selected lowerings, constructor tear offs are encoded using |
| /// synthesized top level functions. |
| int get enabledConstructorTearOffLowerings; |
| |
| /// Returns `true` if lowering of generative constructor tear offs is enabled. |
| /// |
| /// This is determined by the [enabledConstructorTearOffLowerings] mask. |
| bool get isConstructorTearOffLoweringEnabled => |
| (enabledConstructorTearOffLowerings & |
| ConstructorTearOffLowering.constructors) != |
| 0; |
| |
| /// Returns `true` if lowering of non-redirecting factory tear offs is |
| /// enabled. |
| /// |
| /// This is determined by the [enabledConstructorTearOffLowerings] mask. |
| bool get isFactoryTearOffLoweringEnabled => |
| (enabledConstructorTearOffLowerings & |
| ConstructorTearOffLowering.factories) != |
| 0; |
| |
| /// Returns `true` if lowering of redirecting factory tear offs is enabled. |
| /// |
| /// This is determined by the [enabledConstructorTearOffLowerings] mask. |
| bool get isRedirectingFactoryTearOffLoweringEnabled => |
| (enabledConstructorTearOffLowerings & |
| ConstructorTearOffLowering.redirectingFactories) != |
| 0; |
| |
| /// Returns `true` if lowering of typedef tear offs is enabled. |
| /// |
| /// This is determined by the [enabledConstructorTearOffLowerings] mask. |
| bool get isTypedefTearOffLoweringEnabled => |
| (enabledConstructorTearOffLowerings & |
| ConstructorTearOffLowering.typedefs) != |
| 0; |
| |
| /// Returns `true` if the CFE should lower a late local variable given it |
| /// [hasInitializer], [isFinal], and its type [isPotentiallyNullable]. |
| /// |
| /// This is determined by the [enabledLateLowerings] mask. |
| bool isLateLocalLoweringEnabled( |
| {required bool hasInitializer, |
| required bool isFinal, |
| required bool isPotentiallyNullable}) { |
| // ignore: unnecessary_null_comparison |
| assert(hasInitializer != null); |
| // ignore: unnecessary_null_comparison |
| assert(isFinal != null); |
| // ignore: unnecessary_null_comparison |
| assert(isPotentiallyNullable != null); |
| int mask = LateLowering.getLocalLowering( |
| hasInitializer: hasInitializer, |
| isFinal: isFinal, |
| isPotentiallyNullable: isPotentiallyNullable); |
| return enabledLateLowerings & mask != 0; |
| } |
| |
| /// 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; |
| |
| @override |
| 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? concreteSetLiteralClass(CoreTypes coreTypes) => null; |
| Class? concreteConstSetLiteralClass(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 get constantsBackend; |
| |
| /// Returns an [DartLibrarySupport] the defines which, if any, of the |
| /// `dart:` libraries supported in the platform, that should not be |
| /// considered supported when queried in conditional imports and |
| /// `bool.fromEnvironment` constants. |
| /// |
| /// This is used treat `dart:mirrors` as unsupported in AOT but supported |
| /// in JIT. |
| DartLibrarySupport get dartLibrarySupport => |
| const DefaultDartLibrarySupport(); |
| |
| /// Should this target-specific pragma be recognized by annotation parsers? |
| bool isSupportedPragma(String pragmaName) => false; |
| } |
| |
| class NoneConstantsBackend extends ConstantsBackend { |
| @override |
| final bool supportsUnevaluatedConstants; |
| |
| const NoneConstantsBackend({required this.supportsUnevaluatedConstants}); |
| } |
| |
| class NoneTarget extends Target { |
| @override |
| final TargetFlags flags; |
| |
| NoneTarget(this.flags); |
| |
| @override |
| int get enabledLateLowerings => LateLowering.none; |
| |
| @override |
| bool get supportsLateLoweringSentinel => false; |
| |
| @override |
| bool get useStaticFieldLowering => false; |
| |
| @override |
| bool get supportsExplicitGetterCalls => true; |
| |
| @override |
| int get enabledConstructorTearOffLowerings => ConstructorTearOffLowering.none; |
| |
| @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 Function(String msg)? logger, |
| 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 get constantsBackend => |
| // TODO(johnniwinther): Should this vary with the use case? |
| const NoneConstantsBackend(supportsUnevaluatedConstants: true); |
| } |
| |
| class LateLowering { |
| static const int nullableUninitializedNonFinalLocal = 1 << 0; |
| static const int nonNullableUninitializedNonFinalLocal = 1 << 1; |
| static const int nullableUninitializedFinalLocal = 1 << 2; |
| static const int nonNullableUninitializedFinalLocal = 1 << 3; |
| static const int nullableInitializedNonFinalLocal = 1 << 4; |
| static const int nonNullableInitializedNonFinalLocal = 1 << 5; |
| static const int nullableInitializedFinalLocal = 1 << 6; |
| static const int nonNullableInitializedFinalLocal = 1 << 7; |
| static const int uninitializedNonFinalStaticField = 1 << 8; |
| static const int uninitializedFinalStaticField = 1 << 9; |
| static const int initializedNonFinalStaticField = 1 << 10; |
| static const int initializedFinalStaticField = 1 << 11; |
| static const int uninitializedNonFinalInstanceField = 1 << 12; |
| static const int uninitializedFinalInstanceField = 1 << 13; |
| static const int initializedNonFinalInstanceField = 1 << 14; |
| static const int initializedFinalInstanceField = 1 << 15; |
| |
| static const int none = 0; |
| static const int all = (1 << 16) - 1; |
| |
| static int getLocalLowering( |
| {required bool hasInitializer, |
| required bool isFinal, |
| required bool isPotentiallyNullable}) { |
| // ignore: unnecessary_null_comparison |
| assert(hasInitializer != null); |
| // ignore: unnecessary_null_comparison |
| assert(isFinal != null); |
| // ignore: unnecessary_null_comparison |
| assert(isPotentiallyNullable != null); |
| if (hasInitializer) { |
| if (isFinal) { |
| if (isPotentiallyNullable) { |
| return nullableInitializedFinalLocal; |
| } else { |
| return nonNullableInitializedFinalLocal; |
| } |
| } else { |
| if (isPotentiallyNullable) { |
| return nullableInitializedNonFinalLocal; |
| } else { |
| return nonNullableInitializedNonFinalLocal; |
| } |
| } |
| } else { |
| if (isFinal) { |
| if (isPotentiallyNullable) { |
| return nullableUninitializedFinalLocal; |
| } else { |
| return nonNullableUninitializedFinalLocal; |
| } |
| } else { |
| if (isPotentiallyNullable) { |
| return nullableUninitializedNonFinalLocal; |
| } else { |
| return nonNullableUninitializedNonFinalLocal; |
| } |
| } |
| } |
| } |
| |
| static int getFieldLowering( |
| {required bool hasInitializer, |
| required bool isFinal, |
| required bool isStatic}) { |
| // ignore: unnecessary_null_comparison |
| assert(hasInitializer != null); |
| // ignore: unnecessary_null_comparison |
| assert(isFinal != null); |
| // ignore: unnecessary_null_comparison |
| assert(isStatic != null); |
| if (hasInitializer) { |
| if (isFinal) { |
| if (isStatic) { |
| return initializedFinalStaticField; |
| } else { |
| return initializedFinalInstanceField; |
| } |
| } else { |
| if (isStatic) { |
| return initializedNonFinalStaticField; |
| } else { |
| return initializedNonFinalInstanceField; |
| } |
| } |
| } else { |
| if (isFinal) { |
| if (isStatic) { |
| return uninitializedFinalStaticField; |
| } else { |
| return uninitializedFinalInstanceField; |
| } |
| } else { |
| if (isStatic) { |
| return uninitializedNonFinalStaticField; |
| } else { |
| return uninitializedNonFinalInstanceField; |
| } |
| } |
| } |
| } |
| } |
| |
| class ConstructorTearOffLowering { |
| /// Create static functions to use as tear offs of generative constructors. |
| static const int constructors = 1 << 0; |
| |
| /// Create static functions to use as tear offs of non-redirecting factories. |
| static const int factories = 1 << 1; |
| |
| /// Create static functions to use as tear offs of redirecting factories. |
| static const int redirectingFactories = 1 << 2; |
| |
| /// Create top level functions to use as tear offs of typedefs that are not |
| /// proper renames. |
| static const int typedefs = 1 << 3; |
| |
| static const int none = 0; |
| static const int all = (1 << 4) - 1; |
| } |
| |
| class TestTargetFlags extends TargetFlags { |
| final int? forceLateLoweringsForTesting; |
| final bool? forceLateLoweringSentinelForTesting; |
| final bool? forceStaticFieldLoweringForTesting; |
| final bool? forceNoExplicitGetterCallsForTesting; |
| final int? forceConstructorTearOffLoweringForTesting; |
| final Set<String> supportedDartLibraries; |
| final Set<String> unsupportedDartLibraries; |
| |
| const TestTargetFlags( |
| {bool trackWidgetCreation = false, |
| this.forceLateLoweringsForTesting, |
| this.forceLateLoweringSentinelForTesting, |
| this.forceStaticFieldLoweringForTesting, |
| this.forceNoExplicitGetterCallsForTesting, |
| this.forceConstructorTearOffLoweringForTesting, |
| bool enableNullSafety = false, |
| this.supportedDartLibraries: const {}, |
| this.unsupportedDartLibraries: const {}}) |
| : super( |
| trackWidgetCreation: trackWidgetCreation, |
| enableNullSafety: enableNullSafety); |
| } |
| |
| mixin TestTargetMixin on Target { |
| @override |
| TestTargetFlags get flags; |
| |
| @override |
| int get enabledLateLowerings => |
| flags.forceLateLoweringsForTesting ?? super.enabledLateLowerings; |
| |
| @override |
| bool get supportsLateLoweringSentinel => |
| flags.forceLateLoweringSentinelForTesting ?? |
| super.supportsLateLoweringSentinel; |
| |
| @override |
| bool get useStaticFieldLowering => |
| flags.forceStaticFieldLoweringForTesting ?? super.useStaticFieldLowering; |
| |
| @override |
| bool get supportsExplicitGetterCalls => |
| flags.forceNoExplicitGetterCallsForTesting != null |
| ? !flags.forceNoExplicitGetterCallsForTesting! |
| : super.supportsExplicitGetterCalls; |
| |
| @override |
| int get enabledConstructorTearOffLowerings => |
| flags.forceConstructorTearOffLoweringForTesting ?? |
| super.enabledConstructorTearOffLowerings; |
| |
| @override |
| late final DartLibrarySupport dartLibrarySupport = |
| new TestDartLibrarySupport(super.dartLibrarySupport, flags); |
| } |
| |
| class TestDartLibrarySupport implements DartLibrarySupport { |
| final DartLibrarySupport delegate; |
| final TestTargetFlags flags; |
| |
| TestDartLibrarySupport(this.delegate, this.flags); |
| |
| @override |
| bool computeDartLibrarySupport(String libraryName, |
| {required bool isSupportedBySpec}) { |
| if (flags.supportedDartLibraries.contains(libraryName)) { |
| return true; |
| } else if (flags.unsupportedDartLibraries.contains(libraryName)) { |
| return false; |
| } |
| return delegate.computeDartLibrarySupport(libraryName, |
| isSupportedBySpec: isSupportedBySpec); |
| } |
| } |
| |
| class TargetWrapper extends Target { |
| final Target _target; |
| |
| TargetWrapper(this._target); |
| |
| @override |
| TargetFlags get flags => _target.flags; |
| |
| @override |
| int get enabledLateLowerings => _target.enabledLateLowerings; |
| |
| @override |
| bool get supportsLateLoweringSentinel => _target.supportsLateLoweringSentinel; |
| |
| @override |
| bool get useStaticFieldLowering => _target.useStaticFieldLowering; |
| |
| @override |
| bool get supportsExplicitGetterCalls => _target.supportsExplicitGetterCalls; |
| |
| @override |
| int get enabledConstructorTearOffLowerings => |
| _target.enabledConstructorTearOffLowerings; |
| |
| @override |
| bool allowPlatformPrivateLibraryAccess(Uri importer, Uri imported) { |
| return _target.allowPlatformPrivateLibraryAccess(importer, imported); |
| } |
| |
| @override |
| Class? concreteConstListLiteralClass(CoreTypes coreTypes) { |
| return _target.concreteConstListLiteralClass(coreTypes); |
| } |
| |
| @override |
| Class? concreteConstMapLiteralClass(CoreTypes coreTypes) { |
| return _target.concreteConstMapLiteralClass(coreTypes); |
| } |
| |
| @override |
| Class? concreteDoubleLiteralClass(CoreTypes coreTypes, double value) { |
| return _target.concreteDoubleLiteralClass(coreTypes, value); |
| } |
| |
| @override |
| Class? concreteIntLiteralClass(CoreTypes coreTypes, int value) { |
| return _target.concreteIntLiteralClass(coreTypes, value); |
| } |
| |
| @override |
| Class? concreteListLiteralClass(CoreTypes coreTypes) { |
| return _target.concreteListLiteralClass(coreTypes); |
| } |
| |
| @override |
| Class? concreteMapLiteralClass(CoreTypes coreTypes) { |
| return _target.concreteMapLiteralClass(coreTypes); |
| } |
| |
| @override |
| Class? concreteStringLiteralClass(CoreTypes coreTypes, String value) { |
| return _target.concreteStringLiteralClass(coreTypes, value); |
| } |
| |
| @override |
| Component configureComponent(Component component) { |
| return _target.configureComponent(component); |
| } |
| |
| @override |
| ConstantsBackend get constantsBackend => _target.constantsBackend; |
| |
| @override |
| bool enableNative(Uri uri) { |
| return _target.enableNative(uri); |
| } |
| |
| @override |
| bool get enableNoSuchMethodForwarders => _target.enableNoSuchMethodForwarders; |
| |
| @override |
| bool get enableSuperMixins => _target.enableSuperMixins; |
| |
| @override |
| bool get errorOnUnexactWebIntLiterals => _target.errorOnUnexactWebIntLiterals; |
| |
| @override |
| Map<String, String> get extraDeclaredVariables => |
| _target.extraDeclaredVariables; |
| |
| @override |
| List<String> get extraIndexedLibraries => _target.extraIndexedLibraries; |
| |
| @override |
| List<String> get extraRequiredLibraries => _target.extraRequiredLibraries; |
| |
| @override |
| List<String> get extraRequiredLibrariesPlatform => |
| _target.extraRequiredLibrariesPlatform; |
| |
| @override |
| Expression instantiateInvocation(CoreTypes coreTypes, Expression receiver, |
| String name, Arguments arguments, int offset, bool isSuper) { |
| return _target.instantiateInvocation( |
| coreTypes, receiver, name, arguments, offset, isSuper); |
| } |
| |
| @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 _target.instantiateNoSuchMethodError( |
| coreTypes, receiver, name, arguments, offset, |
| isMethod: isMethod, |
| isGetter: isGetter, |
| isSetter: isSetter, |
| isField: isField, |
| isLocalVariable: isLocalVariable, |
| isDynamic: isDynamic, |
| isSuper: isSuper, |
| isStatic: isStatic, |
| isConstructor: isConstructor, |
| isTopLevel: isTopLevel); |
| } |
| |
| @override |
| bool mayDefineRestrictedType(Uri uri) { |
| return _target.mayDefineRestrictedType(uri); |
| } |
| |
| @override |
| String get name => _target.name; |
| |
| @override |
| bool get nativeExtensionExpectsString => _target.nativeExtensionExpectsString; |
| |
| @override |
| void performModularTransformationsOnLibraries( |
| Component component, |
| CoreTypes coreTypes, |
| ClassHierarchy hierarchy, |
| List<Library> libraries, |
| Map<String, String>? environmentDefines, |
| DiagnosticReporter diagnosticReporter, |
| ReferenceFromIndex? referenceFromIndex, |
| {void Function(String msg)? logger, |
| ChangedStructureNotifier? changedStructureNotifier}) { |
| _target.performModularTransformationsOnLibraries( |
| component, |
| coreTypes, |
| hierarchy, |
| libraries, |
| environmentDefines, |
| diagnosticReporter, |
| referenceFromIndex, |
| logger: logger, |
| changedStructureNotifier: changedStructureNotifier); |
| } |
| |
| @override |
| void performOutlineTransformations(Component component) { |
| _target.performOutlineTransformations(component); |
| } |
| |
| @override |
| void performPreConstantEvaluationTransformations( |
| Component component, |
| CoreTypes coreTypes, |
| List<Library> libraries, |
| DiagnosticReporter diagnosticReporter, |
| {void Function(String msg)? logger, |
| ChangedStructureNotifier? changedStructureNotifier}) { |
| _target.performPreConstantEvaluationTransformations( |
| component, coreTypes, libraries, diagnosticReporter, |
| logger: logger, changedStructureNotifier: changedStructureNotifier); |
| } |
| |
| @override |
| void performTransformationsOnProcedure( |
| CoreTypes coreTypes, |
| ClassHierarchy hierarchy, |
| Procedure procedure, |
| Map<String, String>? environmentDefines, |
| {void Function(String msg)? logger}) { |
| _target.performTransformationsOnProcedure( |
| coreTypes, hierarchy, procedure, environmentDefines, |
| logger: logger); |
| } |
| |
| @override |
| Map<String, List<String>> get requiredSdkClasses => |
| _target.requiredSdkClasses; |
| |
| @override |
| bool get supportsSetLiterals => _target.supportsSetLiterals; |
| |
| @override |
| Map<String, String> updateEnvironmentDefines(Map<String, String> map) { |
| return _target.updateEnvironmentDefines(map); |
| } |
| } |
| |
| class TestTargetWrapper extends TargetWrapper with TestTargetMixin { |
| @override |
| final TestTargetFlags flags; |
| |
| TestTargetWrapper(Target target, this.flags) : super(target); |
| } |
| |
| /// Extends a Target to transform outlines to meet the requirements |
| /// of summaries in bazel and package-build. |
| /// |
| /// Build systems like package-build may provide the same input file twice to |
| /// the summary worker, but only intends to have it in one output summary. The |
| /// convention is that if it is listed as a source, it is intended to be part of |
| /// the output, if the source file was loaded as a dependency, then it was |
| /// already included in a different summary. The transformation below ensures |
| /// that the output summary doesn't include those implicit inputs. |
| /// |
| /// Note: this transformation is destructive and is only intended to be used |
| /// when generating summaries. |
| mixin SummaryMixin on Target { |
| List<Uri> get sources; |
| bool get excludeNonSources; |
| |
| @override |
| void performOutlineTransformations(Component component) { |
| super.performOutlineTransformations(component); |
| if (!excludeNonSources) return; |
| |
| List<Library> libraries = new List.of(component.libraries); |
| component.libraries.clear(); |
| Set<Uri> include = sources.toSet(); |
| for (Library library in libraries) { |
| if (include.contains(library.importUri)) { |
| component.libraries.add(library); |
| } else { |
| // Excluding the library also means that their canonical names will not |
| // be computed as part of serialization, so we need to do that |
| // preemptively here to avoid errors when serializing references to |
| // elements of these libraries. |
| component.root |
| .getChildFromUri(library.importUri) |
| .bindTo(library.reference); |
| library.computeCanonicalNames(); |
| } |
| } |
| } |
| } |