| // Copyright (c) 2019, 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. |
| |
| // This file contains logic which is shared between the ffi_definition and |
| // ffi_use_site transformers. |
| |
| library vm.transformations.ffi; |
| |
| import 'package:kernel/ast.dart' hide MapEntry; |
| import 'package:kernel/class_hierarchy.dart' show ClassHierarchy; |
| import 'package:kernel/core_types.dart'; |
| import 'package:kernel/library_index.dart' show LibraryIndex; |
| import 'package:kernel/reference_from_index.dart'; |
| import 'package:kernel/target/targets.dart' show DiagnosticReporter; |
| import 'package:kernel/type_environment.dart' |
| show TypeEnvironment, SubtypeCheckMode; |
| |
| /// Represents the (instantiated) ffi.NativeType. |
| enum NativeType { |
| kNativeType, |
| kNativeInteger, |
| kNativeDouble, |
| kPointer, |
| kNativeFunction, |
| kInt8, |
| kInt16, |
| kInt32, |
| kInt64, |
| kUint8, |
| kUint16, |
| kUint32, |
| kUnit64, |
| kIntptr, |
| kFloat, |
| kDouble, |
| kVoid, |
| kOpaque, |
| kStruct, |
| kHandle, |
| } |
| |
| const NativeType kNativeTypeIntStart = NativeType.kInt8; |
| const NativeType kNativeTypeIntEnd = NativeType.kIntptr; |
| |
| /// The [NativeType] class names, indexed by [NativeType]. |
| const List<String> nativeTypeClassNames = [ |
| 'NativeType', |
| '_NativeInteger', |
| '_NativeDouble', |
| 'Pointer', |
| 'NativeFunction', |
| 'Int8', |
| 'Int16', |
| 'Int32', |
| 'Int64', |
| 'Uint8', |
| 'Uint16', |
| 'Uint32', |
| 'Uint64', |
| 'IntPtr', |
| 'Float', |
| 'Double', |
| 'Void', |
| 'Opaque', |
| 'Struct', |
| 'Handle' |
| ]; |
| |
| const int UNKNOWN = 0; |
| const int WORD_SIZE = -1; |
| |
| /// The [NativeType] sizes in bytes, indexed by [NativeType]. |
| const List<int> nativeTypeSizes = [ |
| UNKNOWN, // NativeType |
| UNKNOWN, // NativeInteger |
| UNKNOWN, // NativeDouble |
| WORD_SIZE, // Pointer |
| UNKNOWN, // NativeFunction |
| 1, // Int8 |
| 2, // Int16 |
| 4, // Int32 |
| 8, // Int64 |
| 1, // Uint8 |
| 2, // Uint16 |
| 4, // Uint32 |
| 8, // Uint64 |
| WORD_SIZE, // IntPtr |
| 4, // Float |
| 8, // Double |
| UNKNOWN, // Void |
| UNKNOWN, // Opaque |
| UNKNOWN, // Struct |
| WORD_SIZE, // Handle |
| ]; |
| |
| /// The struct layout in various ABIs. |
| /// |
| /// ABIs differ per architectures and with different compilers. |
| /// We pick the default struct layout based on the architecture and OS. |
| /// |
| /// Compilers _can_ deviate from the default layout, but this prevents |
| /// executables from making system calls. So this seems rather uncommon. |
| /// |
| /// In the future, we might support custom struct layouts. For more info see |
| /// https://github.com/dart-lang/sdk/issues/35768. |
| enum Abi { |
| /// Layout in all 64bit ABIs (x64 and arm64). |
| wordSize64, |
| |
| /// Layout in System V ABI for x386 (ia32 on Linux) and in iOS Arm 32 bit. |
| wordSize32Align32, |
| |
| /// Layout in both the Arm 32 bit ABI and the Windows ia32 ABI. |
| wordSize32Align64, |
| } |
| |
| /// WORD_SIZE in bytes. |
| const wordSize = <Abi, int>{ |
| Abi.wordSize64: 8, |
| Abi.wordSize32Align32: 4, |
| Abi.wordSize32Align64: 4, |
| }; |
| |
| /// Elements that are not aligned to their size. |
| /// |
| /// Has an entry for all Abis. Empty entries document that every native |
| /// type is aligned to it's own size in this ABI. |
| /// |
| /// See runtime/vm/ffi/abi.cc for asserts in the VM that verify these |
| /// alignments. |
| /// |
| /// TODO(37470): Add uncommon primitive data types when we want to support them. |
| const nonSizeAlignment = <Abi, Map<NativeType, int>>{ |
| Abi.wordSize64: {}, |
| |
| // x86 System V ABI: |
| // > uint64_t | size 8 | alignment 4 |
| // > double | size 8 | alignment 4 |
| // https://github.com/hjl-tools/x86-psABI/wiki/intel386-psABI-1.1.pdf page 8. |
| // |
| // iOS 32 bit alignment: |
| // https://developer.apple.com/documentation/uikit/app_and_environment/updating_your_app_from_32-bit_to_64-bit_architecture/updating_data_structures |
| Abi.wordSize32Align32: { |
| NativeType.kDouble: 4, |
| NativeType.kInt64: 4, |
| NativeType.kUnit64: 4 |
| }, |
| |
| // The default for MSVC x86: |
| // > The alignment-requirement for all data except structures, unions, and |
| // > arrays is either the size of the object or the current packing size |
| // > (specified with either /Zp or the pack pragma, whichever is less). |
| // https://docs.microsoft.com/en-us/cpp/c-language/padding-and-alignment-of-structure-members?view=vs-2019 |
| // |
| // GCC _can_ compile on Linux to this alignment with -malign-double, but does |
| // not do so by default: |
| // > Warning: if you use the -malign-double switch, structures containing the |
| // > above types are aligned differently than the published application |
| // > binary interface specifications for the x86-32 and are not binary |
| // > compatible with structures in code compiled without that switch. |
| // https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html |
| // |
| // Arm always requires 8 byte alignment for 8 byte values: |
| // http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042d/IHI0042D_aapcs.pdf 4.1 Fundamental Data Types |
| Abi.wordSize32Align64: {}, |
| }; |
| |
| /// Load, store, and elementAt are rewired to their static type for these types. |
| const List<NativeType> optimizedTypes = [ |
| NativeType.kInt8, |
| NativeType.kInt16, |
| NativeType.kInt32, |
| NativeType.kInt64, |
| NativeType.kUint8, |
| NativeType.kUint16, |
| NativeType.kUint32, |
| NativeType.kUnit64, |
| NativeType.kIntptr, |
| NativeType.kFloat, |
| NativeType.kDouble, |
| NativeType.kPointer, |
| ]; |
| |
| const List<NativeType> unalignedLoadsStores = [ |
| NativeType.kFloat, |
| NativeType.kDouble, |
| ]; |
| |
| /// [FfiTransformer] contains logic which is shared between |
| /// _FfiUseSiteTransformer and _FfiDefinitionTransformer. |
| class FfiTransformer extends Transformer { |
| final TypeEnvironment env; |
| final CoreTypes coreTypes; |
| final LibraryIndex index; |
| final ClassHierarchy hierarchy; |
| final DiagnosticReporter diagnosticReporter; |
| final ReferenceFromIndex referenceFromIndex; |
| |
| final Class objectClass; |
| final Class intClass; |
| final Class doubleClass; |
| final Class listClass; |
| final Class typeClass; |
| final Procedure unsafeCastMethod; |
| final Class typedDataClass; |
| final Procedure typedDataBufferGetter; |
| final Procedure typedDataOffsetInBytesGetter; |
| final Procedure byteBufferAsUint8List; |
| final Class pragmaClass; |
| final Field pragmaName; |
| final Field pragmaOptions; |
| final Procedure listElementAt; |
| final Procedure numAddition; |
| final Procedure numMultiplication; |
| |
| final Library ffiLibrary; |
| final Class allocatorClass; |
| final Class nativeFunctionClass; |
| final Class opaqueClass; |
| final Class arrayClass; |
| final Class arraySizeClass; |
| final Field arraySizeDimension1Field; |
| final Field arraySizeDimension2Field; |
| final Field arraySizeDimension3Field; |
| final Field arraySizeDimension4Field; |
| final Field arraySizeDimension5Field; |
| final Field arraySizeDimensionsField; |
| final Class pointerClass; |
| final Class structClass; |
| final Class ffiStructLayoutClass; |
| final Field ffiStructLayoutTypesField; |
| final Field ffiStructLayoutPackingField; |
| final Class ffiInlineArrayClass; |
| final Field ffiInlineArrayElementTypeField; |
| final Field ffiInlineArrayLengthField; |
| final Class packedClass; |
| final Field packedMemberAlignmentField; |
| final Procedure allocateMethod; |
| final Procedure allocatorAllocateMethod; |
| final Procedure castMethod; |
| final Procedure offsetByMethod; |
| final Procedure elementAtMethod; |
| final Procedure addressGetter; |
| final Procedure structPointerRef; |
| final Procedure structPointerElemAt; |
| final Procedure structArrayElemAt; |
| final Procedure arrayArrayElemAt; |
| final Procedure arrayArrayAssignAt; |
| final Procedure asFunctionMethod; |
| final Procedure asFunctionInternal; |
| final Procedure sizeOfMethod; |
| final Procedure lookupFunctionMethod; |
| final Procedure fromFunctionMethod; |
| final Field addressOfField; |
| final Field arrayTypedDataBaseField; |
| final Field arraySizeField; |
| final Field arrayNestedDimensionsField; |
| final Procedure arrayCheckIndex; |
| final Field arrayNestedDimensionsFlattened; |
| final Field arrayNestedDimensionsFirst; |
| final Field arrayNestedDimensionsRest; |
| final Constructor structFromPointer; |
| final Constructor arrayConstructor; |
| final Procedure fromAddressInternal; |
| final Procedure libraryLookupMethod; |
| final Procedure abiMethod; |
| final Procedure pointerFromFunctionProcedure; |
| final Procedure nativeCallbackFunctionProcedure; |
| final Map<NativeType, Procedure> loadMethods; |
| final Map<NativeType, Procedure> loadUnalignedMethods; |
| final Map<NativeType, Procedure> storeMethods; |
| final Map<NativeType, Procedure> storeUnalignedMethods; |
| final Map<NativeType, Procedure> elementAtMethods; |
| final Procedure memCopy; |
| final Procedure allocationTearoff; |
| final Procedure asFunctionTearoff; |
| final Procedure lookupFunctionTearoff; |
| |
| /// Classes corresponding to [NativeType], indexed by [NativeType]. |
| final List<Class> nativeTypesClasses; |
| |
| Library currentLibrary; |
| IndexedLibrary currentLibraryIndex; |
| |
| FfiTransformer(this.index, this.coreTypes, this.hierarchy, |
| this.diagnosticReporter, this.referenceFromIndex) |
| : env = new TypeEnvironment(coreTypes, hierarchy), |
| objectClass = coreTypes.objectClass, |
| intClass = coreTypes.intClass, |
| doubleClass = coreTypes.doubleClass, |
| listClass = coreTypes.listClass, |
| typeClass = coreTypes.typeClass, |
| unsafeCastMethod = |
| index.getTopLevelMember('dart:_internal', 'unsafeCast'), |
| typedDataClass = index.getClass('dart:typed_data', 'TypedData'), |
| typedDataBufferGetter = |
| index.getMember('dart:typed_data', 'TypedData', 'get:buffer'), |
| typedDataOffsetInBytesGetter = index.getMember( |
| 'dart:typed_data', 'TypedData', 'get:offsetInBytes'), |
| byteBufferAsUint8List = |
| index.getMember('dart:typed_data', 'ByteBuffer', 'asUint8List'), |
| pragmaClass = coreTypes.pragmaClass, |
| pragmaName = coreTypes.pragmaName, |
| pragmaOptions = coreTypes.pragmaOptions, |
| listElementAt = coreTypes.index.getMember('dart:core', 'List', '[]'), |
| numAddition = coreTypes.index.getMember('dart:core', 'num', '+'), |
| numMultiplication = coreTypes.index.getMember('dart:core', 'num', '*'), |
| ffiLibrary = index.getLibrary('dart:ffi'), |
| allocatorClass = index.getClass('dart:ffi', 'Allocator'), |
| nativeFunctionClass = index.getClass('dart:ffi', 'NativeFunction'), |
| opaqueClass = index.getClass('dart:ffi', 'Opaque'), |
| arrayClass = index.getClass('dart:ffi', 'Array'), |
| arraySizeClass = index.getClass('dart:ffi', '_ArraySize'), |
| arraySizeDimension1Field = |
| index.getMember('dart:ffi', '_ArraySize', 'dimension1'), |
| arraySizeDimension2Field = |
| index.getMember('dart:ffi', '_ArraySize', 'dimension2'), |
| arraySizeDimension3Field = |
| index.getMember('dart:ffi', '_ArraySize', 'dimension3'), |
| arraySizeDimension4Field = |
| index.getMember('dart:ffi', '_ArraySize', 'dimension4'), |
| arraySizeDimension5Field = |
| index.getMember('dart:ffi', '_ArraySize', 'dimension5'), |
| arraySizeDimensionsField = |
| index.getMember('dart:ffi', '_ArraySize', 'dimensions'), |
| pointerClass = index.getClass('dart:ffi', 'Pointer'), |
| structClass = index.getClass('dart:ffi', 'Struct'), |
| ffiStructLayoutClass = index.getClass('dart:ffi', '_FfiStructLayout'), |
| ffiStructLayoutTypesField = |
| index.getMember('dart:ffi', '_FfiStructLayout', 'fieldTypes'), |
| ffiStructLayoutPackingField = |
| index.getMember('dart:ffi', '_FfiStructLayout', 'packing'), |
| ffiInlineArrayClass = index.getClass('dart:ffi', '_FfiInlineArray'), |
| ffiInlineArrayElementTypeField = |
| index.getMember('dart:ffi', '_FfiInlineArray', 'elementType'), |
| ffiInlineArrayLengthField = |
| index.getMember('dart:ffi', '_FfiInlineArray', 'length'), |
| packedClass = index.getClass('dart:ffi', 'Packed'), |
| packedMemberAlignmentField = |
| index.getMember('dart:ffi', 'Packed', 'memberAlignment'), |
| allocateMethod = index.getMember('dart:ffi', 'AllocatorAlloc', 'call'), |
| allocatorAllocateMethod = |
| index.getMember('dart:ffi', 'Allocator', 'allocate'), |
| castMethod = index.getMember('dart:ffi', 'Pointer', 'cast'), |
| offsetByMethod = index.getMember('dart:ffi', 'Pointer', '_offsetBy'), |
| elementAtMethod = index.getMember('dart:ffi', 'Pointer', 'elementAt'), |
| addressGetter = index.getMember('dart:ffi', 'Pointer', 'get:address'), |
| addressOfField = index.getMember('dart:ffi', 'Struct', '_addressOf'), |
| arrayTypedDataBaseField = |
| index.getMember('dart:ffi', 'Array', '_typedDataBase'), |
| arraySizeField = index.getMember('dart:ffi', 'Array', '_size'), |
| arrayNestedDimensionsField = |
| index.getMember('dart:ffi', 'Array', '_nestedDimensions'), |
| arrayCheckIndex = index.getMember('dart:ffi', 'Array', '_checkIndex'), |
| arrayNestedDimensionsFlattened = |
| index.getMember('dart:ffi', 'Array', '_nestedDimensionsFlattened'), |
| arrayNestedDimensionsFirst = |
| index.getMember('dart:ffi', 'Array', '_nestedDimensionsFirst'), |
| arrayNestedDimensionsRest = |
| index.getMember('dart:ffi', 'Array', '_nestedDimensionsRest'), |
| structFromPointer = |
| index.getMember('dart:ffi', 'Struct', '_fromPointer'), |
| arrayConstructor = index.getMember('dart:ffi', 'Array', '_'), |
| fromAddressInternal = |
| index.getTopLevelMember('dart:ffi', '_fromAddress'), |
| structPointerRef = |
| index.getMember('dart:ffi', 'StructPointer', 'get:ref'), |
| structPointerElemAt = |
| index.getMember('dart:ffi', 'StructPointer', '[]'), |
| structArrayElemAt = index.getMember('dart:ffi', 'StructArray', '[]'), |
| arrayArrayElemAt = index.getMember('dart:ffi', 'ArrayArray', '[]'), |
| arrayArrayAssignAt = index.getMember('dart:ffi', 'ArrayArray', '[]='), |
| asFunctionMethod = |
| index.getMember('dart:ffi', 'NativeFunctionPointer', 'asFunction'), |
| asFunctionInternal = |
| index.getTopLevelMember('dart:ffi', '_asFunctionInternal'), |
| sizeOfMethod = index.getTopLevelMember('dart:ffi', 'sizeOf'), |
| lookupFunctionMethod = index.getMember( |
| 'dart:ffi', 'DynamicLibraryExtension', 'lookupFunction'), |
| fromFunctionMethod = |
| index.getMember('dart:ffi', 'Pointer', 'fromFunction'), |
| libraryLookupMethod = |
| index.getMember('dart:ffi', 'DynamicLibrary', 'lookup'), |
| abiMethod = index.getTopLevelMember('dart:ffi', '_abi'), |
| pointerFromFunctionProcedure = |
| index.getTopLevelMember('dart:ffi', '_pointerFromFunction'), |
| nativeCallbackFunctionProcedure = |
| index.getTopLevelMember('dart:ffi', '_nativeCallbackFunction'), |
| nativeTypesClasses = nativeTypeClassNames |
| .map((name) => index.getClass('dart:ffi', name)) |
| .toList(), |
| loadMethods = Map.fromIterable(optimizedTypes, value: (t) { |
| final name = nativeTypeClassNames[t.index]; |
| return index.getTopLevelMember('dart:ffi', "_load$name"); |
| }), |
| loadUnalignedMethods = |
| Map.fromIterable(unalignedLoadsStores, value: (t) { |
| final name = nativeTypeClassNames[t.index]; |
| return index.getTopLevelMember('dart:ffi', "_load${name}Unaligned"); |
| }), |
| storeMethods = Map.fromIterable(optimizedTypes, value: (t) { |
| final name = nativeTypeClassNames[t.index]; |
| return index.getTopLevelMember('dart:ffi', "_store$name"); |
| }), |
| storeUnalignedMethods = |
| Map.fromIterable(unalignedLoadsStores, value: (t) { |
| final name = nativeTypeClassNames[t.index]; |
| return index.getTopLevelMember('dart:ffi', "_store${name}Unaligned"); |
| }), |
| elementAtMethods = Map.fromIterable(optimizedTypes, value: (t) { |
| final name = nativeTypeClassNames[t.index]; |
| return index.getTopLevelMember('dart:ffi', "_elementAt$name"); |
| }), |
| memCopy = index.getTopLevelMember('dart:ffi', '_memCopy'), |
| allocationTearoff = index.getMember( |
| 'dart:ffi', 'AllocatorAlloc', LibraryIndex.tearoffPrefix + 'call'), |
| asFunctionTearoff = index.getMember('dart:ffi', 'NativeFunctionPointer', |
| LibraryIndex.tearoffPrefix + 'asFunction'), |
| lookupFunctionTearoff = index.getMember( |
| 'dart:ffi', |
| 'DynamicLibraryExtension', |
| LibraryIndex.tearoffPrefix + 'lookupFunction'); |
| |
| @override |
| TreeNode visitLibrary(Library node) { |
| assert(currentLibrary == null); |
| currentLibrary = node; |
| currentLibraryIndex = referenceFromIndex?.lookupLibrary(node); |
| final result = super.visitLibrary(node); |
| currentLibrary = null; |
| return result; |
| } |
| |
| /// Computes the Dart type corresponding to a ffi.[NativeType], returns null |
| /// if it is not a valid NativeType. |
| /// |
| /// [Int8] -> [int] |
| /// [Int16] -> [int] |
| /// [Int32] -> [int] |
| /// [Int64] -> [int] |
| /// [Uint8] -> [int] |
| /// [Uint16] -> [int] |
| /// [Uint32] -> [int] |
| /// [Uint64] -> [int] |
| /// [IntPtr] -> [int] |
| /// [Double] -> [double] |
| /// [Float] -> [double] |
| /// [Void] -> [void] |
| /// [Pointer]<T> -> [Pointer]<T> |
| /// T extends [Pointer] -> T |
| /// [Handle] -> [Object] |
| /// [NativeFunction]<T1 Function(T2, T3) -> S1 Function(S2, S3) |
| /// where DartRepresentationOf(Tn) -> Sn |
| DartType convertNativeTypeToDartType(DartType nativeType, |
| {bool allowStructs = false, |
| bool allowStructItself = false, |
| bool allowHandle = false, |
| bool allowInlineArray = false}) { |
| if (nativeType is! InterfaceType) { |
| return null; |
| } |
| final InterfaceType native = nativeType; |
| final Class nativeClass = native.classNode; |
| final NativeType nativeType_ = getType(nativeClass); |
| |
| if (nativeClass == arrayClass) { |
| if (!allowInlineArray) { |
| return null; |
| } |
| return nativeType; |
| } |
| if (hierarchy.isSubclassOf(nativeClass, structClass)) { |
| if (structClass == nativeClass) { |
| return allowStructItself ? nativeType : null; |
| } |
| return allowStructs ? nativeType : null; |
| } |
| if (nativeType_ == null) { |
| return null; |
| } |
| if (nativeType_ == NativeType.kPointer) { |
| return nativeType; |
| } |
| if (kNativeTypeIntStart.index <= nativeType_.index && |
| nativeType_.index <= kNativeTypeIntEnd.index) { |
| return InterfaceType(intClass, Nullability.legacy); |
| } |
| if (nativeType_ == NativeType.kFloat || nativeType_ == NativeType.kDouble) { |
| return InterfaceType(doubleClass, Nullability.legacy); |
| } |
| if (nativeType_ == NativeType.kVoid) { |
| return VoidType(); |
| } |
| if (nativeType_ == NativeType.kHandle && allowHandle) { |
| return InterfaceType(objectClass, Nullability.legacy); |
| } |
| if (nativeType_ != NativeType.kNativeFunction || |
| native.typeArguments[0] is! FunctionType) { |
| return null; |
| } |
| |
| final FunctionType fun = native.typeArguments[0]; |
| if (fun.namedParameters.isNotEmpty) return null; |
| if (fun.positionalParameters.length != fun.requiredParameterCount) { |
| return null; |
| } |
| if (fun.typeParameters.length != 0) return null; |
| |
| final DartType returnType = convertNativeTypeToDartType(fun.returnType, |
| allowStructs: allowStructs, allowHandle: true); |
| if (returnType == null) return null; |
| final List<DartType> argumentTypes = fun.positionalParameters |
| .map((t) => convertNativeTypeToDartType(t, |
| allowStructs: allowStructs, allowHandle: true)) |
| .toList(); |
| if (argumentTypes.contains(null)) return null; |
| return FunctionType(argumentTypes, returnType, Nullability.legacy); |
| } |
| |
| /// The [NativeType] corresponding to [c]. Returns `null` for user-defined |
| /// structs. |
| NativeType getType(Class c) { |
| final int index = nativeTypesClasses.indexOf(c); |
| if (index == -1) { |
| return null; |
| } |
| return NativeType.values[index]; |
| } |
| |
| ConstantExpression intListConstantExpression(List<int> values) => |
| ConstantExpression( |
| ListConstant(InterfaceType(intClass, Nullability.legacy), |
| [for (var v in values) IntConstant(v)]), |
| InterfaceType(listClass, Nullability.legacy, |
| [InterfaceType(intClass, Nullability.legacy)])); |
| |
| /// Expression that queries VM internals at runtime to figure out on which ABI |
| /// we are. |
| Expression runtimeBranchOnLayout(Map<Abi, int> values) { |
| return MethodInvocation( |
| intListConstantExpression([ |
| values[Abi.wordSize64], |
| values[Abi.wordSize32Align32], |
| values[Abi.wordSize32Align64] |
| ]), |
| Name("[]"), |
| Arguments([StaticInvocation(abiMethod, Arguments([]))]), |
| listElementAt); |
| } |
| |
| /// Generates an expression that returns a new `Pointer<dartType>` offset |
| /// by [offset] from [pointer]. |
| /// |
| /// Sample output: |
| /// |
| /// ``` |
| /// _fromAddress<dartType>(pointer.address + #offset) |
| /// ``` |
| Expression _pointerOffset(Expression pointer, Expression offset, |
| DartType dartType, int fileOffset) => |
| StaticInvocation( |
| fromAddressInternal, |
| Arguments([ |
| MethodInvocation( |
| PropertyGet(pointer, addressGetter.name, addressGetter) |
| ..fileOffset = fileOffset, |
| numAddition.name, |
| Arguments([offset]), |
| numAddition) |
| ], types: [ |
| dartType |
| ])) |
| ..fileOffset = fileOffset; |
| |
| /// Generates an expression that returns a new `TypedData` offset |
| /// by [offset] from [typedData]. |
| /// |
| /// Sample output: |
| /// |
| /// ``` |
| /// TypedData #typedData = typedData; |
| /// #typedData.buffer.asInt8List(#typedData.offsetInBytes + offset, length) |
| /// ``` |
| Expression _typedDataOffset(Expression typedData, Expression offset, |
| Expression length, int fileOffset) { |
| final typedDataVar = VariableDeclaration("#typedData", |
| initializer: typedData, |
| type: InterfaceType(typedDataClass, Nullability.nonNullable)) |
| ..fileOffset = fileOffset; |
| return Let( |
| typedDataVar, |
| MethodInvocation( |
| PropertyGet(VariableGet(typedDataVar), typedDataBufferGetter.name, |
| typedDataBufferGetter) |
| ..fileOffset = fileOffset, |
| byteBufferAsUint8List.name, |
| Arguments([ |
| MethodInvocation( |
| PropertyGet( |
| VariableGet(typedDataVar), |
| typedDataOffsetInBytesGetter.name, |
| typedDataOffsetInBytesGetter) |
| ..fileOffset = fileOffset, |
| numAddition.name, |
| Arguments([offset]), |
| numAddition), |
| length |
| ]), |
| byteBufferAsUint8List)); |
| } |
| |
| /// Generates an expression that returns a new `TypedDataBase` offset |
| /// by [offset] from [typedDataBase]. |
| /// |
| /// If [typedDataBase] is a `Pointer`, returns a `Pointer<dartType>`. |
| /// If [typedDataBase] is a `TypedData` returns a `TypedData`. |
| /// |
| /// Sample output: |
| /// |
| /// ``` |
| /// Object #typedDataBase = typedDataBase; |
| /// int #offset = offset; |
| /// #typedDataBase is Pointer ? |
| /// _pointerOffset<dartType>(#typedDataBase, #offset) : |
| /// _typedDataOffset((#typedDataBase as TypedData), #offset, length) |
| /// ``` |
| Expression typedDataBaseOffset(Expression typedDataBase, Expression offset, |
| Expression length, DartType dartType, int fileOffset) { |
| final typedDataBaseVar = VariableDeclaration("#typedDataBase", |
| initializer: typedDataBase, type: coreTypes.objectNonNullableRawType) |
| ..fileOffset = fileOffset; |
| final offsetVar = VariableDeclaration("#offset", |
| initializer: offset, type: coreTypes.intNonNullableRawType) |
| ..fileOffset = fileOffset; |
| return BlockExpression( |
| Block([typedDataBaseVar, offsetVar]), |
| ConditionalExpression( |
| IsExpression(VariableGet(typedDataBaseVar), |
| InterfaceType(pointerClass, Nullability.nonNullable)), |
| _pointerOffset(VariableGet(typedDataBaseVar), |
| VariableGet(offsetVar), dartType, fileOffset), |
| _typedDataOffset( |
| StaticInvocation( |
| unsafeCastMethod, |
| Arguments([ |
| VariableGet(typedDataBaseVar) |
| ], types: [ |
| InterfaceType(typedDataClass, Nullability.nonNullable) |
| ])), |
| VariableGet(offsetVar), |
| length, |
| fileOffset), |
| coreTypes.objectNonNullableRawType)); |
| } |
| |
| bool isPrimitiveType(DartType type) { |
| if (type is InvalidType) { |
| return false; |
| } |
| if (type is NullType) { |
| return false; |
| } |
| if (!env.isSubtypeOf( |
| type, |
| InterfaceType(nativeTypesClasses[NativeType.kNativeType.index], |
| Nullability.legacy), |
| SubtypeCheckMode.ignoringNullabilities)) { |
| return false; |
| } |
| if (isPointerType(type)) { |
| return false; |
| } |
| if (type is InterfaceType) { |
| final nativeType = getType(type.classNode); |
| return nativeType != null; |
| } |
| return false; |
| } |
| |
| bool isPointerType(DartType type) { |
| if (type is InvalidType) { |
| return false; |
| } |
| if (type is NullType) { |
| return false; |
| } |
| return env.isSubtypeOf( |
| type, |
| InterfaceType(pointerClass, Nullability.legacy, [ |
| InterfaceType(nativeTypesClasses[NativeType.kNativeType.index], |
| Nullability.legacy) |
| ]), |
| SubtypeCheckMode.ignoringNullabilities); |
| } |
| |
| bool isArrayType(DartType type) { |
| if (type is InvalidType) { |
| return false; |
| } |
| if (type is NullType) { |
| return false; |
| } |
| return env.isSubtypeOf( |
| type, |
| InterfaceType(arrayClass, Nullability.legacy, [ |
| InterfaceType(nativeTypesClasses[NativeType.kNativeType.index], |
| Nullability.legacy) |
| ]), |
| SubtypeCheckMode.ignoringNullabilities); |
| } |
| |
| /// Returns the single element type nested type argument of `Array`. |
| /// |
| /// `Array<Array<Array<Int8>>>` -> `Int8`. |
| DartType arraySingleElementType(DartType dartType) { |
| InterfaceType elementType = dartType as InterfaceType; |
| while (elementType.classNode == arrayClass) { |
| elementType = elementType.typeArguments[0] as InterfaceType; |
| } |
| return elementType; |
| } |
| |
| /// Returns the number of dimensions of `Array`. |
| /// |
| /// `Array<Array<Array<Int8>>>` -> 3. |
| int arrayDimensions(DartType dartType) { |
| InterfaceType elementType = dartType as InterfaceType; |
| int dimensions = 0; |
| while (elementType.classNode == arrayClass) { |
| elementType = elementType.typeArguments[0] as InterfaceType; |
| dimensions++; |
| } |
| return dimensions; |
| } |
| |
| bool isStructSubtype(DartType type) { |
| if (type is InvalidType) { |
| return false; |
| } |
| if (type is NullType) { |
| return false; |
| } |
| if (type is InterfaceType) { |
| if (type.classNode == structClass) { |
| return false; |
| } |
| } |
| return env.isSubtypeOf(type, InterfaceType(structClass, Nullability.legacy), |
| SubtypeCheckMode.ignoringNullabilities); |
| } |
| } |
| |
| /// Contains all information collected by _FfiDefinitionTransformer that is |
| /// needed in _FfiUseSiteTransformer. |
| class FfiTransformerData { |
| final Map<Field, Procedure> replacedGetters; |
| final Map<Field, Procedure> replacedSetters; |
| final Set<Class> emptyStructs; |
| FfiTransformerData( |
| this.replacedGetters, this.replacedSetters, this.emptyStructs); |
| } |
| |
| /// Checks if any library depends on dart:ffi. |
| bool importsFfi(Component component, List<Library> libraries) { |
| Set<Library> allLibs = {...component.libraries, ...libraries}; |
| final Uri dartFfiUri = Uri.parse("dart:ffi"); |
| for (Library lib in allLibs) { |
| for (LibraryDependency dependency in lib.dependencies) { |
| if (dependency.targetLibrary.importUri == dartFfiUri) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |