Skip to content

WIP: [objective_c] Experimenting with isolate group shared #2444

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

Draft
wants to merge 35 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
2a9cb77
WIP: Migrating package:objective_c to native_assets
liamappelbe May 8, 2025
e7a73d6
trying to migrate ffigen
liamappelbe May 8, 2025
67c3534
WIP
liamappelbe May 26, 2025
79a93fe
revert example changes
liamappelbe May 26, 2025
a9fe416
wip
liamappelbe May 26, 2025
4bb4a27
Merge branch 'main' into objc_native
liamappelbe May 26, 2025
665c57e
merge cruft
liamappelbe May 26, 2025
e4e60b8
Merge branch 'main' into objc_native
liamappelbe Jun 12, 2025
fb4f2d5
merge cruft
liamappelbe Jun 12, 2025
3a10246
Merge branch 'main' into objc_native
liamappelbe Jun 12, 2025
32a78c2
merge cruft
liamappelbe Jun 12, 2025
c1fd221
fixes
liamappelbe Jun 13, 2025
309820c
Merge branch 'main' into objc_native
liamappelbe Jun 18, 2025
d56aa6a
fmt
liamappelbe Jun 18, 2025
e197d81
Remove flutter deps
liamappelbe Jun 18, 2025
763703a
Fix example
liamappelbe Jun 18, 2025
623740a
use dev sdk in workflows
liamappelbe Jun 18, 2025
072d9ef
fix analysis
liamappelbe Jun 18, 2025
6dc4b8a
fix workflow
liamappelbe Jun 18, 2025
d1b7313
workflow
liamappelbe Jun 18, 2025
db35a69
fix workflow
liamappelbe Jun 18, 2025
42ca198
Fix linux and mac builds
liamappelbe Jun 18, 2025
9fb56d1
cleanup
liamappelbe Jun 23, 2025
3e6b920
another code split
liamappelbe Jun 27, 2025
57f5cfe
Merge branch 'main' into objc_native
liamappelbe Jul 2, 2025
c16de8c
Fix generation bug
liamappelbe Jul 2, 2025
73a9009
Revert code split
liamappelbe Jul 2, 2025
edbee93
Fix issues with exported globals
liamappelbe Jul 2, 2025
e9f65d3
fix block test
liamappelbe Jul 24, 2025
59a1585
Merge branch 'objc_cb' into objc_isolate_group_shared
liamappelbe Jul 24, 2025
637d3c4
migrate test
liamappelbe Jul 24, 2025
cb24ac7
block bindings
liamappelbe Jul 24, 2025
e35d578
wip
liamappelbe Jul 24, 2025
21b4dc9
wip
liamappelbe Jul 24, 2025
6576ce3
Switch to send port approach
liamappelbe Aug 4, 2025
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
30 changes: 15 additions & 15 deletions .github/workflows/ffigen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,17 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046
with:
channel: 'stable'
channel: master
- id: install
name: Install dependencies
run: flutter pub get && flutter pub get --directory="example/shared_bindings" && flutter pub get --directory="../objective_c"
run: dart pub get && dart pub get --directory="example/shared_bindings" && dart pub get --directory="../objective_c"
- name: Check formatting
run: dart format --output=none --set-exit-if-changed .
if: always() && steps.install.outcome == 'success'
- name: Build test dylib and bindings
run: dart test/setup.dart
- name: Analyze code
run: flutter analyze --fatal-infos
run: dart analyze --fatal-infos

test-linux:
needs: analyze
Expand All @@ -56,9 +56,9 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046
with:
channel: 'stable'
channel: master
- name: Install dependencies
run: flutter pub get && flutter pub get --directory="../jni"
run: dart pub get && flutter pub get --directory="../jni"
- name: Install libclang-14-dev
run: sudo apt-get install libclang-14-dev
- name: Build test dylib and bindings
Expand All @@ -80,9 +80,9 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046
with:
channel: stable
channel: master
- name: Install dependencies
run: flutter pub get && flutter pub get --directory="../objective_c" && flutter pub get --directory="../jni"
run: dart pub get && dart pub get --directory="../objective_c" && flutter pub get --directory="../jni"
- name: Install clang-format
uses: ConorMacBride/install-package@3e7ad059e07782ee54fa35f827df52aae0626f30
with:
Expand Down Expand Up @@ -122,7 +122,7 @@ jobs:
with:
channel: master
- name: Install dependencies
run: flutter pub get && flutter pub get --directory="../objective_c" && flutter pub get --directory="../jni"
run: dart pub get && dart pub get --directory="../objective_c" && flutter pub get --directory="../jni"
- name: Install clang-format
uses: ConorMacBride/install-package@3e7ad059e07782ee54fa35f827df52aae0626f30
with:
Expand All @@ -142,13 +142,13 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046
with:
channel: 'stable'
channel: master
- name: Install dependencies
run: flutter pub get && flutter pub get --directory="../objective_c"
run: dart pub get && dart pub get --directory="../objective_c"
- name: Build test dylib and bindings
run: dart test/setup.dart --main-thread-dispatcher
run: dart test/setup.dart
- name: Run Flutter tests
run: flutter test
run: dart test

test-windows:
needs: analyze
Expand All @@ -160,9 +160,9 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046
with:
channel: 'stable'
channel: master
- name: Install dependencies
run: flutter pub get && flutter pub get --directory="../jni"
run: dart pub get && flutter pub get --directory="../jni"
- name: Build test dylib and bindings
run: dart test/setup.dart
- name: Run VM tests
Expand Down Expand Up @@ -191,7 +191,7 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046
with:
channel: "master"
channel: master
- name: Install dependencies
run: flutter pub get
- name: Build test dylib and bindings
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/ffigen_weekly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,17 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046
with:
channel: 'stable'
channel: master
- name: Install dependencies
run: flutter pub get && flutter pub get --directory="../objective_c" && flutter pub get --directory="../jni"
run: dart pub get && dart pub get --directory="../objective_c" && flutter pub get --directory="../jni"
- name: Install clang-format
uses: ConorMacBride/install-package@3e7ad059e07782ee54fa35f827df52aae0626f30
with:
brew: clang-format
- name: Build test dylib and bindings
run: dart test/setup.dart --main-thread-dispatcher
run: dart test/setup.dart
- name: Run VM tests
run: flutter test
run: dart test
- name: Generate package:jni bindings
run: dart run tool/generate_ffi_bindings.dart
working-directory: pkgs/jni/
29 changes: 13 additions & 16 deletions .github/workflows/objective_c.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,17 @@ jobs:
fail-fast: false
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046
- uses: dart-lang/setup-dart@e51d8e571e22473a2ddebf0ef8a2123f0ab2c02c
with:
channel: 'stable'
sdk: dev
- id: install
name: Install dependencies
run: flutter pub get
run: dart pub get
- name: Check formatting
run: dart format --output=none --set-exit-if-changed .
if: always() && steps.install.outcome == 'success'
- name: Analyze code
run: flutter analyze --fatal-infos
run: dart analyze --fatal-infos
if: always() && steps.install.outcome == 'success'

test-mac:
Expand All @@ -52,14 +52,11 @@ jobs:
working-directory: pkgs/objective_c/
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046
- uses: dart-lang/setup-dart@e51d8e571e22473a2ddebf0ef8a2123f0ab2c02c
with:
channel: 'stable'
sdk: dev
- name: Install dependencies
run: flutter pub get
- name: Build test dylib
# TODO(https://github.com/dart-lang/native/issues/1068): Remove this.
run: dart test/setup.dart
run: dart pub get
- name: Install coverage
run: dart pub global activate coverage
- name: Run VM tests and collect coverage
Expand Down Expand Up @@ -87,13 +84,13 @@ jobs:
runs-on: 'macos-latest'
defaults:
run:
working-directory: pkgs/objective_c/example/
working-directory: pkgs/objective_c/example
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046
- uses: dart-lang/setup-dart@e51d8e571e22473a2ddebf0ef8a2123f0ab2c02c
with:
channel: 'stable'
sdk: dev
- name: Install dependencies
run: flutter pub get
- name: Build the example app
run: flutter build macos
run: dart pub get
- name: Run the example app
run: dart run lib/main.dart
57 changes: 38 additions & 19 deletions pkgs/ffigen/lib/src/code_generator/objc_block.dart
Original file line number Diff line number Diff line change
Expand Up @@ -141,11 +141,20 @@ class ObjCBlock extends BindingType {
final closureCallable = w.topLevelUniqueNamer.makeUnique(
'_${name}_closureCallable',
);
final sharedTrampoline = w.topLevelUniqueNamer.makeUnique(
'_${name}_sharedTrampoline',
);
final sharedCallable = w.topLevelUniqueNamer.makeUnique(
'_${name}_sharedCallable',
);
final listenerTrampoline = w.topLevelUniqueNamer.makeUnique(
'_${name}_listenerTrampoline',
);
final listenerCallable = w.topLevelUniqueNamer.makeUnique(
'_${name}_listenerCallable',
final listenerPort = w.topLevelUniqueNamer.makeUnique(
'_${name}_listenerPort',
);
final listenerSendPort = w.topLevelUniqueNamer.makeUnique(
'_${name}_listenerSendPort',
);
final blockingTrampoline = w.topLevelUniqueNamer.makeUnique(
'_${name}_blockingTrampoline',
Expand All @@ -163,6 +172,7 @@ class ObjCBlock extends BindingType {
final newPointerBlock = ObjCBuiltInFunctions.newPointerBlock.gen(w);
final newClosureBlock = ObjCBuiltInFunctions.newClosureBlock.gen(w);
final getBlockClosure = ObjCBuiltInFunctions.getBlockClosure.gen(w);
final retainFn = ObjCBuiltInFunctions.objectRetain.gen(w);
final releaseFn = ObjCBuiltInFunctions.objectRelease.gen(w);
final objCContext = ObjCBuiltInFunctions.objCContext.gen(w);
final signalWaiterFn = ObjCBuiltInFunctions.signalWaiter.gen(w);
Expand Down Expand Up @@ -194,14 +204,30 @@ $voidPtrCType $closureCallable = ${w.ffiLibraryPrefix}.Pointer.fromFunction<

if (hasListener) {
// Write the listener trampoline function.
final retains = <String>['$retainFn(block.cast()).cast()'];
for (var i = 0; i < params.length; ++i) {
final param = params[i];
final argName = 'arg$i';
retains.add(param.type.generateDartRetain(w, argName) ?? argName);
}
s.write('''
$returnFfiDartType $listenerTrampoline(
$returnFfiDartType $sharedTrampoline(
$blockCType block, ${func.paramsFfiDartType}) {
final msg = (${retains.join(', ')});
$listenerSendPort.send(msg);
}
${func.trampNatCallType} $sharedCallable =
${func.trampNatCallType}.isolateGroupShared(
$sharedTrampoline $exceptionalReturn)..keepIsolateAlive = false;
$returnFfiDartType $listenerTrampoline(dynamic msg) {
final ${params.isEmpty ? '$blockCType block' : '($blockCType block, ${func.paramsFfiDartType})'} = msg;
($getBlockClosure(block) as ${func.ffiDartType})(${func.paramsNameOnly});
$releaseFn(block.cast());
}
${func.trampNatCallType} $listenerCallable = ${func.trampNatCallType}.listener(
$listenerTrampoline $exceptionalReturn)..keepIsolateAlive = false;
final $listenerPort = RawReceivePort(
$listenerTrampoline)..keepIsolateAlive = false;
@pragma('vm:shared')
final $listenerSendPort = $listenerPort.sendPort;
$returnFfiDartType $blockingTrampoline(
$blockCType block, ${blockingFunc.paramsFfiDartType}) {
try {
Expand Down Expand Up @@ -295,7 +321,6 @@ abstract final class $name {
);
final listenerConvFn =
'(${func.paramsFfiDartType}) => $listenerConvFnInvocation';
final wrapListenerFn = _blockWrappers!.listenerWrapper.name;
final wrapBlockingFn = _blockWrappers!.blockingWrapper.name;

s.write('''
Expand All @@ -311,11 +336,10 @@ abstract final class $name {
/// until it is garbage collected by both Dart and ObjC.
static $blockType listener(${func.dartType} fn,
{bool keepIsolateAlive = true}) {
final raw = $newClosureBlock($listenerCallable.nativeFunction.cast(),
final tramp = $newClosureBlock($sharedCallable.nativeFunction.cast(),
$listenerConvFn, keepIsolateAlive);
final wrapper = $wrapListenerFn(raw);
$releaseFn(raw.cast());
return $blockType(wrapper, retain: false, release: true);
print('listener send port: ' + $listenerSendPort.toString());
return $blockType(tramp, retain: true, release: true);
}

/// Creates a blocking block from a Dart function.
Expand Down Expand Up @@ -419,7 +443,6 @@ ref.pointer.ref.invoke.cast<${func.trampNatFnCType}>()
...argsReceived,
].join(', ');

final listenerWrapper = _blockWrappers!.listenerWrapper.name;
final blockingWrapper = _blockWrappers!.blockingWrapper.name;
final listenerName = UniqueNamer.cSafeName(
w.objCLevelUniqueNamer.makeUnique('ListenerTrampoline'),
Expand All @@ -431,14 +454,6 @@ ref.pointer.ref.invoke.cast<${func.trampNatFnCType}>()
return '''

typedef ${returnType.getNativeType()} (^$listenerName)($argStr);
__attribute__((visibility("default"))) __attribute__((used))
$listenerName $listenerWrapper($listenerName block) NS_RETURNS_RETAINED {
return ^void($argStr) {
${generateRetain('block')};
block(${retains.join(', ')});
};
}

typedef ${returnType.getNativeType()} (^$blockingName)($blockingArgStr);
__attribute__((visibility("default"))) __attribute__((used))
$listenerName $blockingWrapper(
Expand Down Expand Up @@ -532,6 +547,10 @@ $ret $fnName(id target, $argRecv) {
@override
String? generateRetain(String value) => 'objc_retainBlock($value)';

@override
String? generateDartRetain(Writer w, String value) =>
'${ObjCBuiltInFunctions.blockRetain.gen(w)}($value.cast()).cast()';

@override
String toString() =>
'($returnType (^)(${params.map((p) => p.type.toString()).join(', ')}))';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ class ObjCBuiltInFunctions {
'getProtocolMethodSignature',
);
static const getProtocol = ObjCImport('getProtocol');
static const blockRetain = ObjCImport('blockRetain');
static const objectRetain = ObjCImport('objectRetain');
static const objectRelease = ObjCImport('objectRelease');
static const signalWaiter = ObjCImport('signalWaiter');
static const objCContext = ObjCImport('objCContext');
Expand Down
9 changes: 0 additions & 9 deletions pkgs/ffigen/lib/src/code_generator/objc_built_in_types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -131,12 +131,3 @@ const objCBuiltInCategories = {
'NSNumberIsFloat',
'NSStringExtensionMethods',
};

const objCBuiltInGlobals = {
'NSKeyValueChangeIndexesKey',
'NSKeyValueChangeKindKey',
'NSKeyValueChangeNewKey',
'NSKeyValueChangeNotificationIsPriorKey',
'NSKeyValueChangeOldKey',
'NSLocalizedDescriptionKey',
};
4 changes: 4 additions & 0 deletions pkgs/ffigen/lib/src/code_generator/objc_interface.dart
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,10 @@ ${generateAsStub ? '' : _generateMethods(w)}
String? generateRetain(String value) =>
'(__bridge id)(__bridge_retained void*)($value)';

@override
String? generateDartRetain(Writer w, String value) =>
'${ObjCBuiltInFunctions.objectRetain.gen(w)}($value)';

@override
void visit(Visitation visitation) => visitation.visitObjCInterface(this);

Expand Down
4 changes: 4 additions & 0 deletions pkgs/ffigen/lib/src/code_generator/objc_nullable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ class ObjCNullable extends Type {
@override
String? generateRetain(String value) => child.generateRetain(value);

@override
String? generateDartRetain(Writer w, String value) =>
child.generateDartRetain(w, value);

@override
String toString() => '$child?';

Expand Down
4 changes: 4 additions & 0 deletions pkgs/ffigen/lib/src/code_generator/objc_protocol.dart
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,10 @@ Protocol* _${wrapName}_$originalName(void) { return @protocol($originalName); }
String? generateRetain(String value) =>
'(__bridge id)(__bridge_retained void*)($value)';

@override
String? generateDartRetain(Writer w, String value) =>
'${ObjCBuiltInFunctions.objectRetain.gen(w)}($value)';

bool _isSuperProtocolOf(ObjCProtocol protocol) {
if (protocol == this) return true;
for (final superProtocol in protocol.superProtocols) {
Expand Down
8 changes: 8 additions & 0 deletions pkgs/ffigen/lib/src/code_generator/pointer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ class ObjCObjectPointer extends PointerType {
String? generateRetain(String value) =>
'(__bridge id)(__bridge_retained void*)($value)';

@override
String? generateDartRetain(Writer w, String value) =>
'${ObjCBuiltInFunctions.objectRetain.gen(w)}($value)';

@override
bool isSupertypeOf(Type other) {
other = other.typealiasType;
Expand All @@ -177,6 +181,10 @@ class ObjCBlockPointer extends ObjCObjectPointer {
@override
String? generateRetain(String value) => 'objc_retainBlock($value)';

@override
String? generateDartRetain(Writer w, String value) =>
'${ObjCBuiltInFunctions.blockRetain.gen(w)}($value.cast()).cast()';

@override
bool isSupertypeOf(Type other) {
other = other.typealiasType;
Expand Down
7 changes: 7 additions & 0 deletions pkgs/ffigen/lib/src/code_generator/type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ abstract class Type extends AstNode {
/// Returns null if the Type does not need to be retained.
String? generateRetain(String value) => null;

/// Returns generated Dart code that retains a reference to the given value.
/// Returns null if the Type does not need to be retained.
String? generateDartRetain(Writer w, String value) => null;

/// Returns a human readable string representation of the Type. This is mostly
/// just for debugging, but it may also be used for non-functional code (eg to
/// name a variable or type in generated code).
Expand Down Expand Up @@ -230,6 +234,9 @@ abstract class BindingType extends NoLookUpBinding implements Type {
@override
String? generateRetain(String value) => null;

@override
String? generateDartRetain(Writer w, String value) => null;

@override
String toString() => originalName;

Expand Down
Loading
Loading