// 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. // // Dart test program for testing dart:ffi function pointers. // // VMOptions= // VMOptions=--deterministic --optimization-counter-threshold=10 // VMOptions=--use-slow-path // VMOptions=--use-slow-path --stacktrace-every=100 // VMOptions=--write-protect-code --no-dual-map-code // VMOptions=--write-protect-code --no-dual-map-code --use-slow-path // VMOptions=--write-protect-code --no-dual-map-code --stacktrace-every=100 // SharedObjects=ffi_test_functions import 'dart:ffi'; import "package:ffi/ffi.dart"; import "package:expect/expect.dart"; import 'dylib_utils.dart'; void main() { for (int i = 0; i < 100; ++i) { testNativeFunctionFromCast(); testNativeFunctionFromLookup(); test64bitInterpretations(); testExtension(); testTruncation(); testNativeFunctionDoubles(); testNativeFunctionFloats(); testNativeFunctionManyArguments1(); testNativeFunctionManyArguments2(); testNativeFunctionManyArguments3(); testNativeFunctionManyArguments4(); testNativeFunctionManyArguments5(); testNativeFunctionPointer(); testNullPointers(); testFloatRounding(); testVoidReturn(); testNoArgs(); testNativeFunctionNullableInt(); } } final ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions"); typedef NativeBinaryOp = Int32 Function(Int32, Int32); typedef UnaryOp = int Function(int); typedef BinaryOp = int Function(int, int); typedef GenericBinaryOp = int Function(int, T); void testNativeFunctionFromCast() { Pointer p1 = calloc(); Pointer> p2 = p1.cast(); p2.asFunction(); p2.asFunction>(); calloc.free(p1); } typedef NativeQuadOpSigned = Int64 Function(Int8, Int16, Int32, Int64); typedef QuadOp = int Function(int, int, int, int); typedef NativeQuadOpUnsigned = Uint64 Function(Uint8, Uint16, Uint32, Uint64); BinaryOp sumPlus42 = ffiTestFunctions.lookupFunction("SumPlus42"); QuadOp intComputation = ffiTestFunctions .lookupFunction("IntComputation"); void testNativeFunctionFromLookup() { Expect.equals(49, sumPlus42(3, 4)); Expect.equals(49, (sumPlus42 as dynamic)(3, 4)); Expect.throwsNoSuchMethodError(() => (sumPlus42 as dynamic)()); Expect.throwsTypeError(() => (sumPlus42 as dynamic)(3, 4.0)); Expect.equals(625, intComputation(125, 250, 500, 1000)); Expect.equals(625, (intComputation as dynamic)(125, 250, 500, 1000)); Expect.equals( 0x7FFFFFFFFFFFFFFF, intComputation(0, 0, 0, 0x7FFFFFFFFFFFFFFF)); Expect.equals( -0x8000000000000000, intComputation(0, 0, 0, -0x8000000000000000)); Expect.equals(0x7FFFFFFFFFFFFFFF, (intComputation as dynamic)(0, 0, 0, 0x7FFFFFFFFFFFFFFF)); Expect.equals(-0x8000000000000000, (intComputation as dynamic)(0, 0, 0, -0x8000000000000000)); } typedef NativeReturnMaxUint8 = Uint8 Function(); int Function() returnMaxUint8 = ffiTestFunctions .lookup("ReturnMaxUint8") .cast>() .asFunction(); int Function() returnMaxUint8v2 = ffiTestFunctions .lookup("ReturnMaxUint8v2") .cast>() .asFunction(); typedef NativeReturnMaxUint16 = Uint16 Function(); int Function() returnMaxUint16 = ffiTestFunctions .lookup("ReturnMaxUint16") .cast>() .asFunction(); int Function() returnMaxUint16v2 = ffiTestFunctions .lookup("ReturnMaxUint16v2") .cast>() .asFunction(); typedef NativeReturnMaxUint32 = Uint32 Function(); int Function() returnMaxUint32 = ffiTestFunctions .lookup("ReturnMaxUint32") .cast>() .asFunction(); int Function() returnMaxUint32v2 = ffiTestFunctions .lookup("ReturnMaxUint32v2") .cast>() .asFunction(); typedef NativeReturnMinInt8 = Int8 Function(); int Function() returnMinInt8 = ffiTestFunctions .lookup("ReturnMinInt8") .cast>() .asFunction(); int Function() returnMinInt8v2 = ffiTestFunctions .lookup("ReturnMinInt8v2") .cast>() .asFunction(); typedef NativeReturnMinInt16 = Int16 Function(); int Function() returnMinInt16 = ffiTestFunctions .lookup("ReturnMinInt16") .cast>() .asFunction(); int Function() returnMinInt16v2 = ffiTestFunctions .lookup("ReturnMinInt16v2") .cast>() .asFunction(); typedef NativeReturnMinInt32 = Int32 Function(); int Function() returnMinInt32 = ffiTestFunctions .lookup("ReturnMinInt32") .cast>() .asFunction(); int Function() returnMinInt32v2 = ffiTestFunctions .lookup("ReturnMinInt32v2") .cast>() .asFunction(); typedef NativeTakeMaxUint8 = IntPtr Function(Uint8); int Function(int) takeMaxUint8 = ffiTestFunctions .lookup("TakeMaxUint8") .cast>() .asFunction(); typedef NativeTakeMaxUint16 = IntPtr Function(Uint16); int Function(int) takeMaxUint16 = ffiTestFunctions .lookup("TakeMaxUint16") .cast>() .asFunction(); typedef NativeTakeMaxUint32 = IntPtr Function(Uint32); int Function(int) takeMaxUint32 = ffiTestFunctions .lookup("TakeMaxUint32") .cast>() .asFunction(); typedef NativeTakeMinInt8 = IntPtr Function(Int8); int Function(int) takeMinInt8 = ffiTestFunctions .lookup("TakeMinInt8") .cast>() .asFunction(); typedef NativeTakeMinInt16 = IntPtr Function(Int16); int Function(int) takeMinInt16 = ffiTestFunctions .lookup("TakeMinInt16") .cast>() .asFunction(); typedef NativeTakeMinInt32 = IntPtr Function(Int32); int Function(int) takeMinInt32 = ffiTestFunctions .lookup("TakeMinInt32") .cast>() .asFunction(); typedef NativeTakeMaxUint8x10 = IntPtr Function( Uint8, Uint8, Uint8, Uint8, Uint8, Uint8, Uint8, Uint8, Uint8, Uint8); int Function(int, int, int, int, int, int, int, int, int, int) takeMaxUint8x10 = ffiTestFunctions .lookup("TakeMaxUint8x10") .cast>() .asFunction(); void testExtension() { // Sign extension on the way back to Dart. Expect.equals(0xff, returnMaxUint8()); Expect.equals(0xffff, returnMaxUint16()); Expect.equals(0xffffffff, returnMaxUint32()); Expect.equals(-0x80, returnMinInt8()); Expect.equals(-0x8000, returnMinInt16()); Expect.equals(-0x80000000, returnMinInt32()); // Truncation in C, and sign extension back to Dart. Expect.equals(0xff, returnMaxUint8v2()); Expect.equals(0xffff, returnMaxUint16v2()); Expect.equals(0xffffffff, returnMaxUint32v2()); Expect.equals(-0x80, returnMinInt8v2()); Expect.equals(-0x8000, returnMinInt16v2()); Expect.equals(-0x80000000, returnMinInt32v2()); // Upper bits propper, should work without truncation. Expect.equals(1, takeMaxUint8(0xff)); Expect.equals(1, takeMaxUint16(0xffff)); Expect.equals(1, takeMaxUint32(0xffffffff)); Expect.equals(1, takeMinInt8(-0x80)); Expect.equals(1, takeMinInt16(-0x8000)); Expect.equals(1, takeMinInt32(-0x80000000)); Expect.equals( 1, takeMaxUint8x10( 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff)); // Upper bits garbage, needs to truncate. Expect.equals(1, takeMaxUint8(0xabcff)); Expect.equals(1, takeMaxUint16(0xabcffff)); Expect.equals(1, takeMaxUint32(0xabcffffffff)); Expect.equals(1, takeMinInt8(0x8abc80)); Expect.equals(1, takeMinInt16(0x8abc8000)); Expect.equals(1, takeMinInt32(0x8abc80000000)); Expect.equals( 1, takeMaxUint8x10(0xabcff, 0xabcff, 0xabcff, 0xabcff, 0xabcff, 0xabcff, 0xabcff, 0xabcff, 0xabcff, 0xabcff)); } QuadOp uintComputation = ffiTestFunctions .lookupFunction("UintComputation"); void test64bitInterpretations() { // 2 ^ 63 - 1 Expect.equals( 0x7FFFFFFFFFFFFFFF, uintComputation(0, 0, 0, 0x7FFFFFFFFFFFFFFF)); // -2 ^ 63 interpreted as 2 ^ 63 Expect.equals( -0x8000000000000000, uintComputation(0, 0, 0, -0x8000000000000000)); // -1 interpreted as 2 ^ 64 - 1 Expect.equals(-1, uintComputation(0, 0, 0, -1)); } typedef NativeSenaryOp = Int64 Function( Int8, Int16, Int32, Uint8, Uint16, Uint32); typedef SenaryOp = int Function(int, int, int, int, int, int); SenaryOp sumSmallNumbers = ffiTestFunctions .lookupFunction("SumSmallNumbers"); void testTruncation() { sumSmallNumbers(128, 0, 0, 0, 0, 0); sumSmallNumbers(-129, 0, 0, 0, 0, 0); sumSmallNumbers(0, 0, 0, 256, 0, 0); sumSmallNumbers(0, 0, 0, -1, 0, 0); sumSmallNumbers(0, 0x8000, 0, 0, 0, 0); sumSmallNumbers(0, 0xFFFFFFFFFFFF7FFF, 0, 0, 0, 0); sumSmallNumbers(0, 0, 0, 0, 0x10000, 0); sumSmallNumbers(0, 0, 0, 0, -1, 0); Expect.equals(0xFFFFFFFF80000000, sumSmallNumbers(0, 0, 0x80000000, 0, 0, 0)); Expect.equals( 0x000000007FFFFFFF, sumSmallNumbers(0, 0, 0xFFFFFFFF7FFFFFFF, 0, 0, 0)); Expect.equals(0, sumSmallNumbers(0, 0, 0, 0, 0, 0x100000000)); Expect.equals(0xFFFFFFFF, sumSmallNumbers(0, 0, 0, 0, 0, -1)); } typedef NativeDoubleUnaryOp = Double Function(Double); typedef DoubleUnaryOp = double Function(double); DoubleUnaryOp times1_337Double = ffiTestFunctions .lookupFunction("Times1_337Double"); void testNativeFunctionDoubles() { Expect.approxEquals(2.0 * 1.337, times1_337Double(2.0)); } typedef NativeFloatUnaryOp = Float Function(Float); DoubleUnaryOp times1_337Float = ffiTestFunctions .lookupFunction("Times1_337Float"); void testNativeFunctionFloats() { Expect.approxEquals(1337.0, times1_337Float(1000.0)); } typedef NativeDecenaryOp = IntPtr Function(IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr); typedef NativeDecenaryOp2 = Int16 Function( Int8, Int16, Int8, Int16, Int8, Int16, Int8, Int16, Int8, Int16); typedef DecenaryOp = int Function( int, int, int, int, int, int, int, int, int, int); DecenaryOp sumManyInts = ffiTestFunctions .lookupFunction("SumManyInts"); void testNativeFunctionManyArguments1() { Expect.equals(55, sumManyInts(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); } DecenaryOp sumManySmallInts = ffiTestFunctions .lookupFunction("SumManySmallInts"); void testNativeFunctionManyArguments5() { Expect.equals(55, sumManySmallInts(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); } typedef NativeUndenaryOp = IntPtr Function(IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr); typedef UndenaryOp = int Function( int, int, int, int, int, int, int, int, int, int, int); UndenaryOp sumManyIntsOdd = ffiTestFunctions .lookupFunction("SumManyIntsOdd"); void testNativeFunctionManyArguments4() { Expect.equals(66, sumManyIntsOdd(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)); } typedef NativeDoubleDecenaryOp = Double Function(Double, Double, Double, Double, Double, Double, Double, Double, Double, Double); typedef DoubleDecenaryOp = double Function(double, double, double, double, double, double, double, double, double, double); DoubleDecenaryOp sumManyDoubles = ffiTestFunctions .lookupFunction("SumManyDoubles"); void testNativeFunctionManyArguments2() { Expect.approxEquals( 55.0, sumManyDoubles(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0)); } typedef NativeVigesimalOp = Double Function( IntPtr, Float, IntPtr, Double, IntPtr, Float, IntPtr, Double, IntPtr, Float, IntPtr, Double, IntPtr, Float, IntPtr, Double, IntPtr, Float, IntPtr, Double); typedef VigesimalOp = double Function( int, double, int, double, int, double, int, double, int, double, int, double, int, double, int, double, int, double, int, double); VigesimalOp sumManyNumbers = ffiTestFunctions .lookupFunction("SumManyNumbers"); void testNativeFunctionManyArguments3() { Expect.approxEquals( 210.0, sumManyNumbers(1, 2.0, 3, 4.0, 5, 6.0, 7, 8.0, 9, 10.0, 11, 12.0, 13, 14.0, 15, 16.0, 17, 18.0, 19, 20.0)); } typedef Int64PointerUnOp = Pointer Function(Pointer); Int64PointerUnOp assign1337Index1 = ffiTestFunctions .lookupFunction("Assign1337Index1"); void testNativeFunctionPointer() { Pointer p2 = calloc(2); p2.value = 42; p2[1] = 1000; Pointer result = assign1337Index1(p2); Expect.equals(1337, result.value); Expect.equals(1337, p2[1]); Expect.equals(p2.elementAt(1).address, result.address); calloc.free(p2); } Int64PointerUnOp nullableInt64ElemAt1 = ffiTestFunctions .lookupFunction("NullableInt64ElemAt1"); void testNullPointers() { Pointer result = nullableInt64ElemAt1(nullptr); Expect.equals(result, nullptr); Pointer p2 = calloc(2); result = nullableInt64ElemAt1(p2); Expect.notEquals(result, nullptr); calloc.free(p2); } typedef NativeFloatPointerToBool = Uint8 Function(Pointer); typedef FloatPointerToBool = int Function(Pointer); FloatPointerToBool isRoughly1337 = ffiTestFunctions.lookupFunction< NativeFloatPointerToBool, FloatPointerToBool>("IsRoughly1337"); void testFloatRounding() { Pointer p2 = calloc(); p2.value = 1337.0; int result = isRoughly1337(p2); Expect.equals(1, result); calloc.free(p2); } typedef NativeFloatToVoid = Void Function(Float); typedef DoubleToVoid = void Function(double); DoubleToVoid devNullFloat = ffiTestFunctions .lookupFunction("DevNullFloat"); void testVoidReturn() { devNullFloat(1337.0); dynamic loseSignature = devNullFloat; dynamic result = loseSignature(1337.0); Expect.isNull(result); } typedef NativeVoidToFloat = Float Function(); typedef VoidToDouble = double Function(); VoidToDouble inventFloatValue = ffiTestFunctions .lookupFunction("InventFloatValue"); void testNoArgs() { double result = inventFloatValue(); Expect.approxEquals(1337.0, result); } void testNativeFunctionNullableInt() { final sumPlus42 = ffiTestFunctions.lookupFunction< Int32 Function(Int32, Int32), int Function(int, int?)>("SumPlus42"); try { sumPlus42(3, null); } catch (e) { // TODO(http://dartbug.com/47098): Save param names to dwarf. Expect.isTrue(e.toString().contains('ffi_param2') || e.toString().contains('')); } }