blob: 2b3f1396aac473c418823e35a22ba6e9ee520d20 [file] [log] [blame] [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.
// 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 --profile_vm=true
// VMOptions=--profiler --profile_vm=false
// 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),
),
);
}