| // 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 'binding_string.dart'; |
| import 'utils.dart'; |
| import 'writer.dart'; |
| |
| /// A binding for C function. |
| /// |
| /// For a C function - |
| /// ```c |
| /// int sum(int a, int b); |
| /// ``` |
| /// The Generated dart code is - |
| /// ```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); |
| /// ``` |
| class Func extends LookUpBinding { |
| final FunctionType functionType; |
| final bool exposeSymbolAddress; |
| final bool exposeFunctionTypedefs; |
| final bool isLeaf; |
| late final String funcPointerName; |
| |
| /// Contains typealias for function type if [exposeFunctionTypedefs] is true. |
| Typealias? _exposedCFunctionTypealias; |
| Typealias? _exposedDartFunctionTypealias; |
| |
| /// [originalName] is looked up in dynamic library, if not |
| /// provided, takes the value of [name]. |
| Func({ |
| String? usr, |
| required String name, |
| String? originalName, |
| String? dartDoc, |
| required Type returnType, |
| List<Parameter>? parameters, |
| this.exposeSymbolAddress = false, |
| this.exposeFunctionTypedefs = false, |
| this.isLeaf = false, |
| bool isInternal = false, |
| }) : functionType = FunctionType( |
| returnType: returnType, |
| parameters: parameters ?? const [], |
| ), |
| super( |
| usr: usr, |
| originalName: originalName, |
| name: name, |
| dartDoc: dartDoc, |
| isInternal: isInternal, |
| ) { |
| 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) { |
| _exposedCFunctionTypealias = Typealias( |
| name: 'Native$upperCaseName', |
| type: functionType, |
| ); |
| _exposedDartFunctionTypealias = Typealias( |
| name: 'Dart$upperCaseName', |
| type: functionType, |
| useDartType: true, |
| ); |
| } |
| } |
| |
| @override |
| BindingString toBindingString(Writer w) { |
| final s = StringBuffer(); |
| final enclosingFuncName = name; |
| final funcVarName = w.wrapperLevelUniqueNamer.makeUnique('_$name'); |
| funcPointerName = w.wrapperLevelUniqueNamer.makeUnique('_${name}Ptr'); |
| |
| if (dartDoc != null) { |
| s.write(makeDartDoc(dartDoc!)); |
| } |
| // Resolve name conflicts in function parameter names. |
| final paramNamer = UniqueNamer({}); |
| for (final p in functionType.parameters) { |
| p.name = paramNamer.makeUnique(p.name); |
| } |
| // Write enclosing function. |
| s.write('${functionType.returnType.getDartType(w)} $enclosingFuncName(\n'); |
| for (final p in functionType.parameters) { |
| s.write(' ${p.type.getDartType(w)} ${p.name},\n'); |
| } |
| s.write(') {\n'); |
| s.write('return $funcVarName'); |
| |
| s.write('(\n'); |
| for (final p in functionType.parameters) { |
| s.write(' ${p.name},\n'); |
| } |
| s.write(' );\n'); |
| s.write('}\n'); |
| |
| final cType = exposeFunctionTypedefs |
| ? _exposedCFunctionTypealias!.name |
| : functionType.getCType(w, writeArgumentNames: false); |
| final dartType = exposeFunctionTypedefs |
| ? _exposedDartFunctionTypealias!.name |
| : functionType.getDartType(w, writeArgumentNames: false); |
| |
| 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');\n"); |
| final isLeafString = isLeaf ? 'isLeaf:true' : ''; |
| s.write( |
| 'late final $funcVarName = $funcPointerName.asFunction<$dartType>($isLeafString);\n\n'); |
| |
| 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) { |
| _exposedCFunctionTypealias!.addDependencies(dependencies); |
| _exposedDartFunctionTypealias!.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; |
| } |