| // Copyright (c) 2022, 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. | |
| @DefaultAsset('package:objective_c/objective_c.dylib') | |
| library; | |
| import 'dart:ffi'; | |
| import 'dart:io'; | |
| import 'package:ffi/ffi.dart'; | |
| import 'package:ffigen/ffigen.dart'; | |
| import 'package:ffigen/src/config_provider/config.dart'; | |
| import 'package:ffigen/src/header_parser.dart' show parse; | |
| import 'package:leak_tracker/leak_tracker.dart' as leak_tracker; | |
| import 'package:logging/logging.dart'; | |
| import 'package:objective_c/objective_c.dart'; | |
| import 'package:objective_c/src/internal.dart' | |
| as internal_for_testing | |
| show isValidBlock, isValidClass; | |
| import 'package:path/path.dart' as p; | |
| import '../test_utils.dart'; | |
| void verifyBindings( | |
| FfiGenerator config, { | |
| Logger? logger, | |
| bool Function(String expected, String actual)? dartVerify, | |
| bool Function(String expected, String actual)? objCVerify, | |
| }) { | |
| final context = testContext(config); | |
| final library = parse(context); | |
| final bindingsName = context.config.output.dart.path.pathSegments.last; | |
| matchLibraryWithExpected(context, library, bindingsName, [ | |
| 'test', | |
| 'native_objc_test', | |
| bindingsName, | |
| ], verify: dartVerify); | |
| final mFileName = context.config.output.objCFile.pathSegments.last; | |
| matchObjCFileWithExpected(context, library, mFileName, [ | |
| 'test', | |
| 'native_objc_test', | |
| mFileName, | |
| ], verify: objCVerify); | |
| } | |
| // Dart_ExecuteInternalCommand("gc-now") doesn't work on flutter, so we use | |
| // leak_tracker's forceGC function instead. It's less reliable, and to combat | |
| // that we need to wait for quite a long time, which breaks autorelease pools. | |
| Future<void> flutterDoGC() async { | |
| await leak_tracker.forceGC(); | |
| await Future<void>.delayed(const Duration(milliseconds: 500)); | |
| } | |
| // Defined in package:objective_c's test-only util, reference_tracker.h. | |
| @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); | |
| } | |
| } | |
| bool isValidClass(Pointer<Void> clazz) => | |
| internal_for_testing.isValidClass(clazz.cast(), forceReloadClasses: true); | |
| String findDylib(String name) => | |
| p.join(packagePathForTests, '.dart_tool', 'lib', '$name.dylib'); |