| // Copyright (c) 2025, 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. |
| // |
| // SharedObjects=ffi_test_functions |
| // VMOptions= |
| // VMOptions=--deterministic --optimization-counter-threshold=90 |
| // VMOptions=--use-slow-path |
| // VMOptions=--use-slow-path --stacktrace-every=100 |
| |
| import 'dart:ffi'; |
| import 'dart:typed_data'; |
| |
| import 'package:expect/expect.dart'; |
| import 'package:ffi/ffi.dart'; |
| |
| const int arrayLength = 5; |
| const int deeplyNestedArrayLength = 2; |
| |
| void main() { |
| // Loop enough to trigger optimizations or stacktraces. See "VMOptions" above. |
| for (int i = 0; i < 100; ++i) { |
| testPointerArrayElements(); |
| testMallocedPointerArrayElements(); |
| testWriteToPointerArrayElements(); |
| testStructArrayElements(); |
| testMallocedStructArrayElements(); |
| testWriteToStructArrayElements(); |
| testUnionArrayElements(); |
| testMallocedUnionArrayElements(); |
| testWriteToUnionArrayElements(); |
| testArrayArrayElements(); |
| testMallocedArrayArrayElements(); |
| testWriteToArrayArrayElements(); |
| testDeeplyNestedArrayElements(); |
| testMallocedDeeplyNestedArrayElements(); |
| } |
| } |
| |
| final class TestStruct extends Struct { |
| // Placeholder value before array to test the offset calculation logic. |
| @Int8() |
| external int placeholder; |
| |
| @Array(arrayLength) |
| external Array<Pointer<Int8>> pointerArray; |
| |
| @Array(arrayLength) |
| external Array<MyStruct> structArray; |
| |
| @Array(arrayLength) |
| external Array<MyUnion> unionArray; |
| |
| @Array(arrayLength, arrayLength) |
| external Array<Array<Int8>> arrayArray; |
| |
| @Array( |
| deeplyNestedArrayLength, |
| deeplyNestedArrayLength, |
| deeplyNestedArrayLength, |
| deeplyNestedArrayLength, |
| ) |
| external Array<Array<Array<Array<Int8>>>> deplyNestedArray; |
| } |
| |
| final class MyStruct extends Struct { |
| @Int8() |
| external int structValue; |
| } |
| |
| final class MyUnion extends Union { |
| @Int32() |
| external int unionAlt1; |
| |
| @Float() |
| external double unionAlt2; |
| } |
| |
| void testPointerArrayElements() { |
| final struct = Struct.create<TestStruct>(); |
| final array = struct.pointerArray; |
| final expected = <Pointer<Int8>>[]; |
| for (int i = 0; i < arrayLength; i++) { |
| Pointer<Int8> intPointer = malloc<Int8>()..value = 100 + i; |
| array[i] = intPointer; |
| expected.add(intPointer); |
| } |
| Expect.listEquals(expected, array.elements); |
| expected.forEach(malloc.free); |
| } |
| |
| void testMallocedPointerArrayElements() { |
| final struct = malloc<TestStruct>(); |
| final array = struct.ref.pointerArray; |
| final expected = <Pointer<Int8>>[]; |
| for (int i = 0; i < arrayLength; i++) { |
| Pointer<Int8> intPointer = malloc<Int8>()..value = 100 + i; |
| array[i] = intPointer; |
| expected.add(intPointer); |
| } |
| Expect.listEquals(expected, array.elements); |
| expected.forEach(malloc.free); |
| malloc.free(struct); |
| } |
| |
| void testWriteToPointerArrayElements() { |
| final struct = Struct.create<TestStruct>(); |
| final array = struct.pointerArray; |
| final expected = <Pointer<Int8>>[]; |
| for (int i = 0; i < arrayLength; i++) { |
| Pointer<Int8> intPointer = malloc<Int8>()..value = 100 + i; |
| array.elements[i] = intPointer; |
| expected.add(intPointer); |
| } |
| Expect.listEquals(expected, array.elements); |
| final actual = <Pointer<Int8>>[]; |
| for (int i = 0; i < arrayLength; i++) { |
| actual.add(array[i]); |
| } |
| Expect.listEquals(expected, actual); |
| expected.forEach(malloc.free); |
| } |
| |
| void testStructArrayElements() { |
| final struct = Struct.create<TestStruct>(); |
| final array = struct.structArray; |
| final expected = <int>[]; |
| for (int i = 0; i < arrayLength; i++) { |
| int value = 100 + i; |
| array[i].structValue = value; |
| expected.add(value); |
| } |
| Expect.listEquals(expected, [ |
| for (var element in array.elements) element.structValue, |
| ]); |
| } |
| |
| void testMallocedStructArrayElements() { |
| final struct = malloc<TestStruct>(); |
| final array = struct.ref.structArray; |
| final expected = <int>[]; |
| for (int i = 0; i < arrayLength; i++) { |
| int value = 100 + i; |
| array[i].structValue = value; |
| expected.add(value); |
| } |
| Expect.listEquals(expected, [ |
| for (var element in array.elements) element.structValue, |
| ]); |
| malloc.free(struct); |
| } |
| |
| void testWriteToStructArrayElements() { |
| final struct = Struct.create<TestStruct>(); |
| final array = struct.structArray; |
| final myStruct = Struct.create<MyStruct>(); |
| for (int i = 0; i < arrayLength; i++) { |
| final e = Expect.throwsUnsupportedError(() { |
| array.elements[i] = myStruct; |
| }); |
| Expect.isTrue(e.message!.contains('Cannot modify an unmodifiable list')); |
| } |
| } |
| |
| void testUnionArrayElements() { |
| final struct = Struct.create<TestStruct>(); |
| final array = struct.unionArray; |
| final expected = <int>[]; |
| for (int i = 0; i < arrayLength; i++) { |
| int value = 100 + i; |
| array[i].unionAlt1 = value; |
| expected.add(value); |
| } |
| Expect.listEquals(expected, [ |
| for (var element in array.elements) element.unionAlt1, |
| ]); |
| } |
| |
| void testMallocedUnionArrayElements() { |
| final struct = malloc<TestStruct>(); |
| final array = struct.ref.unionArray; |
| final expected = <int>[]; |
| for (int i = 0; i < arrayLength; i++) { |
| int value = 100 + i; |
| array[i].unionAlt1 = value; |
| expected.add(value); |
| } |
| Expect.listEquals(expected, [ |
| for (var element in array.elements) element.unionAlt1, |
| ]); |
| malloc.free(struct); |
| } |
| |
| void testWriteToUnionArrayElements() { |
| final struct = Struct.create<TestStruct>(); |
| final array = struct.unionArray; |
| final myUnion = Union.create<MyUnion>(); |
| for (int i = 0; i < arrayLength; i++) { |
| final e = Expect.throwsUnsupportedError(() { |
| array.elements[i] = myUnion; |
| }); |
| Expect.isTrue(e.message!.contains('Cannot modify an unmodifiable list')); |
| } |
| } |
| |
| void testArrayArrayElements() { |
| final struct = Struct.create<TestStruct>(); |
| final array = struct.arrayArray; |
| final expected = <List<int>>[]; |
| for (int i = 0; i < arrayLength; i++) { |
| final values = <int>[]; |
| for (int j = 0; j < arrayLength; j++) { |
| int value = (10 * i) + j; |
| array[i][j] = value; |
| values.add(value); |
| } |
| expected.add(values); |
| } |
| Expect.deepEquals(expected, [ |
| for (var element in array.elements) element.elements, |
| ]); |
| } |
| |
| void testMallocedArrayArrayElements() { |
| final struct = malloc<TestStruct>(); |
| final array = struct.ref.arrayArray; |
| final expected = <List<int>>[]; |
| for (int i = 0; i < arrayLength; i++) { |
| final values = <int>[]; |
| for (int j = 0; j < arrayLength; j++) { |
| int value = (10 * i) + j; |
| array[i][j] = value; |
| values.add(value); |
| } |
| expected.add(values); |
| } |
| Expect.deepEquals(expected, [ |
| for (var element in array.elements) element.elements, |
| ]); |
| malloc.free(struct); |
| } |
| |
| void testWriteToArrayArrayElements() { |
| final struct = Struct.create<TestStruct>(); |
| final array = struct.arrayArray; |
| final source = Struct.create<TestStruct>().arrayArray; |
| for (int i = 0; i < arrayLength; i++) { |
| for (int j = 0; j < arrayLength; j++) { |
| source[i][j] = (10 * i) + j; |
| } |
| array.elements[i] = source[i]; |
| } |
| for (int i = 0; i < arrayLength; i++) { |
| final actual = <int>[]; |
| for (int j = 0; j < arrayLength; j++) { |
| actual.add(array[i][j]); |
| } |
| Expect.listEquals(source[i].elements, actual); |
| Expect.listEquals(source[i].elements, array[i].elements); |
| } |
| } |
| |
| void testDeeplyNestedArrayElements() { |
| final struct = Struct.create<TestStruct>(); |
| final array = struct.deplyNestedArray; |
| final expected = <int>[]; |
| |
| for (int a = 0; a < deeplyNestedArrayLength; a++) { |
| for (int b = 0; b < deeplyNestedArrayLength; b++) { |
| for (int c = 0; c < deeplyNestedArrayLength; c++) { |
| for (int d = 0; d < deeplyNestedArrayLength; d++) { |
| int value = a + b + c + d; |
| array[a][b][c][d] = value; |
| expected.add(value); |
| } |
| } |
| } |
| } |
| |
| final actual = <int>[]; |
| for (var a in array.elements) { |
| for (var b in a.elements) { |
| for (var c in b.elements) { |
| for (var d in c.elements) { |
| actual.add(d); |
| } |
| } |
| } |
| } |
| |
| Expect.listEquals(expected, actual); |
| } |
| |
| void testMallocedDeeplyNestedArrayElements() { |
| final struct = malloc<TestStruct>(); |
| final array = struct.ref.deplyNestedArray; |
| final expected = <int>[]; |
| for (int a = 0; a < deeplyNestedArrayLength; a++) { |
| for (int b = 0; b < deeplyNestedArrayLength; b++) { |
| for (int c = 0; c < deeplyNestedArrayLength; c++) { |
| for (int d = 0; d < deeplyNestedArrayLength; d++) { |
| int value = a + b + c + d; |
| array[a][b][c][d] = value; |
| expected.add(value); |
| } |
| } |
| } |
| } |
| |
| final actual = <int>[]; |
| for (var a in array.elements) { |
| for (var b in a.elements) { |
| for (var c in b.elements) { |
| for (var d in c.elements) { |
| actual.add(d); |
| } |
| } |
| } |
| } |
| |
| Expect.listEquals(expected, actual); |
| malloc.free(struct); |
| } |