c326065c3f
For the arrays of the Int/Uint/Float/Double types this has been implemented straight in the ffi_patch.dart file and `elements` returns a typed-data list view on the original data backing the array. The Bool and Pointer arrays return helper classes from their `elements` getter. These helper classes implement the `List` interface utilizing the `operator []` implementation of the underlying arrays. This is also implemented directly in the ffi_patch.dart file. The `elements` getter for array arrays is rewritten in the CFE (use_sites.dart) to instantiate a helper class to which the size of a single element of the array is passed (this information is not available in ffi_patch.dart, hence the rewrite in the CFE). The helper class implements the `List` interface and performs some pointer arithmetic with the provided element size to implement its methods. The `elements` getters for struct and union arrays are also rewritten in the CFE to instantiate a helper class to which a constructor tearoff for instantiating the underlying struct/union type is passed. The helper class also implements the `List` interface and uses the provided constructor tearoff to create instantiated structs/unions of the elements in the list. Last, but not least, the `elements` getter for abi-specific integer arrays is also rewritten in the CFI to instantiate a helper class to which a closure is provided to load abi specific integers from the underlying array data structure. TEST=tests/ffi/array_compound_elements_test.dart TEST=tests/ffi/array_primitive_elements_generated_test.dart CoreLibraryReviewExempt: Dart VM only. Bug: https://github.com/dart-lang/sdk/issues/45508 Change-Id: I211a42174057c39632c6a363d4e8e6fa8e94e801 Cq-Include-Trybots: dart/try:vm-aot-android-release-arm64c-try,vm-aot-android-release-arm_x64-try,vm-aot-asan-linux-release-x64-try,vm-aot-linux-debug-x64-try,vm-aot-linux-debug-x64c-try,vm-aot-mac-release-arm64-try,vm-aot-mac-release-x64-try,vm-aot-msan-linux-release-x64-try,vm-aot-obfuscate-linux-release-x64-try,vm-aot-optimization-level-linux-release-x64-try,vm-aot-tsan-linux-release-x64-try,vm-aot-ubsan-linux-release-x64-try,vm-aot-win-debug-arm64-try,vm-aot-win-debug-x64-try,vm-aot-win-debug-x64c-try,vm-appjit-linux-debug-x64-try,vm-asan-linux-release-arm64-try,vm-asan-linux-release-x64-try,vm-checked-mac-release-arm64-try,vm-eager-optimization-linux-release-ia32-try,vm-eager-optimization-linux-release-x64-try,vm-ffi-android-debug-arm-try,vm-ffi-android-debug-arm64c-try,vm-ffi-mac-debug-simarm64_arm64-try,vm-ffi-qemu-linux-release-arm-try,vm-ffi-qemu-linux-release-riscv64-try,vm-fuchsia-release-arm64-try,vm-fuchsia-release-x64-try,vm-linux-debug-ia32-try,vm-linux-debug-x64-try,vm-linux-debug-x64c-try,vm-mac-debug-arm64-try,vm-mac-debug-x64-try,vm-msan-linux-release-arm64-try,vm-msan-linux-release-x64-try,vm-reload-linux-debug-x64-try,vm-reload-rollback-linux-debug-x64-try,vm-tsan-linux-release-arm64-try,vm-tsan-linux-release-x64-try,vm-ubsan-linux-release-arm64-try,vm-ubsan-linux-release-x64-try,vm-win-debug-arm64-try,vm-win-debug-x64-try,vm-win-debug-x64c-try,vm-win-release-ia32-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/414821 Commit-Queue: Daco Harkes <dacoharkes@google.com> Reviewed-by: Daco Harkes <dacoharkes@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com> Reviewed-by: Lasse Nielsen <lrn@google.com>
324 lines
8.7 KiB
Dart
324 lines
8.7 KiB
Dart
// 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);
|
|
}
|