// 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 '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> pointerArray; @Array(arrayLength) external Array structArray; @Array(arrayLength) external Array unionArray; @Array(arrayLength, arrayLength) external Array> arrayArray; @Array( deeplyNestedArrayLength, deeplyNestedArrayLength, deeplyNestedArrayLength, deeplyNestedArrayLength, ) external Array>>> 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(); final array = struct.pointerArray; final expected = >[]; for (int i = 0; i < arrayLength; i++) { Pointer intPointer = malloc()..value = 100 + i; array[i] = intPointer; expected.add(intPointer); } Expect.listEquals(expected, array.elements); expected.forEach(malloc.free); } void testMallocedPointerArrayElements() { final struct = malloc(); final array = struct.ref.pointerArray; final expected = >[]; for (int i = 0; i < arrayLength; i++) { Pointer intPointer = malloc()..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(); final array = struct.pointerArray; final expected = >[]; for (int i = 0; i < arrayLength; i++) { Pointer intPointer = malloc()..value = 100 + i; array.elements[i] = intPointer; expected.add(intPointer); } Expect.listEquals(expected, array.elements); final actual = >[]; 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(); final array = struct.structArray; final expected = []; 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(); final array = struct.ref.structArray; final expected = []; 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(); final array = struct.structArray; final myStruct = Struct.create(); 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(); final array = struct.unionArray; final expected = []; 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(); final array = struct.ref.unionArray; final expected = []; 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(); final array = struct.unionArray; final myUnion = Union.create(); 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(); final array = struct.arrayArray; final expected = >[]; for (int i = 0; i < arrayLength; i++) { final values = []; 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(); final array = struct.ref.arrayArray; final expected = >[]; for (int i = 0; i < arrayLength; i++) { final values = []; 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(); final array = struct.arrayArray; final source = Struct.create().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 = []; 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(); final array = struct.deplyNestedArray; final expected = []; 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 = []; 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(); final array = struct.ref.deplyNestedArray; final expected = []; 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 = []; 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); }