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

dart-lang / ffigen / 4784152981

24 Apr 2023 08:39AM UTC coverage: 92.363% (-7.0%) from 99.366%
4784152981

push

github

GitHub
Merge pull request #560 from dart-lang/stable-to-7-x

1780 of 1780 new or added lines in 41 files covered. (100.0%)

3229 of 3496 relevant lines covered (92.36%)

25.17 hits per line

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

99.43
/lib/src/code_generator/objc_built_in_functions.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
/// Built in functions used by the Objective C bindings.
11
class ObjCBuiltInFunctions {
12
  late final _registerNameFunc = Func(
4✔
13
    name: '_sel_registerName',
14
    originalName: 'sel_registerName',
15
    returnType: PointerType(objCSelType),
4✔
16
    parameters: [Parameter(name: 'str', type: PointerType(charType))],
8✔
17
    isInternal: true,
18
  );
19
  late final registerName = ObjCInternalFunction(
4✔
20
      '_registerName', _registerNameFunc, (Writer w, String name) {
4✔
21
    final selType = _registerNameFunc.functionType.returnType.getCType(w);
8✔
22
    return '''
23
$selType $name(String name) {
24
  final cstr = name.toNativeUtf8();
25
  final sel = ${_registerNameFunc.name}(cstr.cast());
4✔
26
  ${w.ffiPkgLibraryPrefix}.calloc.free(cstr);
2✔
27
  return sel;
28
}
29
''';
2✔
30
  });
31

32
  late final _getClassFunc = Func(
4✔
33
    name: '_objc_getClass',
34
    originalName: 'objc_getClass',
35
    returnType: PointerType(objCObjectType),
4✔
36
    parameters: [Parameter(name: 'str', type: PointerType(charType))],
8✔
37
    isInternal: true,
38
  );
39
  late final getClass =
2✔
40
      ObjCInternalFunction('_getClass', _getClassFunc, (Writer w, String name) {
6✔
41
    final objType = _getClassFunc.functionType.returnType.getCType(w);
8✔
42
    return '''
43
$objType $name(String name) {
44
  final cstr = name.toNativeUtf8();
45
  final clazz = ${_getClassFunc.name}(cstr.cast());
4✔
46
  ${w.ffiPkgLibraryPrefix}.calloc.free(cstr);
2✔
47
  if (clazz == ${w.ffiLibraryPrefix}.nullptr) {
2✔
48
    throw Exception('Failed to load Objective-C class: \$name');
49
  }
50
  return clazz;
51
}
52
''';
2✔
53
  });
54

55
  late final _retainFunc = Func(
4✔
56
    name: '_objc_retain',
57
    originalName: 'objc_retain',
58
    returnType: PointerType(objCObjectType),
4✔
59
    parameters: [Parameter(name: 'value', type: PointerType(objCObjectType))],
8✔
60
    isInternal: true,
61
  );
62
  late final _releaseFunc = Func(
4✔
63
    name: '_objc_release',
64
    originalName: 'objc_release',
65
    returnType: voidType,
2✔
66
    parameters: [Parameter(name: 'value', type: PointerType(objCObjectType))],
8✔
67
    isInternal: true,
68
  );
69
  late final _releaseFinalizer = ObjCInternalGlobal(
4✔
70
    '_objc_releaseFinalizer',
71
    (Writer w) => '${w.ffiLibraryPrefix}.NativeFinalizer('
6✔
72
        '${_releaseFunc.funcPointerName}.cast())',
4✔
73
    _releaseFunc,
2✔
74
  );
75

76
  late final _blockCopyFunc = Func(
4✔
77
    name: '_Block_copy',
78
    originalName: '_Block_copy',
79
    returnType: PointerType(voidType),
4✔
80
    parameters: [Parameter(name: 'value', type: PointerType(voidType))],
8✔
81
    isInternal: true,
82
  );
83
  late final _blockReleaseFunc = Func(
4✔
84
    name: '_Block_release',
85
    originalName: '_Block_release',
86
    returnType: voidType,
2✔
87
    parameters: [Parameter(name: 'value', type: PointerType(voidType))],
8✔
88
    isInternal: true,
89
  );
90
  late final _blockReleaseFinalizer = ObjCInternalGlobal(
4✔
91
    '_objc_releaseFinalizer',
92
    (Writer w) => '${w.ffiLibraryPrefix}.NativeFinalizer('
6✔
93
        '${_blockReleaseFunc.funcPointerName}.cast())',
4✔
94
    _blockReleaseFunc,
2✔
95
  );
96

97
  // We need to load a separate instance of objc_msgSend for each signature.
98
  final _msgSendFuncs = <String, Func>{};
99
  Func getMsgSendFunc(Type returnType, List<ObjCMethodParam> params) {
2✔
100
    var key = returnType.cacheKey();
2✔
101
    for (final p in params) {
4✔
102
      key += ' ${p.type.cacheKey()}';
8✔
103
    }
104
    return _msgSendFuncs[key] ??= Func(
6✔
105
      name: '_objc_msgSend_${_msgSendFuncs.length}',
6✔
106
      originalName: 'objc_msgSend',
107
      returnType: returnType,
108
      parameters: [
2✔
109
        Parameter(name: 'obj', type: PointerType(objCObjectType)),
6✔
110
        Parameter(name: 'sel', type: PointerType(objCSelType)),
6✔
111
        for (final p in params) Parameter(name: p.name, type: p.type),
8✔
112
      ],
113
      isInternal: true,
114
    );
115
  }
116

117
  final _selObjects = <String, ObjCInternalGlobal>{};
118
  ObjCInternalGlobal getSelObject(String methodName) {
2✔
119
    return _selObjects[methodName] ??= ObjCInternalGlobal(
6✔
120
      '_sel_${methodName.replaceAll(":", "_")}',
4✔
121
      (Writer w) => '${registerName.name}("$methodName")',
8✔
122
      registerName,
2✔
123
    );
124
  }
125

126
  // See https://clang.llvm.org/docs/Block-ABI-Apple.html
127
  late final blockStruct = Struct(
4✔
128
    name: '_ObjCBlock',
129
    isInternal: true,
130
    members: [
2✔
131
      Member(name: 'isa', type: PointerType(voidType)),
6✔
132
      Member(name: 'flags', type: intType),
4✔
133
      Member(name: 'reserved', type: intType),
4✔
134
      Member(name: 'invoke', type: PointerType(voidType)),
6✔
135
      Member(name: 'descriptor', type: PointerType(blockDescStruct)),
6✔
136
      Member(name: 'target', type: PointerType(voidType)),
6✔
137
    ],
138
  );
139
  late final blockDescStruct = Struct(
4✔
140
    name: '_ObjCBlockDesc',
141
    isInternal: true,
142
    members: [
2✔
143
      Member(name: 'reserved', type: unsignedLongType),
4✔
144
      Member(name: 'size', type: unsignedLongType),
4✔
145
      Member(name: 'copy_helper', type: PointerType(voidType)),
6✔
146
      Member(name: 'dispose_helper', type: PointerType(voidType)),
6✔
147
      Member(name: 'signature', type: PointerType(charType)),
6✔
148
    ],
149
  );
150
  late final newBlockDesc =
2✔
151
      ObjCInternalFunction('_newBlockDesc', null, (Writer w, String name) {
4✔
152
    final blockType = blockStruct.getCType(w);
4✔
153
    final descType = blockDescStruct.getCType(w);
4✔
154
    final descPtr = PointerType(blockDescStruct).getCType(w);
6✔
155
    return '''
156
$descPtr $name() {
157
  final d = ${w.ffiPkgLibraryPrefix}.calloc.allocate<$descType>(
2✔
158
      ${w.ffiLibraryPrefix}.sizeOf<$descType>());
2✔
159
  d.ref.reserved = 0;
160
  d.ref.size = ${w.ffiLibraryPrefix}.sizeOf<$blockType>();
2✔
161
  d.ref.copy_helper = ${w.ffiLibraryPrefix}.nullptr;
2✔
162
  d.ref.dispose_helper = ${w.ffiLibraryPrefix}.nullptr;
2✔
163
  d.ref.signature = ${w.ffiLibraryPrefix}.nullptr;
2✔
164
  return d;
165
}
166
''';
2✔
167
  });
168
  late final blockDescSingleton = ObjCInternalGlobal(
4✔
169
    '_objc_block_desc',
170
    (Writer w) => '${newBlockDesc.name}()',
8✔
171
    blockDescStruct,
2✔
172
  );
173
  late final concreteGlobalBlock = ObjCInternalGlobal(
4✔
174
    '_objc_concrete_global_block',
175
    (Writer w) => '${w.lookupFuncIdentifier}<${voidType.getCType(w)}>('
10✔
176
        "'_NSConcreteGlobalBlock')",
177
  );
178
  late final newBlock = ObjCInternalFunction('_newBlock', _blockCopyFunc,
6✔
179
      (Writer w, String name) {
2✔
180
    final blockType = blockStruct.getCType(w);
4✔
181
    final blockPtr = PointerType(blockStruct).getCType(w);
6✔
182
    final voidPtr = PointerType(voidType).getCType(w);
6✔
183
    return '''
184
$blockPtr $name($voidPtr invoke, $voidPtr target) {
185
  final b = ${w.ffiPkgLibraryPrefix}.calloc.allocate<$blockType>(
2✔
186
      ${w.ffiLibraryPrefix}.sizeOf<$blockType>());
2✔
187
  b.ref.isa = ${concreteGlobalBlock.name};
4✔
188
  b.ref.flags = 0;
189
  b.ref.reserved = 0;
190
  b.ref.invoke = invoke;
191
  b.ref.target = target;
192
  b.ref.descriptor = ${blockDescSingleton.name};
4✔
193
  final copy = ${_blockCopyFunc.name}(b.cast()).cast<$blockType>();
4✔
194
  ${w.ffiPkgLibraryPrefix}.calloc.free(b);
2✔
195
  return copy;
196
}
197
''';
2✔
198
  });
199

200
  void _writeFinalizableClass(
2✔
201
      Writer w,
202
      StringBuffer s,
203
      String name,
204
      String kind,
205
      String idType,
206
      String retain,
207
      String release,
208
      String finalizer) {
209
    s.write('''
2✔
210
class $name implements ${w.ffiLibraryPrefix}.Finalizable {
2✔
211
  final $idType _id;
212
  final ${w.className} _lib;
2✔
213
  bool _pendingRelease;
214

215
  $name._(this._id, this._lib,
216
      {bool retain = false, bool release = false}) : _pendingRelease = release {
217
    if (retain) {
218
      _lib.$retain(_id.cast());
219
    }
220
    if (release) {
221
      _lib.$finalizer.attach(this, _id.cast(), detach: this);
222
    }
223
  }
224

225
  /// Releases the reference to the underlying ObjC $kind held by this wrapper.
226
  /// Throws a StateError if this wrapper doesn't currently hold a reference.
227
  void release() {
228
    if (_pendingRelease) {
229
      _pendingRelease = false;
230
      _lib.$release(_id.cast());
231
      _lib.$finalizer.detach(this);
232
    } else {
233
      throw StateError(
234
          'Released an ObjC $kind that was unowned or already released.');
235
    }
236
  }
237

238
  @override
239
  bool operator ==(Object other) {
240
    return other is $name && _id == other._id;
241
  }
242

243
  @override
244
  int get hashCode => _id.hashCode;
245
}
246
''');
2✔
247
  }
248

249
  bool utilsExist = false;
250
  void ensureUtilsExist(Writer w, StringBuffer s) {
2✔
251
    if (utilsExist) return;
2✔
252
    utilsExist = true;
2✔
253
    _writeFinalizableClass(
2✔
254
        w,
255
        s,
256
        '_ObjCWrapper',
257
        'object',
258
        PointerType(objCObjectType).getCType(w),
6✔
259
        _retainFunc.name,
4✔
260
        _releaseFunc.name,
4✔
261
        _releaseFinalizer.name);
4✔
262
  }
263

264
  bool blockUtilsExist = false;
265
  void ensureBlockUtilsExist(Writer w, StringBuffer s) {
2✔
266
    if (blockUtilsExist) return;
2✔
267
    blockUtilsExist = true;
2✔
268
    _writeFinalizableClass(
2✔
269
        w,
270
        s,
271
        '_ObjCBlockBase',
272
        'block',
273
        PointerType(blockStruct).getCType(w),
6✔
274
        _blockCopyFunc.name,
4✔
275
        _blockReleaseFunc.name,
4✔
276
        _blockReleaseFinalizer.name);
4✔
277
  }
278

279
  void addDependencies(Set<Binding> dependencies) {
2✔
280
    registerName.addDependencies(dependencies);
4✔
281
    getClass.addDependencies(dependencies);
4✔
282
    _retainFunc.addDependencies(dependencies);
4✔
283
    _releaseFunc.addDependencies(dependencies);
4✔
284
    _releaseFinalizer.addDependencies(dependencies);
4✔
285
    for (final func in _msgSendFuncs.values) {
6✔
286
      func.addDependencies(dependencies);
2✔
287
    }
288
    for (final sel in _selObjects.values) {
6✔
289
      sel.addDependencies(dependencies);
2✔
290
    }
291
  }
292

293
  void addBlockDependencies(Set<Binding> dependencies) {
2✔
294
    newBlockDesc.addDependencies(dependencies);
4✔
295
    blockDescSingleton.addDependencies(dependencies);
4✔
296
    blockStruct.addDependencies(dependencies);
4✔
297
    concreteGlobalBlock.addDependencies(dependencies);
4✔
298
    newBlock.addDependencies(dependencies);
4✔
299
    _blockCopyFunc.addDependencies(dependencies);
4✔
300
    _blockReleaseFunc.addDependencies(dependencies);
4✔
301
    _blockReleaseFinalizer.addDependencies(dependencies);
4✔
302
  }
303

304
  final _interfaceRegistry = <String, ObjCInterface>{};
305
  void registerInterface(ObjCInterface interface) {
2✔
306
    _interfaceRegistry[interface.originalName] = interface;
6✔
307
  }
308

309
  ObjCInterface get nsData {
2✔
310
    return _interfaceRegistry["NSData"] ??
4✔
311
        (ObjCInterface(
×
312
          originalName: "NSData",
313
          builtInFunctions: this,
314
        ));
315
  }
316

317
  void generateNSStringUtils(Writer w, StringBuffer s) {
2✔
318
    // Generate a constructor that wraps stringWithCharacters, and a toString
319
    // method that wraps dataUsingEncoding.
320
    s.write('''
2✔
321
  factory NSString(${w.className} _lib, String str) {
2✔
322
    final cstr = str.toNativeUtf16();
323
    final nsstr = stringWithCharacters_length_(_lib, cstr.cast(), str.length);
324
    ${w.ffiPkgLibraryPrefix}.calloc.free(cstr);
2✔
325
    return nsstr;
326
  }
327

328
  @override
329
  String toString() {
330
    final data = dataUsingEncoding_(
331
        0x94000100 /* NSUTF16LittleEndianStringEncoding */);
332
    return data.bytes.cast<${w.ffiPkgLibraryPrefix}.Utf16>().toDartString(
2✔
333
        length: length);
334
  }
335
''');
2✔
336
  }
337

338
  void generateStringUtils(Writer w, StringBuffer s) {
2✔
339
    // Generate an extension on String to convert to NSString
340
    s.write('''
2✔
341
extension StringToNSString on String {
342
  NSString toNSString(${w.className} lib) => NSString(lib, this);
2✔
343
}
344
''');
2✔
345
  }
346
}
347

348
/// Functions only used internally by ObjC bindings, which may or may not wrap a
349
/// native function, such as getClass.
350
class ObjCInternalFunction extends LookUpBinding {
351
  final Func? _wrappedFunction;
352
  final String Function(Writer, String) _toBindingString;
353

354
  ObjCInternalFunction(
2✔
355
      String name, this._wrappedFunction, this._toBindingString)
356
      : super(originalName: name, name: name, isInternal: true);
2✔
357

358
  @override
2✔
359
  BindingString toBindingString(Writer w) {
360
    name = w.wrapperLevelUniqueNamer.makeUnique(name);
8✔
361
    return BindingString(
2✔
362
        type: BindingStringType.func, string: _toBindingString(w, name));
6✔
363
  }
364

365
  @override
2✔
366
  void addDependencies(Set<Binding> dependencies) {
367
    if (dependencies.contains(this)) return;
2✔
368
    dependencies.add(this);
2✔
369
    _wrappedFunction?.addDependencies(dependencies);
4✔
370
  }
371
}
372

373
/// Globals only used internally by ObjC bindings, such as classes and SELs.
374
class ObjCInternalGlobal extends LookUpBinding {
375
  final String Function(Writer) makeValue;
376
  Binding? binding;
377

378
  ObjCInternalGlobal(String name, this.makeValue, [this.binding])
2✔
379
      : super(originalName: name, name: name, isInternal: true);
2✔
380

381
  @override
2✔
382
  BindingString toBindingString(Writer w) {
383
    final s = StringBuffer();
2✔
384
    name = w.wrapperLevelUniqueNamer.makeUnique(name);
8✔
385
    s.write('late final $name = ${makeValue(w)};');
10✔
386
    return BindingString(type: BindingStringType.global, string: s.toString());
4✔
387
  }
388

389
  @override
2✔
390
  void addDependencies(Set<Binding> dependencies) {
391
    if (dependencies.contains(this)) return;
2✔
392
    dependencies.add(this);
2✔
393
    binding?.addDependencies(dependencies);
4✔
394
  }
395
}
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

© 2026 Coveralls, Inc