blob: 0cad5215345ef5394d33c1439bd40e6e47f91cdd [file] [edit]
// 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();