// 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); typedef Int64PointerParamOp = Void Function(Pointer); typedef NaTyPointerParamOpDart = void Function(Pointer); typedef NaTyPointerParamOp = Void Function(Pointer); typedef Int64PointerReturnOp = Pointer Function(); typedef NaTyPointerReturnOp = Pointer 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>( paramOpName, ); final f = fp.asFunction(); final arg = calloc(); f(arg); calloc.free(arg); } void paramInvariant2() { final fp = ffiTestFunctions.lookup>( paramOpName, ); final f = fp.asFunction(); final arg = calloc().cast(); Expect.type>(arg); f(arg); calloc.free(arg); } // Pass a statically and dynamically subtyped argument. void paramSubtype1() { final fp = ffiTestFunctions.lookup>( paramOpName, ); final f = fp.asFunction(); final arg = calloc(); Expect.type>(arg); f(arg); calloc.free(arg); } // Pass a statically subtyped but dynamically invariant argument. void paramSubtype2() { final fp = ffiTestFunctions.lookup>( paramOpName, ); final f = fp.asFunction(); final Pointer arg = calloc(); Expect.type>(arg); f(arg); calloc.free(arg); } void returnInvariant1() { final fp = ffiTestFunctions.lookup>( returnOpName, ); final f = fp.asFunction(); final result = f(); Expect.type>(result); } void returnInvariant2() { final fp = ffiTestFunctions.lookup>( returnOpName, ); final f = fp.asFunction(); final result = f(); Expect.type>(result); } void returnSubtype() { final fp = ffiTestFunctions.lookup>( returnOpName, ); final f = fp.asFunction(); final NaTyPointerReturnOp f2 = f; Expect.type(f2); final result = f2(); Expect.type>(result); } void functionArgumentVariance() { final p = Pointer< NativeFunction< Pointer Function(Pointer)>> Function( Pointer< NativeFunction Function(Pointer)> >, ) > >.fromAddress(0x1234); p .asFunction< Pointer Function(Pointer)>> Function( Pointer Function(Pointer)>>, ) >(); } void asFunctionTests() { for (int i = 0; i < 100; ++i) { paramInvariant1(); // Parameter invariant: Pointer. paramInvariant2(); // Parameter invariant: Pointer. paramSubtype1(); // Parameter statically and dynamically subtyped. paramSubtype2(); // Parameter statically invariant, dynamically subtyped. returnInvariant1(); // Return value invariant: Pointer. returnInvariant2(); // Return value invariant: Pointer. 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>); typedef CallbackInt64PointerParamOp = Void Function(Pointer>); typedef CallbackNaTyPointerParamOpDart = void Function(Pointer>); typedef CallbackNaTyPointerParamOp = Void Function(Pointer>); typedef CallbackInt64PointerReturnOpDart = void Function(Pointer>); typedef CallbackInt64PointerReturnOp = Void Function(Pointer>); typedef CallbackNaTyPointerReturnOpDart = void Function(Pointer>); typedef CallbackNaTyPointerReturnOp = Void Function(Pointer>); final callbackParamOpName = "CallbackNativeTypePointerParam"; final callbackReturnOpName = "CallbackNativeTypePointerReturn"; void int64PointerParamOp(Pointer p) { p.value = 42; } void naTyPointerParamOp(Pointer p) { final Pointer asInt8 = p.cast(); asInt8.value = 42; } // Pointer to return to C when C calls back into Dart and asks for a Pointer. Pointer data = nullptr; Pointer int64PointerReturnOp() { return data; } Pointer naTyPointerReturnOp() { return data; } void callbackParamInvariant1() { final callback = ffiTestFunctions .lookupFunction< CallbackInt64PointerParamOp, CallbackInt64PointerParamOpDart >(callbackParamOpName); final fp = Pointer.fromFunction(int64PointerParamOp); callback(fp); } void callbackParamInvariant2() { final callback = ffiTestFunctions .lookupFunction< CallbackNaTyPointerParamOp, CallbackNaTyPointerParamOpDart >(callbackParamOpName); final fp = Pointer.fromFunction(naTyPointerParamOp); callback(fp); } void callbackParamImplicitDowncast1() { final callback = ffiTestFunctions .lookupFunction< CallbackNaTyPointerParamOp, CallbackNaTyPointerParamOpDart >(callbackParamOpName); final fp = Pointer.fromFunction(int64PointerParamOp); // Pointer type arguments are not reified, any cast will succeed. callback(fp as Pointer>); } void callbackParamSubtype1() { final callback = ffiTestFunctions .lookupFunction< CallbackInt64PointerParamOp, CallbackInt64PointerParamOpDart >(callbackParamOpName); final fp = Pointer.fromFunction(naTyPointerParamOp); callback(fp); } void callbackReturnInvariant1() { final callback = ffiTestFunctions .lookupFunction< CallbackInt64PointerReturnOp, CallbackInt64PointerReturnOpDart >(callbackReturnOpName); final fp = Pointer.fromFunction(int64PointerReturnOp); callback(fp); } void callbackReturnInvariant2() { final callback = ffiTestFunctions .lookupFunction< CallbackNaTyPointerReturnOp, CallbackNaTyPointerReturnOpDart >(callbackReturnOpName); final fp = Pointer.fromFunction(naTyPointerReturnOp); callback(fp); } void fromFunctionTests() { data = calloc(); for (int i = 0; i < 100; ++i) { callbackParamInvariant1(); // Pointer invariant callbackParamInvariant2(); // Pointer invariant callbackParamImplicitDowncast1(); // static and dynamically supertyped callbackParamSubtype1(); // static and dynamically subtyped callbackReturnInvariant1(); // Pointer invariant callbackReturnInvariant2(); // Pointer invariant } calloc.free(data); } void main() { asFunctionTests(); fromFunctionTests(); }