| // 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(); | |
| } |