// 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 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 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 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] -> [Pointer] /// T extends [Pointer] -> T /// [NativeFunction] 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 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 replacedGetters; final Map replacedSetters; ReplacedMembers(this.replacedGetters, this.replacedSetters); }