blob: 2a0d5081e29f43a9c0b6c670345a639adb187bb9 [file] [log] [blame]
// 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);
}