• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

dart-lang / ffigen / 6727396944

02 Nov 2023 02:02AM UTC coverage: 91.853% (-0.3%) from 92.193%
6727396944

push

github

web-flow
ObjC static functions (#633)

* WIP static functions

* Revert old code gen changes

* Refactor function code gen

* Arg and return conversions

* fix bugs

* Fix analysis, add more tests

* fmt

* Fix autorelease pool test

* Handle NS_RETURNS_RETAINED

* Daco's comments

54 of 54 new or added lines in 9 files covered. (100.0%)

3732 of 4063 relevant lines covered (91.85%)

28.84 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.95
/lib/src/code_generator/func.dart
1
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
2
// for details. All rights reserved. Use of this source code is governed by a
3
// BSD-style license that can be found in the LICENSE file.
4

5
import 'package:ffigen/src/code_generator.dart';
6
import 'package:ffigen/src/config_provider/config_types.dart';
7

8
import 'binding_string.dart';
9
import 'utils.dart';
10
import 'writer.dart';
11

12
/// A binding for C function.
13
///
14
/// For example, take the following C function.
15
///
16
/// ```c
17
/// int sum(int a, int b);
18
/// ```
19
///
20
/// The generated Dart code for this function (without `FfiNative`) is as follows.
21
///
22
/// ```dart
23
/// int sum(int a, int b) {
24
///   return _sum(a, b);
25
/// }
26
///
27
/// final _dart_sum _sum = _dylib.lookupFunction<_c_sum, _dart_sum>('sum');
28
///
29
/// typedef _c_sum = ffi.Int32 Function(ffi.Int32 a, ffi.Int32 b);
30
///
31
/// typedef _dart_sum = int Function(int a, int b);
32
/// ```
33
///
34
/// When using `Native`, the code is as follows.
35
///
36
/// ```dart
37
/// @ffi.Native<ffi.Int32 Function(ffi.Int32 a, ffi.Int32 b)>('sum')
38
/// external int sum(int a, int b);
39
/// ```
40
class Func extends LookUpBinding {
41
  final FunctionType functionType;
42
  final bool exposeSymbolAddress;
43
  final bool exposeFunctionTypedefs;
44
  final bool isLeaf;
45
  final bool objCReturnsRetained;
46
  final FfiNativeConfig ffiNativeConfig;
47
  late final String funcPointerName;
48

49
  /// Contains typealias for function type if [exposeFunctionTypedefs] is true.
50
  Typealias? _exposedFunctionTypealias;
51

52
  /// [originalName] is looked up in dynamic library, if not
53
  /// provided, takes the value of [name].
54
  Func({
29✔
55
    super.usr,
56
    required String name,
57
    super.originalName,
58
    super.dartDoc,
59
    required Type returnType,
60
    List<Parameter>? parameters,
61
    List<Parameter>? varArgParameters,
62
    this.exposeSymbolAddress = false,
63
    this.exposeFunctionTypedefs = false,
64
    this.isLeaf = false,
65
    this.objCReturnsRetained = false,
66
    super.isInternal,
67
    this.ffiNativeConfig = const FfiNativeConfig(enabled: false),
68
  })  : functionType = FunctionType(
29✔
69
          returnType: returnType,
70
          parameters: parameters ?? const [],
71
          varArgParameters: varArgParameters ?? const [],
72
        ),
73
        super(
29✔
74
          name: name,
75
        ) {
76
    for (var i = 0; i < functionType.parameters.length; i++) {
140✔
77
      if (functionType.parameters[i].name.trim() == '') {
144✔
78
        functionType.parameters[i].name = 'arg$i';
30✔
79
      }
80
    }
81

82
    // Get function name with first letter in upper case.
83
    final upperCaseName = name[0].toUpperCase() + name.substring(1);
116✔
84
    if (exposeFunctionTypedefs) {
29✔
85
      _exposedFunctionTypealias = Typealias(
4✔
86
        name: upperCaseName,
87
        type: functionType,
2✔
88
        genFfiDartType: true,
89
        isInternal: true,
90
      );
91
    }
92
  }
93

94
  @override
27✔
95
  BindingString toBindingString(Writer w) {
96
    final s = StringBuffer();
27✔
97
    final enclosingFuncName = name;
27✔
98

99
    if (dartDoc != null) {
27✔
100
      s.write(makeDartDoc(dartDoc!));
18✔
101
    }
102
    // Resolve name conflicts in function parameter names.
103
    final paramNamer = UniqueNamer({});
27✔
104
    for (final p in functionType.dartTypeParameters) {
77✔
105
      p.name = paramNamer.makeUnique(p.name);
69✔
106
    }
107

108
    final cType = _exposedFunctionTypealias?.getCType(w) ??
29✔
109
        functionType.getCType(w, writeArgumentNames: false);
52✔
110
    final dartType = _exposedFunctionTypealias?.getFfiDartType(w) ??
29✔
111
        functionType.getFfiDartType(w, writeArgumentNames: false);
52✔
112
    final needsWrapper = !functionType.sameDartAndFfiDartType && !isInternal;
56✔
113

114
    final isLeafString = isLeaf ? 'isLeaf:true' : '';
27✔
115
    final funcVarName = w.wrapperLevelUniqueNamer.makeUnique('_$name');
108✔
116
    final ffiReturnType = functionType.returnType.getFfiDartType(w);
81✔
117
    final ffiArgDeclString = functionType.dartTypeParameters
54✔
118
        .map((p) => '${p.type.getFfiDartType(w)} ${p.name},\n')
142✔
119
        .join('');
27✔
120

121
    late final String dartReturnType;
122
    late final String dartArgDeclString;
123
    late final String funcImplCall;
124
    if (needsWrapper) {
125
      dartReturnType = functionType.returnType.getDartType(w);
×
126
      dartArgDeclString = functionType.dartTypeParameters
×
127
          .map((p) => '${p.type.getDartType(w)} ${p.name},\n')
×
128
          .join('');
×
129

130
      final argString = functionType.dartTypeParameters
×
131
          .map((p) =>
×
132
              '${p.type.convertDartTypeToFfiDartType(w, p.name, objCRetain: false)},\n')
×
133
          .join('');
×
134
      funcImplCall = functionType.returnType.convertFfiDartTypeToDartType(
×
135
        w,
136
        '$funcVarName($argString)',
×
137
        ffiNativeConfig.enabled ? 'lib' : 'this',
×
138
        objCRetain: !objCReturnsRetained,
×
139
      );
140
    } else {
141
      dartReturnType = ffiReturnType;
142
      dartArgDeclString = ffiArgDeclString;
143
      final argString =
144
          functionType.dartTypeParameters.map((p) => '${p.name},\n').join('');
177✔
145
      funcImplCall = '$funcVarName($argString)';
27✔
146
    }
147

148
    if (ffiNativeConfig.enabled) {
54✔
149
      final assetString = ffiNativeConfig.assetId != null
2✔
150
          ? ", assetId: '${ffiNativeConfig.assetId}'"
2✔
151
          : '';
1✔
152
      final nativeFuncName = needsWrapper ? funcVarName : enclosingFuncName;
153
      s.write('''
1✔
154
@${w.ffiLibraryPrefix}.Native<$cType>(symbol: '$originalName'$assetString$isLeafString)
2✔
155
external $ffiReturnType $nativeFuncName($ffiArgDeclString);
156

157
''');
1✔
158
      if (needsWrapper) {
159
        final libArg = functionType.returnType.sameDartAndFfiDartType
×
160
            ? ''
161
            : '${w.className} lib, ';
×
162
        s.write('''
×
163
$dartReturnType $enclosingFuncName($libArg$dartArgDeclString) => $funcImplCall;
164

165
''');
×
166
      }
167
    } else {
168
      funcPointerName = w.wrapperLevelUniqueNamer.makeUnique('_${name}Ptr');
130✔
169

170
      // Write enclosing function.
171
      s.write('''
26✔
172
$dartReturnType $enclosingFuncName($dartArgDeclString) {
173
  return $funcImplCall;
174
}
175

176
''');
26✔
177

178
      if (exposeSymbolAddress) {
26✔
179
        // Add to SymbolAddress in writer.
180
        w.symbolAddressWriter.addSymbol(
6✔
181
          type:
182
              '${w.ffiLibraryPrefix}.Pointer<${w.ffiLibraryPrefix}.NativeFunction<$cType>>',
9✔
183
          name: name,
3✔
184
          ptrName: funcPointerName,
3✔
185
        );
186
      }
187

188
      // Write function pointer.
189
      s.write('''
26✔
190
late final $funcPointerName = ${w.lookupFuncIdentifier}<
52✔
191
    ${w.ffiLibraryPrefix}.NativeFunction<$cType>>('$originalName');
52✔
192
late final $funcVarName = $funcPointerName.asFunction<$dartType>($isLeafString);
26✔
193

194
''');
26✔
195
    }
196

197
    return BindingString(type: BindingStringType.func, string: s.toString());
54✔
198
  }
199

200
  @override
29✔
201
  void addDependencies(Set<Binding> dependencies) {
202
    if (dependencies.contains(this)) return;
29✔
203

204
    dependencies.add(this);
29✔
205
    functionType.addDependencies(dependencies);
58✔
206
    if (exposeFunctionTypedefs) {
29✔
207
      _exposedFunctionTypealias!.addDependencies(dependencies);
4✔
208
    }
209
  }
210
}
211

212
/// Represents a Parameter, used in [Func] and [Typealias].
213
class Parameter {
214
  final String? originalName;
215
  String name;
216
  final Type type;
217

218
  Parameter({String? originalName, this.name = '', required Type type})
25✔
219
      : originalName = originalName ?? name,
220
        // A [NativeFunc] is wrapped with a pointer because this is a shorthand
221
        // used in C for Pointer to function.
222
        type = type.typealiasType is NativeFunc ? PointerType(type) : type;
51✔
223
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc