Skip to content

[ffigen] Migrate to ARC #1441

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Aug 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .github/workflows/objective_c.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,20 @@ jobs:
carryforward: "ffigen,jni,jnigen,native_assets_builder_macos,native_assets_builder_ubuntu,native_assets_builder_windows,native_assets_cli_macos,native_assets_cli_ubuntu,native_assets_cli_windows,native_toolchain_c_macos,native_toolchain_c_ubuntu,native_toolchain_c_windows,objective_c,swift2objc"
github-token: ${{ secrets.GITHUB_TOKEN }}
parallel-finished: true

build-example:
needs: analyze
runs-on: 'macos-latest'
defaults:
run:
working-directory: pkgs/objective_c/example/
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
- uses: subosito/flutter-action@44ac965b96f18d999802d4b807e3256d5a3f9fa1
with:
flutter-version: 3.19.0
channel: 'stable'
- name: Install dependencies
run: flutter pub get
- name: Build the example app
run: flutter build macos
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ coverage/
*.jar
*.class
*.exe
*.o

# Visual Studio user-specific files.
*.suo
Expand Down
6 changes: 5 additions & 1 deletion pkgs/ffigen/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@
`ObjCBlock<Ret Function(Args...)>`, instead of the codegenned
`ObjCBlock_...` wrapper. The wrapper is now a non-constructible set of util
methods for constructing `ObjCBlock`.

- __Breaking change__: Generated ObjC code has been migrated to ARC (Automatic
Reference Counting), and must now be compiled with ARC enabled. For example,
if you had a line like `s.requires_arc = []` in your podspec, this should
either be removed, or you should add the ffigen generated ObjC code to the
list. If you're compiling directly with clang, add the `-fobjc-arc` flag.

## 13.0.0

Expand Down
34 changes: 22 additions & 12 deletions pkgs/ffigen/lib/src/code_generator/objc_block.dart
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ abstract final class $name {
objCRetain: true);
final listenerConvFn =
'($paramsFfiDartType) => $listenerConvFnInvocation';
final wrapFn = _wrapListenerBlock?.name;
final releaseFn = ObjCBuiltInFunctions.objectRelease.gen(w);

s.write('''

Expand All @@ -205,12 +207,22 @@ abstract final class $name {
///
/// Note that unlike the default behavior of NativeCallable.listener, listener
/// blocks do not keep the isolate alive.
static $blockType listener($funcDartType fn) =>
$blockType(${_wrapListenerBlock?.name ?? ''}($newClosureBlock(
(_dartFuncListenerTrampoline ??= $nativeCallableType.listener(
$closureTrampoline $exceptionalReturn)..keepIsolateAlive =
false).nativeFunction.cast(), $listenerConvFn)),
retain: false, release: true);
static $blockType listener($funcDartType fn) {
final raw = $newClosureBlock(
(_dartFuncListenerTrampoline ??= $nativeCallableType.listener(
$closureTrampoline $exceptionalReturn)..keepIsolateAlive =
false).nativeFunction.cast(), $listenerConvFn);''');
if (wrapFn != null) {
s.write('''
final wrapper = $wrapFn(raw);
$releaseFn(raw.cast());
return $blockType(wrapper, retain: false, release: true);''');
} else {
s.write('''
return $blockType(raw, retain: false, release: true);''');
}
s.write('''
}
static $nativeCallableType? _dartFuncListenerTrampoline;
''');
}
Expand Down Expand Up @@ -256,12 +268,10 @@ pointer.ref.invoke.cast<$natTrampFnType>().asFunction<$trampFuncFfiDartType>()(
s.write('''

typedef ${getNativeType(varName: blockTypedef)};
$blockTypedef $fnName($blockTypedef block) {
$blockTypedef wrapper = [^void(${argsReceived.join(', ')}) {
$blockTypedef $fnName($blockTypedef block) NS_RETURNS_RETAINED {
return ^void(${argsReceived.join(', ')}) {
block(${retains.join(', ')});
} copy];
[block release];
return wrapper;
};
}
''');
return BindingString(
Expand Down Expand Up @@ -337,7 +347,7 @@ $blockTypedef $fnName($blockTypedef block) {
ObjCInterface.generateConstructor(name, value, objCRetain);

@override
String? generateRetain(String value) => '[$value copy]';
String? generateRetain(String value) => 'objc_retainBlock($value)';

@override
String toString() => '($returnType (^)(${argTypes.join(', ')}))';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class ObjCBuiltInFunctions {
static const getProtocolMethodSignature =
ObjCImport('getProtocolMethodSignature');
static const getProtocol = ObjCImport('getProtocol');
static const objectRelease = ObjCImport('objectRelease');
static const objectBase = ObjCImport('ObjCObjectBase');
static const blockType = ObjCImport('ObjCBlock');
static const protocolMethod = ObjCImport('ObjCProtocolMethod');
Expand Down
2 changes: 1 addition & 1 deletion pkgs/ffigen/lib/src/code_generator/objc_interface.dart
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ class ObjCInterface extends BindingType with ObjCMethods {
}

@override
String? generateRetain(String value) => '[$value retain]';
String? generateRetain(String value) => 'objc_retain($value)';

String _getConvertedType(Type type, Writer w, String enclosingClass) {
if (type is ObjCInstanceType) return enclosingClass;
Expand Down
2 changes: 1 addition & 1 deletion pkgs/ffigen/lib/src/code_generator/pointer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -135,5 +135,5 @@ class ObjCObjectPointer extends PointerType {
'${getDartType(w)}($value, retain: $objCRetain, release: true)';

@override
String? generateRetain(String value) => '[$value retain]';
String? generateRetain(String value) => 'objc_retain($value)';
}
10 changes: 9 additions & 1 deletion pkgs/ffigen/lib/src/code_generator/writer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -418,12 +418,20 @@ class Writer {
final s = StringBuffer();
s.write('''
#include <stdint.h>

''');

for (final entryPoint in nativeEntryPoints) {
s.write(_objcImport(entryPoint, outDir));
}
s.write('''

#if !__has_feature(objc_arc)
#error "This file must be compiled with ARC enabled"
#endif

id objc_retain(id);
id objc_retainBlock(id);
''');

var empty = true;
for (final binding in _allBindings) {
Expand Down
17 changes: 17 additions & 0 deletions pkgs/ffigen/test/native_objc_test/arc_config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: ArcTestObjCLibrary
description: 'Tests ARC'
language: objc
output: 'arc_bindings.dart'
exclude-all-by-default: true
functions:
include:
- objc_autoreleasePoolPop
- objc_autoreleasePoolPush
objc-interfaces:
include:
- ArcTestObject
headers:
entry-points:
- 'arc_test.m'
preamble: |
// ignore_for_file: camel_case_types, non_constant_identifier_names, unnecessary_non_null_assertion, unused_element, unused_field
Loading
Loading