[vm] Minor improvements to FFI function tests.
Most importantly, we move resolution of symbols in function_test to the toplevel
so the same function object is re-used on each invocation. This makes it possible to
tests repeated invocations with the optimization counter enabled.
Also added some comments and new tests.
Change-Id: I3772a1f15d1ad9924c8583112ddf3b1349fb6c09
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/96083
Reviewed-by: Daco Harkes <dacoharkes@google.com>
diff --git a/runtime/bin/ffi_test_functions.cc b/runtime/bin/ffi_test_functions.cc
index 28687ca..cfc8d7b 100644
--- a/runtime/bin/ffi_test_functions.cc
+++ b/runtime/bin/ffi_test_functions.cc
@@ -33,6 +33,11 @@
return retval;
}
+// Test 32-bit (int32_t) -> 64-bit (Dart int) sign extension and truncation.
+DART_EXPORT int32_t TestExtension() {
+ return 1UL << 31;
+}
+
// Performs some computation on various sized signed ints.
// Used for testing value ranges for signed ints.
DART_EXPORT int64_t IntComputation(int8_t a, int16_t b, int32_t c, int64_t d) {
@@ -410,6 +415,10 @@
return reinterpret_cast<void*>(-0x80000000L);
}
+DART_EXPORT void* LargePointer() {
+ return reinterpret_cast<void*>(-0x8000000000000000L);
+}
+
#if !defined(_WIN32)
DART_EXPORT int RedirectStderr() {
char filename[256];
diff --git a/tests/standalone_2/ffi/function_stress_test.dart b/tests/standalone_2/ffi/function_stress_test.dart
index 46e716f..fd3f0ec 100644
--- a/tests/standalone_2/ffi/function_stress_test.dart
+++ b/tests/standalone_2/ffi/function_stress_test.dart
@@ -2,10 +2,12 @@
// 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 stress-testing boxing and GC.
// VMOptions=--deterministic --optimization-counter-threshold=500 --verbose-gc
// VMOptions=--deterministic --optimization-counter-threshold=-1 --verbose-gc
//
+// Dart test program for stress-testing boxing and GC in return paths from FFI
+// trampolines.
+//
// NOTE: This test does not produce useful stderr when it fails because the
// stderr is redirected to a file for reflection.
@@ -39,7 +41,8 @@
// Smi.
await test(watcher, testBoxInt32, mustTriggerGC: false);
await test(watcher, testBoxDouble);
- await test(watcher, testBoxPointer);
+ await test(watcher, testBoxSmallPointer);
+ await test(watcher, testBoxLargePointer);
} finally {
watcher.dispose();
}
@@ -56,9 +59,12 @@
typedef NullaryOpDbl = double Function();
typedef NullaryOpPtr = ffi.Pointer<ffi.Void> Function();
+//// These functions return values that require boxing into different types.
+
final minInt64 =
ffiTestFunctions.lookupFunction<NativeNullaryOp64, NullaryOp>("MinInt64");
+// Forces boxing into Mint on all platforms.
void testBoxInt64() {
Expect.equals(0x8000000000000000, minInt64());
}
@@ -66,13 +72,15 @@
NullaryOp minInt32 =
ffiTestFunctions.lookupFunction<NativeNullaryOp32, NullaryOp>("MinInt32");
+// Forces boxing into Mint on 32-bit platforms only.
void testBoxInt32() {
- Expect.equals(0x80000000, minInt32());
+ Expect.equals(-0x80000000, minInt32());
}
final smallDouble = ffiTestFunctions
.lookupFunction<NativeNullaryOpDouble, NullaryOpDbl>("SmallDouble");
+// Forces boxing into Double.
void testBoxDouble() {
Expect.equals(0x80000000 * -1.0, smallDouble());
}
@@ -80,6 +88,16 @@
final smallPointer = ffiTestFunctions
.lookupFunction<NativeNullaryOpPtr, NullaryOpPtr>("SmallPointer");
-void testBoxPointer() {
+// Forces boxing into ffi.Pointer. On 32-bit platforms, also forces boxing into
+// Mint inside of ffi.Pointer.
+void testBoxSmallPointer() {
Expect.equals(-0x80000000, smallPointer().address);
}
+
+final largePointer = ffiTestFunctions
+ .lookupFunction<NativeNullaryOpPtr, NullaryOpPtr>("LargePointer");
+
+// Forces boxing into ffi.Pointer and ffi.Mint on all platforms.
+void testBoxLargePointer() {
+ Expect.equals(-0x8000000000000000, largePointer().address);
+}
diff --git a/tests/standalone_2/ffi/function_test.dart b/tests/standalone_2/ffi/function_test.dart
index c2019f7..b7e8129 100644
--- a/tests/standalone_2/ffi/function_test.dart
+++ b/tests/standalone_2/ffi/function_test.dart
@@ -3,6 +3,9 @@
// 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
library FfiTest;
@@ -13,23 +16,26 @@
import "package:expect/expect.dart";
void main() {
- testNativeFunctionFromCast();
- testNativeFunctionFromLookup();
- test64bitInterpretations();
- testTruncation();
- testNativeFunctionDoubles();
- testNativeFunctionFloats();
- testNativeFunctionManyArguments1();
- testNativeFunctionManyArguments2();
- testNativeFunctionManyArguments3();
- testNativeFunctionPointer();
- testNullInt();
- testNullDouble();
- testNullManyArgs();
- testNullPointers();
- testFloatRounding();
- testVoidReturn();
- testNoArgs();
+ for (int i = 0; i < 100; ++i) {
+ testNativeFunctionFromCast();
+ testNativeFunctionFromLookup();
+ test64bitInterpretations();
+ // TODO(36122): testExtension();
+ testTruncation();
+ testNativeFunctionDoubles();
+ testNativeFunctionFloats();
+ testNativeFunctionManyArguments1();
+ testNativeFunctionManyArguments2();
+ testNativeFunctionManyArguments3();
+ testNativeFunctionPointer();
+ testNullInt();
+ testNullDouble();
+ testNullManyArgs();
+ testNullPointers();
+ testFloatRounding();
+ testVoidReturn();
+ testNoArgs();
+ }
}
ffi.DynamicLibrary ffiTestFunctions =
@@ -43,8 +49,8 @@
void testNativeFunctionFromCast() {
ffi.Pointer<ffi.IntPtr> p1 = ffi.allocate();
ffi.Pointer<ffi.NativeFunction<NativeBinaryOp>> p2 = p1.cast();
- BinaryOp f = p2.asFunction<BinaryOp>();
- BinaryOp f2 = p2.asFunction<GenericBinaryOp<int>>();
+ p2.asFunction<BinaryOp>();
+ p2.asFunction<GenericBinaryOp<int>>();
p1.free();
}
@@ -54,13 +60,15 @@
typedef NativeQuadOpUnsigned = ffi.Uint64 Function(
ffi.Uint64, ffi.Uint32, ffi.Uint16, ffi.Uint8);
+BinaryOp sumPlus42 =
+ ffiTestFunctions.lookupFunction<NativeBinaryOp, BinaryOp>("SumPlus42");
+
+QuadOp intComputation = ffiTestFunctions
+ .lookupFunction<NativeQuadOpSigned, QuadOp>("IntComputation");
+
void testNativeFunctionFromLookup() {
- BinaryOp sumPlus42 =
- ffiTestFunctions.lookupFunction<NativeBinaryOp, BinaryOp>("SumPlus42");
Expect.equals(49, sumPlus42(3, 4));
- QuadOp intComputation = ffiTestFunctions
- .lookupFunction<NativeQuadOpSigned, QuadOp>("IntComputation");
Expect.equals(625, intComputation(125, 250, 500, 1000));
Expect.equals(
@@ -69,10 +77,29 @@
-0x8000000000000000, intComputation(0, 0, 0, -0x8000000000000000));
}
-void test64bitInterpretations() {
- QuadOp uintComputation = ffiTestFunctions
- .lookupFunction<NativeQuadOpUnsigned, QuadOp>("UintComputation");
+typedef NativeNullaryOpSigned = ffi.Int32 Function();
+typedef NativeNullaryOpUnsigned = ffi.Uint32 Function();
+int Function() unsignedOp = ffiTestFunctions
+ .lookup("TestExtension")
+ .cast<ffi.Pointer<ffi.NativeFunction<NativeNullaryOpUnsigned>>>()
+ .asFunction();
+
+int Function() signedOp = ffiTestFunctions
+ .lookup("TestExtension")
+ .cast<ffi.Pointer<ffi.NativeFunction<NativeNullaryOpSigned>>>()
+ .asFunction();
+
+// Test 32-bit (int32_t) -> 64-bit (Dart int) sign extension and truncation.
+void testExtension() {
+ Expect.equals(unsignedOp(), 0x80000000);
+ Expect.equals(signedOp(), 0xffffffff80000000);
+}
+
+QuadOp uintComputation = ffiTestFunctions
+ .lookupFunction<NativeQuadOpUnsigned, QuadOp>("UintComputation");
+
+void test64bitInterpretations() {
// 2 ^ 63 - 1
Expect.equals(
0x7FFFFFFFFFFFFFFF, uintComputation(0, 0, 0, 0x7FFFFFFFFFFFFFFF));
@@ -87,10 +114,10 @@
ffi.Int8, ffi.Int16, ffi.Int32, ffi.Uint8, ffi.Uint16, ffi.Uint32);
typedef SenaryOp = int Function(int, int, int, int, int, int);
-void testTruncation() {
- SenaryOp sumSmallNumbers = ffiTestFunctions
- .lookupFunction<NativeSenaryOp, SenaryOp>("SumSmallNumbers");
+SenaryOp sumSmallNumbers = ffiTestFunctions
+ .lookupFunction<NativeSenaryOp, SenaryOp>("SumSmallNumbers");
+void testTruncation() {
// TODO(dacoharkes): implement truncation and sign extension in trampolines
// for values smaller than 32 bits.
sumSmallNumbers(128, 0, 0, 0, 0, 0);
@@ -113,17 +140,19 @@
typedef NativeDoubleUnaryOp = ffi.Double Function(ffi.Double);
typedef DoubleUnaryOp = double Function(double);
+DoubleUnaryOp times1_337Double = ffiTestFunctions
+ .lookupFunction<NativeDoubleUnaryOp, DoubleUnaryOp>("Times1_337Double");
+
void testNativeFunctionDoubles() {
- DoubleUnaryOp times1_337Double = ffiTestFunctions
- .lookupFunction<NativeDoubleUnaryOp, DoubleUnaryOp>("Times1_337Double");
Expect.approxEquals(2.0 * 1.337, times1_337Double(2.0));
}
typedef NativeFloatUnaryOp = ffi.Float Function(ffi.Float);
+DoubleUnaryOp times1_337Float = ffiTestFunctions
+ .lookupFunction<NativeFloatUnaryOp, DoubleUnaryOp>("Times1_337Float");
+
void testNativeFunctionFloats() {
- DoubleUnaryOp times1_337Float = ffiTestFunctions
- .lookupFunction<NativeFloatUnaryOp, DoubleUnaryOp>("Times1_337Float");
Expect.approxEquals(1337.0, times1_337Float(1000.0));
}
@@ -141,9 +170,10 @@
typedef OctenaryOp = int Function(
int, int, int, int, int, int, int, int, int, int);
+OctenaryOp sumManyInts = ffiTestFunctions
+ .lookupFunction<NativeOctenaryOp, OctenaryOp>("SumManyInts");
+
void testNativeFunctionManyArguments1() {
- OctenaryOp sumManyInts = ffiTestFunctions
- .lookupFunction<NativeOctenaryOp, OctenaryOp>("SumManyInts");
Expect.equals(55, sumManyInts(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
}
@@ -161,10 +191,10 @@
typedef DoubleOctenaryOp = double Function(double, double, double, double,
double, double, double, double, double, double);
+DoubleOctenaryOp sumManyDoubles = ffiTestFunctions
+ .lookupFunction<NativeDoubleOctenaryOp, DoubleOctenaryOp>("SumManyDoubles");
+
void testNativeFunctionManyArguments2() {
- DoubleOctenaryOp sumManyDoubles =
- ffiTestFunctions.lookupFunction<NativeDoubleOctenaryOp, DoubleOctenaryOp>(
- "SumManyDoubles");
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));
}
@@ -212,9 +242,10 @@
int,
double);
+VigesimalOp sumManyNumbers = ffiTestFunctions
+ .lookupFunction<NativeVigesimalOp, VigesimalOp>("SumManyNumbers");
+
void testNativeFunctionManyArguments3() {
- VigesimalOp sumManyNumbers = ffiTestFunctions
- .lookupFunction<NativeVigesimalOp, VigesimalOp>("SumManyNumbers");
Expect.approxEquals(
210.0,
sumManyNumbers(1, 2.0, 3, 4.0, 5, 6.0, 7, 8.0, 9, 10.0, 11, 12.0, 13,
@@ -224,9 +255,10 @@
typedef Int64PointerUnOp = ffi.Pointer<ffi.Int64> Function(
ffi.Pointer<ffi.Int64>);
+Int64PointerUnOp assign1337Index1 = ffiTestFunctions
+ .lookupFunction<Int64PointerUnOp, Int64PointerUnOp>("Assign1337Index1");
+
void testNativeFunctionPointer() {
- Int64PointerUnOp assign1337Index1 = ffiTestFunctions
- .lookupFunction<Int64PointerUnOp, Int64PointerUnOp>("Assign1337Index1");
ffi.Pointer<ffi.Int64> p2 = ffi.allocate(count: 2);
p2.store(42);
p2.elementAt(1).store(1000);
@@ -245,23 +277,18 @@
}
void testNullDouble() {
- DoubleUnaryOp times1_337Double = ffiTestFunctions
- .lookupFunction<NativeDoubleUnaryOp, DoubleUnaryOp>("Times1_337Double");
Expect.throws(() => times1_337Double(null));
}
void testNullManyArgs() {
- VigesimalOp sumManyNumbers = ffiTestFunctions
- .lookupFunction<NativeVigesimalOp, VigesimalOp>("SumManyNumbers");
Expect.throws(() => 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, null, 20.0));
}
-void testNullPointers() {
- Int64PointerUnOp nullableInt64ElemAt1 =
- ffiTestFunctions.lookupFunction<Int64PointerUnOp, Int64PointerUnOp>(
- "NullableInt64ElemAt1");
+Int64PointerUnOp nullableInt64ElemAt1 = ffiTestFunctions
+ .lookupFunction<Int64PointerUnOp, Int64PointerUnOp>("NullableInt64ElemAt1");
+void testNullPointers() {
ffi.Pointer<ffi.Int64> result = nullableInt64ElemAt1(null);
Expect.isNull(result);
@@ -274,10 +301,10 @@
typedef NativeFloatPointerToBool = ffi.Uint8 Function(ffi.Pointer<ffi.Float>);
typedef FloatPointerToBool = int Function(ffi.Pointer<ffi.Float>);
-void testFloatRounding() {
- FloatPointerToBool isRoughly1337 = ffiTestFunctions.lookupFunction<
- NativeFloatPointerToBool, FloatPointerToBool>("IsRoughly1337");
+FloatPointerToBool isRoughly1337 = ffiTestFunctions.lookupFunction<
+ NativeFloatPointerToBool, FloatPointerToBool>("IsRoughly1337");
+void testFloatRounding() {
ffi.Pointer<ffi.Float> p2 = ffi.allocate();
p2.store(1337.0);
@@ -290,10 +317,10 @@
typedef NativeFloatToVoid = ffi.Void Function(ffi.Float);
typedef DoubleToVoid = void Function(double);
-void testVoidReturn() {
- DoubleToVoid devNullFloat = ffiTestFunctions
- .lookupFunction<NativeFloatToVoid, DoubleToVoid>("DevNullFloat");
+DoubleToVoid devNullFloat = ffiTestFunctions
+ .lookupFunction<NativeFloatToVoid, DoubleToVoid>("DevNullFloat");
+void testVoidReturn() {
devNullFloat(1337.0);
dynamic loseSignature = devNullFloat;
@@ -304,10 +331,10 @@
typedef NativeVoidToFloat = ffi.Float Function();
typedef VoidToDouble = double Function();
-void testNoArgs() {
- VoidToDouble inventFloatValue = ffiTestFunctions
- .lookupFunction<NativeVoidToFloat, VoidToDouble>("InventFloatValue");
+VoidToDouble inventFloatValue = ffiTestFunctions
+ .lookupFunction<NativeVoidToFloat, VoidToDouble>("InventFloatValue");
+void testNoArgs() {
double result = inventFloatValue();
Expect.approxEquals(1337.0, result);
}
diff --git a/tests/standalone_2/ffi/negative_function_test.dart b/tests/standalone_2/ffi/negative_function_test.dart
index f716ca5..a529d67 100644
--- a/tests/standalone_2/ffi/negative_function_test.dart
+++ b/tests/standalone_2/ffi/negative_function_test.dart
@@ -19,8 +19,8 @@
typedef NativeBinaryOp = ffi.Int32 Function(ffi.Int32, ffi.Int32);
typedef BinaryOp = int Function(int, int);
-typedef NativeUnaryOp = ffi.Int64 Function(ffi.Int64);
-typedef UnaryOp = int Function(int);
+typedef NativeUnaryOp = ffi.Int64 Function(ffi.Pointer<ffi.Int64>);
+typedef UnaryOp = int Function(ffi.Pointer<ffi.Int64>);
void testWrongArity() {
{
@@ -44,18 +44,18 @@
{
dynamic sumPlus42 =
ffiTestFunctions.lookupFunction<NativeBinaryOp, BinaryOp>("SumPlus42");
- Expect.throwsNoSuchMethodError(() => sumPlus42("abc", "def"));
+ Expect.throwsTypeError(() => sumPlus42("abc", "def"));
}
{
Function sumPlus42 =
ffiTestFunctions.lookupFunction<NativeBinaryOp, BinaryOp>("SumPlus42");
- Expect.throwsNoSuchMethodError(() => sumPlus42("abc", "def"));
+ Expect.throwsTypeError(() => sumPlus42("abc", "def"));
}
{
dynamic pointerOp = ffiTestFunctions
.lookupFunction<NativeUnaryOp, UnaryOp>("Assign1337Index1");
- Expect.throwsNoSuchMethodError(() => pointerOp(0));
+ Expect.throwsTypeError(() => pointerOp(0));
}
}