| // 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'; |
| 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/target/targets.dart' show DiagnosticReporter; |
| import 'package:kernel/type_environment.dart' show TypeEnvironment; |
| |
| /// Represents the (instantiated) ffi.NativeType. |
| enum NativeType { |
| kPointer, |
| kNativeFunction, |
| kInt8, |
| kInt16, |
| kInt32, |
| kInt64, |
| kUint8, |
| kUint16, |
| kUint32, |
| kUnit64, |
| kIntptr, |
| kFloat, |
| kDouble, |
| kVoid |
| } |
| |
| const NativeType kNativeTypeIntStart = NativeType.kInt8; |
| const NativeType kNativeTypeIntEnd = NativeType.kIntptr; |
| |
| /// The [NativeType] class names, indexed by [NativeType]. |
| const List<String> nativeTypeClassNames = [ |
| 'Pointer', |
| 'NativeFunction', |
| 'Int8', |
| 'Int16', |
| 'Int32', |
| 'Int64', |
| 'Uint8', |
| 'Uint16', |
| 'Uint32', |
| 'Uint64', |
| 'IntPtr', |
| 'Float', |
| 'Double', |
| 'Void' |
| ]; |
| |
| const int UNKNOWN = 0; |
| const int WORD_SIZE = -1; |
| |
| /// The [NativeType] sizes in bytes, indexed by [NativeType]. |
| const List<int> nativeTypeSizes = [ |
| 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 |
| ]; |
| |
| /// [FfiTransformer] contains logic which is shared between |
| /// _FfiUseSiteTransformer and _FfiDefinitionTransformer. |
| class FfiTransformer extends Transformer { |
| final TypeEnvironment env; |
| final LibraryIndex index; |
| final ClassHierarchy hierarchy; |
| final DiagnosticReporter diagnosticReporter; |
| |
| final Class intClass; |
| final Class doubleClass; |
| final Constructor pragmaConstructor; |
| |
| final Library ffiLibrary; |
| final Class nativeFunctionClass; |
| final Class pointerClass; |
| final Class structClass; |
| final Procedure castMethod; |
| final Procedure loadMethod; |
| final Procedure storeMethod; |
| final Procedure offsetByMethod; |
| final Procedure asFunctionMethod; |
| final Procedure lookupFunctionMethod; |
| final Procedure fromFunctionMethod; |
| final Field structField; |
| |
| /// Classes corresponding to [NativeType], indexed by [NativeType]. |
| final List<Class> nativeTypesClasses; |
| |
| FfiTransformer( |
| this.index, CoreTypes coreTypes, this.hierarchy, this.diagnosticReporter) |
| : env = new TypeEnvironment(coreTypes, hierarchy), |
| intClass = coreTypes.intClass, |
| doubleClass = coreTypes.doubleClass, |
| pragmaConstructor = coreTypes.pragmaConstructor, |
| ffiLibrary = index.getLibrary('dart:ffi'), |
| nativeFunctionClass = index.getClass('dart:ffi', 'NativeFunction'), |
| pointerClass = index.getClass('dart:ffi', 'Pointer'), |
| structClass = index.getClass('dart:ffi', 'Struct'), |
| castMethod = index.getMember('dart:ffi', 'Pointer', 'cast'), |
| loadMethod = index.getMember('dart:ffi', 'Pointer', 'load'), |
| storeMethod = index.getMember('dart:ffi', 'Pointer', 'store'), |
| offsetByMethod = index.getMember('dart:ffi', 'Pointer', 'offsetBy'), |
| asFunctionMethod = index.getMember('dart:ffi', 'Pointer', 'asFunction'), |
| lookupFunctionMethod = |
| index.getMember('dart:ffi', 'DynamicLibrary', 'lookupFunction'), |
| fromFunctionMethod = |
| index.getTopLevelMember('dart:ffi', 'fromFunction'), |
| structField = index.getTopLevelMember('dart:ffi', 'struct'), |
| nativeTypesClasses = nativeTypeClassNames |
| .map((name) => index.getClass('dart:ffi', name)) |
| .toList() {} |
| |
| /// 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 |
| /// [NativeFunction]<T1 Function(T2, T3) -> S1 Function(S2, S3) |
| /// where DartRepresentationOf(Tn) -> Sn |
| DartType convertNativeTypeToDartType(DartType nativeType) { |
| if (nativeType is! InterfaceType) { |
| return null; |
| } |
| Class nativeClass = (nativeType as InterfaceType).classNode; |
| if (env.isSubtypeOf( |
| InterfaceType(nativeClass), InterfaceType(pointerClass))) { |
| return nativeType; |
| } |
| NativeType nativeType_ = getType(nativeClass); |
| if (nativeType_ == null) { |
| return null; |
| } |
| if (kNativeTypeIntStart.index <= nativeType_.index && |
| nativeType_.index <= kNativeTypeIntEnd.index) { |
| return InterfaceType(intClass); |
| } |
| if (nativeType_ == NativeType.kFloat || nativeType_ == NativeType.kDouble) { |
| return InterfaceType(doubleClass); |
| } |
| if (nativeType_ == NativeType.kVoid) { |
| return VoidType(); |
| } |
| if (nativeType_ == NativeType.kNativeFunction) { |
| DartType fun = (nativeType as InterfaceType).typeArguments[0]; |
| if (fun is FunctionType) { |
| if (fun.namedParameters.isNotEmpty) return null; |
| if (fun.positionalParameters.length != fun.requiredParameterCount) |
| return null; |
| if (fun.typeParameters.length != 0) return null; |
| DartType returnType = convertNativeTypeToDartType(fun.returnType); |
| if (returnType == null) return null; |
| List<DartType> argumentTypes = fun.positionalParameters |
| .map(this.convertNativeTypeToDartType) |
| .toList(); |
| if (argumentTypes.contains(null)) return null; |
| return FunctionType(argumentTypes, returnType); |
| } |
| } |
| return null; |
| } |
| |
| NativeType getType(Class c) { |
| int index = nativeTypesClasses.indexOf(c); |
| if (index == -1) { |
| return null; |
| } |
| return NativeType.values[index]; |
| } |
| } |
| |
| /// Contains replaced members, of which all the call sites need to be replaced. |
| /// |
| /// [ReplacedMembers] is populated by _FfiDefinitionTransformer and consumed by |
| /// _FfiUseSiteTransformer. |
| class ReplacedMembers { |
| final Map<Field, Procedure> replacedGetters; |
| final Map<Field, Procedure> replacedSetters; |
| ReplacedMembers(this.replacedGetters, this.replacedSetters); |
| } |