Skip to content

Commit 6a9282c

Browse files
authored
Delete the Dart functions associated with closure blocks (#1100)
* ObjC example, and infra for shipping native code with plugin * Fix the block leak * Mostly working * Fix analysis * Fix analysis * Fix example linker error * fmt * Daco's comments * Fix NSData
1 parent 042af36 commit 6a9282c

File tree

132 files changed

+9187
-739
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

132 files changed

+9187
-739
lines changed

.github/labeler.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,7 @@
2727
'package:native_toolchain_c':
2828
- changed-files:
2929
- any-glob-to-any-file: 'pkgs/native_toolchain_c/**'
30+
31+
'package:objective_c':
32+
- changed-files:
33+
- any-glob-to-any-file: 'pkgs/objective_c/**'

.github/workflows/objective_c.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ jobs:
5757
channel: 'stable'
5858
- name: Install dependencies
5959
run: flutter pub get
60+
- name: Build test dylib
61+
# TODO(https://github.com/dart-lang/native/issues/1068): Remove this.
62+
run: dart test/setup.dart
6063
- name: Run VM tests
6164
run: dart test
6265
- name: Collect coverage

pkgs/ffigen/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
.dart_tool/
55
.packages
66
pubspec.lock
7+
.flutter-plugins
8+
.flutter-plugins-dependencies
79

810
# IDE and debugger files.
911
.clangd

pkgs/ffigen/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
generated bindings.
2828
- Add --[no-]format option to ffigen command line, which controls whether the
2929
formatting step happens. Defaults to true.
30+
- Delete Dart functions associated with ObjC closure blocks when the block is
31+
destroyed. Fixes https://github.com/dart-lang/native/issues/204
3032

3133
## 11.0.0
3234

pkgs/ffigen/lib/src/code_generator/objc_block.dart

Lines changed: 10 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,9 @@ class ObjCBlock extends BindingType {
5858
w.topLevelUniqueNamer.makeUnique('_${name}_fnPtrTrampoline');
5959
final closureTrampoline =
6060
w.topLevelUniqueNamer.makeUnique('_${name}_closureTrampoline');
61-
final registerClosure =
62-
w.topLevelUniqueNamer.makeUnique('_${name}_registerClosure');
63-
final closureRegistry =
64-
w.topLevelUniqueNamer.makeUnique('_${name}_closureRegistry');
65-
final closureRegistryIndex =
66-
w.topLevelUniqueNamer.makeUnique('_${name}_closureRegistryIndex');
61+
final newPointerBlock = ObjCBuiltInFunctions.newPointerBlock.gen(w);
62+
final newClosureBlock = ObjCBuiltInFunctions.newClosureBlock.gen(w);
63+
final getBlockClosure = ObjCBuiltInFunctions.getBlockClosure.gen(w);
6764
final trampFuncType = FunctionType(
6865
returnType: returnType,
6966
parameters: [Parameter(type: blockPtr, name: 'block'), ...params]);
@@ -92,21 +89,10 @@ $returnFfiDartType $funcPtrTrampoline($blockCType block, $paramsFfiDartType) =>
9289
.asFunction<$funcFfiDartType>()($paramsNameOnly);
9390
''');
9491

95-
// Write the closure registry function.
96-
s.write('''
97-
final $closureRegistry = <int, $funcFfiDartType>{};
98-
int $closureRegistryIndex = 0;
99-
$voidPtr $registerClosure($funcFfiDartType fn) {
100-
final id = ++$closureRegistryIndex;
101-
$closureRegistry[id] = fn;
102-
return $voidPtr.fromAddress(id);
103-
}
104-
''');
105-
10692
// Write the closure based trampoline function.
10793
s.write('''
10894
$returnFfiDartType $closureTrampoline($blockCType block, $paramsFfiDartType) =>
109-
$closureRegistry[block.ref.target.address]!($paramsNameOnly);
95+
($getBlockClosure(block) as $funcFfiDartType)($paramsNameOnly);
11096
''');
11197

11298
// Snippet that converts a Dart typed closure to FfiDart type. This snippet
@@ -141,7 +127,7 @@ class $name extends ${ObjCBuiltInFunctions.blockBase.gen(w)} {
141127
/// the isolate that registered it. Invoking the block on the wrong thread
142128
/// will result in a crash.
143129
$name.fromFunctionPointer($natFnPtr ptr) :
144-
this._(${ObjCBuiltInFunctions.newBlock.gen(w)}(
130+
this._($newPointerBlock(
145131
_cFuncTrampoline ??= ${w.ffiLibraryPrefix}.Pointer.fromFunction<
146132
$trampFuncCType>($funcPtrTrampoline
147133
$exceptionalReturn).cast(), ptr.cast()));
@@ -153,10 +139,10 @@ class $name extends ${ObjCBuiltInFunctions.blockBase.gen(w)} {
153139
/// the isolate that registered it. Invoking the block on the wrong thread
154140
/// will result in a crash.
155141
$name.fromFunction($funcDartType fn) :
156-
this._(${ObjCBuiltInFunctions.newBlock.gen(w)}(
142+
this._($newClosureBlock(
157143
_dartFuncTrampoline ??= ${w.ffiLibraryPrefix}.Pointer.fromFunction<
158-
$trampFuncCType>($closureTrampoline
159-
$exceptionalReturn).cast(), $registerClosure($convFn)));
144+
$trampFuncCType>($closureTrampoline $exceptionalReturn).cast(),
145+
$convFn));
160146
static $voidPtr? _dartFuncTrampoline;
161147
162148
''');
@@ -174,11 +160,10 @@ class $name extends ${ObjCBuiltInFunctions.blockBase.gen(w)} {
174160
/// Note that unlike the default behavior of NativeCallable.listener, listener
175161
/// blocks do not keep the isolate alive.
176162
$name.listener($funcDartType fn) :
177-
this._(${ObjCBuiltInFunctions.newBlock.gen(w)}(
163+
this._($newClosureBlock(
178164
(_dartFuncListenerTrampoline ??= $nativeCallableType.listener(
179165
$closureTrampoline $exceptionalReturn)..keepIsolateAlive =
180-
false).nativeFunction.cast(),
181-
$registerClosure($convFn)));
166+
false).nativeFunction.cast(), $convFn));
182167
static $nativeCallableType? _dartFuncListenerTrampoline;
183168
184169
''');

pkgs/ffigen/lib/src/code_generator/objc_built_in_functions.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ class ObjCBuiltInFunctions {
1919
static const msgSendFpretPointer = ObjCImport('msgSendFpretPointer');
2020
static const msgSendStretPointer = ObjCImport('msgSendStretPointer');
2121
static const useMsgSendVariants = ObjCImport('useMsgSendVariants');
22-
static const newBlock = ObjCImport('newBlock');
22+
static const newPointerBlock = ObjCImport('newPointerBlock');
23+
static const newClosureBlock = ObjCImport('newClosureBlock');
24+
static const getBlockClosure = ObjCImport('getBlockClosure');
2325
static const objectBase = ObjCImport('ObjCObjectBase');
2426
static const blockBase = ObjCImport('ObjCBlockBase');
2527

pkgs/ffigen/test/native_objc_test/automated_ref_count_test.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ void main() {
2323
group('Automatic reference counting', () {
2424
setUpAll(() {
2525
logWarnings();
26+
// TODO(https://github.com/dart-lang/native/issues/1068): Remove this.
27+
DynamicLibrary.open('../objective_c/test/objective_c.dylib');
2628
final dylib =
2729
File('test/native_objc_test/automated_ref_count_test.dylib');
2830
verifySetupFile(dylib);

pkgs/ffigen/test/native_objc_test/bad_method_test.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ void main() {
1717
group('bad_method_test', () {
1818
setUpAll(() {
1919
logWarnings();
20+
// TODO(https://github.com/dart-lang/native/issues/1068): Remove this.
21+
DynamicLibrary.open('../objective_c/test/objective_c.dylib');
2022
final dylib = File('test/native_objc_test/bad_method_test.dylib');
2123
verifySetupFile(dylib);
2224
DynamicLibrary.open(dylib.absolute.path);

pkgs/ffigen/test/native_objc_test/block_test.dart

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import 'dart:ffi';
1212
import 'dart:io';
1313

1414
import 'package:ffi/ffi.dart';
15+
import 'package:objective_c/src/internal.dart' as internal_for_testing
16+
show blockHasRegisteredClosure;
1517
import 'package:test/test.dart';
1618

1719
import '../test_utils.dart';
@@ -34,6 +36,8 @@ void main() {
3436
group('Blocks', () {
3537
setUpAll(() {
3638
logWarnings();
39+
// TODO(https://github.com/dart-lang/native/issues/1068): Remove this.
40+
DynamicLibrary.open('../objective_c/test/objective_c.dylib');
3741
final dylib = File('test/native_objc_test/block_test.dylib');
3842
verifySetupFile(dylib);
3943
lib = BlockTestObjCLibrary(DynamicLibrary.open(dylib.absolute.path));
@@ -229,6 +233,8 @@ void main() {
229233
Pointer<Void> funcPointerBlockRefCountTest() {
230234
final block =
231235
IntBlock.fromFunctionPointer(Pointer.fromFunction(_add100, 999));
236+
expect(
237+
internal_for_testing.blockHasRegisteredClosure(block.pointer), false);
232238
expect(lib.getBlockRetainCount(block.pointer.cast()), 1);
233239
return block.pointer.cast();
234240
}
@@ -241,18 +247,25 @@ void main() {
241247

242248
Pointer<Void> funcBlockRefCountTest() {
243249
final block = IntBlock.fromFunction(makeAdder(4000));
250+
expect(
251+
internal_for_testing.blockHasRegisteredClosure(block.pointer), true);
244252
expect(lib.getBlockRetainCount(block.pointer.cast()), 1);
245253
return block.pointer.cast();
246254
}
247255

248-
test('Function block ref counting', () {
256+
test('Function block ref counting', () async {
249257
final rawBlock = funcBlockRefCountTest();
250258
doGC();
259+
await Future<void>.delayed(Duration.zero); // Let dispose message arrive.
251260
expect(lib.getBlockRetainCount(rawBlock), 0);
261+
expect(internal_for_testing.blockHasRegisteredClosure(rawBlock.cast()),
262+
false);
252263
});
253264

254265
Pointer<Void> blockManualRetainRefCountTest() {
255266
final block = IntBlock.fromFunction(makeAdder(4000));
267+
expect(
268+
internal_for_testing.blockHasRegisteredClosure(block.pointer), true);
256269
expect(lib.getBlockRetainCount(block.pointer.cast()), 1);
257270
final rawBlock = block.retainAndReturnPointer().cast<Void>();
258271
expect(lib.getBlockRetainCount(rawBlock.cast()), 2);
@@ -265,13 +278,16 @@ void main() {
265278
return lib.getBlockRetainCount(block.pointer.cast());
266279
}
267280

268-
test('Block ref counting with manual retain and release', () {
281+
test('Block ref counting with manual retain and release', () async {
269282
final rawBlock = blockManualRetainRefCountTest();
270283
doGC();
271284
expect(lib.getBlockRetainCount(rawBlock), 1);
272285
expect(blockManualRetainRefCountTest2(rawBlock), 1);
273286
doGC();
287+
await Future<void>.delayed(Duration.zero); // Let dispose message arrive.
274288
expect(lib.getBlockRetainCount(rawBlock), 0);
289+
expect(internal_for_testing.blockHasRegisteredClosure(rawBlock.cast()),
290+
false);
275291
});
276292

277293
(Pointer<Void>, Pointer<Void>, Pointer<Void>)
@@ -474,7 +490,7 @@ void main() {
474490
expect(descPtr.ref.reserved, 0);
475491
expect(descPtr.ref.size, isNot(0));
476492
expect(descPtr.ref.copy_helper, nullptr);
477-
expect(descPtr.ref.dispose_helper, nullptr);
493+
expect(descPtr.ref.dispose_helper, isNot(nullptr));
478494
expect(descPtr.ref.signature, nullptr);
479495
});
480496
});

pkgs/ffigen/test/native_objc_test/cast_test.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ void main() {
2121
group('cast', () {
2222
setUpAll(() {
2323
logWarnings();
24+
// TODO(https://github.com/dart-lang/native/issues/1068): Remove this.
25+
DynamicLibrary.open('../objective_c/test/objective_c.dylib');
2426
final dylib = File('test/native_objc_test/cast_test.dylib');
2527
verifySetupFile(dylib);
2628
DynamicLibrary.open(dylib.absolute.path);

pkgs/ffigen/test/native_objc_test/category_test.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ void main() {
1919
group('categories', () {
2020
setUpAll(() {
2121
logWarnings();
22+
// TODO(https://github.com/dart-lang/native/issues/1068): Remove this.
23+
DynamicLibrary.open('../objective_c/test/objective_c.dylib');
2224
final dylib = File('test/native_objc_test/category_test.dylib');
2325
verifySetupFile(dylib);
2426
DynamicLibrary.open(dylib.absolute.path);

pkgs/ffigen/test/native_objc_test/forward_decl_test.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ void main() {
1717
group('forward decl', () {
1818
setUpAll(() {
1919
logWarnings();
20+
// TODO(https://github.com/dart-lang/native/issues/1068): Remove this.
21+
DynamicLibrary.open('../objective_c/test/objective_c.dylib');
2022
final dylib = File('test/native_objc_test/forward_decl_test.dylib');
2123
verifySetupFile(dylib);
2224
DynamicLibrary.open(dylib.absolute.path);

pkgs/ffigen/test/native_objc_test/inherited_instancetype_test.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ void main() {
1919
group('inheritedInstancetype', () {
2020
setUpAll(() {
2121
logWarnings();
22+
// TODO(https://github.com/dart-lang/native/issues/1068): Remove this.
23+
DynamicLibrary.open('../objective_c/test/objective_c.dylib');
2224
final dylib =
2325
File('test/native_objc_test/inherited_instancetype_test.dylib');
2426
verifySetupFile(dylib);

pkgs/ffigen/test/native_objc_test/is_instance_test.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ void main() {
2020
group('isInstance', () {
2121
setUpAll(() {
2222
logWarnings();
23+
// TODO(https://github.com/dart-lang/native/issues/1068): Remove this.
24+
DynamicLibrary.open('../objective_c/test/objective_c.dylib');
2325
final dylib = File('test/native_objc_test/is_instance_test.dylib');
2426
verifySetupFile(dylib);
2527
DynamicLibrary.open(dylib.absolute.path);

pkgs/ffigen/test/native_objc_test/method_test.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ void main() {
2020
group('method calls', () {
2121
setUpAll(() {
2222
logWarnings();
23+
// TODO(https://github.com/dart-lang/native/issues/1068): Remove this.
24+
DynamicLibrary.open('../objective_c/test/objective_c.dylib');
2325
final dylib = File('test/native_objc_test/method_test.dylib');
2426
verifySetupFile(dylib);
2527
DynamicLibrary.open(dylib.absolute.path);

pkgs/ffigen/test/native_objc_test/native_objc_test.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ void main() {
1818
group('native_objc_test', () {
1919
setUpAll(() {
2020
logWarnings();
21+
// TODO(https://github.com/dart-lang/native/issues/1068): Remove this.
22+
DynamicLibrary.open('../objective_c/test/objective_c.dylib');
2123
final dylib = File('test/native_objc_test/native_objc_test.dylib');
2224
verifySetupFile(dylib);
2325
DynamicLibrary.open(dylib.absolute.path);

pkgs/ffigen/test/native_objc_test/nullable_inheritance_test.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ void main() {
2222
group('Nullable inheritance', () {
2323
setUpAll(() {
2424
logWarnings();
25+
// TODO(https://github.com/dart-lang/native/issues/1068): Remove this.
26+
DynamicLibrary.open('../objective_c/test/objective_c.dylib');
2527
final dylib =
2628
File('test/native_objc_test/nullable_inheritance_test.dylib');
2729
verifySetupFile(dylib);

pkgs/ffigen/test/native_objc_test/nullable_test.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ void main() {
2121
group('Nullability', () {
2222
setUpAll(() {
2323
logWarnings();
24+
// TODO(https://github.com/dart-lang/native/issues/1068): Remove this.
25+
DynamicLibrary.open('../objective_c/test/objective_c.dylib');
2426
final dylib = File('test/native_objc_test/nullable_test.dylib');
2527
verifySetupFile(dylib);
2628
DynamicLibrary.open(dylib.absolute.path);

pkgs/ffigen/test/native_objc_test/property_test.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ void main() {
2020
group('properties', () {
2121
setUpAll(() {
2222
logWarnings();
23+
// TODO(https://github.com/dart-lang/native/issues/1068): Remove this.
24+
DynamicLibrary.open('../objective_c/test/objective_c.dylib');
2325
final dylib = File('test/native_objc_test/property_test.dylib');
2426
verifySetupFile(dylib);
2527
DynamicLibrary.open(dylib.absolute.path);

pkgs/ffigen/test/native_objc_test/rename_test.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ void main() {
1717
group('rename_test', () {
1818
setUpAll(() {
1919
logWarnings();
20+
// TODO(https://github.com/dart-lang/native/issues/1068): Remove this.
21+
DynamicLibrary.open('../objective_c/test/objective_c.dylib');
2022
final dylib = File('test/native_objc_test/rename_test.dylib');
2123
verifySetupFile(dylib);
2224
DynamicLibrary.open(dylib.absolute.path);

pkgs/ffigen/test/native_objc_test/setup.dart

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,21 +48,24 @@ Future<void> _buildSwift(
4848
print('Generated files: $outputHeader and $outputLib');
4949
}
5050

51-
Future<void> _generateBindings(String config) async {
52-
final args = [
53-
'run',
54-
'ffigen',
55-
'--config',
56-
'test/native_objc_test/$config',
57-
];
51+
Future<void> _runDart(List<String> args) async {
5852
final process =
5953
await Process.start(Platform.executable, args, workingDirectory: '../..');
6054
unawaited(stdout.addStream(process.stdout));
6155
unawaited(stderr.addStream(process.stderr));
6256
final result = await process.exitCode;
6357
if (result != 0) {
64-
throw ProcessException('dart', args, 'Generating bindings', result);
58+
throw ProcessException('dart', args, 'Running Dart command', result);
6559
}
60+
}
61+
62+
Future<void> _generateBindings(String config) async {
63+
await _runDart([
64+
'run',
65+
'ffigen',
66+
'--config',
67+
'test/native_objc_test/$config',
68+
]);
6669
print('Generated bindings for: $config');
6770
}
6871

@@ -129,5 +132,6 @@ Future<void> main(List<String> arguments) async {
129132
return await clean(_getTestNames());
130133
}
131134

135+
await _runDart(['../objective_c/test/setup.dart']);
132136
return await build(arguments.isNotEmpty ? arguments : _getTestNames());
133137
}

pkgs/ffigen/test/native_objc_test/static_func_native_test.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ void main() {
2626
group('static functions', () {
2727
setUpAll(() {
2828
logWarnings();
29+
// TODO(https://github.com/dart-lang/native/issues/1068): Remove this.
30+
DynamicLibrary.open('../objective_c/test/objective_c.dylib');
2931
final dylib = File('test/native_objc_test/static_func_test.dylib');
3032
verifySetupFile(dylib);
3133
DynamicLibrary.open(dylib.absolute.path);

pkgs/ffigen/test/native_objc_test/static_func_test.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ void main() {
2828
group('static functions', () {
2929
setUpAll(() {
3030
logWarnings();
31+
// TODO(https://github.com/dart-lang/native/issues/1068): Remove this.
32+
DynamicLibrary.open('../objective_c/test/objective_c.dylib');
3133
final dylib = File('test/native_objc_test/static_func_test.dylib');
3234
verifySetupFile(dylib);
3335
lib = StaticFuncTestObjCLibrary(DynamicLibrary.open(dylib.absolute.path));

pkgs/ffigen/test/native_objc_test/string_test.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ void main() {
1919
group('string', () {
2020
setUpAll(() {
2121
logWarnings();
22+
// TODO(https://github.com/dart-lang/native/issues/1068): Remove this.
23+
DynamicLibrary.open('../objective_c/test/objective_c.dylib');
2224
final dylib = File('test/native_objc_test/string_test.dylib');
2325
verifySetupFile(dylib);
2426
DynamicLibrary.open(dylib.absolute.path);

pkgs/ffigen/test/native_objc_test/swift_class_test.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ void main() {
1717
group('swift_class_test', () {
1818
setUpAll(() {
1919
logWarnings();
20+
// TODO(https://github.com/dart-lang/native/issues/1068): Remove this.
21+
DynamicLibrary.open('../objective_c/test/objective_c.dylib');
2022
final dylib = File('test/native_objc_test/swift_class_test.dylib');
2123
verifySetupFile(dylib);
2224
DynamicLibrary.open(dylib.absolute.path);

0 commit comments

Comments
 (0)