blob: b8120878ead89db2bd1acf2c2b2f7a50f16c8dfa [file] [log] [blame]
// 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.
// Dart test program for testing dart:ffi callbacks that take advantage of
// subtyping rules.
//
// 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
// SharedObjects=ffi_test_functions
import 'dart:async';
import 'dart:ffi';
import "package:expect/expect.dart";
import 'dylib_utils.dart';
final ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions");
typedef TwoIntVoidFnNativeType = Void Function(Pointer, Int32, Int32);
typedef TwoIntVoidFnType = void Function(Pointer, int, int);
final callTwoIntVoidFunction =
ffiTestFunctions.lookupFunction<TwoIntVoidFnNativeType, TwoIntVoidFnType>(
"CallTwoIntVoidFunction");
typedef TwoIntPointerFnNativeType = Pointer<NativeType> Function(
Pointer, Int32, Int32);
typedef TwoIntPointerFnType = Pointer<NativeType> Function(Pointer, int, int);
final callTwoIntPointerFunction = ffiTestFunctions.lookupFunction<
TwoIntPointerFnNativeType,
TwoIntPointerFnType>("CallTwoIntPointerFunction");
typedef TwoPointerIntFnNativeType = Int32 Function(
Pointer, Pointer<NativeType>, Pointer<NativeType>);
typedef TwoPointerIntFnType = int Function(
Pointer, Pointer<NativeType>, Pointer<NativeType>);
final callTwoPointerIntFunction = ffiTestFunctions.lookupFunction<
TwoPointerIntFnNativeType,
TwoPointerIntFnType>("CallTwoPointerIntFunction");
typedef VoidReturnFunction = Void Function(Int32, Int32);
int addVoidResult = 0;
int addVoid(int x, int y) {
print("addVoid($x, $y)");
addVoidResult = x + y;
return addVoidResult;
}
final addVoidAsyncResult = Completer<int>();
int addVoidAsync(int x, int y) {
print("addVoidAsync($x, $y)");
final result = x + y;
addVoidAsyncResult.complete(result);
return result;
}
typedef NaTyPtrReturnFunction = Pointer<NativeType> Function(Int32, Int32);
Pointer<Int64> addInt64PtrReturn(int x, int y) {
print("addInt64PtrReturn($x, $y)");
return Pointer<Int64>.fromAddress(x + y);
}
typedef Int64PtrParamFunction = Int32 Function(Pointer<Int64>, Pointer<Int64>);
int addNaTyPtrParam(Pointer<NativeType> x, Pointer<NativeType> y) {
print("addNaTyPtrParam($x, $y)");
return x.address + y.address;
}
Future<void> main() async {
await testReturnVoid();
testReturnSubtype();
testParamSubtype();
print("Done! :)");
}
Future<void> testReturnVoid() async {
// If the native function type returns void, the Dart function can return
// anything.
final legacyCallback = Pointer.fromFunction<VoidReturnFunction>(addVoid);
callTwoIntVoidFunction(legacyCallback, 100, 23);
Expect.equals(123, addVoidResult);
final isolateLocal = NativeCallable<VoidReturnFunction>.isolateLocal(addVoid)
..keepIsolateAlive = false;
callTwoIntVoidFunction(isolateLocal.nativeFunction, 400, 56);
Expect.equals(456, addVoidResult);
final listener = NativeCallable<VoidReturnFunction>.listener(addVoidAsync)
..keepIsolateAlive = false;
callTwoIntVoidFunction(listener.nativeFunction, 700, 89);
Expect.equals(789, await addVoidAsyncResult.future);
}
void testReturnSubtype() {
// The Dart function is allowed to return a subtype of the native return type.
final legacyCallback =
Pointer.fromFunction<NaTyPtrReturnFunction>(addInt64PtrReturn);
Expect.equals(
123, callTwoIntPointerFunction(legacyCallback, 100, 23).address);
final isolateLocal =
NativeCallable<NaTyPtrReturnFunction>.isolateLocal(addInt64PtrReturn)
..keepIsolateAlive = false;
Expect.equals(456,
callTwoIntPointerFunction(isolateLocal.nativeFunction, 400, 56).address);
}
void testParamSubtype() {
// The Dart function is allowed to accept params that are a supertype of the
// native type's params.
final legacyCallback =
Pointer.fromFunction<Int64PtrParamFunction>(addNaTyPtrParam, 0);
Expect.equals(
123,
callTwoPointerIntFunction(legacyCallback, Pointer<Int64>.fromAddress(100),
Pointer<Int64>.fromAddress(23)));
final isolateLocal = NativeCallable<Int64PtrParamFunction>.isolateLocal(
addNaTyPtrParam,
exceptionalReturn: 0)
..keepIsolateAlive = false;
Expect.equals(
456,
callTwoPointerIntFunction(isolateLocal.nativeFunction,
Pointer<Int64>.fromAddress(400), Pointer<Int64>.fromAddress(56)));
}