| // Copyright (c) 2021, 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. |
| |
| // @dart=2.12 |
| |
| import 'package:kernel/ast.dart'; |
| import 'package:kernel/library_index.dart' show LibraryIndex; |
| import 'package:kernel/reference_from_index.dart' |
| show IndexedLibrary, ReferenceFromIndex; |
| import 'package:kernel/target/targets.dart' show DiagnosticReporter; |
| import 'package:front_end/src/api_unstable/vm.dart' |
| show messageFfiNativeAnnotationMustAnnotateStatic; |
| |
| /// Transform @FfiNative annotated functions into FFI native function pointer |
| /// functions. |
| void transformLibraries(Component component, List<Library> libraries, |
| DiagnosticReporter diagnosticReporter, |
| ReferenceFromIndex? referenceFromIndex) { |
| final index = LibraryIndex(component, ['dart:ffi']); |
| // Skip if dart:ffi isn't loaded (e.g. during incremental compile). |
| if (index.tryGetClass('dart:ffi', 'FfiNative') == null) { |
| return; |
| } |
| final transformer = FfiNativeTransformer(index, diagnosticReporter, |
| referenceFromIndex); |
| libraries.forEach(transformer.visitLibrary); |
| } |
| |
| class FfiNativeTransformer extends Transformer { |
| Library? currentLibrary; |
| IndexedLibrary? currentLibraryIndex; |
| |
| final DiagnosticReporter diagnosticReporter; |
| final ReferenceFromIndex? referenceFromIndex; |
| final Class ffiNativeClass; |
| final Class nativeFunctionClass; |
| final Field ffiNativeNameField; |
| final Field ffiNativeIsLeafField; |
| final Field resolverField; |
| final Procedure asFunctionProcedure; |
| final Procedure fromAddressInternal; |
| |
| FfiNativeTransformer(LibraryIndex index, this.diagnosticReporter, |
| this.referenceFromIndex) |
| : ffiNativeClass = index.getClass('dart:ffi', 'FfiNative'), |
| nativeFunctionClass = index.getClass('dart:ffi', 'NativeFunction'), |
| ffiNativeNameField = |
| index.getField('dart:ffi', 'FfiNative', 'nativeName'), |
| ffiNativeIsLeafField = |
| index.getField('dart:ffi', 'FfiNative', 'isLeaf'), |
| resolverField = index.getTopLevelField('dart:ffi', '_ffi_resolver'), |
| asFunctionProcedure = index.getProcedure( |
| 'dart:ffi', 'NativeFunctionPointer', 'asFunction'), |
| fromAddressInternal = |
| index.getTopLevelProcedure('dart:ffi', '_fromAddress') {} |
| |
| @override |
| TreeNode visitLibrary(Library node) { |
| assert(currentLibrary == null); |
| currentLibrary = node; |
| currentLibraryIndex = referenceFromIndex?.lookupLibrary(node); |
| final result = super.visitLibrary(node); |
| currentLibrary = null; |
| return result; |
| } |
| |
| InstanceConstant? _tryGetFfiNativeAnnotation(Member node) { |
| for (final Expression annotation in node.annotations) { |
| if (annotation is ConstantExpression) { |
| if (annotation.constant is InstanceConstant) { |
| final instConst = annotation.constant as InstanceConstant; |
| if (instConst.classNode == ffiNativeClass) { |
| return instConst; |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| // Transform: |
| // @FfiNative<Double Function(Double)>('Math_sqrt', isLeaf:true) |
| // external double _square_root(double x); |
| // |
| // Into: |
| // final _@FfiNative__square_root = |
| // Pointer<NativeFunction<Double Function(Double)>> |
| // .fromAddress(_ffi_resolver('dart:math', 'Math_sqrt')) |
| // .asFunction<double Function(double)>(isLeaf:true); |
| // double _square_root(double x) => _@FfiNative__square_root(x); |
| Statement transformFfiNative( |
| Procedure node, InstanceConstant annotationConst) { |
| assert(currentLibrary != null); |
| final params = node.function.positionalParameters; |
| final functionName = annotationConst |
| .fieldValues[ffiNativeNameField.getterReference] as StringConstant; |
| final isLeaf = annotationConst |
| .fieldValues[ffiNativeIsLeafField.getterReference] as BoolConstant; |
| |
| // double Function(double) |
| final DartType dartType = |
| node.function.computeThisFunctionType(Nullability.nonNullable); |
| // Double Function(Double) |
| final nativeType = annotationConst.typeArguments[0]; |
| // InterfaceType(NativeFunction<Double Function(Double)>*) |
| final DartType nativeInterfaceType = |
| InterfaceType(nativeFunctionClass, Nullability.legacy, [nativeType]); |
| |
| // TODO(dartbug.com/31579): Add `..fileOffset`s once we can handle these in |
| // patch files. |
| |
| // _ffi_resolver('dart:math', 'Math_sqrt') |
| final resolverInvocation = FunctionInvocation( |
| FunctionAccessKind.FunctionType, |
| StaticGet(resolverField), |
| Arguments([ |
| ConstantExpression( |
| StringConstant(currentLibrary!.importUri.toString())), |
| ConstantExpression(functionName) |
| ]), |
| functionType: resolverField.type as FunctionType); |
| |
| // _fromAddress<NativeFunction<Double Function(Double)>>(...) |
| final fromAddressInvocation = StaticInvocation(fromAddressInternal, |
| Arguments([resolverInvocation], types: [nativeInterfaceType])); |
| |
| // NativeFunctionPointer.asFunction |
| // <Double Function(Double), double Function(double)>(..., isLeaf:true) |
| final asFunctionInvocation = StaticInvocation( |
| asFunctionProcedure, |
| Arguments([fromAddressInvocation], |
| types: [nativeType, dartType], |
| named: [NamedExpression("isLeaf", BoolLiteral(isLeaf.value))])); |
| |
| // final _@FfiNative__square_root = ... |
| final fieldName = Name('_@FfiNative_${node.name.text}', currentLibrary); |
| final funcPtrField = Field.immutable(fieldName, |
| type: dartType, |
| initializer: asFunctionInvocation, |
| isStatic: true, |
| isFinal: true, |
| fileUri: currentLibrary!.fileUri, |
| getterReference: currentLibraryIndex?.lookupGetterReference(fieldName)) |
| ..fileOffset = node.fileOffset; |
| currentLibrary!.addField(funcPtrField); |
| |
| // _@FfiNative__square_root(x) |
| final callFuncPtrInvocation = FunctionInvocation( |
| FunctionAccessKind.FunctionType, |
| StaticGet(funcPtrField), |
| Arguments(params.map<Expression>((p) => VariableGet(p)).toList()), |
| functionType: dartType as FunctionType); |
| |
| return ReturnStatement(callFuncPtrInvocation); |
| } |
| |
| @override |
| visitProcedure(Procedure node) { |
| // Only transform functions that are external and have FfiNative annotation: |
| // @FfiNative<Double Function(Double)>('Math_sqrt') |
| // external double _square_root(double x); |
| if (!node.isExternal) { |
| return node; |
| } |
| InstanceConstant? ffiNativeAnnotation = _tryGetFfiNativeAnnotation(node); |
| if (ffiNativeAnnotation == null) { |
| return node; |
| } |
| |
| if (!node.isStatic) { |
| diagnosticReporter.report( |
| messageFfiNativeAnnotationMustAnnotateStatic, |
| node.fileOffset, |
| 1, |
| node.location!.file); |
| } |
| |
| node.isExternal = false; |
| node.function.body = transformFfiNative(node, ffiNativeAnnotation) |
| ..parent = node.function; |
| |
| return node; |
| } |
| } |