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

dart-lang / ffigen / 6470767942

10 Oct 2023 02:29PM UTC coverage: 91.966% (-0.6%) from 92.593%
6470767942

push

github

web-flow
Refactor `sameFfiDartAndCType` to not require a `Writer` (#629)

40 of 40 new or added lines in 12 files covered. (100.0%)

3709 of 4033 relevant lines covered (91.97%)

28.07 hits per line

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

98.15
/lib/src/code_generator/objc_block.dart
1
// Copyright (c) 2022, 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

7
import 'binding_string.dart';
8
import 'writer.dart';
9

10
class ObjCBlock extends BindingType {
11
  final Type returnType;
12
  final List<Type> argTypes;
13
  final ObjCBuiltInFunctions builtInFunctions;
14

15
  ObjCBlock({
2✔
16
    required String usr,
17
    required Type returnType,
18
    required List<Type> argTypes,
19
    required ObjCBuiltInFunctions builtInFunctions,
20
  }) : this._(
2✔
21
          usr: usr,
22
          name: _getBlockName(returnType, argTypes),
2✔
23
          returnType: returnType,
24
          argTypes: argTypes,
25
          builtInFunctions: builtInFunctions,
26
        );
27

28
  ObjCBlock._({
2✔
29
    required String usr,
30
    required String name,
31
    required this.returnType,
32
    required this.argTypes,
33
    required this.builtInFunctions,
34
  }) : super(
2✔
35
          usr: usr,
36
          originalName: name,
37
          name: name,
38
        );
39

40
  // Generates a human readable name for the block based on the args and return
41
  // type. These names will be pretty verbose and unweildy, but they're at least
42
  // sensible and stable. Users can always add their own typedef with a simpler
43
  // name if necessary.
44
  static String _getBlockName(Type returnType, List<Type> argTypes) =>
2✔
45
      'ObjCBlock_${[returnType, ...argTypes].map(_typeName).join('_')}';
10✔
46
  static String _typeName(Type type) =>
2✔
47
      type.toString().replaceAll(_illegalNameChar, '');
6✔
48
  static final _illegalNameChar = RegExp(r'[^0-9a-zA-Z]');
6✔
49

50
  @override
2✔
51
  BindingString toBindingString(Writer w) {
52
    final s = StringBuffer();
2✔
53

54
    builtInFunctions.ensureBlockUtilsExist(w, s);
4✔
55

56
    final params = <Parameter>[];
2✔
57
    for (int i = 0; i < argTypes.length; ++i) {
8✔
58
      params.add(Parameter(name: 'arg$i', type: argTypes[i]));
10✔
59
    }
60

61
    final isVoid = returnType == voidType;
6✔
62
    final voidPtr = PointerType(voidType).getCType(w);
6✔
63
    final blockPtr = PointerType(builtInFunctions.blockStruct);
6✔
64
    final funcType = FunctionType(returnType: returnType, parameters: params);
4✔
65
    final natFnType = NativeFunc(funcType);
2✔
66
    final natFnPtr = PointerType(natFnType).getCType(w);
4✔
67
    final funcPtrTrampoline =
68
        w.topLevelUniqueNamer.makeUnique('_${name}_fnPtrTrampoline');
8✔
69
    final closureTrampoline =
70
        w.topLevelUniqueNamer.makeUnique('_${name}_closureTrampoline');
8✔
71
    final registerClosure =
72
        w.topLevelUniqueNamer.makeUnique('_${name}_registerClosure');
8✔
73
    final closureRegistry =
74
        w.topLevelUniqueNamer.makeUnique('_${name}_closureRegistry');
8✔
75
    final closureRegistryIndex =
76
        w.topLevelUniqueNamer.makeUnique('_${name}_closureRegistryIndex');
8✔
77
    final trampFuncType = FunctionType(
2✔
78
        returnType: returnType,
2✔
79
        parameters: [Parameter(type: blockPtr, name: 'block'), ...params]);
6✔
80
    final natTrampFnType = NativeFunc(trampFuncType);
2✔
81
    final nativeCallableType =
82
        '${w.ffiLibraryPrefix}.NativeCallable<${trampFuncType.getCType(w)}>';
6✔
83

84
    // Write the function pointer based trampoline function.
85
    s.write(returnType.getFfiDartType(w));
6✔
86
    s.write(' $funcPtrTrampoline(${blockPtr.getCType(w)} block');
6✔
87
    for (int i = 0; i < params.length; ++i) {
6✔
88
      s.write(', ${params[i].type.getFfiDartType(w)} ${params[i].name}');
14✔
89
    }
90
    s.write(') {\n');
2✔
91
    s.write('  ${isVoid ? '' : 'return '}block.ref.target.cast<'
4✔
92
        '${natFnType.getFfiDartType(w)}>().asFunction<'
2✔
93
        '${funcType.getFfiDartType(w)}>()(');
2✔
94
    for (int i = 0; i < params.length; ++i) {
6✔
95
      s.write('${i == 0 ? '' : ', '}${params[i].name}');
10✔
96
    }
97
    s.write(');\n');
2✔
98
    s.write('}\n');
2✔
99

100
    // Write the closure registry function.
101
    s.write('''
2✔
102
final $closureRegistry = <int, Function>{};
103
int $closureRegistryIndex = 0;
104
$voidPtr $registerClosure(Function fn) {
105
  final id = ++$closureRegistryIndex;
106
  $closureRegistry[id] = fn;
107
  return $voidPtr.fromAddress(id);
108
}
109
''');
2✔
110

111
    // Write the closure based trampoline function.
112
    s.write(returnType.getFfiDartType(w));
6✔
113
    s.write(' $closureTrampoline(${blockPtr.getCType(w)} block');
6✔
114
    for (int i = 0; i < params.length; ++i) {
6✔
115
      s.write(', ${params[i].type.getFfiDartType(w)} ${params[i].name}');
14✔
116
    }
117
    s.write(') {\n');
2✔
118
    s.write('  ${isVoid ? '' : 'return '}');
4✔
119
    s.write('($closureRegistry[block.ref.target.address]');
4✔
120
    s.write(' as ${returnType.getFfiDartType(w)} Function(');
8✔
121
    for (int i = 0; i < params.length; ++i) {
6✔
122
      s.write('${i == 0 ? '' : ', '}${params[i].type.getFfiDartType(w)}');
12✔
123
    }
124
    s.write('))');
2✔
125
    s.write('(');
2✔
126
    for (int i = 0; i < params.length; ++i) {
6✔
127
      s.write('${i == 0 ? '' : ', '}${params[i].name}');
10✔
128
    }
129
    s.write(');\n');
2✔
130
    s.write('}\n');
2✔
131

132
    // Write the wrapper class.
133
    final defaultValue = returnType.getDefaultValue(w, '_lib');
4✔
134
    final exceptionalReturn = defaultValue == null ? '' : ', $defaultValue';
2✔
135
    s.write('''
2✔
136
class $name extends _ObjCBlockBase {
2✔
137
  $name._(${blockPtr.getCType(w)} id, ${w.className} lib) :
6✔
138
      super._(id, lib, retain: false, release: true);
139

140
  /// Creates a block from a C function pointer.
141
  ///
142
  /// This block must be invoked by native code running on the same thread as
143
  /// the isolate that registered it. Invoking the block on the wrong thread
144
  /// will result in a crash.
145
  $name.fromFunctionPointer(${w.className} lib, $natFnPtr ptr) :
4✔
146
      this._(lib.${builtInFunctions.newBlock.name}(
6✔
147
          _cFuncTrampoline ??= ${w.ffiLibraryPrefix}.Pointer.fromFunction<
2✔
148
              ${trampFuncType.getCType(w)}>($funcPtrTrampoline
2✔
149
                  $exceptionalReturn).cast(), ptr.cast()), lib);
150
  static $voidPtr? _cFuncTrampoline;
151

152
  /// Creates a block from a Dart function.
153
  ///
154
  /// This block must be invoked by native code running on the same thread as
155
  /// the isolate that registered it. Invoking the block on the wrong thread
156
  /// will result in a crash.
157
  $name.fromFunction(${w.className} lib, ${funcType.getFfiDartType(w)} fn) :
6✔
158
      this._(lib.${builtInFunctions.newBlock.name}(
6✔
159
          _dartFuncTrampoline ??= ${w.ffiLibraryPrefix}.Pointer.fromFunction<
2✔
160
              ${trampFuncType.getCType(w)}>($closureTrampoline
2✔
161
                  $exceptionalReturn).cast(), $registerClosure(fn)), lib);
162
  static $voidPtr? _dartFuncTrampoline;
163

164
''');
2✔
165

166
    // Listener block constructor is only available for void blocks.
167
    if (isVoid) {
168
      s.write('''
2✔
169
  /// Creates a listener block from a Dart function.
170
  ///
171
  /// This is based on FFI's NativeCallable.listener, and has the same
172
  /// capabilities and limitations. This block can be invoked from any thread,
173
  /// but only supports void functions, and is not run synchronously. See
174
  /// NativeCallable.listener for more details.
175
  ///
176
  /// Note that unlike the default behavior of NativeCallable.listener, listener
177
  /// blocks do not keep the isolate alive.
178
  $name.listener(${w.className} lib, ${funcType.getFfiDartType(w)} fn) :
6✔
179
      this._(lib.${builtInFunctions.newBlock.name}(
6✔
180
          (_dartFuncListenerTrampoline ??= $nativeCallableType.listener($closureTrampoline
181
                  $exceptionalReturn)..keepIsolateAlive = false).nativeFunction.cast(),
182
          $registerClosure(fn)), lib);
183
  static $nativeCallableType? _dartFuncListenerTrampoline;
184

185
''');
2✔
186
    }
187

188
    // Call method.
189
    s.write('  ${returnType.getFfiDartType(w)} call(');
8✔
190
    for (int i = 0; i < params.length; ++i) {
6✔
191
      s.write('${i == 0 ? '' : ', '}${params[i].type.getFfiDartType(w)}');
12✔
192
      s.write(' ${params[i].name}');
8✔
193
    }
194
    s.write(''') {
2✔
195
    ${isVoid ? '' : 'return '}_id.ref.invoke.cast<
196
        ${natTrampFnType.getCType(w)}>().asFunction<
2✔
197
            ${trampFuncType.getFfiDartType(w)}>()(_id''');
4✔
198
    for (int i = 0; i < params.length; ++i) {
6✔
199
      s.write(', ${params[i].name}');
8✔
200
    }
201
    s.write(''');
2✔
202
  }''');
203

204
    s.write('}\n');
2✔
205
    return BindingString(
2✔
206
        type: BindingStringType.objcBlock, string: s.toString());
2✔
207
  }
208

209
  @override
2✔
210
  void addDependencies(Set<Binding> dependencies) {
211
    if (dependencies.contains(this)) return;
2✔
212
    dependencies.add(this);
2✔
213

214
    returnType.addDependencies(dependencies);
4✔
215
    for (final t in argTypes) {
4✔
216
      t.addDependencies(dependencies);
2✔
217
    }
218
    builtInFunctions.addBlockDependencies(dependencies);
4✔
219
  }
220

221
  @override
2✔
222
  String getCType(Writer w) =>
223
      PointerType(builtInFunctions.blockStruct).getCType(w);
8✔
224

225
  @override
2✔
226
  String getDartType(Writer w) => name;
2✔
227

228
  @override
×
229
  bool get sameFfiDartAndCType => true;
230

231
  @override
×
232
  bool get sameDartAndCType => false;
233

234
  @override
2✔
235
  String toString() => '($returnType (^)(${argTypes.join(', ')}))';
8✔
236
}
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