diff --git a/pkgs/ffigen/lib/src/code_generator/func.dart b/pkgs/ffigen/lib/src/code_generator/func.dart index debc7e25f..065ac9edc 100644 --- a/pkgs/ffigen/lib/src/code_generator/func.dart +++ b/pkgs/ffigen/lib/src/code_generator/func.dart @@ -132,8 +132,8 @@ class Func extends LookUpBinding { .join(''); final argString = functionType.dartTypeParameters.map((p) { - final type = - p.type.convertDartTypeToFfiDartType(w, p.name, objCRetain: false); + final type = p.type.convertDartTypeToFfiDartType(w, p.name, + objCRetain: p.objCConsumed); return '$type,\n'; }).join(''); funcImplCall = functionType.returnType.convertFfiDartTypeToDartType( @@ -228,9 +228,14 @@ class Parameter { final String? originalName; String name; final Type type; - - Parameter({String? originalName, this.name = '', required Type type}) - : originalName = originalName ?? name, + final bool objCConsumed; + + Parameter({ + String? originalName, + this.name = '', + required Type type, + required this.objCConsumed, + }) : 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; diff --git a/pkgs/ffigen/lib/src/code_generator/func_type.dart b/pkgs/ffigen/lib/src/code_generator/func_type.dart index a0cb60925..de80572fb 100644 --- a/pkgs/ffigen/lib/src/code_generator/func_type.dart +++ b/pkgs/ffigen/lib/src/code_generator/func_type.dart @@ -114,6 +114,7 @@ class FunctionType extends Type { type: parameters[i].type, originalName: names[i], name: finalName, + objCConsumed: false, ); } } diff --git a/pkgs/ffigen/lib/src/code_generator/objc_block.dart b/pkgs/ffigen/lib/src/code_generator/objc_block.dart index 6a5a8ae55..6cd439fa2 100644 --- a/pkgs/ffigen/lib/src/code_generator/objc_block.dart +++ b/pkgs/ffigen/lib/src/code_generator/objc_block.dart @@ -78,7 +78,8 @@ class ObjCBlock extends BindingType { final params = []; for (var i = 0; i < argTypes.length; ++i) { - params.add(Parameter(name: 'arg$i', type: argTypes[i])); + params.add( + Parameter(name: 'arg$i', type: argTypes[i], objCConsumed: false)); } final voidPtr = PointerType(voidType).getCType(w); @@ -95,9 +96,10 @@ class ObjCBlock extends BindingType { final newPointerBlock = ObjCBuiltInFunctions.newPointerBlock.gen(w); final newClosureBlock = ObjCBuiltInFunctions.newClosureBlock.gen(w); final getBlockClosure = ObjCBuiltInFunctions.getBlockClosure.gen(w); - final trampFuncType = FunctionType( - returnType: returnType, - parameters: [Parameter(type: blockPtr, name: 'block'), ...params]); + final trampFuncType = FunctionType(returnType: returnType, parameters: [ + Parameter(type: blockPtr, name: 'block', objCConsumed: false), + ...params + ]); final trampFuncCType = trampFuncType.getCType(w, writeArgumentNames: false); final trampFuncFfiDartType = trampFuncType.getFfiDartType(w, writeArgumentNames: false); @@ -292,7 +294,7 @@ $blockTypedef $fnName($blockTypedef block) NS_RETURNS_RETAINED { _wrapListenerBlock = Func( name: 'wrapListenerBlock_$name', returnType: this, - parameters: [Parameter(name: 'block', type: this)], + parameters: [Parameter(name: 'block', type: this, objCConsumed: false)], objCReturnsRetained: true, isLeaf: true, isInternal: true, diff --git a/pkgs/ffigen/lib/src/code_generator/objc_built_in_functions.dart b/pkgs/ffigen/lib/src/code_generator/objc_built_in_functions.dart index e2944b61c..3315000e3 100644 --- a/pkgs/ffigen/lib/src/code_generator/objc_built_in_functions.dart +++ b/pkgs/ffigen/lib/src/code_generator/objc_built_in_functions.dart @@ -287,10 +287,11 @@ class ObjCMsgSendFunc { static List _params(List params, {Type? structRetPtr}) { return [ - if (structRetPtr != null) Parameter(type: structRetPtr), - Parameter(type: PointerType(objCObjectType)), - Parameter(type: PointerType(objCSelType)), - for (final p in params) Parameter(type: p.type), + if (structRetPtr != null) + Parameter(type: structRetPtr, objCConsumed: false), + Parameter(type: PointerType(objCObjectType), objCConsumed: false), + Parameter(type: PointerType(objCSelType), objCConsumed: false), + for (final p in params) Parameter(type: p.type, objCConsumed: false), ]; } diff --git a/pkgs/ffigen/lib/src/code_generator/objc_interface.dart b/pkgs/ffigen/lib/src/code_generator/objc_interface.dart index 74ec46754..c76af9cce 100644 --- a/pkgs/ffigen/lib/src/code_generator/objc_interface.dart +++ b/pkgs/ffigen/lib/src/code_generator/objc_interface.dart @@ -64,12 +64,11 @@ class ObjCInterface extends BindingType with ObjCMethods { type: BindingStringType.objcInterface, string: ''); } - String paramsToString(List params, - {required bool isStatic}) { - final stringParams = []; - - stringParams.addAll( - params.map((p) => '${_getConvertedType(p.type, w, name)} ${p.name}')); + String paramsToString(List params) { + final stringParams = [ + for (final p in params) + '${_getConvertedType(p.type, w, name)} ${p.name}', + ]; return '(${stringParams.join(", ")})'; } @@ -120,7 +119,10 @@ class ObjCInterface extends BindingType with ObjCMethods { var returnType = m.returnType; var params = m.params; if (isStret) { - params = [ObjCMethodParam(PointerType(returnType), 'stret'), ...params]; + params = [ + ObjCMethodParam(PointerType(returnType), 'stret', consumed: false), + ...params + ]; returnType = voidType; } @@ -148,14 +150,14 @@ class ObjCInterface extends BindingType with ObjCMethods { s.write(methodName[0].toUpperCase() + methodName.substring(1)); break; } - s.write(paramsToString(params, isStatic: true)); + s.write(paramsToString(params)); } else { switch (m.kind) { case ObjCMethodKind.method: // returnType methodName(...) s.write(_getConvertedType(returnType, w, name)); s.write(' $methodName'); - s.write(paramsToString(params, isStatic: false)); + s.write(paramsToString(params)); break; case ObjCMethodKind.propertyGetter: s.write(_getConvertedType(returnType, w, name)); @@ -163,7 +165,7 @@ class ObjCInterface extends BindingType with ObjCMethods { // void getMethodName(Pointer stret) s.write(' get'); s.write(methodName[0].toUpperCase() + methodName.substring(1)); - s.write(paramsToString(params, isStatic: false)); + s.write(paramsToString(params)); } else { // returnType get methodName s.write(' get $methodName'); @@ -172,7 +174,7 @@ class ObjCInterface extends BindingType with ObjCMethods { case ObjCMethodKind.propertySetter: // set methodName(...) s.write(' set $methodName'); - s.write(paramsToString(params, isStatic: false)); + s.write(paramsToString(params)); break; } } @@ -194,7 +196,7 @@ class ObjCInterface extends BindingType with ObjCMethods { objCRetain: m.consumesSelf), m.selObject!.name, m.params.map((p) => p.type - .convertDartTypeToFfiDartType(w, p.name, objCRetain: false)), + .convertDartTypeToFfiDartType(w, p.name, objCRetain: p.consumed)), structRetPtr: 'stret')); s.write(';\n'); if (convertReturn) { @@ -225,8 +227,9 @@ class ObjCInterface extends BindingType with ObjCMethods { (Writer w) => '${ObjCBuiltInFunctions.getClass.gen(w)}("$lookupName")') ..addDependencies(dependencies); _isKindOfClass = builtInFunctions.getSelObject('isKindOfClass:'); - _isKindOfClassMsgSend = builtInFunctions.getMsgSendFunc( - BooleanType(), [ObjCMethodParam(PointerType(objCObjectType), 'clazz')]); + _isKindOfClassMsgSend = builtInFunctions.getMsgSendFunc(BooleanType(), [ + ObjCMethodParam(PointerType(objCObjectType), 'clazz', consumed: false) + ]); addMethodDependencies(dependencies, needMsgSend: true); diff --git a/pkgs/ffigen/lib/src/code_generator/objc_methods.dart b/pkgs/ffigen/lib/src/code_generator/objc_methods.dart index a431afde3..1e7128901 100644 --- a/pkgs/ffigen/lib/src/code_generator/objc_methods.dart +++ b/pkgs/ffigen/lib/src/code_generator/objc_methods.dart @@ -274,7 +274,12 @@ class ObjCMethod { class ObjCMethodParam { Type type; final String name; - ObjCMethodParam(this.type, this.name); + final bool consumed; + ObjCMethodParam( + this.type, + this.name, { + required this.consumed, + }); @override String toString() => '$type $name'; diff --git a/pkgs/ffigen/lib/src/code_generator/objc_protocol.dart b/pkgs/ffigen/lib/src/code_generator/objc_protocol.dart index a54fd7ad0..feacc00e7 100644 --- a/pkgs/ffigen/lib/src/code_generator/objc_protocol.dart +++ b/pkgs/ffigen/lib/src/code_generator/objc_protocol.dart @@ -55,7 +55,8 @@ class ObjCProtocol extends NoLookUpBinding with ObjCMethods { // The function type omits the first arg of the block, which is unused. final func = FunctionType(returnType: block.returnType, parameters: [ for (int i = 1; i < block.argTypes.length; ++i) - Parameter(name: 'arg$i', type: block.argTypes[i]), + Parameter( + name: 'arg$i', type: block.argTypes[i], objCConsumed: false), ]); final funcType = func.getDartType(w, writeArgumentNames: false); diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/functiondecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/functiondecl_parser.dart index 4f819f138..ba0ebe465 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/functiondecl_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/functiondecl_parser.dart @@ -52,6 +52,8 @@ List parseFunctionDeclaration(clang_types.CXCursor cursor) { } final paramName = paramCursor.spelling(); + final objCConsumed = paramCursor + .hasChildWithKind(clang_types.CXCursorKind.CXCursor_NSConsumed); /// If [paramName] is null or empty, its set to `arg$i` by code_generator. parameters.add( @@ -59,6 +61,7 @@ List parseFunctionDeclaration(clang_types.CXCursor cursor) { originalName: paramName, name: config.functionDecl.renameMember(decl, paramName), type: paramType, + objCConsumed: objCConsumed, ), ); } @@ -121,8 +124,9 @@ List parseFunctionDeclaration(clang_types.CXCursor cursor) { originalName: funcName, returnType: returnType, parameters: parameters, - varArgParameters: - vaFunc.types.map((ta) => Parameter(type: ta, name: 'va')).toList(), + varArgParameters: vaFunc.types + .map((ta) => Parameter(type: ta, name: 'va', objCConsumed: false)) + .toList(), exposeSymbolAddress: config.functionDecl.shouldIncludeSymbolAddress(decl), exposeFunctionTypedefs: config.shouldExposeFunctionTypedef(decl), diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcinterfacedecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcinterfacedecl_parser.dart index 9dcab87b4..be8188bbd 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcinterfacedecl_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcinterfacedecl_parser.dart @@ -170,7 +170,7 @@ void _parseProperty(clang_types.CXCursor cursor, ObjCInterface itf) { returnType: NativeType(SupportedNativeType.voidType), family: null, ); - setter.params.add(ObjCMethodParam(fieldType, 'value')); + setter.params.add(ObjCMethodParam(fieldType, 'value', consumed: false)); itf.addMethod(setter); } } @@ -249,7 +249,9 @@ bool _parseMethodParam( } _logger.fine( ' >> Parameter: $type $name ${cursor.completeStringRepr()}'); - method.params.add(ObjCMethodParam(type, name)); + final consumed = + cursor.hasChildWithKind(clang_types.CXCursorKind.CXCursor_NSConsumed); + method.params.add(ObjCMethodParam(type, name, consumed: consumed)); return true; } diff --git a/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart b/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart index cf94346ac..14885cf03 100644 --- a/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart +++ b/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart @@ -333,7 +333,7 @@ Type _extractFromFunctionProto(clang_types.CXType cxtype, } parameters.add( - Parameter(name: '', type: pt), + Parameter(name: '', type: pt, objCConsumed: false), ); } diff --git a/pkgs/ffigen/test/code_generator_tests/code_generator_test.dart b/pkgs/ffigen/test/code_generator_tests/code_generator_test.dart index 6ef127049..06857b297 100644 --- a/pkgs/ffigen/test/code_generator_tests/code_generator_test.dart +++ b/pkgs/ffigen/test/code_generator_tests/code_generator_test.dart @@ -53,12 +53,14 @@ void main() { type: NativeType( SupportedNativeType.int32, ), + objCConsumed: false, ), Parameter( name: 'b', type: NativeType( SupportedNativeType.uint8, ), + objCConsumed: false, ), ], returnType: NativeType( @@ -76,6 +78,7 @@ void main() { SupportedNativeType.int32, ), ), + objCConsumed: false, ), Parameter( name: 'b', @@ -86,6 +89,7 @@ void main() { ), ), ), + objCConsumed: false, ), ], returnType: PointerType( @@ -105,6 +109,7 @@ void main() { type: NativeType( SupportedNativeType.int32, ), + objCConsumed: false, ), ], returnType: NativeType( @@ -247,6 +252,7 @@ void main() { structSome, ), ), + objCConsumed: false, ), ], returnType: PointerType( @@ -436,12 +442,24 @@ void main() { Func( name: 'acceptsEnum', returnType: enum1, - parameters: [Parameter(name: 'value', type: enum1)], + parameters: [ + Parameter( + name: 'value', + type: enum1, + objCConsumed: false, + ) + ], ), Func( name: 'acceptsInt', returnType: enum2, - parameters: [Parameter(name: 'value', type: enum2)], + parameters: [ + Parameter( + name: 'value', + type: enum2, + objCConsumed: false, + ) + ], ), ], ); @@ -589,8 +607,16 @@ void main() { name: 'test1', returnType: BooleanType(), parameters: [ - Parameter(name: 'a', type: BooleanType()), - Parameter(name: 'b', type: PointerType(BooleanType())), + Parameter( + name: 'a', + type: BooleanType(), + objCConsumed: false, + ), + Parameter( + name: 'b', + type: PointerType(BooleanType()), + objCConsumed: false, + ), ], ), Struct( @@ -714,9 +740,11 @@ void main() { parameters: []))), parameters: [ Parameter( - name: 't', - type: Typealias( - name: 'Struct3Typealias', type: Struct(name: 'Struct3'))) + name: 't', + type: Typealias( + name: 'Struct3Typealias', type: Struct(name: 'Struct3')), + objCConsumed: false, + ) ]), ], ); diff --git a/pkgs/ffigen/test/collision_tests/reserved_keyword_collision_test.dart b/pkgs/ffigen/test/collision_tests/reserved_keyword_collision_test.dart index 980e3b8e4..7d325ce27 100644 --- a/pkgs/ffigen/test/collision_tests/reserved_keyword_collision_test.dart +++ b/pkgs/ffigen/test/collision_tests/reserved_keyword_collision_test.dart @@ -28,14 +28,17 @@ void main() { Parameter( type: intType, name: 'if', + objCConsumed: false, ), Parameter( type: intType, name: 'abstract', + objCConsumed: false, ), Parameter( type: intType, name: 'in', + objCConsumed: false, ), ], returnType: NativeType(SupportedNativeType.voidType)), diff --git a/pkgs/ffigen/test/header_parser_tests/function_n_struct_test.dart b/pkgs/ffigen/test/header_parser_tests/function_n_struct_test.dart index 22c268f9b..a05d295ac 100644 --- a/pkgs/ffigen/test/header_parser_tests/function_n_struct_test.dart +++ b/pkgs/ffigen/test/header_parser_tests/function_n_struct_test.dart @@ -91,7 +91,7 @@ Library expectedLibrary() { Func( name: 'func1', parameters: [ - Parameter(name: 's', type: PointerType(struct2)), + Parameter(name: 's', type: PointerType(struct2), objCConsumed: false), ], returnType: NativeType( SupportedNativeType.voidType, @@ -100,7 +100,7 @@ Library expectedLibrary() { Func( name: 'func2', parameters: [ - Parameter(name: 's', type: PointerType(struct3)), + Parameter(name: 's', type: PointerType(struct3), objCConsumed: false), ], returnType: NativeType( SupportedNativeType.voidType, @@ -109,7 +109,7 @@ Library expectedLibrary() { Func( name: 'func3', parameters: [ - Parameter(name: 'a', type: PointerType(intType)), + Parameter(name: 'a', type: PointerType(intType), objCConsumed: false), ], returnType: NativeType( SupportedNativeType.voidType, diff --git a/pkgs/ffigen/test/native_objc_test/arc_test.dart b/pkgs/ffigen/test/native_objc_test/arc_test.dart index ab77942a4..02cdbd868 100644 --- a/pkgs/ffigen/test/native_objc_test/arc_test.dart +++ b/pkgs/ffigen/test/native_objc_test/arc_test.dart @@ -441,7 +441,27 @@ void main() { expect(counter.value, 1); } - test("objectRetainCount large ref count", () { + test('Consumed arguments', () { + final counter = calloc(); + ArcTestObject? obj1 = ArcTestObject.newWithCounter_(counter); + final obj1raw = obj1.pointer; + + expect(objectRetainCount(obj1raw), 1); + expect(counter.value, 1); + + ArcTestObject.consumeArg_(obj1); + + expect(objectRetainCount(obj1raw), 1); + expect(counter.value, 1); + + obj1 = null; + doGC(); + expect(objectRetainCount(obj1raw), 0); + expect(counter.value, 0); + calloc.free(counter); + }); + + test('objectRetainCount large ref count', () { // Most ObjC API methods return us a reference without incrementing the // ref count (ie, returns us a reference we don't own). So the wrapper // object has to take ownership by calling retain. This test verifies that diff --git a/pkgs/ffigen/test/native_objc_test/arc_test.m b/pkgs/ffigen/test/native_objc_test/arc_test.m index 1b9693b9c..dfda41242 100644 --- a/pkgs/ffigen/test/native_objc_test/arc_test.m +++ b/pkgs/ffigen/test/native_objc_test/arc_test.m @@ -30,6 +30,7 @@ - (ArcTestObject*)returnsRetained NS_RETURNS_RETAINED; - (ArcTestObject*)copyMeNoRetain __attribute__((ns_returns_not_retained)); - (ArcTestObject*)copyMeAutorelease __attribute__((ns_returns_autoreleased)); - (ArcTestObject*)copyMeConsumeSelf __attribute__((ns_consumes_self)); ++ (void)consumeArg:(ArcTestObject*) __attribute((ns_consumed)) arg; @property (assign) ArcTestObject* assignedProperty; @property (retain) ArcTestObject* retainedProperty; @@ -94,4 +95,6 @@ - (ArcTestObject*)copyMeConsumeSelf __attribute__((ns_consumes_self)) { return [self copyMe]; } ++ (void)consumeArg:(ArcTestObject*) __attribute((ns_consumed)) arg {} + @end diff --git a/pkgs/ffigen/test/native_objc_test/ref_count_test.dart b/pkgs/ffigen/test/native_objc_test/ref_count_test.dart index da9375c0b..dda630eeb 100644 --- a/pkgs/ffigen/test/native_objc_test/ref_count_test.dart +++ b/pkgs/ffigen/test/native_objc_test/ref_count_test.dart @@ -550,7 +550,27 @@ void main() { expect(counter.value, 1); } - test("objectRetainCount large ref count", () { + test('Consumed arguments', () { + final counter = calloc(); + RefCountTestObject? obj1 = RefCountTestObject.newWithCounter_(counter); + final obj1raw = obj1.pointer; + + expect(objectRetainCount(obj1raw), 1); + expect(counter.value, 1); + + RefCountTestObject.consumeArg_(obj1); + + expect(objectRetainCount(obj1raw), 1); + expect(counter.value, 1); + + obj1 = null; + doGC(); + expect(objectRetainCount(obj1raw), 0); + expect(counter.value, 0); + calloc.free(counter); + }); + + test('objectRetainCount large ref count', () { // Most ObjC API methods return us a reference without incrementing the // ref count (ie, returns us a reference we don't own). So the wrapper // object has to take ownership by calling retain. This test verifies that diff --git a/pkgs/ffigen/test/native_objc_test/ref_count_test.m b/pkgs/ffigen/test/native_objc_test/ref_count_test.m index d12158c2f..14c54c0d0 100644 --- a/pkgs/ffigen/test/native_objc_test/ref_count_test.m +++ b/pkgs/ffigen/test/native_objc_test/ref_count_test.m @@ -32,6 +32,7 @@ - (RefCountTestObject*)returnsRetained NS_RETURNS_RETAINED; - (RefCountTestObject*)copyMeNoRetain __attribute__((ns_returns_not_retained)); - (RefCountTestObject*)copyMeAutorelease __attribute__((ns_returns_autoreleased)); - (RefCountTestObject*)copyMeConsumeSelf __attribute__((ns_consumes_self)); ++ (void)consumeArg:(RefCountTestObject*) __attribute((ns_consumed)) arg; @property (assign) RefCountTestObject* assignedProperty; @property (retain) RefCountTestObject* retainedProperty; @@ -112,6 +113,10 @@ - (RefCountTestObject*)copyMeConsumeSelf __attribute__((ns_consumes_self)) { return [self copyMe]; } ++ (void)consumeArg:(RefCountTestObject*) __attribute((ns_consumed)) arg { + [arg release]; +} + @end @implementation RefCounted diff --git a/pkgs/ffigen/test/native_objc_test/static_func_config.yaml b/pkgs/ffigen/test/native_objc_test/static_func_config.yaml index 826472162..0a45ba852 100644 --- a/pkgs/ffigen/test/native_objc_test/static_func_config.yaml +++ b/pkgs/ffigen/test/native_objc_test/static_func_config.yaml @@ -10,6 +10,7 @@ functions: - staticFuncOfBlock - staticFuncReturnsRetained - staticFuncReturnsRetainedArg + - staticFuncConsumesArg - objc_autoreleasePoolPush - objc_autoreleasePoolPop headers: diff --git a/pkgs/ffigen/test/native_objc_test/static_func_native_config.yaml b/pkgs/ffigen/test/native_objc_test/static_func_native_config.yaml index 4fa0645dc..cc855fc0f 100644 --- a/pkgs/ffigen/test/native_objc_test/static_func_native_config.yaml +++ b/pkgs/ffigen/test/native_objc_test/static_func_native_config.yaml @@ -11,6 +11,7 @@ functions: - staticFuncOfBlock - staticFuncReturnsRetained - staticFuncReturnsRetainedArg + - staticFuncConsumesArg - objc_autoreleasePoolPush - objc_autoreleasePoolPop headers: diff --git a/pkgs/ffigen/test/native_objc_test/static_func_native_test.dart b/pkgs/ffigen/test/native_objc_test/static_func_native_test.dart index 5d6e33c4e..308b5fbe7 100644 --- a/pkgs/ffigen/test/native_objc_test/static_func_native_test.dart +++ b/pkgs/ffigen/test/native_objc_test/static_func_native_test.dart @@ -149,5 +149,27 @@ void main() { expect(counter.value, 0); }); }); + + test( + 'Objects passed to static functions that consume them ' + 'have correct ref counts', () { + final counter = calloc(); + StaticFuncTestObj? obj1 = StaticFuncTestObj.newWithCounter_(counter); + final obj1raw = obj1.pointer; + + expect(objectRetainCount(obj1raw), 1); + expect(counter.value, 1); + + staticFuncConsumesArg(obj1); + + expect(objectRetainCount(obj1raw), 1); + expect(counter.value, 1); + + obj1 = null; + doGC(); + expect(objectRetainCount(obj1raw), 0); + expect(counter.value, 0); + calloc.free(counter); + }); }); } diff --git a/pkgs/ffigen/test/native_objc_test/static_func_test.dart b/pkgs/ffigen/test/native_objc_test/static_func_test.dart index 8eb02224d..f9bc73d68 100644 --- a/pkgs/ffigen/test/native_objc_test/static_func_test.dart +++ b/pkgs/ffigen/test/native_objc_test/static_func_test.dart @@ -151,5 +151,27 @@ void main() { expect(counter.value, 0); }); }); + + test( + 'Objects passed to static functions that consume them ' + 'have correct ref counts', () { + final counter = calloc(); + StaticFuncTestObj? obj1 = StaticFuncTestObj.newWithCounter_(counter); + final obj1raw = obj1.pointer; + + expect(objectRetainCount(obj1raw), 1); + expect(counter.value, 1); + + lib.staticFuncConsumesArg(obj1); + + expect(objectRetainCount(obj1raw), 1); + expect(counter.value, 1); + + obj1 = null; + doGC(); + expect(objectRetainCount(obj1raw), 0); + expect(counter.value, 0); + calloc.free(counter); + }); }); } diff --git a/pkgs/ffigen/test/native_objc_test/static_func_test.m b/pkgs/ffigen/test/native_objc_test/static_func_test.m index f21283d38..ab46de20d 100644 --- a/pkgs/ffigen/test/native_objc_test/static_func_test.m +++ b/pkgs/ffigen/test/native_objc_test/static_func_test.m @@ -41,6 +41,9 @@ IntBlock staticFuncOfBlock(IntBlock a) { return a; } +void staticFuncConsumesArg(StaticFuncTestObj* __attribute((ns_consumed)) a) { +} + @implementation StaticFuncTestObj + (instancetype)newWithCounter:(int32_t*) _counter { diff --git a/pkgs/ffigen/test/rename_tests/rename_test.dart b/pkgs/ffigen/test/rename_tests/rename_test.dart index 9e49f9031..ccf4984d0 100644 --- a/pkgs/ffigen/test/rename_tests/rename_test.dart +++ b/pkgs/ffigen/test/rename_tests/rename_test.dart @@ -175,6 +175,7 @@ Library expectedLibrary() { Parameter( name: 's', type: PointerType(struct1), + objCConsumed: false, ), ], ), @@ -188,6 +189,7 @@ Library expectedLibrary() { Parameter( name: 's', type: PointerType(struct2), + objCConsumed: false, ), ], ), @@ -201,6 +203,7 @@ Library expectedLibrary() { Parameter( name: 's', type: PointerType(struct3), + objCConsumed: false, ), ], ), @@ -214,14 +217,17 @@ Library expectedLibrary() { Parameter( name: 'underscore', type: intType, + objCConsumed: false, ), Parameter( name: 'fullMatchSuccess', type: floatType, + objCConsumed: false, ), Parameter( name: 'unnamed', type: intType, + objCConsumed: false, ), ], ), @@ -237,6 +243,7 @@ Library expectedLibrary() { type: Typealias( name: 'Struct5_Alias_Renamed', type: Struct(name: '${structPrefix}Struct5')), + objCConsumed: false, ), ], ),