blob: c3fe1241626556660bdd5df898ab9c200a200587 [file] [log] [blame]
// Copyright (c) 2023, 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.
// Dart test program for testing dart:ffi async callbacks.
//
// VMOptions=
// VMOptions=--stacktrace-every=100
// VMOptions=--use-slow-path
// VMOptions=--use-slow-path --stacktrace-every=100
// VMOptions=--dwarf_stack_traces --no-retain_function_objects --no-retain_code_objects
// VMOptions=--test_il_serialization
// VMOptions=--profiler --profile_vm=true
// VMOptions=--profiler --profile_vm=false
// SharedObjects=ffi_test_functions
import 'dart:async';
import 'dart:ffi';
import 'dart:isolate';
import "package:expect/expect.dart";
import 'dylib_utils.dart';
main(args, message) async {
testNativeCallableStatic();
testNativeCallableClosure();
testNativeCallableDoubleCloseError();
testNativeCallableNestedCloseCallStatic();
testNativeCallableNestedCloseCallClosure();
testNativeCallableExceptionalReturnStatic();
testNativeCallableExceptionalReturnClosure();
await testNativeCallableDontKeepAliveStatic();
await testNativeCallableDontKeepAliveClosure();
testNativeCallableKeepAliveGetter();
print("All tests completed :)");
}
final ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions");
typedef TwoIntFnNativeType = Int32 Function(Pointer, Int32, Int32);
typedef TwoIntFnType = int Function(Pointer, int, int);
final callTwoIntFunction = ffiTestFunctions
.lookupFunction<TwoIntFnNativeType, TwoIntFnType>("CallTwoIntFunction");
typedef CallbackNativeType = Int32 Function(Int32, Int32);
int add(int a, int b) {
return a + b;
}
testNativeCallableStatic() {
final callback = NativeCallable<CallbackNativeType>.isolateLocal(add,
exceptionalReturn: 0);
Expect.equals(1234, callTwoIntFunction(callback.nativeFunction, 1000, 234));
callback.close();
}
testNativeCallableClosure() {
int c = 70000;
final callback = NativeCallable<CallbackNativeType>.isolateLocal(
(int a, int b) => a + b + c,
exceptionalReturn: 0);
Expect.equals(71234, callTwoIntFunction(callback.nativeFunction, 1000, 234));
c = 80000;
Expect.equals(81234, callTwoIntFunction(callback.nativeFunction, 1000, 234));
callback.close();
}
testNativeCallableDoubleCloseError() {
final callback = NativeCallable<CallbackNativeType>.isolateLocal(add,
exceptionalReturn: 0);
Expect.notEquals(nullptr, callback.nativeFunction);
callback.close();
Expect.throwsStateError(() {
final _ = callback.nativeFunction;
});
// Expect that these do not throw.
callback.close();
callback.keepIsolateAlive = true;
Expect.isFalse(callback.keepIsolateAlive);
}
late NativeCallable selfClosingStaticCallback;
int selfClosingStatic(int a, int b) {
selfClosingStaticCallback.close();
return a + b;
}
testNativeCallableNestedCloseCallStatic() {
selfClosingStaticCallback = NativeCallable<CallbackNativeType>.isolateLocal(
selfClosingStatic,
exceptionalReturn: 0);
Expect.equals(1234,
callTwoIntFunction(selfClosingStaticCallback.nativeFunction, 1000, 234));
// The callback is already closed.
Expect.throwsStateError(() {
final _ = selfClosingStaticCallback.nativeFunction;
});
}
testNativeCallableNestedCloseCallClosure() {
late NativeCallable callback;
callback = NativeCallable<CallbackNativeType>.isolateLocal((int a, int b) {
callback.close();
return a + b;
}, exceptionalReturn: 0);
Expect.equals(1234, callTwoIntFunction(callback.nativeFunction, 1000, 234));
// The callback is already closed.
Expect.throwsStateError(() {
final _ = callback.nativeFunction;
});
}
int throwerCallback(int a, int b) {
if (a != 1000) {
throw "Oh no!";
}
return a + b;
}
testNativeCallableExceptionalReturnStatic() {
final callback = NativeCallable<CallbackNativeType>.isolateLocal(
throwerCallback,
exceptionalReturn: 5678);
Expect.equals(1234, callTwoIntFunction(callback.nativeFunction, 1000, 234));
Expect.equals(5678, callTwoIntFunction(callback.nativeFunction, 0, 0));
callback.close();
}
testNativeCallableExceptionalReturnClosure() {
final callback =
NativeCallable<CallbackNativeType>.isolateLocal((int a, int b) {
if (a != 1000) {
throw "Oh no!";
}
return a + b;
}, exceptionalReturn: 5678);
Expect.equals(1234, callTwoIntFunction(callback.nativeFunction, 1000, 234));
Expect.equals(5678, callTwoIntFunction(callback.nativeFunction, 0, 0));
callback.close();
}
Future<void> testNativeCallableDontKeepAliveStatic() async {
final exitPort = ReceivePort();
await Isolate.spawn((_) async {
final callback = NativeCallable<CallbackNativeType>.isolateLocal(add,
exceptionalReturn: 0);
Expect.equals(1234, callTwoIntFunction(callback.nativeFunction, 1000, 234));
callback.keepIsolateAlive = false;
}, null, onExit: exitPort.sendPort);
await exitPort.first;
exitPort.close();
}
Future<void> testNativeCallableDontKeepAliveClosure() async {
int c = 70000;
final exitPort = ReceivePort();
await Isolate.spawn((_) async {
final callback = NativeCallable<CallbackNativeType>.isolateLocal(
(int a, int b) => a + b + c,
exceptionalReturn: 0);
Expect.equals(
71234, callTwoIntFunction(callback.nativeFunction, 1000, 234));
callback.keepIsolateAlive = false;
}, null, onExit: exitPort.sendPort);
await exitPort.first;
exitPort.close();
}
testNativeCallableKeepAliveGetter() {
final callback = NativeCallable<CallbackNativeType>.isolateLocal(add,
exceptionalReturn: 0);
// Check that only the flag changes are counted by decrementing and
// incrementing a lot, and by different amounts.
for (int i = 0; i < 100; ++i) {
callback.keepIsolateAlive = false;
Expect.isFalse(callback.keepIsolateAlive);
}
for (int i = 0; i < 200; ++i) {
callback.keepIsolateAlive = true;
Expect.isTrue(callback.keepIsolateAlive);
}
callback.close();
}