blob: 5c7a67e6c7cec4ffab63853b8e309dba5f340e18 [file] [log] [blame]
// 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();
}