// 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. library FfiTest; import 'dart:ffi' as ffi; import "package:expect/expect.dart"; void main() { testPointerBasic(); testPointerFromPointer(); testPointerPointerArithmetic(); testPointerPointerArithmeticSizes(); testPointerAllocateNonPositive(); testPointerCast(); testCastGeneric(); testCastGeneric2(); testCastNativeType(); testCondensedNumbersInt8(); testCondensedNumbersFloat(); testRangeInt8(); testRangeUint8(); testRangeInt16(); testRangeUint16(); testRangeInt32(); testRangeUint32(); testRangeInt64(); testRangeUint64(); testRangeIntPtr(); testFloat(); testDouble(); testVoid(); testPointerPointer(); testPointerPointerNull(); testPointerStoreNull(); testSizeOf(); testPointerChain(100); testTypeTest(); testToString(); testEquality(); testAllocateGeneric(); testAllocateVoid(); testAllocateNativeFunction(); testAllocateNativeType(); testSizeOfGeneric(); testSizeOfVoid(); testSizeOfNativeFunction(); testSizeOfNativeType(); testFreeZeroOut(); } void testPointerBasic() { ffi.Pointer p = ffi.allocate(); p.store(42); Expect.equals(42, p.load()); p.free(); } void testPointerFromPointer() { ffi.Pointer p = ffi.allocate(); p.store(1337); int ptr = p.address; ffi.Pointer p2 = ffi.fromAddress(ptr); Expect.equals(1337, p2.load()); p.free(); } void testPointerPointerArithmetic() { ffi.Pointer p = ffi.allocate(count: 2); ffi.Pointer p2 = p.elementAt(1); p2.store(100); ffi.Pointer p3 = p.offsetBy(8); Expect.equals(100, p3.load()); p.free(); } void testPointerPointerArithmeticSizes() { ffi.Pointer p = ffi.allocate(count: 2); ffi.Pointer p2 = p.elementAt(1); int addr = p.address; Expect.equals(addr + 8, p2.address); p.free(); ffi.Pointer p3 = ffi.allocate(count: 2); ffi.Pointer p4 = p3.elementAt(1); addr = p3.address; Expect.equals(addr + 4, p4.address); p3.free(); } void testPointerAllocateNonPositive() { Expect.throws(() => ffi.allocate(count: 0)); Expect.throws(() => ffi.allocate(count: -1)); } void testPointerCast() { ffi.Pointer p = ffi.allocate(); ffi.Pointer p2 = p.cast(); // gets the correct type args back p.free(); } void testCastGeneric() { ffi.Pointer generic(ffi.Pointer p) { return p.cast(); } ffi.Pointer p = ffi.allocate(); ffi.Pointer p2 = generic(p); p.free(); } void testCastGeneric2() { ffi.Pointer generic(ffi.Pointer p) { return p.cast(); } ffi.Pointer p = ffi.allocate(); ffi.Pointer p2 = generic(p); p.free(); } void testCastNativeType() { ffi.Pointer p = ffi.allocate(); Expect.throws(() { p.cast(); }); p.free(); } void testCondensedNumbersInt8() { ffi.Pointer p = ffi.allocate(count: 8); for (var i in [0, 1, 2, 3, 4, 5, 6, 7]) { p.elementAt(i).store(i * 3); } for (var i in [0, 1, 2, 3, 4, 5, 6, 7]) { Expect.equals(i * 3, p.elementAt(i).load()); } p.free(); } void testCondensedNumbersFloat() { ffi.Pointer p = ffi.allocate(count: 8); for (var i in [0, 1, 2, 3, 4, 5, 6, 7]) { p.elementAt(i).store(1.511366173271439e-13); } for (var i in [0, 1, 2, 3, 4, 5, 6, 7]) { Expect.equals(1.511366173271439e-13, p.elementAt(i).load()); } p.free(); } void testRangeInt8() { ffi.Pointer p = ffi.allocate(); p.store(127); Expect.equals(127, p.load()); p.store(-128); Expect.equals(-128, p.load()); Expect.equals(0x0000000000000080, 128); Expect.equals(0xFFFFFFFFFFFFFF80, -128); p.store(128); Expect.equals(-128, p.load()); // truncated and sign extended Expect.equals(0xFFFFFFFFFFFFFF7F, -129); Expect.equals(0x000000000000007F, 127); p.store(-129); Expect.equals(127, p.load()); // truncated p.free(); } void testRangeUint8() { ffi.Pointer p = ffi.allocate(); p.store(255); Expect.equals(255, p.load()); p.store(0); Expect.equals(0, p.load()); Expect.equals(0x0000000000000000, 0); Expect.equals(0x0000000000000100, 256); p.store(256); Expect.equals(0, p.load()); // truncated Expect.equals(0xFFFFFFFFFFFFFFFF, -1); Expect.equals(0x00000000000000FF, 255); p.store(-1); Expect.equals(255, p.load()); // truncated p.free(); } void testRangeInt16() { ffi.Pointer p = ffi.allocate(); p.store(0x7FFF); Expect.equals(0x7FFF, p.load()); p.store(-0x8000); Expect.equals(-0x8000, p.load()); p.store(0x8000); Expect.equals( 0xFFFFFFFFFFFF8000, p.load()); // truncated and sign extended p.store(-0x8001); Expect.equals(0x7FFF, p.load()); // truncated p.free(); } void testRangeUint16() { ffi.Pointer p = ffi.allocate(); p.store(0xFFFF); Expect.equals(0xFFFF, p.load()); p.store(0); Expect.equals(0, p.load()); p.store(0x10000); Expect.equals(0, p.load()); // truncated p.store(-1); Expect.equals(0xFFFF, p.load()); // truncated p.free(); } void testRangeInt32() { ffi.Pointer p = ffi.allocate(); p.store(0x7FFFFFFF); Expect.equals(0x7FFFFFFF, p.load()); p.store(-0x80000000); Expect.equals(-0x80000000, p.load()); p.store(0x80000000); Expect.equals( 0xFFFFFFFF80000000, p.load()); // truncated and sign extended p.store(-0x80000001); Expect.equals(0x7FFFFFFF, p.load()); // truncated p.free(); } void testRangeUint32() { ffi.Pointer p = ffi.allocate(); p.store(0xFFFFFFFF); Expect.equals(0xFFFFFFFF, p.load()); p.store(0); Expect.equals(0, p.load()); p.store(0x100000000); Expect.equals(0, p.load()); // truncated p.store(-1); Expect.equals(0xFFFFFFFF, p.load()); // truncated p.free(); } void testRangeInt64() { ffi.Pointer p = ffi.allocate(); p.store(0x7FFFFFFFFFFFFFFF); // 2 ^ 63 - 1 Expect.equals(0x7FFFFFFFFFFFFFFF, p.load()); p.store(-0x8000000000000000); // -2 ^ 63 Expect.equals(-0x8000000000000000, p.load()); p.free(); } void testRangeUint64() { ffi.Pointer p = ffi.allocate(); p.store(0x7FFFFFFFFFFFFFFF); // 2 ^ 63 - 1 Expect.equals(0x7FFFFFFFFFFFFFFF, p.load()); p.store(-0x8000000000000000); // -2 ^ 63 interpreted as 2 ^ 63 Expect.equals(-0x8000000000000000, p.load()); // Dart allows interpreting bits both signed and unsigned Expect.equals(0xFFFFFFFFFFFFFFFF, -1); p.store(-1); // -1 interpreted as 2 ^ 64 - 1 Expect.equals(-1, p.load()); Expect.equals(0xFFFFFFFFFFFFFFFF, p.load()); p.free(); } void testRangeIntPtr() { ffi.Pointer p = ffi.allocate(); int pAddr = p.address; p.store(pAddr); // its own address should fit p.store(0x7FFFFFFF); // and 32 bit addresses should fit Expect.equals(0x7FFFFFFF, p.load()); p.store(-0x80000000); Expect.equals(-0x80000000, p.load()); p.free(); } void testFloat() { ffi.Pointer p = ffi.allocate(); p.store(1.511366173271439e-13); Expect.equals(1.511366173271439e-13, p.load()); p.store(1.4260258159703532e-105); // float does not have enough precision Expect.notEquals(1.4260258159703532e-105, p.load()); p.free(); } void testDouble() { ffi.Pointer p = ffi.allocate(); p.store(1.4260258159703532e-105); Expect.equals(1.4260258159703532e-105, p.load()); p.free(); } void testVoid() { ffi.Pointer p1 = ffi.allocate(); ffi.Pointer p2 = p1.cast(); // make this dart pointer opaque p2.address; // we can print the address p2.free(); } void testPointerPointer() { ffi.Pointer p = ffi.allocate(); p.store(17); ffi.Pointer> p2 = ffi.allocate(); p2.store(p); Expect.equals(17, p2.load>().load()); p2.free(); p.free(); } void testPointerPointerNull() { ffi.Pointer> pointerToPointer = ffi.allocate(); ffi.Pointer value = null; pointerToPointer.store(value); value = pointerToPointer.load(); Expect.isNull(value); value = ffi.allocate(); pointerToPointer.store(value); value = pointerToPointer.load(); Expect.isNotNull(value); value.free(); value = null; pointerToPointer.store(value); value = pointerToPointer.load(); Expect.isNull(value); pointerToPointer.free(); } void testPointerStoreNull() { int i = null; ffi.Pointer p = ffi.allocate(); Expect.throws(() => p.store(i)); p.free(); double d = null; ffi.Pointer p2 = ffi.allocate(); Expect.throws(() => p2.store(d)); p2.free(); } void testSizeOf() { Expect.equals(1, ffi.sizeOf()); Expect.equals(2, ffi.sizeOf()); Expect.equals(4, ffi.sizeOf()); Expect.equals(8, ffi.sizeOf()); Expect.equals(1, ffi.sizeOf()); Expect.equals(2, ffi.sizeOf()); Expect.equals(4, ffi.sizeOf()); Expect.equals(8, ffi.sizeOf()); Expect.equals( true, 4 == ffi.sizeOf() || 8 == ffi.sizeOf()); Expect.equals(4, ffi.sizeOf()); Expect.equals(8, ffi.sizeOf()); } // note: stack overflows at around 15k calls void testPointerChain(int length) { void createChain(ffi.Pointer head, int length, int value) { if (length == 0) { head.store(value); return; } ffi.Pointer next = ffi.allocate(); head.store(next.address); createChain(next, length - 1, value); } int getChainValue(ffi.Pointer head, int length) { if (length == 0) { return head.load(); } ffi.Pointer next = ffi.fromAddress(head.load()); return getChainValue(next, length - 1); } void freeChain(ffi.Pointer head, int length) { ffi.Pointer next = ffi.fromAddress(head.load()); head.free(); if (length == 0) { return; } freeChain(next, length - 1); } ffi.Pointer head = ffi.allocate(); createChain(head, length, 512); int tailValue = getChainValue(head, length); Expect.equals(512, tailValue); freeChain(head, length); } void testTypeTest() { ffi.Pointer p = ffi.allocate(); Expect.isTrue(p is ffi.Pointer); p.free(); } void testToString() { ffi.Pointer p = ffi.allocate(); Expect.stringEquals( "Pointer: address=0x", p.toString().substring(0, 26)); p.free(); ffi.Pointer p2 = ffi.fromAddress(0x123abc); Expect.stringEquals("Pointer: address=0x123abc", p2.toString()); } void testEquality() { ffi.Pointer p = ffi.fromAddress(12345678); ffi.Pointer p2 = ffi.fromAddress(12345678); Expect.equals(p, p2); Expect.equals(p.hashCode, p2.hashCode); ffi.Pointer p3 = p.cast(); Expect.equals(p, p3); Expect.equals(p.hashCode, p3.hashCode); Expect.notEquals(p, null); Expect.notEquals(null, p); ffi.Pointer p4 = p.offsetBy(1337); Expect.notEquals(p, p4); } typedef Int8UnOp = ffi.Int8 Function(ffi.Int8); void testAllocateGeneric() { ffi.Pointer generic() { ffi.Pointer pointer; pointer = ffi.allocate(); return pointer; } ffi.Pointer p = generic(); p.free(); } void testAllocateVoid() { Expect.throws(() { ffi.Pointer p = ffi.allocate(); }); } void testAllocateNativeFunction() { Expect.throws(() { ffi.Pointer> p = ffi.allocate(); }); } void testAllocateNativeType() { Expect.throws(() { ffi.allocate(); }); } void testSizeOfGeneric() { int generic() { int size; size = ffi.sizeOf(); return size; } int size = generic>(); Expect.isTrue(size == 8 || size == 4); } void testSizeOfVoid() { Expect.throws(() { ffi.sizeOf(); }); } void testSizeOfNativeFunction() { Expect.throws(() { ffi.sizeOf>(); }); } void testSizeOfNativeType() { Expect.throws(() { ffi.sizeOf(); }); } void testFreeZeroOut() { // at least one of these pointers should have address != 0 on all platforms ffi.Pointer p1 = ffi.allocate(); ffi.Pointer p2 = ffi.allocate(); Expect.notEquals(0, p1.address & p2.address); p1.free(); p2.free(); Expect.equals(0, p1.address); Expect.equals(0, p2.address); }