// 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, Pointer)>>( 'Dart_ExecuteInternalCommand', ) .asFunction, Pointer)>(); // 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, Pointer)>( symbol: 'attachReferenceTracker', ) external void _attachReferenceTracker( Pointer host, Pointer isAlive, ); class ReferenceTracker { Pointer _host = nullptr; final Pointer _isAlivePtr; ReferenceTracker(Arena arena) : this._(arena, arena()..value = true); ReferenceTracker._(Arena arena, this._isAlivePtr); bool get isAlive => _isAlivePtr.value; Pointer 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(isLeaf: true, symbol: 'initGCInject') external void initGCInject(); @Native(isLeaf: true, symbol: 'installGCInjectSwizzle') external void installGCInjectSwizzle(); @Native(isLeaf: true, symbol: 'removeGCInjectSwizzle') external void removeGCInjectSwizzle(); @Native(isLeaf: true, symbol: 'setGCInjectActive') external void setGCInjectActive(bool active); @Native(isLeaf: true, symbol: 'wasBlockFreedBeforeRetain') external bool wasBlockFreedBeforeRetain(); @Native(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(symbol: 'callGCNowFromNative') external void callGCNowFromNative();