blob: 3b1732c1e5309ea786c8bf49d735147387c10a7a [file] [log] [blame] [edit]
// Copyright (c) 2019, 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.
//
// VMOptions=
// VMOptions=--deterministic --optimization-counter-threshold=90
// VMOptions=--use-slow-path
// SharedObjects=ffi_test_functions
//
// This file tests subtyping relationships (at compile time and at runtime) of
// parameters and return types of ffi trampolines and ffi callback trampolines.
import 'dart:ffi';
import "package:expect/expect.dart";
import "package:ffi/ffi.dart";
import 'dylib_utils.dart';
typedef Int64PointerParamOpDart = void Function(Pointer<Int64>);
typedef Int64PointerParamOp = Void Function(Pointer<Int64>);
typedef NaTyPointerParamOpDart = void Function(Pointer<NativeType>);
typedef NaTyPointerParamOp = Void Function(Pointer<NativeType>);
typedef Int64PointerReturnOp = Pointer<Int64> Function();
typedef NaTyPointerReturnOp = Pointer<NativeType> Function();
final paramOpName = "NativeTypePointerParam";
final returnOpName = "NativeTypePointerReturn";
final DynamicLibrary ffiTestFunctions = dlopenPlatformSpecific(
"ffi_test_functions",
);
// =============================================
// Tests calls from Dart to native (asFunction).
// =============================================
void paramInvariant1() {
final fp = ffiTestFunctions.lookup<NativeFunction<Int64PointerParamOp>>(
paramOpName,
);
final f = fp.asFunction<Int64PointerParamOpDart>();
final arg = calloc<Int64>();
f(arg);
calloc.free(arg);
}
void paramInvariant2() {
final fp = ffiTestFunctions.lookup<NativeFunction<NaTyPointerParamOp>>(
paramOpName,
);
final f = fp.asFunction<NaTyPointerParamOpDart>();
final arg = calloc<Int64>().cast<NativeType>();
Expect.type<Pointer<NativeType>>(arg);
f(arg);
calloc.free(arg);
}
// Pass a statically and dynamically subtyped argument.
void paramSubtype1() {
final fp = ffiTestFunctions.lookup<NativeFunction<NaTyPointerParamOp>>(
paramOpName,
);
final f = fp.asFunction<NaTyPointerParamOpDart>();
final arg = calloc<Int64>();
Expect.type<Pointer<Int64>>(arg);
f(arg);
calloc.free(arg);
}
// Pass a statically subtyped but dynamically invariant argument.
void paramSubtype2() {
final fp = ffiTestFunctions.lookup<NativeFunction<NaTyPointerParamOp>>(
paramOpName,
);
final f = fp.asFunction<NaTyPointerParamOpDart>();
final Pointer<NativeType> arg = calloc<Int64>();
Expect.type<Pointer<Int64>>(arg);
f(arg);
calloc.free(arg);
}
void returnInvariant1() {
final fp = ffiTestFunctions.lookup<NativeFunction<Int64PointerReturnOp>>(
returnOpName,
);
final f = fp.asFunction<Int64PointerReturnOp>();
final result = f();
Expect.type<Pointer<Int64>>(result);
}
void returnInvariant2() {
final fp = ffiTestFunctions.lookup<NativeFunction<NaTyPointerReturnOp>>(
returnOpName,
);
final f = fp.asFunction<NaTyPointerReturnOp>();
final result = f();
Expect.type<Pointer<NativeType>>(result);
}
void returnSubtype() {
final fp = ffiTestFunctions.lookup<NativeFunction<Int64PointerReturnOp>>(
returnOpName,
);
final f = fp.asFunction<Int64PointerReturnOp>();
final NaTyPointerReturnOp f2 = f;
Expect.type<Int64PointerReturnOp>(f2);
final result = f2();
Expect.type<Pointer<NativeType>>(result);
}
void functionArgumentVariance() {
final p = Pointer<
NativeFunction<
Pointer<NativeFunction<Pointer<Int8> Function(Pointer<NativeType>)>>
Function(
Pointer<NativeFunction<Pointer<NativeType> Function(Pointer<Int8>)>>,
)
>
>.fromAddress(0x1234);
p
.asFunction<
Pointer<NativeFunction<Pointer<NativeType> Function(Pointer<Int8>)>>
Function(
Pointer<NativeFunction<Pointer<Int8> Function(Pointer<NativeType>)>>,
)
>();
}
void asFunctionTests() {
for (int i = 0; i < 100; ++i) {
paramInvariant1(); // Parameter invariant: Pointer<Int64>.
paramInvariant2(); // Parameter invariant: Pointer<NativeType>.
paramSubtype1(); // Parameter statically and dynamically subtyped.
paramSubtype2(); // Parameter statically invariant, dynamically subtyped.
returnInvariant1(); // Return value invariant: Pointer<Int64>.
returnInvariant2(); // Return value invariant: Pointer<NativeType>.
returnSubtype(); // Return value static subtyped, dynamically invariant.
functionArgumentVariance(); // Check nested function signatures.
}
}
// =======================================================
// Test with callbacks from native to Dart (fromFunction).
// =======================================================
typedef CallbackInt64PointerParamOpDart =
void Function(Pointer<NativeFunction<Int64PointerParamOp>>);
typedef CallbackInt64PointerParamOp =
Void Function(Pointer<NativeFunction<Int64PointerParamOp>>);
typedef CallbackNaTyPointerParamOpDart =
void Function(Pointer<NativeFunction<NaTyPointerParamOp>>);
typedef CallbackNaTyPointerParamOp =
Void Function(Pointer<NativeFunction<NaTyPointerParamOp>>);
typedef CallbackInt64PointerReturnOpDart =
void Function(Pointer<NativeFunction<Int64PointerReturnOp>>);
typedef CallbackInt64PointerReturnOp =
Void Function(Pointer<NativeFunction<Int64PointerReturnOp>>);
typedef CallbackNaTyPointerReturnOpDart =
void Function(Pointer<NativeFunction<NaTyPointerReturnOp>>);
typedef CallbackNaTyPointerReturnOp =
Void Function(Pointer<NativeFunction<NaTyPointerReturnOp>>);
final callbackParamOpName = "CallbackNativeTypePointerParam";
final callbackReturnOpName = "CallbackNativeTypePointerReturn";
void int64PointerParamOp(Pointer<Int64> p) {
p.value = 42;
}
void naTyPointerParamOp(Pointer<NativeType> p) {
final Pointer<Int8> asInt8 = p.cast();
asInt8.value = 42;
}
// Pointer to return to C when C calls back into Dart and asks for a Pointer.
Pointer<Int64> data = nullptr;
Pointer<Int64> int64PointerReturnOp() {
return data;
}
Pointer<NativeType> naTyPointerReturnOp() {
return data;
}
void callbackParamInvariant1() {
final callback = ffiTestFunctions.lookupFunction<
CallbackInt64PointerParamOp,
CallbackInt64PointerParamOpDart
>(callbackParamOpName);
final fp = Pointer.fromFunction<Int64PointerParamOp>(int64PointerParamOp);
callback(fp);
}
void callbackParamInvariant2() {
final callback = ffiTestFunctions.lookupFunction<
CallbackNaTyPointerParamOp,
CallbackNaTyPointerParamOpDart
>(callbackParamOpName);
final fp = Pointer.fromFunction<NaTyPointerParamOp>(naTyPointerParamOp);
callback(fp);
}
void callbackParamImplicitDowncast1() {
final callback = ffiTestFunctions.lookupFunction<
CallbackNaTyPointerParamOp,
CallbackNaTyPointerParamOpDart
>(callbackParamOpName);
final fp = Pointer.fromFunction<Int64PointerParamOp>(int64PointerParamOp);
// Pointer type arguments are not reified, any cast will succeed.
callback(fp as Pointer<NativeFunction<NaTyPointerParamOp>>);
}
void callbackParamSubtype1() {
final callback = ffiTestFunctions.lookupFunction<
CallbackInt64PointerParamOp,
CallbackInt64PointerParamOpDart
>(callbackParamOpName);
final fp = Pointer.fromFunction<Int64PointerParamOp>(naTyPointerParamOp);
callback(fp);
}
void callbackReturnInvariant1() {
final callback = ffiTestFunctions.lookupFunction<
CallbackInt64PointerReturnOp,
CallbackInt64PointerReturnOpDart
>(callbackReturnOpName);
final fp = Pointer.fromFunction<Int64PointerReturnOp>(int64PointerReturnOp);
callback(fp);
}
void callbackReturnInvariant2() {
final callback = ffiTestFunctions.lookupFunction<
CallbackNaTyPointerReturnOp,
CallbackNaTyPointerReturnOpDart
>(callbackReturnOpName);
final fp = Pointer.fromFunction<NaTyPointerReturnOp>(naTyPointerReturnOp);
callback(fp);
}
void fromFunctionTests() {
data = calloc();
for (int i = 0; i < 100; ++i) {
callbackParamInvariant1(); // Pointer<Int64> invariant
callbackParamInvariant2(); // Pointer<NativeType> invariant
callbackParamImplicitDowncast1(); // static and dynamically supertyped
callbackParamSubtype1(); // static and dynamically subtyped
callbackReturnInvariant1(); // Pointer<Int64> invariant
callbackReturnInvariant2(); // Pointer<NativeType> invariant
}
calloc.free(data);
}
void main() {
asFunctionTests();
fromFunctionTests();
}