dart-sdk/tests/ffi/data_test.dart
Daco Harkes 3593de9179 [vm/ffi] Change Pointer.elementAt and sizeOf to use static type
This CL changes the semantics of
`Pointer<T extends NativeType>.elementAt` and
`sizeOf<T extends NativeType>` to use the compile-time `T` rather than
the runtime `T`.

Issue: https://github.com/dart-lang/sdk/issues/38721

TEST=tests/ffi/data_test.dart
TEST=tests/ffi/sizeof_test.dart
TEST=tests/ffi/structs_test.dart
TEST=tests/ffi/vmspecific_static_checks_test.dart

Change-Id: Ifb25a4bd66d50a385d3db6dec9213b96dff21722
Cq-Include-Trybots: luci.dart.try:vm-precomp-ffi-qemu-linux-release-arm-try,analyzer-analysis-server-linux-try,analyzer-linux-release-try,analyzer-nnbd-linux-release-try,front-end-linux-release-x64-try,front-end-nnbd-linux-release-x64-try,benchmark-linux-try,dart-sdk-linux-try,pkg-linux-release-try,vm-ffi-android-release-arm-try,vm-ffi-android-release-arm64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/178200
Reviewed-by: Aske Simon Christensen <askesc@google.com>
2021-02-17 11:39:42 +00:00

480 lines
12 KiB
Dart

// 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 primitive data pointers.
//
// SharedObjects=ffi_test_functions
import 'dart:ffi';
import "package:expect/expect.dart";
import "package:ffi/ffi.dart";
import 'ffi_test_helpers.dart';
void main() {
testPointerBasic();
testPointerFromPointer();
testPointerPointerArithmetic();
testPointerPointerArithmeticSizes();
testPointerAllocateZero();
testPointerCast();
testCastGeneric();
testCastGeneric2();
testCastNativeType();
testCondensedNumbersInt8();
testCondensedNumbersFloat();
testRangeInt8();
testRangeUint8();
testRangeInt16();
testRangeUint16();
testRangeInt32();
testRangeUint32();
testRangeInt64();
testRangeUint64();
testRangeIntPtr();
testFloat();
testDouble();
testVoid();
testPointerPointer();
testPointerPointerNull();
testSizeOf();
testPointerChain(100);
testTypeTest();
testToString();
testEquality();
testAllocateVoid();
testAllocateNativeFunction();
testSizeOfVoid();
testSizeOfNativeFunction();
testDynamicInvocation();
testMemoryAddressTruncation();
testNullptrCast();
}
void testPointerBasic() {
Pointer<Int64> p = calloc();
p.value = 42;
Expect.equals(42, p.value);
calloc.free(p);
}
void testPointerFromPointer() {
Pointer<Int64> p = calloc();
p.value = 1337;
int ptr = p.address;
Pointer<Int64> p2 = Pointer.fromAddress(ptr);
Expect.equals(1337, p2.value);
calloc.free(p);
}
void testPointerPointerArithmetic() {
Pointer<Int64> p = calloc(2);
Pointer<Int64> p2 = p.elementAt(1);
p2.value = 100;
Pointer<Int64> p3 = p.offsetBy(8);
Expect.equals(100, p3.value);
calloc.free(p);
}
void testPointerPointerArithmeticSizes() {
Pointer<Int64> p = calloc(2);
Pointer<Int64> p2 = p.elementAt(1);
int addr = p.address;
Expect.equals(addr + 8, p2.address);
calloc.free(p);
Pointer<Int32> p3 = calloc(2);
Pointer<Int32> p4 = p3.elementAt(1);
addr = p3.address;
Expect.equals(addr + 4, p4.address);
calloc.free(p3);
}
void testPointerAllocateZero() {
// > If size is 0, either a null pointer or a unique pointer that can be
// > successfully passed to calloc.free() shall be returned.
// http://pubs.opengroup.org/onlinepubs/009695399/functions/calloc.html
//
// Null pointer throws a Dart exception.
bool returnedNullPointer = false;
Pointer<Int8> p = nullptr;
try {
p = calloc<Int8>(0);
} on Exception {
returnedNullPointer = true;
}
if (!returnedNullPointer) {
calloc.free(p);
}
}
void testPointerCast() {
Pointer<Int64> p = calloc();
p.cast<Int32>(); // gets the correct type args back
calloc.free(p);
}
void testCastGeneric() {
Pointer<T> generic<T extends NativeType>(Pointer<Int16> p) {
return p.cast();
}
Pointer<Int16> p = calloc();
Pointer<Int64> p2 = generic(p);
calloc.free(p);
}
void testCastGeneric2() {
Pointer<Int64> generic<T extends NativeType>(Pointer<T> p) {
return p.cast();
}
Pointer<Int16> p = calloc();
Pointer<Int64> p2 = generic(p);
calloc.free(p);
}
void testCastNativeType() {
Pointer<Int64> p = calloc();
p.cast<Pointer>();
calloc.free(p);
}
void testCondensedNumbersInt8() {
Pointer<Int8> p = calloc(8);
for (var i in [0, 1, 2, 3, 4, 5, 6, 7]) {
p[i] = i * 3;
}
for (var i in [0, 1, 2, 3, 4, 5, 6, 7]) {
Expect.equals(i * 3, p[i]);
}
calloc.free(p);
}
void testCondensedNumbersFloat() {
Pointer<Float> p = calloc(8);
for (var i in [0, 1, 2, 3, 4, 5, 6, 7]) {
p[i] = 1.511366173271439e-13;
}
for (var i in [0, 1, 2, 3, 4, 5, 6, 7]) {
Expect.equals(1.511366173271439e-13, p[i]);
}
calloc.free(p);
}
void testRangeInt8() {
Pointer<Int8> p = calloc();
p.value = 127;
Expect.equals(127, p.value);
p.value = -128;
Expect.equals(-128, p.value);
Expect.equals(0x0000000000000080, 128);
Expect.equals(0xFFFFFFFFFFFFFF80, -128);
p.value = 128;
Expect.equals(-128, p.value); // truncated and sign extended
Expect.equals(0xFFFFFFFFFFFFFF7F, -129);
Expect.equals(0x000000000000007F, 127);
p.value = -129;
Expect.equals(127, p.value); // truncated
calloc.free(p);
}
void testRangeUint8() {
Pointer<Uint8> p = calloc();
p.value = 255;
Expect.equals(255, p.value);
p.value = 0;
Expect.equals(0, p.value);
Expect.equals(0x0000000000000000, 0);
Expect.equals(0x0000000000000100, 256);
p.value = 256;
Expect.equals(0, p.value); // truncated
Expect.equals(0xFFFFFFFFFFFFFFFF, -1);
Expect.equals(0x00000000000000FF, 255);
p.value = -1;
Expect.equals(255, p.value); // truncated
calloc.free(p);
}
void testRangeInt16() {
Pointer<Int16> p = calloc();
p.value = 0x7FFF;
Expect.equals(0x7FFF, p.value);
p.value = -0x8000;
Expect.equals(-0x8000, p.value);
p.value = 0x8000;
Expect.equals(0xFFFFFFFFFFFF8000, p.value); // truncated and sign extended
p.value = -0x8001;
Expect.equals(0x7FFF, p.value); // truncated
calloc.free(p);
}
void testRangeUint16() {
Pointer<Uint16> p = calloc();
p.value = 0xFFFF;
Expect.equals(0xFFFF, p.value);
p.value = 0;
Expect.equals(0, p.value);
p.value = 0x10000;
Expect.equals(0, p.value); // truncated
p.value = -1;
Expect.equals(0xFFFF, p.value); // truncated
calloc.free(p);
}
void testRangeInt32() {
Pointer<Int32> p = calloc();
p.value = 0x7FFFFFFF;
Expect.equals(0x7FFFFFFF, p.value);
p.value = -0x80000000;
Expect.equals(-0x80000000, p.value);
p.value = 0x80000000;
Expect.equals(0xFFFFFFFF80000000, p.value); // truncated and sign extended
p.value = -0x80000001;
Expect.equals(0x7FFFFFFF, p.value); // truncated
calloc.free(p);
}
void testRangeUint32() {
Pointer<Uint32> p = calloc();
p.value = 0xFFFFFFFF;
Expect.equals(0xFFFFFFFF, p.value);
p.value = 0;
Expect.equals(0, p.value);
p.value = 0x100000000;
Expect.equals(0, p.value); // truncated
p.value = -1;
Expect.equals(0xFFFFFFFF, p.value); // truncated
calloc.free(p);
}
void testRangeInt64() {
Pointer<Int64> p = calloc();
p.value = 0x7FFFFFFFFFFFFFFF; // 2 ^ 63 - 1
Expect.equals(0x7FFFFFFFFFFFFFFF, p.value);
p.value = -0x8000000000000000; // -2 ^ 63
Expect.equals(-0x8000000000000000, p.value);
calloc.free(p);
}
void testRangeUint64() {
Pointer<Uint64> p = calloc();
p.value = 0x7FFFFFFFFFFFFFFF; // 2 ^ 63 - 1
Expect.equals(0x7FFFFFFFFFFFFFFF, p.value);
p.value = -0x8000000000000000; // -2 ^ 63 interpreted as 2 ^ 63
Expect.equals(-0x8000000000000000, p.value);
// Dart allows interpreting bits both signed and unsigned
Expect.equals(0xFFFFFFFFFFFFFFFF, -1);
p.value = -1; // -1 interpreted as 2 ^ 64 - 1
Expect.equals(-1, p.value);
Expect.equals(0xFFFFFFFFFFFFFFFF, p.value);
calloc.free(p);
}
void testRangeIntPtr() {
Pointer<IntPtr> p = calloc();
int pAddr = p.address;
p.value = pAddr; // its own address should fit
p.value = 0x7FFFFFFF; // and 32 bit addresses should fit
Expect.equals(0x7FFFFFFF, p.value);
p.value = -0x80000000;
Expect.equals(-0x80000000, p.value);
calloc.free(p);
}
void testFloat() {
Pointer<Float> p = calloc();
p.value = 1.511366173271439e-13;
Expect.equals(1.511366173271439e-13, p.value);
p.value = 1.4260258159703532e-105; // float does not have enough precision
Expect.notEquals(1.4260258159703532e-105, p.value);
calloc.free(p);
}
void testDouble() {
Pointer<Double> p = calloc();
p.value = 1.4260258159703532e-105;
Expect.equals(1.4260258159703532e-105, p.value);
calloc.free(p);
}
void testVoid() {
Pointer<IntPtr> p1 = calloc();
Pointer<Void> p2 = p1.cast(); // make this dart pointer opaque
p2.address; // we can print the address
calloc.free(p2);
}
void testPointerPointer() {
Pointer<Int16> p = calloc();
p.value = 17;
Pointer<Pointer<Int16>> p2 = calloc();
p2.value = p;
Expect.equals(17, p2.value.value);
calloc.free(p2);
calloc.free(p);
}
void testPointerPointerNull() {
Pointer<Pointer<Int8>> pointerToPointer = calloc();
Pointer<Int8> value = nullptr;
pointerToPointer.value = value;
value = pointerToPointer.value;
Expect.equals(value, nullptr);
value = calloc();
pointerToPointer.value = value;
value = pointerToPointer.value;
Expect.isNotNull(value);
calloc.free(value);
value = nullptr;
pointerToPointer.value = value;
value = pointerToPointer.value;
Expect.equals(value, nullptr);
calloc.free(pointerToPointer);
}
void testSizeOf() {
Expect.equals(1, sizeOf<Int8>());
Expect.equals(2, sizeOf<Int16>());
Expect.equals(4, sizeOf<Int32>());
Expect.equals(8, sizeOf<Int64>());
Expect.equals(1, sizeOf<Uint8>());
Expect.equals(2, sizeOf<Uint16>());
Expect.equals(4, sizeOf<Uint32>());
Expect.equals(8, sizeOf<Uint64>());
Expect.equals(true, 4 == sizeOf<IntPtr>() || 8 == sizeOf<IntPtr>());
Expect.equals(4, sizeOf<Float>());
Expect.equals(8, sizeOf<Double>());
}
// note: stack overflows at around 15k calls
void testPointerChain(int length) {
void createChain(Pointer<IntPtr> head, int length, int value) {
if (length == 0) {
head.value = value;
return;
}
Pointer<IntPtr> next = calloc();
head.value = next.address;
createChain(next, length - 1, value);
}
int getChainValue(Pointer<IntPtr> head, int length) {
if (length == 0) {
return head.value;
}
Pointer<IntPtr> next = Pointer.fromAddress(head.value);
return getChainValue(next, length - 1);
}
void freeChain(Pointer<IntPtr> head, int length) {
Pointer<IntPtr> next = Pointer.fromAddress(head.value);
calloc.free(head);
if (length == 0) {
return;
}
freeChain(next, length - 1);
}
Pointer<IntPtr> head = calloc();
createChain(head, length, 512);
int tailValue = getChainValue(head, length);
Expect.equals(512, tailValue);
freeChain(head, length);
}
void testTypeTest() {
Pointer<Int8> p = calloc();
Expect.isTrue(p is Pointer);
calloc.free(p);
}
void testToString() {
Pointer<Int16> p = calloc();
Expect.stringEquals(
"Pointer<Int16>: address=0x", p.toString().substring(0, 26));
calloc.free(p);
Pointer<Int64> p2 = Pointer.fromAddress(0x123abc);
Expect.stringEquals("Pointer<Int64>: address=0x123abc", p2.toString());
}
void testEquality() {
Pointer<Int8> p = Pointer.fromAddress(12345678);
Pointer<Int8> p2 = Pointer.fromAddress(12345678);
Expect.equals(p, p2);
Expect.equals(p.hashCode, p2.hashCode);
Pointer<Int16> p3 = p.cast();
Expect.equals(p, p3);
Expect.equals(p.hashCode, p3.hashCode);
Pointer<Int8> p4 = p.offsetBy(1337);
Expect.notEquals(p, p4);
}
typedef Int8UnOp = Int8 Function(Int8);
void testAllocateVoid() {
Expect.throws(() {
Pointer<Void> p = calloc();
});
}
void testAllocateNativeFunction() {
Expect.throws(() {
Pointer<NativeFunction<Int8UnOp>> p = calloc();
});
}
void testSizeOfVoid() {
Expect.throws(() {
sizeOf<Void>();
});
}
void testSizeOfNativeFunction() {
Expect.throws(() {
sizeOf<NativeFunction<Int8UnOp>>();
});
}
void testDynamicInvocation() {
dynamic p = calloc<Int8>();
Expect.throws(() {
final int i = p.value;
});
Expect.throws(() => p.value = 1);
Expect.throws(() => p.elementAt(5));
final int addr = p.address;
final Pointer<Int16> p2 = p.cast<Int16>();
calloc.free(p);
}
final nullableInt64ElementAt1 = ffiTestFunctions.lookupFunction<
Pointer<Int64> Function(Pointer<Int64>),
Pointer<Int64> Function(Pointer<Int64>)>("NullableInt64ElemAt1");
void testNullptrCast() {
Pointer<Int64> ptr = nullptr;
ptr = nullableInt64ElementAt1(ptr);
Expect.equals(ptr, nullptr);
}
void testMemoryAddressTruncation() {
const int kIgnoreBytesPositive = 0x1122334400000000;
const int kIgnoreBytesNegative = 0xffddccbb00000000;
if (sizeOf<IntPtr>() == 4) {
final p1 = Pointer<Int8>.fromAddress(123);
final p2 = Pointer<Int8>.fromAddress(123 + kIgnoreBytesPositive);
final p3 = Pointer<Int8>.fromAddress(123 + kIgnoreBytesNegative);
Expect.equals(p1.address, p2.address);
Expect.equals(p1, p2);
Expect.equals(p1.address, p3.address);
Expect.equals(p1, p3);
}
}