Skip to content
This repository has been archived by the owner on Jan 28, 2024. It is now read-only.

Commit

Permalink
Handle NS_RETURNS_RETAINED
Browse files Browse the repository at this point in the history
  • Loading branch information
liamappelbe committed Nov 1, 2023
1 parent d22cf20 commit 610d1dc
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 18 deletions.
4 changes: 3 additions & 1 deletion lib/src/code_generator/func.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class Func extends LookUpBinding {
final bool exposeSymbolAddress;
final bool exposeFunctionTypedefs;
final bool isLeaf;
final bool objCReturnsRetained;
final FfiNativeConfig ffiNativeConfig;
late final String funcPointerName;

Expand All @@ -61,6 +62,7 @@ class Func extends LookUpBinding {
this.exposeSymbolAddress = false,
this.exposeFunctionTypedefs = false,
this.isLeaf = false,
this.objCReturnsRetained = false,
super.isInternal,
this.ffiNativeConfig = const FfiNativeConfig(enabled: false),
}) : functionType = FunctionType(
Expand Down Expand Up @@ -133,7 +135,7 @@ class Func extends LookUpBinding {
w,
'$funcVarName($argString)',
ffiNativeConfig.enabled ? 'lib' : 'this',
objCRetain: true,
objCRetain: !objCReturnsRetained,
);
} else {
dartReturnType = ffiReturnType;
Expand Down
27 changes: 27 additions & 0 deletions lib/src/header_parser/sub_parsers/functiondecl_parser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:ffi';

import 'package:ffigen/src/code_generator.dart';
import 'package:ffigen/src/config_provider/config_types.dart';
import 'package:ffigen/src/header_parser/data.dart';
Expand All @@ -20,6 +22,7 @@ class _ParserFunc {
List<Func> funcs = [];
bool incompleteStructParameter = false;
bool unimplementedParameterType = false;
bool objCReturnsRetained = false;
_ParserFunc();
}

Expand Down Expand Up @@ -69,6 +72,13 @@ List<Func>? parseFunctionDeclaration(clang_types.CXCursor cursor) {
return _stack.pop().funcs;
}

// Look for any annotations on the function.
clang.clang_visitChildren(
cursor,
_parseAnnotationVisitorPtr ??= Pointer.fromFunction(
_parseAnnotationVisitor, exceptional_visitor_return),
nullptr);

// Initialized with a single value with no prefix and empty var args.
var varArgFunctions = [VarArgFunction('', [])];
if (config.varArgFunctions.containsKey(funcName)) {
Expand Down Expand Up @@ -97,6 +107,7 @@ List<Func>? parseFunctionDeclaration(clang_types.CXCursor cursor) {
exposeFunctionTypedefs:
config.exposeFunctionTypedefs.shouldInclude(funcName),
isLeaf: config.leafFunctions.shouldInclude(funcName),
objCReturnsRetained: _stack.top.objCReturnsRetained,
ffiNativeConfig: config.ffiNativeConfig,
));
}
Expand Down Expand Up @@ -147,3 +158,19 @@ List<Parameter> _getParameters(clang_types.CXCursor cursor, String funcName) {
Type _getParameterType(clang_types.CXCursor cursor) {
return cursor.toCodeGenType();
}

Pointer<
NativeFunction<
Int32 Function(
clang_types.CXCursor, clang_types.CXCursor, Pointer<Void>)>>?
_parseAnnotationVisitorPtr;
int _parseAnnotationVisitor(clang_types.CXCursor cursor,
clang_types.CXCursor parent, Pointer<Void> clientData) {
switch (cursor.kind) {
case clang_types.CXCursorKind.CXCursor_NSReturnsRetained:
_stack.top.objCReturnsRetained = true;
break;
default:
}
return clang_types.CXChildVisitResult.CXChildVisit_Continue;
}
2 changes: 2 additions & 0 deletions test/native_objc_test/automated_ref_count_test.m
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ - (ArcTestObject*)returnsRetained NS_RETURNS_RETAINED {

@end

// Pass around the NSAutoreleasePool as a void* to bypass the Dart wrappers so
// that we can precisely control the life cycle.
void* createAutoreleasePool() {
return (void*)[NSAutoreleasePool new];
}
Expand Down
3 changes: 2 additions & 1 deletion test/native_objc_test/static_func_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ functions:
- staticFuncOfObject
- staticFuncOfNullableObject
- staticFuncOfBlock
- staticFuncReturningNSString
- staticFuncReturnsRetained
- staticFuncReturnsRetainedArg
headers:
entry-points:
- 'static_func_test.m'
Expand Down
3 changes: 2 additions & 1 deletion test/native_objc_test/static_func_native_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ functions:
- staticFuncOfObject
- staticFuncOfNullableObject
- staticFuncOfBlock
- staticFuncReturningNSString
- staticFuncReturnsRetained
- staticFuncReturnsRetainedArg
headers:
entry-points:
- 'static_func_test.m'
Expand Down
50 changes: 45 additions & 5 deletions test/native_objc_test/static_func_native_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,6 @@ void main() {
calloc.free(gcNow);
}

test('Static function returning NSString', () {
expect(staticFuncReturningNSString(lib).length, 12);
expect(staticFuncReturningNSString(lib).toString(), "Hello World!");
});

Pointer<Int32> staticFuncOfObjectRefCountTest(Allocator alloc) {
final counter = alloc<Int32>();
counter.value = 0;
Expand Down Expand Up @@ -112,5 +107,50 @@ void main() {
doGC();
expect(getBlockRetainCount(rawBlock), 0);
});

Pointer<Int32> staticFuncReturnsRetainedRefCountTest(Allocator alloc) {
final counter = alloc<Int32>();
counter.value = 0;

final outputObj = staticFuncReturnsRetained(lib, counter);
expect(counter.value, 1);

return counter;
}

test(
'Objects returned from static functions with NS_RETURNS_RETAINED '
'have correct ref counts', () {
using((Arena arena) {
final (counter) = staticFuncReturnsRetainedRefCountTest(arena);
doGC();
expect(counter.value, 0);
});
});

Pointer<Int32> staticFuncOfObjectReturnsRetainedRefCountTest(
Allocator alloc) {
final counter = alloc<Int32>();
counter.value = 0;

final obj = StaticFuncTestObj.newWithCounter_(lib, counter);
expect(counter.value, 1);

final outputObj = staticFuncReturnsRetainedArg(lib, obj);
expect(obj, outputObj);
expect(counter.value, 1);

return counter;
}

test(
'Objects passed through static functions with NS_RETURNS_RETAINED '
'have correct ref counts', () {
using((Arena arena) {
final (counter) = staticFuncOfObjectReturnsRetainedRefCountTest(arena);
doGC();
expect(counter.value, 0);
});
});
});
}
50 changes: 45 additions & 5 deletions test/native_objc_test/static_func_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,6 @@ void main() {
calloc.free(gcNow);
}

test('Static function returning NSString', () {
expect(lib.staticFuncReturningNSString().length, 12);
expect(lib.staticFuncReturningNSString().toString(), "Hello World!");
});

Pointer<Int32> staticFuncOfObjectRefCountTest(Allocator alloc) {
final counter = alloc<Int32>();
counter.value = 0;
Expand Down Expand Up @@ -112,5 +107,50 @@ void main() {
doGC();
expect(lib.getBlockRetainCount(rawBlock), 0);
});

Pointer<Int32> staticFuncReturnsRetainedRefCountTest(Allocator alloc) {
final counter = alloc<Int32>();
counter.value = 0;

final outputObj = lib.staticFuncReturnsRetained(counter);
expect(counter.value, 1);

return counter;
}

test(
'Objects returned from static functions with NS_RETURNS_RETAINED '
'have correct ref counts', () {
using((Arena arena) {
final (counter) = staticFuncReturnsRetainedRefCountTest(arena);
doGC();
expect(counter.value, 0);
});
});

Pointer<Int32> staticFuncOfObjectReturnsRetainedRefCountTest(
Allocator alloc) {
final counter = alloc<Int32>();
counter.value = 0;

final obj = StaticFuncTestObj.newWithCounter_(lib, counter);
expect(counter.value, 1);

final outputObj = lib.staticFuncReturnsRetainedArg(obj);
expect(obj, outputObj);
expect(counter.value, 1);

return counter;
}

test(
'Objects passed through static functions with NS_RETURNS_RETAINED '
'have correct ref counts', () {
using((Arena arena) {
final (counter) = staticFuncOfObjectReturnsRetainedRefCountTest(arena);
doGC();
expect(counter.value, 0);
});
});
});
}
14 changes: 9 additions & 5 deletions test/native_objc_test/static_func_test.m
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
Expand All @@ -6,9 +7,6 @@

#include "util.h"

// Take and return object, nullable, and block
// ref counting tests

@interface StaticFuncTestObj : NSObject {
int32_t* counter;
}
Expand Down Expand Up @@ -55,6 +53,12 @@ IntBlock staticFuncOfBlock(IntBlock a) {
return a;
}

NSString* staticFuncReturningNSString() {
return @"Hello World!";
NS_RETURNS_RETAINED StaticFuncTestObj* staticFuncReturnsRetained(
int32_t* counter) {
return [StaticFuncTestObj newWithCounter: counter];
}

NS_RETURNS_RETAINED StaticFuncTestObj* staticFuncReturnsRetainedArg(
StaticFuncTestObj* a) {
return [a retain];
}

0 comments on commit 610d1dc

Please sign in to comment.