Skip to content

Commit 029838d

Browse files
authored
[ffigen] Fix ARC for blocks (#1491)
1 parent 79b29f2 commit 029838d

33 files changed

+1358
-715
lines changed

pkgs/ffigen/lib/src/code_generator/enum_class.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ class EnumClass extends BindingType {
311311
Writer w,
312312
String value, {
313313
required bool objCRetain,
314+
required bool objCAutorelease,
314315
}) =>
315316
sameDartAndFfiDartType ? value : '$value.value';
316317

pkgs/ffigen/lib/src/code_generator/func.dart

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,12 @@ class Func extends LookUpBinding {
132132
.join('');
133133

134134
final argString = functionType.dartTypeParameters.map((p) {
135-
final type = p.type.convertDartTypeToFfiDartType(w, p.name,
136-
objCRetain: p.objCConsumed);
135+
final type = p.type.convertDartTypeToFfiDartType(
136+
w,
137+
p.name,
138+
objCRetain: p.objCConsumed,
139+
objCAutorelease: false,
140+
);
137141
return '$type,\n';
138142
}).join('');
139143
funcImplCall = functionType.returnType.convertFfiDartTypeToDartType(
@@ -223,11 +227,12 @@ late final $funcVarName = $funcPointerName.asFunction<$dartType>($isLeafString);
223227
}
224228
}
225229

226-
/// Represents a Parameter, used in [Func] and [Typealias].
230+
/// Represents a Parameter, used in [Func], [Typealias], [ObjCMethod], and
231+
/// [ObjCBlock].
227232
class Parameter {
228233
final String? originalName;
229234
String name;
230-
final Type type;
235+
Type type;
231236
final bool objCConsumed;
232237

233238
Parameter({
@@ -239,4 +244,8 @@ class Parameter {
239244
// A [NativeFunc] is wrapped with a pointer because this is a shorthand
240245
// used in C for Pointer to function.
241246
type = type.typealiasType is NativeFunc ? PointerType(type) : type;
247+
248+
String getNativeType({String varName = ''}) =>
249+
'${type.getNativeType(varName: varName)}'
250+
'${objCConsumed ? ' __attribute__((ns_consumed))' : ''}';
242251
}

pkgs/ffigen/lib/src/code_generator/global.dart

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,12 @@ class Global extends LookUpBinding {
5656
if (!constant) {
5757
final releaseOldValue = type
5858
.convertFfiDartTypeToDartType(w, pointerValue, objCRetain: false);
59-
final newValue =
60-
type.convertDartTypeToFfiDartType(w, 'value', objCRetain: true);
59+
final newValue = type.convertDartTypeToFfiDartType(
60+
w,
61+
'value',
62+
objCRetain: true,
63+
objCAutorelease: false,
64+
);
6165
s.write('''set $globalVarName($dartType value) {
6266
$releaseOldValue.ref.release();
6367
$pointerValue = $newValue;

pkgs/ffigen/lib/src/code_generator/objc_block.dart

Lines changed: 69 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,16 @@ import 'writer.dart';
1111

1212
class ObjCBlock extends BindingType {
1313
final Type returnType;
14-
final List<Type> argTypes;
14+
final List<Parameter> params;
15+
final bool returnsRetained;
1516
Func? _wrapListenerBlock;
1617

1718
factory ObjCBlock({
1819
required Type returnType,
19-
required List<Type> argTypes,
20+
required List<Parameter> params,
21+
required bool returnsRetained,
2022
}) {
21-
final usr = _getBlockUsr(returnType, argTypes);
23+
final usr = _getBlockUsr(returnType, params, returnsRetained);
2224

2325
final oldBlock = bindingsIndex.getSeenObjCBlock(usr);
2426
if (oldBlock != null) {
@@ -27,9 +29,10 @@ class ObjCBlock extends BindingType {
2729

2830
final block = ObjCBlock._(
2931
usr: usr,
30-
name: _getBlockName(returnType, argTypes),
32+
name: _getBlockName(returnType, params.map((a) => a.type)),
3133
returnType: returnType,
32-
argTypes: argTypes,
34+
params: params,
35+
returnsRetained: returnsRetained,
3336
);
3437
bindingsIndex.addObjCBlockToSeen(usr, block);
3538

@@ -40,48 +43,54 @@ class ObjCBlock extends BindingType {
4043
required String super.usr,
4144
required super.name,
4245
required this.returnType,
43-
required this.argTypes,
46+
required this.params,
47+
required this.returnsRetained,
4448
}) : super(originalName: name);
4549

4650
// Generates a human readable name for the block based on the args and return
4751
// type. These names will be pretty verbose and unweildy, but they're at least
4852
// sensible and stable. Users can always add their own typedef with a simpler
4953
// name if necessary.
50-
static String _getBlockName(Type returnType, List<Type> argTypes) =>
54+
static String _getBlockName(Type returnType, Iterable<Type> argTypes) =>
5155
'ObjCBlock_${[returnType, ...argTypes].map(_typeName).join('_')}';
5256
static String _typeName(Type type) =>
5357
type.toString().replaceAll(_illegalNameChar, '');
5458
static final _illegalNameChar = RegExp(r'[^0-9a-zA-Z]');
5559

56-
static String _getBlockUsr(Type returnType, List<Type> argTypes) {
60+
static String _getBlockUsr(
61+
Type returnType, List<Parameter> params, bool returnsRetained) {
5762
// Create a fake USR code for the block. This code is used to dedupe blocks
58-
// with the same signature.
63+
// with the same signature. Not intended to be human readable.
5964
final usr = StringBuffer();
60-
usr.write('objcBlock: ${returnType.cacheKey()}');
61-
for (final type in argTypes) {
62-
usr.write(' ${type.cacheKey()}');
65+
usr.write(
66+
'objcBlock: ${returnType.cacheKey()} ${returnsRetained ? 'R' : ''}');
67+
for (final param in params) {
68+
usr.write(' ${param.type.cacheKey()} ${param.objCConsumed ? 'C' : ''}');
6369
}
6470
return usr.toString();
6571
}
6672

6773
bool get hasListener => returnType == voidType;
6874

6975
String _blockType(Writer w) {
70-
final args = argTypes.map((t) => t.getObjCBlockSignatureType(w)).join(', ');
71-
final func = '${returnType.getObjCBlockSignatureType(w)} Function($args)';
76+
final argStr = params.map((param) {
77+
final type = param.type.getObjCBlockSignatureType(w);
78+
return param.objCConsumed
79+
? '${ObjCBuiltInFunctions.consumedType.gen(w)}<$type>'
80+
: type;
81+
}).join(', ');
82+
final retType = returnType.getObjCBlockSignatureType(w);
83+
final retStr = returnsRetained
84+
? '${ObjCBuiltInFunctions.retainedType.gen(w)}<$retType>'
85+
: retType;
86+
final func = '$retStr Function($argStr)';
7287
return '${ObjCBuiltInFunctions.blockType.gen(w)}<$func>';
7388
}
7489

7590
@override
7691
BindingString toBindingString(Writer w) {
7792
final s = StringBuffer();
7893

79-
final params = <Parameter>[];
80-
for (var i = 0; i < argTypes.length; ++i) {
81-
params.add(
82-
Parameter(name: 'arg$i', type: argTypes[i], objCConsumed: false));
83-
}
84-
8594
final voidPtr = PointerType(voidType).getCType(w);
8695
final blockPtr = PointerType(objCBlockType);
8796
final funcType = FunctionType(returnType: returnType, parameters: params);
@@ -135,12 +144,18 @@ $returnFfiDartType $closureTrampoline($blockCType block, $paramsFfiDartType) =>
135144
// Snippet that converts a Dart typed closure to FfiDart type. This snippet
136145
// is used below. Note that the closure being converted is called `fn`.
137146
final convertedFnArgs = params
138-
.map((p) =>
139-
p.type.convertFfiDartTypeToDartType(w, p.name, objCRetain: true))
147+
.map((p) => p.type.convertFfiDartTypeToDartType(
148+
w,
149+
p.name,
150+
objCRetain: !p.objCConsumed,
151+
))
140152
.join(', ');
141153
final convFnInvocation = returnType.convertDartTypeToFfiDartType(
142-
w, 'fn($convertedFnArgs)',
143-
objCRetain: true);
154+
w,
155+
'fn($convertedFnArgs)',
156+
objCRetain: true,
157+
objCAutorelease: !returnsRetained,
158+
);
144159
final convFn = '($paramsFfiDartType) => $convFnInvocation';
145160

146161
// Write the wrapper class.
@@ -183,16 +198,19 @@ abstract final class $name {
183198

184199
// Listener block constructor is only available for void blocks.
185200
if (hasListener) {
186-
// This snippet is the same as the convFn above, except that the args
201+
// This snippet is the same as the convFn above, except that the params
187202
// don't need to be retained because they've already been retained by
188203
// _wrapListenerBlock.
189204
final listenerConvertedFnArgs = params
190205
.map((p) =>
191206
p.type.convertFfiDartTypeToDartType(w, p.name, objCRetain: false))
192207
.join(', ');
193208
final listenerConvFnInvocation = returnType.convertDartTypeToFfiDartType(
194-
w, 'fn($listenerConvertedFnArgs)',
195-
objCRetain: true);
209+
w,
210+
'fn($listenerConvertedFnArgs)',
211+
objCRetain: true,
212+
objCAutorelease: !returnsRetained,
213+
);
196214
final listenerConvFn =
197215
'($paramsFfiDartType) => $listenerConvFnInvocation';
198216
final wrapFn = _wrapListenerBlock?.name;
@@ -236,14 +254,21 @@ abstract final class $name {
236254
extension $callExtension on $blockType {
237255
${returnType.getDartType(w)} call($paramsDartType) =>''');
238256
final callMethodArgs = params
239-
.map((p) =>
240-
p.type.convertDartTypeToFfiDartType(w, p.name, objCRetain: false))
257+
.map((p) => p.type.convertDartTypeToFfiDartType(
258+
w,
259+
p.name,
260+
objCRetain: p.objCConsumed,
261+
objCAutorelease: false,
262+
))
241263
.join(', ');
242264
final callMethodInvocation = '''
243265
ref.pointer.ref.invoke.cast<$natTrampFnType>().asFunction<$trampFuncFfiDartType>()(
244266
ref.pointer, $callMethodArgs)''';
245-
s.write(returnType.convertFfiDartTypeToDartType(w, callMethodInvocation,
246-
objCRetain: false));
267+
s.write(returnType.convertFfiDartTypeToDartType(
268+
w,
269+
callMethodInvocation,
270+
objCRetain: !returnsRetained,
271+
));
247272
s.write(';\n');
248273

249274
s.write('}\n\n');
@@ -257,11 +282,11 @@ ref.pointer.ref.invoke.cast<$natTrampFnType>().asFunction<$trampFuncFfiDartType>
257282

258283
final argsReceived = <String>[];
259284
final retains = <String>[];
260-
for (var i = 0; i < argTypes.length; ++i) {
261-
final t = argTypes[i];
285+
for (var i = 0; i < params.length; ++i) {
286+
final param = params[i];
262287
final argName = 'arg$i';
263-
argsReceived.add(t.getNativeType(varName: argName));
264-
retains.add(t.generateRetain(argName) ?? argName);
288+
argsReceived.add(param.getNativeType(varName: argName));
289+
retains.add(param.type.generateRetain(argName) ?? argName);
265290
}
266291
final fnName = _wrapListenerBlock!.name;
267292
final blockTypedef = w.objCLevelUniqueNamer.makeUnique('ListenerBlock');
@@ -286,11 +311,11 @@ $blockTypedef $fnName($blockTypedef block) NS_RETURNS_RETAINED {
286311
dependencies.add(this);
287312

288313
returnType.addDependencies(dependencies);
289-
for (final t in argTypes) {
290-
t.addDependencies(dependencies);
314+
for (final p in params) {
315+
p.type.addDependencies(dependencies);
291316
}
292317

293-
if (hasListener && argTypes.any((t) => t.generateRetain('') != null)) {
318+
if (hasListener && params.any((p) => p.type.generateRetain('') != null)) {
294319
_wrapListenerBlock = Func(
295320
name: 'wrapListenerBlock_$name',
296321
returnType: this,
@@ -318,8 +343,8 @@ $blockTypedef $fnName($blockTypedef block) NS_RETURNS_RETAINED {
318343

319344
@override
320345
String getNativeType({String varName = ''}) {
321-
final args = argTypes.map<String>((t) => t.getNativeType());
322-
return '${returnType.getNativeType()} (^$varName)(${args.join(', ')})';
346+
final paramStrs = params.map<String>((p) => p.getNativeType());
347+
return '${returnType.getNativeType()} (^$varName)(${paramStrs.join(', ')})';
323348
}
324349

325350
@override
@@ -336,8 +361,9 @@ $blockTypedef $fnName($blockTypedef block) NS_RETURNS_RETAINED {
336361
Writer w,
337362
String value, {
338363
required bool objCRetain,
364+
required bool objCAutorelease,
339365
}) =>
340-
ObjCInterface.generateGetId(value, objCRetain);
366+
ObjCInterface.generateGetId(value, objCRetain, objCAutorelease);
341367

342368
@override
343369
String convertFfiDartTypeToDartType(
@@ -352,5 +378,6 @@ $blockTypedef $fnName($blockTypedef block) NS_RETURNS_RETAINED {
352378
String? generateRetain(String value) => 'objc_retainBlock($value)';
353379

354380
@override
355-
String toString() => '($returnType (^)(${argTypes.join(', ')}))';
381+
String toString() =>
382+
'($returnType (^)(${params.map((p) => p.type.toString()).join(', ')}))';
356383
}

pkgs/ffigen/lib/src/code_generator/objc_built_in_functions.dart

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ class ObjCBuiltInFunctions {
2828
static const objectRelease = ObjCImport('objectRelease');
2929
static const objectBase = ObjCImport('ObjCObjectBase');
3030
static const blockType = ObjCImport('ObjCBlock');
31+
static const consumedType = ObjCImport('Consumed');
32+
static const retainedType = ObjCImport('Retained');
3133
static const protocolMethod = ObjCImport('ObjCProtocolMethod');
3234
static const protocolListenableMethod =
3335
ObjCImport('ObjCProtocolListenableMethod');
@@ -113,8 +115,7 @@ class ObjCBuiltInFunctions {
113115
// the return type is a struct, we need to use objc_msgSend_stret instead, and
114116
// for float return types we need objc_msgSend_fpret.
115117
final _msgSendFuncs = <String, ObjCMsgSendFunc>{};
116-
ObjCMsgSendFunc getMsgSendFunc(
117-
Type returnType, List<ObjCMethodParam> params) {
118+
ObjCMsgSendFunc getMsgSendFunc(Type returnType, List<Parameter> params) {
118119
var key = returnType.cacheKey();
119120
for (final p in params) {
120121
key += ' ${p.type.cacheKey()}';
@@ -255,8 +256,8 @@ class ObjCMsgSendFunc {
255256
late final ObjCMsgSendVariantFunc normalFunc;
256257
late final ObjCMsgSendVariantFunc? variantFunc;
257258

258-
ObjCMsgSendFunc(String name, Type returnType, List<ObjCMethodParam> params,
259-
this.useVariants)
259+
ObjCMsgSendFunc(
260+
String name, Type returnType, List<Parameter> params, this.useVariants)
260261
: variant = ObjCMsgSendVariant.fromReturnType(returnType) {
261262
normalFunc = ObjCMsgSendVariantFunc(
262263
name: name,
@@ -284,14 +285,13 @@ class ObjCMsgSendFunc {
284285
}
285286
}
286287

287-
static List<Parameter> _params(List<ObjCMethodParam> params,
288-
{Type? structRetPtr}) {
288+
static List<Parameter> _params(List<Parameter> params, {Type? structRetPtr}) {
289289
return [
290290
if (structRetPtr != null)
291291
Parameter(type: structRetPtr, objCConsumed: false),
292292
Parameter(type: PointerType(objCObjectType), objCConsumed: false),
293293
Parameter(type: PointerType(objCSelType), objCConsumed: false),
294-
for (final p in params) Parameter(type: p.type, objCConsumed: false),
294+
...params,
295295
];
296296
}
297297

0 commit comments

Comments
 (0)