| // 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. | |
| import 'package:kernel/ast.dart'; | |
| import 'package:kernel/class_hierarchy.dart'; | |
| import 'package:kernel/clone.dart'; | |
| import 'package:kernel/core_types.dart'; | |
| import 'package:kernel/reference_from_index.dart'; | |
| import 'package:kernel/target/changed_structure_notifier.dart'; | |
| import 'package:kernel/target/targets.dart'; | |
| import 'package:kernel/transformations/track_widget_constructor_locations.dart'; | |
| import '../transformations/call_site_annotator.dart' as callSiteAnnotator; | |
| import '../transformations/deeply_immutable.dart' as deeply_immutable; | |
| import '../transformations/lowering.dart' | |
| as lowering | |
| show transformLibraries, transformProcedure; | |
| import '../transformations/mixin_full_resolution.dart' | |
| as transformMixins | |
| show transformLibraries; | |
| import '../transformations/ffi/common.dart' | |
| as ffiHelper | |
| show calculateTransitiveImportsOfDartFfiIfUsed; | |
| import '../transformations/ffi/definitions.dart' | |
| as transformFfiDefinitions | |
| show transformLibraries; | |
| import '../transformations/ffi/native.dart' | |
| as transformFfiNative | |
| show transformLibraries; | |
| import '../transformations/ffi/use_sites.dart' | |
| as transformFfiUseSites | |
| show transformLibraries, transformProcedure; | |
| class VmTarget extends Target { | |
| final TargetFlags flags; | |
| Class? _growableList; | |
| Class? _immutableList; | |
| Class? _constMap; | |
| Class? _constSet; | |
| Class? _map; | |
| Class? _set; | |
| Class? _record; | |
| Class? _oneByteString; | |
| Class? _twoByteString; | |
| Class? _smi; | |
| Class? _double; // _Double, not double. | |
| Class? _symbol; | |
| Class? _closure; | |
| Class? _syncStarIterable; | |
| DartLibrarySupport? _dartLibrarySupport; | |
| VmTarget(this.flags); | |
| @override | |
| bool get enableNoSuchMethodForwarders => true; | |
| @override | |
| bool get supportsSetLiterals => false; | |
| @override | |
| bool get supportsFileUriExpression => true; | |
| @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.typedefs; | |
| @override | |
| String get name => 'vm'; | |
| // This is the order that bootstrap libraries are loaded according to | |
| // `runtime/vm/object_store.h`. | |
| @override | |
| List<String> get extraRequiredLibraries => const <String>[ | |
| 'dart:async', | |
| 'dart:collection', | |
| 'dart:_compact_hash', | |
| 'dart:concurrent', | |
| 'dart:convert', | |
| 'dart:developer', | |
| 'dart:ffi', | |
| 'dart:_internal', | |
| 'dart:isolate', | |
| 'dart:math', | |
| // The library dart:mirrors may be ignored by the VM, e.g. when built in | |
| // PRODUCT mode. | |
| 'dart:mirrors', | |
| 'dart:typed_data', | |
| 'dart:_vm', | |
| 'dart:vmservice_io', | |
| 'dart:_vmservice', | |
| 'dart:_builtin', | |
| 'dart:nativewrappers', | |
| 'dart:io', | |
| 'dart:cli', | |
| ]; | |
| @override | |
| bool mayDefineRestrictedType(Uri uri) => uri.isScheme('dart'); | |
| @override | |
| List<String> get extraRequiredLibrariesPlatform => const <String>[]; | |
| void _patchVmConstants(CoreTypes coreTypes) { | |
| // Fix Endian.host to be a const field equal to Endian.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.getField( | |
| 'dart:typed_data', | |
| 'Endian', | |
| 'host', | |
| ); | |
| final Field little = coreTypes.index.getField( | |
| 'dart:typed_data', | |
| 'Endian', | |
| 'little', | |
| ); | |
| host.isConst = true; | |
| host.initializer = new CloneVisitorNotMembers().clone(little.initializer!) | |
| ..parent = host; | |
| } | |
| late final WidgetCreatorTracker _widgetTracker = WidgetCreatorTracker(); | |
| @override | |
| void performPreConstantEvaluationTransformations( | |
| Component component, | |
| CoreTypes coreTypes, | |
| List<Library> libraries, | |
| DiagnosticReporter diagnosticReporter, { | |
| void Function(String msg)? logger, | |
| ChangedStructureNotifier? changedStructureNotifier, | |
| }) { | |
| super.performPreConstantEvaluationTransformations( | |
| component, | |
| coreTypes, | |
| libraries, | |
| diagnosticReporter, | |
| logger: logger, | |
| changedStructureNotifier: changedStructureNotifier, | |
| ); | |
| _patchVmConstants(coreTypes); | |
| if (flags.trackCreationLocations) { | |
| _widgetTracker.transform( | |
| libraries, | |
| component.libraries, | |
| changedStructureNotifier, | |
| ); | |
| } | |
| } | |
| @override | |
| List<String> get extraIndexedLibraries => const <String>[ | |
| "dart:_compact_hash", | |
| "dart:collection", | |
| // TODO(askesc): This is for the VM host endian optimization, which | |
| // could possibly be done more cleanly after the VM no longer supports | |
| // doing constant evaluation on its own. See http://dartbug.com/32836 | |
| "dart:typed_data", | |
| ]; | |
| @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, | |
| }) { | |
| transformMixins.transformLibraries( | |
| this, | |
| coreTypes, | |
| hierarchy, | |
| libraries, | |
| referenceFromIndex, | |
| ); | |
| logger?.call("Transformed mixin applications"); | |
| List<Library>? transitiveImportingDartFfi = ffiHelper | |
| .calculateTransitiveImportsOfDartFfiIfUsed(component, libraries); | |
| if (transitiveImportingDartFfi == null) { | |
| logger?.call("Skipped ffi transformation"); | |
| } else { | |
| transformFfiDefinitions.transformLibraries( | |
| component, | |
| coreTypes, | |
| hierarchy, | |
| transitiveImportingDartFfi, | |
| diagnosticReporter, | |
| referenceFromIndex, | |
| changedStructureNotifier, | |
| ); | |
| logger?.call("Transformed ffi definitions"); | |
| // Transform @Native(..) functions into FFI native call functions. | |
| // Pass instance method receivers as implicit first argument to the static | |
| // native function. | |
| // Transform arguments that extend NativeFieldWrapperClass1 to Pointer if | |
| // the native function expects Pointer (to avoid Handle overhead). | |
| transformFfiNative.transformLibraries( | |
| component, | |
| coreTypes, | |
| hierarchy, | |
| transitiveImportingDartFfi, | |
| diagnosticReporter, | |
| referenceFromIndex, | |
| ); | |
| logger?.call("Transformed ffi natives"); | |
| // The use sites transformer implements `Native.addressOf` by reading a | |
| // VM pragma attached to valid targets in the native transformer. Hence, | |
| // it can only run after `@Native` targets have been transformed. | |
| transformFfiUseSites.transformLibraries( | |
| this, | |
| component, | |
| coreTypes, | |
| hierarchy, | |
| transitiveImportingDartFfi, | |
| diagnosticReporter, | |
| referenceFromIndex, | |
| environmentDefines, | |
| ); | |
| logger?.call("Transformed ffi use sites"); | |
| } | |
| deeply_immutable.validateLibraries( | |
| component, | |
| libraries, | |
| coreTypes, | |
| diagnosticReporter, | |
| ); | |
| logger?.call("Validated deeply immutable"); | |
| bool productMode = environmentDefines!["dart.vm.product"] == "true"; | |
| lowering.transformLibraries( | |
| libraries, | |
| coreTypes, | |
| hierarchy, | |
| productMode: productMode, | |
| isClosureContextLoweringEnabled: flags.isClosureContextLoweringEnabled, | |
| ); | |
| logger?.call("Lowering transformations performed"); | |
| callSiteAnnotator.transformLibraries( | |
| component, | |
| libraries, | |
| coreTypes, | |
| hierarchy, | |
| ); | |
| logger?.call("Annotated call sites"); | |
| } | |
| @override | |
| void performTransformationsOnProcedure( | |
| CoreTypes coreTypes, | |
| ClassHierarchy hierarchy, | |
| Procedure procedure, | |
| Map<String, String>? environmentDefines, { | |
| void Function(String msg)? logger, | |
| required DiagnosticReporter diagnosticReporter, | |
| }) { | |
| final TreeNode? component = procedure.enclosingLibrary.parent; | |
| if (component is Component) { | |
| final List<Library>? transitiveImportingDartFfi = ffiHelper | |
| .calculateTransitiveImportsOfDartFfiIfUsed(component, [ | |
| procedure.enclosingLibrary, | |
| ]); | |
| if (transitiveImportingDartFfi != null) { | |
| transformFfiUseSites.transformProcedure( | |
| this, | |
| component, | |
| coreTypes, | |
| hierarchy, | |
| procedure, | |
| diagnosticReporter, | |
| null, | |
| environmentDefines, | |
| ); | |
| logger?.call("Transformed ffi use sites"); | |
| } | |
| } | |
| bool productMode = environmentDefines!["dart.vm.product"] == "true"; | |
| lowering.transformProcedure( | |
| procedure, | |
| coreTypes, | |
| hierarchy, | |
| productMode: productMode, | |
| isClosureContextLoweringEnabled: flags.isClosureContextLoweringEnabled, | |
| ); | |
| logger?.call("Lowering transformations performed"); | |
| } | |
| Expression _instantiateInvocationMirrorWithType( | |
| CoreTypes coreTypes, | |
| Expression receiver, | |
| String name, | |
| Arguments arguments, | |
| int offset, | |
| int type, | |
| ) { | |
| return new ConstructorInvocation( | |
| coreTypes.invocationMirrorWithTypeConstructor, | |
| new Arguments( | |
| ExpressionList( | |
| new SymbolLiteral(name)..fileOffset = offset, | |
| new IntLiteral(type)..fileOffset = offset, | |
| _fixedLengthList( | |
| coreTypes, | |
| coreTypes.typeNonNullableRawType, | |
| arguments.types.map<Expression>((t) => new TypeLiteral(t)).toList(), | |
| arguments.fileOffset, | |
| ), | |
| _fixedLengthList( | |
| coreTypes, | |
| const DynamicType(), | |
| arguments.positional, | |
| arguments.fileOffset, | |
| ), | |
| new StaticInvocation( | |
| coreTypes.mapUnmodifiable, | |
| new Arguments( | |
| ExpressionList( | |
| new MapLiteral( | |
| new List<MapLiteralEntry>.from( | |
| arguments.named.map((NamedExpression arg) { | |
| return new MapLiteralEntry( | |
| new SymbolLiteral(arg.name) | |
| ..fileOffset = arg.fileOffset, | |
| arg.value, | |
| )..fileOffset = arg.fileOffset; | |
| }), | |
| ), | |
| keyType: coreTypes.symbolNonNullableRawType, | |
| ) | |
| ..isConst = (arguments.named.isEmpty) | |
| ..fileOffset = arguments.fileOffset, | |
| ), | |
| types: DartTypeList( | |
| coreTypes.symbolNonNullableRawType, | |
| new DynamicType(), | |
| ), | |
| ), | |
| )..fileOffset = offset, | |
| ), | |
| ), | |
| ); | |
| } | |
| @override | |
| Expression instantiateInvocation( | |
| CoreTypes coreTypes, | |
| Expression receiver, | |
| String name, | |
| Arguments arguments, | |
| int offset, | |
| bool isSuper, | |
| ) { | |
| bool isGetter = false, isSetter = false, isMethod = false; | |
| if (name.startsWith("set:")) { | |
| isSetter = true; | |
| name = name.substring(4); | |
| } else if (name.startsWith("get:")) { | |
| isGetter = true; | |
| name = name.substring(4); | |
| } else { | |
| isMethod = true; | |
| } | |
| int type = _invocationType( | |
| isGetter: isGetter, | |
| isSetter: isSetter, | |
| isMethod: isMethod, | |
| isSuper: isSuper, | |
| ); | |
| return _instantiateInvocationMirrorWithType( | |
| coreTypes, | |
| receiver, | |
| name, | |
| arguments, | |
| offset, | |
| type, | |
| ); | |
| } | |
| int _invocationType({ | |
| bool isMethod = false, | |
| bool isGetter = false, | |
| bool isSetter = false, | |
| bool isSuper = false, | |
| }) { | |
| // This is copied from [_InvocationMirror]( | |
| // ../../../../../../runtime/lib/invocation_mirror_patch.dart). | |
| // Constants describing the invocation type. | |
| const int _METHOD = 0; | |
| const int _GETTER = 1; | |
| const int _SETTER = 2; | |
| const int _KIND_BITS = 3; | |
| // These values, except _SUPER, are only used when throwing | |
| // NoSuchMethodError for compile-time resolution failures. | |
| const int _SUPER = 1; | |
| const int _LEVEL_SHIFT = _KIND_BITS; | |
| int type = -1; | |
| // For convenience, [isGetter] and [isSetter] takes precedence over | |
| // [isMethod]. | |
| if (isGetter) { | |
| type = _GETTER; | |
| } else if (isSetter) { | |
| type = _SETTER; | |
| } else if (isMethod) { | |
| type = _METHOD; | |
| } | |
| if (isSuper) { | |
| type |= (_SUPER << _LEVEL_SHIFT); | |
| } | |
| return type; | |
| } | |
| Expression _fixedLengthList( | |
| CoreTypes coreTypes, | |
| DartType typeArgument, | |
| List<Expression> elements, | |
| int offset, | |
| ) { | |
| // TODO(ahe): It's possible that it would be better to create a fixed-length | |
| // list first, and then populate it. That would create fewer objects. But as | |
| // this is currently only used in (statically resolved) no-such-method | |
| // handling, the current approach seems sufficient. | |
| // The 0-element list must be exactly 'const[]'. | |
| if (elements.isEmpty) { | |
| return new ListLiteral(ExpressionList.empty, typeArgument: typeArgument) | |
| ..isConst = true; | |
| } | |
| return new StaticInvocation( | |
| coreTypes.listUnmodifiableConstructor, | |
| new Arguments( | |
| ExpressionList( | |
| new ListLiteral( | |
| new ExpressionList.from(elements), | |
| typeArgument: typeArgument, | |
| )..fileOffset = offset, | |
| ), | |
| types: DartTypeList(typeArgument), | |
| ), | |
| ); | |
| } | |
| // In addition to the default implementation, we allow VM tests to import | |
| // private platform libraries - such as `dart:_internal` - for testing | |
| // purposes. | |
| bool allowPlatformPrivateLibraryAccess(Uri importer, Uri imported) { | |
| if (super.allowPlatformPrivateLibraryAccess(importer, imported)) { | |
| return true; | |
| } | |
| final importerString = importer.toString(); | |
| // Allow dart-lang/sdk tests to import `dart:_*` libraries. | |
| if (importerString.contains('runtime/observatory/tests') || | |
| importerString.contains('runtime/tests/vm/dart') || | |
| importerString.contains('tests/standalone/io') || | |
| importerString.contains('test-lib') || | |
| importerString.contains('tests/ffi')) { | |
| return true; | |
| } | |
| // The `package:dart_runtime_service_vm` is a non-published package that | |
| // talks to VM internals. | |
| if (importerString.startsWith( | |
| 'package:dart_runtime_service_vm/src/native_bindings.dart', | |
| ) && | |
| imported.toString() == 'dart:_vmservice') { | |
| return true; | |
| } | |
| // Allow CFE to access VM internals for testing & verification purposes. | |
| if (importerString.startsWith('package:front_end') || | |
| importerString.startsWith('package:kernel')) { | |
| return true; | |
| } | |
| return false; | |
| } | |
| @override | |
| Component configureComponent(Component component) { | |
| callSiteAnnotator.addRepositoryTo(component); | |
| return super.configureComponent(component); | |
| } | |
| @override | |
| Class concreteListLiteralClass(CoreTypes coreTypes) { | |
| return _growableList ??= coreTypes.index.getClass( | |
| 'dart:core', | |
| '_GrowableList', | |
| ); | |
| } | |
| @override | |
| Class concreteConstListLiteralClass(CoreTypes coreTypes) { | |
| return _immutableList ??= coreTypes.index.getClass( | |
| 'dart:core', | |
| '_ImmutableList', | |
| ); | |
| } | |
| @override | |
| Class concreteMapLiteralClass(CoreTypes coreTypes) { | |
| return _map ??= coreTypes.index.getClass('dart:_compact_hash', '_Map'); | |
| } | |
| @override | |
| Class concreteConstMapLiteralClass(CoreTypes coreTypes) { | |
| return _constMap ??= coreTypes.index.getClass( | |
| 'dart:_compact_hash', | |
| '_ConstMap', | |
| ); | |
| } | |
| @override | |
| Class concreteSetLiteralClass(CoreTypes coreTypes) { | |
| return _set ??= coreTypes.index.getClass('dart:_compact_hash', '_Set'); | |
| } | |
| @override | |
| Class concreteConstSetLiteralClass(CoreTypes coreTypes) { | |
| return _constSet ??= coreTypes.index.getClass( | |
| 'dart:_compact_hash', | |
| '_ConstSet', | |
| ); | |
| } | |
| @override | |
| Class concreteConstSymbolLiteralClass(CoreTypes coreTypes) { | |
| return _symbol ??= coreTypes.index.getClass('dart:_internal', 'Symbol'); | |
| } | |
| @override | |
| Class getRecordImplementationClass( | |
| CoreTypes coreTypes, | |
| int numPositionalFields, | |
| List<String> namedFields, | |
| ) { | |
| return _record ??= coreTypes.index.getClass('dart:core', '_Record'); | |
| } | |
| @override | |
| Class? concreteIntLiteralClass(CoreTypes coreTypes, int value) { | |
| const int bitsPerInt32 = 32; | |
| const int smiBits32 = bitsPerInt32 - 2; | |
| const int smiMin32 = -(1 << smiBits32); | |
| const int smiMax32 = (1 << smiBits32) - 1; | |
| if ((smiMin32 <= value) && (value <= smiMax32)) { | |
| // Value fits into Smi on all platforms. | |
| return _smi ??= coreTypes.index.getClass('dart:core', '_Smi'); | |
| } | |
| // Otherwise, class could be either _Smi or _Mint depending on a platform. | |
| return null; | |
| } | |
| @override | |
| Class concreteDoubleLiteralClass(CoreTypes coreTypes, double value) { | |
| return _double ??= coreTypes.index.getClass('dart:core', '_Double'); | |
| } | |
| @override | |
| Class concreteStringLiteralClass(CoreTypes coreTypes, String value) { | |
| const int maxLatin1 = 0xff; | |
| for (int i = 0; i < value.length; ++i) { | |
| if (value.codeUnitAt(i) > maxLatin1) { | |
| return _twoByteString ??= coreTypes.index.getClass( | |
| 'dart:core', | |
| '_TwoByteString', | |
| ); | |
| } | |
| } | |
| return _oneByteString ??= coreTypes.index.getClass( | |
| 'dart:core', | |
| '_OneByteString', | |
| ); | |
| } | |
| @override | |
| Class concreteClosureClass(CoreTypes coreTypes) { | |
| return _closure ??= coreTypes.index.getClass('dart:core', '_Closure'); | |
| } | |
| @override | |
| Class? concreteAsyncResultClass(CoreTypes coreTypes) => | |
| coreTypes.futureImplClass; | |
| @override | |
| Class? concreteSyncStarResultClass(CoreTypes coreTypes) { | |
| return _syncStarIterable ??= coreTypes.index.getClass( | |
| 'dart:async', | |
| '_SyncStarIterable', | |
| ); | |
| } | |
| @override | |
| ConstantsBackend get constantsBackend => | |
| switch (flags.constKeepLocalsIndicator) { | |
| null => const ConstantsBackend(/* keeps defaults */), | |
| true => const ConstantsBackend(keepLocals: true), | |
| false => const ConstantsBackend(keepLocals: false), | |
| }; | |
| @override | |
| Map<String, String> updateEnvironmentDefines(Map<String, String> map) { | |
| // TODO(alexmarkov): Call this from the front-end in order to have | |
| // the same defines when compiling platform. | |
| map['dart.isVM'] = 'true'; | |
| return map; | |
| } | |
| @override | |
| DartLibrarySupport get dartLibrarySupport => | |
| _dartLibrarySupport ??= flags.supportMirrors && flags.supportFfi | |
| ? const DefaultDartLibrarySupport() | |
| : CustomizedDartLibrarySupport( | |
| unsupported: { | |
| if (!flags.supportMirrors) 'mirrors', | |
| if (!flags.supportFfi) 'ffi', | |
| }, | |
| ); | |
| @override | |
| bool isSupportedPragma(String pragmaName) => | |
| pragmaName.startsWith("vm:") || pragmaName.startsWith("dyn-module:"); | |
| } |