| // 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. | |
| // All @Native bindings in this file live in the package's native asset dylib. | |
| @DefaultAsset('package:objective_c/objective_c.dylib') | |
| library; | |
| import 'dart:ffi'; | |
| import 'package:ffi/ffi.dart'; | |
| import 'package:native_test_helpers/native_test_helpers.dart'; | |
| import 'package:objective_c/objective_c.dart'; | |
| final _executeInternalCommand = () { | |
| try { | |
| return DynamicLibrary.process() | |
| .lookup<NativeFunction<Void Function(Pointer<Char>, Pointer<Void>)>>( | |
| 'Dart_ExecuteInternalCommand', | |
| ) | |
| .asFunction<void Function(Pointer<Char>, Pointer<Void>)>(); | |
| // ignore: avoid_catching_errors | |
| } on ArgumentError { | |
| return null; | |
| } | |
| }(); | |
| bool canDoGC = _executeInternalCommand != null; | |
| void doGC() { | |
| final gcNow = 'gc-now'.toNativeUtf8(); | |
| _executeInternalCommand!(gcNow.cast(), nullptr); | |
| calloc.free(gcNow); | |
| } | |
| @Native<Void Function(Pointer<Void>, Pointer<Bool>)>( | |
| symbol: 'attachReferenceTracker', | |
| ) | |
| external void _attachReferenceTracker( | |
| Pointer<Void> host, | |
| Pointer<Bool> isAlive, | |
| ); | |
| class ReferenceTracker { | |
| Pointer<Void> _host = nullptr; | |
| final Pointer<Bool> _isAlivePtr; | |
| ReferenceTracker(Arena arena) : this._(arena, arena<Bool>()..value = true); | |
| ReferenceTracker._(Arena arena, this._isAlivePtr); | |
| bool get isAlive => _isAlivePtr.value; | |
| Pointer<Void> get host => _host; | |
| void track(ObjCObject host) { | |
| assert(_host == nullptr); | |
| final hostRef = host.ref; | |
| _attachReferenceTracker(_host = hostRef.pointer.cast(), _isAlivePtr); | |
| } | |
| void trackBlock(ObjCBlock host) { | |
| assert(_host == nullptr); | |
| final hostRef = host.ref; | |
| _attachReferenceTracker(_host = hostRef.pointer.cast(), _isAlivePtr); | |
| } | |
| } | |
| String pkgDir = findPackageRoot('objective_c').toFilePath(); | |
| // --------------------------------------------------------------------------- | |
| // GC injection helpers (from gc_inject.m) — only available on macOS. | |
| // --------------------------------------------------------------------------- | |
| @Native<Void Function()>(isLeaf: true, symbol: 'initGCInject') | |
| external void initGCInject(); | |
| @Native<Void Function()>(isLeaf: true, symbol: 'installGCInjectSwizzle') | |
| external void installGCInjectSwizzle(); | |
| @Native<Void Function()>(isLeaf: true, symbol: 'removeGCInjectSwizzle') | |
| external void removeGCInjectSwizzle(); | |
| @Native<Void Function(Bool)>(isLeaf: true, symbol: 'setGCInjectActive') | |
| external void setGCInjectActive(bool active); | |
| @Native<Bool Function()>(isLeaf: true, symbol: 'wasBlockFreedBeforeRetain') | |
| external bool wasBlockFreedBeforeRetain(); | |
| @Native<Bool Function()>(isLeaf: true, symbol: 'gcNowAvailableFromNative') | |
| external bool gcNowAvailableFromNative(); | |
| // Must NOT be isLeaf: the native side calls Dart_ExecuteInternalCommand which | |
| // requires the Dart thread to be at a proper native-mode safepoint. | |
| @Native<Void Function()>(symbol: 'callGCNowFromNative') | |
| external void callGCNowFromNative(); |