[test/ffi] Test generator pointer arguments
This CL extends the test generator to support struct pointers. For
callbacks, only synchronous use of pointers is supported in tests.
(Ownership of memory is _not_ passed to Dart.) We have plenty of other
tests passing ownership. These tests are meant to check the ABIs, for
which sync calls and callbacks suffice.
Split off https://dart-review.googlesource.com/c/sdk/+/371960 to make
the changes in that CL only relate to variable length arrays. That CL
will pass pointers to structs with variable length arrays.
TEST=tests/ffi/*
Change-Id: Ib8ff7b4e1d0f2451892ea693555803682bbf3bc0
tools/find_builders.dart ffi/function_structs_by_value_generated_args_test
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-ffi-android-debug-arm-try,vm-ffi-android-debug-arm64c-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/+/378704
Reviewed-by: Tess Strickland <sstrickl@google.com>
Reviewed-by: Liam Appelbe <liama@google.com>
Commit-Queue: Daco Harkes <dacoharkes@google.com>
diff --git a/runtime/bin/ffi_test/ffi_test_functions_generated.cc b/runtime/bin/ffi_test/ffi_test_functions_generated.cc
index 9be77c1..9b0d891 100644
--- a/runtime/bin/ffi_test/ffi_test_functions_generated.cc
+++ b/runtime/bin/ffi_test/ffi_test_functions_generated.cc
@@ -4876,6 +4876,25 @@
}
// Used for testing structs and unions by value.
+// Passing a pointer to a struct
+DART_EXPORT int64_t
+PassPointerStruct12BytesHomogeneousInt32(Struct12BytesHomogeneousInt32* a0) {
+ std::cout << "PassPointerStruct12BytesHomogeneousInt32"
+ << "((" << a0->a0 << ", " << a0->a1 << ", " << a0->a2 << "))"
+ << "\n";
+
+ int64_t result = 0;
+
+ result += a0->a0;
+ result += a0->a1;
+ result += a0->a2;
+
+ std::cout << "result = " << result << "\n";
+
+ return result;
+}
+
+// Used for testing structs and unions by value.
// Smallest struct with data.
DART_EXPORT Struct1ByteInt ReturnStruct1ByteInt(int8_t a0) {
std::cout << "ReturnStruct1ByteInt"
@@ -12607,6 +12626,45 @@
}
// Used for testing structs and unions by value.
+// Passing a pointer to a struct
+DART_EXPORT intptr_t TestPassPointerStruct12BytesHomogeneousInt32(
+ // NOLINTNEXTLINE(whitespace/parens)
+ int64_t (*f)(Struct12BytesHomogeneousInt32* a0)) {
+ Struct12BytesHomogeneousInt32 a0_value = {};
+ Struct12BytesHomogeneousInt32* a0 = &a0_value;
+
+ a0->a0 = -1;
+ a0->a1 = 2;
+ a0->a2 = -3;
+
+ std::cout << "Calling TestPassPointerStruct12BytesHomogeneousInt32("
+ << "((" << a0->a0 << ", " << a0->a1 << ", " << a0->a2 << "))"
+ << ")\n";
+
+ int64_t result = f(a0);
+
+ std::cout << "result = " << result << "\n";
+
+ CHECK_EQ(-2, result);
+
+ // Pass argument that will make the Dart callback throw.
+ a0->a0 = 42;
+
+ result = f(a0);
+
+ CHECK_EQ(0, result);
+
+ // Pass argument that will make the Dart callback return null.
+ a0->a0 = 84;
+
+ result = f(a0);
+
+ CHECK_EQ(0, result);
+
+ return 0;
+}
+
+// Used for testing structs and unions by value.
// Smallest struct with data.
DART_EXPORT intptr_t TestReturnStruct1ByteInt(
// NOLINTNEXTLINE(whitespace/parens)
@@ -21225,6 +21283,25 @@
}
// Used for testing structs and unions by value.
+// Passing a pointer to a struct
+DART_EXPORT void TestAsyncPassPointerStruct12BytesHomogeneousInt32(
+ // NOLINTNEXTLINE(whitespace/parens)
+ void (*f)(Struct12BytesHomogeneousInt32* a0)) {
+ Struct12BytesHomogeneousInt32 a0_value = {};
+ Struct12BytesHomogeneousInt32* a0 = &a0_value;
+
+ a0->a0 = -1;
+ a0->a1 = 2;
+ a0->a2 = -3;
+
+ std::cout << "Calling TestAsyncPassPointerStruct12BytesHomogeneousInt32("
+ << "((" << a0->a0 << ", " << a0->a1 << ", " << a0->a2 << "))"
+ << ")\n";
+
+ f(a0);
+}
+
+// Used for testing structs and unions by value.
// Smallest struct with data.
DART_EXPORT void TestAsyncReturnStruct1ByteInt(
// NOLINTNEXTLINE(whitespace/parens)
diff --git a/tests/ffi/async_callback_tests_utils.dart b/tests/ffi/async_callback_tests_utils.dart
index 65f63f2..e943c29 100644
--- a/tests/ffi/async_callback_tests_utils.dart
+++ b/tests/ffi/async_callback_tests_utils.dart
@@ -35,3 +35,5 @@
}
void noChecks() {}
+
+Future<void> noChecksAsync() async {}
diff --git a/tests/ffi/function_callbacks_structs_by_value_generated_test.dart b/tests/ffi/function_callbacks_structs_by_value_generated_test.dart
index 3e785e1..147910e 100644
--- a/tests/ffi/function_callbacks_structs_by_value_generated_test.dart
+++ b/tests/ffi/function_callbacks_structs_by_value_generated_test.dart
@@ -378,6 +378,11 @@
passInt64x7Struct12BytesHomogeneousInt32, 0),
passInt64x7Struct12BytesHomogeneousInt32AfterCallback),
CallbackTest.withCheck(
+ "PassPointerStruct12BytesHomogeneousInt32",
+ Pointer.fromFunction<PassPointerStruct12BytesHomogeneousInt32Type>(
+ passPointerStruct12BytesHomogeneousInt32, 0),
+ noChecks),
+ CallbackTest.withCheck(
"ReturnStruct1ByteInt",
Pointer.fromFunction<ReturnStruct1ByteIntType>(returnStruct1ByteInt),
returnStruct1ByteIntAfterCallback),
@@ -7922,6 +7927,49 @@
Expect.equals(5, result);
}
+typedef PassPointerStruct12BytesHomogeneousInt32Type = Int64 Function(
+ Pointer<Struct12BytesHomogeneousInt32>);
+
+// Global variables to be able to test inputs after callback returned.
+Pointer<Struct12BytesHomogeneousInt32>
+ passPointerStruct12BytesHomogeneousInt32_a0 = nullptr;
+
+// Result variable also global, so we can delete it after the callback.
+int passPointerStruct12BytesHomogeneousInt32Result = 0;
+
+int passPointerStruct12BytesHomogeneousInt32CalculateResult() {
+ int result = 0;
+
+ result += passPointerStruct12BytesHomogeneousInt32_a0.ref.a0;
+ result += passPointerStruct12BytesHomogeneousInt32_a0.ref.a1;
+ result += passPointerStruct12BytesHomogeneousInt32_a0.ref.a2;
+
+ passPointerStruct12BytesHomogeneousInt32Result = result;
+
+ return result;
+}
+
+/// Passing a pointer to a struct
+int passPointerStruct12BytesHomogeneousInt32(
+ Pointer<Struct12BytesHomogeneousInt32> a0) {
+ print("passPointerStruct12BytesHomogeneousInt32(${a0})");
+
+ // Possibly throw.
+ if (a0.ref.a0 == 42 || a0.ref.a0 == 84) {
+ print("throwing!");
+ throw Exception(
+ "PassPointerStruct12BytesHomogeneousInt32 throwing on purpose!");
+ }
+
+ passPointerStruct12BytesHomogeneousInt32_a0 = a0;
+
+ final result = passPointerStruct12BytesHomogeneousInt32CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
typedef ReturnStruct1ByteIntType = Struct1ByteInt Function(Int8);
// Global variables to be able to test inputs after callback returned.
diff --git a/tests/ffi/function_callbacks_structs_by_value_native_callable_generated_test.dart b/tests/ffi/function_callbacks_structs_by_value_native_callable_generated_test.dart
index 7ad47ec..47aa721 100644
--- a/tests/ffi/function_callbacks_structs_by_value_native_callable_generated_test.dart
+++ b/tests/ffi/function_callbacks_structs_by_value_native_callable_generated_test.dart
@@ -452,6 +452,12 @@
exceptionalReturn: 0),
passInt64x7Struct12BytesHomogeneousInt32AfterCallback),
CallbackTest.withCheck(
+ "PassPointerStruct12BytesHomogeneousInt32",
+ NativeCallable<PassPointerStruct12BytesHomogeneousInt32Type>.isolateLocal(
+ passPointerStruct12BytesHomogeneousInt32,
+ exceptionalReturn: 0),
+ noChecks),
+ CallbackTest.withCheck(
"ReturnStruct1ByteInt",
NativeCallable<ReturnStruct1ByteIntType>.isolateLocal(
returnStruct1ByteInt),
@@ -8006,6 +8012,49 @@
Expect.equals(5, result);
}
+typedef PassPointerStruct12BytesHomogeneousInt32Type = Int64 Function(
+ Pointer<Struct12BytesHomogeneousInt32>);
+
+// Global variables to be able to test inputs after callback returned.
+Pointer<Struct12BytesHomogeneousInt32>
+ passPointerStruct12BytesHomogeneousInt32_a0 = nullptr;
+
+// Result variable also global, so we can delete it after the callback.
+int passPointerStruct12BytesHomogeneousInt32Result = 0;
+
+int passPointerStruct12BytesHomogeneousInt32CalculateResult() {
+ int result = 0;
+
+ result += passPointerStruct12BytesHomogeneousInt32_a0.ref.a0;
+ result += passPointerStruct12BytesHomogeneousInt32_a0.ref.a1;
+ result += passPointerStruct12BytesHomogeneousInt32_a0.ref.a2;
+
+ passPointerStruct12BytesHomogeneousInt32Result = result;
+
+ return result;
+}
+
+/// Passing a pointer to a struct
+int passPointerStruct12BytesHomogeneousInt32(
+ Pointer<Struct12BytesHomogeneousInt32> a0) {
+ print("passPointerStruct12BytesHomogeneousInt32(${a0})");
+
+ // Possibly throw.
+ if (a0.ref.a0 == 42 || a0.ref.a0 == 84) {
+ print("throwing!");
+ throw Exception(
+ "PassPointerStruct12BytesHomogeneousInt32 throwing on purpose!");
+ }
+
+ passPointerStruct12BytesHomogeneousInt32_a0 = a0;
+
+ final result = passPointerStruct12BytesHomogeneousInt32CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
typedef ReturnStruct1ByteIntType = Struct1ByteInt Function(Int8);
// Global variables to be able to test inputs after callback returned.
diff --git a/tests/ffi/function_structs_by_value_generated_args_leaf_test.dart b/tests/ffi/function_structs_by_value_generated_args_leaf_test.dart
index 3c5ea10..9b7ba35 100644
--- a/tests/ffi/function_structs_by_value_generated_args_leaf_test.dart
+++ b/tests/ffi/function_structs_by_value_generated_args_leaf_test.dart
@@ -92,6 +92,7 @@
testPassUint8Struct1ByteBoolLeaf();
testPassWCharStructInlineArrayIntUintPtrx2LongUnsignedLeaf();
testPassInt64x7Struct12BytesHomogeneousInt32Leaf();
+ testPassPointerStruct12BytesHomogeneousInt32Leaf();
}
}
@@ -5498,3 +5499,27 @@
calloc.free(a7Pointer);
}
+
+final passPointerStruct12BytesHomogeneousInt32Leaf =
+ ffiTestFunctions.lookupFunction<
+ Int64 Function(Pointer<Struct12BytesHomogeneousInt32>),
+ int Function(Pointer<Struct12BytesHomogeneousInt32>)>(
+ "PassPointerStruct12BytesHomogeneousInt32",
+ isLeaf: true);
+
+/// Passing a pointer to a struct
+void testPassPointerStruct12BytesHomogeneousInt32Leaf() {
+ final a0 = calloc<Struct12BytesHomogeneousInt32>();
+
+ a0.ref.a0 = -1;
+ a0.ref.a1 = 2;
+ a0.ref.a2 = -3;
+
+ final result = passPointerStruct12BytesHomogeneousInt32Leaf(a0);
+
+ print("result = $result");
+
+ Expect.equals(-2, result);
+
+ calloc.free(a0);
+}
diff --git a/tests/ffi/function_structs_by_value_generated_args_native_leaf_test.dart b/tests/ffi/function_structs_by_value_generated_args_native_leaf_test.dart
index 91089ce..012b6a5 100644
--- a/tests/ffi/function_structs_by_value_generated_args_native_leaf_test.dart
+++ b/tests/ffi/function_structs_by_value_generated_args_native_leaf_test.dart
@@ -95,6 +95,7 @@
testPassUint8Struct1ByteBoolNativeLeaf();
testPassWCharStructInlineArrayIntUintPtrx2LongUnsignedNativeLeaf();
testPassInt64x7Struct12BytesHomogeneousInt32NativeLeaf();
+ testPassPointerStruct12BytesHomogeneousInt32NativeLeaf();
}
}
@@ -5462,3 +5463,25 @@
calloc.free(a7Pointer);
}
+
+@Native<Int64 Function(Pointer<Struct12BytesHomogeneousInt32>)>(
+ symbol: 'PassPointerStruct12BytesHomogeneousInt32', isLeaf: true)
+external int passPointerStruct12BytesHomogeneousInt32NativeLeaf(
+ Pointer<Struct12BytesHomogeneousInt32> a0);
+
+/// Passing a pointer to a struct
+void testPassPointerStruct12BytesHomogeneousInt32NativeLeaf() {
+ final a0 = calloc<Struct12BytesHomogeneousInt32>();
+
+ a0.ref.a0 = -1;
+ a0.ref.a1 = 2;
+ a0.ref.a2 = -3;
+
+ final result = passPointerStruct12BytesHomogeneousInt32NativeLeaf(a0);
+
+ print("result = $result");
+
+ Expect.equals(-2, result);
+
+ calloc.free(a0);
+}
diff --git a/tests/ffi/function_structs_by_value_generated_args_native_test.dart b/tests/ffi/function_structs_by_value_generated_args_native_test.dart
index c846e5c..368e0d7 100644
--- a/tests/ffi/function_structs_by_value_generated_args_native_test.dart
+++ b/tests/ffi/function_structs_by_value_generated_args_native_test.dart
@@ -95,6 +95,7 @@
testPassUint8Struct1ByteBoolNative();
testPassWCharStructInlineArrayIntUintPtrx2LongUnsignedNative();
testPassInt64x7Struct12BytesHomogeneousInt32Native();
+ testPassPointerStruct12BytesHomogeneousInt32Native();
}
}
@@ -5472,3 +5473,25 @@
calloc.free(a7Pointer);
}
+
+@Native<Int64 Function(Pointer<Struct12BytesHomogeneousInt32>)>(
+ symbol: 'PassPointerStruct12BytesHomogeneousInt32')
+external int passPointerStruct12BytesHomogeneousInt32Native(
+ Pointer<Struct12BytesHomogeneousInt32> a0);
+
+/// Passing a pointer to a struct
+void testPassPointerStruct12BytesHomogeneousInt32Native() {
+ final a0 = calloc<Struct12BytesHomogeneousInt32>();
+
+ a0.ref.a0 = -1;
+ a0.ref.a1 = 2;
+ a0.ref.a2 = -3;
+
+ final result = passPointerStruct12BytesHomogeneousInt32Native(a0);
+
+ print("result = $result");
+
+ Expect.equals(-2, result);
+
+ calloc.free(a0);
+}
diff --git a/tests/ffi/function_structs_by_value_generated_args_test.dart b/tests/ffi/function_structs_by_value_generated_args_test.dart
index ba7ae69..6536956 100644
--- a/tests/ffi/function_structs_by_value_generated_args_test.dart
+++ b/tests/ffi/function_structs_by_value_generated_args_test.dart
@@ -92,6 +92,7 @@
testPassUint8Struct1ByteBool();
testPassWCharStructInlineArrayIntUintPtrx2LongUnsigned();
testPassInt64x7Struct12BytesHomogeneousInt32();
+ testPassPointerStruct12BytesHomogeneousInt32();
}
}
@@ -5420,3 +5421,26 @@
calloc.free(a7Pointer);
}
+
+final passPointerStruct12BytesHomogeneousInt32 =
+ ffiTestFunctions.lookupFunction<
+ Int64 Function(Pointer<Struct12BytesHomogeneousInt32>),
+ int Function(Pointer<Struct12BytesHomogeneousInt32>)>(
+ "PassPointerStruct12BytesHomogeneousInt32");
+
+/// Passing a pointer to a struct
+void testPassPointerStruct12BytesHomogeneousInt32() {
+ final a0 = calloc<Struct12BytesHomogeneousInt32>();
+
+ a0.ref.a0 = -1;
+ a0.ref.a1 = 2;
+ a0.ref.a2 = -3;
+
+ final result = passPointerStruct12BytesHomogeneousInt32(a0);
+
+ print("result = $result");
+
+ Expect.equals(-2, result);
+
+ calloc.free(a0);
+}
diff --git a/tests/ffi/generator/c_types.dart b/tests/ffi/generator/c_types.dart
index 4afd2a2..9bc255e 100644
--- a/tests/ffi/generator/c_types.dart
+++ b/tests/ffi/generator/c_types.dart
@@ -588,7 +588,9 @@
}
for (final group in argumentsGrouped) {
- result += group.first.type.dartCType;
+ final dartCType =
+ group.first.type.dartCType.replaceAll('<', '').replaceAll('>', '');
+ result += dartCType;
if (group.length > 1) {
result += "x${group.length}";
}
@@ -606,8 +608,22 @@
}
extension MemberList on List<Member> {
- bool get containsComposites =>
- map((m) => m.type is CompositeType).contains(true);
+ bool get containsComposites => map((m) {
+ final type = m.type;
+ switch (type) {
+ case CompositeType _:
+ return true;
+ case PointerType _:
+ final pointerTo = type.pointerTo;
+ switch (pointerTo) {
+ case CompositeType _:
+ return true;
+ }
+ }
+ return false;
+ }).contains(true);
+
+ bool get containsPointers => any((m) => m.type is PointerType);
}
extension ListT<T> on List<T> {
diff --git a/tests/ffi/generator/structs_by_value_tests_configuration.dart b/tests/ffi/generator/structs_by_value_tests_configuration.dart
index d1a0043..f217b7e 100644
--- a/tests/ffi/generator/structs_by_value_tests_configuration.dart
+++ b/tests/ffi/generator/structs_by_value_tests_configuration.dart
@@ -450,6 +450,13 @@
int64,
"""
Struct stradles last argument register"""),
+ FunctionType(
+ [
+ PointerType(struct12bytesInt),
+ ],
+ int64,
+ """
+Passing a pointer to a struct"""),
];
/// Functions that return a struct by value.
diff --git a/tests/ffi/generator/structs_by_value_tests_generator.dart b/tests/ffi/generator/structs_by_value_tests_generator.dart
index 1b72f51..077a9e3 100644
--- a/tests/ffi/generator/structs_by_value_tests_generator.dart
+++ b/tests/ffi/generator/structs_by_value_tests_generator.dart
@@ -63,6 +63,14 @@
final this_ = this as StructType;
return this_.members.coutExpression("$variableName.");
+ case PointerType:
+ final this_ = this as PointerType;
+ final pointerTo = this_.pointerTo;
+ switch (pointerTo) {
+ case StructType _:
+ return pointerTo.members.coutExpression("$variableName->");
+ }
+
case UnionType:
final this_ = this as UnionType;
return this_.members.take(1).toList().coutExpression("$variableName.");
@@ -105,7 +113,7 @@
/// A list of statements adding all members recursively to `result`.
///
/// Both valid in Dart and C.
- String addToResultStatements(String variableName) {
+ String addToResultStatements(String variableName, bool isDart) {
switch (this.runtimeType) {
case FundamentalType:
final this_ = this as FundamentalType;
@@ -114,20 +122,33 @@
case StructType:
final this_ = this as StructType;
- return this_.members.addToResultStatements("$variableName.");
+ return this_.members.addToResultStatements(isDart, "$variableName.");
+
+ case PointerType:
+ final this_ = this as PointerType;
+ final pointerTo = this_.pointerTo;
+ switch (pointerTo) {
+ case StructType _:
+ if (isDart) {
+ return pointerTo.members
+ .addToResultStatements(isDart, "$variableName.ref.");
+ }
+ return pointerTo.members
+ .addToResultStatements(isDart, "$variableName->");
+ }
case UnionType:
final this_ = this as UnionType;
final member = this_.members.first;
return member.type
- .addToResultStatements("$variableName.${member.name}");
+ .addToResultStatements("$variableName.${member.name}", isDart);
case FixedLengthArrayType:
final this_ = this as FixedLengthArrayType;
final indices = [for (var i = 0; i < this_.length; i += 1) i];
return indices
- .map((i) =>
- this_.elementType.addToResultStatements("$variableName[$i]"))
+ .map((i) => this_.elementType
+ .addToResultStatements("$variableName[$i]", isDart))
.join();
}
@@ -139,8 +160,9 @@
/// A list of statements adding all members recursively to `result`.
///
/// Both valid in Dart and C.
- String addToResultStatements([String namePrefix = ""]) {
- return map((m) => m.type.addToResultStatements("$namePrefix${m.name}"))
+ String addToResultStatements(bool isDart, [String namePrefix = ""]) {
+ return map(
+ (m) => m.type.addToResultStatements("$namePrefix${m.name}", isDart))
.join();
}
}
@@ -149,7 +171,11 @@
/// A list of statements recursively assigning all members with [a].
///
/// Both valid in Dart and C.
- String assignValueStatements(ArgumentValueAssigner a, String variableName) {
+ String assignValueStatements(
+ ArgumentValueAssigner a,
+ String variableName,
+ bool isDart,
+ ) {
switch (this.runtimeType) {
case FundamentalType:
final this_ = this as FundamentalType;
@@ -157,21 +183,33 @@
case StructType:
final this_ = this as StructType;
- return this_.members.assignValueStatements(a, "$variableName.");
+ return this_.members.assignValueStatements(a, isDart, "$variableName.");
case UnionType:
final this_ = this as UnionType;
final member = this_.members.first;
return member.type
- .assignValueStatements(a, "$variableName.${member.name}");
+ .assignValueStatements(a, "$variableName.${member.name}", isDart);
case FixedLengthArrayType:
final this_ = this as FixedLengthArrayType;
final indices = [for (var i = 0; i < this_.length; i += 1) i];
return indices
- .map((i) =>
- this_.elementType.assignValueStatements(a, "$variableName[$i]"))
+ .map((i) => this_.elementType
+ .assignValueStatements(a, "$variableName[$i]", isDart))
.join();
+ case PointerType:
+ final this_ = this as PointerType;
+ final pointerTo = this_.pointerTo;
+ switch (pointerTo) {
+ case StructType _:
+ if (isDart) {
+ return pointerTo.members
+ .assignValueStatements(a, isDart, "$variableName.ref.");
+ }
+ return pointerTo.members
+ .assignValueStatements(a, isDart, "$variableName->");
+ }
}
throw Exception("Not implemented for ${this.runtimeType}");
@@ -198,10 +236,16 @@
/// A list of statements recursively assigning all members with [a].
///
/// Both valid in Dart and C.
- String assignValueStatements(ArgumentValueAssigner a,
- [String namePrefix = ""]) {
- return map((m) => m.type.assignValueStatements(a, "$namePrefix${m.name}"))
- .join();
+ String assignValueStatements(
+ ArgumentValueAssigner a,
+ bool isDart, [
+ String namePrefix = "",
+ ]) {
+ return map((m) => m.type.assignValueStatements(
+ a,
+ "$namePrefix${m.name}",
+ isDart,
+ )).join();
}
/// A list of statements recursively coping all members from [source].
@@ -252,6 +296,17 @@
/// A list of Dart statements recursively allocating all members.
String dartAllocateStatements(String variableName) {
switch (this.runtimeType) {
+ case PointerType:
+ final this_ = this as PointerType;
+ final pointerTo = this_.pointerTo;
+ switch (pointerTo) {
+ case StructType _:
+ return '''
+ final ${variableName} = calloc<${pointerTo.dartType}>();
+ ''';
+ }
+ return "\n";
+
case FundamentalType:
return " ${dartType} ${variableName};\n";
@@ -290,6 +345,9 @@
} else {
return "${dartType} ${variableName} = Pointer<${dartType}>.fromAddress(0).ref;\n";
}
+
+ case PointerType:
+ return "${dartType} ${variableName} = nullptr;\n";
}
throw Exception("Not implemented for ${this.runtimeType}");
@@ -320,6 +378,9 @@
case StructType:
case UnionType:
return "calloc.free(${variableName}Pointer);\n";
+
+ case PointerType:
+ return "calloc.free(${variableName});\n";
}
throw Exception("Not implemented for ${this.runtimeType}");
@@ -342,6 +403,16 @@
case StructType:
case UnionType:
return "${cType} ${variableName} = {};\n";
+ case PointerType:
+ final this_ = this as PointerType;
+ final pointerTo = this_.pointerTo;
+ switch (pointerTo) {
+ case StructType _:
+ return '''
+${pointerTo.cType} ${variableName}_value = {};
+${cType} ${variableName} = &${variableName}_value;
+''';
+ }
}
throw Exception("Not implemented for ${this.runtimeType}");
@@ -474,7 +545,7 @@
/// Expression denoting the first FundamentalType field.
///
/// Both valid in Dart and C.
- String firstArgumentName(String variableName) {
+ String firstArgumentName(String variableName, bool isDart) {
switch (this.runtimeType) {
case FundamentalType:
return variableName;
@@ -482,11 +553,24 @@
case StructType:
case UnionType:
final this_ = this as CompositeType;
- return this_.members.firstArgumentName("$variableName.");
+ return this_.members.firstArgumentName(isDart, "$variableName.");
+
+ case PointerType:
+ final this_ = this as PointerType;
+ final pointerTo = this_.pointerTo;
+ switch (pointerTo) {
+ case StructType _:
+ if (isDart) {
+ return pointerTo.members
+ .firstArgumentName(isDart, "$variableName.ref.");
+ }
+ return pointerTo.members
+ .firstArgumentName(isDart, "$variableName->");
+ }
case FixedLengthArrayType:
final this_ = this as FixedLengthArrayType;
- return this_.elementType.firstArgumentName("$variableName[0]");
+ return this_.elementType.firstArgumentName("$variableName[0]", isDart);
}
throw Exception("Not implemented for ${this.runtimeType}");
@@ -497,8 +581,8 @@
/// Expression denoting the first FundamentalType field.
///
/// Both valid in Dart and C.
- String firstArgumentName([String prefix = ""]) {
- return this[0].type.firstArgumentName("$prefix${this[0].name}");
+ String firstArgumentName(bool isDart, [String prefix = ""]) {
+ return this[0].type.firstArgumentName("$prefix${this[0].name}", isDart);
}
}
@@ -565,7 +649,7 @@
extension on FunctionType {
String dartCallCode({required bool isLeaf, required bool isNative}) {
final a = ArgumentValueAssigner();
- final assignValues = arguments.assignValueStatements(a);
+ final assignValues = arguments.assignValueStatements(a, true);
final argumentFrees = arguments.dartFreeStatements();
final argumentNames = arguments.map((e) => e.name).join(", ");
@@ -639,7 +723,7 @@
buildReturnValue = """
$returnValueType result = 0;
-${arguments.addToResultStatements('${dartName}_')}
+${arguments.addToResultStatements(true, '${dartName}_')}
""";
assignReturnGlobal = "${dartName}Result = $result;";
break;
@@ -664,13 +748,21 @@
final globals = arguments.dartAllocateZeroStatements("${dartName}_");
- final copyToGlobals =
- arguments.map((a) => '${dartName}_${a.name} = ${a.name};').join("\n ");
+ final copyToGlobals = arguments.map((a) {
+ final type = a.type;
+ switch (type) {
+ case PointerType _:
+ // Copy the pointer, but don't use after callback returned.
+ return '${dartName}_${a.name} = ${a.name};';
+ default:
+ return '${dartName}_${a.name} = ${a.name};';
+ }
+ }).join("\n ");
// Simulate assigning values the same way as in C, so that we know what the
// final return value should be.
final a = ArgumentValueAssigner();
- arguments.assignValueStatements(a);
+ arguments.assignValueStatements(a, true);
String afterCallbackExpects = "";
String afterCallbackFrees = "";
switch (testType) {
@@ -711,8 +803,8 @@
print("$dartName($prints)");
// Possibly throw.
- if (${arguments.firstArgumentName()} == $throwExceptionValue ||
- ${arguments.firstArgumentName()} == $returnNullValue) {
+ if (${arguments.firstArgumentName(true)} == $throwExceptionValue ||
+ ${arguments.firstArgumentName(true)} == $returnNullValue) {
print("throwing!");
throw Exception("$cName throwing on purpose!");
}
@@ -726,6 +818,7 @@
return result;
}
+${this.arguments.containsPointers ? '' : '''
void ${dartName}AfterCallback() {
$afterCallbackFrees
@@ -737,6 +830,7 @@
$afterCallbackFrees
}
+'''}
""";
}
@@ -760,10 +854,14 @@
final constructor = isNativeCallable
? 'NativeCallable<$T>.isolateLocal'
: 'Pointer.fromFunction<$T>';
+ final afterCallback = this.arguments.containsPointers
+ ? 'noChecks'
+ : '${dartName}AfterCallback';
+
return """
CallbackTest.withCheck("$cName",
$constructor($dartName$exceptionalReturn),
- ${dartName}AfterCallback),
+ $afterCallback),
""";
}
@@ -778,7 +876,7 @@
FunctionType(argumentTypes, void_, reason, varArgsIndex: varArgsIndex);
final a = ArgumentValueAssigner();
- arguments.assignValueStatements(a);
+ arguments.assignValueStatements(a, true);
final expectedResult = a.sumValue(int64);
return """
@@ -793,7 +891,7 @@
double result = 0;
-${arguments.addToResultStatements()}
+${arguments.addToResultStatements(true)}
print("result = \$result");
${cName}Result.complete(result);
@@ -812,10 +910,13 @@
final T = '${cName}Type';
final constructor =
isAsync ? 'NativeCallable<$T>.listener' : 'Pointer.fromFunction<$T>';
+ final afterCallback = this.arguments.containsPointers
+ ? 'noChecksAsync'
+ : '${dartName}AfterCallback';
return """
AsyncCallbackTest("$cName",
$constructor($dartName),
- ${dartName}AfterCallback),
+ $afterCallback),
""";
}
@@ -833,7 +934,7 @@
body = """
$returnValueType result = 0;
- ${arguments.addToResultStatements()}
+ ${arguments.addToResultStatements(false)}
""";
break;
case TestType.structReturn:
@@ -892,7 +993,7 @@
String get cCallbackCode {
final a = ArgumentValueAssigner();
final argumentAllocations = arguments.cAllocateStatements();
- final assignValues = arguments.assignValueStatements(a);
+ final assignValues = arguments.assignValueStatements(a, false);
final argumentString = [
for (final argument in arguments.take(varArgsIndex ?? arguments.length))
@@ -948,14 +1049,14 @@
$expects
// Pass argument that will make the Dart callback throw.
- ${arguments.firstArgumentName()} = $throwExceptionValue;
+ ${arguments.firstArgumentName(false)} = $throwExceptionValue;
result = f($argumentNames);
$expectsZero
// Pass argument that will make the Dart callback return null.
- ${arguments.firstArgumentName()} = $returnNullValue;
+ ${arguments.firstArgumentName(false)} = $returnNullValue;
result = f($argumentNames);
@@ -970,7 +1071,7 @@
String get cAsyncCallbackCode {
final a = ArgumentValueAssigner();
final argumentAllocations = arguments.cAllocateStatements();
- final assignValues = arguments.assignValueStatements(a);
+ final assignValues = arguments.assignValueStatements(a, false);
final argumentString = [
for (final argument in arguments.take(varArgsIndex ?? arguments.length))
@@ -1245,10 +1346,15 @@
""";
}
-Future<void> writeDartNativeCallableListenerTest(List<FunctionType> functions,
- {required bool isAsync}) async {
+Future<void> writeDartNativeCallableListenerTest(
+ List<FunctionType> functions, {
+ required bool isAsync,
+}) async {
final StringBuffer buffer = StringBuffer();
buffer.write(headerDartAsyncCallbackTest());
+ if (isAsync) {
+ functions = functions.where((f) => !f.arguments.containsPointers).toList();
+ }
final constructors = functions
.map((e) => e.dartNativeCallableListenerTestConstructor(isAsync: isAsync))
.join("\n");
diff --git a/tests/ffi/native_callables_sync_structs_by_value_generated_test.dart b/tests/ffi/native_callables_sync_structs_by_value_generated_test.dart
index cc293ae..66a13fb 100644
--- a/tests/ffi/native_callables_sync_structs_by_value_generated_test.dart
+++ b/tests/ffi/native_callables_sync_structs_by_value_generated_test.dart
@@ -378,6 +378,11 @@
passInt64x7Struct12BytesHomogeneousInt32),
passInt64x7Struct12BytesHomogeneousInt32AfterCallback),
AsyncCallbackTest(
+ "PassPointerStruct12BytesHomogeneousInt32",
+ Pointer.fromFunction<PassPointerStruct12BytesHomogeneousInt32Type>(
+ passPointerStruct12BytesHomogeneousInt32),
+ noChecksAsync),
+ AsyncCallbackTest(
"ReturnStruct1ByteInt",
Pointer.fromFunction<ReturnStruct1ByteIntType>(returnStruct1ByteInt),
returnStruct1ByteIntAfterCallback),
@@ -5135,6 +5140,33 @@
Expect.approxEquals(5, result);
}
+typedef PassPointerStruct12BytesHomogeneousInt32Type = Void Function(
+ Pointer<Struct12BytesHomogeneousInt32>);
+
+// Global variable that stores the result.
+final PassPointerStruct12BytesHomogeneousInt32Result = Completer<double>();
+
+/// Passing a pointer to a struct
+void passPointerStruct12BytesHomogeneousInt32(
+ Pointer<Struct12BytesHomogeneousInt32> a0) {
+ print("passPointerStruct12BytesHomogeneousInt32(${a0})");
+
+ double result = 0;
+
+ result += a0.ref.a0;
+ result += a0.ref.a1;
+ result += a0.ref.a2;
+
+ print("result = $result");
+ PassPointerStruct12BytesHomogeneousInt32Result.complete(result);
+}
+
+Future<void> passPointerStruct12BytesHomogeneousInt32AfterCallback() async {
+ final result = await PassPointerStruct12BytesHomogeneousInt32Result.future;
+ print("after callback result = $result");
+ Expect.approxEquals(-2, result);
+}
+
typedef ReturnStruct1ByteIntType = Void Function(Int8);
// Global variable that stores the result.