From 3e6dfe2484bb0aecf655bfd82dc3bfd4d290e965 Mon Sep 17 00:00:00 2001 From: Gurleen-kansary Date: Fri, 30 Jan 2026 03:57:41 +0530 Subject: [PATCH 1/4] [objective_c] Move test/util.c to helper package (#2999) --- pkgs/objective_c_helper/lib/src/util.c | 46 ++++++++++++++++++++++++++ pkgs/objective_c_helper/pubspec.yaml | 7 ++++ 2 files changed, 53 insertions(+) create mode 100644 pkgs/objective_c_helper/lib/src/util.c create mode 100644 pkgs/objective_c_helper/pubspec.yaml diff --git a/pkgs/objective_c_helper/lib/src/util.c b/pkgs/objective_c_helper/lib/src/util.c new file mode 100644 index 0000000000..09c6016bd3 --- /dev/null +++ b/pkgs/objective_c_helper/lib/src/util.c @@ -0,0 +1,46 @@ +// Copyright (c) 2024, 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. + +#include +#include + +typedef struct { + void* isa; + int flags; + // There are other fields, but we just need the flags and isa. +} BlockRefCountExtractor; + +uint64_t getBlockRetainCount(BlockRefCountExtractor* block) { + // The ref count is stored in the lower bits of the flags field, but skips the + // 0x1 bit. + return (block->flags & 0xFFFF) >> 1; +} + +typedef struct { + uint64_t header; +} ObjectRefCountExtractor; + +static const uint64_t k128OrMore = 128; + +// Returns the ref count of the object, up to 127. For counts above this, always +// returns k128OrMore. +uint64_t getObjectRetainCount(ObjectRefCountExtractor* object) { + uint64_t count = object->header >> 56; + return count < 0x80 ? count : k128OrMore; +} + +int isReadableMemory(void* ptr) { + vm_map_t task = mach_task_self(); + mach_vm_address_t address = (mach_vm_address_t)ptr; + mach_vm_size_t size = 0; + vm_region_basic_info_data_64_t info; + mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64; + mach_port_t object_name; + kern_return_t status = + mach_vm_region(task, &address, &size, VM_REGION_BASIC_INFO_64, + (vm_region_info_t)&info, &count, &object_name); + if (status != KERN_SUCCESS) return 0; + return ((mach_vm_address_t)ptr) >= address && + (info.protection & VM_PROT_READ); +} diff --git a/pkgs/objective_c_helper/pubspec.yaml b/pkgs/objective_c_helper/pubspec.yaml new file mode 100644 index 0000000000..21dbe41655 --- /dev/null +++ b/pkgs/objective_c_helper/pubspec.yaml @@ -0,0 +1,7 @@ +name: objective_c_helper +description: Helper package for objective_c tests +version: 0.0.1 +environment: + sdk: ">=2.19.0 <3.0.0" +dependencies: {} +dev_dependencies: {} From 2f4ab8207d6b40b19c88b68f03f7ca77f30a723d Mon Sep 17 00:00:00 2001 From: Gurleen-kansary Date: Thu, 5 Feb 2026 03:49:44 +0530 Subject: [PATCH 2/4] [objective_c] Remove test/util.c from objective_c now that helper exists --- pkgs/objective_c/test/util.c | 46 ------------------------------------ 1 file changed, 46 deletions(-) delete mode 100644 pkgs/objective_c/test/util.c diff --git a/pkgs/objective_c/test/util.c b/pkgs/objective_c/test/util.c deleted file mode 100644 index 09c6016bd3..0000000000 --- a/pkgs/objective_c/test/util.c +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2024, 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. - -#include -#include - -typedef struct { - void* isa; - int flags; - // There are other fields, but we just need the flags and isa. -} BlockRefCountExtractor; - -uint64_t getBlockRetainCount(BlockRefCountExtractor* block) { - // The ref count is stored in the lower bits of the flags field, but skips the - // 0x1 bit. - return (block->flags & 0xFFFF) >> 1; -} - -typedef struct { - uint64_t header; -} ObjectRefCountExtractor; - -static const uint64_t k128OrMore = 128; - -// Returns the ref count of the object, up to 127. For counts above this, always -// returns k128OrMore. -uint64_t getObjectRetainCount(ObjectRefCountExtractor* object) { - uint64_t count = object->header >> 56; - return count < 0x80 ? count : k128OrMore; -} - -int isReadableMemory(void* ptr) { - vm_map_t task = mach_task_self(); - mach_vm_address_t address = (mach_vm_address_t)ptr; - mach_vm_size_t size = 0; - vm_region_basic_info_data_64_t info; - mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64; - mach_port_t object_name; - kern_return_t status = - mach_vm_region(task, &address, &size, VM_REGION_BASIC_INFO_64, - (vm_region_info_t)&info, &count, &object_name); - if (status != KERN_SUCCESS) return 0; - return ((mach_vm_address_t)ptr) >= address && - (info.protection & VM_PROT_READ); -} From 5406919ed84eb5efc44757bb8a0c20d8b3fd0a8c Mon Sep 17 00:00:00 2001 From: Gurleen-kansary Date: Thu, 5 Feb 2026 04:30:54 +0530 Subject: [PATCH 3/4] [objective_c] Move test/util.c to helper package (dart-lang#2999) --- pkgs/objective_c/pubspec.yaml | 2 ++ pkgs/objective_c/test/autorelease_test.dart | 2 +- pkgs/objective_c/test/interface_lists_test.dart | 3 +-- pkgs/objective_c/test/ns_input_stream_test.dart | 2 +- pkgs/objective_c/test/nsarray_test.dart | 2 +- pkgs/objective_c/test/nsdictionary_test.dart | 2 +- pkgs/objective_c/test/nsset_test.dart | 2 +- pkgs/objective_c/test/observer_test.dart | 2 +- 8 files changed, 9 insertions(+), 8 deletions(-) diff --git a/pkgs/objective_c/pubspec.yaml b/pkgs/objective_c/pubspec.yaml index a54fa017e3..7c2fe05ea2 100644 --- a/pkgs/objective_c/pubspec.yaml +++ b/pkgs/objective_c/pubspec.yaml @@ -45,3 +45,5 @@ dependency_overrides: path: ../hooks native_toolchain_c: path: ../native_toolchain_c + objective_c_helper: + path: ../objective_c_helper diff --git a/pkgs/objective_c/test/autorelease_test.dart b/pkgs/objective_c/test/autorelease_test.dart index 3a323920c0..b8bc07e301 100644 --- a/pkgs/objective_c/test/autorelease_test.dart +++ b/pkgs/objective_c/test/autorelease_test.dart @@ -11,7 +11,7 @@ import 'dart:ffi'; import 'package:objective_c/objective_c.dart'; import 'package:test/test.dart'; -import 'util.dart'; +import 'package:objective_c_helper/src/util.dart'; void main() { group('autoReleasePool', () { diff --git a/pkgs/objective_c/test/interface_lists_test.dart b/pkgs/objective_c/test/interface_lists_test.dart index 19837be516..f4d8f18012 100644 --- a/pkgs/objective_c/test/interface_lists_test.dart +++ b/pkgs/objective_c/test/interface_lists_test.dart @@ -11,8 +11,7 @@ import 'dart:io'; import 'package:ffigen/src/code_generator/objc_built_in_types.dart'; import 'package:path/path.dart' as p; import 'package:test/test.dart'; - -import 'util.dart'; +import 'package:objective_c_helper/src/util.dart'; // The default expect error message for sets isn't very useful. In the common // case where the sets are different lengths, the default matcher just says the diff --git a/pkgs/objective_c/test/ns_input_stream_test.dart b/pkgs/objective_c/test/ns_input_stream_test.dart index 6c04dbc88b..ffbe52afec 100644 --- a/pkgs/objective_c/test/ns_input_stream_test.dart +++ b/pkgs/objective_c/test/ns_input_stream_test.dart @@ -17,7 +17,7 @@ import 'package:ffi/ffi.dart'; import 'package:objective_c/objective_c.dart'; import 'package:test/test.dart'; -import 'util.dart'; +import 'package:objective_c_helper/src/util.dart'; Future<(int, Uint8List, bool, NSStreamStatus, NSError?)> read( NSInputStream stream, diff --git a/pkgs/objective_c/test/nsarray_test.dart b/pkgs/objective_c/test/nsarray_test.dart index 7b410fa983..f2130c29f7 100644 --- a/pkgs/objective_c/test/nsarray_test.dart +++ b/pkgs/objective_c/test/nsarray_test.dart @@ -11,7 +11,7 @@ import 'dart:ffi'; import 'package:objective_c/objective_c.dart'; import 'package:test/test.dart'; -import 'util.dart'; +import 'package:objective_c_helper/src/util.dart'; void main() { group('NSArray', () { diff --git a/pkgs/objective_c/test/nsdictionary_test.dart b/pkgs/objective_c/test/nsdictionary_test.dart index e7afb6d2ee..b9b2b0df45 100644 --- a/pkgs/objective_c/test/nsdictionary_test.dart +++ b/pkgs/objective_c/test/nsdictionary_test.dart @@ -11,7 +11,7 @@ import 'dart:ffi'; import 'package:objective_c/objective_c.dart'; import 'package:test/test.dart'; -import 'util.dart'; +import 'package:objective_c_helper/src/util.dart'; void main() { group('NSDictionary', () { diff --git a/pkgs/objective_c/test/nsset_test.dart b/pkgs/objective_c/test/nsset_test.dart index 90f6f3f7f9..dc315eee85 100644 --- a/pkgs/objective_c/test/nsset_test.dart +++ b/pkgs/objective_c/test/nsset_test.dart @@ -11,7 +11,7 @@ import 'dart:ffi'; import 'package:objective_c/objective_c.dart'; import 'package:test/test.dart'; -import 'util.dart'; +import 'package:objective_c_helper/src/util.dart'; void main() { group('NSSet', () { diff --git a/pkgs/objective_c/test/observer_test.dart b/pkgs/objective_c/test/observer_test.dart index c5e17a6760..8127f15f37 100644 --- a/pkgs/objective_c/test/observer_test.dart +++ b/pkgs/objective_c/test/observer_test.dart @@ -11,7 +11,7 @@ import 'dart:ffi'; import 'package:objective_c/objective_c.dart'; import 'package:test/test.dart'; -import 'util.dart'; +import 'package:objective_c_helper/src/util.dart'; void main() { group('Observer', () { From d57594e441011bab7b60264e73ccf33faa14f1ec Mon Sep 17 00:00:00 2001 From: Gurleen-kansary Date: Sat, 7 Feb 2026 11:48:03 +0530 Subject: [PATCH 4/4] Split util.dart: Dart-only helpers stay in objective_c, native helpers moved to objective_c_helper; updated tests and build hook --- pkgs/objective_c/hook/build.dart | 9 ----- pkgs/objective_c/test/autorelease_test.dart | 3 +- .../test/interface_lists_test.dart | 2 +- .../test/ns_input_stream_test.dart | 2 +- pkgs/objective_c/test/nsarray_test.dart | 2 +- pkgs/objective_c/test/nsdictionary_test.dart | 3 +- pkgs/objective_c/test/nsset_test.dart | 2 +- pkgs/objective_c/test/observer_test.dart | 3 +- pkgs/objective_c/test/util.dart | 33 ----------------- pkgs/objective_c_helper/lib/src/util.dart | 37 +++++++++++++++++++ 10 files changed, 44 insertions(+), 52 deletions(-) create mode 100644 pkgs/objective_c_helper/lib/src/util.dart diff --git a/pkgs/objective_c/hook/build.dart b/pkgs/objective_c/hook/build.dart index 94c2b63313..4ab0bee7d1 100644 --- a/pkgs/objective_c/hook/build.dart +++ b/pkgs/objective_c/hook/build.dart @@ -15,7 +15,6 @@ const assetName = 'objective_c.dylib'; // TODO(https://github.com/dart-lang/native/issues/2272): Remove this from the // main build. -const testFiles = ['test/util.c']; final logger = Logger('') ..level = Level.INFO @@ -59,14 +58,6 @@ void main(List args) async { } } - // Only include the test utils on mac OS. They use memory functions that - // aren't supported on iOS, like mach_vm_region. We don't need them on iOS - // anyway since we only run memory tests on mac. - if (os == OS.macOS) { - cFiles.addAll( - testFiles.map((f) => input.packageRoot.resolve(f).toFilePath()), - ); - } final sysroot = sdkPath(codeConfig); final minVersion = minOSVersion(codeConfig); diff --git a/pkgs/objective_c/test/autorelease_test.dart b/pkgs/objective_c/test/autorelease_test.dart index e8877d06bc..3130fcb368 100644 --- a/pkgs/objective_c/test/autorelease_test.dart +++ b/pkgs/objective_c/test/autorelease_test.dart @@ -10,8 +10,7 @@ import 'dart:ffi'; import 'package:objective_c/objective_c.dart'; import 'package:test/test.dart'; - -import 'package:objective_c_helper/src/util.dart'; +import 'util.dart'; void main() { group('autoReleasePool', () { diff --git a/pkgs/objective_c/test/interface_lists_test.dart b/pkgs/objective_c/test/interface_lists_test.dart index f4d8f18012..a76402e6b6 100644 --- a/pkgs/objective_c/test/interface_lists_test.dart +++ b/pkgs/objective_c/test/interface_lists_test.dart @@ -11,7 +11,7 @@ import 'dart:io'; import 'package:ffigen/src/code_generator/objc_built_in_types.dart'; import 'package:path/path.dart' as p; import 'package:test/test.dart'; -import 'package:objective_c_helper/src/util.dart'; +import 'util.dart'; // The default expect error message for sets isn't very useful. In the common // case where the sets are different lengths, the default matcher just says the diff --git a/pkgs/objective_c/test/ns_input_stream_test.dart b/pkgs/objective_c/test/ns_input_stream_test.dart index ffbe52afec..6c04dbc88b 100644 --- a/pkgs/objective_c/test/ns_input_stream_test.dart +++ b/pkgs/objective_c/test/ns_input_stream_test.dart @@ -17,7 +17,7 @@ import 'package:ffi/ffi.dart'; import 'package:objective_c/objective_c.dart'; import 'package:test/test.dart'; -import 'package:objective_c_helper/src/util.dart'; +import 'util.dart'; Future<(int, Uint8List, bool, NSStreamStatus, NSError?)> read( NSInputStream stream, diff --git a/pkgs/objective_c/test/nsarray_test.dart b/pkgs/objective_c/test/nsarray_test.dart index f2130c29f7..7b410fa983 100644 --- a/pkgs/objective_c/test/nsarray_test.dart +++ b/pkgs/objective_c/test/nsarray_test.dart @@ -11,7 +11,7 @@ import 'dart:ffi'; import 'package:objective_c/objective_c.dart'; import 'package:test/test.dart'; -import 'package:objective_c_helper/src/util.dart'; +import 'util.dart'; void main() { group('NSArray', () { diff --git a/pkgs/objective_c/test/nsdictionary_test.dart b/pkgs/objective_c/test/nsdictionary_test.dart index b9b2b0df45..852adab472 100644 --- a/pkgs/objective_c/test/nsdictionary_test.dart +++ b/pkgs/objective_c/test/nsdictionary_test.dart @@ -10,8 +10,7 @@ import 'dart:ffi'; import 'package:objective_c/objective_c.dart'; import 'package:test/test.dart'; - -import 'package:objective_c_helper/src/util.dart'; +import 'util.dart'; void main() { group('NSDictionary', () { diff --git a/pkgs/objective_c/test/nsset_test.dart b/pkgs/objective_c/test/nsset_test.dart index dc315eee85..90f6f3f7f9 100644 --- a/pkgs/objective_c/test/nsset_test.dart +++ b/pkgs/objective_c/test/nsset_test.dart @@ -11,7 +11,7 @@ import 'dart:ffi'; import 'package:objective_c/objective_c.dart'; import 'package:test/test.dart'; -import 'package:objective_c_helper/src/util.dart'; +import 'util.dart'; void main() { group('NSSet', () { diff --git a/pkgs/objective_c/test/observer_test.dart b/pkgs/objective_c/test/observer_test.dart index 8127f15f37..59b2c4f75f 100644 --- a/pkgs/objective_c/test/observer_test.dart +++ b/pkgs/objective_c/test/observer_test.dart @@ -10,8 +10,7 @@ import 'dart:ffi'; import 'package:objective_c/objective_c.dart'; import 'package:test/test.dart'; - -import 'package:objective_c_helper/src/util.dart'; +import 'util.dart'; void main() { group('Observer', () { diff --git a/pkgs/objective_c/test/util.dart b/pkgs/objective_c/test/util.dart index d0300e8eb3..0ec8e32bb5 100644 --- a/pkgs/objective_c/test/util.dart +++ b/pkgs/objective_c/test/util.dart @@ -32,37 +32,4 @@ void doGC() { calloc.free(gcNow); } -@Native)>(isLeaf: true, symbol: 'isReadableMemory') -external int _isReadableMemory(Pointer ptr); - -@Native)>( - isLeaf: true, - symbol: 'getObjectRetainCount', -) -external int _getObjectRetainCount(Pointer object); - -int objectRetainCount(Pointer object) { - if (_isReadableMemory(object.cast()) == 0) return 0; - final header = object.cast().value; - - // package:objective_c's isValidObject function internally calls - // object_getClass then isValidClass. But object_getClass can occasionally - // crash for invalid objects. This masking logic is a simplified version of - // what object_getClass does internally. This is less likely to crash, but - // more likely to break due to ObjC runtime updates, which is a reasonable - // trade off to make in tests where we're explicitly calling it many times - // on invalid objects. In package:objective_c's case, it doesn't matter so - // much if isValidObject crashes, since it's a best effort attempt to give a - // nice stack trace before the real crash, but it would be a problem if - // isValidObject broke due to a runtime update. - // These constants are the ISA_MASK macro defined in runtime/objc-private.h. - const maskX64 = 0x00007ffffffffff8; - const maskArm = 0x0000000ffffffff8; - final mask = Abi.current() == Abi.macosX64 ? maskX64 : maskArm; - final clazz = Pointer.fromAddress(header & mask); - - if (!internal_for_testing.isValidClass(clazz)) return 0; - return _getObjectRetainCount(object.cast()); -} - String pkgDir = findPackageRoot('objective_c').toFilePath(); diff --git a/pkgs/objective_c_helper/lib/src/util.dart b/pkgs/objective_c_helper/lib/src/util.dart new file mode 100644 index 0000000000..66db4c12a8 --- /dev/null +++ b/pkgs/objective_c_helper/lib/src/util.dart @@ -0,0 +1,37 @@ +import 'dart:ffi'; +import 'package:objective_c/objective_c.dart'; +import 'package:objective_c/src/internal.dart' as internal_for_testing show isValidClass; + +@Native)>(isLeaf: true, symbol: 'isReadableMemory') +external int _isReadableMemory(Pointer ptr); + +@Native)>( + isLeaf: true, + symbol: 'getObjectRetainCount', +) +external int _getObjectRetainCount(Pointer object); + +int objectRetainCount(Pointer object) { + if (_isReadableMemory(object.cast()) == 0) return 0; + final header = object.cast().value; + + // package:objective_c's isValidObject function internally calls + // object_getClass then isValidClass. But object_getClass can occasionally + // crash for invalid objects. This masking logic is a simplified version of + // what object_getClass does internally. This is less likely to crash, but + // more likely to break due to ObjC runtime updates, which is a reasonable + // trade off to make in tests where we're explicitly calling it many times + // on invalid objects. In package:objective_c's case, it doesn't matter so + // much if isValidObject crashes, since it's a best effort attempt to give a + // nice stack trace before the real crash, but it would be a problem if + // isValidObject broke due to a runtime update. + // These constants are the ISA_MASK macro defined in runtime/objc-private.h. + const maskX64 = 0x00007ffffffffff8; + const maskArm = 0x0000000ffffffff8; + final mask = Abi.current() == Abi.macosX64 ? maskX64 : maskArm; + final clazz = Pointer.fromAddress(header & mask); + + if (!internal_for_testing.isValidClass(clazz)) return 0; + return _getObjectRetainCount(object.cast()); +} +