| // Copyright (c) 2020, 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:ffigen/src/code_generator.dart'; |
| import 'package:ffigen/src/config_provider/config_types.dart'; |
| |
| import 'binding_string.dart'; |
| import 'utils.dart'; |
| import 'writer.dart'; |
| |
| /// A binding for C function. |
| /// |
| /// For example, take the following C function. |
| /// |
| /// ```c |
| /// int sum(int a, int b); |
| /// ``` |
| /// |
| /// The generated Dart code for this function (without `FfiNative`) is as follows. |
| /// |
| /// ```dart |
| /// int sum(int a, int b) { |
| /// return _sum(a, b); |
| /// } |
| /// |
| /// final _dart_sum _sum = _dylib.lookupFunction<_c_sum, _dart_sum>('sum'); |
| /// |
| /// typedef _c_sum = ffi.Int32 Function(ffi.Int32 a, ffi.Int32 b); |
| /// |
| /// typedef _dart_sum = int Function(int a, int b); |
| /// ``` |
| /// |
| /// When using `Native`, the code is as follows. |
| /// |
| /// ```dart |
| /// @ffi.Native<ffi.Int32 Function(ffi.Int32 a, ffi.Int32 b)>('sum') |
| /// external int sum(int a, int b); |
| /// ``` |
| class Func extends LookUpBinding { |
| final FunctionType functionType; |
| final bool exposeSymbolAddress; |
| final bool exposeFunctionTypedefs; |
| final bool isLeaf; |
| final bool objCReturnsRetained; |
| final FfiNativeConfig ffiNativeConfig; |
| late final String funcPointerName; |
| |
| /// Contains typealias for function type if [exposeFunctionTypedefs] is true. |
| Typealias? _exposedFunctionTypealias; |
| |
| /// [originalName] is looked up in dynamic library, if not |
| /// provided, takes the value of [name]. |
| Func({ |
| super.usr, |
| required String name, |
| super.originalName, |
| super.dartDoc, |
| required Type returnType, |
| List<Parameter>? parameters, |
| List<Parameter>? varArgParameters, |
| this.exposeSymbolAddress = false, |
| this.exposeFunctionTypedefs = false, |
| this.isLeaf = false, |
| this.objCReturnsRetained = false, |
| super.isInternal, |
| this.ffiNativeConfig = const FfiNativeConfig(enabled: false), |
| }) : functionType = FunctionType( |
| returnType: returnType, |
| parameters: parameters ?? const [], |
| varArgParameters: varArgParameters ?? const [], |
| ), |
| super( |
| name: name, |
| ) { |
| for (var i = 0; i < functionType.parameters.length; i++) { |
| if (functionType.parameters[i].name.trim() == '') { |
| functionType.parameters[i].name = 'arg$i'; |
| } |
| } |
| |
| // Get function name with first letter in upper case. |
| final upperCaseName = name[0].toUpperCase() + name.substring(1); |
| if (exposeFunctionTypedefs) { |
| _exposedFunctionTypealias = Typealias( |
| name: upperCaseName, |
| type: functionType, |
| genFfiDartType: true, |
| isInternal: true, |
| ); |
| } |
| } |
| |
| @override |
| BindingString toBindingString(Writer w) { |
| final s = StringBuffer(); |
| final enclosingFuncName = name; |
| |
| if (dartDoc != null) { |
| s.write(makeDartDoc(dartDoc!)); |
| } |
| // Resolve name conflicts in function parameter names. |
| final paramNamer = UniqueNamer({}); |
| for (final p in functionType.dartTypeParameters) { |
| p.name = paramNamer.makeUnique(p.name); |
| } |
| |
| final cType = _exposedFunctionTypealias?.getCType(w) ?? |
| functionType.getCType(w, writeArgumentNames: false); |
| final dartType = _exposedFunctionTypealias?.getFfiDartType(w) ?? |
| functionType.getFfiDartType(w, writeArgumentNames: false); |
| final needsWrapper = !functionType.sameDartAndFfiDartType && !isInternal; |
| |
| final funcVarName = w.wrapperLevelUniqueNamer.makeUnique('_$name'); |
| final ffiReturnType = functionType.returnType.getFfiDartType(w); |
| final ffiArgDeclString = functionType.dartTypeParameters |
| .map((p) => '${p.type.getFfiDartType(w)} ${p.name},\n') |
| .join(''); |
| |
| late final String dartReturnType; |
| late final String dartArgDeclString; |
| late final String funcImplCall; |
| if (needsWrapper) { |
| dartReturnType = functionType.returnType.getDartType(w); |
| dartArgDeclString = functionType.dartTypeParameters |
| .map((p) => '${p.type.getDartType(w)} ${p.name},\n') |
| .join(''); |
| |
| final argString = functionType.dartTypeParameters |
| .map((p) => |
| '${p.type.convertDartTypeToFfiDartType(w, p.name, objCRetain: false)},\n') |
| .join(''); |
| funcImplCall = functionType.returnType.convertFfiDartTypeToDartType( |
| w, |
| '$funcVarName($argString)', |
| ffiNativeConfig.enabled ? 'lib' : 'this', |
| objCRetain: !objCReturnsRetained, |
| ); |
| } else { |
| dartReturnType = ffiReturnType; |
| dartArgDeclString = ffiArgDeclString; |
| final argString = |
| functionType.dartTypeParameters.map((p) => '${p.name},\n').join(''); |
| funcImplCall = '$funcVarName($argString)'; |
| } |
| |
| if (ffiNativeConfig.enabled) { |
| final nativeFuncName = needsWrapper ? funcVarName : enclosingFuncName; |
| s.write(''' |
| ${makeNativeAnnotation( |
| w, |
| nativeType: cType, |
| dartName: nativeFuncName, |
| nativeSymbolName: originalName, |
| isLeaf: isLeaf, |
| )} |
| external $ffiReturnType $nativeFuncName($ffiArgDeclString); |
| |
| '''); |
| if (needsWrapper) { |
| final libArg = functionType.returnType.sameDartAndFfiDartType |
| ? '' |
| : '${w.className} lib, '; |
| s.write(''' |
| $dartReturnType $enclosingFuncName($libArg$dartArgDeclString) => $funcImplCall; |
| |
| '''); |
| } |
| |
| if (exposeSymbolAddress) { |
| // Add to SymbolAddress in writer. |
| w.symbolAddressWriter.addNativeSymbol( |
| type: |
| '${w.ffiLibraryPrefix}.Pointer<${w.ffiLibraryPrefix}.NativeFunction<$cType>>', |
| name: name, |
| ); |
| } |
| } else { |
| funcPointerName = w.wrapperLevelUniqueNamer.makeUnique('_${name}Ptr'); |
| final isLeafString = isLeaf ? 'isLeaf:true' : ''; |
| |
| // Write enclosing function. |
| s.write(''' |
| $dartReturnType $enclosingFuncName($dartArgDeclString) { |
| return $funcImplCall; |
| } |
| |
| '''); |
| |
| if (exposeSymbolAddress) { |
| // Add to SymbolAddress in writer. |
| w.symbolAddressWriter.addSymbol( |
| type: |
| '${w.ffiLibraryPrefix}.Pointer<${w.ffiLibraryPrefix}.NativeFunction<$cType>>', |
| name: name, |
| ptrName: funcPointerName, |
| ); |
| } |
| |
| // Write function pointer. |
| s.write(''' |
| late final $funcPointerName = ${w.lookupFuncIdentifier}< |
| ${w.ffiLibraryPrefix}.NativeFunction<$cType>>('$originalName'); |
| late final $funcVarName = $funcPointerName.asFunction<$dartType>($isLeafString); |
| |
| '''); |
| } |
| |
| return BindingString(type: BindingStringType.func, string: s.toString()); |
| } |
| |
| @override |
| void addDependencies(Set<Binding> dependencies) { |
| if (dependencies.contains(this)) return; |
| |
| dependencies.add(this); |
| functionType.addDependencies(dependencies); |
| if (exposeFunctionTypedefs) { |
| _exposedFunctionTypealias!.addDependencies(dependencies); |
| } |
| } |
| } |
| |
| /// Represents a Parameter, used in [Func] and [Typealias]. |
| class Parameter { |
| final String? originalName; |
| String name; |
| final Type type; |
| |
| Parameter({String? originalName, this.name = '', required Type type}) |
| : originalName = originalName ?? name, |
| // A [NativeFunc] is wrapped with a pointer because this is a shorthand |
| // used in C for Pointer to function. |
| type = type.typealiasType is NativeFunc ? PointerType(type) : type; |
| } |