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

ObjC static functions #633

Merged
merged 12 commits into from
Nov 2, 2023
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- When generating typedefs for `Pointer<NativeFunction<Function>>`, also
generate a typedef for the `Function`.
- Use Dart wrapper types in args and returns of ObjCBlocks.
- Use Dart wrapper types in args and returns of static functions.
- Bump min SDK version to 3.2.0-114.0.dev.
- Renamed `asset` to `assetId` for `ffi-native`.

Expand Down
94 changes: 65 additions & 29 deletions 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 @@ -93,8 +95,6 @@ class Func extends LookUpBinding {
BindingString toBindingString(Writer w) {
final s = StringBuffer();
final enclosingFuncName = name;
final funcVarName = w.wrapperLevelUniqueNamer.makeUnique('_$name');
funcPointerName = w.wrapperLevelUniqueNamer.makeUnique('_${name}Ptr');

if (dartDoc != null) {
s.write(makeDartDoc(dartDoc!));
Expand All @@ -109,37 +109,71 @@ class Func extends LookUpBinding {
functionType.getCType(w, writeArgumentNames: false);
final dartType = _exposedFunctionTypealias?.getFfiDartType(w) ??
functionType.getFfiDartType(w, writeArgumentNames: false);
final needsWrapper = !functionType.sameDartAndFfiDartType && !isInternal;

final isLeafString = isLeaf ? 'isLeaf:true' : '';
final funcVarName = w.wrapperLevelUniqueNamer.makeUnique('_$name');
final ffiReturnType = functionType.returnType.getFfiDartType(w);
final ffiArgDeclString = functionType.dartTypeParameters
.map((p) => '${p.type.getFfiDartType(w)} ${p.name},\n')
.join('');

late final String dartReturnType;
late final String dartArgDeclString;
late final String funcImplCall;
if (needsWrapper) {
dartReturnType = functionType.returnType.getDartType(w);
dartArgDeclString = functionType.dartTypeParameters
.map((p) => '${p.type.getDartType(w)} ${p.name},\n')
.join('');

final argString = functionType.dartTypeParameters
.map((p) =>
'${p.type.convertDartTypeToFfiDartType(w, p.name, objCRetain: false)},\n')
.join('');
funcImplCall = functionType.returnType.convertFfiDartTypeToDartType(
w,
'$funcVarName($argString)',
ffiNativeConfig.enabled ? 'lib' : 'this',
objCRetain: !objCReturnsRetained,
);
} else {
dartReturnType = ffiReturnType;
dartArgDeclString = ffiArgDeclString;
final argString =
functionType.dartTypeParameters.map((p) => '${p.name},\n').join('');
funcImplCall = '$funcVarName($argString)';
}

if (ffiNativeConfig.enabled) {
final assetString = ffiNativeConfig.assetId != null
? ", assetId: '${ffiNativeConfig.assetId}'"
: '';
final isLeafString = isLeaf ? ', isLeaf: true' : '';
s.write(
"@${w.ffiLibraryPrefix}.Native<$cType>(symbol: '$originalName'$assetString$isLeafString)\n");

s.write(
'external ${functionType.returnType.getFfiDartType(w)} $enclosingFuncName(\n');
for (final p in functionType.dartTypeParameters) {
s.write(' ${p.type.getFfiDartType(w)} ${p.name},\n');
final nativeFuncName = needsWrapper ? funcVarName : enclosingFuncName;
s.write('''
@${w.ffiLibraryPrefix}.Native<$cType>(symbol: '$originalName'$assetString$isLeafString)
external $ffiReturnType $nativeFuncName($ffiArgDeclString);

''');
if (needsWrapper) {
final libArg = functionType.returnType.sameDartAndFfiDartType
? ''
: '${w.className} lib, ';
s.write('''
$dartReturnType $enclosingFuncName($libArg$dartArgDeclString) => $funcImplCall;

''');
}
s.write(');\n\n');
} else {
funcPointerName = w.wrapperLevelUniqueNamer.makeUnique('_${name}Ptr');

// Write enclosing function.
s.write(
'${functionType.returnType.getFfiDartType(w)} $enclosingFuncName(\n');
for (final p in functionType.dartTypeParameters) {
s.write(' ${p.type.getFfiDartType(w)} ${p.name},\n');
}
s.write(') {\n');
s.write('return $funcVarName');
s.write('''
$dartReturnType $enclosingFuncName($dartArgDeclString) {
return $funcImplCall;
}

s.write('(\n');
for (final p in functionType.dartTypeParameters) {
s.write(' ${p.name},\n');
}
s.write(' );\n');
s.write('}\n');
''');

if (exposeSymbolAddress) {
// Add to SymbolAddress in writer.
Expand All @@ -150,12 +184,14 @@ class Func extends LookUpBinding {
ptrName: funcPointerName,
);
}

// Write function pointer.
s.write(
"late final $funcPointerName = ${w.lookupFuncIdentifier}<${w.ffiLibraryPrefix}.NativeFunction<$cType>>('$originalName');\n");
final isLeafString = isLeaf ? 'isLeaf:true' : '';
s.write(
'late final $funcVarName = $funcPointerName.asFunction<$dartType>($isLeafString);\n\n');
s.write('''
late final $funcPointerName = ${w.lookupFuncIdentifier}<
${w.ffiLibraryPrefix}.NativeFunction<$cType>>('$originalName');
late final $funcVarName = $funcPointerName.asFunction<$dartType>($isLeafString);

''');
}

return BindingString(type: BindingStringType.func, string: s.toString());
Expand Down
5 changes: 5 additions & 0 deletions lib/src/code_generator/func_type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ class FunctionType extends Type {
returnType.sameDartAndCType &&
dartTypeParameters.every((p) => p.type.sameDartAndCType);

@override
bool get sameDartAndFfiDartType =>
returnType.sameDartAndFfiDartType &&
dartTypeParameters.every((p) => p.type.sameDartAndFfiDartType);

@override
String toString() => _getTypeImpl(false, (Type t) => t.toString());

Expand Down
3 changes: 3 additions & 0 deletions lib/src/code_generator/objc_block.dart
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,9 @@ _id.ref.invoke.cast<$natTrampFnType>().asFunction<$trampFuncFfiDartType>()(
@override
bool get sameDartAndCType => false;

@override
bool get sameDartAndFfiDartType => false;

@override
String convertDartTypeToFfiDartType(
Writer w,
Expand Down
3 changes: 3 additions & 0 deletions lib/src/code_generator/objc_interface.dart
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,9 @@ class $name extends ${superType?.name ?? '_ObjCWrapper'} {
@override
bool get sameDartAndCType => false;

@override
bool get sameDartAndFfiDartType => false;

@override
String convertDartTypeToFfiDartType(
Writer w,
Expand Down
3 changes: 3 additions & 0 deletions lib/src/code_generator/objc_nullable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ class ObjCNullable extends Type {
@override
bool get sameDartAndCType => false;

@override
bool get sameDartAndFfiDartType => false;

@override
String convertDartTypeToFfiDartType(
Writer w,
Expand Down
3 changes: 3 additions & 0 deletions lib/src/code_generator/pointer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ class ObjCObjectPointer extends PointerType {
@override
bool get sameDartAndCType => false;

@override
bool get sameDartAndFfiDartType => false;

@override
String convertDartTypeToFfiDartType(
Writer w,
Expand Down
6 changes: 6 additions & 0 deletions lib/src/code_generator/type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ abstract class Type {
/// Returns whether the dart type and C type string are same.
bool get sameDartAndCType => sameFfiDartAndCType;

/// Returns whether the dart type and FFI dart type string are same.
bool get sameDartAndFfiDartType => true;

/// Returns generated Dart code that converts the given value from its
/// DartType to its FfiDartType.
///
Expand Down Expand Up @@ -138,6 +141,9 @@ abstract class BindingType extends NoLookUpBinding implements Type {
@override
bool get sameDartAndCType => sameFfiDartAndCType;

@override
bool get sameDartAndFfiDartType => true;

@override
String convertDartTypeToFfiDartType(
Writer w,
Expand Down
3 changes: 3 additions & 0 deletions lib/src/code_generator/typealias.dart
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ class Typealias extends BindingType {
@override
bool get sameDartAndCType => type.sameDartAndCType;

@override
bool get sameDartAndFfiDartType => type.sameDartAndFfiDartType;

@override
String convertDartTypeToFfiDartType(
Writer w,
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;
}
10 changes: 6 additions & 4 deletions test/native_objc_test/automated_ref_count_test.m
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,14 @@ - (ArcTestObject*)returnsRetained NS_RETURNS_RETAINED {

@end

id createAutoreleasePool() {
return [NSAutoreleasePool new];
// 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];
}

void destroyAutoreleasePool(id pool) {
[pool release];
void destroyAutoreleasePool(void* pool) {
[((NSAutoreleasePool*)pool) release];
}

@implementation RefCounted
Expand Down
3 changes: 3 additions & 0 deletions test/native_objc_test/block_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ exclude-all-by-default: true
objc-interfaces:
include:
- BlockTester
functions:
include:
- getBlockRetainCount
headers:
entry-points:
- 'block_test.m'
Expand Down
Loading
Loading