Version 2.12.0-168.0.dev
Merge commit 'b6b82dd3ac756b39e7fe9cab21f060fae74e358d' into 'dev'
diff --git a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
index d3dd826..0faba7a 100644
--- a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
@@ -3789,6 +3789,31 @@
}
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<Message Function(String name, List<String> _names)>
+ templateFfiFieldCyclic =
+ const Template<Message Function(String name, List<String> _names)>(
+ messageTemplate: r"""Struct '#name' contains itself. Cycle elements:
+#names""", withArguments: _withArgumentsFfiFieldCyclic);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String name, List<String> _names)>
+ codeFfiFieldCyclic =
+ const Code<Message Function(String name, List<String> _names)>(
+ "FfiFieldCyclic",
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsFfiFieldCyclic(String name, List<String> _names) {
+ if (name.isEmpty) throw 'No name provided';
+ name = demangleMixinApplicationName(name);
+ if (_names.isEmpty) throw 'No names provided';
+ String names = itemizeNames(_names);
+ return new Message(codeFfiFieldCyclic,
+ message: """Struct '${name}' contains itself. Cycle elements:
+${names}""", arguments: {'name': name, 'names': _names});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Template<
Message Function(String name)> templateFfiFieldInitializer = const Template<
Message Function(String name)>(
diff --git a/pkg/analysis_server/lib/src/context_manager.dart b/pkg/analysis_server/lib/src/context_manager.dart
index 62cf759..0f60e67 100644
--- a/pkg/analysis_server/lib/src/context_manager.dart
+++ b/pkg/analysis_server/lib/src/context_manager.dart
@@ -38,6 +38,14 @@
import 'package:watcher/watcher.dart';
import 'package:yaml/yaml.dart';
+/// Enables watching of files generated by Bazel.
+///
+/// TODO(michalt): This is a temporary flag that we use to disable this
+/// functionality due its performance issues. We plan to benchmark and optimize
+/// it and re-enable it everywhere.
+/// Not private to enable testing.
+var experimentalEnableBazelWatching = false;
+
/// An indication of which files have been added, changed, removed, or deleted.
///
/// No file should be added to the change set more than once, either with the
@@ -1560,6 +1568,7 @@
///
/// Does nothing if the [driver] is not in a Bazel workspace.
void _watchBazelFilesIfNeeded(Folder folder, AnalysisDriver analysisDriver) {
+ if (!experimentalEnableBazelWatching) return;
var workspace = analysisDriver.analysisContext.workspace;
if (workspace is BazelWorkspace &&
!bazelSubscriptions.containsKey(folder)) {
diff --git a/pkg/analysis_server/test/analysis/bazel_changes_test.dart b/pkg/analysis_server/test/analysis/bazel_changes_test.dart
index 168f461..896d08c 100644
--- a/pkg/analysis_server/test/analysis/bazel_changes_test.dart
+++ b/pkg/analysis_server/test/analysis/bazel_changes_test.dart
@@ -6,6 +6,7 @@
import 'package:analysis_server/protocol/protocol.dart';
import 'package:analysis_server/protocol/protocol_constants.dart';
import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/context_manager.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -36,6 +37,8 @@
void setUp() {
super.setUp();
+ experimentalEnableBazelWatching = true;
+
projectPath = convertPath('/workspaceRoot/third_party/dart/project');
testFile =
convertPath('/workspaceRoot/third_party/dart/project/lib/test.dart');
@@ -50,6 +53,8 @@
// file watchers.
server.contextManager.setRoots([], []);
+ experimentalEnableBazelWatching = false;
+
super.tearDown();
}
diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart
index b4ca2b5..965f076 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -450,6 +450,7 @@
CompileTimeErrorCode.YIELD_IN_NON_GENERATOR,
CompileTimeErrorCode.YIELD_OF_INVALID_TYPE,
FfiCode.ANNOTATION_ON_POINTER_FIELD,
+ FfiCode.EMPTY_STRUCT,
FfiCode.EXTRA_ANNOTATION_ON_STRUCT_FIELD,
FfiCode.FIELD_IN_STRUCT_WITH_INITIALIZER,
FfiCode.FIELD_INITIALIZER_IN_STRUCT,
diff --git a/pkg/analyzer/lib/src/dart/error/ffi_code.dart b/pkg/analyzer/lib/src/dart/error/ffi_code.dart
index eda16d1..cacf7bc 100644
--- a/pkg/analyzer/lib/src/dart/error/ffi_code.dart
+++ b/pkg/analyzer/lib/src/dart/error/ffi_code.dart
@@ -23,6 +23,15 @@
correction: "Try removing the annotation.");
/**
+ * Parameters:
+ * 0: the name of the struct class
+ */
+ static const FfiCode EMPTY_STRUCT = FfiCode(
+ name: 'EMPTY_STRUCT',
+ message: "Struct '{0}' is empty. Empty structs are undefined behavior.",
+ correction: "Try adding a field to '{0}' or use a different Struct.");
+
+ /**
* No parameters.
*/
static const FfiCode EXTRA_ANNOTATION_ON_STRUCT_FIELD = FfiCode(
@@ -76,8 +85,9 @@
name: 'INVALID_FIELD_TYPE_IN_STRUCT',
message:
"Fields in struct classes can't have the type '{0}'. They can only "
- "be declared as 'int', 'double' or 'Pointer'.",
- correction: "Try using 'int', 'double' or 'Pointer'.");
+ "be declared as 'int', 'double', 'Pointer', or subtype of 'Struct'.",
+ correction:
+ "Try using 'int', 'double', 'Pointer', or subtype of 'Struct'.");
/**
* No parameters.
diff --git a/pkg/analyzer/lib/src/generated/ffi_verifier.dart b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
index f373869..160ed2f 100644
--- a/pkg/analyzer/lib/src/generated/ffi_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
@@ -174,6 +174,8 @@
structFieldCount++;
} else if (_isPointer(declaredType.element)) {
structFieldCount++;
+ } else if (_isStructClass(declaredType)) {
+ structFieldCount++;
}
}
return structFieldCount == 0;
@@ -228,7 +230,11 @@
/// Returns `true` iff [nativeType] is a struct type.
bool _isStructClass(DartType nativeType) {
if (nativeType is InterfaceType) {
- final superClassElement = nativeType.element.supertype.element;
+ final superType = nativeType.element.supertype;
+ if (superType == null) {
+ return false;
+ }
+ final superClassElement = superType.element;
if (superClassElement.library.name == 'dart.ffi') {
return superClassElement.name == 'Struct' &&
nativeType.typeArguments?.isEmpty == true;
@@ -518,6 +524,12 @@
_validateAnnotations(fieldType, annotations, _PrimitiveDartType.double);
} else if (_isPointer(declaredType.element)) {
_validateNoAnnotations(annotations);
+ } else if (_isStructClass(declaredType)) {
+ final clazz = (declaredType as InterfaceType).element;
+ if (_isEmptyStruct(clazz)) {
+ _errorReporter
+ .reportErrorForNode(FfiCode.EMPTY_STRUCT, node, [clazz.name]);
+ }
} else {
_errorReporter.reportErrorForNode(FfiCode.INVALID_FIELD_TYPE_IN_STRUCT,
fieldType, [fieldType.toSource()]);
diff --git a/pkg/front_end/lib/src/api_unstable/vm.dart b/pkg/front_end/lib/src/api_unstable/vm.dart
index f6c6267..6e45bfc 100644
--- a/pkg/front_end/lib/src/api_unstable/vm.dart
+++ b/pkg/front_end/lib/src/api_unstable/vm.dart
@@ -52,6 +52,7 @@
templateFfiExpectedNoExceptionalReturn,
templateFfiExtendsOrImplementsSealedClass,
templateFfiFieldAnnotation,
+ templateFfiFieldCyclic,
templateFfiFieldInitializer,
templateFfiFieldNoAnnotation,
templateFfiNotStatic,
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index 55c1399..c275e1e 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -322,6 +322,7 @@
FfiExpectedNoExceptionalReturn/analyzerCode: Fail
FfiExtendsOrImplementsSealedClass/analyzerCode: Fail
FfiFieldAnnotation/analyzerCode: Fail
+FfiFieldCyclic/analyzerCode: Fail
FfiFieldInitializer/analyzerCode: Fail
FfiFieldNoAnnotation/analyzerCode: Fail
FfiNotStatic/analyzerCode: Fail
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index e33cdcb..2bcb9d4 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -4253,6 +4253,13 @@
template: "Field '#name' requires no annotation to declare its native type, it is a Pointer which is represented by the same type in Dart and native code."
external: test/ffi_test.dart
+FfiFieldCyclic:
+ # Used by dart:ffi
+ template: |
+ Struct '#name' contains itself. Cycle elements:
+ #names
+ external: test/ffi_test.dart
+
FfiNotStatic:
# Used by dart:ffi
template: "#name expects a static function as parameter. dart:ffi only supports calling static Dart functions from native code."
diff --git a/pkg/front_end/testcases/general/ffi_sample.dart.strong.transformed.expect b/pkg/front_end/testcases/general/ffi_sample.dart.strong.transformed.expect
index 5c415f7..906fb3f 100644
--- a/pkg/front_end/testcases/general/ffi_sample.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/general/ffi_sample.dart.strong.transformed.expect
@@ -13,7 +13,7 @@
@#C3
static final field core::int* #sizeOf = (#C11).{core::List::[]}(ffi::_abi());
@#C3
- constructor #fromPointer(dynamic #pointer) → dynamic
+ constructor #fromTypedDataBase(dynamic #pointer) → dynamic
: super ffi::Struct::_fromPointer(#pointer)
;
static factory allocate(core::double* x, core::double* y, ffi::Pointer<self::Coordinate*>* next) → self::Coordinate* {
diff --git a/pkg/front_end/testcases/general_nnbd_opt_out/ffi_sample.dart.strong.transformed.expect b/pkg/front_end/testcases/general_nnbd_opt_out/ffi_sample.dart.strong.transformed.expect
index 241578e..387fd15 100644
--- a/pkg/front_end/testcases/general_nnbd_opt_out/ffi_sample.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/general_nnbd_opt_out/ffi_sample.dart.strong.transformed.expect
@@ -12,7 +12,7 @@
@#C3
static final field core::int* #sizeOf = (#C6).{core::List::[]}(ffi::_abi());
@#C3
- constructor #fromPointer(dynamic #pointer) → dynamic
+ constructor #fromTypedDataBase(dynamic #pointer) → dynamic
: super ffi::Struct::_fromPointer(#pointer)
;
static factory allocate(core::double* x, core::double* y, ffi::Pointer<self::Coordinate*>* next) → self::Coordinate* {
diff --git a/pkg/front_end/testcases/general_nnbd_opt_out/ffi_sample.dart.weak.transformed.expect b/pkg/front_end/testcases/general_nnbd_opt_out/ffi_sample.dart.weak.transformed.expect
index 5c415f7..906fb3f 100644
--- a/pkg/front_end/testcases/general_nnbd_opt_out/ffi_sample.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/general_nnbd_opt_out/ffi_sample.dart.weak.transformed.expect
@@ -13,7 +13,7 @@
@#C3
static final field core::int* #sizeOf = (#C11).{core::List::[]}(ffi::_abi());
@#C3
- constructor #fromPointer(dynamic #pointer) → dynamic
+ constructor #fromTypedDataBase(dynamic #pointer) → dynamic
: super ffi::Struct::_fromPointer(#pointer)
;
static factory allocate(core::double* x, core::double* y, ffi::Pointer<self::Coordinate*>* next) → self::Coordinate* {
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/ffi_01.yaml.world.1.expect b/pkg/front_end/testcases/incremental_initialize_from_dill/ffi_01.yaml.world.1.expect
index 6ad0475..f93c1a3 100644
--- a/pkg/front_end/testcases/incremental_initialize_from_dill/ffi_01.yaml.world.1.expect
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/ffi_01.yaml.world.1.expect
@@ -9,7 +9,7 @@
@#C3
static final field dart.core::int* #sizeOf = (#C11).{dart.core::List::[]}(dart.ffi::_abi());
@#C3
- constructor #fromPointer(dynamic #pointer) → dynamic
+ constructor #fromTypedDataBase(dynamic #pointer) → dynamic
: super dart.ffi::Struct::_fromPointer(#pointer)
;
static factory allocate(dart.core::double* x, dart.core::double* y, dart.ffi::Pointer<lib::Coordinate*>* next) → lib::Coordinate* {
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/ffi_01.yaml.world.2.expect b/pkg/front_end/testcases/incremental_initialize_from_dill/ffi_01.yaml.world.2.expect
index 0baac00..466b63f 100644
--- a/pkg/front_end/testcases/incremental_initialize_from_dill/ffi_01.yaml.world.2.expect
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/ffi_01.yaml.world.2.expect
@@ -9,7 +9,7 @@
@#C3
static final field dart.core::int* #sizeOf = (#C11).{dart.core::List::[]}(dart.ffi::_abi());
@#C3
- constructor #fromPointer(dynamic #pointer) → dynamic
+ constructor #fromTypedDataBase(dynamic #pointer) → dynamic
: super dart.ffi::Struct::_fromPointer(#pointer)
;
static factory allocate(dart.core::double* x, dart.core::double* y, dart.ffi::Pointer<lib::Coordinate*>* next) → lib::Coordinate* {
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/ffi_02.yaml.world.1.expect b/pkg/front_end/testcases/incremental_initialize_from_dill/ffi_02.yaml.world.1.expect
index a3cbec1..5d79437 100644
--- a/pkg/front_end/testcases/incremental_initialize_from_dill/ffi_02.yaml.world.1.expect
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/ffi_02.yaml.world.1.expect
@@ -9,7 +9,7 @@
@#C3
static final field dart.core::int* #sizeOf = (#C11).{dart.core::List::[]}(dart.ffi::_abi());
@#C3
- constructor #fromPointer(dynamic #pointer) → dynamic
+ constructor #fromTypedDataBase(dynamic #pointer) → dynamic
: super dart.ffi::Struct::_fromPointer(#pointer)
;
static factory allocate(dart.core::double* x, dart.core::double* y, dart.ffi::Pointer<lib::Coordinate*>* next) → lib::Coordinate* {
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_35.yaml.world.1.expect b/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_35.yaml.world.1.expect
index 6ad0475..f93c1a3 100644
--- a/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_35.yaml.world.1.expect
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_35.yaml.world.1.expect
@@ -9,7 +9,7 @@
@#C3
static final field dart.core::int* #sizeOf = (#C11).{dart.core::List::[]}(dart.ffi::_abi());
@#C3
- constructor #fromPointer(dynamic #pointer) → dynamic
+ constructor #fromTypedDataBase(dynamic #pointer) → dynamic
: super dart.ffi::Struct::_fromPointer(#pointer)
;
static factory allocate(dart.core::double* x, dart.core::double* y, dart.ffi::Pointer<lib::Coordinate*>* next) → lib::Coordinate* {
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_35.yaml.world.2.expect b/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_35.yaml.world.2.expect
index 05ed4f7..01f4106 100644
--- a/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_35.yaml.world.2.expect
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_35.yaml.world.2.expect
@@ -9,7 +9,7 @@
@#C3
static final field dart.core::int* #sizeOf = (#C11).{dart.core::List::[]}(dart.ffi::_abi());
@#C3
- constructor #fromPointer(dynamic #pointer) → dynamic
+ constructor #fromTypedDataBase(dynamic #pointer) → dynamic
: super dart.ffi::Struct::_fromPointer(#pointer)
;
static factory allocate(dart.core::double* x, dart.core::double* y, dart.ffi::Pointer<lib::Coordinate*>* next) → lib::Coordinate* {
diff --git a/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_35.yaml.world.3.expect b/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_35.yaml.world.3.expect
index b1b1bda..2c057af 100644
--- a/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_35.yaml.world.3.expect
+++ b/pkg/front_end/testcases/incremental_initialize_from_dill/no_outline_change_35.yaml.world.3.expect
@@ -9,7 +9,7 @@
@#C3
static final field dart.core::int* #sizeOf = (#C11).{dart.core::List::[]}(dart.ffi::_abi());
@#C3
- constructor #fromPointer(dynamic #pointer) → dynamic
+ constructor #fromTypedDataBase(dynamic #pointer) → dynamic
: super dart.ffi::Struct::_fromPointer(#pointer)
;
static factory allocate(dart.core::double* x, dart.core::double* y, dart.ffi::Pointer<lib::Coordinate*>* next) → lib::Coordinate* {
diff --git a/pkg/vm/lib/transformations/ffi.dart b/pkg/vm/lib/transformations/ffi.dart
index 7bdd598..f8b473f 100644
--- a/pkg/vm/lib/transformations/ffi.dart
+++ b/pkg/vm/lib/transformations/ffi.dart
@@ -193,10 +193,16 @@
final Class doubleClass;
final Class listClass;
final Class typeClass;
+ final Procedure unsafeCastMethod;
+ final Class typedDataClass;
+ final Procedure typedDataBufferGetter;
+ final Procedure typedDataOffsetInBytesGetter;
+ final Procedure byteBufferAsUint8List;
final Class pragmaClass;
final Field pragmaName;
final Field pragmaOptions;
final Procedure listElementAt;
+ final Procedure numAddition;
final Library ffiLibrary;
final Class nativeFunctionClass;
@@ -221,6 +227,7 @@
final Map<NativeType, Procedure> storeMethods;
final Map<NativeType, Procedure> elementAtMethods;
final Procedure loadStructMethod;
+ final Procedure memCopy;
final Procedure asFunctionTearoff;
final Procedure lookupFunctionTearoff;
@@ -235,10 +242,20 @@
doubleClass = coreTypes.doubleClass,
listClass = coreTypes.listClass,
typeClass = coreTypes.typeClass,
+ unsafeCastMethod =
+ index.getTopLevelMember('dart:_internal', 'unsafeCast'),
+ typedDataClass = index.getClass('dart:typed_data', 'TypedData'),
+ typedDataBufferGetter =
+ index.getMember('dart:typed_data', 'TypedData', 'get:buffer'),
+ typedDataOffsetInBytesGetter = index.getMember(
+ 'dart:typed_data', 'TypedData', 'get:offsetInBytes'),
+ byteBufferAsUint8List =
+ index.getMember('dart:typed_data', 'ByteBuffer', 'asUint8List'),
pragmaClass = coreTypes.pragmaClass,
pragmaName = coreTypes.pragmaName,
pragmaOptions = coreTypes.pragmaOptions,
listElementAt = coreTypes.index.getMember('dart:core', 'List', '[]'),
+ numAddition = coreTypes.index.getMember('dart:core', 'num', '+'),
ffiLibrary = index.getLibrary('dart:ffi'),
nativeFunctionClass = index.getClass('dart:ffi', 'NativeFunction'),
pointerClass = index.getClass('dart:ffi', 'Pointer'),
@@ -283,6 +300,7 @@
return index.getTopLevelMember('dart:ffi', "_elementAt$name");
}),
loadStructMethod = index.getTopLevelMember('dart:ffi', '_loadStruct'),
+ memCopy = index.getTopLevelMember('dart:ffi', '_memCopy'),
asFunctionTearoff = index.getMember('dart:ffi', 'NativeFunctionPointer',
LibraryIndex.tearoffPrefix + 'asFunction'),
lookupFunctionTearoff = index.getMember(
diff --git a/pkg/vm/lib/transformations/ffi_definitions.dart b/pkg/vm/lib/transformations/ffi_definitions.dart
index ec6849a..868524b 100644
--- a/pkg/vm/lib/transformations/ffi_definitions.dart
+++ b/pkg/vm/lib/transformations/ffi_definitions.dart
@@ -8,7 +8,9 @@
import 'package:front_end/src/api_unstable/vm.dart'
show
+ templateFfiEmptyStruct,
templateFfiFieldAnnotation,
+ templateFfiFieldCyclic,
templateFfiFieldNoAnnotation,
templateFfiTypeMismatch,
templateFfiFieldInitializer,
@@ -22,6 +24,7 @@
import 'package:kernel/target/changed_structure_notifier.dart';
import 'package:kernel/target/targets.dart' show DiagnosticReporter;
import 'package:kernel/type_environment.dart' show SubtypeCheckMode;
+import 'package:kernel/util/graph.dart';
import 'ffi.dart';
@@ -40,7 +43,7 @@
///
/// Output:
/// class Coord extends Struct {
-/// Coord.#fromPointer(Pointer<Coord> coord) : super._(coord);
+/// Coord.#fromTypedDataBase(Pointer<Coord> coord) : super._(coord);
///
/// Pointer<Double> get _xPtr => addressOf.cast();
/// set x(double v) => _xPtr.store(v);
@@ -64,8 +67,8 @@
DiagnosticReporter diagnosticReporter,
ReferenceFromIndex referenceFromIndex,
ChangedStructureNotifier changedStructureNotifier) {
- final LibraryIndex index =
- LibraryIndex(component, const ["dart:ffi", "dart:core"]);
+ final LibraryIndex index = LibraryIndex(component,
+ const ["dart:core", "dart:ffi", "dart:_internal", "dart:typed_data"]);
if (!index.containsLibrary("dart:ffi")) {
// TODO: This check doesn't make sense: "dart:ffi" is always loaded/created
// for the VM target.
@@ -79,14 +82,29 @@
final transformer = new _FfiDefinitionTransformer(index, coreTypes, hierarchy,
diagnosticReporter, referenceFromIndex, changedStructureNotifier);
libraries.forEach(transformer.visitLibrary);
+ transformer.manualVisitInTopologicalOrder();
return FfiTransformerData(transformer.replacedGetters,
transformer.replacedSetters, transformer.emptyStructs);
}
+class StructDependencyGraph<T> implements Graph<T> {
+ final Map<T, Iterable<T>> map;
+ StructDependencyGraph(this.map);
+
+ Iterable<T> get vertices => map.keys;
+ Iterable<T> neighborsOf(T vertex) => map[vertex];
+}
+
/// Checks and elaborates the dart:ffi structs and fields.
class _FfiDefinitionTransformer extends FfiTransformer {
final LibraryIndex index;
+ // Data structures for topological navigation.
+ Map<Class, IndexedClass> indexedStructClasses = {};
+ Map<Class, Set<Class>> structClassDependencies = {};
+ Map<Class, bool> fieldsValid = {};
+ Map<Class, Map<Abi, StructLayout>> structLayouts = {};
+
Map<Field, Procedure> replacedGetters = {};
Map<Field, Procedure> replacedSetters = {};
Set<Class> emptyStructs = {};
@@ -105,6 +123,40 @@
: super(index, coreTypes, hierarchy, diagnosticReporter,
referenceFromIndex) {}
+ void manualVisitInTopologicalOrder() {
+ final connectedComponents =
+ computeStrongComponents(StructDependencyGraph(structClassDependencies));
+
+ connectedComponents.forEach((List<Class> component) {
+ bool report = false;
+ if (component.length > 1) {
+ // Indirect cycle.
+ report = true;
+ }
+ if (component.length == 1) {
+ if (structClassDependencies[component.single]
+ .contains(component.single)) {
+ // Direct cycle.
+ report = true;
+ }
+ }
+ if (report) {
+ component.forEach((Class e) {
+ diagnosticReporter.report(
+ templateFfiFieldCyclic.withArguments(
+ e.name, component.map((e) => e.name).toList()),
+ e.fileOffset,
+ e.name.length,
+ e.fileUri);
+ });
+ }
+ });
+
+ final structClassesSorted = connectedComponents.expand((i) => i).toList();
+
+ structClassesSorted.forEach(visitClassInTopologicalOrder);
+ }
+
@override
visitLibrary(Library node) {
currentLibraryIndex = referenceFromIndex?.lookupLibrary(node);
@@ -128,17 +180,22 @@
// Struct objects are manufactured in the VM by 'allocate' and 'load'.
_makeEntryPoint(node);
- var indexedClass = currentLibraryIndex?.lookupIndexedClass(node.name);
+ final indexedClass = currentLibraryIndex?.lookupIndexedClass(node.name);
_checkConstructors(node, indexedClass);
- final bool fieldsValid = _checkFieldAnnotations(node);
+ indexedStructClasses[node] = indexedClass;
- if (fieldsValid) {
+ fieldsValid[node] = _checkFieldAnnotations(node);
+
+ return node;
+ }
+
+ void visitClassInTopologicalOrder(Class node) {
+ final indexedClass = indexedStructClasses[node];
+ if (fieldsValid[node]) {
final structSize = _replaceFields(node, indexedClass);
_replaceSizeOfMethod(node, structSize, indexedClass);
changedStructureNotifier?.registerClassMemberChange(node);
}
-
- return node;
}
void _checkStructClass(Class node) {
@@ -167,6 +224,11 @@
SubtypeCheckMode.ignoringNullabilities);
}
+ bool _isStructSubtype(DartType type) {
+ return env.isSubtypeOf(type, InterfaceType(structClass, Nullability.legacy),
+ SubtypeCheckMode.ignoringNullabilities);
+ }
+
/// Returns members of [node] that correspond to struct fields.
///
/// Note that getters and setters that originate from an external field have
@@ -198,6 +260,7 @@
bool _checkFieldAnnotations(Class node) {
bool success = true;
+ structClassDependencies[node] = {};
final membersWithAnnotations = _structFieldMembers(node)
..retainWhere((m) => (m is Field) || (m is Procedure && m.isGetter));
for (final Member f in membersWithAnnotations) {
@@ -212,7 +275,7 @@
}
final nativeTypeAnnos = _getNativeTypeAnnotations(f).toList();
final type = _structFieldMemberType(f);
- if (_isPointerType(type)) {
+ if (_isPointerType(type) || _isStructSubtype(type)) {
if (nativeTypeAnnos.length != 0) {
diagnosticReporter.report(
templateFfiFieldNoAnnotation.withArguments(f.name.text),
@@ -220,6 +283,10 @@
f.name.text.length,
f.fileUri);
}
+ if (_isStructSubtype(type)) {
+ final clazz = (type as InterfaceType).classNode;
+ structClassDependencies[node].add(clazz);
+ }
} else if (nativeTypeAnnos.length != 1) {
diagnosticReporter.report(
templateFfiFieldAnnotation.withArguments(f.name.text),
@@ -230,10 +297,9 @@
final DartType nativeType = InterfaceType(
nativeTypesClasses[_getFieldType(nativeTypeAnnos.first).index],
Nullability.legacy);
- // TODO(dartbug.com/37271): Support structs inside structs.
final DartType shouldBeDartType = convertNativeTypeToDartType(
nativeType,
- allowStructs: false,
+ allowStructs: true,
allowHandle: false);
if (shouldBeDartType == null ||
!env.isSubtypeOf(type, shouldBeDartType,
@@ -274,9 +340,9 @@
}
// Add a constructor which 'load' can use.
- // C.#fromPointer(Pointer<Void> address) : super.fromPointer(address);
+ // C.#fromTypedDataBase(Object address) : super.fromPointer(address);
final VariableDeclaration pointer = new VariableDeclaration("#pointer");
- final name = Name("#fromPointer");
+ final name = Name("#fromTypedDataBase");
final referenceFrom = indexedClass?.lookupConstructor(name.text);
final Constructor ctor = Constructor(
FunctionNode(EmptyStatement(), positionalParameters: [pointer]),
@@ -312,6 +378,16 @@
if (_isPointerType(dartType)) {
nativeType = NativeType.kPointer;
clazz = pointerClass;
+ } else if (_isStructSubtype(dartType)) {
+ nativeType = NativeType.kStruct;
+ clazz = (dartType as InterfaceType).classNode;
+ if (emptyStructs.contains(clazz)) {
+ diagnosticReporter.report(
+ templateFfiEmptyStruct.withArguments(clazz.name),
+ m.fileOffset,
+ 1,
+ m.location.file);
+ }
} else {
final nativeTypeAnnos = _getNativeTypeAnnotations(m).toList();
if (nativeTypeAnnos.length == 1) {
@@ -345,14 +421,15 @@
emptyStructs.add(node);
}
- final sizeAndOffsets = <Abi, SizeAndOffsets>{};
+ final structLayout = <Abi, StructLayout>{};
for (final Abi abi in Abi.values) {
- sizeAndOffsets[abi] = _calculateSizeAndOffsets(types, abi);
+ structLayout[abi] = _calculateStructLayout(types, classes, abi);
}
+ structLayouts[node] = structLayout;
for (final i in fields.keys) {
- final fieldOffsets = sizeAndOffsets
- .map((Abi abi, SizeAndOffsets v) => MapEntry(abi, v.offsets[i]));
+ final fieldOffsets = structLayout
+ .map((Abi abi, StructLayout v) => MapEntry(abi, v.offsets[i]));
final methods = _generateMethodsForField(
fields[i], types[i], fieldOffsets, indexedClass);
methods.forEach((p) => node.addProcedure(p));
@@ -363,8 +440,8 @@
}
for (final i in getters.keys) {
- final fieldOffsets = sizeAndOffsets
- .map((Abi abi, SizeAndOffsets v) => MapEntry(abi, v.offsets[i]));
+ final fieldOffsets = structLayout
+ .map((Abi abi, StructLayout v) => MapEntry(abi, v.offsets[i]));
Procedure getter = getters[i];
getter.function.body = _generateGetterStatement(
getter.function.returnType,
@@ -375,8 +452,8 @@
}
for (final i in setters.keys) {
- final fieldOffsets = sizeAndOffsets
- .map((Abi abi, SizeAndOffsets v) => MapEntry(abi, v.offsets[i]));
+ final fieldOffsets = structLayout
+ .map((Abi abi, StructLayout v) => MapEntry(abi, v.offsets[i]));
Procedure setter = setters[i];
setter.function.body = _generateSetterStatement(
setter.function.positionalParameters.single.type,
@@ -387,7 +464,7 @@
setter.isExternal = false;
}
- return sizeAndOffsets.map((k, v) => MapEntry(k, v.size));
+ return structLayout.map((k, v) => MapEntry(k, v.size));
}
void _annoteStructWithFields(Class node, List<Class> fieldTypes) {
@@ -433,6 +510,7 @@
Statement _generateGetterStatement(DartType dartType, NativeType type,
int fileOffset, Map<Abi, int> offsets) {
final bool isPointer = type == NativeType.kPointer;
+ final bool isStruct = type == NativeType.kStruct;
// Sample output:
// int get x => _loadInt8(pointer, offset);
@@ -440,11 +518,87 @@
// Treat Pointer fields different to get correct behavior without casts:
// Pointer<Int8> get x =>
// _fromAddress<Int8>(_loadIntPtr(pointer, offset));
- final loadMethod = isPointer
- ? loadMethods[NativeType.kIntptr]
- : optimizedTypes.contains(type)
- ? loadMethods[type]
- : loadStructMethod;
+ //
+ // Nested structs:
+ // MyStruct get x =>
+ // MyStruct.#fromTypedDataBase(
+ // _addressOf is Pointer ?
+ // _fromAddress<MyStruct>((_addressOf as Pointer).address + offset) :
+ // (_addressOf as TypedData).buffer.asInt8List(
+ // (_addressOf as TypedData).offsetInBytes + offset,
+ // size
+ // )
+ // );
+ if (isStruct) {
+ final clazz = (dartType as InterfaceType).classNode;
+ final constructor = clazz.constructors
+ .firstWhere((c) => c.name == Name("#fromTypedDataBase"));
+ final lengths =
+ structLayouts[clazz].map((key, value) => MapEntry(key, value.size));
+ Expression thisDotAddressOf() =>
+ PropertyGet(ThisExpression(), addressOfField.name, addressOfField)
+ ..fileOffset = fileOffset;
+ return ReturnStatement(ConstructorInvocation(
+ constructor,
+ Arguments([
+ ConditionalExpression(
+ IsExpression(thisDotAddressOf(),
+ InterfaceType(pointerClass, Nullability.legacy)),
+ StaticInvocation(
+ fromAddressInternal,
+ Arguments([
+ MethodInvocation(
+ PropertyGet(thisDotAddressOf(), addressGetter.name,
+ addressGetter)
+ ..fileOffset = fileOffset,
+ numAddition.name,
+ Arguments([_runtimeBranchOnLayout(offsets)]),
+ numAddition)
+ ], types: [
+ dartType
+ ]))
+ ..fileOffset = fileOffset,
+ MethodInvocation(
+ PropertyGet(
+ StaticInvocation(
+ unsafeCastMethod,
+ Arguments([
+ thisDotAddressOf()
+ ], types: [
+ InterfaceType(typedDataClass, Nullability.legacy)
+ ]))
+ ..fileOffset = fileOffset,
+ typedDataBufferGetter.name,
+ typedDataBufferGetter)
+ ..fileOffset = fileOffset,
+ byteBufferAsUint8List.name,
+ Arguments([
+ MethodInvocation(
+ PropertyGet(
+ StaticInvocation(
+ unsafeCastMethod,
+ Arguments([
+ thisDotAddressOf()
+ ], types: [
+ InterfaceType(
+ typedDataClass, Nullability.legacy)
+ ]))
+ ..fileOffset = fileOffset,
+ typedDataOffsetInBytesGetter.name,
+ typedDataOffsetInBytesGetter)
+ ..fileOffset = fileOffset,
+ numAddition.name,
+ Arguments([_runtimeBranchOnLayout(offsets)]),
+ numAddition),
+ _runtimeBranchOnLayout(lengths)
+ ]),
+ byteBufferAsUint8List),
+ InterfaceType(objectClass, Nullability.nonNullable))
+ ]))
+ ..fileOffset = fileOffset);
+ }
+ final loadMethod =
+ isPointer ? loadMethods[NativeType.kIntptr] : loadMethods[type];
Expression getterReturnValue = StaticInvocation(
loadMethod,
Arguments([
@@ -465,6 +619,7 @@
Statement _generateSetterStatement(DartType dartType, NativeType type,
int fileOffset, Map<Abi, int> offsets, VariableDeclaration argument) {
final bool isPointer = type == NativeType.kPointer;
+ final bool isStruct = type == NativeType.kStruct;
// Sample output:
// set x(int v) => _storeInt8(pointer, offset, v);
@@ -472,6 +627,28 @@
// Treat Pointer fields different to get correct behavior without casts:
// set x(Pointer<Int8> v) =>
// _storeIntPtr(pointer, offset, (v as Pointer<Int8>).address);
+ //
+ // Nested structs:
+ // set x(MyStruct v) =>
+ // _memCopy(this._address, offset, v._address, 0, size);
+ if (isStruct) {
+ final clazz = (dartType as InterfaceType).classNode;
+ final lengths =
+ structLayouts[clazz].map((key, value) => MapEntry(key, value.size));
+ return ReturnStatement(StaticInvocation(
+ memCopy,
+ Arguments([
+ PropertyGet(ThisExpression(), addressOfField.name, addressOfField)
+ ..fileOffset = fileOffset,
+ _runtimeBranchOnLayout(offsets),
+ PropertyGet(
+ VariableGet(argument), addressOfField.name, addressOfField)
+ ..fileOffset = fileOffset,
+ ConstantExpression(IntConstant(0)),
+ _runtimeBranchOnLayout(lengths),
+ ]))
+ ..fileOffset = fileOffset);
+ }
final storeMethod =
isPointer ? storeMethods[NativeType.kIntptr] : storeMethods[type];
Expression argumentExpression = VariableGet(argument)
@@ -530,7 +707,7 @@
}
/// Sample output:
- /// static int #sizeOf() => 24;
+ /// int #sizeOf => [24,24,16][_abi()];
void _replaceSizeOfMethod(
Class struct, Map<Abi, int> sizes, IndexedClass indexedClass) {
var name = Name("#sizeOf");
@@ -548,7 +725,15 @@
struct.addField(sizeOf);
}
- int _sizeInBytes(NativeType type, Abi abi) {
+ int _sizeInBytes(NativeType type, Class clazz, Abi abi) {
+ if (type == NativeType.kStruct) {
+ final structLayout = structLayouts[clazz];
+ if (structLayout == null) {
+ // We have a cycle, so we don't know the size.
+ return 0;
+ }
+ return structLayout[abi].size;
+ }
final int size = nativeTypeSizes[type.index];
if (size == WORD_SIZE) {
return wordSize[abi];
@@ -556,10 +741,18 @@
return size;
}
- int _alignmentOf(NativeType type, Abi abi) {
+ int _alignmentOf(NativeType type, Class clazz, Abi abi) {
+ if (type == NativeType.kStruct) {
+ final structLayout = structLayouts[clazz];
+ if (structLayout == null) {
+ // We have a cycle, so we don't know the size.
+ return 0;
+ }
+ return structLayout[abi].alignment;
+ }
final int alignment = nonSizeAlignment[abi][type];
if (alignment != null) return alignment;
- return _sizeInBytes(type, abi);
+ return _sizeInBytes(type, clazz, abi);
}
int _alignOffset(int offset, int alignment) {
@@ -573,24 +766,21 @@
// Keep consistent with runtime/vm/compiler/ffi/native_type.cc
// NativeCompoundType::FromNativeTypes.
- //
- // TODO(37271): Support nested structs.
- SizeAndOffsets _calculateSizeAndOffsets(List<NativeType> types, Abi abi) {
+ StructLayout _calculateStructLayout(
+ List<NativeType> types, List<Class> classes, Abi abi) {
int offset = 0;
final offsets = <int>[];
- for (final NativeType t in types) {
- final int size = _sizeInBytes(t, abi);
- final int alignment = _alignmentOf(t, abi);
+ int structAlignment = 1;
+ for (int i = 0; i < types.length; i++) {
+ final int size = _sizeInBytes(types[i], classes[i], abi);
+ final int alignment = _alignmentOf(types[i], classes[i], abi);
offset = _alignOffset(offset, alignment);
offsets.add(offset);
offset += size;
+ structAlignment = math.max(structAlignment, alignment);
}
- final int minimumAlignment = 1;
- final sizeAlignment = types
- .map((t) => _alignmentOf(t, abi))
- .followedBy([minimumAlignment]).reduce(math.max);
- final int size = _alignOffset(offset, sizeAlignment);
- return SizeAndOffsets(size, offsets);
+ final int size = _alignOffset(offset, structAlignment);
+ return StructLayout(size, structAlignment, offsets);
}
void _makeEntryPoint(Annotatable node) {
@@ -622,12 +812,16 @@
}
}
-class SizeAndOffsets {
+/// The layout of a `Struct` in one [Abi].
+class StructLayout {
/// Size of the entire struct.
final int size;
+ /// Alignment of struct when nested in other struct.
+ final int alignment;
+
/// Offset in bytes for each field, indexed by field number.
final List<int> offsets;
- SizeAndOffsets(this.size, this.offsets);
+ StructLayout(this.size, this.alignment, this.offsets);
}
diff --git a/pkg/vm/lib/transformations/ffi_use_sites.dart b/pkg/vm/lib/transformations/ffi_use_sites.dart
index f4a9c05..295b92d 100644
--- a/pkg/vm/lib/transformations/ffi_use_sites.dart
+++ b/pkg/vm/lib/transformations/ffi_use_sites.dart
@@ -37,7 +37,8 @@
DiagnosticReporter diagnosticReporter,
FfiTransformerData ffiTransformerData,
ReferenceFromIndex referenceFromIndex) {
- final index = new LibraryIndex(component, ["dart:ffi"]);
+ final index = new LibraryIndex(
+ component, ["dart:ffi", "dart:_internal", "dart:typed_data"]);
if (!index.containsLibrary("dart:ffi")) {
// TODO: This check doesn't make sense: "dart:ffi" is always loaded/created
// for the VM target.
diff --git a/runtime/bin/ffi_test/ffi_test_functions.cc b/runtime/bin/ffi_test/ffi_test_functions.cc
index 57d6a01..69a0e51 100644
--- a/runtime/bin/ffi_test/ffi_test_functions.cc
+++ b/runtime/bin/ffi_test/ffi_test_functions.cc
@@ -612,7 +612,6 @@
// A struct designed to exercise all kinds of alignment rules.
// Note that offset32A (System V ia32, iOS arm) aligns doubles on 4 bytes while
// offset32B (Arm 32 bit and MSVC ia32) aligns on 8 bytes.
-// TODO(37271): Support nested structs.
// TODO(37470): Add uncommon primitive data types when we want to support them.
struct VeryLargeStruct {
// size32 size64 offset32A offset32B offset64
@@ -784,6 +783,31 @@
return result;
}
+// Can't easily share this with the generated file.
+struct Struct4BytesHomogeneousInt16Copy {
+ int16_t a0;
+ int16_t a1;
+};
+
+// Can't easily share this with the generated file.
+struct Struct8BytesNestedIntCopy {
+ Struct4BytesHomogeneousInt16Copy a0;
+ Struct4BytesHomogeneousInt16Copy a1;
+};
+
+DART_EXPORT void CallbackWithStruct(void (*f)(Struct8BytesNestedIntCopy)) {
+ std::cout << "CallbackWithStruct"
+ << "(" << reinterpret_cast<void*>(f) << ")\n";
+
+ Struct8BytesNestedIntCopy arg;
+ arg.a0.a0 = 10;
+ arg.a0.a1 = 11;
+ arg.a1.a0 = 12;
+ arg.a1.a1 = 13;
+
+ f(arg);
+}
+
////////////////////////////////////////////////////////////////////////////////
// Tests for callbacks.
diff --git a/runtime/bin/ffi_test/ffi_test_functions_generated.cc b/runtime/bin/ffi_test/ffi_test_functions_generated.cc
index 74f27e4..a77bff1 100644
--- a/runtime/bin/ffi_test/ffi_test_functions_generated.cc
+++ b/runtime/bin/ffi_test/ffi_test_functions_generated.cc
@@ -56,6 +56,10 @@
int16_t a1;
};
+struct Struct4BytesFloat {
+ float a0;
+};
+
struct Struct7BytesHomogeneousUint8 {
uint8_t a0;
uint8_t a1;
@@ -339,6 +343,75 @@
int8_t a2;
};
+struct Struct8BytesNestedInt {
+ Struct4BytesHomogeneousInt16 a0;
+ Struct4BytesHomogeneousInt16 a1;
+};
+
+struct Struct8BytesNestedFloat {
+ Struct4BytesFloat a0;
+ Struct4BytesFloat a1;
+};
+
+struct Struct8BytesNestedFloat2 {
+ Struct4BytesFloat a0;
+ float a1;
+};
+
+struct Struct8BytesNestedMixed {
+ Struct4BytesHomogeneousInt16 a0;
+ Struct4BytesFloat a1;
+};
+
+struct Struct16BytesNestedInt {
+ Struct8BytesNestedInt a0;
+ Struct8BytesNestedInt a1;
+};
+
+struct Struct32BytesNestedInt {
+ Struct16BytesNestedInt a0;
+ Struct16BytesNestedInt a1;
+};
+
+struct StructNestedIntStructAlignmentInt16 {
+ StructAlignmentInt16 a0;
+ StructAlignmentInt16 a1;
+};
+
+struct StructNestedIntStructAlignmentInt32 {
+ StructAlignmentInt32 a0;
+ StructAlignmentInt32 a1;
+};
+
+struct StructNestedIntStructAlignmentInt64 {
+ StructAlignmentInt64 a0;
+ StructAlignmentInt64 a1;
+};
+
+struct StructNestedIrregularBig {
+ uint16_t a0;
+ Struct8BytesNestedMixed a1;
+ uint16_t a2;
+ Struct8BytesNestedFloat2 a3;
+ uint16_t a4;
+ Struct8BytesNestedFloat a5;
+ uint16_t a6;
+};
+
+struct StructNestedIrregularBigger {
+ StructNestedIrregularBig a0;
+ Struct8BytesNestedMixed a1;
+ float a2;
+ double a3;
+};
+
+struct StructNestedIrregularEvenBigger {
+ uint64_t a0;
+ StructNestedIrregularBigger a1;
+ StructNestedIrregularBigger a2;
+ double a3;
+};
+
// Used for testing structs by value.
// Smallest struct with data.
// 10 struct arguments will exhaust available registers.
@@ -2569,6 +2642,639 @@
}
// Used for testing structs by value.
+// Simple nested struct. No alignment gaps on any architectures.
+// 10 arguments exhaust registers on all platforms.
+DART_EXPORT int64_t PassStruct8BytesNestedIntx10(Struct8BytesNestedInt a0,
+ Struct8BytesNestedInt a1,
+ Struct8BytesNestedInt a2,
+ Struct8BytesNestedInt a3,
+ Struct8BytesNestedInt a4,
+ Struct8BytesNestedInt a5,
+ Struct8BytesNestedInt a6,
+ Struct8BytesNestedInt a7,
+ Struct8BytesNestedInt a8,
+ Struct8BytesNestedInt a9) {
+ std::cout << "PassStruct8BytesNestedIntx10"
+ << "(((" << a0.a0.a0 << ", " << a0.a0.a1 << "), (" << a0.a1.a0
+ << ", " << a0.a1.a1 << ")), ((" << a1.a0.a0 << ", " << a1.a0.a1
+ << "), (" << a1.a1.a0 << ", " << a1.a1.a1 << ")), ((" << a2.a0.a0
+ << ", " << a2.a0.a1 << "), (" << a2.a1.a0 << ", " << a2.a1.a1
+ << ")), ((" << a3.a0.a0 << ", " << a3.a0.a1 << "), (" << a3.a1.a0
+ << ", " << a3.a1.a1 << ")), ((" << a4.a0.a0 << ", " << a4.a0.a1
+ << "), (" << a4.a1.a0 << ", " << a4.a1.a1 << ")), ((" << a5.a0.a0
+ << ", " << a5.a0.a1 << "), (" << a5.a1.a0 << ", " << a5.a1.a1
+ << ")), ((" << a6.a0.a0 << ", " << a6.a0.a1 << "), (" << a6.a1.a0
+ << ", " << a6.a1.a1 << ")), ((" << a7.a0.a0 << ", " << a7.a0.a1
+ << "), (" << a7.a1.a0 << ", " << a7.a1.a1 << ")), ((" << a8.a0.a0
+ << ", " << a8.a0.a1 << "), (" << a8.a1.a0 << ", " << a8.a1.a1
+ << ")), ((" << a9.a0.a0 << ", " << a9.a0.a1 << "), (" << a9.a1.a0
+ << ", " << a9.a1.a1 << ")))"
+ << "\n";
+
+ int64_t result = 0;
+
+ result += a0.a0.a0;
+ result += a0.a0.a1;
+ result += a0.a1.a0;
+ result += a0.a1.a1;
+ result += a1.a0.a0;
+ result += a1.a0.a1;
+ result += a1.a1.a0;
+ result += a1.a1.a1;
+ result += a2.a0.a0;
+ result += a2.a0.a1;
+ result += a2.a1.a0;
+ result += a2.a1.a1;
+ result += a3.a0.a0;
+ result += a3.a0.a1;
+ result += a3.a1.a0;
+ result += a3.a1.a1;
+ result += a4.a0.a0;
+ result += a4.a0.a1;
+ result += a4.a1.a0;
+ result += a4.a1.a1;
+ result += a5.a0.a0;
+ result += a5.a0.a1;
+ result += a5.a1.a0;
+ result += a5.a1.a1;
+ result += a6.a0.a0;
+ result += a6.a0.a1;
+ result += a6.a1.a0;
+ result += a6.a1.a1;
+ result += a7.a0.a0;
+ result += a7.a0.a1;
+ result += a7.a1.a0;
+ result += a7.a1.a1;
+ result += a8.a0.a0;
+ result += a8.a0.a1;
+ result += a8.a1.a0;
+ result += a8.a1.a1;
+ result += a9.a0.a0;
+ result += a9.a0.a1;
+ result += a9.a1.a0;
+ result += a9.a1.a1;
+
+ std::cout << "result = " << result << "\n";
+
+ return result;
+}
+
+// Used for testing structs by value.
+// Simple nested struct. No alignment gaps on any architectures.
+// 10 arguments exhaust fpu registers on all platforms.
+DART_EXPORT float PassStruct8BytesNestedFloatx10(Struct8BytesNestedFloat a0,
+ Struct8BytesNestedFloat a1,
+ Struct8BytesNestedFloat a2,
+ Struct8BytesNestedFloat a3,
+ Struct8BytesNestedFloat a4,
+ Struct8BytesNestedFloat a5,
+ Struct8BytesNestedFloat a6,
+ Struct8BytesNestedFloat a7,
+ Struct8BytesNestedFloat a8,
+ Struct8BytesNestedFloat a9) {
+ std::cout << "PassStruct8BytesNestedFloatx10"
+ << "(((" << a0.a0.a0 << "), (" << a0.a1.a0 << ")), ((" << a1.a0.a0
+ << "), (" << a1.a1.a0 << ")), ((" << a2.a0.a0 << "), (" << a2.a1.a0
+ << ")), ((" << a3.a0.a0 << "), (" << a3.a1.a0 << ")), (("
+ << a4.a0.a0 << "), (" << a4.a1.a0 << ")), ((" << a5.a0.a0 << "), ("
+ << a5.a1.a0 << ")), ((" << a6.a0.a0 << "), (" << a6.a1.a0
+ << ")), ((" << a7.a0.a0 << "), (" << a7.a1.a0 << ")), (("
+ << a8.a0.a0 << "), (" << a8.a1.a0 << ")), ((" << a9.a0.a0 << "), ("
+ << a9.a1.a0 << ")))"
+ << "\n";
+
+ float result = 0;
+
+ result += a0.a0.a0;
+ result += a0.a1.a0;
+ result += a1.a0.a0;
+ result += a1.a1.a0;
+ result += a2.a0.a0;
+ result += a2.a1.a0;
+ result += a3.a0.a0;
+ result += a3.a1.a0;
+ result += a4.a0.a0;
+ result += a4.a1.a0;
+ result += a5.a0.a0;
+ result += a5.a1.a0;
+ result += a6.a0.a0;
+ result += a6.a1.a0;
+ result += a7.a0.a0;
+ result += a7.a1.a0;
+ result += a8.a0.a0;
+ result += a8.a1.a0;
+ result += a9.a0.a0;
+ result += a9.a1.a0;
+
+ std::cout << "result = " << result << "\n";
+
+ return result;
+}
+
+// Used for testing structs by value.
+// Simple nested struct. No alignment gaps on any architectures.
+// 10 arguments exhaust fpu registers on all platforms.
+// The nesting is irregular, testing homogenous float rules on arm and arm64,
+// and the fpu register usage on x64.
+DART_EXPORT float PassStruct8BytesNestedFloat2x10(Struct8BytesNestedFloat2 a0,
+ Struct8BytesNestedFloat2 a1,
+ Struct8BytesNestedFloat2 a2,
+ Struct8BytesNestedFloat2 a3,
+ Struct8BytesNestedFloat2 a4,
+ Struct8BytesNestedFloat2 a5,
+ Struct8BytesNestedFloat2 a6,
+ Struct8BytesNestedFloat2 a7,
+ Struct8BytesNestedFloat2 a8,
+ Struct8BytesNestedFloat2 a9) {
+ std::cout << "PassStruct8BytesNestedFloat2x10"
+ << "(((" << a0.a0.a0 << "), " << a0.a1 << "), ((" << a1.a0.a0
+ << "), " << a1.a1 << "), ((" << a2.a0.a0 << "), " << a2.a1
+ << "), ((" << a3.a0.a0 << "), " << a3.a1 << "), ((" << a4.a0.a0
+ << "), " << a4.a1 << "), ((" << a5.a0.a0 << "), " << a5.a1
+ << "), ((" << a6.a0.a0 << "), " << a6.a1 << "), ((" << a7.a0.a0
+ << "), " << a7.a1 << "), ((" << a8.a0.a0 << "), " << a8.a1
+ << "), ((" << a9.a0.a0 << "), " << a9.a1 << "))"
+ << "\n";
+
+ float result = 0;
+
+ result += a0.a0.a0;
+ result += a0.a1;
+ result += a1.a0.a0;
+ result += a1.a1;
+ result += a2.a0.a0;
+ result += a2.a1;
+ result += a3.a0.a0;
+ result += a3.a1;
+ result += a4.a0.a0;
+ result += a4.a1;
+ result += a5.a0.a0;
+ result += a5.a1;
+ result += a6.a0.a0;
+ result += a6.a1;
+ result += a7.a0.a0;
+ result += a7.a1;
+ result += a8.a0.a0;
+ result += a8.a1;
+ result += a9.a0.a0;
+ result += a9.a1;
+
+ std::cout << "result = " << result << "\n";
+
+ return result;
+}
+
+// Used for testing structs by value.
+// Simple nested struct. No alignment gaps on any architectures.
+// 10 arguments exhaust all registers on all platforms.
+DART_EXPORT double PassStruct8BytesNestedMixedx10(Struct8BytesNestedMixed a0,
+ Struct8BytesNestedMixed a1,
+ Struct8BytesNestedMixed a2,
+ Struct8BytesNestedMixed a3,
+ Struct8BytesNestedMixed a4,
+ Struct8BytesNestedMixed a5,
+ Struct8BytesNestedMixed a6,
+ Struct8BytesNestedMixed a7,
+ Struct8BytesNestedMixed a8,
+ Struct8BytesNestedMixed a9) {
+ std::cout << "PassStruct8BytesNestedMixedx10"
+ << "(((" << a0.a0.a0 << ", " << a0.a0.a1 << "), (" << a0.a1.a0
+ << ")), ((" << a1.a0.a0 << ", " << a1.a0.a1 << "), (" << a1.a1.a0
+ << ")), ((" << a2.a0.a0 << ", " << a2.a0.a1 << "), (" << a2.a1.a0
+ << ")), ((" << a3.a0.a0 << ", " << a3.a0.a1 << "), (" << a3.a1.a0
+ << ")), ((" << a4.a0.a0 << ", " << a4.a0.a1 << "), (" << a4.a1.a0
+ << ")), ((" << a5.a0.a0 << ", " << a5.a0.a1 << "), (" << a5.a1.a0
+ << ")), ((" << a6.a0.a0 << ", " << a6.a0.a1 << "), (" << a6.a1.a0
+ << ")), ((" << a7.a0.a0 << ", " << a7.a0.a1 << "), (" << a7.a1.a0
+ << ")), ((" << a8.a0.a0 << ", " << a8.a0.a1 << "), (" << a8.a1.a0
+ << ")), ((" << a9.a0.a0 << ", " << a9.a0.a1 << "), (" << a9.a1.a0
+ << ")))"
+ << "\n";
+
+ double result = 0;
+
+ result += a0.a0.a0;
+ result += a0.a0.a1;
+ result += a0.a1.a0;
+ result += a1.a0.a0;
+ result += a1.a0.a1;
+ result += a1.a1.a0;
+ result += a2.a0.a0;
+ result += a2.a0.a1;
+ result += a2.a1.a0;
+ result += a3.a0.a0;
+ result += a3.a0.a1;
+ result += a3.a1.a0;
+ result += a4.a0.a0;
+ result += a4.a0.a1;
+ result += a4.a1.a0;
+ result += a5.a0.a0;
+ result += a5.a0.a1;
+ result += a5.a1.a0;
+ result += a6.a0.a0;
+ result += a6.a0.a1;
+ result += a6.a1.a0;
+ result += a7.a0.a0;
+ result += a7.a0.a1;
+ result += a7.a1.a0;
+ result += a8.a0.a0;
+ result += a8.a0.a1;
+ result += a8.a1.a0;
+ result += a9.a0.a0;
+ result += a9.a0.a1;
+ result += a9.a1.a0;
+
+ std::cout << "result = " << result << "\n";
+
+ return result;
+}
+
+// Used for testing structs by value.
+// Deeper nested struct to test recursive member access.
+DART_EXPORT int64_t PassStruct16BytesNestedIntx2(Struct16BytesNestedInt a0,
+ Struct16BytesNestedInt a1) {
+ std::cout << "PassStruct16BytesNestedIntx2"
+ << "((((" << a0.a0.a0.a0 << ", " << a0.a0.a0.a1 << "), ("
+ << a0.a0.a1.a0 << ", " << a0.a0.a1.a1 << ")), ((" << a0.a1.a0.a0
+ << ", " << a0.a1.a0.a1 << "), (" << a0.a1.a1.a0 << ", "
+ << a0.a1.a1.a1 << "))), (((" << a1.a0.a0.a0 << ", " << a1.a0.a0.a1
+ << "), (" << a1.a0.a1.a0 << ", " << a1.a0.a1.a1 << ")), (("
+ << a1.a1.a0.a0 << ", " << a1.a1.a0.a1 << "), (" << a1.a1.a1.a0
+ << ", " << a1.a1.a1.a1 << "))))"
+ << "\n";
+
+ int64_t result = 0;
+
+ result += a0.a0.a0.a0;
+ result += a0.a0.a0.a1;
+ result += a0.a0.a1.a0;
+ result += a0.a0.a1.a1;
+ result += a0.a1.a0.a0;
+ result += a0.a1.a0.a1;
+ result += a0.a1.a1.a0;
+ result += a0.a1.a1.a1;
+ result += a1.a0.a0.a0;
+ result += a1.a0.a0.a1;
+ result += a1.a0.a1.a0;
+ result += a1.a0.a1.a1;
+ result += a1.a1.a0.a0;
+ result += a1.a1.a0.a1;
+ result += a1.a1.a1.a0;
+ result += a1.a1.a1.a1;
+
+ std::cout << "result = " << result << "\n";
+
+ return result;
+}
+
+// Used for testing structs by value.
+// Even deeper nested struct to test recursive member access.
+DART_EXPORT int64_t PassStruct32BytesNestedIntx2(Struct32BytesNestedInt a0,
+ Struct32BytesNestedInt a1) {
+ std::cout << "PassStruct32BytesNestedIntx2"
+ << "(((((" << a0.a0.a0.a0.a0 << ", " << a0.a0.a0.a0.a1 << "), ("
+ << a0.a0.a0.a1.a0 << ", " << a0.a0.a0.a1.a1 << ")), (("
+ << a0.a0.a1.a0.a0 << ", " << a0.a0.a1.a0.a1 << "), ("
+ << a0.a0.a1.a1.a0 << ", " << a0.a0.a1.a1.a1 << "))), ((("
+ << a0.a1.a0.a0.a0 << ", " << a0.a1.a0.a0.a1 << "), ("
+ << a0.a1.a0.a1.a0 << ", " << a0.a1.a0.a1.a1 << ")), (("
+ << a0.a1.a1.a0.a0 << ", " << a0.a1.a1.a0.a1 << "), ("
+ << a0.a1.a1.a1.a0 << ", " << a0.a1.a1.a1.a1 << ")))), (((("
+ << a1.a0.a0.a0.a0 << ", " << a1.a0.a0.a0.a1 << "), ("
+ << a1.a0.a0.a1.a0 << ", " << a1.a0.a0.a1.a1 << ")), (("
+ << a1.a0.a1.a0.a0 << ", " << a1.a0.a1.a0.a1 << "), ("
+ << a1.a0.a1.a1.a0 << ", " << a1.a0.a1.a1.a1 << "))), ((("
+ << a1.a1.a0.a0.a0 << ", " << a1.a1.a0.a0.a1 << "), ("
+ << a1.a1.a0.a1.a0 << ", " << a1.a1.a0.a1.a1 << ")), (("
+ << a1.a1.a1.a0.a0 << ", " << a1.a1.a1.a0.a1 << "), ("
+ << a1.a1.a1.a1.a0 << ", " << a1.a1.a1.a1.a1 << ")))))"
+ << "\n";
+
+ int64_t result = 0;
+
+ result += a0.a0.a0.a0.a0;
+ result += a0.a0.a0.a0.a1;
+ result += a0.a0.a0.a1.a0;
+ result += a0.a0.a0.a1.a1;
+ result += a0.a0.a1.a0.a0;
+ result += a0.a0.a1.a0.a1;
+ result += a0.a0.a1.a1.a0;
+ result += a0.a0.a1.a1.a1;
+ result += a0.a1.a0.a0.a0;
+ result += a0.a1.a0.a0.a1;
+ result += a0.a1.a0.a1.a0;
+ result += a0.a1.a0.a1.a1;
+ result += a0.a1.a1.a0.a0;
+ result += a0.a1.a1.a0.a1;
+ result += a0.a1.a1.a1.a0;
+ result += a0.a1.a1.a1.a1;
+ result += a1.a0.a0.a0.a0;
+ result += a1.a0.a0.a0.a1;
+ result += a1.a0.a0.a1.a0;
+ result += a1.a0.a0.a1.a1;
+ result += a1.a0.a1.a0.a0;
+ result += a1.a0.a1.a0.a1;
+ result += a1.a0.a1.a1.a0;
+ result += a1.a0.a1.a1.a1;
+ result += a1.a1.a0.a0.a0;
+ result += a1.a1.a0.a0.a1;
+ result += a1.a1.a0.a1.a0;
+ result += a1.a1.a0.a1.a1;
+ result += a1.a1.a1.a0.a0;
+ result += a1.a1.a1.a0.a1;
+ result += a1.a1.a1.a1.a0;
+ result += a1.a1.a1.a1.a1;
+
+ std::cout << "result = " << result << "\n";
+
+ return result;
+}
+
+// Used for testing structs by value.
+// Test alignment and padding of nested struct with 16 byte int.
+DART_EXPORT int64_t PassStructNestedIntStructAlignmentInt16(
+ StructNestedIntStructAlignmentInt16 a0) {
+ std::cout << "PassStructNestedIntStructAlignmentInt16"
+ << "(((" << static_cast<int>(a0.a0.a0) << ", " << a0.a0.a1 << ", "
+ << static_cast<int>(a0.a0.a2) << "), ("
+ << static_cast<int>(a0.a1.a0) << ", " << a0.a1.a1 << ", "
+ << static_cast<int>(a0.a1.a2) << ")))"
+ << "\n";
+
+ int64_t result = 0;
+
+ result += a0.a0.a0;
+ result += a0.a0.a1;
+ result += a0.a0.a2;
+ result += a0.a1.a0;
+ result += a0.a1.a1;
+ result += a0.a1.a2;
+
+ std::cout << "result = " << result << "\n";
+
+ return result;
+}
+
+// Used for testing structs by value.
+// Test alignment and padding of nested struct with 32 byte int.
+DART_EXPORT int64_t PassStructNestedIntStructAlignmentInt32(
+ StructNestedIntStructAlignmentInt32 a0) {
+ std::cout << "PassStructNestedIntStructAlignmentInt32"
+ << "(((" << static_cast<int>(a0.a0.a0) << ", " << a0.a0.a1 << ", "
+ << static_cast<int>(a0.a0.a2) << "), ("
+ << static_cast<int>(a0.a1.a0) << ", " << a0.a1.a1 << ", "
+ << static_cast<int>(a0.a1.a2) << ")))"
+ << "\n";
+
+ int64_t result = 0;
+
+ result += a0.a0.a0;
+ result += a0.a0.a1;
+ result += a0.a0.a2;
+ result += a0.a1.a0;
+ result += a0.a1.a1;
+ result += a0.a1.a2;
+
+ std::cout << "result = " << result << "\n";
+
+ return result;
+}
+
+// Used for testing structs by value.
+// Test alignment and padding of nested struct with 64 byte int.
+DART_EXPORT int64_t PassStructNestedIntStructAlignmentInt64(
+ StructNestedIntStructAlignmentInt64 a0) {
+ std::cout << "PassStructNestedIntStructAlignmentInt64"
+ << "(((" << static_cast<int>(a0.a0.a0) << ", " << a0.a0.a1 << ", "
+ << static_cast<int>(a0.a0.a2) << "), ("
+ << static_cast<int>(a0.a1.a0) << ", " << a0.a1.a1 << ", "
+ << static_cast<int>(a0.a1.a2) << ")))"
+ << "\n";
+
+ int64_t result = 0;
+
+ result += a0.a0.a0;
+ result += a0.a0.a1;
+ result += a0.a0.a2;
+ result += a0.a1.a0;
+ result += a0.a1.a1;
+ result += a0.a1.a2;
+
+ std::cout << "result = " << result << "\n";
+
+ return result;
+}
+
+// Used for testing structs by value.
+// Return big irregular struct as smoke test.
+DART_EXPORT double PassStructNestedIrregularEvenBiggerx4(
+ StructNestedIrregularEvenBigger a0,
+ StructNestedIrregularEvenBigger a1,
+ StructNestedIrregularEvenBigger a2,
+ StructNestedIrregularEvenBigger a3) {
+ std::cout
+ << "PassStructNestedIrregularEvenBiggerx4"
+ << "((" << a0.a0 << ", ((" << a0.a1.a0.a0 << ", ((" << a0.a1.a0.a1.a0.a0
+ << ", " << a0.a1.a0.a1.a0.a1 << "), (" << a0.a1.a0.a1.a1.a0 << ")), "
+ << a0.a1.a0.a2 << ", ((" << a0.a1.a0.a3.a0.a0 << "), " << a0.a1.a0.a3.a1
+ << "), " << a0.a1.a0.a4 << ", ((" << a0.a1.a0.a5.a0.a0 << "), ("
+ << a0.a1.a0.a5.a1.a0 << ")), " << a0.a1.a0.a6 << "), ((" << a0.a1.a1.a0.a0
+ << ", " << a0.a1.a1.a0.a1 << "), (" << a0.a1.a1.a1.a0 << ")), "
+ << a0.a1.a2 << ", " << a0.a1.a3 << "), ((" << a0.a2.a0.a0 << ", (("
+ << a0.a2.a0.a1.a0.a0 << ", " << a0.a2.a0.a1.a0.a1 << "), ("
+ << a0.a2.a0.a1.a1.a0 << ")), " << a0.a2.a0.a2 << ", (("
+ << a0.a2.a0.a3.a0.a0 << "), " << a0.a2.a0.a3.a1 << "), " << a0.a2.a0.a4
+ << ", ((" << a0.a2.a0.a5.a0.a0 << "), (" << a0.a2.a0.a5.a1.a0 << ")), "
+ << a0.a2.a0.a6 << "), ((" << a0.a2.a1.a0.a0 << ", " << a0.a2.a1.a0.a1
+ << "), (" << a0.a2.a1.a1.a0 << ")), " << a0.a2.a2 << ", " << a0.a2.a3
+ << "), " << a0.a3 << "), (" << a1.a0 << ", ((" << a1.a1.a0.a0 << ", (("
+ << a1.a1.a0.a1.a0.a0 << ", " << a1.a1.a0.a1.a0.a1 << "), ("
+ << a1.a1.a0.a1.a1.a0 << ")), " << a1.a1.a0.a2 << ", (("
+ << a1.a1.a0.a3.a0.a0 << "), " << a1.a1.a0.a3.a1 << "), " << a1.a1.a0.a4
+ << ", ((" << a1.a1.a0.a5.a0.a0 << "), (" << a1.a1.a0.a5.a1.a0 << ")), "
+ << a1.a1.a0.a6 << "), ((" << a1.a1.a1.a0.a0 << ", " << a1.a1.a1.a0.a1
+ << "), (" << a1.a1.a1.a1.a0 << ")), " << a1.a1.a2 << ", " << a1.a1.a3
+ << "), ((" << a1.a2.a0.a0 << ", ((" << a1.a2.a0.a1.a0.a0 << ", "
+ << a1.a2.a0.a1.a0.a1 << "), (" << a1.a2.a0.a1.a1.a0 << ")), "
+ << a1.a2.a0.a2 << ", ((" << a1.a2.a0.a3.a0.a0 << "), " << a1.a2.a0.a3.a1
+ << "), " << a1.a2.a0.a4 << ", ((" << a1.a2.a0.a5.a0.a0 << "), ("
+ << a1.a2.a0.a5.a1.a0 << ")), " << a1.a2.a0.a6 << "), ((" << a1.a2.a1.a0.a0
+ << ", " << a1.a2.a1.a0.a1 << "), (" << a1.a2.a1.a1.a0 << ")), "
+ << a1.a2.a2 << ", " << a1.a2.a3 << "), " << a1.a3 << "), (" << a2.a0
+ << ", ((" << a2.a1.a0.a0 << ", ((" << a2.a1.a0.a1.a0.a0 << ", "
+ << a2.a1.a0.a1.a0.a1 << "), (" << a2.a1.a0.a1.a1.a0 << ")), "
+ << a2.a1.a0.a2 << ", ((" << a2.a1.a0.a3.a0.a0 << "), " << a2.a1.a0.a3.a1
+ << "), " << a2.a1.a0.a4 << ", ((" << a2.a1.a0.a5.a0.a0 << "), ("
+ << a2.a1.a0.a5.a1.a0 << ")), " << a2.a1.a0.a6 << "), ((" << a2.a1.a1.a0.a0
+ << ", " << a2.a1.a1.a0.a1 << "), (" << a2.a1.a1.a1.a0 << ")), "
+ << a2.a1.a2 << ", " << a2.a1.a3 << "), ((" << a2.a2.a0.a0 << ", (("
+ << a2.a2.a0.a1.a0.a0 << ", " << a2.a2.a0.a1.a0.a1 << "), ("
+ << a2.a2.a0.a1.a1.a0 << ")), " << a2.a2.a0.a2 << ", (("
+ << a2.a2.a0.a3.a0.a0 << "), " << a2.a2.a0.a3.a1 << "), " << a2.a2.a0.a4
+ << ", ((" << a2.a2.a0.a5.a0.a0 << "), (" << a2.a2.a0.a5.a1.a0 << ")), "
+ << a2.a2.a0.a6 << "), ((" << a2.a2.a1.a0.a0 << ", " << a2.a2.a1.a0.a1
+ << "), (" << a2.a2.a1.a1.a0 << ")), " << a2.a2.a2 << ", " << a2.a2.a3
+ << "), " << a2.a3 << "), (" << a3.a0 << ", ((" << a3.a1.a0.a0 << ", (("
+ << a3.a1.a0.a1.a0.a0 << ", " << a3.a1.a0.a1.a0.a1 << "), ("
+ << a3.a1.a0.a1.a1.a0 << ")), " << a3.a1.a0.a2 << ", (("
+ << a3.a1.a0.a3.a0.a0 << "), " << a3.a1.a0.a3.a1 << "), " << a3.a1.a0.a4
+ << ", ((" << a3.a1.a0.a5.a0.a0 << "), (" << a3.a1.a0.a5.a1.a0 << ")), "
+ << a3.a1.a0.a6 << "), ((" << a3.a1.a1.a0.a0 << ", " << a3.a1.a1.a0.a1
+ << "), (" << a3.a1.a1.a1.a0 << ")), " << a3.a1.a2 << ", " << a3.a1.a3
+ << "), ((" << a3.a2.a0.a0 << ", ((" << a3.a2.a0.a1.a0.a0 << ", "
+ << a3.a2.a0.a1.a0.a1 << "), (" << a3.a2.a0.a1.a1.a0 << ")), "
+ << a3.a2.a0.a2 << ", ((" << a3.a2.a0.a3.a0.a0 << "), " << a3.a2.a0.a3.a1
+ << "), " << a3.a2.a0.a4 << ", ((" << a3.a2.a0.a5.a0.a0 << "), ("
+ << a3.a2.a0.a5.a1.a0 << ")), " << a3.a2.a0.a6 << "), ((" << a3.a2.a1.a0.a0
+ << ", " << a3.a2.a1.a0.a1 << "), (" << a3.a2.a1.a1.a0 << ")), "
+ << a3.a2.a2 << ", " << a3.a2.a3 << "), " << a3.a3 << "))"
+ << "\n";
+
+ double result = 0;
+
+ result += a0.a0;
+ result += a0.a1.a0.a0;
+ result += a0.a1.a0.a1.a0.a0;
+ result += a0.a1.a0.a1.a0.a1;
+ result += a0.a1.a0.a1.a1.a0;
+ result += a0.a1.a0.a2;
+ result += a0.a1.a0.a3.a0.a0;
+ result += a0.a1.a0.a3.a1;
+ result += a0.a1.a0.a4;
+ result += a0.a1.a0.a5.a0.a0;
+ result += a0.a1.a0.a5.a1.a0;
+ result += a0.a1.a0.a6;
+ result += a0.a1.a1.a0.a0;
+ result += a0.a1.a1.a0.a1;
+ result += a0.a1.a1.a1.a0;
+ result += a0.a1.a2;
+ result += a0.a1.a3;
+ result += a0.a2.a0.a0;
+ result += a0.a2.a0.a1.a0.a0;
+ result += a0.a2.a0.a1.a0.a1;
+ result += a0.a2.a0.a1.a1.a0;
+ result += a0.a2.a0.a2;
+ result += a0.a2.a0.a3.a0.a0;
+ result += a0.a2.a0.a3.a1;
+ result += a0.a2.a0.a4;
+ result += a0.a2.a0.a5.a0.a0;
+ result += a0.a2.a0.a5.a1.a0;
+ result += a0.a2.a0.a6;
+ result += a0.a2.a1.a0.a0;
+ result += a0.a2.a1.a0.a1;
+ result += a0.a2.a1.a1.a0;
+ result += a0.a2.a2;
+ result += a0.a2.a3;
+ result += a0.a3;
+ result += a1.a0;
+ result += a1.a1.a0.a0;
+ result += a1.a1.a0.a1.a0.a0;
+ result += a1.a1.a0.a1.a0.a1;
+ result += a1.a1.a0.a1.a1.a0;
+ result += a1.a1.a0.a2;
+ result += a1.a1.a0.a3.a0.a0;
+ result += a1.a1.a0.a3.a1;
+ result += a1.a1.a0.a4;
+ result += a1.a1.a0.a5.a0.a0;
+ result += a1.a1.a0.a5.a1.a0;
+ result += a1.a1.a0.a6;
+ result += a1.a1.a1.a0.a0;
+ result += a1.a1.a1.a0.a1;
+ result += a1.a1.a1.a1.a0;
+ result += a1.a1.a2;
+ result += a1.a1.a3;
+ result += a1.a2.a0.a0;
+ result += a1.a2.a0.a1.a0.a0;
+ result += a1.a2.a0.a1.a0.a1;
+ result += a1.a2.a0.a1.a1.a0;
+ result += a1.a2.a0.a2;
+ result += a1.a2.a0.a3.a0.a0;
+ result += a1.a2.a0.a3.a1;
+ result += a1.a2.a0.a4;
+ result += a1.a2.a0.a5.a0.a0;
+ result += a1.a2.a0.a5.a1.a0;
+ result += a1.a2.a0.a6;
+ result += a1.a2.a1.a0.a0;
+ result += a1.a2.a1.a0.a1;
+ result += a1.a2.a1.a1.a0;
+ result += a1.a2.a2;
+ result += a1.a2.a3;
+ result += a1.a3;
+ result += a2.a0;
+ result += a2.a1.a0.a0;
+ result += a2.a1.a0.a1.a0.a0;
+ result += a2.a1.a0.a1.a0.a1;
+ result += a2.a1.a0.a1.a1.a0;
+ result += a2.a1.a0.a2;
+ result += a2.a1.a0.a3.a0.a0;
+ result += a2.a1.a0.a3.a1;
+ result += a2.a1.a0.a4;
+ result += a2.a1.a0.a5.a0.a0;
+ result += a2.a1.a0.a5.a1.a0;
+ result += a2.a1.a0.a6;
+ result += a2.a1.a1.a0.a0;
+ result += a2.a1.a1.a0.a1;
+ result += a2.a1.a1.a1.a0;
+ result += a2.a1.a2;
+ result += a2.a1.a3;
+ result += a2.a2.a0.a0;
+ result += a2.a2.a0.a1.a0.a0;
+ result += a2.a2.a0.a1.a0.a1;
+ result += a2.a2.a0.a1.a1.a0;
+ result += a2.a2.a0.a2;
+ result += a2.a2.a0.a3.a0.a0;
+ result += a2.a2.a0.a3.a1;
+ result += a2.a2.a0.a4;
+ result += a2.a2.a0.a5.a0.a0;
+ result += a2.a2.a0.a5.a1.a0;
+ result += a2.a2.a0.a6;
+ result += a2.a2.a1.a0.a0;
+ result += a2.a2.a1.a0.a1;
+ result += a2.a2.a1.a1.a0;
+ result += a2.a2.a2;
+ result += a2.a2.a3;
+ result += a2.a3;
+ result += a3.a0;
+ result += a3.a1.a0.a0;
+ result += a3.a1.a0.a1.a0.a0;
+ result += a3.a1.a0.a1.a0.a1;
+ result += a3.a1.a0.a1.a1.a0;
+ result += a3.a1.a0.a2;
+ result += a3.a1.a0.a3.a0.a0;
+ result += a3.a1.a0.a3.a1;
+ result += a3.a1.a0.a4;
+ result += a3.a1.a0.a5.a0.a0;
+ result += a3.a1.a0.a5.a1.a0;
+ result += a3.a1.a0.a6;
+ result += a3.a1.a1.a0.a0;
+ result += a3.a1.a1.a0.a1;
+ result += a3.a1.a1.a1.a0;
+ result += a3.a1.a2;
+ result += a3.a1.a3;
+ result += a3.a2.a0.a0;
+ result += a3.a2.a0.a1.a0.a0;
+ result += a3.a2.a0.a1.a0.a1;
+ result += a3.a2.a0.a1.a1.a0;
+ result += a3.a2.a0.a2;
+ result += a3.a2.a0.a3.a0.a0;
+ result += a3.a2.a0.a3.a1;
+ result += a3.a2.a0.a4;
+ result += a3.a2.a0.a5.a0.a0;
+ result += a3.a2.a0.a5.a1.a0;
+ result += a3.a2.a0.a6;
+ result += a3.a2.a1.a0.a0;
+ result += a3.a2.a1.a0.a1;
+ result += a3.a2.a1.a1.a0;
+ result += a3.a2.a2;
+ result += a3.a2.a3;
+ result += a3.a3;
+
+ std::cout << "result = " << result << "\n";
+
+ return result;
+}
+
+// Used for testing structs by value.
// Smallest struct with data.
DART_EXPORT Struct1ByteInt ReturnStruct1ByteInt(int8_t a0) {
std::cout << "ReturnStruct1ByteInt"
@@ -3695,6 +4401,349 @@
}
// Used for testing structs by value.
+// Simple nested struct.
+DART_EXPORT Struct8BytesNestedInt
+ReturnStruct8BytesNestedInt(Struct4BytesHomogeneousInt16 a0,
+ Struct4BytesHomogeneousInt16 a1) {
+ std::cout << "ReturnStruct8BytesNestedInt"
+ << "((" << a0.a0 << ", " << a0.a1 << "), (" << a1.a0 << ", "
+ << a1.a1 << "))"
+ << "\n";
+
+ Struct8BytesNestedInt result;
+
+ result.a0.a0 = a0.a0;
+ result.a0.a1 = a0.a1;
+ result.a1.a0 = a1.a0;
+ result.a1.a1 = a1.a1;
+
+ std::cout << "result = "
+ << "((" << result.a0.a0 << ", " << result.a0.a1 << "), ("
+ << result.a1.a0 << ", " << result.a1.a1 << "))"
+ << "\n";
+
+ return result;
+}
+
+// Used for testing structs by value.
+// Simple nested struct with floats.
+DART_EXPORT Struct8BytesNestedFloat
+ReturnStruct8BytesNestedFloat(Struct4BytesFloat a0, Struct4BytesFloat a1) {
+ std::cout << "ReturnStruct8BytesNestedFloat"
+ << "((" << a0.a0 << "), (" << a1.a0 << "))"
+ << "\n";
+
+ Struct8BytesNestedFloat result;
+
+ result.a0.a0 = a0.a0;
+ result.a1.a0 = a1.a0;
+
+ std::cout << "result = "
+ << "((" << result.a0.a0 << "), (" << result.a1.a0 << "))"
+ << "\n";
+
+ return result;
+}
+
+// Used for testing structs by value.
+// The nesting is irregular, testing homogenous float rules on arm and arm64,
+// and the fpu register usage on x64.
+DART_EXPORT Struct8BytesNestedFloat2
+ReturnStruct8BytesNestedFloat2(Struct4BytesFloat a0, float a1) {
+ std::cout << "ReturnStruct8BytesNestedFloat2"
+ << "((" << a0.a0 << "), " << a1 << ")"
+ << "\n";
+
+ Struct8BytesNestedFloat2 result;
+
+ result.a0.a0 = a0.a0;
+ result.a1 = a1;
+
+ std::cout << "result = "
+ << "((" << result.a0.a0 << "), " << result.a1 << ")"
+ << "\n";
+
+ return result;
+}
+
+// Used for testing structs by value.
+// Simple nested struct with mixed members.
+DART_EXPORT Struct8BytesNestedMixed
+ReturnStruct8BytesNestedMixed(Struct4BytesHomogeneousInt16 a0,
+ Struct4BytesFloat a1) {
+ std::cout << "ReturnStruct8BytesNestedMixed"
+ << "((" << a0.a0 << ", " << a0.a1 << "), (" << a1.a0 << "))"
+ << "\n";
+
+ Struct8BytesNestedMixed result;
+
+ result.a0.a0 = a0.a0;
+ result.a0.a1 = a0.a1;
+ result.a1.a0 = a1.a0;
+
+ std::cout << "result = "
+ << "((" << result.a0.a0 << ", " << result.a0.a1 << "), ("
+ << result.a1.a0 << "))"
+ << "\n";
+
+ return result;
+}
+
+// Used for testing structs by value.
+// Deeper nested struct to test recursive member access.
+DART_EXPORT Struct16BytesNestedInt
+ReturnStruct16BytesNestedInt(Struct8BytesNestedInt a0,
+ Struct8BytesNestedInt a1) {
+ std::cout << "ReturnStruct16BytesNestedInt"
+ << "(((" << a0.a0.a0 << ", " << a0.a0.a1 << "), (" << a0.a1.a0
+ << ", " << a0.a1.a1 << ")), ((" << a1.a0.a0 << ", " << a1.a0.a1
+ << "), (" << a1.a1.a0 << ", " << a1.a1.a1 << ")))"
+ << "\n";
+
+ Struct16BytesNestedInt result;
+
+ result.a0.a0.a0 = a0.a0.a0;
+ result.a0.a0.a1 = a0.a0.a1;
+ result.a0.a1.a0 = a0.a1.a0;
+ result.a0.a1.a1 = a0.a1.a1;
+ result.a1.a0.a0 = a1.a0.a0;
+ result.a1.a0.a1 = a1.a0.a1;
+ result.a1.a1.a0 = a1.a1.a0;
+ result.a1.a1.a1 = a1.a1.a1;
+
+ std::cout << "result = "
+ << "(((" << result.a0.a0.a0 << ", " << result.a0.a0.a1 << "), ("
+ << result.a0.a1.a0 << ", " << result.a0.a1.a1 << ")), (("
+ << result.a1.a0.a0 << ", " << result.a1.a0.a1 << "), ("
+ << result.a1.a1.a0 << ", " << result.a1.a1.a1 << ")))"
+ << "\n";
+
+ return result;
+}
+
+// Used for testing structs by value.
+// Even deeper nested struct to test recursive member access.
+DART_EXPORT Struct32BytesNestedInt
+ReturnStruct32BytesNestedInt(Struct16BytesNestedInt a0,
+ Struct16BytesNestedInt a1) {
+ std::cout << "ReturnStruct32BytesNestedInt"
+ << "((((" << a0.a0.a0.a0 << ", " << a0.a0.a0.a1 << "), ("
+ << a0.a0.a1.a0 << ", " << a0.a0.a1.a1 << ")), ((" << a0.a1.a0.a0
+ << ", " << a0.a1.a0.a1 << "), (" << a0.a1.a1.a0 << ", "
+ << a0.a1.a1.a1 << "))), (((" << a1.a0.a0.a0 << ", " << a1.a0.a0.a1
+ << "), (" << a1.a0.a1.a0 << ", " << a1.a0.a1.a1 << ")), (("
+ << a1.a1.a0.a0 << ", " << a1.a1.a0.a1 << "), (" << a1.a1.a1.a0
+ << ", " << a1.a1.a1.a1 << "))))"
+ << "\n";
+
+ Struct32BytesNestedInt result;
+
+ result.a0.a0.a0.a0 = a0.a0.a0.a0;
+ result.a0.a0.a0.a1 = a0.a0.a0.a1;
+ result.a0.a0.a1.a0 = a0.a0.a1.a0;
+ result.a0.a0.a1.a1 = a0.a0.a1.a1;
+ result.a0.a1.a0.a0 = a0.a1.a0.a0;
+ result.a0.a1.a0.a1 = a0.a1.a0.a1;
+ result.a0.a1.a1.a0 = a0.a1.a1.a0;
+ result.a0.a1.a1.a1 = a0.a1.a1.a1;
+ result.a1.a0.a0.a0 = a1.a0.a0.a0;
+ result.a1.a0.a0.a1 = a1.a0.a0.a1;
+ result.a1.a0.a1.a0 = a1.a0.a1.a0;
+ result.a1.a0.a1.a1 = a1.a0.a1.a1;
+ result.a1.a1.a0.a0 = a1.a1.a0.a0;
+ result.a1.a1.a0.a1 = a1.a1.a0.a1;
+ result.a1.a1.a1.a0 = a1.a1.a1.a0;
+ result.a1.a1.a1.a1 = a1.a1.a1.a1;
+
+ std::cout << "result = "
+ << "((((" << result.a0.a0.a0.a0 << ", " << result.a0.a0.a0.a1
+ << "), (" << result.a0.a0.a1.a0 << ", " << result.a0.a0.a1.a1
+ << ")), ((" << result.a0.a1.a0.a0 << ", " << result.a0.a1.a0.a1
+ << "), (" << result.a0.a1.a1.a0 << ", " << result.a0.a1.a1.a1
+ << "))), (((" << result.a1.a0.a0.a0 << ", " << result.a1.a0.a0.a1
+ << "), (" << result.a1.a0.a1.a0 << ", " << result.a1.a0.a1.a1
+ << ")), ((" << result.a1.a1.a0.a0 << ", " << result.a1.a1.a0.a1
+ << "), (" << result.a1.a1.a1.a0 << ", " << result.a1.a1.a1.a1
+ << "))))"
+ << "\n";
+
+ return result;
+}
+
+// Used for testing structs by value.
+// Test alignment and padding of nested struct with 16 byte int.
+DART_EXPORT StructNestedIntStructAlignmentInt16
+ReturnStructNestedIntStructAlignmentInt16(StructAlignmentInt16 a0,
+ StructAlignmentInt16 a1) {
+ std::cout << "ReturnStructNestedIntStructAlignmentInt16"
+ << "((" << static_cast<int>(a0.a0) << ", " << a0.a1 << ", "
+ << static_cast<int>(a0.a2) << "), (" << static_cast<int>(a1.a0)
+ << ", " << a1.a1 << ", " << static_cast<int>(a1.a2) << "))"
+ << "\n";
+
+ StructNestedIntStructAlignmentInt16 result;
+
+ result.a0.a0 = a0.a0;
+ result.a0.a1 = a0.a1;
+ result.a0.a2 = a0.a2;
+ result.a1.a0 = a1.a0;
+ result.a1.a1 = a1.a1;
+ result.a1.a2 = a1.a2;
+
+ std::cout << "result = "
+ << "((" << static_cast<int>(result.a0.a0) << ", " << result.a0.a1
+ << ", " << static_cast<int>(result.a0.a2) << "), ("
+ << static_cast<int>(result.a1.a0) << ", " << result.a1.a1 << ", "
+ << static_cast<int>(result.a1.a2) << "))"
+ << "\n";
+
+ return result;
+}
+
+// Used for testing structs by value.
+// Test alignment and padding of nested struct with 32 byte int.
+DART_EXPORT StructNestedIntStructAlignmentInt32
+ReturnStructNestedIntStructAlignmentInt32(StructAlignmentInt32 a0,
+ StructAlignmentInt32 a1) {
+ std::cout << "ReturnStructNestedIntStructAlignmentInt32"
+ << "((" << static_cast<int>(a0.a0) << ", " << a0.a1 << ", "
+ << static_cast<int>(a0.a2) << "), (" << static_cast<int>(a1.a0)
+ << ", " << a1.a1 << ", " << static_cast<int>(a1.a2) << "))"
+ << "\n";
+
+ StructNestedIntStructAlignmentInt32 result;
+
+ result.a0.a0 = a0.a0;
+ result.a0.a1 = a0.a1;
+ result.a0.a2 = a0.a2;
+ result.a1.a0 = a1.a0;
+ result.a1.a1 = a1.a1;
+ result.a1.a2 = a1.a2;
+
+ std::cout << "result = "
+ << "((" << static_cast<int>(result.a0.a0) << ", " << result.a0.a1
+ << ", " << static_cast<int>(result.a0.a2) << "), ("
+ << static_cast<int>(result.a1.a0) << ", " << result.a1.a1 << ", "
+ << static_cast<int>(result.a1.a2) << "))"
+ << "\n";
+
+ return result;
+}
+
+// Used for testing structs by value.
+// Test alignment and padding of nested struct with 64 byte int.
+DART_EXPORT StructNestedIntStructAlignmentInt64
+ReturnStructNestedIntStructAlignmentInt64(StructAlignmentInt64 a0,
+ StructAlignmentInt64 a1) {
+ std::cout << "ReturnStructNestedIntStructAlignmentInt64"
+ << "((" << static_cast<int>(a0.a0) << ", " << a0.a1 << ", "
+ << static_cast<int>(a0.a2) << "), (" << static_cast<int>(a1.a0)
+ << ", " << a1.a1 << ", " << static_cast<int>(a1.a2) << "))"
+ << "\n";
+
+ StructNestedIntStructAlignmentInt64 result;
+
+ result.a0.a0 = a0.a0;
+ result.a0.a1 = a0.a1;
+ result.a0.a2 = a0.a2;
+ result.a1.a0 = a1.a0;
+ result.a1.a1 = a1.a1;
+ result.a1.a2 = a1.a2;
+
+ std::cout << "result = "
+ << "((" << static_cast<int>(result.a0.a0) << ", " << result.a0.a1
+ << ", " << static_cast<int>(result.a0.a2) << "), ("
+ << static_cast<int>(result.a1.a0) << ", " << result.a1.a1 << ", "
+ << static_cast<int>(result.a1.a2) << "))"
+ << "\n";
+
+ return result;
+}
+
+// Used for testing structs by value.
+// Return big irregular struct as smoke test.
+DART_EXPORT StructNestedIrregularEvenBigger
+ReturnStructNestedIrregularEvenBigger(uint64_t a0,
+ StructNestedIrregularBigger a1,
+ StructNestedIrregularBigger a2,
+ double a3) {
+ std::cout << "ReturnStructNestedIrregularEvenBigger"
+ << "(" << a0 << ", ((" << a1.a0.a0 << ", ((" << a1.a0.a1.a0.a0
+ << ", " << a1.a0.a1.a0.a1 << "), (" << a1.a0.a1.a1.a0 << ")), "
+ << a1.a0.a2 << ", ((" << a1.a0.a3.a0.a0 << "), " << a1.a0.a3.a1
+ << "), " << a1.a0.a4 << ", ((" << a1.a0.a5.a0.a0 << "), ("
+ << a1.a0.a5.a1.a0 << ")), " << a1.a0.a6 << "), ((" << a1.a1.a0.a0
+ << ", " << a1.a1.a0.a1 << "), (" << a1.a1.a1.a0 << ")), " << a1.a2
+ << ", " << a1.a3 << "), ((" << a2.a0.a0 << ", ((" << a2.a0.a1.a0.a0
+ << ", " << a2.a0.a1.a0.a1 << "), (" << a2.a0.a1.a1.a0 << ")), "
+ << a2.a0.a2 << ", ((" << a2.a0.a3.a0.a0 << "), " << a2.a0.a3.a1
+ << "), " << a2.a0.a4 << ", ((" << a2.a0.a5.a0.a0 << "), ("
+ << a2.a0.a5.a1.a0 << ")), " << a2.a0.a6 << "), ((" << a2.a1.a0.a0
+ << ", " << a2.a1.a0.a1 << "), (" << a2.a1.a1.a0 << ")), " << a2.a2
+ << ", " << a2.a3 << "), " << a3 << ")"
+ << "\n";
+
+ StructNestedIrregularEvenBigger result;
+
+ result.a0 = a0;
+ result.a1.a0.a0 = a1.a0.a0;
+ result.a1.a0.a1.a0.a0 = a1.a0.a1.a0.a0;
+ result.a1.a0.a1.a0.a1 = a1.a0.a1.a0.a1;
+ result.a1.a0.a1.a1.a0 = a1.a0.a1.a1.a0;
+ result.a1.a0.a2 = a1.a0.a2;
+ result.a1.a0.a3.a0.a0 = a1.a0.a3.a0.a0;
+ result.a1.a0.a3.a1 = a1.a0.a3.a1;
+ result.a1.a0.a4 = a1.a0.a4;
+ result.a1.a0.a5.a0.a0 = a1.a0.a5.a0.a0;
+ result.a1.a0.a5.a1.a0 = a1.a0.a5.a1.a0;
+ result.a1.a0.a6 = a1.a0.a6;
+ result.a1.a1.a0.a0 = a1.a1.a0.a0;
+ result.a1.a1.a0.a1 = a1.a1.a0.a1;
+ result.a1.a1.a1.a0 = a1.a1.a1.a0;
+ result.a1.a2 = a1.a2;
+ result.a1.a3 = a1.a3;
+ result.a2.a0.a0 = a2.a0.a0;
+ result.a2.a0.a1.a0.a0 = a2.a0.a1.a0.a0;
+ result.a2.a0.a1.a0.a1 = a2.a0.a1.a0.a1;
+ result.a2.a0.a1.a1.a0 = a2.a0.a1.a1.a0;
+ result.a2.a0.a2 = a2.a0.a2;
+ result.a2.a0.a3.a0.a0 = a2.a0.a3.a0.a0;
+ result.a2.a0.a3.a1 = a2.a0.a3.a1;
+ result.a2.a0.a4 = a2.a0.a4;
+ result.a2.a0.a5.a0.a0 = a2.a0.a5.a0.a0;
+ result.a2.a0.a5.a1.a0 = a2.a0.a5.a1.a0;
+ result.a2.a0.a6 = a2.a0.a6;
+ result.a2.a1.a0.a0 = a2.a1.a0.a0;
+ result.a2.a1.a0.a1 = a2.a1.a0.a1;
+ result.a2.a1.a1.a0 = a2.a1.a1.a0;
+ result.a2.a2 = a2.a2;
+ result.a2.a3 = a2.a3;
+ result.a3 = a3;
+
+ std::cout << "result = "
+ << "(" << result.a0 << ", ((" << result.a1.a0.a0 << ", (("
+ << result.a1.a0.a1.a0.a0 << ", " << result.a1.a0.a1.a0.a1 << "), ("
+ << result.a1.a0.a1.a1.a0 << ")), " << result.a1.a0.a2 << ", (("
+ << result.a1.a0.a3.a0.a0 << "), " << result.a1.a0.a3.a1 << "), "
+ << result.a1.a0.a4 << ", ((" << result.a1.a0.a5.a0.a0 << "), ("
+ << result.a1.a0.a5.a1.a0 << ")), " << result.a1.a0.a6 << "), (("
+ << result.a1.a1.a0.a0 << ", " << result.a1.a1.a0.a1 << "), ("
+ << result.a1.a1.a1.a0 << ")), " << result.a1.a2 << ", "
+ << result.a1.a3 << "), ((" << result.a2.a0.a0 << ", (("
+ << result.a2.a0.a1.a0.a0 << ", " << result.a2.a0.a1.a0.a1 << "), ("
+ << result.a2.a0.a1.a1.a0 << ")), " << result.a2.a0.a2 << ", (("
+ << result.a2.a0.a3.a0.a0 << "), " << result.a2.a0.a3.a1 << "), "
+ << result.a2.a0.a4 << ", ((" << result.a2.a0.a5.a0.a0 << "), ("
+ << result.a2.a0.a5.a1.a0 << ")), " << result.a2.a0.a6 << "), (("
+ << result.a2.a1.a0.a0 << ", " << result.a2.a1.a0.a1 << "), ("
+ << result.a2.a1.a1.a0 << ")), " << result.a2.a2 << ", "
+ << result.a2.a3 << "), " << result.a3 << ")"
+ << "\n";
+
+ return result;
+}
+
+// Used for testing structs by value.
// Smallest struct with data.
// 10 struct arguments will exhaust available registers.
DART_EXPORT intptr_t TestPassStruct1ByteIntx10(
@@ -6790,6 +7839,874 @@
}
// Used for testing structs by value.
+// Simple nested struct. No alignment gaps on any architectures.
+// 10 arguments exhaust registers on all platforms.
+DART_EXPORT intptr_t TestPassStruct8BytesNestedIntx10(
+ // NOLINTNEXTLINE(whitespace/parens)
+ int64_t (*f)(Struct8BytesNestedInt a0,
+ Struct8BytesNestedInt a1,
+ Struct8BytesNestedInt a2,
+ Struct8BytesNestedInt a3,
+ Struct8BytesNestedInt a4,
+ Struct8BytesNestedInt a5,
+ Struct8BytesNestedInt a6,
+ Struct8BytesNestedInt a7,
+ Struct8BytesNestedInt a8,
+ Struct8BytesNestedInt a9)) {
+ Struct8BytesNestedInt a0;
+ Struct8BytesNestedInt a1;
+ Struct8BytesNestedInt a2;
+ Struct8BytesNestedInt a3;
+ Struct8BytesNestedInt a4;
+ Struct8BytesNestedInt a5;
+ Struct8BytesNestedInt a6;
+ Struct8BytesNestedInt a7;
+ Struct8BytesNestedInt a8;
+ Struct8BytesNestedInt a9;
+
+ a0.a0.a0 = -1;
+ a0.a0.a1 = 2;
+ a0.a1.a0 = -3;
+ a0.a1.a1 = 4;
+ a1.a0.a0 = -5;
+ a1.a0.a1 = 6;
+ a1.a1.a0 = -7;
+ a1.a1.a1 = 8;
+ a2.a0.a0 = -9;
+ a2.a0.a1 = 10;
+ a2.a1.a0 = -11;
+ a2.a1.a1 = 12;
+ a3.a0.a0 = -13;
+ a3.a0.a1 = 14;
+ a3.a1.a0 = -15;
+ a3.a1.a1 = 16;
+ a4.a0.a0 = -17;
+ a4.a0.a1 = 18;
+ a4.a1.a0 = -19;
+ a4.a1.a1 = 20;
+ a5.a0.a0 = -21;
+ a5.a0.a1 = 22;
+ a5.a1.a0 = -23;
+ a5.a1.a1 = 24;
+ a6.a0.a0 = -25;
+ a6.a0.a1 = 26;
+ a6.a1.a0 = -27;
+ a6.a1.a1 = 28;
+ a7.a0.a0 = -29;
+ a7.a0.a1 = 30;
+ a7.a1.a0 = -31;
+ a7.a1.a1 = 32;
+ a8.a0.a0 = -33;
+ a8.a0.a1 = 34;
+ a8.a1.a0 = -35;
+ a8.a1.a1 = 36;
+ a9.a0.a0 = -37;
+ a9.a0.a1 = 38;
+ a9.a1.a0 = -39;
+ a9.a1.a1 = 40;
+
+ std::cout << "Calling TestPassStruct8BytesNestedIntx10("
+ << "(((" << a0.a0.a0 << ", " << a0.a0.a1 << "), (" << a0.a1.a0
+ << ", " << a0.a1.a1 << ")), ((" << a1.a0.a0 << ", " << a1.a0.a1
+ << "), (" << a1.a1.a0 << ", " << a1.a1.a1 << ")), ((" << a2.a0.a0
+ << ", " << a2.a0.a1 << "), (" << a2.a1.a0 << ", " << a2.a1.a1
+ << ")), ((" << a3.a0.a0 << ", " << a3.a0.a1 << "), (" << a3.a1.a0
+ << ", " << a3.a1.a1 << ")), ((" << a4.a0.a0 << ", " << a4.a0.a1
+ << "), (" << a4.a1.a0 << ", " << a4.a1.a1 << ")), ((" << a5.a0.a0
+ << ", " << a5.a0.a1 << "), (" << a5.a1.a0 << ", " << a5.a1.a1
+ << ")), ((" << a6.a0.a0 << ", " << a6.a0.a1 << "), (" << a6.a1.a0
+ << ", " << a6.a1.a1 << ")), ((" << a7.a0.a0 << ", " << a7.a0.a1
+ << "), (" << a7.a1.a0 << ", " << a7.a1.a1 << ")), ((" << a8.a0.a0
+ << ", " << a8.a0.a1 << "), (" << a8.a1.a0 << ", " << a8.a1.a1
+ << ")), ((" << a9.a0.a0 << ", " << a9.a0.a1 << "), (" << a9.a1.a0
+ << ", " << a9.a1.a1 << ")))"
+ << ")\n";
+
+ int64_t result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+ std::cout << "result = " << result << "\n";
+
+ CHECK_EQ(20, result);
+
+ // Pass argument that will make the Dart callback throw.
+ a0.a0.a0 = 42;
+
+ result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+ CHECK_EQ(0, result);
+
+ // Pass argument that will make the Dart callback return null.
+ a0.a0.a0 = 84;
+
+ result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+ CHECK_EQ(0, result);
+
+ return 0;
+}
+
+// Used for testing structs by value.
+// Simple nested struct. No alignment gaps on any architectures.
+// 10 arguments exhaust fpu registers on all platforms.
+DART_EXPORT intptr_t TestPassStruct8BytesNestedFloatx10(
+ // NOLINTNEXTLINE(whitespace/parens)
+ float (*f)(Struct8BytesNestedFloat a0,
+ Struct8BytesNestedFloat a1,
+ Struct8BytesNestedFloat a2,
+ Struct8BytesNestedFloat a3,
+ Struct8BytesNestedFloat a4,
+ Struct8BytesNestedFloat a5,
+ Struct8BytesNestedFloat a6,
+ Struct8BytesNestedFloat a7,
+ Struct8BytesNestedFloat a8,
+ Struct8BytesNestedFloat a9)) {
+ Struct8BytesNestedFloat a0;
+ Struct8BytesNestedFloat a1;
+ Struct8BytesNestedFloat a2;
+ Struct8BytesNestedFloat a3;
+ Struct8BytesNestedFloat a4;
+ Struct8BytesNestedFloat a5;
+ Struct8BytesNestedFloat a6;
+ Struct8BytesNestedFloat a7;
+ Struct8BytesNestedFloat a8;
+ Struct8BytesNestedFloat a9;
+
+ a0.a0.a0 = -1.0;
+ a0.a1.a0 = 2.0;
+ a1.a0.a0 = -3.0;
+ a1.a1.a0 = 4.0;
+ a2.a0.a0 = -5.0;
+ a2.a1.a0 = 6.0;
+ a3.a0.a0 = -7.0;
+ a3.a1.a0 = 8.0;
+ a4.a0.a0 = -9.0;
+ a4.a1.a0 = 10.0;
+ a5.a0.a0 = -11.0;
+ a5.a1.a0 = 12.0;
+ a6.a0.a0 = -13.0;
+ a6.a1.a0 = 14.0;
+ a7.a0.a0 = -15.0;
+ a7.a1.a0 = 16.0;
+ a8.a0.a0 = -17.0;
+ a8.a1.a0 = 18.0;
+ a9.a0.a0 = -19.0;
+ a9.a1.a0 = 20.0;
+
+ std::cout << "Calling TestPassStruct8BytesNestedFloatx10("
+ << "(((" << a0.a0.a0 << "), (" << a0.a1.a0 << ")), ((" << a1.a0.a0
+ << "), (" << a1.a1.a0 << ")), ((" << a2.a0.a0 << "), (" << a2.a1.a0
+ << ")), ((" << a3.a0.a0 << "), (" << a3.a1.a0 << ")), (("
+ << a4.a0.a0 << "), (" << a4.a1.a0 << ")), ((" << a5.a0.a0 << "), ("
+ << a5.a1.a0 << ")), ((" << a6.a0.a0 << "), (" << a6.a1.a0
+ << ")), ((" << a7.a0.a0 << "), (" << a7.a1.a0 << ")), (("
+ << a8.a0.a0 << "), (" << a8.a1.a0 << ")), ((" << a9.a0.a0 << "), ("
+ << a9.a1.a0 << ")))"
+ << ")\n";
+
+ float result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+ std::cout << "result = " << result << "\n";
+
+ CHECK_APPROX(10.0, result);
+
+ // Pass argument that will make the Dart callback throw.
+ a0.a0.a0 = 42;
+
+ result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+ CHECK_APPROX(0.0, result);
+
+ // Pass argument that will make the Dart callback return null.
+ a0.a0.a0 = 84;
+
+ result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+ CHECK_APPROX(0.0, result);
+
+ return 0;
+}
+
+// Used for testing structs by value.
+// Simple nested struct. No alignment gaps on any architectures.
+// 10 arguments exhaust fpu registers on all platforms.
+// The nesting is irregular, testing homogenous float rules on arm and arm64,
+// and the fpu register usage on x64.
+DART_EXPORT intptr_t TestPassStruct8BytesNestedFloat2x10(
+ // NOLINTNEXTLINE(whitespace/parens)
+ float (*f)(Struct8BytesNestedFloat2 a0,
+ Struct8BytesNestedFloat2 a1,
+ Struct8BytesNestedFloat2 a2,
+ Struct8BytesNestedFloat2 a3,
+ Struct8BytesNestedFloat2 a4,
+ Struct8BytesNestedFloat2 a5,
+ Struct8BytesNestedFloat2 a6,
+ Struct8BytesNestedFloat2 a7,
+ Struct8BytesNestedFloat2 a8,
+ Struct8BytesNestedFloat2 a9)) {
+ Struct8BytesNestedFloat2 a0;
+ Struct8BytesNestedFloat2 a1;
+ Struct8BytesNestedFloat2 a2;
+ Struct8BytesNestedFloat2 a3;
+ Struct8BytesNestedFloat2 a4;
+ Struct8BytesNestedFloat2 a5;
+ Struct8BytesNestedFloat2 a6;
+ Struct8BytesNestedFloat2 a7;
+ Struct8BytesNestedFloat2 a8;
+ Struct8BytesNestedFloat2 a9;
+
+ a0.a0.a0 = -1.0;
+ a0.a1 = 2.0;
+ a1.a0.a0 = -3.0;
+ a1.a1 = 4.0;
+ a2.a0.a0 = -5.0;
+ a2.a1 = 6.0;
+ a3.a0.a0 = -7.0;
+ a3.a1 = 8.0;
+ a4.a0.a0 = -9.0;
+ a4.a1 = 10.0;
+ a5.a0.a0 = -11.0;
+ a5.a1 = 12.0;
+ a6.a0.a0 = -13.0;
+ a6.a1 = 14.0;
+ a7.a0.a0 = -15.0;
+ a7.a1 = 16.0;
+ a8.a0.a0 = -17.0;
+ a8.a1 = 18.0;
+ a9.a0.a0 = -19.0;
+ a9.a1 = 20.0;
+
+ std::cout << "Calling TestPassStruct8BytesNestedFloat2x10("
+ << "(((" << a0.a0.a0 << "), " << a0.a1 << "), ((" << a1.a0.a0
+ << "), " << a1.a1 << "), ((" << a2.a0.a0 << "), " << a2.a1
+ << "), ((" << a3.a0.a0 << "), " << a3.a1 << "), ((" << a4.a0.a0
+ << "), " << a4.a1 << "), ((" << a5.a0.a0 << "), " << a5.a1
+ << "), ((" << a6.a0.a0 << "), " << a6.a1 << "), ((" << a7.a0.a0
+ << "), " << a7.a1 << "), ((" << a8.a0.a0 << "), " << a8.a1
+ << "), ((" << a9.a0.a0 << "), " << a9.a1 << "))"
+ << ")\n";
+
+ float result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+ std::cout << "result = " << result << "\n";
+
+ CHECK_APPROX(10.0, result);
+
+ // Pass argument that will make the Dart callback throw.
+ a0.a0.a0 = 42;
+
+ result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+ CHECK_APPROX(0.0, result);
+
+ // Pass argument that will make the Dart callback return null.
+ a0.a0.a0 = 84;
+
+ result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+ CHECK_APPROX(0.0, result);
+
+ return 0;
+}
+
+// Used for testing structs by value.
+// Simple nested struct. No alignment gaps on any architectures.
+// 10 arguments exhaust all registers on all platforms.
+DART_EXPORT intptr_t TestPassStruct8BytesNestedMixedx10(
+ // NOLINTNEXTLINE(whitespace/parens)
+ double (*f)(Struct8BytesNestedMixed a0,
+ Struct8BytesNestedMixed a1,
+ Struct8BytesNestedMixed a2,
+ Struct8BytesNestedMixed a3,
+ Struct8BytesNestedMixed a4,
+ Struct8BytesNestedMixed a5,
+ Struct8BytesNestedMixed a6,
+ Struct8BytesNestedMixed a7,
+ Struct8BytesNestedMixed a8,
+ Struct8BytesNestedMixed a9)) {
+ Struct8BytesNestedMixed a0;
+ Struct8BytesNestedMixed a1;
+ Struct8BytesNestedMixed a2;
+ Struct8BytesNestedMixed a3;
+ Struct8BytesNestedMixed a4;
+ Struct8BytesNestedMixed a5;
+ Struct8BytesNestedMixed a6;
+ Struct8BytesNestedMixed a7;
+ Struct8BytesNestedMixed a8;
+ Struct8BytesNestedMixed a9;
+
+ a0.a0.a0 = -1;
+ a0.a0.a1 = 2;
+ a0.a1.a0 = -3.0;
+ a1.a0.a0 = 4;
+ a1.a0.a1 = -5;
+ a1.a1.a0 = 6.0;
+ a2.a0.a0 = -7;
+ a2.a0.a1 = 8;
+ a2.a1.a0 = -9.0;
+ a3.a0.a0 = 10;
+ a3.a0.a1 = -11;
+ a3.a1.a0 = 12.0;
+ a4.a0.a0 = -13;
+ a4.a0.a1 = 14;
+ a4.a1.a0 = -15.0;
+ a5.a0.a0 = 16;
+ a5.a0.a1 = -17;
+ a5.a1.a0 = 18.0;
+ a6.a0.a0 = -19;
+ a6.a0.a1 = 20;
+ a6.a1.a0 = -21.0;
+ a7.a0.a0 = 22;
+ a7.a0.a1 = -23;
+ a7.a1.a0 = 24.0;
+ a8.a0.a0 = -25;
+ a8.a0.a1 = 26;
+ a8.a1.a0 = -27.0;
+ a9.a0.a0 = 28;
+ a9.a0.a1 = -29;
+ a9.a1.a0 = 30.0;
+
+ std::cout << "Calling TestPassStruct8BytesNestedMixedx10("
+ << "(((" << a0.a0.a0 << ", " << a0.a0.a1 << "), (" << a0.a1.a0
+ << ")), ((" << a1.a0.a0 << ", " << a1.a0.a1 << "), (" << a1.a1.a0
+ << ")), ((" << a2.a0.a0 << ", " << a2.a0.a1 << "), (" << a2.a1.a0
+ << ")), ((" << a3.a0.a0 << ", " << a3.a0.a1 << "), (" << a3.a1.a0
+ << ")), ((" << a4.a0.a0 << ", " << a4.a0.a1 << "), (" << a4.a1.a0
+ << ")), ((" << a5.a0.a0 << ", " << a5.a0.a1 << "), (" << a5.a1.a0
+ << ")), ((" << a6.a0.a0 << ", " << a6.a0.a1 << "), (" << a6.a1.a0
+ << ")), ((" << a7.a0.a0 << ", " << a7.a0.a1 << "), (" << a7.a1.a0
+ << ")), ((" << a8.a0.a0 << ", " << a8.a0.a1 << "), (" << a8.a1.a0
+ << ")), ((" << a9.a0.a0 << ", " << a9.a0.a1 << "), (" << a9.a1.a0
+ << ")))"
+ << ")\n";
+
+ double result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+ std::cout << "result = " << result << "\n";
+
+ CHECK_APPROX(15.0, result);
+
+ // Pass argument that will make the Dart callback throw.
+ a0.a0.a0 = 42;
+
+ result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+ CHECK_APPROX(0.0, result);
+
+ // Pass argument that will make the Dart callback return null.
+ a0.a0.a0 = 84;
+
+ result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+ CHECK_APPROX(0.0, result);
+
+ return 0;
+}
+
+// Used for testing structs by value.
+// Deeper nested struct to test recursive member access.
+DART_EXPORT intptr_t TestPassStruct16BytesNestedIntx2(
+ // NOLINTNEXTLINE(whitespace/parens)
+ int64_t (*f)(Struct16BytesNestedInt a0, Struct16BytesNestedInt a1)) {
+ Struct16BytesNestedInt a0;
+ Struct16BytesNestedInt a1;
+
+ a0.a0.a0.a0 = -1;
+ a0.a0.a0.a1 = 2;
+ a0.a0.a1.a0 = -3;
+ a0.a0.a1.a1 = 4;
+ a0.a1.a0.a0 = -5;
+ a0.a1.a0.a1 = 6;
+ a0.a1.a1.a0 = -7;
+ a0.a1.a1.a1 = 8;
+ a1.a0.a0.a0 = -9;
+ a1.a0.a0.a1 = 10;
+ a1.a0.a1.a0 = -11;
+ a1.a0.a1.a1 = 12;
+ a1.a1.a0.a0 = -13;
+ a1.a1.a0.a1 = 14;
+ a1.a1.a1.a0 = -15;
+ a1.a1.a1.a1 = 16;
+
+ std::cout << "Calling TestPassStruct16BytesNestedIntx2("
+ << "((((" << a0.a0.a0.a0 << ", " << a0.a0.a0.a1 << "), ("
+ << a0.a0.a1.a0 << ", " << a0.a0.a1.a1 << ")), ((" << a0.a1.a0.a0
+ << ", " << a0.a1.a0.a1 << "), (" << a0.a1.a1.a0 << ", "
+ << a0.a1.a1.a1 << "))), (((" << a1.a0.a0.a0 << ", " << a1.a0.a0.a1
+ << "), (" << a1.a0.a1.a0 << ", " << a1.a0.a1.a1 << ")), (("
+ << a1.a1.a0.a0 << ", " << a1.a1.a0.a1 << "), (" << a1.a1.a1.a0
+ << ", " << a1.a1.a1.a1 << "))))"
+ << ")\n";
+
+ int64_t result = f(a0, a1);
+
+ std::cout << "result = " << result << "\n";
+
+ CHECK_EQ(8, result);
+
+ // Pass argument that will make the Dart callback throw.
+ a0.a0.a0.a0 = 42;
+
+ result = f(a0, a1);
+
+ CHECK_EQ(0, result);
+
+ // Pass argument that will make the Dart callback return null.
+ a0.a0.a0.a0 = 84;
+
+ result = f(a0, a1);
+
+ CHECK_EQ(0, result);
+
+ return 0;
+}
+
+// Used for testing structs by value.
+// Even deeper nested struct to test recursive member access.
+DART_EXPORT intptr_t TestPassStruct32BytesNestedIntx2(
+ // NOLINTNEXTLINE(whitespace/parens)
+ int64_t (*f)(Struct32BytesNestedInt a0, Struct32BytesNestedInt a1)) {
+ Struct32BytesNestedInt a0;
+ Struct32BytesNestedInt a1;
+
+ a0.a0.a0.a0.a0 = -1;
+ a0.a0.a0.a0.a1 = 2;
+ a0.a0.a0.a1.a0 = -3;
+ a0.a0.a0.a1.a1 = 4;
+ a0.a0.a1.a0.a0 = -5;
+ a0.a0.a1.a0.a1 = 6;
+ a0.a0.a1.a1.a0 = -7;
+ a0.a0.a1.a1.a1 = 8;
+ a0.a1.a0.a0.a0 = -9;
+ a0.a1.a0.a0.a1 = 10;
+ a0.a1.a0.a1.a0 = -11;
+ a0.a1.a0.a1.a1 = 12;
+ a0.a1.a1.a0.a0 = -13;
+ a0.a1.a1.a0.a1 = 14;
+ a0.a1.a1.a1.a0 = -15;
+ a0.a1.a1.a1.a1 = 16;
+ a1.a0.a0.a0.a0 = -17;
+ a1.a0.a0.a0.a1 = 18;
+ a1.a0.a0.a1.a0 = -19;
+ a1.a0.a0.a1.a1 = 20;
+ a1.a0.a1.a0.a0 = -21;
+ a1.a0.a1.a0.a1 = 22;
+ a1.a0.a1.a1.a0 = -23;
+ a1.a0.a1.a1.a1 = 24;
+ a1.a1.a0.a0.a0 = -25;
+ a1.a1.a0.a0.a1 = 26;
+ a1.a1.a0.a1.a0 = -27;
+ a1.a1.a0.a1.a1 = 28;
+ a1.a1.a1.a0.a0 = -29;
+ a1.a1.a1.a0.a1 = 30;
+ a1.a1.a1.a1.a0 = -31;
+ a1.a1.a1.a1.a1 = 32;
+
+ std::cout << "Calling TestPassStruct32BytesNestedIntx2("
+ << "(((((" << a0.a0.a0.a0.a0 << ", " << a0.a0.a0.a0.a1 << "), ("
+ << a0.a0.a0.a1.a0 << ", " << a0.a0.a0.a1.a1 << ")), (("
+ << a0.a0.a1.a0.a0 << ", " << a0.a0.a1.a0.a1 << "), ("
+ << a0.a0.a1.a1.a0 << ", " << a0.a0.a1.a1.a1 << "))), ((("
+ << a0.a1.a0.a0.a0 << ", " << a0.a1.a0.a0.a1 << "), ("
+ << a0.a1.a0.a1.a0 << ", " << a0.a1.a0.a1.a1 << ")), (("
+ << a0.a1.a1.a0.a0 << ", " << a0.a1.a1.a0.a1 << "), ("
+ << a0.a1.a1.a1.a0 << ", " << a0.a1.a1.a1.a1 << ")))), (((("
+ << a1.a0.a0.a0.a0 << ", " << a1.a0.a0.a0.a1 << "), ("
+ << a1.a0.a0.a1.a0 << ", " << a1.a0.a0.a1.a1 << ")), (("
+ << a1.a0.a1.a0.a0 << ", " << a1.a0.a1.a0.a1 << "), ("
+ << a1.a0.a1.a1.a0 << ", " << a1.a0.a1.a1.a1 << "))), ((("
+ << a1.a1.a0.a0.a0 << ", " << a1.a1.a0.a0.a1 << "), ("
+ << a1.a1.a0.a1.a0 << ", " << a1.a1.a0.a1.a1 << ")), (("
+ << a1.a1.a1.a0.a0 << ", " << a1.a1.a1.a0.a1 << "), ("
+ << a1.a1.a1.a1.a0 << ", " << a1.a1.a1.a1.a1 << ")))))"
+ << ")\n";
+
+ int64_t result = f(a0, a1);
+
+ std::cout << "result = " << result << "\n";
+
+ CHECK_EQ(16, result);
+
+ // Pass argument that will make the Dart callback throw.
+ a0.a0.a0.a0.a0 = 42;
+
+ result = f(a0, a1);
+
+ CHECK_EQ(0, result);
+
+ // Pass argument that will make the Dart callback return null.
+ a0.a0.a0.a0.a0 = 84;
+
+ result = f(a0, a1);
+
+ CHECK_EQ(0, result);
+
+ return 0;
+}
+
+// Used for testing structs by value.
+// Test alignment and padding of nested struct with 16 byte int.
+DART_EXPORT intptr_t TestPassStructNestedIntStructAlignmentInt16(
+ // NOLINTNEXTLINE(whitespace/parens)
+ int64_t (*f)(StructNestedIntStructAlignmentInt16 a0)) {
+ StructNestedIntStructAlignmentInt16 a0;
+
+ a0.a0.a0 = -1;
+ a0.a0.a1 = 2;
+ a0.a0.a2 = -3;
+ a0.a1.a0 = 4;
+ a0.a1.a1 = -5;
+ a0.a1.a2 = 6;
+
+ std::cout << "Calling TestPassStructNestedIntStructAlignmentInt16("
+ << "(((" << static_cast<int>(a0.a0.a0) << ", " << a0.a0.a1 << ", "
+ << static_cast<int>(a0.a0.a2) << "), ("
+ << static_cast<int>(a0.a1.a0) << ", " << a0.a1.a1 << ", "
+ << static_cast<int>(a0.a1.a2) << ")))"
+ << ")\n";
+
+ int64_t result = f(a0);
+
+ std::cout << "result = " << result << "\n";
+
+ CHECK_EQ(3, result);
+
+ // Pass argument that will make the Dart callback throw.
+ a0.a0.a0 = 42;
+
+ result = f(a0);
+
+ CHECK_EQ(0, result);
+
+ // Pass argument that will make the Dart callback return null.
+ a0.a0.a0 = 84;
+
+ result = f(a0);
+
+ CHECK_EQ(0, result);
+
+ return 0;
+}
+
+// Used for testing structs by value.
+// Test alignment and padding of nested struct with 32 byte int.
+DART_EXPORT intptr_t TestPassStructNestedIntStructAlignmentInt32(
+ // NOLINTNEXTLINE(whitespace/parens)
+ int64_t (*f)(StructNestedIntStructAlignmentInt32 a0)) {
+ StructNestedIntStructAlignmentInt32 a0;
+
+ a0.a0.a0 = -1;
+ a0.a0.a1 = 2;
+ a0.a0.a2 = -3;
+ a0.a1.a0 = 4;
+ a0.a1.a1 = -5;
+ a0.a1.a2 = 6;
+
+ std::cout << "Calling TestPassStructNestedIntStructAlignmentInt32("
+ << "(((" << static_cast<int>(a0.a0.a0) << ", " << a0.a0.a1 << ", "
+ << static_cast<int>(a0.a0.a2) << "), ("
+ << static_cast<int>(a0.a1.a0) << ", " << a0.a1.a1 << ", "
+ << static_cast<int>(a0.a1.a2) << ")))"
+ << ")\n";
+
+ int64_t result = f(a0);
+
+ std::cout << "result = " << result << "\n";
+
+ CHECK_EQ(3, result);
+
+ // Pass argument that will make the Dart callback throw.
+ a0.a0.a0 = 42;
+
+ result = f(a0);
+
+ CHECK_EQ(0, result);
+
+ // Pass argument that will make the Dart callback return null.
+ a0.a0.a0 = 84;
+
+ result = f(a0);
+
+ CHECK_EQ(0, result);
+
+ return 0;
+}
+
+// Used for testing structs by value.
+// Test alignment and padding of nested struct with 64 byte int.
+DART_EXPORT intptr_t TestPassStructNestedIntStructAlignmentInt64(
+ // NOLINTNEXTLINE(whitespace/parens)
+ int64_t (*f)(StructNestedIntStructAlignmentInt64 a0)) {
+ StructNestedIntStructAlignmentInt64 a0;
+
+ a0.a0.a0 = -1;
+ a0.a0.a1 = 2;
+ a0.a0.a2 = -3;
+ a0.a1.a0 = 4;
+ a0.a1.a1 = -5;
+ a0.a1.a2 = 6;
+
+ std::cout << "Calling TestPassStructNestedIntStructAlignmentInt64("
+ << "(((" << static_cast<int>(a0.a0.a0) << ", " << a0.a0.a1 << ", "
+ << static_cast<int>(a0.a0.a2) << "), ("
+ << static_cast<int>(a0.a1.a0) << ", " << a0.a1.a1 << ", "
+ << static_cast<int>(a0.a1.a2) << ")))"
+ << ")\n";
+
+ int64_t result = f(a0);
+
+ std::cout << "result = " << result << "\n";
+
+ CHECK_EQ(3, result);
+
+ // Pass argument that will make the Dart callback throw.
+ a0.a0.a0 = 42;
+
+ result = f(a0);
+
+ CHECK_EQ(0, result);
+
+ // Pass argument that will make the Dart callback return null.
+ a0.a0.a0 = 84;
+
+ result = f(a0);
+
+ CHECK_EQ(0, result);
+
+ return 0;
+}
+
+// Used for testing structs by value.
+// Return big irregular struct as smoke test.
+DART_EXPORT intptr_t TestPassStructNestedIrregularEvenBiggerx4(
+ // NOLINTNEXTLINE(whitespace/parens)
+ double (*f)(StructNestedIrregularEvenBigger a0,
+ StructNestedIrregularEvenBigger a1,
+ StructNestedIrregularEvenBigger a2,
+ StructNestedIrregularEvenBigger a3)) {
+ StructNestedIrregularEvenBigger a0;
+ StructNestedIrregularEvenBigger a1;
+ StructNestedIrregularEvenBigger a2;
+ StructNestedIrregularEvenBigger a3;
+
+ a0.a0 = 1;
+ a0.a1.a0.a0 = 2;
+ a0.a1.a0.a1.a0.a0 = -3;
+ a0.a1.a0.a1.a0.a1 = 4;
+ a0.a1.a0.a1.a1.a0 = -5.0;
+ a0.a1.a0.a2 = 6;
+ a0.a1.a0.a3.a0.a0 = -7.0;
+ a0.a1.a0.a3.a1 = 8.0;
+ a0.a1.a0.a4 = 9;
+ a0.a1.a0.a5.a0.a0 = 10.0;
+ a0.a1.a0.a5.a1.a0 = -11.0;
+ a0.a1.a0.a6 = 12;
+ a0.a1.a1.a0.a0 = -13;
+ a0.a1.a1.a0.a1 = 14;
+ a0.a1.a1.a1.a0 = -15.0;
+ a0.a1.a2 = 16.0;
+ a0.a1.a3 = -17.0;
+ a0.a2.a0.a0 = 18;
+ a0.a2.a0.a1.a0.a0 = -19;
+ a0.a2.a0.a1.a0.a1 = 20;
+ a0.a2.a0.a1.a1.a0 = -21.0;
+ a0.a2.a0.a2 = 22;
+ a0.a2.a0.a3.a0.a0 = -23.0;
+ a0.a2.a0.a3.a1 = 24.0;
+ a0.a2.a0.a4 = 25;
+ a0.a2.a0.a5.a0.a0 = 26.0;
+ a0.a2.a0.a5.a1.a0 = -27.0;
+ a0.a2.a0.a6 = 28;
+ a0.a2.a1.a0.a0 = -29;
+ a0.a2.a1.a0.a1 = 30;
+ a0.a2.a1.a1.a0 = -31.0;
+ a0.a2.a2 = 32.0;
+ a0.a2.a3 = -33.0;
+ a0.a3 = 34.0;
+ a1.a0 = 35;
+ a1.a1.a0.a0 = 36;
+ a1.a1.a0.a1.a0.a0 = -37;
+ a1.a1.a0.a1.a0.a1 = 38;
+ a1.a1.a0.a1.a1.a0 = -39.0;
+ a1.a1.a0.a2 = 40;
+ a1.a1.a0.a3.a0.a0 = -41.0;
+ a1.a1.a0.a3.a1 = 42.0;
+ a1.a1.a0.a4 = 43;
+ a1.a1.a0.a5.a0.a0 = 44.0;
+ a1.a1.a0.a5.a1.a0 = -45.0;
+ a1.a1.a0.a6 = 46;
+ a1.a1.a1.a0.a0 = -47;
+ a1.a1.a1.a0.a1 = 48;
+ a1.a1.a1.a1.a0 = -49.0;
+ a1.a1.a2 = 50.0;
+ a1.a1.a3 = -51.0;
+ a1.a2.a0.a0 = 52;
+ a1.a2.a0.a1.a0.a0 = -53;
+ a1.a2.a0.a1.a0.a1 = 54;
+ a1.a2.a0.a1.a1.a0 = -55.0;
+ a1.a2.a0.a2 = 56;
+ a1.a2.a0.a3.a0.a0 = -57.0;
+ a1.a2.a0.a3.a1 = 58.0;
+ a1.a2.a0.a4 = 59;
+ a1.a2.a0.a5.a0.a0 = 60.0;
+ a1.a2.a0.a5.a1.a0 = -61.0;
+ a1.a2.a0.a6 = 62;
+ a1.a2.a1.a0.a0 = -63;
+ a1.a2.a1.a0.a1 = 64;
+ a1.a2.a1.a1.a0 = -65.0;
+ a1.a2.a2 = 66.0;
+ a1.a2.a3 = -67.0;
+ a1.a3 = 68.0;
+ a2.a0 = 69;
+ a2.a1.a0.a0 = 70;
+ a2.a1.a0.a1.a0.a0 = -71;
+ a2.a1.a0.a1.a0.a1 = 72;
+ a2.a1.a0.a1.a1.a0 = -73.0;
+ a2.a1.a0.a2 = 74;
+ a2.a1.a0.a3.a0.a0 = -75.0;
+ a2.a1.a0.a3.a1 = 76.0;
+ a2.a1.a0.a4 = 77;
+ a2.a1.a0.a5.a0.a0 = 78.0;
+ a2.a1.a0.a5.a1.a0 = -79.0;
+ a2.a1.a0.a6 = 80;
+ a2.a1.a1.a0.a0 = -81;
+ a2.a1.a1.a0.a1 = 82;
+ a2.a1.a1.a1.a0 = -83.0;
+ a2.a1.a2 = 84.0;
+ a2.a1.a3 = -85.0;
+ a2.a2.a0.a0 = 86;
+ a2.a2.a0.a1.a0.a0 = -87;
+ a2.a2.a0.a1.a0.a1 = 88;
+ a2.a2.a0.a1.a1.a0 = -89.0;
+ a2.a2.a0.a2 = 90;
+ a2.a2.a0.a3.a0.a0 = -91.0;
+ a2.a2.a0.a3.a1 = 92.0;
+ a2.a2.a0.a4 = 93;
+ a2.a2.a0.a5.a0.a0 = 94.0;
+ a2.a2.a0.a5.a1.a0 = -95.0;
+ a2.a2.a0.a6 = 96;
+ a2.a2.a1.a0.a0 = -97;
+ a2.a2.a1.a0.a1 = 98;
+ a2.a2.a1.a1.a0 = -99.0;
+ a2.a2.a2 = 100.0;
+ a2.a2.a3 = -101.0;
+ a2.a3 = 102.0;
+ a3.a0 = 103;
+ a3.a1.a0.a0 = 104;
+ a3.a1.a0.a1.a0.a0 = -105;
+ a3.a1.a0.a1.a0.a1 = 106;
+ a3.a1.a0.a1.a1.a0 = -107.0;
+ a3.a1.a0.a2 = 108;
+ a3.a1.a0.a3.a0.a0 = -109.0;
+ a3.a1.a0.a3.a1 = 110.0;
+ a3.a1.a0.a4 = 111;
+ a3.a1.a0.a5.a0.a0 = 112.0;
+ a3.a1.a0.a5.a1.a0 = -113.0;
+ a3.a1.a0.a6 = 114;
+ a3.a1.a1.a0.a0 = -115;
+ a3.a1.a1.a0.a1 = 116;
+ a3.a1.a1.a1.a0 = -117.0;
+ a3.a1.a2 = 118.0;
+ a3.a1.a3 = -119.0;
+ a3.a2.a0.a0 = 120;
+ a3.a2.a0.a1.a0.a0 = -121;
+ a3.a2.a0.a1.a0.a1 = 122;
+ a3.a2.a0.a1.a1.a0 = -123.0;
+ a3.a2.a0.a2 = 124;
+ a3.a2.a0.a3.a0.a0 = -125.0;
+ a3.a2.a0.a3.a1 = 126.0;
+ a3.a2.a0.a4 = 127;
+ a3.a2.a0.a5.a0.a0 = 128.0;
+ a3.a2.a0.a5.a1.a0 = -129.0;
+ a3.a2.a0.a6 = 130;
+ a3.a2.a1.a0.a0 = -131;
+ a3.a2.a1.a0.a1 = 132;
+ a3.a2.a1.a1.a0 = -133.0;
+ a3.a2.a2 = 134.0;
+ a3.a2.a3 = -135.0;
+ a3.a3 = 136.0;
+
+ std::cout
+ << "Calling TestPassStructNestedIrregularEvenBiggerx4("
+ << "((" << a0.a0 << ", ((" << a0.a1.a0.a0 << ", ((" << a0.a1.a0.a1.a0.a0
+ << ", " << a0.a1.a0.a1.a0.a1 << "), (" << a0.a1.a0.a1.a1.a0 << ")), "
+ << a0.a1.a0.a2 << ", ((" << a0.a1.a0.a3.a0.a0 << "), " << a0.a1.a0.a3.a1
+ << "), " << a0.a1.a0.a4 << ", ((" << a0.a1.a0.a5.a0.a0 << "), ("
+ << a0.a1.a0.a5.a1.a0 << ")), " << a0.a1.a0.a6 << "), ((" << a0.a1.a1.a0.a0
+ << ", " << a0.a1.a1.a0.a1 << "), (" << a0.a1.a1.a1.a0 << ")), "
+ << a0.a1.a2 << ", " << a0.a1.a3 << "), ((" << a0.a2.a0.a0 << ", (("
+ << a0.a2.a0.a1.a0.a0 << ", " << a0.a2.a0.a1.a0.a1 << "), ("
+ << a0.a2.a0.a1.a1.a0 << ")), " << a0.a2.a0.a2 << ", (("
+ << a0.a2.a0.a3.a0.a0 << "), " << a0.a2.a0.a3.a1 << "), " << a0.a2.a0.a4
+ << ", ((" << a0.a2.a0.a5.a0.a0 << "), (" << a0.a2.a0.a5.a1.a0 << ")), "
+ << a0.a2.a0.a6 << "), ((" << a0.a2.a1.a0.a0 << ", " << a0.a2.a1.a0.a1
+ << "), (" << a0.a2.a1.a1.a0 << ")), " << a0.a2.a2 << ", " << a0.a2.a3
+ << "), " << a0.a3 << "), (" << a1.a0 << ", ((" << a1.a1.a0.a0 << ", (("
+ << a1.a1.a0.a1.a0.a0 << ", " << a1.a1.a0.a1.a0.a1 << "), ("
+ << a1.a1.a0.a1.a1.a0 << ")), " << a1.a1.a0.a2 << ", (("
+ << a1.a1.a0.a3.a0.a0 << "), " << a1.a1.a0.a3.a1 << "), " << a1.a1.a0.a4
+ << ", ((" << a1.a1.a0.a5.a0.a0 << "), (" << a1.a1.a0.a5.a1.a0 << ")), "
+ << a1.a1.a0.a6 << "), ((" << a1.a1.a1.a0.a0 << ", " << a1.a1.a1.a0.a1
+ << "), (" << a1.a1.a1.a1.a0 << ")), " << a1.a1.a2 << ", " << a1.a1.a3
+ << "), ((" << a1.a2.a0.a0 << ", ((" << a1.a2.a0.a1.a0.a0 << ", "
+ << a1.a2.a0.a1.a0.a1 << "), (" << a1.a2.a0.a1.a1.a0 << ")), "
+ << a1.a2.a0.a2 << ", ((" << a1.a2.a0.a3.a0.a0 << "), " << a1.a2.a0.a3.a1
+ << "), " << a1.a2.a0.a4 << ", ((" << a1.a2.a0.a5.a0.a0 << "), ("
+ << a1.a2.a0.a5.a1.a0 << ")), " << a1.a2.a0.a6 << "), ((" << a1.a2.a1.a0.a0
+ << ", " << a1.a2.a1.a0.a1 << "), (" << a1.a2.a1.a1.a0 << ")), "
+ << a1.a2.a2 << ", " << a1.a2.a3 << "), " << a1.a3 << "), (" << a2.a0
+ << ", ((" << a2.a1.a0.a0 << ", ((" << a2.a1.a0.a1.a0.a0 << ", "
+ << a2.a1.a0.a1.a0.a1 << "), (" << a2.a1.a0.a1.a1.a0 << ")), "
+ << a2.a1.a0.a2 << ", ((" << a2.a1.a0.a3.a0.a0 << "), " << a2.a1.a0.a3.a1
+ << "), " << a2.a1.a0.a4 << ", ((" << a2.a1.a0.a5.a0.a0 << "), ("
+ << a2.a1.a0.a5.a1.a0 << ")), " << a2.a1.a0.a6 << "), ((" << a2.a1.a1.a0.a0
+ << ", " << a2.a1.a1.a0.a1 << "), (" << a2.a1.a1.a1.a0 << ")), "
+ << a2.a1.a2 << ", " << a2.a1.a3 << "), ((" << a2.a2.a0.a0 << ", (("
+ << a2.a2.a0.a1.a0.a0 << ", " << a2.a2.a0.a1.a0.a1 << "), ("
+ << a2.a2.a0.a1.a1.a0 << ")), " << a2.a2.a0.a2 << ", (("
+ << a2.a2.a0.a3.a0.a0 << "), " << a2.a2.a0.a3.a1 << "), " << a2.a2.a0.a4
+ << ", ((" << a2.a2.a0.a5.a0.a0 << "), (" << a2.a2.a0.a5.a1.a0 << ")), "
+ << a2.a2.a0.a6 << "), ((" << a2.a2.a1.a0.a0 << ", " << a2.a2.a1.a0.a1
+ << "), (" << a2.a2.a1.a1.a0 << ")), " << a2.a2.a2 << ", " << a2.a2.a3
+ << "), " << a2.a3 << "), (" << a3.a0 << ", ((" << a3.a1.a0.a0 << ", (("
+ << a3.a1.a0.a1.a0.a0 << ", " << a3.a1.a0.a1.a0.a1 << "), ("
+ << a3.a1.a0.a1.a1.a0 << ")), " << a3.a1.a0.a2 << ", (("
+ << a3.a1.a0.a3.a0.a0 << "), " << a3.a1.a0.a3.a1 << "), " << a3.a1.a0.a4
+ << ", ((" << a3.a1.a0.a5.a0.a0 << "), (" << a3.a1.a0.a5.a1.a0 << ")), "
+ << a3.a1.a0.a6 << "), ((" << a3.a1.a1.a0.a0 << ", " << a3.a1.a1.a0.a1
+ << "), (" << a3.a1.a1.a1.a0 << ")), " << a3.a1.a2 << ", " << a3.a1.a3
+ << "), ((" << a3.a2.a0.a0 << ", ((" << a3.a2.a0.a1.a0.a0 << ", "
+ << a3.a2.a0.a1.a0.a1 << "), (" << a3.a2.a0.a1.a1.a0 << ")), "
+ << a3.a2.a0.a2 << ", ((" << a3.a2.a0.a3.a0.a0 << "), " << a3.a2.a0.a3.a1
+ << "), " << a3.a2.a0.a4 << ", ((" << a3.a2.a0.a5.a0.a0 << "), ("
+ << a3.a2.a0.a5.a1.a0 << ")), " << a3.a2.a0.a6 << "), ((" << a3.a2.a1.a0.a0
+ << ", " << a3.a2.a1.a0.a1 << "), (" << a3.a2.a1.a1.a0 << ")), "
+ << a3.a2.a2 << ", " << a3.a2.a3 << "), " << a3.a3 << "))"
+ << ")\n";
+
+ double result = f(a0, a1, a2, a3);
+
+ std::cout << "result = " << result << "\n";
+
+ CHECK_APPROX(1572.0, result);
+
+ // Pass argument that will make the Dart callback throw.
+ a0.a0 = 42;
+
+ result = f(a0, a1, a2, a3);
+
+ CHECK_APPROX(0.0, result);
+
+ // Pass argument that will make the Dart callback return null.
+ a0.a0 = 84;
+
+ result = f(a0, a1, a2, a3);
+
+ CHECK_APPROX(0.0, result);
+
+ return 0;
+}
+
+// Used for testing structs by value.
// Smallest struct with data.
DART_EXPORT intptr_t TestReturnStruct1ByteInt(
// NOLINTNEXTLINE(whitespace/parens)
@@ -9387,4 +11304,780 @@
return 0;
}
+// Used for testing structs by value.
+// Simple nested struct.
+DART_EXPORT intptr_t TestReturnStruct8BytesNestedInt(
+ // NOLINTNEXTLINE(whitespace/parens)
+ Struct8BytesNestedInt (*f)(Struct4BytesHomogeneousInt16 a0,
+ Struct4BytesHomogeneousInt16 a1)) {
+ Struct4BytesHomogeneousInt16 a0;
+ Struct4BytesHomogeneousInt16 a1;
+
+ a0.a0 = -1;
+ a0.a1 = 2;
+ a1.a0 = -3;
+ a1.a1 = 4;
+
+ std::cout << "Calling TestReturnStruct8BytesNestedInt("
+ << "((" << a0.a0 << ", " << a0.a1 << "), (" << a1.a0 << ", "
+ << a1.a1 << "))"
+ << ")\n";
+
+ Struct8BytesNestedInt result = f(a0, a1);
+
+ std::cout << "result = "
+ << "((" << result.a0.a0 << ", " << result.a0.a1 << "), ("
+ << result.a1.a0 << ", " << result.a1.a1 << "))"
+ << "\n";
+
+ CHECK_EQ(a0.a0, result.a0.a0);
+ CHECK_EQ(a0.a1, result.a0.a1);
+ CHECK_EQ(a1.a0, result.a1.a0);
+ CHECK_EQ(a1.a1, result.a1.a1);
+
+ // Pass argument that will make the Dart callback throw.
+ a0.a0 = 42;
+
+ result = f(a0, a1);
+
+ CHECK_EQ(0, result.a0.a0);
+ CHECK_EQ(0, result.a0.a1);
+ CHECK_EQ(0, result.a1.a0);
+ CHECK_EQ(0, result.a1.a1);
+
+ // Pass argument that will make the Dart callback return null.
+ a0.a0 = 84;
+
+ result = f(a0, a1);
+
+ CHECK_EQ(0, result.a0.a0);
+ CHECK_EQ(0, result.a0.a1);
+ CHECK_EQ(0, result.a1.a0);
+ CHECK_EQ(0, result.a1.a1);
+
+ return 0;
+}
+
+// Used for testing structs by value.
+// Simple nested struct with floats.
+DART_EXPORT intptr_t TestReturnStruct8BytesNestedFloat(
+ // NOLINTNEXTLINE(whitespace/parens)
+ Struct8BytesNestedFloat (*f)(Struct4BytesFloat a0, Struct4BytesFloat a1)) {
+ Struct4BytesFloat a0;
+ Struct4BytesFloat a1;
+
+ a0.a0 = -1.0;
+ a1.a0 = 2.0;
+
+ std::cout << "Calling TestReturnStruct8BytesNestedFloat("
+ << "((" << a0.a0 << "), (" << a1.a0 << "))"
+ << ")\n";
+
+ Struct8BytesNestedFloat result = f(a0, a1);
+
+ std::cout << "result = "
+ << "((" << result.a0.a0 << "), (" << result.a1.a0 << "))"
+ << "\n";
+
+ CHECK_APPROX(a0.a0, result.a0.a0);
+ CHECK_APPROX(a1.a0, result.a1.a0);
+
+ // Pass argument that will make the Dart callback throw.
+ a0.a0 = 42;
+
+ result = f(a0, a1);
+
+ CHECK_APPROX(0.0, result.a0.a0);
+ CHECK_APPROX(0.0, result.a1.a0);
+
+ // Pass argument that will make the Dart callback return null.
+ a0.a0 = 84;
+
+ result = f(a0, a1);
+
+ CHECK_APPROX(0.0, result.a0.a0);
+ CHECK_APPROX(0.0, result.a1.a0);
+
+ return 0;
+}
+
+// Used for testing structs by value.
+// The nesting is irregular, testing homogenous float rules on arm and arm64,
+// and the fpu register usage on x64.
+DART_EXPORT intptr_t TestReturnStruct8BytesNestedFloat2(
+ // NOLINTNEXTLINE(whitespace/parens)
+ Struct8BytesNestedFloat2 (*f)(Struct4BytesFloat a0, float a1)) {
+ Struct4BytesFloat a0;
+ float a1;
+
+ a0.a0 = -1.0;
+ a1 = 2.0;
+
+ std::cout << "Calling TestReturnStruct8BytesNestedFloat2("
+ << "((" << a0.a0 << "), " << a1 << ")"
+ << ")\n";
+
+ Struct8BytesNestedFloat2 result = f(a0, a1);
+
+ std::cout << "result = "
+ << "((" << result.a0.a0 << "), " << result.a1 << ")"
+ << "\n";
+
+ CHECK_APPROX(a0.a0, result.a0.a0);
+ CHECK_APPROX(a1, result.a1);
+
+ // Pass argument that will make the Dart callback throw.
+ a0.a0 = 42;
+
+ result = f(a0, a1);
+
+ CHECK_APPROX(0.0, result.a0.a0);
+ CHECK_APPROX(0.0, result.a1);
+
+ // Pass argument that will make the Dart callback return null.
+ a0.a0 = 84;
+
+ result = f(a0, a1);
+
+ CHECK_APPROX(0.0, result.a0.a0);
+ CHECK_APPROX(0.0, result.a1);
+
+ return 0;
+}
+
+// Used for testing structs by value.
+// Simple nested struct with mixed members.
+DART_EXPORT intptr_t TestReturnStruct8BytesNestedMixed(
+ // NOLINTNEXTLINE(whitespace/parens)
+ Struct8BytesNestedMixed (*f)(Struct4BytesHomogeneousInt16 a0,
+ Struct4BytesFloat a1)) {
+ Struct4BytesHomogeneousInt16 a0;
+ Struct4BytesFloat a1;
+
+ a0.a0 = -1;
+ a0.a1 = 2;
+ a1.a0 = -3.0;
+
+ std::cout << "Calling TestReturnStruct8BytesNestedMixed("
+ << "((" << a0.a0 << ", " << a0.a1 << "), (" << a1.a0 << "))"
+ << ")\n";
+
+ Struct8BytesNestedMixed result = f(a0, a1);
+
+ std::cout << "result = "
+ << "((" << result.a0.a0 << ", " << result.a0.a1 << "), ("
+ << result.a1.a0 << "))"
+ << "\n";
+
+ CHECK_EQ(a0.a0, result.a0.a0);
+ CHECK_EQ(a0.a1, result.a0.a1);
+ CHECK_APPROX(a1.a0, result.a1.a0);
+
+ // Pass argument that will make the Dart callback throw.
+ a0.a0 = 42;
+
+ result = f(a0, a1);
+
+ CHECK_EQ(0, result.a0.a0);
+ CHECK_EQ(0, result.a0.a1);
+ CHECK_APPROX(0.0, result.a1.a0);
+
+ // Pass argument that will make the Dart callback return null.
+ a0.a0 = 84;
+
+ result = f(a0, a1);
+
+ CHECK_EQ(0, result.a0.a0);
+ CHECK_EQ(0, result.a0.a1);
+ CHECK_APPROX(0.0, result.a1.a0);
+
+ return 0;
+}
+
+// Used for testing structs by value.
+// Deeper nested struct to test recursive member access.
+DART_EXPORT intptr_t TestReturnStruct16BytesNestedInt(
+ // NOLINTNEXTLINE(whitespace/parens)
+ Struct16BytesNestedInt (*f)(Struct8BytesNestedInt a0,
+ Struct8BytesNestedInt a1)) {
+ Struct8BytesNestedInt a0;
+ Struct8BytesNestedInt a1;
+
+ a0.a0.a0 = -1;
+ a0.a0.a1 = 2;
+ a0.a1.a0 = -3;
+ a0.a1.a1 = 4;
+ a1.a0.a0 = -5;
+ a1.a0.a1 = 6;
+ a1.a1.a0 = -7;
+ a1.a1.a1 = 8;
+
+ std::cout << "Calling TestReturnStruct16BytesNestedInt("
+ << "(((" << a0.a0.a0 << ", " << a0.a0.a1 << "), (" << a0.a1.a0
+ << ", " << a0.a1.a1 << ")), ((" << a1.a0.a0 << ", " << a1.a0.a1
+ << "), (" << a1.a1.a0 << ", " << a1.a1.a1 << ")))"
+ << ")\n";
+
+ Struct16BytesNestedInt result = f(a0, a1);
+
+ std::cout << "result = "
+ << "(((" << result.a0.a0.a0 << ", " << result.a0.a0.a1 << "), ("
+ << result.a0.a1.a0 << ", " << result.a0.a1.a1 << ")), (("
+ << result.a1.a0.a0 << ", " << result.a1.a0.a1 << "), ("
+ << result.a1.a1.a0 << ", " << result.a1.a1.a1 << ")))"
+ << "\n";
+
+ CHECK_EQ(a0.a0.a0, result.a0.a0.a0);
+ CHECK_EQ(a0.a0.a1, result.a0.a0.a1);
+ CHECK_EQ(a0.a1.a0, result.a0.a1.a0);
+ CHECK_EQ(a0.a1.a1, result.a0.a1.a1);
+ CHECK_EQ(a1.a0.a0, result.a1.a0.a0);
+ CHECK_EQ(a1.a0.a1, result.a1.a0.a1);
+ CHECK_EQ(a1.a1.a0, result.a1.a1.a0);
+ CHECK_EQ(a1.a1.a1, result.a1.a1.a1);
+
+ // Pass argument that will make the Dart callback throw.
+ a0.a0.a0 = 42;
+
+ result = f(a0, a1);
+
+ CHECK_EQ(0, result.a0.a0.a0);
+ CHECK_EQ(0, result.a0.a0.a1);
+ CHECK_EQ(0, result.a0.a1.a0);
+ CHECK_EQ(0, result.a0.a1.a1);
+ CHECK_EQ(0, result.a1.a0.a0);
+ CHECK_EQ(0, result.a1.a0.a1);
+ CHECK_EQ(0, result.a1.a1.a0);
+ CHECK_EQ(0, result.a1.a1.a1);
+
+ // Pass argument that will make the Dart callback return null.
+ a0.a0.a0 = 84;
+
+ result = f(a0, a1);
+
+ CHECK_EQ(0, result.a0.a0.a0);
+ CHECK_EQ(0, result.a0.a0.a1);
+ CHECK_EQ(0, result.a0.a1.a0);
+ CHECK_EQ(0, result.a0.a1.a1);
+ CHECK_EQ(0, result.a1.a0.a0);
+ CHECK_EQ(0, result.a1.a0.a1);
+ CHECK_EQ(0, result.a1.a1.a0);
+ CHECK_EQ(0, result.a1.a1.a1);
+
+ return 0;
+}
+
+// Used for testing structs by value.
+// Even deeper nested struct to test recursive member access.
+DART_EXPORT intptr_t TestReturnStruct32BytesNestedInt(
+ // NOLINTNEXTLINE(whitespace/parens)
+ Struct32BytesNestedInt (*f)(Struct16BytesNestedInt a0,
+ Struct16BytesNestedInt a1)) {
+ Struct16BytesNestedInt a0;
+ Struct16BytesNestedInt a1;
+
+ a0.a0.a0.a0 = -1;
+ a0.a0.a0.a1 = 2;
+ a0.a0.a1.a0 = -3;
+ a0.a0.a1.a1 = 4;
+ a0.a1.a0.a0 = -5;
+ a0.a1.a0.a1 = 6;
+ a0.a1.a1.a0 = -7;
+ a0.a1.a1.a1 = 8;
+ a1.a0.a0.a0 = -9;
+ a1.a0.a0.a1 = 10;
+ a1.a0.a1.a0 = -11;
+ a1.a0.a1.a1 = 12;
+ a1.a1.a0.a0 = -13;
+ a1.a1.a0.a1 = 14;
+ a1.a1.a1.a0 = -15;
+ a1.a1.a1.a1 = 16;
+
+ std::cout << "Calling TestReturnStruct32BytesNestedInt("
+ << "((((" << a0.a0.a0.a0 << ", " << a0.a0.a0.a1 << "), ("
+ << a0.a0.a1.a0 << ", " << a0.a0.a1.a1 << ")), ((" << a0.a1.a0.a0
+ << ", " << a0.a1.a0.a1 << "), (" << a0.a1.a1.a0 << ", "
+ << a0.a1.a1.a1 << "))), (((" << a1.a0.a0.a0 << ", " << a1.a0.a0.a1
+ << "), (" << a1.a0.a1.a0 << ", " << a1.a0.a1.a1 << ")), (("
+ << a1.a1.a0.a0 << ", " << a1.a1.a0.a1 << "), (" << a1.a1.a1.a0
+ << ", " << a1.a1.a1.a1 << "))))"
+ << ")\n";
+
+ Struct32BytesNestedInt result = f(a0, a1);
+
+ std::cout << "result = "
+ << "((((" << result.a0.a0.a0.a0 << ", " << result.a0.a0.a0.a1
+ << "), (" << result.a0.a0.a1.a0 << ", " << result.a0.a0.a1.a1
+ << ")), ((" << result.a0.a1.a0.a0 << ", " << result.a0.a1.a0.a1
+ << "), (" << result.a0.a1.a1.a0 << ", " << result.a0.a1.a1.a1
+ << "))), (((" << result.a1.a0.a0.a0 << ", " << result.a1.a0.a0.a1
+ << "), (" << result.a1.a0.a1.a0 << ", " << result.a1.a0.a1.a1
+ << ")), ((" << result.a1.a1.a0.a0 << ", " << result.a1.a1.a0.a1
+ << "), (" << result.a1.a1.a1.a0 << ", " << result.a1.a1.a1.a1
+ << "))))"
+ << "\n";
+
+ CHECK_EQ(a0.a0.a0.a0, result.a0.a0.a0.a0);
+ CHECK_EQ(a0.a0.a0.a1, result.a0.a0.a0.a1);
+ CHECK_EQ(a0.a0.a1.a0, result.a0.a0.a1.a0);
+ CHECK_EQ(a0.a0.a1.a1, result.a0.a0.a1.a1);
+ CHECK_EQ(a0.a1.a0.a0, result.a0.a1.a0.a0);
+ CHECK_EQ(a0.a1.a0.a1, result.a0.a1.a0.a1);
+ CHECK_EQ(a0.a1.a1.a0, result.a0.a1.a1.a0);
+ CHECK_EQ(a0.a1.a1.a1, result.a0.a1.a1.a1);
+ CHECK_EQ(a1.a0.a0.a0, result.a1.a0.a0.a0);
+ CHECK_EQ(a1.a0.a0.a1, result.a1.a0.a0.a1);
+ CHECK_EQ(a1.a0.a1.a0, result.a1.a0.a1.a0);
+ CHECK_EQ(a1.a0.a1.a1, result.a1.a0.a1.a1);
+ CHECK_EQ(a1.a1.a0.a0, result.a1.a1.a0.a0);
+ CHECK_EQ(a1.a1.a0.a1, result.a1.a1.a0.a1);
+ CHECK_EQ(a1.a1.a1.a0, result.a1.a1.a1.a0);
+ CHECK_EQ(a1.a1.a1.a1, result.a1.a1.a1.a1);
+
+ // Pass argument that will make the Dart callback throw.
+ a0.a0.a0.a0 = 42;
+
+ result = f(a0, a1);
+
+ CHECK_EQ(0, result.a0.a0.a0.a0);
+ CHECK_EQ(0, result.a0.a0.a0.a1);
+ CHECK_EQ(0, result.a0.a0.a1.a0);
+ CHECK_EQ(0, result.a0.a0.a1.a1);
+ CHECK_EQ(0, result.a0.a1.a0.a0);
+ CHECK_EQ(0, result.a0.a1.a0.a1);
+ CHECK_EQ(0, result.a0.a1.a1.a0);
+ CHECK_EQ(0, result.a0.a1.a1.a1);
+ CHECK_EQ(0, result.a1.a0.a0.a0);
+ CHECK_EQ(0, result.a1.a0.a0.a1);
+ CHECK_EQ(0, result.a1.a0.a1.a0);
+ CHECK_EQ(0, result.a1.a0.a1.a1);
+ CHECK_EQ(0, result.a1.a1.a0.a0);
+ CHECK_EQ(0, result.a1.a1.a0.a1);
+ CHECK_EQ(0, result.a1.a1.a1.a0);
+ CHECK_EQ(0, result.a1.a1.a1.a1);
+
+ // Pass argument that will make the Dart callback return null.
+ a0.a0.a0.a0 = 84;
+
+ result = f(a0, a1);
+
+ CHECK_EQ(0, result.a0.a0.a0.a0);
+ CHECK_EQ(0, result.a0.a0.a0.a1);
+ CHECK_EQ(0, result.a0.a0.a1.a0);
+ CHECK_EQ(0, result.a0.a0.a1.a1);
+ CHECK_EQ(0, result.a0.a1.a0.a0);
+ CHECK_EQ(0, result.a0.a1.a0.a1);
+ CHECK_EQ(0, result.a0.a1.a1.a0);
+ CHECK_EQ(0, result.a0.a1.a1.a1);
+ CHECK_EQ(0, result.a1.a0.a0.a0);
+ CHECK_EQ(0, result.a1.a0.a0.a1);
+ CHECK_EQ(0, result.a1.a0.a1.a0);
+ CHECK_EQ(0, result.a1.a0.a1.a1);
+ CHECK_EQ(0, result.a1.a1.a0.a0);
+ CHECK_EQ(0, result.a1.a1.a0.a1);
+ CHECK_EQ(0, result.a1.a1.a1.a0);
+ CHECK_EQ(0, result.a1.a1.a1.a1);
+
+ return 0;
+}
+
+// Used for testing structs by value.
+// Test alignment and padding of nested struct with 16 byte int.
+DART_EXPORT intptr_t TestReturnStructNestedIntStructAlignmentInt16(
+ // NOLINTNEXTLINE(whitespace/parens)
+ StructNestedIntStructAlignmentInt16 (*f)(StructAlignmentInt16 a0,
+ StructAlignmentInt16 a1)) {
+ StructAlignmentInt16 a0;
+ StructAlignmentInt16 a1;
+
+ a0.a0 = -1;
+ a0.a1 = 2;
+ a0.a2 = -3;
+ a1.a0 = 4;
+ a1.a1 = -5;
+ a1.a2 = 6;
+
+ std::cout << "Calling TestReturnStructNestedIntStructAlignmentInt16("
+ << "((" << static_cast<int>(a0.a0) << ", " << a0.a1 << ", "
+ << static_cast<int>(a0.a2) << "), (" << static_cast<int>(a1.a0)
+ << ", " << a1.a1 << ", " << static_cast<int>(a1.a2) << "))"
+ << ")\n";
+
+ StructNestedIntStructAlignmentInt16 result = f(a0, a1);
+
+ std::cout << "result = "
+ << "((" << static_cast<int>(result.a0.a0) << ", " << result.a0.a1
+ << ", " << static_cast<int>(result.a0.a2) << "), ("
+ << static_cast<int>(result.a1.a0) << ", " << result.a1.a1 << ", "
+ << static_cast<int>(result.a1.a2) << "))"
+ << "\n";
+
+ CHECK_EQ(a0.a0, result.a0.a0);
+ CHECK_EQ(a0.a1, result.a0.a1);
+ CHECK_EQ(a0.a2, result.a0.a2);
+ CHECK_EQ(a1.a0, result.a1.a0);
+ CHECK_EQ(a1.a1, result.a1.a1);
+ CHECK_EQ(a1.a2, result.a1.a2);
+
+ // Pass argument that will make the Dart callback throw.
+ a0.a0 = 42;
+
+ result = f(a0, a1);
+
+ CHECK_EQ(0, result.a0.a0);
+ CHECK_EQ(0, result.a0.a1);
+ CHECK_EQ(0, result.a0.a2);
+ CHECK_EQ(0, result.a1.a0);
+ CHECK_EQ(0, result.a1.a1);
+ CHECK_EQ(0, result.a1.a2);
+
+ // Pass argument that will make the Dart callback return null.
+ a0.a0 = 84;
+
+ result = f(a0, a1);
+
+ CHECK_EQ(0, result.a0.a0);
+ CHECK_EQ(0, result.a0.a1);
+ CHECK_EQ(0, result.a0.a2);
+ CHECK_EQ(0, result.a1.a0);
+ CHECK_EQ(0, result.a1.a1);
+ CHECK_EQ(0, result.a1.a2);
+
+ return 0;
+}
+
+// Used for testing structs by value.
+// Test alignment and padding of nested struct with 32 byte int.
+DART_EXPORT intptr_t TestReturnStructNestedIntStructAlignmentInt32(
+ // NOLINTNEXTLINE(whitespace/parens)
+ StructNestedIntStructAlignmentInt32 (*f)(StructAlignmentInt32 a0,
+ StructAlignmentInt32 a1)) {
+ StructAlignmentInt32 a0;
+ StructAlignmentInt32 a1;
+
+ a0.a0 = -1;
+ a0.a1 = 2;
+ a0.a2 = -3;
+ a1.a0 = 4;
+ a1.a1 = -5;
+ a1.a2 = 6;
+
+ std::cout << "Calling TestReturnStructNestedIntStructAlignmentInt32("
+ << "((" << static_cast<int>(a0.a0) << ", " << a0.a1 << ", "
+ << static_cast<int>(a0.a2) << "), (" << static_cast<int>(a1.a0)
+ << ", " << a1.a1 << ", " << static_cast<int>(a1.a2) << "))"
+ << ")\n";
+
+ StructNestedIntStructAlignmentInt32 result = f(a0, a1);
+
+ std::cout << "result = "
+ << "((" << static_cast<int>(result.a0.a0) << ", " << result.a0.a1
+ << ", " << static_cast<int>(result.a0.a2) << "), ("
+ << static_cast<int>(result.a1.a0) << ", " << result.a1.a1 << ", "
+ << static_cast<int>(result.a1.a2) << "))"
+ << "\n";
+
+ CHECK_EQ(a0.a0, result.a0.a0);
+ CHECK_EQ(a0.a1, result.a0.a1);
+ CHECK_EQ(a0.a2, result.a0.a2);
+ CHECK_EQ(a1.a0, result.a1.a0);
+ CHECK_EQ(a1.a1, result.a1.a1);
+ CHECK_EQ(a1.a2, result.a1.a2);
+
+ // Pass argument that will make the Dart callback throw.
+ a0.a0 = 42;
+
+ result = f(a0, a1);
+
+ CHECK_EQ(0, result.a0.a0);
+ CHECK_EQ(0, result.a0.a1);
+ CHECK_EQ(0, result.a0.a2);
+ CHECK_EQ(0, result.a1.a0);
+ CHECK_EQ(0, result.a1.a1);
+ CHECK_EQ(0, result.a1.a2);
+
+ // Pass argument that will make the Dart callback return null.
+ a0.a0 = 84;
+
+ result = f(a0, a1);
+
+ CHECK_EQ(0, result.a0.a0);
+ CHECK_EQ(0, result.a0.a1);
+ CHECK_EQ(0, result.a0.a2);
+ CHECK_EQ(0, result.a1.a0);
+ CHECK_EQ(0, result.a1.a1);
+ CHECK_EQ(0, result.a1.a2);
+
+ return 0;
+}
+
+// Used for testing structs by value.
+// Test alignment and padding of nested struct with 64 byte int.
+DART_EXPORT intptr_t TestReturnStructNestedIntStructAlignmentInt64(
+ // NOLINTNEXTLINE(whitespace/parens)
+ StructNestedIntStructAlignmentInt64 (*f)(StructAlignmentInt64 a0,
+ StructAlignmentInt64 a1)) {
+ StructAlignmentInt64 a0;
+ StructAlignmentInt64 a1;
+
+ a0.a0 = -1;
+ a0.a1 = 2;
+ a0.a2 = -3;
+ a1.a0 = 4;
+ a1.a1 = -5;
+ a1.a2 = 6;
+
+ std::cout << "Calling TestReturnStructNestedIntStructAlignmentInt64("
+ << "((" << static_cast<int>(a0.a0) << ", " << a0.a1 << ", "
+ << static_cast<int>(a0.a2) << "), (" << static_cast<int>(a1.a0)
+ << ", " << a1.a1 << ", " << static_cast<int>(a1.a2) << "))"
+ << ")\n";
+
+ StructNestedIntStructAlignmentInt64 result = f(a0, a1);
+
+ std::cout << "result = "
+ << "((" << static_cast<int>(result.a0.a0) << ", " << result.a0.a1
+ << ", " << static_cast<int>(result.a0.a2) << "), ("
+ << static_cast<int>(result.a1.a0) << ", " << result.a1.a1 << ", "
+ << static_cast<int>(result.a1.a2) << "))"
+ << "\n";
+
+ CHECK_EQ(a0.a0, result.a0.a0);
+ CHECK_EQ(a0.a1, result.a0.a1);
+ CHECK_EQ(a0.a2, result.a0.a2);
+ CHECK_EQ(a1.a0, result.a1.a0);
+ CHECK_EQ(a1.a1, result.a1.a1);
+ CHECK_EQ(a1.a2, result.a1.a2);
+
+ // Pass argument that will make the Dart callback throw.
+ a0.a0 = 42;
+
+ result = f(a0, a1);
+
+ CHECK_EQ(0, result.a0.a0);
+ CHECK_EQ(0, result.a0.a1);
+ CHECK_EQ(0, result.a0.a2);
+ CHECK_EQ(0, result.a1.a0);
+ CHECK_EQ(0, result.a1.a1);
+ CHECK_EQ(0, result.a1.a2);
+
+ // Pass argument that will make the Dart callback return null.
+ a0.a0 = 84;
+
+ result = f(a0, a1);
+
+ CHECK_EQ(0, result.a0.a0);
+ CHECK_EQ(0, result.a0.a1);
+ CHECK_EQ(0, result.a0.a2);
+ CHECK_EQ(0, result.a1.a0);
+ CHECK_EQ(0, result.a1.a1);
+ CHECK_EQ(0, result.a1.a2);
+
+ return 0;
+}
+
+// Used for testing structs by value.
+// Return big irregular struct as smoke test.
+DART_EXPORT intptr_t TestReturnStructNestedIrregularEvenBigger(
+ // NOLINTNEXTLINE(whitespace/parens)
+ StructNestedIrregularEvenBigger (*f)(uint64_t a0,
+ StructNestedIrregularBigger a1,
+ StructNestedIrregularBigger a2,
+ double a3)) {
+ uint64_t a0;
+ StructNestedIrregularBigger a1;
+ StructNestedIrregularBigger a2;
+ double a3;
+
+ a0 = 1;
+ a1.a0.a0 = 2;
+ a1.a0.a1.a0.a0 = -3;
+ a1.a0.a1.a0.a1 = 4;
+ a1.a0.a1.a1.a0 = -5.0;
+ a1.a0.a2 = 6;
+ a1.a0.a3.a0.a0 = -7.0;
+ a1.a0.a3.a1 = 8.0;
+ a1.a0.a4 = 9;
+ a1.a0.a5.a0.a0 = 10.0;
+ a1.a0.a5.a1.a0 = -11.0;
+ a1.a0.a6 = 12;
+ a1.a1.a0.a0 = -13;
+ a1.a1.a0.a1 = 14;
+ a1.a1.a1.a0 = -15.0;
+ a1.a2 = 16.0;
+ a1.a3 = -17.0;
+ a2.a0.a0 = 18;
+ a2.a0.a1.a0.a0 = -19;
+ a2.a0.a1.a0.a1 = 20;
+ a2.a0.a1.a1.a0 = -21.0;
+ a2.a0.a2 = 22;
+ a2.a0.a3.a0.a0 = -23.0;
+ a2.a0.a3.a1 = 24.0;
+ a2.a0.a4 = 25;
+ a2.a0.a5.a0.a0 = 26.0;
+ a2.a0.a5.a1.a0 = -27.0;
+ a2.a0.a6 = 28;
+ a2.a1.a0.a0 = -29;
+ a2.a1.a0.a1 = 30;
+ a2.a1.a1.a0 = -31.0;
+ a2.a2 = 32.0;
+ a2.a3 = -33.0;
+ a3 = 34.0;
+
+ std::cout << "Calling TestReturnStructNestedIrregularEvenBigger("
+ << "(" << a0 << ", ((" << a1.a0.a0 << ", ((" << a1.a0.a1.a0.a0
+ << ", " << a1.a0.a1.a0.a1 << "), (" << a1.a0.a1.a1.a0 << ")), "
+ << a1.a0.a2 << ", ((" << a1.a0.a3.a0.a0 << "), " << a1.a0.a3.a1
+ << "), " << a1.a0.a4 << ", ((" << a1.a0.a5.a0.a0 << "), ("
+ << a1.a0.a5.a1.a0 << ")), " << a1.a0.a6 << "), ((" << a1.a1.a0.a0
+ << ", " << a1.a1.a0.a1 << "), (" << a1.a1.a1.a0 << ")), " << a1.a2
+ << ", " << a1.a3 << "), ((" << a2.a0.a0 << ", ((" << a2.a0.a1.a0.a0
+ << ", " << a2.a0.a1.a0.a1 << "), (" << a2.a0.a1.a1.a0 << ")), "
+ << a2.a0.a2 << ", ((" << a2.a0.a3.a0.a0 << "), " << a2.a0.a3.a1
+ << "), " << a2.a0.a4 << ", ((" << a2.a0.a5.a0.a0 << "), ("
+ << a2.a0.a5.a1.a0 << ")), " << a2.a0.a6 << "), ((" << a2.a1.a0.a0
+ << ", " << a2.a1.a0.a1 << "), (" << a2.a1.a1.a0 << ")), " << a2.a2
+ << ", " << a2.a3 << "), " << a3 << ")"
+ << ")\n";
+
+ StructNestedIrregularEvenBigger result = f(a0, a1, a2, a3);
+
+ std::cout << "result = "
+ << "(" << result.a0 << ", ((" << result.a1.a0.a0 << ", (("
+ << result.a1.a0.a1.a0.a0 << ", " << result.a1.a0.a1.a0.a1 << "), ("
+ << result.a1.a0.a1.a1.a0 << ")), " << result.a1.a0.a2 << ", (("
+ << result.a1.a0.a3.a0.a0 << "), " << result.a1.a0.a3.a1 << "), "
+ << result.a1.a0.a4 << ", ((" << result.a1.a0.a5.a0.a0 << "), ("
+ << result.a1.a0.a5.a1.a0 << ")), " << result.a1.a0.a6 << "), (("
+ << result.a1.a1.a0.a0 << ", " << result.a1.a1.a0.a1 << "), ("
+ << result.a1.a1.a1.a0 << ")), " << result.a1.a2 << ", "
+ << result.a1.a3 << "), ((" << result.a2.a0.a0 << ", (("
+ << result.a2.a0.a1.a0.a0 << ", " << result.a2.a0.a1.a0.a1 << "), ("
+ << result.a2.a0.a1.a1.a0 << ")), " << result.a2.a0.a2 << ", (("
+ << result.a2.a0.a3.a0.a0 << "), " << result.a2.a0.a3.a1 << "), "
+ << result.a2.a0.a4 << ", ((" << result.a2.a0.a5.a0.a0 << "), ("
+ << result.a2.a0.a5.a1.a0 << ")), " << result.a2.a0.a6 << "), (("
+ << result.a2.a1.a0.a0 << ", " << result.a2.a1.a0.a1 << "), ("
+ << result.a2.a1.a1.a0 << ")), " << result.a2.a2 << ", "
+ << result.a2.a3 << "), " << result.a3 << ")"
+ << "\n";
+
+ CHECK_EQ(a0, result.a0);
+ CHECK_EQ(a1.a0.a0, result.a1.a0.a0);
+ CHECK_EQ(a1.a0.a1.a0.a0, result.a1.a0.a1.a0.a0);
+ CHECK_EQ(a1.a0.a1.a0.a1, result.a1.a0.a1.a0.a1);
+ CHECK_APPROX(a1.a0.a1.a1.a0, result.a1.a0.a1.a1.a0);
+ CHECK_EQ(a1.a0.a2, result.a1.a0.a2);
+ CHECK_APPROX(a1.a0.a3.a0.a0, result.a1.a0.a3.a0.a0);
+ CHECK_APPROX(a1.a0.a3.a1, result.a1.a0.a3.a1);
+ CHECK_EQ(a1.a0.a4, result.a1.a0.a4);
+ CHECK_APPROX(a1.a0.a5.a0.a0, result.a1.a0.a5.a0.a0);
+ CHECK_APPROX(a1.a0.a5.a1.a0, result.a1.a0.a5.a1.a0);
+ CHECK_EQ(a1.a0.a6, result.a1.a0.a6);
+ CHECK_EQ(a1.a1.a0.a0, result.a1.a1.a0.a0);
+ CHECK_EQ(a1.a1.a0.a1, result.a1.a1.a0.a1);
+ CHECK_APPROX(a1.a1.a1.a0, result.a1.a1.a1.a0);
+ CHECK_APPROX(a1.a2, result.a1.a2);
+ CHECK_APPROX(a1.a3, result.a1.a3);
+ CHECK_EQ(a2.a0.a0, result.a2.a0.a0);
+ CHECK_EQ(a2.a0.a1.a0.a0, result.a2.a0.a1.a0.a0);
+ CHECK_EQ(a2.a0.a1.a0.a1, result.a2.a0.a1.a0.a1);
+ CHECK_APPROX(a2.a0.a1.a1.a0, result.a2.a0.a1.a1.a0);
+ CHECK_EQ(a2.a0.a2, result.a2.a0.a2);
+ CHECK_APPROX(a2.a0.a3.a0.a0, result.a2.a0.a3.a0.a0);
+ CHECK_APPROX(a2.a0.a3.a1, result.a2.a0.a3.a1);
+ CHECK_EQ(a2.a0.a4, result.a2.a0.a4);
+ CHECK_APPROX(a2.a0.a5.a0.a0, result.a2.a0.a5.a0.a0);
+ CHECK_APPROX(a2.a0.a5.a1.a0, result.a2.a0.a5.a1.a0);
+ CHECK_EQ(a2.a0.a6, result.a2.a0.a6);
+ CHECK_EQ(a2.a1.a0.a0, result.a2.a1.a0.a0);
+ CHECK_EQ(a2.a1.a0.a1, result.a2.a1.a0.a1);
+ CHECK_APPROX(a2.a1.a1.a0, result.a2.a1.a1.a0);
+ CHECK_APPROX(a2.a2, result.a2.a2);
+ CHECK_APPROX(a2.a3, result.a2.a3);
+ CHECK_APPROX(a3, result.a3);
+
+ // Pass argument that will make the Dart callback throw.
+ a0 = 42;
+
+ result = f(a0, a1, a2, a3);
+
+ CHECK_EQ(0, result.a0);
+ CHECK_EQ(0, result.a1.a0.a0);
+ CHECK_EQ(0, result.a1.a0.a1.a0.a0);
+ CHECK_EQ(0, result.a1.a0.a1.a0.a1);
+ CHECK_APPROX(0.0, result.a1.a0.a1.a1.a0);
+ CHECK_EQ(0, result.a1.a0.a2);
+ CHECK_APPROX(0.0, result.a1.a0.a3.a0.a0);
+ CHECK_APPROX(0.0, result.a1.a0.a3.a1);
+ CHECK_EQ(0, result.a1.a0.a4);
+ CHECK_APPROX(0.0, result.a1.a0.a5.a0.a0);
+ CHECK_APPROX(0.0, result.a1.a0.a5.a1.a0);
+ CHECK_EQ(0, result.a1.a0.a6);
+ CHECK_EQ(0, result.a1.a1.a0.a0);
+ CHECK_EQ(0, result.a1.a1.a0.a1);
+ CHECK_APPROX(0.0, result.a1.a1.a1.a0);
+ CHECK_APPROX(0.0, result.a1.a2);
+ CHECK_APPROX(0.0, result.a1.a3);
+ CHECK_EQ(0, result.a2.a0.a0);
+ CHECK_EQ(0, result.a2.a0.a1.a0.a0);
+ CHECK_EQ(0, result.a2.a0.a1.a0.a1);
+ CHECK_APPROX(0.0, result.a2.a0.a1.a1.a0);
+ CHECK_EQ(0, result.a2.a0.a2);
+ CHECK_APPROX(0.0, result.a2.a0.a3.a0.a0);
+ CHECK_APPROX(0.0, result.a2.a0.a3.a1);
+ CHECK_EQ(0, result.a2.a0.a4);
+ CHECK_APPROX(0.0, result.a2.a0.a5.a0.a0);
+ CHECK_APPROX(0.0, result.a2.a0.a5.a1.a0);
+ CHECK_EQ(0, result.a2.a0.a6);
+ CHECK_EQ(0, result.a2.a1.a0.a0);
+ CHECK_EQ(0, result.a2.a1.a0.a1);
+ CHECK_APPROX(0.0, result.a2.a1.a1.a0);
+ CHECK_APPROX(0.0, result.a2.a2);
+ CHECK_APPROX(0.0, result.a2.a3);
+ CHECK_APPROX(0.0, result.a3);
+
+ // Pass argument that will make the Dart callback return null.
+ a0 = 84;
+
+ result = f(a0, a1, a2, a3);
+
+ CHECK_EQ(0, result.a0);
+ CHECK_EQ(0, result.a1.a0.a0);
+ CHECK_EQ(0, result.a1.a0.a1.a0.a0);
+ CHECK_EQ(0, result.a1.a0.a1.a0.a1);
+ CHECK_APPROX(0.0, result.a1.a0.a1.a1.a0);
+ CHECK_EQ(0, result.a1.a0.a2);
+ CHECK_APPROX(0.0, result.a1.a0.a3.a0.a0);
+ CHECK_APPROX(0.0, result.a1.a0.a3.a1);
+ CHECK_EQ(0, result.a1.a0.a4);
+ CHECK_APPROX(0.0, result.a1.a0.a5.a0.a0);
+ CHECK_APPROX(0.0, result.a1.a0.a5.a1.a0);
+ CHECK_EQ(0, result.a1.a0.a6);
+ CHECK_EQ(0, result.a1.a1.a0.a0);
+ CHECK_EQ(0, result.a1.a1.a0.a1);
+ CHECK_APPROX(0.0, result.a1.a1.a1.a0);
+ CHECK_APPROX(0.0, result.a1.a2);
+ CHECK_APPROX(0.0, result.a1.a3);
+ CHECK_EQ(0, result.a2.a0.a0);
+ CHECK_EQ(0, result.a2.a0.a1.a0.a0);
+ CHECK_EQ(0, result.a2.a0.a1.a0.a1);
+ CHECK_APPROX(0.0, result.a2.a0.a1.a1.a0);
+ CHECK_EQ(0, result.a2.a0.a2);
+ CHECK_APPROX(0.0, result.a2.a0.a3.a0.a0);
+ CHECK_APPROX(0.0, result.a2.a0.a3.a1);
+ CHECK_EQ(0, result.a2.a0.a4);
+ CHECK_APPROX(0.0, result.a2.a0.a5.a0.a0);
+ CHECK_APPROX(0.0, result.a2.a0.a5.a1.a0);
+ CHECK_EQ(0, result.a2.a0.a6);
+ CHECK_EQ(0, result.a2.a1.a0.a0);
+ CHECK_EQ(0, result.a2.a1.a0.a1);
+ CHECK_APPROX(0.0, result.a2.a1.a1.a0);
+ CHECK_APPROX(0.0, result.a2.a2);
+ CHECK_APPROX(0.0, result.a2.a3);
+ CHECK_APPROX(0.0, result.a3);
+
+ return 0;
+}
+
} // namespace dart
diff --git a/runtime/lib/ffi.cc b/runtime/lib/ffi.cc
index cd97151..cefe5a0 100644
--- a/runtime/lib/ffi.cc
+++ b/runtime/lib/ffi.cc
@@ -91,14 +91,14 @@
static ObjectPtr LoadValueStruct(Zone* zone,
const Pointer& target,
const AbstractType& instance_type_arg) {
- // Result is a struct class -- find <class name>.#fromPointer
+ // Result is a struct class -- find <class name>.#fromTypedDataBase
// constructor and call it.
const Class& cls = Class::Handle(zone, instance_type_arg.type_class());
const Function& constructor =
Function::Handle(cls.LookupFunctionAllowPrivate(String::Handle(
String::Concat(String::Handle(String::Concat(
String::Handle(cls.Name()), Symbols::Dot())),
- Symbols::StructFromPointer()))));
+ Symbols::StructFromTypedDataBase()))));
ASSERT(!constructor.IsNull());
ASSERT(constructor.IsGenerativeConstructor());
ASSERT(!Object::Handle(constructor.VerifyCallEntryPoint()).IsError());
diff --git a/runtime/vm/compiler/ffi/native_calling_convention.cc b/runtime/vm/compiler/ffi/native_calling_convention.cc
index e53a1e8..86b2eba 100644
--- a/runtime/vm/compiler/ffi/native_calling_convention.cc
+++ b/runtime/vm/compiler/ffi/native_calling_convention.cc
@@ -225,10 +225,10 @@
const NativeCompoundType& payload_type) {
const auto& compound_type = payload_type.AsCompound();
if (compound_type.ContainsHomogenuousFloats() && !SoftFpAbi() &&
- compound_type.members().length() <= 4) {
- const auto& elem_type = *(compound_type.members().At(0));
+ compound_type.NumPrimitiveMembersRecursive() <= 4) {
+ const auto& elem_type = compound_type.FirstPrimitiveMember();
const intptr_t size = compound_type.SizeInBytes();
- const intptr_t elem_size = compound_type.members().At(0)->SizeInBytes();
+ const intptr_t elem_size = elem_type.SizeInBytes();
const auto reg_kind = FpuRegisterKindFromSize(elem_size);
ASSERT(size % elem_size == 0);
const intptr_t num_registers = size / elem_size;
@@ -291,9 +291,9 @@
const auto& compound_type = payload_type.AsCompound();
const intptr_t size = compound_type.SizeInBytes();
if (compound_type.ContainsHomogenuousFloats() &&
- compound_type.members().length() <= 4) {
- const auto& elem_type = *(compound_type.members().At(0));
- const intptr_t elem_size = compound_type.members().At(0)->SizeInBytes();
+ compound_type.NumPrimitiveMembersRecursive() <= 4) {
+ const auto& elem_type = compound_type.FirstPrimitiveMember();
+ const intptr_t elem_size = elem_type.SizeInBytes();
const auto reg_kind = kQuadFpuReg;
ASSERT(size % elem_size == 0);
const intptr_t num_registers = size / elem_size;
@@ -623,13 +623,13 @@
static const NativeLocation& CompoundResultLocation(
Zone* zone,
const NativeCompoundType& payload_type) {
- const intptr_t num_members = payload_type.members().length();
+ const intptr_t num_members = payload_type.NumPrimitiveMembersRecursive();
if (payload_type.ContainsHomogenuousFloats() && !SoftFpAbi() &&
num_members <= 4) {
NativeLocations& multiple_locations =
*new (zone) NativeLocations(zone, num_members);
for (int i = 0; i < num_members; i++) {
- const auto& member = payload_type.members().At(0)->AsPrimitive();
+ const auto& member = payload_type.FirstPrimitiveMember();
multiple_locations.Add(new (zone) NativeFpuRegistersLocation(
member, member, FpuRegisterKindFromSize(member.SizeInBytes()), i));
}
diff --git a/runtime/vm/compiler/ffi/native_type.cc b/runtime/vm/compiler/ffi/native_type.cc
index 1ffb1a2e..837c17f 100644
--- a/runtime/vm/compiler/ffi/native_type.cc
+++ b/runtime/vm/compiler/ffi/native_type.cc
@@ -156,7 +156,7 @@
static bool ContainsHomogenuousFloatsInternal(const NativeTypes& types);
// Keep consistent with
-// pkg/vm/lib/transformations/ffi_definitions.dart:_calculateSizeAndOffsets.
+// pkg/vm/lib/transformations/ffi_definitions.dart:_calculateStructLayout.
NativeCompoundType& NativeCompoundType::FromNativeTypes(
Zone* zone,
const NativeTypes& members) {
@@ -514,6 +514,32 @@
return_type_.PrintTo(f);
}
+intptr_t NativePrimitiveType::NumPrimitiveMembersRecursive() const {
+ return 1;
+}
+
+intptr_t NativeCompoundType::NumPrimitiveMembersRecursive() const {
+ intptr_t count = 0;
+ for (intptr_t i = 0; i < members_.length(); i++) {
+ count += members_[i]->NumPrimitiveMembersRecursive();
+ }
+ return count;
+}
+
+const NativePrimitiveType& NativePrimitiveType::FirstPrimitiveMember() const {
+ return *this;
+}
+
+const NativePrimitiveType& NativeCompoundType::FirstPrimitiveMember() const {
+ ASSERT(NumPrimitiveMembersRecursive() >= 1);
+ for (intptr_t i = 0; i < members().length(); i++) {
+ if (members_[i]->NumPrimitiveMembersRecursive() >= 1) {
+ return members_[i]->FirstPrimitiveMember();
+ }
+ }
+ UNREACHABLE();
+}
+
bool NativeCompoundType::ContainsOnlyFloats(intptr_t offset_in_bytes,
intptr_t size_in_bytes) const {
ASSERT(size_in_bytes >= 0);
diff --git a/runtime/vm/compiler/ffi/native_type.h b/runtime/vm/compiler/ffi/native_type.h
index 140c54f..73d6915 100644
--- a/runtime/vm/compiler/ffi/native_type.h
+++ b/runtime/vm/compiler/ffi/native_type.h
@@ -117,6 +117,11 @@
const char* ToCString() const;
#endif
+ virtual intptr_t NumPrimitiveMembersRecursive() const { UNREACHABLE(); }
+ virtual const NativePrimitiveType& FirstPrimitiveMember() const {
+ UNREACHABLE();
+ }
+
virtual ~NativeType() {}
protected:
@@ -177,6 +182,9 @@
bool multi_line = false,
bool verbose = true) const;
+ virtual intptr_t NumPrimitiveMembersRecursive() const;
+ virtual const NativePrimitiveType& FirstPrimitiveMember() const;
+
virtual ~NativePrimitiveType() {}
private:
@@ -188,7 +196,6 @@
// Struct
//
// TODO(dartbug.com/38491): Support unions.
-// TODO(dartbug.com/37271): Support nested compound types.
// TODO(dartbug.com/35763): Support inline fixed-length arrays.
class NativeCompoundType : public NativeType {
public:
@@ -234,6 +241,9 @@
// and arm64.
bool ContainsHomogenuousFloats() const;
+ virtual intptr_t NumPrimitiveMembersRecursive() const;
+ virtual const NativePrimitiveType& FirstPrimitiveMember() const;
+
private:
NativeCompoundType(const NativeTypes& members,
const ZoneGrowableArray<intptr_t>& member_offsets,
diff --git a/runtime/vm/kernel_loader.cc b/runtime/vm/kernel_loader.cc
index f6fe2fa..32ca631 100644
--- a/runtime/vm/kernel_loader.cc
+++ b/runtime/vm/kernel_loader.cc
@@ -1646,6 +1646,22 @@
fields_.Add(&deleted_enum_sentinel);
}
+ // TODO(https://dartbug.com/44454): Make VM recognize the Struct class.
+ //
+ // The FfiTrampolines currently allocate subtypes of structs and store
+ // TypedData in them, without using guards because they are force
+ // optimized. We immediately set the guarded_cid_ to kDynamicCid, which
+ // is effectively the same as calling this method first with Pointer and
+ // subsequently with TypedData with field guards.
+ if (klass.Name() == Symbols::Struct().raw() &&
+ Library::Handle(Z, klass.library()).url() == Symbols::DartFfi().raw()) {
+ ASSERT(fields_.length() == 1);
+ ASSERT(String::Handle(Z, fields_[0]->name())
+ .StartsWith(Symbols::_addressOf()));
+ fields_[0]->set_guarded_cid(kDynamicCid);
+ fields_[0]->set_is_nullable(true);
+ }
+
// Due to ReadVMAnnotations(), the klass may have been loaded at this point
// (loading the class while evaluating annotations).
if (klass.is_loaded()) {
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index 5642b0a..2d21c6f 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -262,7 +262,7 @@
V(StreamIteratorConstructor, "StreamIterator.") \
V(StringBase, "_StringBase") \
V(Struct, "Struct") \
- V(StructFromPointer, "#fromPointer") \
+ V(StructFromTypedDataBase, "#fromTypedDataBase") \
V(SubtypeTestCache, "SubtypeTestCache") \
V(LoadingUnit, "LoadingUnit") \
V(SwitchExpr, ":switch_expr") \
diff --git a/sdk/lib/_internal/vm/lib/ffi_patch.dart b/sdk/lib/_internal/vm/lib/ffi_patch.dart
index 79fc3a3..8bc4c14 100644
--- a/sdk/lib/_internal/vm/lib/ffi_patch.dart
+++ b/sdk/lib/_internal/vm/lib/ffi_patch.dart
@@ -119,6 +119,48 @@
int _abi()
native "Recognized method: IR graph is built in the flow graph builder.";
+/// Copies data byte-wise from [source] to [target].
+///
+/// [source] and [target] should either be [Pointer] or [TypedData].
+///
+/// TODO(dartbug.com/37271): Make recognized method and use MemoryCopyInstr.
+void _memCopy(Object target, int targetOffsetInBytes, Object source,
+ int sourceOffsetInBytes, int lengthInBytes) {
+ assert(source is Pointer || source is TypedData);
+ assert(target is Pointer || target is TypedData);
+ if (source is Pointer) {
+ final sourcePointer = source.cast<Uint8>();
+ if (target is Pointer) {
+ final targetPointer = target.cast<Uint8>();
+ for (int i = 0; i < lengthInBytes; i++) {
+ targetPointer[i + targetOffsetInBytes] =
+ sourcePointer[i + sourceOffsetInBytes];
+ }
+ } else if (target is TypedData) {
+ final targetTypedData = target.buffer.asUint8List(target.offsetInBytes);
+ for (int i = 0; i < lengthInBytes; i++) {
+ targetTypedData[i + targetOffsetInBytes] =
+ sourcePointer[i + sourceOffsetInBytes];
+ }
+ }
+ } else if (source is TypedData) {
+ final sourceTypedData = source.buffer.asUint8List(source.offsetInBytes);
+ if (target is Pointer) {
+ final targetPointer = target.cast<Uint8>();
+ for (int i = 0; i < lengthInBytes; i++) {
+ targetPointer[i + targetOffsetInBytes] =
+ sourceTypedData[i + sourceOffsetInBytes];
+ }
+ } else if (target is TypedData) {
+ final targetTypedData = target.buffer.asUint8List(target.offsetInBytes);
+ targetTypedData.setRange(
+ targetOffsetInBytes,
+ targetOffsetInBytes + lengthInBytes,
+ sourceTypedData.sublist(sourceOffsetInBytes));
+ }
+ }
+}
+
// The following functions are implemented in the method recognizer.
//
// TODO(38172): Since these are not inlined (force optimize), they force
diff --git a/sdk/lib/ffi/struct.dart b/sdk/lib/ffi/struct.dart
index da034ef..0a65119 100644
--- a/sdk/lib/ffi/struct.dart
+++ b/sdk/lib/ffi/struct.dart
@@ -47,6 +47,7 @@
/// by native memory. The may allocated via allocation or loaded from a
/// [Pointer], but cannot be created by a generative constructor.
abstract class Struct extends NativeType {
+ @pragma("vm:entry-point")
final Object _addressOf;
/// Construct a reference to the [nullptr].
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 845ebb9..75ef76f 100644
--- a/tests/ffi/function_callbacks_structs_by_value_generated_test.dart
+++ b/tests/ffi/function_callbacks_structs_by_value_generated_test.dart
@@ -193,6 +193,56 @@
passStructAlignmentInt64, 0),
passStructAlignmentInt64AfterCallback),
CallbackTest.withCheck(
+ "PassStruct8BytesNestedIntx10",
+ Pointer.fromFunction<PassStruct8BytesNestedIntx10Type>(
+ passStruct8BytesNestedIntx10, 0),
+ passStruct8BytesNestedIntx10AfterCallback),
+ CallbackTest.withCheck(
+ "PassStruct8BytesNestedFloatx10",
+ Pointer.fromFunction<PassStruct8BytesNestedFloatx10Type>(
+ passStruct8BytesNestedFloatx10, 0.0),
+ passStruct8BytesNestedFloatx10AfterCallback),
+ CallbackTest.withCheck(
+ "PassStruct8BytesNestedFloat2x10",
+ Pointer.fromFunction<PassStruct8BytesNestedFloat2x10Type>(
+ passStruct8BytesNestedFloat2x10, 0.0),
+ passStruct8BytesNestedFloat2x10AfterCallback),
+ CallbackTest.withCheck(
+ "PassStruct8BytesNestedMixedx10",
+ Pointer.fromFunction<PassStruct8BytesNestedMixedx10Type>(
+ passStruct8BytesNestedMixedx10, 0.0),
+ passStruct8BytesNestedMixedx10AfterCallback),
+ CallbackTest.withCheck(
+ "PassStruct16BytesNestedIntx2",
+ Pointer.fromFunction<PassStruct16BytesNestedIntx2Type>(
+ passStruct16BytesNestedIntx2, 0),
+ passStruct16BytesNestedIntx2AfterCallback),
+ CallbackTest.withCheck(
+ "PassStruct32BytesNestedIntx2",
+ Pointer.fromFunction<PassStruct32BytesNestedIntx2Type>(
+ passStruct32BytesNestedIntx2, 0),
+ passStruct32BytesNestedIntx2AfterCallback),
+ CallbackTest.withCheck(
+ "PassStructNestedIntStructAlignmentInt16",
+ Pointer.fromFunction<PassStructNestedIntStructAlignmentInt16Type>(
+ passStructNestedIntStructAlignmentInt16, 0),
+ passStructNestedIntStructAlignmentInt16AfterCallback),
+ CallbackTest.withCheck(
+ "PassStructNestedIntStructAlignmentInt32",
+ Pointer.fromFunction<PassStructNestedIntStructAlignmentInt32Type>(
+ passStructNestedIntStructAlignmentInt32, 0),
+ passStructNestedIntStructAlignmentInt32AfterCallback),
+ CallbackTest.withCheck(
+ "PassStructNestedIntStructAlignmentInt64",
+ Pointer.fromFunction<PassStructNestedIntStructAlignmentInt64Type>(
+ passStructNestedIntStructAlignmentInt64, 0),
+ passStructNestedIntStructAlignmentInt64AfterCallback),
+ CallbackTest.withCheck(
+ "PassStructNestedIrregularEvenBiggerx4",
+ Pointer.fromFunction<PassStructNestedIrregularEvenBiggerx4Type>(
+ passStructNestedIrregularEvenBiggerx4, 0.0),
+ passStructNestedIrregularEvenBiggerx4AfterCallback),
+ CallbackTest.withCheck(
"ReturnStruct1ByteInt",
Pointer.fromFunction<ReturnStruct1ByteIntType>(returnStruct1ByteInt),
returnStruct1ByteIntAfterCallback),
@@ -342,6 +392,56 @@
Pointer.fromFunction<ReturnStructAlignmentInt64Type>(
returnStructAlignmentInt64),
returnStructAlignmentInt64AfterCallback),
+ CallbackTest.withCheck(
+ "ReturnStruct8BytesNestedInt",
+ Pointer.fromFunction<ReturnStruct8BytesNestedIntType>(
+ returnStruct8BytesNestedInt),
+ returnStruct8BytesNestedIntAfterCallback),
+ CallbackTest.withCheck(
+ "ReturnStruct8BytesNestedFloat",
+ Pointer.fromFunction<ReturnStruct8BytesNestedFloatType>(
+ returnStruct8BytesNestedFloat),
+ returnStruct8BytesNestedFloatAfterCallback),
+ CallbackTest.withCheck(
+ "ReturnStruct8BytesNestedFloat2",
+ Pointer.fromFunction<ReturnStruct8BytesNestedFloat2Type>(
+ returnStruct8BytesNestedFloat2),
+ returnStruct8BytesNestedFloat2AfterCallback),
+ CallbackTest.withCheck(
+ "ReturnStruct8BytesNestedMixed",
+ Pointer.fromFunction<ReturnStruct8BytesNestedMixedType>(
+ returnStruct8BytesNestedMixed),
+ returnStruct8BytesNestedMixedAfterCallback),
+ CallbackTest.withCheck(
+ "ReturnStruct16BytesNestedInt",
+ Pointer.fromFunction<ReturnStruct16BytesNestedIntType>(
+ returnStruct16BytesNestedInt),
+ returnStruct16BytesNestedIntAfterCallback),
+ CallbackTest.withCheck(
+ "ReturnStruct32BytesNestedInt",
+ Pointer.fromFunction<ReturnStruct32BytesNestedIntType>(
+ returnStruct32BytesNestedInt),
+ returnStruct32BytesNestedIntAfterCallback),
+ CallbackTest.withCheck(
+ "ReturnStructNestedIntStructAlignmentInt16",
+ Pointer.fromFunction<ReturnStructNestedIntStructAlignmentInt16Type>(
+ returnStructNestedIntStructAlignmentInt16),
+ returnStructNestedIntStructAlignmentInt16AfterCallback),
+ CallbackTest.withCheck(
+ "ReturnStructNestedIntStructAlignmentInt32",
+ Pointer.fromFunction<ReturnStructNestedIntStructAlignmentInt32Type>(
+ returnStructNestedIntStructAlignmentInt32),
+ returnStructNestedIntStructAlignmentInt32AfterCallback),
+ CallbackTest.withCheck(
+ "ReturnStructNestedIntStructAlignmentInt64",
+ Pointer.fromFunction<ReturnStructNestedIntStructAlignmentInt64Type>(
+ returnStructNestedIntStructAlignmentInt64),
+ returnStructNestedIntStructAlignmentInt64AfterCallback),
+ CallbackTest.withCheck(
+ "ReturnStructNestedIrregularEvenBigger",
+ Pointer.fromFunction<ReturnStructNestedIrregularEvenBiggerType>(
+ returnStructNestedIrregularEvenBigger),
+ returnStructNestedIrregularEvenBiggerAfterCallback),
];
typedef PassStruct1ByteIntx10Type = Int64 Function(
Struct1ByteInt,
@@ -4128,6 +4228,1013 @@
Expect.equals(-2, result);
}
+typedef PassStruct8BytesNestedIntx10Type = Int64 Function(
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt);
+
+// Global variables to be able to test inputs after callback returned.
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a0 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a1 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a2 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a3 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a4 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a5 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a6 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a7 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a8 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a9 = Struct8BytesNestedInt();
+
+// Result variable also global, so we can delete it after the callback.
+int passStruct8BytesNestedIntx10Result = 0;
+
+int passStruct8BytesNestedIntx10CalculateResult() {
+ int result = 0;
+
+ result += passStruct8BytesNestedIntx10_a0.a0.a0;
+ result += passStruct8BytesNestedIntx10_a0.a0.a1;
+ result += passStruct8BytesNestedIntx10_a0.a1.a0;
+ result += passStruct8BytesNestedIntx10_a0.a1.a1;
+ result += passStruct8BytesNestedIntx10_a1.a0.a0;
+ result += passStruct8BytesNestedIntx10_a1.a0.a1;
+ result += passStruct8BytesNestedIntx10_a1.a1.a0;
+ result += passStruct8BytesNestedIntx10_a1.a1.a1;
+ result += passStruct8BytesNestedIntx10_a2.a0.a0;
+ result += passStruct8BytesNestedIntx10_a2.a0.a1;
+ result += passStruct8BytesNestedIntx10_a2.a1.a0;
+ result += passStruct8BytesNestedIntx10_a2.a1.a1;
+ result += passStruct8BytesNestedIntx10_a3.a0.a0;
+ result += passStruct8BytesNestedIntx10_a3.a0.a1;
+ result += passStruct8BytesNestedIntx10_a3.a1.a0;
+ result += passStruct8BytesNestedIntx10_a3.a1.a1;
+ result += passStruct8BytesNestedIntx10_a4.a0.a0;
+ result += passStruct8BytesNestedIntx10_a4.a0.a1;
+ result += passStruct8BytesNestedIntx10_a4.a1.a0;
+ result += passStruct8BytesNestedIntx10_a4.a1.a1;
+ result += passStruct8BytesNestedIntx10_a5.a0.a0;
+ result += passStruct8BytesNestedIntx10_a5.a0.a1;
+ result += passStruct8BytesNestedIntx10_a5.a1.a0;
+ result += passStruct8BytesNestedIntx10_a5.a1.a1;
+ result += passStruct8BytesNestedIntx10_a6.a0.a0;
+ result += passStruct8BytesNestedIntx10_a6.a0.a1;
+ result += passStruct8BytesNestedIntx10_a6.a1.a0;
+ result += passStruct8BytesNestedIntx10_a6.a1.a1;
+ result += passStruct8BytesNestedIntx10_a7.a0.a0;
+ result += passStruct8BytesNestedIntx10_a7.a0.a1;
+ result += passStruct8BytesNestedIntx10_a7.a1.a0;
+ result += passStruct8BytesNestedIntx10_a7.a1.a1;
+ result += passStruct8BytesNestedIntx10_a8.a0.a0;
+ result += passStruct8BytesNestedIntx10_a8.a0.a1;
+ result += passStruct8BytesNestedIntx10_a8.a1.a0;
+ result += passStruct8BytesNestedIntx10_a8.a1.a1;
+ result += passStruct8BytesNestedIntx10_a9.a0.a0;
+ result += passStruct8BytesNestedIntx10_a9.a0.a1;
+ result += passStruct8BytesNestedIntx10_a9.a1.a0;
+ result += passStruct8BytesNestedIntx10_a9.a1.a1;
+
+ passStruct8BytesNestedIntx10Result = result;
+
+ return result;
+}
+
+/// Simple nested struct. No alignment gaps on any architectures.
+/// 10 arguments exhaust registers on all platforms.
+int passStruct8BytesNestedIntx10(
+ Struct8BytesNestedInt a0,
+ Struct8BytesNestedInt a1,
+ Struct8BytesNestedInt a2,
+ Struct8BytesNestedInt a3,
+ Struct8BytesNestedInt a4,
+ Struct8BytesNestedInt a5,
+ Struct8BytesNestedInt a6,
+ Struct8BytesNestedInt a7,
+ Struct8BytesNestedInt a8,
+ Struct8BytesNestedInt a9) {
+ print(
+ "passStruct8BytesNestedIntx10(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9})");
+
+ // In legacy mode, possibly return null.
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+ print("throwing!");
+ throw Exception("PassStruct8BytesNestedIntx10 throwing on purpuse!");
+ }
+
+ passStruct8BytesNestedIntx10_a0 = a0;
+ passStruct8BytesNestedIntx10_a1 = a1;
+ passStruct8BytesNestedIntx10_a2 = a2;
+ passStruct8BytesNestedIntx10_a3 = a3;
+ passStruct8BytesNestedIntx10_a4 = a4;
+ passStruct8BytesNestedIntx10_a5 = a5;
+ passStruct8BytesNestedIntx10_a6 = a6;
+ passStruct8BytesNestedIntx10_a7 = a7;
+ passStruct8BytesNestedIntx10_a8 = a8;
+ passStruct8BytesNestedIntx10_a9 = a9;
+
+ final result = passStruct8BytesNestedIntx10CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passStruct8BytesNestedIntx10AfterCallback() {
+ final result = passStruct8BytesNestedIntx10CalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.equals(20, result);
+}
+
+typedef PassStruct8BytesNestedFloatx10Type = Float Function(
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat);
+
+// Global variables to be able to test inputs after callback returned.
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a0 =
+ Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a1 =
+ Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a2 =
+ Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a3 =
+ Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a4 =
+ Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a5 =
+ Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a6 =
+ Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a7 =
+ Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a8 =
+ Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a9 =
+ Struct8BytesNestedFloat();
+
+// Result variable also global, so we can delete it after the callback.
+double passStruct8BytesNestedFloatx10Result = 0.0;
+
+double passStruct8BytesNestedFloatx10CalculateResult() {
+ double result = 0;
+
+ result += passStruct8BytesNestedFloatx10_a0.a0.a0;
+ result += passStruct8BytesNestedFloatx10_a0.a1.a0;
+ result += passStruct8BytesNestedFloatx10_a1.a0.a0;
+ result += passStruct8BytesNestedFloatx10_a1.a1.a0;
+ result += passStruct8BytesNestedFloatx10_a2.a0.a0;
+ result += passStruct8BytesNestedFloatx10_a2.a1.a0;
+ result += passStruct8BytesNestedFloatx10_a3.a0.a0;
+ result += passStruct8BytesNestedFloatx10_a3.a1.a0;
+ result += passStruct8BytesNestedFloatx10_a4.a0.a0;
+ result += passStruct8BytesNestedFloatx10_a4.a1.a0;
+ result += passStruct8BytesNestedFloatx10_a5.a0.a0;
+ result += passStruct8BytesNestedFloatx10_a5.a1.a0;
+ result += passStruct8BytesNestedFloatx10_a6.a0.a0;
+ result += passStruct8BytesNestedFloatx10_a6.a1.a0;
+ result += passStruct8BytesNestedFloatx10_a7.a0.a0;
+ result += passStruct8BytesNestedFloatx10_a7.a1.a0;
+ result += passStruct8BytesNestedFloatx10_a8.a0.a0;
+ result += passStruct8BytesNestedFloatx10_a8.a1.a0;
+ result += passStruct8BytesNestedFloatx10_a9.a0.a0;
+ result += passStruct8BytesNestedFloatx10_a9.a1.a0;
+
+ passStruct8BytesNestedFloatx10Result = result;
+
+ return result;
+}
+
+/// Simple nested struct. No alignment gaps on any architectures.
+/// 10 arguments exhaust fpu registers on all platforms.
+double passStruct8BytesNestedFloatx10(
+ Struct8BytesNestedFloat a0,
+ Struct8BytesNestedFloat a1,
+ Struct8BytesNestedFloat a2,
+ Struct8BytesNestedFloat a3,
+ Struct8BytesNestedFloat a4,
+ Struct8BytesNestedFloat a5,
+ Struct8BytesNestedFloat a6,
+ Struct8BytesNestedFloat a7,
+ Struct8BytesNestedFloat a8,
+ Struct8BytesNestedFloat a9) {
+ print(
+ "passStruct8BytesNestedFloatx10(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9})");
+
+ // In legacy mode, possibly return null.
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+ print("throwing!");
+ throw Exception("PassStruct8BytesNestedFloatx10 throwing on purpuse!");
+ }
+
+ passStruct8BytesNestedFloatx10_a0 = a0;
+ passStruct8BytesNestedFloatx10_a1 = a1;
+ passStruct8BytesNestedFloatx10_a2 = a2;
+ passStruct8BytesNestedFloatx10_a3 = a3;
+ passStruct8BytesNestedFloatx10_a4 = a4;
+ passStruct8BytesNestedFloatx10_a5 = a5;
+ passStruct8BytesNestedFloatx10_a6 = a6;
+ passStruct8BytesNestedFloatx10_a7 = a7;
+ passStruct8BytesNestedFloatx10_a8 = a8;
+ passStruct8BytesNestedFloatx10_a9 = a9;
+
+ final result = passStruct8BytesNestedFloatx10CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passStruct8BytesNestedFloatx10AfterCallback() {
+ final result = passStruct8BytesNestedFloatx10CalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.approxEquals(10.0, result);
+}
+
+typedef PassStruct8BytesNestedFloat2x10Type = Float Function(
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2);
+
+// Global variables to be able to test inputs after callback returned.
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a0 =
+ Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a1 =
+ Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a2 =
+ Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a3 =
+ Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a4 =
+ Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a5 =
+ Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a6 =
+ Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a7 =
+ Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a8 =
+ Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a9 =
+ Struct8BytesNestedFloat2();
+
+// Result variable also global, so we can delete it after the callback.
+double passStruct8BytesNestedFloat2x10Result = 0.0;
+
+double passStruct8BytesNestedFloat2x10CalculateResult() {
+ double result = 0;
+
+ result += passStruct8BytesNestedFloat2x10_a0.a0.a0;
+ result += passStruct8BytesNestedFloat2x10_a0.a1;
+ result += passStruct8BytesNestedFloat2x10_a1.a0.a0;
+ result += passStruct8BytesNestedFloat2x10_a1.a1;
+ result += passStruct8BytesNestedFloat2x10_a2.a0.a0;
+ result += passStruct8BytesNestedFloat2x10_a2.a1;
+ result += passStruct8BytesNestedFloat2x10_a3.a0.a0;
+ result += passStruct8BytesNestedFloat2x10_a3.a1;
+ result += passStruct8BytesNestedFloat2x10_a4.a0.a0;
+ result += passStruct8BytesNestedFloat2x10_a4.a1;
+ result += passStruct8BytesNestedFloat2x10_a5.a0.a0;
+ result += passStruct8BytesNestedFloat2x10_a5.a1;
+ result += passStruct8BytesNestedFloat2x10_a6.a0.a0;
+ result += passStruct8BytesNestedFloat2x10_a6.a1;
+ result += passStruct8BytesNestedFloat2x10_a7.a0.a0;
+ result += passStruct8BytesNestedFloat2x10_a7.a1;
+ result += passStruct8BytesNestedFloat2x10_a8.a0.a0;
+ result += passStruct8BytesNestedFloat2x10_a8.a1;
+ result += passStruct8BytesNestedFloat2x10_a9.a0.a0;
+ result += passStruct8BytesNestedFloat2x10_a9.a1;
+
+ passStruct8BytesNestedFloat2x10Result = result;
+
+ return result;
+}
+
+/// Simple nested struct. No alignment gaps on any architectures.
+/// 10 arguments exhaust fpu registers on all platforms.
+/// The nesting is irregular, testing homogenous float rules on arm and arm64,
+/// and the fpu register usage on x64.
+double passStruct8BytesNestedFloat2x10(
+ Struct8BytesNestedFloat2 a0,
+ Struct8BytesNestedFloat2 a1,
+ Struct8BytesNestedFloat2 a2,
+ Struct8BytesNestedFloat2 a3,
+ Struct8BytesNestedFloat2 a4,
+ Struct8BytesNestedFloat2 a5,
+ Struct8BytesNestedFloat2 a6,
+ Struct8BytesNestedFloat2 a7,
+ Struct8BytesNestedFloat2 a8,
+ Struct8BytesNestedFloat2 a9) {
+ print(
+ "passStruct8BytesNestedFloat2x10(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9})");
+
+ // In legacy mode, possibly return null.
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+ print("throwing!");
+ throw Exception("PassStruct8BytesNestedFloat2x10 throwing on purpuse!");
+ }
+
+ passStruct8BytesNestedFloat2x10_a0 = a0;
+ passStruct8BytesNestedFloat2x10_a1 = a1;
+ passStruct8BytesNestedFloat2x10_a2 = a2;
+ passStruct8BytesNestedFloat2x10_a3 = a3;
+ passStruct8BytesNestedFloat2x10_a4 = a4;
+ passStruct8BytesNestedFloat2x10_a5 = a5;
+ passStruct8BytesNestedFloat2x10_a6 = a6;
+ passStruct8BytesNestedFloat2x10_a7 = a7;
+ passStruct8BytesNestedFloat2x10_a8 = a8;
+ passStruct8BytesNestedFloat2x10_a9 = a9;
+
+ final result = passStruct8BytesNestedFloat2x10CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passStruct8BytesNestedFloat2x10AfterCallback() {
+ final result = passStruct8BytesNestedFloat2x10CalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.approxEquals(10.0, result);
+}
+
+typedef PassStruct8BytesNestedMixedx10Type = Double Function(
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed);
+
+// Global variables to be able to test inputs after callback returned.
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a0 =
+ Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a1 =
+ Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a2 =
+ Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a3 =
+ Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a4 =
+ Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a5 =
+ Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a6 =
+ Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a7 =
+ Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a8 =
+ Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a9 =
+ Struct8BytesNestedMixed();
+
+// Result variable also global, so we can delete it after the callback.
+double passStruct8BytesNestedMixedx10Result = 0.0;
+
+double passStruct8BytesNestedMixedx10CalculateResult() {
+ double result = 0;
+
+ result += passStruct8BytesNestedMixedx10_a0.a0.a0;
+ result += passStruct8BytesNestedMixedx10_a0.a0.a1;
+ result += passStruct8BytesNestedMixedx10_a0.a1.a0;
+ result += passStruct8BytesNestedMixedx10_a1.a0.a0;
+ result += passStruct8BytesNestedMixedx10_a1.a0.a1;
+ result += passStruct8BytesNestedMixedx10_a1.a1.a0;
+ result += passStruct8BytesNestedMixedx10_a2.a0.a0;
+ result += passStruct8BytesNestedMixedx10_a2.a0.a1;
+ result += passStruct8BytesNestedMixedx10_a2.a1.a0;
+ result += passStruct8BytesNestedMixedx10_a3.a0.a0;
+ result += passStruct8BytesNestedMixedx10_a3.a0.a1;
+ result += passStruct8BytesNestedMixedx10_a3.a1.a0;
+ result += passStruct8BytesNestedMixedx10_a4.a0.a0;
+ result += passStruct8BytesNestedMixedx10_a4.a0.a1;
+ result += passStruct8BytesNestedMixedx10_a4.a1.a0;
+ result += passStruct8BytesNestedMixedx10_a5.a0.a0;
+ result += passStruct8BytesNestedMixedx10_a5.a0.a1;
+ result += passStruct8BytesNestedMixedx10_a5.a1.a0;
+ result += passStruct8BytesNestedMixedx10_a6.a0.a0;
+ result += passStruct8BytesNestedMixedx10_a6.a0.a1;
+ result += passStruct8BytesNestedMixedx10_a6.a1.a0;
+ result += passStruct8BytesNestedMixedx10_a7.a0.a0;
+ result += passStruct8BytesNestedMixedx10_a7.a0.a1;
+ result += passStruct8BytesNestedMixedx10_a7.a1.a0;
+ result += passStruct8BytesNestedMixedx10_a8.a0.a0;
+ result += passStruct8BytesNestedMixedx10_a8.a0.a1;
+ result += passStruct8BytesNestedMixedx10_a8.a1.a0;
+ result += passStruct8BytesNestedMixedx10_a9.a0.a0;
+ result += passStruct8BytesNestedMixedx10_a9.a0.a1;
+ result += passStruct8BytesNestedMixedx10_a9.a1.a0;
+
+ passStruct8BytesNestedMixedx10Result = result;
+
+ return result;
+}
+
+/// Simple nested struct. No alignment gaps on any architectures.
+/// 10 arguments exhaust all registers on all platforms.
+double passStruct8BytesNestedMixedx10(
+ Struct8BytesNestedMixed a0,
+ Struct8BytesNestedMixed a1,
+ Struct8BytesNestedMixed a2,
+ Struct8BytesNestedMixed a3,
+ Struct8BytesNestedMixed a4,
+ Struct8BytesNestedMixed a5,
+ Struct8BytesNestedMixed a6,
+ Struct8BytesNestedMixed a7,
+ Struct8BytesNestedMixed a8,
+ Struct8BytesNestedMixed a9) {
+ print(
+ "passStruct8BytesNestedMixedx10(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9})");
+
+ // In legacy mode, possibly return null.
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+ print("throwing!");
+ throw Exception("PassStruct8BytesNestedMixedx10 throwing on purpuse!");
+ }
+
+ passStruct8BytesNestedMixedx10_a0 = a0;
+ passStruct8BytesNestedMixedx10_a1 = a1;
+ passStruct8BytesNestedMixedx10_a2 = a2;
+ passStruct8BytesNestedMixedx10_a3 = a3;
+ passStruct8BytesNestedMixedx10_a4 = a4;
+ passStruct8BytesNestedMixedx10_a5 = a5;
+ passStruct8BytesNestedMixedx10_a6 = a6;
+ passStruct8BytesNestedMixedx10_a7 = a7;
+ passStruct8BytesNestedMixedx10_a8 = a8;
+ passStruct8BytesNestedMixedx10_a9 = a9;
+
+ final result = passStruct8BytesNestedMixedx10CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passStruct8BytesNestedMixedx10AfterCallback() {
+ final result = passStruct8BytesNestedMixedx10CalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.approxEquals(15.0, result);
+}
+
+typedef PassStruct16BytesNestedIntx2Type = Int64 Function(
+ Struct16BytesNestedInt, Struct16BytesNestedInt);
+
+// Global variables to be able to test inputs after callback returned.
+Struct16BytesNestedInt passStruct16BytesNestedIntx2_a0 =
+ Struct16BytesNestedInt();
+Struct16BytesNestedInt passStruct16BytesNestedIntx2_a1 =
+ Struct16BytesNestedInt();
+
+// Result variable also global, so we can delete it after the callback.
+int passStruct16BytesNestedIntx2Result = 0;
+
+int passStruct16BytesNestedIntx2CalculateResult() {
+ int result = 0;
+
+ result += passStruct16BytesNestedIntx2_a0.a0.a0.a0;
+ result += passStruct16BytesNestedIntx2_a0.a0.a0.a1;
+ result += passStruct16BytesNestedIntx2_a0.a0.a1.a0;
+ result += passStruct16BytesNestedIntx2_a0.a0.a1.a1;
+ result += passStruct16BytesNestedIntx2_a0.a1.a0.a0;
+ result += passStruct16BytesNestedIntx2_a0.a1.a0.a1;
+ result += passStruct16BytesNestedIntx2_a0.a1.a1.a0;
+ result += passStruct16BytesNestedIntx2_a0.a1.a1.a1;
+ result += passStruct16BytesNestedIntx2_a1.a0.a0.a0;
+ result += passStruct16BytesNestedIntx2_a1.a0.a0.a1;
+ result += passStruct16BytesNestedIntx2_a1.a0.a1.a0;
+ result += passStruct16BytesNestedIntx2_a1.a0.a1.a1;
+ result += passStruct16BytesNestedIntx2_a1.a1.a0.a0;
+ result += passStruct16BytesNestedIntx2_a1.a1.a0.a1;
+ result += passStruct16BytesNestedIntx2_a1.a1.a1.a0;
+ result += passStruct16BytesNestedIntx2_a1.a1.a1.a1;
+
+ passStruct16BytesNestedIntx2Result = result;
+
+ return result;
+}
+
+/// Deeper nested struct to test recursive member access.
+int passStruct16BytesNestedIntx2(
+ Struct16BytesNestedInt a0, Struct16BytesNestedInt a1) {
+ print("passStruct16BytesNestedIntx2(${a0}, ${a1})");
+
+ // In legacy mode, possibly return null.
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0.a0.a0 == 42 || a0.a0.a0.a0 == 84) {
+ print("throwing!");
+ throw Exception("PassStruct16BytesNestedIntx2 throwing on purpuse!");
+ }
+
+ passStruct16BytesNestedIntx2_a0 = a0;
+ passStruct16BytesNestedIntx2_a1 = a1;
+
+ final result = passStruct16BytesNestedIntx2CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passStruct16BytesNestedIntx2AfterCallback() {
+ final result = passStruct16BytesNestedIntx2CalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.equals(8, result);
+}
+
+typedef PassStruct32BytesNestedIntx2Type = Int64 Function(
+ Struct32BytesNestedInt, Struct32BytesNestedInt);
+
+// Global variables to be able to test inputs after callback returned.
+Struct32BytesNestedInt passStruct32BytesNestedIntx2_a0 =
+ Struct32BytesNestedInt();
+Struct32BytesNestedInt passStruct32BytesNestedIntx2_a1 =
+ Struct32BytesNestedInt();
+
+// Result variable also global, so we can delete it after the callback.
+int passStruct32BytesNestedIntx2Result = 0;
+
+int passStruct32BytesNestedIntx2CalculateResult() {
+ int result = 0;
+
+ result += passStruct32BytesNestedIntx2_a0.a0.a0.a0.a0;
+ result += passStruct32BytesNestedIntx2_a0.a0.a0.a0.a1;
+ result += passStruct32BytesNestedIntx2_a0.a0.a0.a1.a0;
+ result += passStruct32BytesNestedIntx2_a0.a0.a0.a1.a1;
+ result += passStruct32BytesNestedIntx2_a0.a0.a1.a0.a0;
+ result += passStruct32BytesNestedIntx2_a0.a0.a1.a0.a1;
+ result += passStruct32BytesNestedIntx2_a0.a0.a1.a1.a0;
+ result += passStruct32BytesNestedIntx2_a0.a0.a1.a1.a1;
+ result += passStruct32BytesNestedIntx2_a0.a1.a0.a0.a0;
+ result += passStruct32BytesNestedIntx2_a0.a1.a0.a0.a1;
+ result += passStruct32BytesNestedIntx2_a0.a1.a0.a1.a0;
+ result += passStruct32BytesNestedIntx2_a0.a1.a0.a1.a1;
+ result += passStruct32BytesNestedIntx2_a0.a1.a1.a0.a0;
+ result += passStruct32BytesNestedIntx2_a0.a1.a1.a0.a1;
+ result += passStruct32BytesNestedIntx2_a0.a1.a1.a1.a0;
+ result += passStruct32BytesNestedIntx2_a0.a1.a1.a1.a1;
+ result += passStruct32BytesNestedIntx2_a1.a0.a0.a0.a0;
+ result += passStruct32BytesNestedIntx2_a1.a0.a0.a0.a1;
+ result += passStruct32BytesNestedIntx2_a1.a0.a0.a1.a0;
+ result += passStruct32BytesNestedIntx2_a1.a0.a0.a1.a1;
+ result += passStruct32BytesNestedIntx2_a1.a0.a1.a0.a0;
+ result += passStruct32BytesNestedIntx2_a1.a0.a1.a0.a1;
+ result += passStruct32BytesNestedIntx2_a1.a0.a1.a1.a0;
+ result += passStruct32BytesNestedIntx2_a1.a0.a1.a1.a1;
+ result += passStruct32BytesNestedIntx2_a1.a1.a0.a0.a0;
+ result += passStruct32BytesNestedIntx2_a1.a1.a0.a0.a1;
+ result += passStruct32BytesNestedIntx2_a1.a1.a0.a1.a0;
+ result += passStruct32BytesNestedIntx2_a1.a1.a0.a1.a1;
+ result += passStruct32BytesNestedIntx2_a1.a1.a1.a0.a0;
+ result += passStruct32BytesNestedIntx2_a1.a1.a1.a0.a1;
+ result += passStruct32BytesNestedIntx2_a1.a1.a1.a1.a0;
+ result += passStruct32BytesNestedIntx2_a1.a1.a1.a1.a1;
+
+ passStruct32BytesNestedIntx2Result = result;
+
+ return result;
+}
+
+/// Even deeper nested struct to test recursive member access.
+int passStruct32BytesNestedIntx2(
+ Struct32BytesNestedInt a0, Struct32BytesNestedInt a1) {
+ print("passStruct32BytesNestedIntx2(${a0}, ${a1})");
+
+ // In legacy mode, possibly return null.
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0.a0.a0.a0 == 42 || a0.a0.a0.a0.a0 == 84) {
+ print("throwing!");
+ throw Exception("PassStruct32BytesNestedIntx2 throwing on purpuse!");
+ }
+
+ passStruct32BytesNestedIntx2_a0 = a0;
+ passStruct32BytesNestedIntx2_a1 = a1;
+
+ final result = passStruct32BytesNestedIntx2CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passStruct32BytesNestedIntx2AfterCallback() {
+ final result = passStruct32BytesNestedIntx2CalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.equals(16, result);
+}
+
+typedef PassStructNestedIntStructAlignmentInt16Type = Int64 Function(
+ StructNestedIntStructAlignmentInt16);
+
+// Global variables to be able to test inputs after callback returned.
+StructNestedIntStructAlignmentInt16 passStructNestedIntStructAlignmentInt16_a0 =
+ StructNestedIntStructAlignmentInt16();
+
+// Result variable also global, so we can delete it after the callback.
+int passStructNestedIntStructAlignmentInt16Result = 0;
+
+int passStructNestedIntStructAlignmentInt16CalculateResult() {
+ int result = 0;
+
+ result += passStructNestedIntStructAlignmentInt16_a0.a0.a0;
+ result += passStructNestedIntStructAlignmentInt16_a0.a0.a1;
+ result += passStructNestedIntStructAlignmentInt16_a0.a0.a2;
+ result += passStructNestedIntStructAlignmentInt16_a0.a1.a0;
+ result += passStructNestedIntStructAlignmentInt16_a0.a1.a1;
+ result += passStructNestedIntStructAlignmentInt16_a0.a1.a2;
+
+ passStructNestedIntStructAlignmentInt16Result = result;
+
+ return result;
+}
+
+/// Test alignment and padding of nested struct with 16 byte int.
+int passStructNestedIntStructAlignmentInt16(
+ StructNestedIntStructAlignmentInt16 a0) {
+ print("passStructNestedIntStructAlignmentInt16(${a0})");
+
+ // In legacy mode, possibly return null.
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+ print("throwing!");
+ throw Exception(
+ "PassStructNestedIntStructAlignmentInt16 throwing on purpuse!");
+ }
+
+ passStructNestedIntStructAlignmentInt16_a0 = a0;
+
+ final result = passStructNestedIntStructAlignmentInt16CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passStructNestedIntStructAlignmentInt16AfterCallback() {
+ final result = passStructNestedIntStructAlignmentInt16CalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.equals(3, result);
+}
+
+typedef PassStructNestedIntStructAlignmentInt32Type = Int64 Function(
+ StructNestedIntStructAlignmentInt32);
+
+// Global variables to be able to test inputs after callback returned.
+StructNestedIntStructAlignmentInt32 passStructNestedIntStructAlignmentInt32_a0 =
+ StructNestedIntStructAlignmentInt32();
+
+// Result variable also global, so we can delete it after the callback.
+int passStructNestedIntStructAlignmentInt32Result = 0;
+
+int passStructNestedIntStructAlignmentInt32CalculateResult() {
+ int result = 0;
+
+ result += passStructNestedIntStructAlignmentInt32_a0.a0.a0;
+ result += passStructNestedIntStructAlignmentInt32_a0.a0.a1;
+ result += passStructNestedIntStructAlignmentInt32_a0.a0.a2;
+ result += passStructNestedIntStructAlignmentInt32_a0.a1.a0;
+ result += passStructNestedIntStructAlignmentInt32_a0.a1.a1;
+ result += passStructNestedIntStructAlignmentInt32_a0.a1.a2;
+
+ passStructNestedIntStructAlignmentInt32Result = result;
+
+ return result;
+}
+
+/// Test alignment and padding of nested struct with 32 byte int.
+int passStructNestedIntStructAlignmentInt32(
+ StructNestedIntStructAlignmentInt32 a0) {
+ print("passStructNestedIntStructAlignmentInt32(${a0})");
+
+ // In legacy mode, possibly return null.
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+ print("throwing!");
+ throw Exception(
+ "PassStructNestedIntStructAlignmentInt32 throwing on purpuse!");
+ }
+
+ passStructNestedIntStructAlignmentInt32_a0 = a0;
+
+ final result = passStructNestedIntStructAlignmentInt32CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passStructNestedIntStructAlignmentInt32AfterCallback() {
+ final result = passStructNestedIntStructAlignmentInt32CalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.equals(3, result);
+}
+
+typedef PassStructNestedIntStructAlignmentInt64Type = Int64 Function(
+ StructNestedIntStructAlignmentInt64);
+
+// Global variables to be able to test inputs after callback returned.
+StructNestedIntStructAlignmentInt64 passStructNestedIntStructAlignmentInt64_a0 =
+ StructNestedIntStructAlignmentInt64();
+
+// Result variable also global, so we can delete it after the callback.
+int passStructNestedIntStructAlignmentInt64Result = 0;
+
+int passStructNestedIntStructAlignmentInt64CalculateResult() {
+ int result = 0;
+
+ result += passStructNestedIntStructAlignmentInt64_a0.a0.a0;
+ result += passStructNestedIntStructAlignmentInt64_a0.a0.a1;
+ result += passStructNestedIntStructAlignmentInt64_a0.a0.a2;
+ result += passStructNestedIntStructAlignmentInt64_a0.a1.a0;
+ result += passStructNestedIntStructAlignmentInt64_a0.a1.a1;
+ result += passStructNestedIntStructAlignmentInt64_a0.a1.a2;
+
+ passStructNestedIntStructAlignmentInt64Result = result;
+
+ return result;
+}
+
+/// Test alignment and padding of nested struct with 64 byte int.
+int passStructNestedIntStructAlignmentInt64(
+ StructNestedIntStructAlignmentInt64 a0) {
+ print("passStructNestedIntStructAlignmentInt64(${a0})");
+
+ // In legacy mode, possibly return null.
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+ print("throwing!");
+ throw Exception(
+ "PassStructNestedIntStructAlignmentInt64 throwing on purpuse!");
+ }
+
+ passStructNestedIntStructAlignmentInt64_a0 = a0;
+
+ final result = passStructNestedIntStructAlignmentInt64CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passStructNestedIntStructAlignmentInt64AfterCallback() {
+ final result = passStructNestedIntStructAlignmentInt64CalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.equals(3, result);
+}
+
+typedef PassStructNestedIrregularEvenBiggerx4Type = Double Function(
+ StructNestedIrregularEvenBigger,
+ StructNestedIrregularEvenBigger,
+ StructNestedIrregularEvenBigger,
+ StructNestedIrregularEvenBigger);
+
+// Global variables to be able to test inputs after callback returned.
+StructNestedIrregularEvenBigger passStructNestedIrregularEvenBiggerx4_a0 =
+ StructNestedIrregularEvenBigger();
+StructNestedIrregularEvenBigger passStructNestedIrregularEvenBiggerx4_a1 =
+ StructNestedIrregularEvenBigger();
+StructNestedIrregularEvenBigger passStructNestedIrregularEvenBiggerx4_a2 =
+ StructNestedIrregularEvenBigger();
+StructNestedIrregularEvenBigger passStructNestedIrregularEvenBiggerx4_a3 =
+ StructNestedIrregularEvenBigger();
+
+// Result variable also global, so we can delete it after the callback.
+double passStructNestedIrregularEvenBiggerx4Result = 0.0;
+
+double passStructNestedIrregularEvenBiggerx4CalculateResult() {
+ double result = 0;
+
+ result += passStructNestedIrregularEvenBiggerx4_a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a1.a0.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a1.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a2;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a3.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a3.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a4;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a5.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a5.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a6;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a1.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a1.a1.a0.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a1.a1.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a1.a2;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a1.a3;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a1.a0.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a1.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a2;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a3.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a3.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a4;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a5.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a5.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a6;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a2.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a2.a1.a0.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a2.a1.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a2.a2;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a2.a3;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a3;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a1.a0.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a1.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a2;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a3.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a3.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a4;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a5.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a5.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a6;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a1.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a1.a1.a0.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a1.a1.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a1.a2;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a1.a3;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a1.a0.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a1.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a2;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a3.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a3.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a4;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a5.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a5.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a6;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a2.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a2.a1.a0.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a2.a1.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a2.a2;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a2.a3;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a3;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a1.a0.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a1.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a2;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a3.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a3.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a4;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a5.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a5.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a6;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a1.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a1.a1.a0.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a1.a1.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a1.a2;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a1.a3;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a1.a0.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a1.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a2;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a3.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a3.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a4;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a5.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a5.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a6;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a2.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a2.a1.a0.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a2.a1.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a2.a2;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a2.a3;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a3;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a1.a0.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a1.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a2;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a3.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a3.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a4;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a5.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a5.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a6;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a1.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a1.a1.a0.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a1.a1.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a1.a2;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a1.a3;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a1.a0.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a1.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a2;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a3.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a3.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a4;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a5.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a5.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a6;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a2.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a2.a1.a0.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a2.a1.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a2.a2;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a2.a3;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a3;
+
+ passStructNestedIrregularEvenBiggerx4Result = result;
+
+ return result;
+}
+
+/// Return big irregular struct as smoke test.
+double passStructNestedIrregularEvenBiggerx4(
+ StructNestedIrregularEvenBigger a0,
+ StructNestedIrregularEvenBigger a1,
+ StructNestedIrregularEvenBigger a2,
+ StructNestedIrregularEvenBigger a3) {
+ print("passStructNestedIrregularEvenBiggerx4(${a0}, ${a1}, ${a2}, ${a3})");
+
+ // In legacy mode, possibly return null.
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0 == 42 || a0.a0 == 84) {
+ print("throwing!");
+ throw Exception(
+ "PassStructNestedIrregularEvenBiggerx4 throwing on purpuse!");
+ }
+
+ passStructNestedIrregularEvenBiggerx4_a0 = a0;
+ passStructNestedIrregularEvenBiggerx4_a1 = a1;
+ passStructNestedIrregularEvenBiggerx4_a2 = a2;
+ passStructNestedIrregularEvenBiggerx4_a3 = a3;
+
+ final result = passStructNestedIrregularEvenBiggerx4CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passStructNestedIrregularEvenBiggerx4AfterCallback() {
+ final result = passStructNestedIrregularEvenBiggerx4CalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.approxEquals(1572.0, result);
+}
+
typedef ReturnStruct1ByteIntType = Struct1ByteInt Function(Int8);
// Global variables to be able to test inputs after callback returned.
@@ -6666,3 +7773,657 @@
free(returnStructAlignmentInt64Result.addressOf);
}
+
+typedef ReturnStruct8BytesNestedIntType = Struct8BytesNestedInt Function(
+ Struct4BytesHomogeneousInt16, Struct4BytesHomogeneousInt16);
+
+// Global variables to be able to test inputs after callback returned.
+Struct4BytesHomogeneousInt16 returnStruct8BytesNestedInt_a0 =
+ Struct4BytesHomogeneousInt16();
+Struct4BytesHomogeneousInt16 returnStruct8BytesNestedInt_a1 =
+ Struct4BytesHomogeneousInt16();
+
+// Result variable also global, so we can delete it after the callback.
+Struct8BytesNestedInt returnStruct8BytesNestedIntResult =
+ Struct8BytesNestedInt();
+
+Struct8BytesNestedInt returnStruct8BytesNestedIntCalculateResult() {
+ Struct8BytesNestedInt result = allocate<Struct8BytesNestedInt>().ref;
+
+ result.a0.a0 = returnStruct8BytesNestedInt_a0.a0;
+ result.a0.a1 = returnStruct8BytesNestedInt_a0.a1;
+ result.a1.a0 = returnStruct8BytesNestedInt_a1.a0;
+ result.a1.a1 = returnStruct8BytesNestedInt_a1.a1;
+
+ returnStruct8BytesNestedIntResult = result;
+
+ return result;
+}
+
+/// Simple nested struct.
+Struct8BytesNestedInt returnStruct8BytesNestedInt(
+ Struct4BytesHomogeneousInt16 a0, Struct4BytesHomogeneousInt16 a1) {
+ print("returnStruct8BytesNestedInt(${a0}, ${a1})");
+
+ // In legacy mode, possibly return null.
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0 == 42 || a0.a0 == 84) {
+ print("throwing!");
+ throw Exception("ReturnStruct8BytesNestedInt throwing on purpuse!");
+ }
+
+ returnStruct8BytesNestedInt_a0 = a0;
+ returnStruct8BytesNestedInt_a1 = a1;
+
+ final result = returnStruct8BytesNestedIntCalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void returnStruct8BytesNestedIntAfterCallback() {
+ free(returnStruct8BytesNestedIntResult.addressOf);
+
+ final result = returnStruct8BytesNestedIntCalculateResult();
+
+ print("after callback result = $result");
+
+ free(returnStruct8BytesNestedIntResult.addressOf);
+}
+
+typedef ReturnStruct8BytesNestedFloatType = Struct8BytesNestedFloat Function(
+ Struct4BytesFloat, Struct4BytesFloat);
+
+// Global variables to be able to test inputs after callback returned.
+Struct4BytesFloat returnStruct8BytesNestedFloat_a0 = Struct4BytesFloat();
+Struct4BytesFloat returnStruct8BytesNestedFloat_a1 = Struct4BytesFloat();
+
+// Result variable also global, so we can delete it after the callback.
+Struct8BytesNestedFloat returnStruct8BytesNestedFloatResult =
+ Struct8BytesNestedFloat();
+
+Struct8BytesNestedFloat returnStruct8BytesNestedFloatCalculateResult() {
+ Struct8BytesNestedFloat result = allocate<Struct8BytesNestedFloat>().ref;
+
+ result.a0.a0 = returnStruct8BytesNestedFloat_a0.a0;
+ result.a1.a0 = returnStruct8BytesNestedFloat_a1.a0;
+
+ returnStruct8BytesNestedFloatResult = result;
+
+ return result;
+}
+
+/// Simple nested struct with floats.
+Struct8BytesNestedFloat returnStruct8BytesNestedFloat(
+ Struct4BytesFloat a0, Struct4BytesFloat a1) {
+ print("returnStruct8BytesNestedFloat(${a0}, ${a1})");
+
+ // In legacy mode, possibly return null.
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0 == 42 || a0.a0 == 84) {
+ print("throwing!");
+ throw Exception("ReturnStruct8BytesNestedFloat throwing on purpuse!");
+ }
+
+ returnStruct8BytesNestedFloat_a0 = a0;
+ returnStruct8BytesNestedFloat_a1 = a1;
+
+ final result = returnStruct8BytesNestedFloatCalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void returnStruct8BytesNestedFloatAfterCallback() {
+ free(returnStruct8BytesNestedFloatResult.addressOf);
+
+ final result = returnStruct8BytesNestedFloatCalculateResult();
+
+ print("after callback result = $result");
+
+ free(returnStruct8BytesNestedFloatResult.addressOf);
+}
+
+typedef ReturnStruct8BytesNestedFloat2Type = Struct8BytesNestedFloat2 Function(
+ Struct4BytesFloat, Float);
+
+// Global variables to be able to test inputs after callback returned.
+Struct4BytesFloat returnStruct8BytesNestedFloat2_a0 = Struct4BytesFloat();
+double returnStruct8BytesNestedFloat2_a1 = 0.0;
+
+// Result variable also global, so we can delete it after the callback.
+Struct8BytesNestedFloat2 returnStruct8BytesNestedFloat2Result =
+ Struct8BytesNestedFloat2();
+
+Struct8BytesNestedFloat2 returnStruct8BytesNestedFloat2CalculateResult() {
+ Struct8BytesNestedFloat2 result = allocate<Struct8BytesNestedFloat2>().ref;
+
+ result.a0.a0 = returnStruct8BytesNestedFloat2_a0.a0;
+ result.a1 = returnStruct8BytesNestedFloat2_a1;
+
+ returnStruct8BytesNestedFloat2Result = result;
+
+ return result;
+}
+
+/// The nesting is irregular, testing homogenous float rules on arm and arm64,
+/// and the fpu register usage on x64.
+Struct8BytesNestedFloat2 returnStruct8BytesNestedFloat2(
+ Struct4BytesFloat a0, double a1) {
+ print("returnStruct8BytesNestedFloat2(${a0}, ${a1})");
+
+ // In legacy mode, possibly return null.
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0 == 42 || a0.a0 == 84) {
+ print("throwing!");
+ throw Exception("ReturnStruct8BytesNestedFloat2 throwing on purpuse!");
+ }
+
+ returnStruct8BytesNestedFloat2_a0 = a0;
+ returnStruct8BytesNestedFloat2_a1 = a1;
+
+ final result = returnStruct8BytesNestedFloat2CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void returnStruct8BytesNestedFloat2AfterCallback() {
+ free(returnStruct8BytesNestedFloat2Result.addressOf);
+
+ final result = returnStruct8BytesNestedFloat2CalculateResult();
+
+ print("after callback result = $result");
+
+ free(returnStruct8BytesNestedFloat2Result.addressOf);
+}
+
+typedef ReturnStruct8BytesNestedMixedType = Struct8BytesNestedMixed Function(
+ Struct4BytesHomogeneousInt16, Struct4BytesFloat);
+
+// Global variables to be able to test inputs after callback returned.
+Struct4BytesHomogeneousInt16 returnStruct8BytesNestedMixed_a0 =
+ Struct4BytesHomogeneousInt16();
+Struct4BytesFloat returnStruct8BytesNestedMixed_a1 = Struct4BytesFloat();
+
+// Result variable also global, so we can delete it after the callback.
+Struct8BytesNestedMixed returnStruct8BytesNestedMixedResult =
+ Struct8BytesNestedMixed();
+
+Struct8BytesNestedMixed returnStruct8BytesNestedMixedCalculateResult() {
+ Struct8BytesNestedMixed result = allocate<Struct8BytesNestedMixed>().ref;
+
+ result.a0.a0 = returnStruct8BytesNestedMixed_a0.a0;
+ result.a0.a1 = returnStruct8BytesNestedMixed_a0.a1;
+ result.a1.a0 = returnStruct8BytesNestedMixed_a1.a0;
+
+ returnStruct8BytesNestedMixedResult = result;
+
+ return result;
+}
+
+/// Simple nested struct with mixed members.
+Struct8BytesNestedMixed returnStruct8BytesNestedMixed(
+ Struct4BytesHomogeneousInt16 a0, Struct4BytesFloat a1) {
+ print("returnStruct8BytesNestedMixed(${a0}, ${a1})");
+
+ // In legacy mode, possibly return null.
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0 == 42 || a0.a0 == 84) {
+ print("throwing!");
+ throw Exception("ReturnStruct8BytesNestedMixed throwing on purpuse!");
+ }
+
+ returnStruct8BytesNestedMixed_a0 = a0;
+ returnStruct8BytesNestedMixed_a1 = a1;
+
+ final result = returnStruct8BytesNestedMixedCalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void returnStruct8BytesNestedMixedAfterCallback() {
+ free(returnStruct8BytesNestedMixedResult.addressOf);
+
+ final result = returnStruct8BytesNestedMixedCalculateResult();
+
+ print("after callback result = $result");
+
+ free(returnStruct8BytesNestedMixedResult.addressOf);
+}
+
+typedef ReturnStruct16BytesNestedIntType = Struct16BytesNestedInt Function(
+ Struct8BytesNestedInt, Struct8BytesNestedInt);
+
+// Global variables to be able to test inputs after callback returned.
+Struct8BytesNestedInt returnStruct16BytesNestedInt_a0 = Struct8BytesNestedInt();
+Struct8BytesNestedInt returnStruct16BytesNestedInt_a1 = Struct8BytesNestedInt();
+
+// Result variable also global, so we can delete it after the callback.
+Struct16BytesNestedInt returnStruct16BytesNestedIntResult =
+ Struct16BytesNestedInt();
+
+Struct16BytesNestedInt returnStruct16BytesNestedIntCalculateResult() {
+ Struct16BytesNestedInt result = allocate<Struct16BytesNestedInt>().ref;
+
+ result.a0.a0.a0 = returnStruct16BytesNestedInt_a0.a0.a0;
+ result.a0.a0.a1 = returnStruct16BytesNestedInt_a0.a0.a1;
+ result.a0.a1.a0 = returnStruct16BytesNestedInt_a0.a1.a0;
+ result.a0.a1.a1 = returnStruct16BytesNestedInt_a0.a1.a1;
+ result.a1.a0.a0 = returnStruct16BytesNestedInt_a1.a0.a0;
+ result.a1.a0.a1 = returnStruct16BytesNestedInt_a1.a0.a1;
+ result.a1.a1.a0 = returnStruct16BytesNestedInt_a1.a1.a0;
+ result.a1.a1.a1 = returnStruct16BytesNestedInt_a1.a1.a1;
+
+ returnStruct16BytesNestedIntResult = result;
+
+ return result;
+}
+
+/// Deeper nested struct to test recursive member access.
+Struct16BytesNestedInt returnStruct16BytesNestedInt(
+ Struct8BytesNestedInt a0, Struct8BytesNestedInt a1) {
+ print("returnStruct16BytesNestedInt(${a0}, ${a1})");
+
+ // In legacy mode, possibly return null.
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+ print("throwing!");
+ throw Exception("ReturnStruct16BytesNestedInt throwing on purpuse!");
+ }
+
+ returnStruct16BytesNestedInt_a0 = a0;
+ returnStruct16BytesNestedInt_a1 = a1;
+
+ final result = returnStruct16BytesNestedIntCalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void returnStruct16BytesNestedIntAfterCallback() {
+ free(returnStruct16BytesNestedIntResult.addressOf);
+
+ final result = returnStruct16BytesNestedIntCalculateResult();
+
+ print("after callback result = $result");
+
+ free(returnStruct16BytesNestedIntResult.addressOf);
+}
+
+typedef ReturnStruct32BytesNestedIntType = Struct32BytesNestedInt Function(
+ Struct16BytesNestedInt, Struct16BytesNestedInt);
+
+// Global variables to be able to test inputs after callback returned.
+Struct16BytesNestedInt returnStruct32BytesNestedInt_a0 =
+ Struct16BytesNestedInt();
+Struct16BytesNestedInt returnStruct32BytesNestedInt_a1 =
+ Struct16BytesNestedInt();
+
+// Result variable also global, so we can delete it after the callback.
+Struct32BytesNestedInt returnStruct32BytesNestedIntResult =
+ Struct32BytesNestedInt();
+
+Struct32BytesNestedInt returnStruct32BytesNestedIntCalculateResult() {
+ Struct32BytesNestedInt result = allocate<Struct32BytesNestedInt>().ref;
+
+ result.a0.a0.a0.a0 = returnStruct32BytesNestedInt_a0.a0.a0.a0;
+ result.a0.a0.a0.a1 = returnStruct32BytesNestedInt_a0.a0.a0.a1;
+ result.a0.a0.a1.a0 = returnStruct32BytesNestedInt_a0.a0.a1.a0;
+ result.a0.a0.a1.a1 = returnStruct32BytesNestedInt_a0.a0.a1.a1;
+ result.a0.a1.a0.a0 = returnStruct32BytesNestedInt_a0.a1.a0.a0;
+ result.a0.a1.a0.a1 = returnStruct32BytesNestedInt_a0.a1.a0.a1;
+ result.a0.a1.a1.a0 = returnStruct32BytesNestedInt_a0.a1.a1.a0;
+ result.a0.a1.a1.a1 = returnStruct32BytesNestedInt_a0.a1.a1.a1;
+ result.a1.a0.a0.a0 = returnStruct32BytesNestedInt_a1.a0.a0.a0;
+ result.a1.a0.a0.a1 = returnStruct32BytesNestedInt_a1.a0.a0.a1;
+ result.a1.a0.a1.a0 = returnStruct32BytesNestedInt_a1.a0.a1.a0;
+ result.a1.a0.a1.a1 = returnStruct32BytesNestedInt_a1.a0.a1.a1;
+ result.a1.a1.a0.a0 = returnStruct32BytesNestedInt_a1.a1.a0.a0;
+ result.a1.a1.a0.a1 = returnStruct32BytesNestedInt_a1.a1.a0.a1;
+ result.a1.a1.a1.a0 = returnStruct32BytesNestedInt_a1.a1.a1.a0;
+ result.a1.a1.a1.a1 = returnStruct32BytesNestedInt_a1.a1.a1.a1;
+
+ returnStruct32BytesNestedIntResult = result;
+
+ return result;
+}
+
+/// Even deeper nested struct to test recursive member access.
+Struct32BytesNestedInt returnStruct32BytesNestedInt(
+ Struct16BytesNestedInt a0, Struct16BytesNestedInt a1) {
+ print("returnStruct32BytesNestedInt(${a0}, ${a1})");
+
+ // In legacy mode, possibly return null.
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0.a0.a0 == 42 || a0.a0.a0.a0 == 84) {
+ print("throwing!");
+ throw Exception("ReturnStruct32BytesNestedInt throwing on purpuse!");
+ }
+
+ returnStruct32BytesNestedInt_a0 = a0;
+ returnStruct32BytesNestedInt_a1 = a1;
+
+ final result = returnStruct32BytesNestedIntCalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void returnStruct32BytesNestedIntAfterCallback() {
+ free(returnStruct32BytesNestedIntResult.addressOf);
+
+ final result = returnStruct32BytesNestedIntCalculateResult();
+
+ print("after callback result = $result");
+
+ free(returnStruct32BytesNestedIntResult.addressOf);
+}
+
+typedef ReturnStructNestedIntStructAlignmentInt16Type
+ = StructNestedIntStructAlignmentInt16 Function(
+ StructAlignmentInt16, StructAlignmentInt16);
+
+// Global variables to be able to test inputs after callback returned.
+StructAlignmentInt16 returnStructNestedIntStructAlignmentInt16_a0 =
+ StructAlignmentInt16();
+StructAlignmentInt16 returnStructNestedIntStructAlignmentInt16_a1 =
+ StructAlignmentInt16();
+
+// Result variable also global, so we can delete it after the callback.
+StructNestedIntStructAlignmentInt16
+ returnStructNestedIntStructAlignmentInt16Result =
+ StructNestedIntStructAlignmentInt16();
+
+StructNestedIntStructAlignmentInt16
+ returnStructNestedIntStructAlignmentInt16CalculateResult() {
+ StructNestedIntStructAlignmentInt16 result =
+ allocate<StructNestedIntStructAlignmentInt16>().ref;
+
+ result.a0.a0 = returnStructNestedIntStructAlignmentInt16_a0.a0;
+ result.a0.a1 = returnStructNestedIntStructAlignmentInt16_a0.a1;
+ result.a0.a2 = returnStructNestedIntStructAlignmentInt16_a0.a2;
+ result.a1.a0 = returnStructNestedIntStructAlignmentInt16_a1.a0;
+ result.a1.a1 = returnStructNestedIntStructAlignmentInt16_a1.a1;
+ result.a1.a2 = returnStructNestedIntStructAlignmentInt16_a1.a2;
+
+ returnStructNestedIntStructAlignmentInt16Result = result;
+
+ return result;
+}
+
+/// Test alignment and padding of nested struct with 16 byte int.
+StructNestedIntStructAlignmentInt16 returnStructNestedIntStructAlignmentInt16(
+ StructAlignmentInt16 a0, StructAlignmentInt16 a1) {
+ print("returnStructNestedIntStructAlignmentInt16(${a0}, ${a1})");
+
+ // In legacy mode, possibly return null.
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0 == 42 || a0.a0 == 84) {
+ print("throwing!");
+ throw Exception(
+ "ReturnStructNestedIntStructAlignmentInt16 throwing on purpuse!");
+ }
+
+ returnStructNestedIntStructAlignmentInt16_a0 = a0;
+ returnStructNestedIntStructAlignmentInt16_a1 = a1;
+
+ final result = returnStructNestedIntStructAlignmentInt16CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void returnStructNestedIntStructAlignmentInt16AfterCallback() {
+ free(returnStructNestedIntStructAlignmentInt16Result.addressOf);
+
+ final result = returnStructNestedIntStructAlignmentInt16CalculateResult();
+
+ print("after callback result = $result");
+
+ free(returnStructNestedIntStructAlignmentInt16Result.addressOf);
+}
+
+typedef ReturnStructNestedIntStructAlignmentInt32Type
+ = StructNestedIntStructAlignmentInt32 Function(
+ StructAlignmentInt32, StructAlignmentInt32);
+
+// Global variables to be able to test inputs after callback returned.
+StructAlignmentInt32 returnStructNestedIntStructAlignmentInt32_a0 =
+ StructAlignmentInt32();
+StructAlignmentInt32 returnStructNestedIntStructAlignmentInt32_a1 =
+ StructAlignmentInt32();
+
+// Result variable also global, so we can delete it after the callback.
+StructNestedIntStructAlignmentInt32
+ returnStructNestedIntStructAlignmentInt32Result =
+ StructNestedIntStructAlignmentInt32();
+
+StructNestedIntStructAlignmentInt32
+ returnStructNestedIntStructAlignmentInt32CalculateResult() {
+ StructNestedIntStructAlignmentInt32 result =
+ allocate<StructNestedIntStructAlignmentInt32>().ref;
+
+ result.a0.a0 = returnStructNestedIntStructAlignmentInt32_a0.a0;
+ result.a0.a1 = returnStructNestedIntStructAlignmentInt32_a0.a1;
+ result.a0.a2 = returnStructNestedIntStructAlignmentInt32_a0.a2;
+ result.a1.a0 = returnStructNestedIntStructAlignmentInt32_a1.a0;
+ result.a1.a1 = returnStructNestedIntStructAlignmentInt32_a1.a1;
+ result.a1.a2 = returnStructNestedIntStructAlignmentInt32_a1.a2;
+
+ returnStructNestedIntStructAlignmentInt32Result = result;
+
+ return result;
+}
+
+/// Test alignment and padding of nested struct with 32 byte int.
+StructNestedIntStructAlignmentInt32 returnStructNestedIntStructAlignmentInt32(
+ StructAlignmentInt32 a0, StructAlignmentInt32 a1) {
+ print("returnStructNestedIntStructAlignmentInt32(${a0}, ${a1})");
+
+ // In legacy mode, possibly return null.
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0 == 42 || a0.a0 == 84) {
+ print("throwing!");
+ throw Exception(
+ "ReturnStructNestedIntStructAlignmentInt32 throwing on purpuse!");
+ }
+
+ returnStructNestedIntStructAlignmentInt32_a0 = a0;
+ returnStructNestedIntStructAlignmentInt32_a1 = a1;
+
+ final result = returnStructNestedIntStructAlignmentInt32CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void returnStructNestedIntStructAlignmentInt32AfterCallback() {
+ free(returnStructNestedIntStructAlignmentInt32Result.addressOf);
+
+ final result = returnStructNestedIntStructAlignmentInt32CalculateResult();
+
+ print("after callback result = $result");
+
+ free(returnStructNestedIntStructAlignmentInt32Result.addressOf);
+}
+
+typedef ReturnStructNestedIntStructAlignmentInt64Type
+ = StructNestedIntStructAlignmentInt64 Function(
+ StructAlignmentInt64, StructAlignmentInt64);
+
+// Global variables to be able to test inputs after callback returned.
+StructAlignmentInt64 returnStructNestedIntStructAlignmentInt64_a0 =
+ StructAlignmentInt64();
+StructAlignmentInt64 returnStructNestedIntStructAlignmentInt64_a1 =
+ StructAlignmentInt64();
+
+// Result variable also global, so we can delete it after the callback.
+StructNestedIntStructAlignmentInt64
+ returnStructNestedIntStructAlignmentInt64Result =
+ StructNestedIntStructAlignmentInt64();
+
+StructNestedIntStructAlignmentInt64
+ returnStructNestedIntStructAlignmentInt64CalculateResult() {
+ StructNestedIntStructAlignmentInt64 result =
+ allocate<StructNestedIntStructAlignmentInt64>().ref;
+
+ result.a0.a0 = returnStructNestedIntStructAlignmentInt64_a0.a0;
+ result.a0.a1 = returnStructNestedIntStructAlignmentInt64_a0.a1;
+ result.a0.a2 = returnStructNestedIntStructAlignmentInt64_a0.a2;
+ result.a1.a0 = returnStructNestedIntStructAlignmentInt64_a1.a0;
+ result.a1.a1 = returnStructNestedIntStructAlignmentInt64_a1.a1;
+ result.a1.a2 = returnStructNestedIntStructAlignmentInt64_a1.a2;
+
+ returnStructNestedIntStructAlignmentInt64Result = result;
+
+ return result;
+}
+
+/// Test alignment and padding of nested struct with 64 byte int.
+StructNestedIntStructAlignmentInt64 returnStructNestedIntStructAlignmentInt64(
+ StructAlignmentInt64 a0, StructAlignmentInt64 a1) {
+ print("returnStructNestedIntStructAlignmentInt64(${a0}, ${a1})");
+
+ // In legacy mode, possibly return null.
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0 == 42 || a0.a0 == 84) {
+ print("throwing!");
+ throw Exception(
+ "ReturnStructNestedIntStructAlignmentInt64 throwing on purpuse!");
+ }
+
+ returnStructNestedIntStructAlignmentInt64_a0 = a0;
+ returnStructNestedIntStructAlignmentInt64_a1 = a1;
+
+ final result = returnStructNestedIntStructAlignmentInt64CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void returnStructNestedIntStructAlignmentInt64AfterCallback() {
+ free(returnStructNestedIntStructAlignmentInt64Result.addressOf);
+
+ final result = returnStructNestedIntStructAlignmentInt64CalculateResult();
+
+ print("after callback result = $result");
+
+ free(returnStructNestedIntStructAlignmentInt64Result.addressOf);
+}
+
+typedef ReturnStructNestedIrregularEvenBiggerType
+ = StructNestedIrregularEvenBigger Function(Uint64,
+ StructNestedIrregularBigger, StructNestedIrregularBigger, Double);
+
+// Global variables to be able to test inputs after callback returned.
+int returnStructNestedIrregularEvenBigger_a0 = 0;
+StructNestedIrregularBigger returnStructNestedIrregularEvenBigger_a1 =
+ StructNestedIrregularBigger();
+StructNestedIrregularBigger returnStructNestedIrregularEvenBigger_a2 =
+ StructNestedIrregularBigger();
+double returnStructNestedIrregularEvenBigger_a3 = 0.0;
+
+// Result variable also global, so we can delete it after the callback.
+StructNestedIrregularEvenBigger returnStructNestedIrregularEvenBiggerResult =
+ StructNestedIrregularEvenBigger();
+
+StructNestedIrregularEvenBigger
+ returnStructNestedIrregularEvenBiggerCalculateResult() {
+ StructNestedIrregularEvenBigger result =
+ allocate<StructNestedIrregularEvenBigger>().ref;
+
+ result.a0 = returnStructNestedIrregularEvenBigger_a0;
+ result.a1.a0.a0 = returnStructNestedIrregularEvenBigger_a1.a0.a0;
+ result.a1.a0.a1.a0.a0 = returnStructNestedIrregularEvenBigger_a1.a0.a1.a0.a0;
+ result.a1.a0.a1.a0.a1 = returnStructNestedIrregularEvenBigger_a1.a0.a1.a0.a1;
+ result.a1.a0.a1.a1.a0 = returnStructNestedIrregularEvenBigger_a1.a0.a1.a1.a0;
+ result.a1.a0.a2 = returnStructNestedIrregularEvenBigger_a1.a0.a2;
+ result.a1.a0.a3.a0.a0 = returnStructNestedIrregularEvenBigger_a1.a0.a3.a0.a0;
+ result.a1.a0.a3.a1 = returnStructNestedIrregularEvenBigger_a1.a0.a3.a1;
+ result.a1.a0.a4 = returnStructNestedIrregularEvenBigger_a1.a0.a4;
+ result.a1.a0.a5.a0.a0 = returnStructNestedIrregularEvenBigger_a1.a0.a5.a0.a0;
+ result.a1.a0.a5.a1.a0 = returnStructNestedIrregularEvenBigger_a1.a0.a5.a1.a0;
+ result.a1.a0.a6 = returnStructNestedIrregularEvenBigger_a1.a0.a6;
+ result.a1.a1.a0.a0 = returnStructNestedIrregularEvenBigger_a1.a1.a0.a0;
+ result.a1.a1.a0.a1 = returnStructNestedIrregularEvenBigger_a1.a1.a0.a1;
+ result.a1.a1.a1.a0 = returnStructNestedIrregularEvenBigger_a1.a1.a1.a0;
+ result.a1.a2 = returnStructNestedIrregularEvenBigger_a1.a2;
+ result.a1.a3 = returnStructNestedIrregularEvenBigger_a1.a3;
+ result.a2.a0.a0 = returnStructNestedIrregularEvenBigger_a2.a0.a0;
+ result.a2.a0.a1.a0.a0 = returnStructNestedIrregularEvenBigger_a2.a0.a1.a0.a0;
+ result.a2.a0.a1.a0.a1 = returnStructNestedIrregularEvenBigger_a2.a0.a1.a0.a1;
+ result.a2.a0.a1.a1.a0 = returnStructNestedIrregularEvenBigger_a2.a0.a1.a1.a0;
+ result.a2.a0.a2 = returnStructNestedIrregularEvenBigger_a2.a0.a2;
+ result.a2.a0.a3.a0.a0 = returnStructNestedIrregularEvenBigger_a2.a0.a3.a0.a0;
+ result.a2.a0.a3.a1 = returnStructNestedIrregularEvenBigger_a2.a0.a3.a1;
+ result.a2.a0.a4 = returnStructNestedIrregularEvenBigger_a2.a0.a4;
+ result.a2.a0.a5.a0.a0 = returnStructNestedIrregularEvenBigger_a2.a0.a5.a0.a0;
+ result.a2.a0.a5.a1.a0 = returnStructNestedIrregularEvenBigger_a2.a0.a5.a1.a0;
+ result.a2.a0.a6 = returnStructNestedIrregularEvenBigger_a2.a0.a6;
+ result.a2.a1.a0.a0 = returnStructNestedIrregularEvenBigger_a2.a1.a0.a0;
+ result.a2.a1.a0.a1 = returnStructNestedIrregularEvenBigger_a2.a1.a0.a1;
+ result.a2.a1.a1.a0 = returnStructNestedIrregularEvenBigger_a2.a1.a1.a0;
+ result.a2.a2 = returnStructNestedIrregularEvenBigger_a2.a2;
+ result.a2.a3 = returnStructNestedIrregularEvenBigger_a2.a3;
+ result.a3 = returnStructNestedIrregularEvenBigger_a3;
+
+ returnStructNestedIrregularEvenBiggerResult = result;
+
+ return result;
+}
+
+/// Return big irregular struct as smoke test.
+StructNestedIrregularEvenBigger returnStructNestedIrregularEvenBigger(int a0,
+ StructNestedIrregularBigger a1, StructNestedIrregularBigger a2, double a3) {
+ print("returnStructNestedIrregularEvenBigger(${a0}, ${a1}, ${a2}, ${a3})");
+
+ // In legacy mode, possibly return null.
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0 == 42 || a0 == 84) {
+ print("throwing!");
+ throw Exception(
+ "ReturnStructNestedIrregularEvenBigger throwing on purpuse!");
+ }
+
+ returnStructNestedIrregularEvenBigger_a0 = a0;
+ returnStructNestedIrregularEvenBigger_a1 = a1;
+ returnStructNestedIrregularEvenBigger_a2 = a2;
+ returnStructNestedIrregularEvenBigger_a3 = a3;
+
+ final result = returnStructNestedIrregularEvenBiggerCalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void returnStructNestedIrregularEvenBiggerAfterCallback() {
+ free(returnStructNestedIrregularEvenBiggerResult.addressOf);
+
+ final result = returnStructNestedIrregularEvenBiggerCalculateResult();
+
+ print("after callback result = $result");
+
+ free(returnStructNestedIrregularEvenBiggerResult.addressOf);
+}
diff --git a/tests/ffi/function_callbacks_structs_by_value_test.dart b/tests/ffi/function_callbacks_structs_by_value_test.dart
index 98ab518..23c9946 100644
--- a/tests/ffi/function_callbacks_structs_by_value_test.dart
+++ b/tests/ffi/function_callbacks_structs_by_value_test.dart
@@ -18,6 +18,7 @@
for (int i = 0; i < 10; i++) {
recursiveTest(10);
recursiveTest(11);
+ testCopyLogic();
}
}
@@ -69,3 +70,65 @@
Struct20BytesHomogeneousInt32 struct, Pointer callbackAddress),
Struct20BytesHomogeneousInt32 Function(int recursionCounter,
Struct20BytesHomogeneousInt32, Pointer)>("PassStructRecursive");
+
+Struct8BytesNestedInt typedDataBackedStruct = Struct8BytesNestedInt();
+bool typedDataBackedStructSet = false;
+void _receiveStructByValue(Struct8BytesNestedInt struct) {
+ typedDataBackedStruct = struct;
+ typedDataBackedStructSet = true;
+}
+
+final _receiveStructByValuePointer =
+ Pointer.fromFunction<Void Function(Struct8BytesNestedInt)>(
+ _receiveStructByValue);
+
+final _invokeReceiveStructByValue = ffiTestFunctions.lookupFunction<
+ Void Function(
+ Pointer<NativeFunction<Void Function(Struct8BytesNestedInt)>>),
+ void Function(
+ Pointer<NativeFunction<Void Function(Struct8BytesNestedInt)>>)>(
+ "CallbackWithStruct");
+
+void testCopyLogic() {
+ _invokeReceiveStructByValue(_receiveStructByValuePointer);
+ Expect.isTrue(typedDataBackedStructSet);
+
+ final pointerBackedStruct = allocate<Struct8BytesNestedInt>().ref;
+
+ void reset() {
+ pointerBackedStruct.a0.a0 = 1;
+ pointerBackedStruct.a0.a1 = 2;
+ pointerBackedStruct.a1.a0 = 3;
+ pointerBackedStruct.a1.a1 = 4;
+ typedDataBackedStruct.a0.a0 = 5;
+ typedDataBackedStruct.a0.a1 = 6;
+ typedDataBackedStruct.a1.a0 = 7;
+ typedDataBackedStruct.a1.a1 = 8;
+ }
+
+ // Pointer -> Pointer.
+ reset();
+ pointerBackedStruct.a1 = pointerBackedStruct.a0;
+ Expect.equals(1, pointerBackedStruct.a1.a0);
+ Expect.equals(2, pointerBackedStruct.a1.a1);
+
+ // Pointer -> TypedData.
+ reset();
+ typedDataBackedStruct.a1 = pointerBackedStruct.a0;
+ Expect.equals(1, typedDataBackedStruct.a1.a0);
+ Expect.equals(2, typedDataBackedStruct.a1.a1);
+
+ // TypedData -> Pointer.
+ reset();
+ pointerBackedStruct.a1 = typedDataBackedStruct.a0;
+ Expect.equals(5, pointerBackedStruct.a1.a0);
+ Expect.equals(6, pointerBackedStruct.a1.a1);
+
+ // TypedData -> TypedData.
+ reset();
+ typedDataBackedStruct.a1 = typedDataBackedStruct.a0;
+ Expect.equals(5, typedDataBackedStruct.a1.a0);
+ Expect.equals(6, typedDataBackedStruct.a1.a1);
+
+ free(pointerBackedStruct.addressOf);
+}
diff --git a/tests/ffi/function_structs_by_value_generated_test.dart b/tests/ffi/function_structs_by_value_generated_test.dart
index 35936e9..8e800fc 100644
--- a/tests/ffi/function_structs_by_value_generated_test.dart
+++ b/tests/ffi/function_structs_by_value_generated_test.dart
@@ -52,6 +52,16 @@
testPassStructAlignmentInt16();
testPassStructAlignmentInt32();
testPassStructAlignmentInt64();
+ testPassStruct8BytesNestedIntx10();
+ testPassStruct8BytesNestedFloatx10();
+ testPassStruct8BytesNestedFloat2x10();
+ testPassStruct8BytesNestedMixedx10();
+ testPassStruct16BytesNestedIntx2();
+ testPassStruct32BytesNestedIntx2();
+ testPassStructNestedIntStructAlignmentInt16();
+ testPassStructNestedIntStructAlignmentInt32();
+ testPassStructNestedIntStructAlignmentInt64();
+ testPassStructNestedIrregularEvenBiggerx4();
testReturnStruct1ByteInt();
testReturnStruct3BytesHomogeneousUint8();
testReturnStruct3BytesInt2ByteAligned();
@@ -82,6 +92,16 @@
testReturnStructAlignmentInt16();
testReturnStructAlignmentInt32();
testReturnStructAlignmentInt64();
+ testReturnStruct8BytesNestedInt();
+ testReturnStruct8BytesNestedFloat();
+ testReturnStruct8BytesNestedFloat2();
+ testReturnStruct8BytesNestedMixed();
+ testReturnStruct16BytesNestedInt();
+ testReturnStruct32BytesNestedInt();
+ testReturnStructNestedIntStructAlignmentInt16();
+ testReturnStructNestedIntStructAlignmentInt32();
+ testReturnStructNestedIntStructAlignmentInt64();
+ testReturnStructNestedIrregularEvenBigger();
}
}
@@ -129,6 +149,13 @@
String toString() => "(${a0}, ${a1})";
}
+class Struct4BytesFloat extends Struct {
+ @Float()
+ external double a0;
+
+ String toString() => "(${a0})";
+}
+
class Struct7BytesHomogeneousUint8 extends Struct {
@Uint8()
external int a0;
@@ -876,6 +903,129 @@
String toString() => "(${a0}, ${a1}, ${a2})";
}
+class Struct8BytesNestedInt extends Struct {
+ external Struct4BytesHomogeneousInt16 a0;
+
+ external Struct4BytesHomogeneousInt16 a1;
+
+ String toString() => "(${a0}, ${a1})";
+}
+
+class Struct8BytesNestedFloat extends Struct {
+ external Struct4BytesFloat a0;
+
+ external Struct4BytesFloat a1;
+
+ String toString() => "(${a0}, ${a1})";
+}
+
+class Struct8BytesNestedFloat2 extends Struct {
+ external Struct4BytesFloat a0;
+
+ @Float()
+ external double a1;
+
+ String toString() => "(${a0}, ${a1})";
+}
+
+class Struct8BytesNestedMixed extends Struct {
+ external Struct4BytesHomogeneousInt16 a0;
+
+ external Struct4BytesFloat a1;
+
+ String toString() => "(${a0}, ${a1})";
+}
+
+class Struct16BytesNestedInt extends Struct {
+ external Struct8BytesNestedInt a0;
+
+ external Struct8BytesNestedInt a1;
+
+ String toString() => "(${a0}, ${a1})";
+}
+
+class Struct32BytesNestedInt extends Struct {
+ external Struct16BytesNestedInt a0;
+
+ external Struct16BytesNestedInt a1;
+
+ String toString() => "(${a0}, ${a1})";
+}
+
+class StructNestedIntStructAlignmentInt16 extends Struct {
+ external StructAlignmentInt16 a0;
+
+ external StructAlignmentInt16 a1;
+
+ String toString() => "(${a0}, ${a1})";
+}
+
+class StructNestedIntStructAlignmentInt32 extends Struct {
+ external StructAlignmentInt32 a0;
+
+ external StructAlignmentInt32 a1;
+
+ String toString() => "(${a0}, ${a1})";
+}
+
+class StructNestedIntStructAlignmentInt64 extends Struct {
+ external StructAlignmentInt64 a0;
+
+ external StructAlignmentInt64 a1;
+
+ String toString() => "(${a0}, ${a1})";
+}
+
+class StructNestedIrregularBig extends Struct {
+ @Uint16()
+ external int a0;
+
+ external Struct8BytesNestedMixed a1;
+
+ @Uint16()
+ external int a2;
+
+ external Struct8BytesNestedFloat2 a3;
+
+ @Uint16()
+ external int a4;
+
+ external Struct8BytesNestedFloat a5;
+
+ @Uint16()
+ external int a6;
+
+ String toString() => "(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6})";
+}
+
+class StructNestedIrregularBigger extends Struct {
+ external StructNestedIrregularBig a0;
+
+ external Struct8BytesNestedMixed a1;
+
+ @Float()
+ external double a2;
+
+ @Double()
+ external double a3;
+
+ String toString() => "(${a0}, ${a1}, ${a2}, ${a3})";
+}
+
+class StructNestedIrregularEvenBigger extends Struct {
+ @Uint64()
+ external int a0;
+
+ external StructNestedIrregularBigger a1;
+
+ external StructNestedIrregularBigger a2;
+
+ @Double()
+ external double a3;
+
+ String toString() => "(${a0}, ${a1}, ${a2}, ${a3})";
+}
+
final passStruct1ByteIntx10 = ffiTestFunctions.lookupFunction<
Int64 Function(
Struct1ByteInt,
@@ -3660,6 +3810,691 @@
free(a0.addressOf);
}
+final passStruct8BytesNestedIntx10 = ffiTestFunctions.lookupFunction<
+ Int64 Function(
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt),
+ int Function(
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt)>("PassStruct8BytesNestedIntx10");
+
+/// Simple nested struct. No alignment gaps on any architectures.
+/// 10 arguments exhaust registers on all platforms.
+void testPassStruct8BytesNestedIntx10() {
+ Struct8BytesNestedInt a0 = allocate<Struct8BytesNestedInt>().ref;
+ Struct8BytesNestedInt a1 = allocate<Struct8BytesNestedInt>().ref;
+ Struct8BytesNestedInt a2 = allocate<Struct8BytesNestedInt>().ref;
+ Struct8BytesNestedInt a3 = allocate<Struct8BytesNestedInt>().ref;
+ Struct8BytesNestedInt a4 = allocate<Struct8BytesNestedInt>().ref;
+ Struct8BytesNestedInt a5 = allocate<Struct8BytesNestedInt>().ref;
+ Struct8BytesNestedInt a6 = allocate<Struct8BytesNestedInt>().ref;
+ Struct8BytesNestedInt a7 = allocate<Struct8BytesNestedInt>().ref;
+ Struct8BytesNestedInt a8 = allocate<Struct8BytesNestedInt>().ref;
+ Struct8BytesNestedInt a9 = allocate<Struct8BytesNestedInt>().ref;
+
+ a0.a0.a0 = -1;
+ a0.a0.a1 = 2;
+ a0.a1.a0 = -3;
+ a0.a1.a1 = 4;
+ a1.a0.a0 = -5;
+ a1.a0.a1 = 6;
+ a1.a1.a0 = -7;
+ a1.a1.a1 = 8;
+ a2.a0.a0 = -9;
+ a2.a0.a1 = 10;
+ a2.a1.a0 = -11;
+ a2.a1.a1 = 12;
+ a3.a0.a0 = -13;
+ a3.a0.a1 = 14;
+ a3.a1.a0 = -15;
+ a3.a1.a1 = 16;
+ a4.a0.a0 = -17;
+ a4.a0.a1 = 18;
+ a4.a1.a0 = -19;
+ a4.a1.a1 = 20;
+ a5.a0.a0 = -21;
+ a5.a0.a1 = 22;
+ a5.a1.a0 = -23;
+ a5.a1.a1 = 24;
+ a6.a0.a0 = -25;
+ a6.a0.a1 = 26;
+ a6.a1.a0 = -27;
+ a6.a1.a1 = 28;
+ a7.a0.a0 = -29;
+ a7.a0.a1 = 30;
+ a7.a1.a0 = -31;
+ a7.a1.a1 = 32;
+ a8.a0.a0 = -33;
+ a8.a0.a1 = 34;
+ a8.a1.a0 = -35;
+ a8.a1.a1 = 36;
+ a9.a0.a0 = -37;
+ a9.a0.a1 = 38;
+ a9.a1.a0 = -39;
+ a9.a1.a1 = 40;
+
+ final result =
+ passStruct8BytesNestedIntx10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+ print("result = $result");
+
+ Expect.equals(20, result);
+
+ free(a0.addressOf);
+ free(a1.addressOf);
+ free(a2.addressOf);
+ free(a3.addressOf);
+ free(a4.addressOf);
+ free(a5.addressOf);
+ free(a6.addressOf);
+ free(a7.addressOf);
+ free(a8.addressOf);
+ free(a9.addressOf);
+}
+
+final passStruct8BytesNestedFloatx10 = ffiTestFunctions.lookupFunction<
+ Float Function(
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat),
+ double Function(
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat)>("PassStruct8BytesNestedFloatx10");
+
+/// Simple nested struct. No alignment gaps on any architectures.
+/// 10 arguments exhaust fpu registers on all platforms.
+void testPassStruct8BytesNestedFloatx10() {
+ Struct8BytesNestedFloat a0 = allocate<Struct8BytesNestedFloat>().ref;
+ Struct8BytesNestedFloat a1 = allocate<Struct8BytesNestedFloat>().ref;
+ Struct8BytesNestedFloat a2 = allocate<Struct8BytesNestedFloat>().ref;
+ Struct8BytesNestedFloat a3 = allocate<Struct8BytesNestedFloat>().ref;
+ Struct8BytesNestedFloat a4 = allocate<Struct8BytesNestedFloat>().ref;
+ Struct8BytesNestedFloat a5 = allocate<Struct8BytesNestedFloat>().ref;
+ Struct8BytesNestedFloat a6 = allocate<Struct8BytesNestedFloat>().ref;
+ Struct8BytesNestedFloat a7 = allocate<Struct8BytesNestedFloat>().ref;
+ Struct8BytesNestedFloat a8 = allocate<Struct8BytesNestedFloat>().ref;
+ Struct8BytesNestedFloat a9 = allocate<Struct8BytesNestedFloat>().ref;
+
+ a0.a0.a0 = -1.0;
+ a0.a1.a0 = 2.0;
+ a1.a0.a0 = -3.0;
+ a1.a1.a0 = 4.0;
+ a2.a0.a0 = -5.0;
+ a2.a1.a0 = 6.0;
+ a3.a0.a0 = -7.0;
+ a3.a1.a0 = 8.0;
+ a4.a0.a0 = -9.0;
+ a4.a1.a0 = 10.0;
+ a5.a0.a0 = -11.0;
+ a5.a1.a0 = 12.0;
+ a6.a0.a0 = -13.0;
+ a6.a1.a0 = 14.0;
+ a7.a0.a0 = -15.0;
+ a7.a1.a0 = 16.0;
+ a8.a0.a0 = -17.0;
+ a8.a1.a0 = 18.0;
+ a9.a0.a0 = -19.0;
+ a9.a1.a0 = 20.0;
+
+ final result =
+ passStruct8BytesNestedFloatx10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+ print("result = $result");
+
+ Expect.approxEquals(10.0, result);
+
+ free(a0.addressOf);
+ free(a1.addressOf);
+ free(a2.addressOf);
+ free(a3.addressOf);
+ free(a4.addressOf);
+ free(a5.addressOf);
+ free(a6.addressOf);
+ free(a7.addressOf);
+ free(a8.addressOf);
+ free(a9.addressOf);
+}
+
+final passStruct8BytesNestedFloat2x10 = ffiTestFunctions.lookupFunction<
+ Float Function(
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2),
+ double Function(
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2)>("PassStruct8BytesNestedFloat2x10");
+
+/// Simple nested struct. No alignment gaps on any architectures.
+/// 10 arguments exhaust fpu registers on all platforms.
+/// The nesting is irregular, testing homogenous float rules on arm and arm64,
+/// and the fpu register usage on x64.
+void testPassStruct8BytesNestedFloat2x10() {
+ Struct8BytesNestedFloat2 a0 = allocate<Struct8BytesNestedFloat2>().ref;
+ Struct8BytesNestedFloat2 a1 = allocate<Struct8BytesNestedFloat2>().ref;
+ Struct8BytesNestedFloat2 a2 = allocate<Struct8BytesNestedFloat2>().ref;
+ Struct8BytesNestedFloat2 a3 = allocate<Struct8BytesNestedFloat2>().ref;
+ Struct8BytesNestedFloat2 a4 = allocate<Struct8BytesNestedFloat2>().ref;
+ Struct8BytesNestedFloat2 a5 = allocate<Struct8BytesNestedFloat2>().ref;
+ Struct8BytesNestedFloat2 a6 = allocate<Struct8BytesNestedFloat2>().ref;
+ Struct8BytesNestedFloat2 a7 = allocate<Struct8BytesNestedFloat2>().ref;
+ Struct8BytesNestedFloat2 a8 = allocate<Struct8BytesNestedFloat2>().ref;
+ Struct8BytesNestedFloat2 a9 = allocate<Struct8BytesNestedFloat2>().ref;
+
+ a0.a0.a0 = -1.0;
+ a0.a1 = 2.0;
+ a1.a0.a0 = -3.0;
+ a1.a1 = 4.0;
+ a2.a0.a0 = -5.0;
+ a2.a1 = 6.0;
+ a3.a0.a0 = -7.0;
+ a3.a1 = 8.0;
+ a4.a0.a0 = -9.0;
+ a4.a1 = 10.0;
+ a5.a0.a0 = -11.0;
+ a5.a1 = 12.0;
+ a6.a0.a0 = -13.0;
+ a6.a1 = 14.0;
+ a7.a0.a0 = -15.0;
+ a7.a1 = 16.0;
+ a8.a0.a0 = -17.0;
+ a8.a1 = 18.0;
+ a9.a0.a0 = -19.0;
+ a9.a1 = 20.0;
+
+ final result =
+ passStruct8BytesNestedFloat2x10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+ print("result = $result");
+
+ Expect.approxEquals(10.0, result);
+
+ free(a0.addressOf);
+ free(a1.addressOf);
+ free(a2.addressOf);
+ free(a3.addressOf);
+ free(a4.addressOf);
+ free(a5.addressOf);
+ free(a6.addressOf);
+ free(a7.addressOf);
+ free(a8.addressOf);
+ free(a9.addressOf);
+}
+
+final passStruct8BytesNestedMixedx10 = ffiTestFunctions.lookupFunction<
+ Double Function(
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed),
+ double Function(
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed)>("PassStruct8BytesNestedMixedx10");
+
+/// Simple nested struct. No alignment gaps on any architectures.
+/// 10 arguments exhaust all registers on all platforms.
+void testPassStruct8BytesNestedMixedx10() {
+ Struct8BytesNestedMixed a0 = allocate<Struct8BytesNestedMixed>().ref;
+ Struct8BytesNestedMixed a1 = allocate<Struct8BytesNestedMixed>().ref;
+ Struct8BytesNestedMixed a2 = allocate<Struct8BytesNestedMixed>().ref;
+ Struct8BytesNestedMixed a3 = allocate<Struct8BytesNestedMixed>().ref;
+ Struct8BytesNestedMixed a4 = allocate<Struct8BytesNestedMixed>().ref;
+ Struct8BytesNestedMixed a5 = allocate<Struct8BytesNestedMixed>().ref;
+ Struct8BytesNestedMixed a6 = allocate<Struct8BytesNestedMixed>().ref;
+ Struct8BytesNestedMixed a7 = allocate<Struct8BytesNestedMixed>().ref;
+ Struct8BytesNestedMixed a8 = allocate<Struct8BytesNestedMixed>().ref;
+ Struct8BytesNestedMixed a9 = allocate<Struct8BytesNestedMixed>().ref;
+
+ a0.a0.a0 = -1;
+ a0.a0.a1 = 2;
+ a0.a1.a0 = -3.0;
+ a1.a0.a0 = 4;
+ a1.a0.a1 = -5;
+ a1.a1.a0 = 6.0;
+ a2.a0.a0 = -7;
+ a2.a0.a1 = 8;
+ a2.a1.a0 = -9.0;
+ a3.a0.a0 = 10;
+ a3.a0.a1 = -11;
+ a3.a1.a0 = 12.0;
+ a4.a0.a0 = -13;
+ a4.a0.a1 = 14;
+ a4.a1.a0 = -15.0;
+ a5.a0.a0 = 16;
+ a5.a0.a1 = -17;
+ a5.a1.a0 = 18.0;
+ a6.a0.a0 = -19;
+ a6.a0.a1 = 20;
+ a6.a1.a0 = -21.0;
+ a7.a0.a0 = 22;
+ a7.a0.a1 = -23;
+ a7.a1.a0 = 24.0;
+ a8.a0.a0 = -25;
+ a8.a0.a1 = 26;
+ a8.a1.a0 = -27.0;
+ a9.a0.a0 = 28;
+ a9.a0.a1 = -29;
+ a9.a1.a0 = 30.0;
+
+ final result =
+ passStruct8BytesNestedMixedx10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+ print("result = $result");
+
+ Expect.approxEquals(15.0, result);
+
+ free(a0.addressOf);
+ free(a1.addressOf);
+ free(a2.addressOf);
+ free(a3.addressOf);
+ free(a4.addressOf);
+ free(a5.addressOf);
+ free(a6.addressOf);
+ free(a7.addressOf);
+ free(a8.addressOf);
+ free(a9.addressOf);
+}
+
+final passStruct16BytesNestedIntx2 = ffiTestFunctions.lookupFunction<
+ Int64 Function(Struct16BytesNestedInt, Struct16BytesNestedInt),
+ int Function(Struct16BytesNestedInt,
+ Struct16BytesNestedInt)>("PassStruct16BytesNestedIntx2");
+
+/// Deeper nested struct to test recursive member access.
+void testPassStruct16BytesNestedIntx2() {
+ Struct16BytesNestedInt a0 = allocate<Struct16BytesNestedInt>().ref;
+ Struct16BytesNestedInt a1 = allocate<Struct16BytesNestedInt>().ref;
+
+ a0.a0.a0.a0 = -1;
+ a0.a0.a0.a1 = 2;
+ a0.a0.a1.a0 = -3;
+ a0.a0.a1.a1 = 4;
+ a0.a1.a0.a0 = -5;
+ a0.a1.a0.a1 = 6;
+ a0.a1.a1.a0 = -7;
+ a0.a1.a1.a1 = 8;
+ a1.a0.a0.a0 = -9;
+ a1.a0.a0.a1 = 10;
+ a1.a0.a1.a0 = -11;
+ a1.a0.a1.a1 = 12;
+ a1.a1.a0.a0 = -13;
+ a1.a1.a0.a1 = 14;
+ a1.a1.a1.a0 = -15;
+ a1.a1.a1.a1 = 16;
+
+ final result = passStruct16BytesNestedIntx2(a0, a1);
+
+ print("result = $result");
+
+ Expect.equals(8, result);
+
+ free(a0.addressOf);
+ free(a1.addressOf);
+}
+
+final passStruct32BytesNestedIntx2 = ffiTestFunctions.lookupFunction<
+ Int64 Function(Struct32BytesNestedInt, Struct32BytesNestedInt),
+ int Function(Struct32BytesNestedInt,
+ Struct32BytesNestedInt)>("PassStruct32BytesNestedIntx2");
+
+/// Even deeper nested struct to test recursive member access.
+void testPassStruct32BytesNestedIntx2() {
+ Struct32BytesNestedInt a0 = allocate<Struct32BytesNestedInt>().ref;
+ Struct32BytesNestedInt a1 = allocate<Struct32BytesNestedInt>().ref;
+
+ a0.a0.a0.a0.a0 = -1;
+ a0.a0.a0.a0.a1 = 2;
+ a0.a0.a0.a1.a0 = -3;
+ a0.a0.a0.a1.a1 = 4;
+ a0.a0.a1.a0.a0 = -5;
+ a0.a0.a1.a0.a1 = 6;
+ a0.a0.a1.a1.a0 = -7;
+ a0.a0.a1.a1.a1 = 8;
+ a0.a1.a0.a0.a0 = -9;
+ a0.a1.a0.a0.a1 = 10;
+ a0.a1.a0.a1.a0 = -11;
+ a0.a1.a0.a1.a1 = 12;
+ a0.a1.a1.a0.a0 = -13;
+ a0.a1.a1.a0.a1 = 14;
+ a0.a1.a1.a1.a0 = -15;
+ a0.a1.a1.a1.a1 = 16;
+ a1.a0.a0.a0.a0 = -17;
+ a1.a0.a0.a0.a1 = 18;
+ a1.a0.a0.a1.a0 = -19;
+ a1.a0.a0.a1.a1 = 20;
+ a1.a0.a1.a0.a0 = -21;
+ a1.a0.a1.a0.a1 = 22;
+ a1.a0.a1.a1.a0 = -23;
+ a1.a0.a1.a1.a1 = 24;
+ a1.a1.a0.a0.a0 = -25;
+ a1.a1.a0.a0.a1 = 26;
+ a1.a1.a0.a1.a0 = -27;
+ a1.a1.a0.a1.a1 = 28;
+ a1.a1.a1.a0.a0 = -29;
+ a1.a1.a1.a0.a1 = 30;
+ a1.a1.a1.a1.a0 = -31;
+ a1.a1.a1.a1.a1 = 32;
+
+ final result = passStruct32BytesNestedIntx2(a0, a1);
+
+ print("result = $result");
+
+ Expect.equals(16, result);
+
+ free(a0.addressOf);
+ free(a1.addressOf);
+}
+
+final passStructNestedIntStructAlignmentInt16 = ffiTestFunctions.lookupFunction<
+ Int64 Function(StructNestedIntStructAlignmentInt16),
+ int Function(StructNestedIntStructAlignmentInt16)>(
+ "PassStructNestedIntStructAlignmentInt16");
+
+/// Test alignment and padding of nested struct with 16 byte int.
+void testPassStructNestedIntStructAlignmentInt16() {
+ StructNestedIntStructAlignmentInt16 a0 =
+ allocate<StructNestedIntStructAlignmentInt16>().ref;
+
+ a0.a0.a0 = -1;
+ a0.a0.a1 = 2;
+ a0.a0.a2 = -3;
+ a0.a1.a0 = 4;
+ a0.a1.a1 = -5;
+ a0.a1.a2 = 6;
+
+ final result = passStructNestedIntStructAlignmentInt16(a0);
+
+ print("result = $result");
+
+ Expect.equals(3, result);
+
+ free(a0.addressOf);
+}
+
+final passStructNestedIntStructAlignmentInt32 = ffiTestFunctions.lookupFunction<
+ Int64 Function(StructNestedIntStructAlignmentInt32),
+ int Function(StructNestedIntStructAlignmentInt32)>(
+ "PassStructNestedIntStructAlignmentInt32");
+
+/// Test alignment and padding of nested struct with 32 byte int.
+void testPassStructNestedIntStructAlignmentInt32() {
+ StructNestedIntStructAlignmentInt32 a0 =
+ allocate<StructNestedIntStructAlignmentInt32>().ref;
+
+ a0.a0.a0 = -1;
+ a0.a0.a1 = 2;
+ a0.a0.a2 = -3;
+ a0.a1.a0 = 4;
+ a0.a1.a1 = -5;
+ a0.a1.a2 = 6;
+
+ final result = passStructNestedIntStructAlignmentInt32(a0);
+
+ print("result = $result");
+
+ Expect.equals(3, result);
+
+ free(a0.addressOf);
+}
+
+final passStructNestedIntStructAlignmentInt64 = ffiTestFunctions.lookupFunction<
+ Int64 Function(StructNestedIntStructAlignmentInt64),
+ int Function(StructNestedIntStructAlignmentInt64)>(
+ "PassStructNestedIntStructAlignmentInt64");
+
+/// Test alignment and padding of nested struct with 64 byte int.
+void testPassStructNestedIntStructAlignmentInt64() {
+ StructNestedIntStructAlignmentInt64 a0 =
+ allocate<StructNestedIntStructAlignmentInt64>().ref;
+
+ a0.a0.a0 = -1;
+ a0.a0.a1 = 2;
+ a0.a0.a2 = -3;
+ a0.a1.a0 = 4;
+ a0.a1.a1 = -5;
+ a0.a1.a2 = 6;
+
+ final result = passStructNestedIntStructAlignmentInt64(a0);
+
+ print("result = $result");
+
+ Expect.equals(3, result);
+
+ free(a0.addressOf);
+}
+
+final passStructNestedIrregularEvenBiggerx4 = ffiTestFunctions.lookupFunction<
+ Double Function(
+ StructNestedIrregularEvenBigger,
+ StructNestedIrregularEvenBigger,
+ StructNestedIrregularEvenBigger,
+ StructNestedIrregularEvenBigger),
+ double Function(
+ StructNestedIrregularEvenBigger,
+ StructNestedIrregularEvenBigger,
+ StructNestedIrregularEvenBigger,
+ StructNestedIrregularEvenBigger)>(
+ "PassStructNestedIrregularEvenBiggerx4");
+
+/// Return big irregular struct as smoke test.
+void testPassStructNestedIrregularEvenBiggerx4() {
+ StructNestedIrregularEvenBigger a0 =
+ allocate<StructNestedIrregularEvenBigger>().ref;
+ StructNestedIrregularEvenBigger a1 =
+ allocate<StructNestedIrregularEvenBigger>().ref;
+ StructNestedIrregularEvenBigger a2 =
+ allocate<StructNestedIrregularEvenBigger>().ref;
+ StructNestedIrregularEvenBigger a3 =
+ allocate<StructNestedIrregularEvenBigger>().ref;
+
+ a0.a0 = 1;
+ a0.a1.a0.a0 = 2;
+ a0.a1.a0.a1.a0.a0 = -3;
+ a0.a1.a0.a1.a0.a1 = 4;
+ a0.a1.a0.a1.a1.a0 = -5.0;
+ a0.a1.a0.a2 = 6;
+ a0.a1.a0.a3.a0.a0 = -7.0;
+ a0.a1.a0.a3.a1 = 8.0;
+ a0.a1.a0.a4 = 9;
+ a0.a1.a0.a5.a0.a0 = 10.0;
+ a0.a1.a0.a5.a1.a0 = -11.0;
+ a0.a1.a0.a6 = 12;
+ a0.a1.a1.a0.a0 = -13;
+ a0.a1.a1.a0.a1 = 14;
+ a0.a1.a1.a1.a0 = -15.0;
+ a0.a1.a2 = 16.0;
+ a0.a1.a3 = -17.0;
+ a0.a2.a0.a0 = 18;
+ a0.a2.a0.a1.a0.a0 = -19;
+ a0.a2.a0.a1.a0.a1 = 20;
+ a0.a2.a0.a1.a1.a0 = -21.0;
+ a0.a2.a0.a2 = 22;
+ a0.a2.a0.a3.a0.a0 = -23.0;
+ a0.a2.a0.a3.a1 = 24.0;
+ a0.a2.a0.a4 = 25;
+ a0.a2.a0.a5.a0.a0 = 26.0;
+ a0.a2.a0.a5.a1.a0 = -27.0;
+ a0.a2.a0.a6 = 28;
+ a0.a2.a1.a0.a0 = -29;
+ a0.a2.a1.a0.a1 = 30;
+ a0.a2.a1.a1.a0 = -31.0;
+ a0.a2.a2 = 32.0;
+ a0.a2.a3 = -33.0;
+ a0.a3 = 34.0;
+ a1.a0 = 35;
+ a1.a1.a0.a0 = 36;
+ a1.a1.a0.a1.a0.a0 = -37;
+ a1.a1.a0.a1.a0.a1 = 38;
+ a1.a1.a0.a1.a1.a0 = -39.0;
+ a1.a1.a0.a2 = 40;
+ a1.a1.a0.a3.a0.a0 = -41.0;
+ a1.a1.a0.a3.a1 = 42.0;
+ a1.a1.a0.a4 = 43;
+ a1.a1.a0.a5.a0.a0 = 44.0;
+ a1.a1.a0.a5.a1.a0 = -45.0;
+ a1.a1.a0.a6 = 46;
+ a1.a1.a1.a0.a0 = -47;
+ a1.a1.a1.a0.a1 = 48;
+ a1.a1.a1.a1.a0 = -49.0;
+ a1.a1.a2 = 50.0;
+ a1.a1.a3 = -51.0;
+ a1.a2.a0.a0 = 52;
+ a1.a2.a0.a1.a0.a0 = -53;
+ a1.a2.a0.a1.a0.a1 = 54;
+ a1.a2.a0.a1.a1.a0 = -55.0;
+ a1.a2.a0.a2 = 56;
+ a1.a2.a0.a3.a0.a0 = -57.0;
+ a1.a2.a0.a3.a1 = 58.0;
+ a1.a2.a0.a4 = 59;
+ a1.a2.a0.a5.a0.a0 = 60.0;
+ a1.a2.a0.a5.a1.a0 = -61.0;
+ a1.a2.a0.a6 = 62;
+ a1.a2.a1.a0.a0 = -63;
+ a1.a2.a1.a0.a1 = 64;
+ a1.a2.a1.a1.a0 = -65.0;
+ a1.a2.a2 = 66.0;
+ a1.a2.a3 = -67.0;
+ a1.a3 = 68.0;
+ a2.a0 = 69;
+ a2.a1.a0.a0 = 70;
+ a2.a1.a0.a1.a0.a0 = -71;
+ a2.a1.a0.a1.a0.a1 = 72;
+ a2.a1.a0.a1.a1.a0 = -73.0;
+ a2.a1.a0.a2 = 74;
+ a2.a1.a0.a3.a0.a0 = -75.0;
+ a2.a1.a0.a3.a1 = 76.0;
+ a2.a1.a0.a4 = 77;
+ a2.a1.a0.a5.a0.a0 = 78.0;
+ a2.a1.a0.a5.a1.a0 = -79.0;
+ a2.a1.a0.a6 = 80;
+ a2.a1.a1.a0.a0 = -81;
+ a2.a1.a1.a0.a1 = 82;
+ a2.a1.a1.a1.a0 = -83.0;
+ a2.a1.a2 = 84.0;
+ a2.a1.a3 = -85.0;
+ a2.a2.a0.a0 = 86;
+ a2.a2.a0.a1.a0.a0 = -87;
+ a2.a2.a0.a1.a0.a1 = 88;
+ a2.a2.a0.a1.a1.a0 = -89.0;
+ a2.a2.a0.a2 = 90;
+ a2.a2.a0.a3.a0.a0 = -91.0;
+ a2.a2.a0.a3.a1 = 92.0;
+ a2.a2.a0.a4 = 93;
+ a2.a2.a0.a5.a0.a0 = 94.0;
+ a2.a2.a0.a5.a1.a0 = -95.0;
+ a2.a2.a0.a6 = 96;
+ a2.a2.a1.a0.a0 = -97;
+ a2.a2.a1.a0.a1 = 98;
+ a2.a2.a1.a1.a0 = -99.0;
+ a2.a2.a2 = 100.0;
+ a2.a2.a3 = -101.0;
+ a2.a3 = 102.0;
+ a3.a0 = 103;
+ a3.a1.a0.a0 = 104;
+ a3.a1.a0.a1.a0.a0 = -105;
+ a3.a1.a0.a1.a0.a1 = 106;
+ a3.a1.a0.a1.a1.a0 = -107.0;
+ a3.a1.a0.a2 = 108;
+ a3.a1.a0.a3.a0.a0 = -109.0;
+ a3.a1.a0.a3.a1 = 110.0;
+ a3.a1.a0.a4 = 111;
+ a3.a1.a0.a5.a0.a0 = 112.0;
+ a3.a1.a0.a5.a1.a0 = -113.0;
+ a3.a1.a0.a6 = 114;
+ a3.a1.a1.a0.a0 = -115;
+ a3.a1.a1.a0.a1 = 116;
+ a3.a1.a1.a1.a0 = -117.0;
+ a3.a1.a2 = 118.0;
+ a3.a1.a3 = -119.0;
+ a3.a2.a0.a0 = 120;
+ a3.a2.a0.a1.a0.a0 = -121;
+ a3.a2.a0.a1.a0.a1 = 122;
+ a3.a2.a0.a1.a1.a0 = -123.0;
+ a3.a2.a0.a2 = 124;
+ a3.a2.a0.a3.a0.a0 = -125.0;
+ a3.a2.a0.a3.a1 = 126.0;
+ a3.a2.a0.a4 = 127;
+ a3.a2.a0.a5.a0.a0 = 128.0;
+ a3.a2.a0.a5.a1.a0 = -129.0;
+ a3.a2.a0.a6 = 130;
+ a3.a2.a1.a0.a0 = -131;
+ a3.a2.a1.a0.a1 = 132;
+ a3.a2.a1.a1.a0 = -133.0;
+ a3.a2.a2 = 134.0;
+ a3.a2.a3 = -135.0;
+ a3.a3 = 136.0;
+
+ final result = passStructNestedIrregularEvenBiggerx4(a0, a1, a2, a3);
+
+ print("result = $result");
+
+ Expect.approxEquals(1572.0, result);
+
+ free(a0.addressOf);
+ free(a1.addressOf);
+ free(a2.addressOf);
+ free(a3.addressOf);
+}
+
final returnStruct1ByteInt = ffiTestFunctions.lookupFunction<
Struct1ByteInt Function(Int8),
Struct1ByteInt Function(int)>("ReturnStruct1ByteInt");
@@ -5319,3 +6154,396 @@
Expect.equals(a1, result.a1);
Expect.equals(a2, result.a2);
}
+
+final returnStruct8BytesNestedInt = ffiTestFunctions.lookupFunction<
+ Struct8BytesNestedInt Function(
+ Struct4BytesHomogeneousInt16, Struct4BytesHomogeneousInt16),
+ Struct8BytesNestedInt Function(Struct4BytesHomogeneousInt16,
+ Struct4BytesHomogeneousInt16)>("ReturnStruct8BytesNestedInt");
+
+/// Simple nested struct.
+void testReturnStruct8BytesNestedInt() {
+ Struct4BytesHomogeneousInt16 a0 =
+ allocate<Struct4BytesHomogeneousInt16>().ref;
+ Struct4BytesHomogeneousInt16 a1 =
+ allocate<Struct4BytesHomogeneousInt16>().ref;
+
+ a0.a0 = -1;
+ a0.a1 = 2;
+ a1.a0 = -3;
+ a1.a1 = 4;
+
+ final result = returnStruct8BytesNestedInt(a0, a1);
+
+ print("result = $result");
+
+ Expect.equals(a0.a0, result.a0.a0);
+ Expect.equals(a0.a1, result.a0.a1);
+ Expect.equals(a1.a0, result.a1.a0);
+ Expect.equals(a1.a1, result.a1.a1);
+
+ free(a0.addressOf);
+ free(a1.addressOf);
+}
+
+final returnStruct8BytesNestedFloat = ffiTestFunctions.lookupFunction<
+ Struct8BytesNestedFloat Function(Struct4BytesFloat, Struct4BytesFloat),
+ Struct8BytesNestedFloat Function(
+ Struct4BytesFloat, Struct4BytesFloat)>("ReturnStruct8BytesNestedFloat");
+
+/// Simple nested struct with floats.
+void testReturnStruct8BytesNestedFloat() {
+ Struct4BytesFloat a0 = allocate<Struct4BytesFloat>().ref;
+ Struct4BytesFloat a1 = allocate<Struct4BytesFloat>().ref;
+
+ a0.a0 = -1.0;
+ a1.a0 = 2.0;
+
+ final result = returnStruct8BytesNestedFloat(a0, a1);
+
+ print("result = $result");
+
+ Expect.approxEquals(a0.a0, result.a0.a0);
+ Expect.approxEquals(a1.a0, result.a1.a0);
+
+ free(a0.addressOf);
+ free(a1.addressOf);
+}
+
+final returnStruct8BytesNestedFloat2 = ffiTestFunctions.lookupFunction<
+ Struct8BytesNestedFloat2 Function(Struct4BytesFloat, Float),
+ Struct8BytesNestedFloat2 Function(
+ Struct4BytesFloat, double)>("ReturnStruct8BytesNestedFloat2");
+
+/// The nesting is irregular, testing homogenous float rules on arm and arm64,
+/// and the fpu register usage on x64.
+void testReturnStruct8BytesNestedFloat2() {
+ Struct4BytesFloat a0 = allocate<Struct4BytesFloat>().ref;
+ double a1;
+
+ a0.a0 = -1.0;
+ a1 = 2.0;
+
+ final result = returnStruct8BytesNestedFloat2(a0, a1);
+
+ print("result = $result");
+
+ Expect.approxEquals(a0.a0, result.a0.a0);
+ Expect.approxEquals(a1, result.a1);
+
+ free(a0.addressOf);
+}
+
+final returnStruct8BytesNestedMixed = ffiTestFunctions.lookupFunction<
+ Struct8BytesNestedMixed Function(
+ Struct4BytesHomogeneousInt16, Struct4BytesFloat),
+ Struct8BytesNestedMixed Function(Struct4BytesHomogeneousInt16,
+ Struct4BytesFloat)>("ReturnStruct8BytesNestedMixed");
+
+/// Simple nested struct with mixed members.
+void testReturnStruct8BytesNestedMixed() {
+ Struct4BytesHomogeneousInt16 a0 =
+ allocate<Struct4BytesHomogeneousInt16>().ref;
+ Struct4BytesFloat a1 = allocate<Struct4BytesFloat>().ref;
+
+ a0.a0 = -1;
+ a0.a1 = 2;
+ a1.a0 = -3.0;
+
+ final result = returnStruct8BytesNestedMixed(a0, a1);
+
+ print("result = $result");
+
+ Expect.equals(a0.a0, result.a0.a0);
+ Expect.equals(a0.a1, result.a0.a1);
+ Expect.approxEquals(a1.a0, result.a1.a0);
+
+ free(a0.addressOf);
+ free(a1.addressOf);
+}
+
+final returnStruct16BytesNestedInt = ffiTestFunctions.lookupFunction<
+ Struct16BytesNestedInt Function(
+ Struct8BytesNestedInt, Struct8BytesNestedInt),
+ Struct16BytesNestedInt Function(Struct8BytesNestedInt,
+ Struct8BytesNestedInt)>("ReturnStruct16BytesNestedInt");
+
+/// Deeper nested struct to test recursive member access.
+void testReturnStruct16BytesNestedInt() {
+ Struct8BytesNestedInt a0 = allocate<Struct8BytesNestedInt>().ref;
+ Struct8BytesNestedInt a1 = allocate<Struct8BytesNestedInt>().ref;
+
+ a0.a0.a0 = -1;
+ a0.a0.a1 = 2;
+ a0.a1.a0 = -3;
+ a0.a1.a1 = 4;
+ a1.a0.a0 = -5;
+ a1.a0.a1 = 6;
+ a1.a1.a0 = -7;
+ a1.a1.a1 = 8;
+
+ final result = returnStruct16BytesNestedInt(a0, a1);
+
+ print("result = $result");
+
+ Expect.equals(a0.a0.a0, result.a0.a0.a0);
+ Expect.equals(a0.a0.a1, result.a0.a0.a1);
+ Expect.equals(a0.a1.a0, result.a0.a1.a0);
+ Expect.equals(a0.a1.a1, result.a0.a1.a1);
+ Expect.equals(a1.a0.a0, result.a1.a0.a0);
+ Expect.equals(a1.a0.a1, result.a1.a0.a1);
+ Expect.equals(a1.a1.a0, result.a1.a1.a0);
+ Expect.equals(a1.a1.a1, result.a1.a1.a1);
+
+ free(a0.addressOf);
+ free(a1.addressOf);
+}
+
+final returnStruct32BytesNestedInt = ffiTestFunctions.lookupFunction<
+ Struct32BytesNestedInt Function(
+ Struct16BytesNestedInt, Struct16BytesNestedInt),
+ Struct32BytesNestedInt Function(Struct16BytesNestedInt,
+ Struct16BytesNestedInt)>("ReturnStruct32BytesNestedInt");
+
+/// Even deeper nested struct to test recursive member access.
+void testReturnStruct32BytesNestedInt() {
+ Struct16BytesNestedInt a0 = allocate<Struct16BytesNestedInt>().ref;
+ Struct16BytesNestedInt a1 = allocate<Struct16BytesNestedInt>().ref;
+
+ a0.a0.a0.a0 = -1;
+ a0.a0.a0.a1 = 2;
+ a0.a0.a1.a0 = -3;
+ a0.a0.a1.a1 = 4;
+ a0.a1.a0.a0 = -5;
+ a0.a1.a0.a1 = 6;
+ a0.a1.a1.a0 = -7;
+ a0.a1.a1.a1 = 8;
+ a1.a0.a0.a0 = -9;
+ a1.a0.a0.a1 = 10;
+ a1.a0.a1.a0 = -11;
+ a1.a0.a1.a1 = 12;
+ a1.a1.a0.a0 = -13;
+ a1.a1.a0.a1 = 14;
+ a1.a1.a1.a0 = -15;
+ a1.a1.a1.a1 = 16;
+
+ final result = returnStruct32BytesNestedInt(a0, a1);
+
+ print("result = $result");
+
+ Expect.equals(a0.a0.a0.a0, result.a0.a0.a0.a0);
+ Expect.equals(a0.a0.a0.a1, result.a0.a0.a0.a1);
+ Expect.equals(a0.a0.a1.a0, result.a0.a0.a1.a0);
+ Expect.equals(a0.a0.a1.a1, result.a0.a0.a1.a1);
+ Expect.equals(a0.a1.a0.a0, result.a0.a1.a0.a0);
+ Expect.equals(a0.a1.a0.a1, result.a0.a1.a0.a1);
+ Expect.equals(a0.a1.a1.a0, result.a0.a1.a1.a0);
+ Expect.equals(a0.a1.a1.a1, result.a0.a1.a1.a1);
+ Expect.equals(a1.a0.a0.a0, result.a1.a0.a0.a0);
+ Expect.equals(a1.a0.a0.a1, result.a1.a0.a0.a1);
+ Expect.equals(a1.a0.a1.a0, result.a1.a0.a1.a0);
+ Expect.equals(a1.a0.a1.a1, result.a1.a0.a1.a1);
+ Expect.equals(a1.a1.a0.a0, result.a1.a1.a0.a0);
+ Expect.equals(a1.a1.a0.a1, result.a1.a1.a0.a1);
+ Expect.equals(a1.a1.a1.a0, result.a1.a1.a1.a0);
+ Expect.equals(a1.a1.a1.a1, result.a1.a1.a1.a1);
+
+ free(a0.addressOf);
+ free(a1.addressOf);
+}
+
+final returnStructNestedIntStructAlignmentInt16 =
+ ffiTestFunctions.lookupFunction<
+ StructNestedIntStructAlignmentInt16 Function(
+ StructAlignmentInt16, StructAlignmentInt16),
+ StructNestedIntStructAlignmentInt16 Function(StructAlignmentInt16,
+ StructAlignmentInt16)>("ReturnStructNestedIntStructAlignmentInt16");
+
+/// Test alignment and padding of nested struct with 16 byte int.
+void testReturnStructNestedIntStructAlignmentInt16() {
+ StructAlignmentInt16 a0 = allocate<StructAlignmentInt16>().ref;
+ StructAlignmentInt16 a1 = allocate<StructAlignmentInt16>().ref;
+
+ a0.a0 = -1;
+ a0.a1 = 2;
+ a0.a2 = -3;
+ a1.a0 = 4;
+ a1.a1 = -5;
+ a1.a2 = 6;
+
+ final result = returnStructNestedIntStructAlignmentInt16(a0, a1);
+
+ print("result = $result");
+
+ Expect.equals(a0.a0, result.a0.a0);
+ Expect.equals(a0.a1, result.a0.a1);
+ Expect.equals(a0.a2, result.a0.a2);
+ Expect.equals(a1.a0, result.a1.a0);
+ Expect.equals(a1.a1, result.a1.a1);
+ Expect.equals(a1.a2, result.a1.a2);
+
+ free(a0.addressOf);
+ free(a1.addressOf);
+}
+
+final returnStructNestedIntStructAlignmentInt32 =
+ ffiTestFunctions.lookupFunction<
+ StructNestedIntStructAlignmentInt32 Function(
+ StructAlignmentInt32, StructAlignmentInt32),
+ StructNestedIntStructAlignmentInt32 Function(StructAlignmentInt32,
+ StructAlignmentInt32)>("ReturnStructNestedIntStructAlignmentInt32");
+
+/// Test alignment and padding of nested struct with 32 byte int.
+void testReturnStructNestedIntStructAlignmentInt32() {
+ StructAlignmentInt32 a0 = allocate<StructAlignmentInt32>().ref;
+ StructAlignmentInt32 a1 = allocate<StructAlignmentInt32>().ref;
+
+ a0.a0 = -1;
+ a0.a1 = 2;
+ a0.a2 = -3;
+ a1.a0 = 4;
+ a1.a1 = -5;
+ a1.a2 = 6;
+
+ final result = returnStructNestedIntStructAlignmentInt32(a0, a1);
+
+ print("result = $result");
+
+ Expect.equals(a0.a0, result.a0.a0);
+ Expect.equals(a0.a1, result.a0.a1);
+ Expect.equals(a0.a2, result.a0.a2);
+ Expect.equals(a1.a0, result.a1.a0);
+ Expect.equals(a1.a1, result.a1.a1);
+ Expect.equals(a1.a2, result.a1.a2);
+
+ free(a0.addressOf);
+ free(a1.addressOf);
+}
+
+final returnStructNestedIntStructAlignmentInt64 =
+ ffiTestFunctions.lookupFunction<
+ StructNestedIntStructAlignmentInt64 Function(
+ StructAlignmentInt64, StructAlignmentInt64),
+ StructNestedIntStructAlignmentInt64 Function(StructAlignmentInt64,
+ StructAlignmentInt64)>("ReturnStructNestedIntStructAlignmentInt64");
+
+/// Test alignment and padding of nested struct with 64 byte int.
+void testReturnStructNestedIntStructAlignmentInt64() {
+ StructAlignmentInt64 a0 = allocate<StructAlignmentInt64>().ref;
+ StructAlignmentInt64 a1 = allocate<StructAlignmentInt64>().ref;
+
+ a0.a0 = -1;
+ a0.a1 = 2;
+ a0.a2 = -3;
+ a1.a0 = 4;
+ a1.a1 = -5;
+ a1.a2 = 6;
+
+ final result = returnStructNestedIntStructAlignmentInt64(a0, a1);
+
+ print("result = $result");
+
+ Expect.equals(a0.a0, result.a0.a0);
+ Expect.equals(a0.a1, result.a0.a1);
+ Expect.equals(a0.a2, result.a0.a2);
+ Expect.equals(a1.a0, result.a1.a0);
+ Expect.equals(a1.a1, result.a1.a1);
+ Expect.equals(a1.a2, result.a1.a2);
+
+ free(a0.addressOf);
+ free(a1.addressOf);
+}
+
+final returnStructNestedIrregularEvenBigger = ffiTestFunctions.lookupFunction<
+ StructNestedIrregularEvenBigger Function(Uint64,
+ StructNestedIrregularBigger, StructNestedIrregularBigger, Double),
+ StructNestedIrregularEvenBigger Function(
+ int,
+ StructNestedIrregularBigger,
+ StructNestedIrregularBigger,
+ double)>("ReturnStructNestedIrregularEvenBigger");
+
+/// Return big irregular struct as smoke test.
+void testReturnStructNestedIrregularEvenBigger() {
+ int a0;
+ StructNestedIrregularBigger a1 = allocate<StructNestedIrregularBigger>().ref;
+ StructNestedIrregularBigger a2 = allocate<StructNestedIrregularBigger>().ref;
+ double a3;
+
+ a0 = 1;
+ a1.a0.a0 = 2;
+ a1.a0.a1.a0.a0 = -3;
+ a1.a0.a1.a0.a1 = 4;
+ a1.a0.a1.a1.a0 = -5.0;
+ a1.a0.a2 = 6;
+ a1.a0.a3.a0.a0 = -7.0;
+ a1.a0.a3.a1 = 8.0;
+ a1.a0.a4 = 9;
+ a1.a0.a5.a0.a0 = 10.0;
+ a1.a0.a5.a1.a0 = -11.0;
+ a1.a0.a6 = 12;
+ a1.a1.a0.a0 = -13;
+ a1.a1.a0.a1 = 14;
+ a1.a1.a1.a0 = -15.0;
+ a1.a2 = 16.0;
+ a1.a3 = -17.0;
+ a2.a0.a0 = 18;
+ a2.a0.a1.a0.a0 = -19;
+ a2.a0.a1.a0.a1 = 20;
+ a2.a0.a1.a1.a0 = -21.0;
+ a2.a0.a2 = 22;
+ a2.a0.a3.a0.a0 = -23.0;
+ a2.a0.a3.a1 = 24.0;
+ a2.a0.a4 = 25;
+ a2.a0.a5.a0.a0 = 26.0;
+ a2.a0.a5.a1.a0 = -27.0;
+ a2.a0.a6 = 28;
+ a2.a1.a0.a0 = -29;
+ a2.a1.a0.a1 = 30;
+ a2.a1.a1.a0 = -31.0;
+ a2.a2 = 32.0;
+ a2.a3 = -33.0;
+ a3 = 34.0;
+
+ final result = returnStructNestedIrregularEvenBigger(a0, a1, a2, a3);
+
+ print("result = $result");
+
+ Expect.equals(a0, result.a0);
+ Expect.equals(a1.a0.a0, result.a1.a0.a0);
+ Expect.equals(a1.a0.a1.a0.a0, result.a1.a0.a1.a0.a0);
+ Expect.equals(a1.a0.a1.a0.a1, result.a1.a0.a1.a0.a1);
+ Expect.approxEquals(a1.a0.a1.a1.a0, result.a1.a0.a1.a1.a0);
+ Expect.equals(a1.a0.a2, result.a1.a0.a2);
+ Expect.approxEquals(a1.a0.a3.a0.a0, result.a1.a0.a3.a0.a0);
+ Expect.approxEquals(a1.a0.a3.a1, result.a1.a0.a3.a1);
+ Expect.equals(a1.a0.a4, result.a1.a0.a4);
+ Expect.approxEquals(a1.a0.a5.a0.a0, result.a1.a0.a5.a0.a0);
+ Expect.approxEquals(a1.a0.a5.a1.a0, result.a1.a0.a5.a1.a0);
+ Expect.equals(a1.a0.a6, result.a1.a0.a6);
+ Expect.equals(a1.a1.a0.a0, result.a1.a1.a0.a0);
+ Expect.equals(a1.a1.a0.a1, result.a1.a1.a0.a1);
+ Expect.approxEquals(a1.a1.a1.a0, result.a1.a1.a1.a0);
+ Expect.approxEquals(a1.a2, result.a1.a2);
+ Expect.approxEquals(a1.a3, result.a1.a3);
+ Expect.equals(a2.a0.a0, result.a2.a0.a0);
+ Expect.equals(a2.a0.a1.a0.a0, result.a2.a0.a1.a0.a0);
+ Expect.equals(a2.a0.a1.a0.a1, result.a2.a0.a1.a0.a1);
+ Expect.approxEquals(a2.a0.a1.a1.a0, result.a2.a0.a1.a1.a0);
+ Expect.equals(a2.a0.a2, result.a2.a0.a2);
+ Expect.approxEquals(a2.a0.a3.a0.a0, result.a2.a0.a3.a0.a0);
+ Expect.approxEquals(a2.a0.a3.a1, result.a2.a0.a3.a1);
+ Expect.equals(a2.a0.a4, result.a2.a0.a4);
+ Expect.approxEquals(a2.a0.a5.a0.a0, result.a2.a0.a5.a0.a0);
+ Expect.approxEquals(a2.a0.a5.a1.a0, result.a2.a0.a5.a1.a0);
+ Expect.equals(a2.a0.a6, result.a2.a0.a6);
+ Expect.equals(a2.a1.a0.a0, result.a2.a1.a0.a0);
+ Expect.equals(a2.a1.a0.a1, result.a2.a1.a0.a1);
+ Expect.approxEquals(a2.a1.a1.a0, result.a2.a1.a1.a0);
+ Expect.approxEquals(a2.a2, result.a2.a2);
+ Expect.approxEquals(a2.a3, result.a2.a3);
+ Expect.approxEquals(a3, result.a3);
+
+ free(a1.addressOf);
+ free(a2.addressOf);
+}
diff --git a/tests/ffi/generator/c_types.dart b/tests/ffi/generator/c_types.dart
index f2948fa..56736bf5 100644
--- a/tests/ffi/generator/c_types.dart
+++ b/tests/ffi/generator/c_types.dart
@@ -140,11 +140,19 @@
/// To disambiguate same size structs.
final String suffix;
+ /// To override names.
+ final String overrideName;
+
StructType(List<CType> memberTypes)
: this.members = generateMemberNames(memberTypes),
- this.suffix = "";
+ this.suffix = "",
+ this.overrideName = "";
StructType.disambiguate(List<CType> memberTypes, this.suffix)
- : this.members = generateMemberNames(memberTypes);
+ : this.members = generateMemberNames(memberTypes),
+ this.overrideName = "";
+ StructType.override(List<CType> memberTypes, this.overrideName)
+ : this.members = generateMemberNames(memberTypes),
+ this.suffix = "";
List<CType> get memberTypes => members.map((a) => a.type).toList();
@@ -191,6 +199,9 @@
String get name {
String result = "Struct";
+ if (overrideName != "") {
+ return result + overrideName;
+ }
if (hasSize) {
result += "${size}Byte" + (size != 1 ? "s" : "");
}
diff --git a/tests/ffi/generator/structs_by_value_tests_configuration.dart b/tests/ffi/generator/structs_by_value_tests_configuration.dart
index 46f5108..718caea 100644
--- a/tests/ffi/generator/structs_by_value_tests_configuration.dart
+++ b/tests/ffi/generator/structs_by_value_tests_configuration.dart
@@ -255,6 +255,41 @@
int64,
"""
Test alignment and padding of 64 byte int within struct."""),
+ FunctionType(List.filled(10, struct8bytesNestedInt), int64, """
+Simple nested struct. No alignment gaps on any architectures.
+10 arguments exhaust registers on all platforms."""),
+ FunctionType(List.filled(10, struct8bytesNestedFloat), float, """
+Simple nested struct. No alignment gaps on any architectures.
+10 arguments exhaust fpu registers on all platforms."""),
+ FunctionType(List.filled(10, struct8bytesNestedFloat2), float, """
+Simple nested struct. No alignment gaps on any architectures.
+10 arguments exhaust fpu registers on all platforms.
+The nesting is irregular, testing homogenous float rules on arm and arm64,
+and the fpu register usage on x64."""),
+ FunctionType(List.filled(10, struct8bytesNestedMixed), double_, """
+Simple nested struct. No alignment gaps on any architectures.
+10 arguments exhaust all registers on all platforms."""),
+ FunctionType(List.filled(2, struct16bytesNestedInt), int64, """
+Deeper nested struct to test recursive member access."""),
+ FunctionType(List.filled(2, struct32bytesNestedInt), int64, """
+Even deeper nested struct to test recursive member access."""),
+ FunctionType(
+ [structNestedAlignmentInt16],
+ int64,
+ """
+Test alignment and padding of nested struct with 16 byte int."""),
+ FunctionType(
+ [structNestedAlignmentInt32],
+ int64,
+ """
+Test alignment and padding of nested struct with 32 byte int."""),
+ FunctionType(
+ [structNestedAlignmentInt64],
+ int64,
+ """
+Test alignment and padding of nested struct with 64 byte int."""),
+ FunctionType(List.filled(4, structNestedEvenBigger), double_, """
+Return big irregular struct as smoke test."""),
FunctionType(struct1byteInt.memberTypes, struct1byteInt, """
Smallest struct with data."""),
FunctionType(struct3bytesInt.memberTypes, struct3bytesInt, """
@@ -358,6 +393,31 @@
Test alignment and padding of 32 byte int within struct."""),
FunctionType(structAlignmentInt64.memberTypes, structAlignmentInt64, """
Test alignment and padding of 64 byte int within struct."""),
+ FunctionType(struct8bytesNestedInt.memberTypes, struct8bytesNestedInt, """
+Simple nested struct."""),
+ FunctionType(struct8bytesNestedFloat.memberTypes, struct8bytesNestedFloat, """
+Simple nested struct with floats."""),
+ FunctionType(
+ struct8bytesNestedFloat2.memberTypes, struct8bytesNestedFloat2, """
+The nesting is irregular, testing homogenous float rules on arm and arm64,
+and the fpu register usage on x64."""),
+ FunctionType(struct8bytesNestedMixed.memberTypes, struct8bytesNestedMixed, """
+Simple nested struct with mixed members."""),
+ FunctionType(struct16bytesNestedInt.memberTypes, struct16bytesNestedInt, """
+Deeper nested struct to test recursive member access."""),
+ FunctionType(struct32bytesNestedInt.memberTypes, struct32bytesNestedInt, """
+Even deeper nested struct to test recursive member access."""),
+ FunctionType(
+ structNestedAlignmentInt16.memberTypes, structNestedAlignmentInt16, """
+Test alignment and padding of nested struct with 16 byte int."""),
+ FunctionType(
+ structNestedAlignmentInt32.memberTypes, structNestedAlignmentInt32, """
+Test alignment and padding of nested struct with 32 byte int."""),
+ FunctionType(
+ structNestedAlignmentInt64.memberTypes, structNestedAlignmentInt64, """
+Test alignment and padding of nested struct with 64 byte int."""),
+ FunctionType(structNestedEvenBigger.memberTypes, structNestedEvenBigger, """
+Return big irregular struct as smoke test."""),
];
final structs = [
@@ -366,6 +426,7 @@
struct3bytesInt,
struct3bytesInt2,
struct4bytesInt,
+ struct4bytesFloat,
struct7bytesInt,
struct7bytesInt2,
struct8bytesInt,
@@ -387,6 +448,18 @@
structAlignmentInt16,
structAlignmentInt32,
structAlignmentInt64,
+ struct8bytesNestedInt,
+ struct8bytesNestedFloat,
+ struct8bytesNestedFloat2,
+ struct8bytesNestedMixed,
+ struct16bytesNestedInt,
+ struct32bytesNestedInt,
+ structNestedAlignmentInt16,
+ structNestedAlignmentInt32,
+ structNestedAlignmentInt64,
+ structNestedBig,
+ structNestedBigger,
+ structNestedEvenBigger,
];
/// Using empty structs is undefined behavior in C.
@@ -396,6 +469,7 @@
final struct3bytesInt = StructType(List.filled(3, uint8));
final struct3bytesInt2 = StructType.disambiguate([int16, int8], "2ByteAligned");
final struct4bytesInt = StructType([int16, int16]);
+final struct4bytesFloat = StructType([float]);
final struct7bytesInt = StructType(List.filled(7, uint8));
final struct7bytesInt2 =
StructType.disambiguate([int32, int16, int8], "4ByteAligned");
@@ -444,3 +518,39 @@
final structAlignmentInt16 = StructType([int8, int16, int8]);
final structAlignmentInt32 = StructType([int8, int32, int8]);
final structAlignmentInt64 = StructType([int8, int64, int8]);
+
+final struct8bytesNestedInt = StructType([struct4bytesInt, struct4bytesInt]);
+final struct8bytesNestedFloat =
+ StructType([struct4bytesFloat, struct4bytesFloat]);
+final struct8bytesNestedFloat2 =
+ StructType.disambiguate([struct4bytesFloat, float], "2");
+final struct8bytesNestedMixed =
+ StructType([struct4bytesInt, struct4bytesFloat]);
+
+final struct16bytesNestedInt =
+ StructType([struct8bytesNestedInt, struct8bytesNestedInt]);
+final struct32bytesNestedInt =
+ StructType([struct16bytesNestedInt, struct16bytesNestedInt]);
+
+final structNestedAlignmentInt16 = StructType.disambiguate(
+ List.filled(2, structAlignmentInt16), structAlignmentInt16.name);
+final structNestedAlignmentInt32 = StructType.disambiguate(
+ List.filled(2, structAlignmentInt32), structAlignmentInt32.name);
+final structNestedAlignmentInt64 = StructType.disambiguate(
+ List.filled(2, structAlignmentInt64), structAlignmentInt64.name);
+
+final structNestedBig = StructType.override([
+ uint16,
+ struct8bytesNestedMixed,
+ uint16,
+ struct8bytesNestedFloat2,
+ uint16,
+ struct8bytesNestedFloat,
+ uint16
+], "NestedIrregularBig");
+final structNestedBigger = StructType.override(
+ [structNestedBig, struct8bytesNestedMixed, float, double_],
+ "NestedIrregularBigger");
+final structNestedEvenBigger = StructType.override(
+ [uint64, structNestedBigger, structNestedBigger, double_],
+ "NestedIrregularEvenBigger");
diff --git a/tests/ffi/structs_nested_test.dart b/tests/ffi/structs_nested_test.dart
new file mode 100644
index 0000000..ea4b6db
--- /dev/null
+++ b/tests/ffi/structs_nested_test.dart
@@ -0,0 +1,105 @@
+// Copyright (c) 2020, 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.
+//
+// This tests the non trampoline aspects of nested structs.
+//
+// SharedObjects=ffi_test_functions
+
+import 'dart:ffi';
+
+import "package:expect/expect.dart";
+import "package:ffi/ffi.dart";
+
+import 'dylib_utils.dart';
+
+final ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions");
+void main() {
+ for (int i = 0; i < 10; ++i) {
+ testSizeOf();
+ testAllocate();
+ testRead();
+ testWrite();
+ testCopy();
+ }
+}
+
+class Struct4BytesHomogeneousInt16 extends Struct {
+ @Int16()
+ external int a0;
+
+ @Int16()
+ external int a1;
+
+ String toString() => "(${a0}, ${a1})";
+}
+
+class Struct8BytesNestedInt extends Struct {
+ external Struct4BytesHomogeneousInt16 a0;
+
+ external Struct4BytesHomogeneousInt16 a1;
+
+ String toString() => "(${a0}, ${a1})";
+}
+
+void testSizeOf() {
+ print(sizeOf<Struct8BytesNestedInt>());
+ Expect.equals(8, sizeOf<Struct8BytesNestedInt>());
+}
+
+void testAllocate() {
+ final p = allocate<Struct8BytesNestedInt>();
+ Expect.type<Pointer<Struct8BytesNestedInt>>(p);
+ print(p);
+ free(p);
+}
+
+/// Test that reading does not segfault, even uninitialized.
+void testRead() {
+ print("read");
+ final p = allocate<Struct8BytesNestedInt>();
+ print(p);
+ print(p.ref.runtimeType);
+ print(p.ref.addressOf);
+ print(p.ref.addressOf.address);
+ print(p.ref.a0.runtimeType);
+ print(p.ref.a0.addressOf);
+ print(p.ref.a0.a0);
+ free(p);
+ print("read");
+}
+
+void testWrite() {
+ print("write");
+ final p = allocate<Struct8BytesNestedInt>(count: 2);
+ p[0].a0.a0 = 12;
+ p[0].a0.a1 = 13;
+ p[0].a1.a0 = 14;
+ p[0].a1.a1 = 15;
+ p[1].a0.a0 = 16;
+ p[1].a0.a1 = 17;
+ p[1].a1.a0 = 18;
+ p[1].a1.a1 = 19;
+ Expect.equals(12, p[0].a0.a0);
+ Expect.equals(13, p[0].a0.a1);
+ Expect.equals(14, p[0].a1.a0);
+ Expect.equals(15, p[0].a1.a1);
+ Expect.equals(16, p[1].a0.a0);
+ Expect.equals(17, p[1].a0.a1);
+ Expect.equals(18, p[1].a1.a0);
+ Expect.equals(19, p[1].a1.a1);
+ free(p);
+ print("written");
+}
+
+void testCopy() {
+ print("copy");
+ final p = allocate<Struct8BytesNestedInt>();
+ p.ref.a0.a0 = 12;
+ p.ref.a0.a1 = 13;
+ p.ref.a1 = p.ref.a0;
+ Expect.equals(12, p.ref.a1.a0);
+ Expect.equals(13, p.ref.a1.a1);
+ free(p);
+ print("copied");
+}
diff --git a/tests/ffi/vmspecific_static_checks_test.dart b/tests/ffi/vmspecific_static_checks_test.dart
index b32acba..c77245e 100644
--- a/tests/ffi/vmspecific_static_checks_test.dart
+++ b/tests/ffi/vmspecific_static_checks_test.dart
@@ -526,3 +526,7 @@
Pointer.fromFunction<EmptyStruct Function()>(//# 1105: compile-time error
_returnEmptyStruct); //# 1105: compile-time error
}
+
+class HasNestedEmptyStruct extends Struct {
+ external EmptyStruct nestedEmptyStruct; //# 1106: compile-time error
+}
diff --git a/tests/ffi_2/function_callbacks_structs_by_value_generated_test.dart b/tests/ffi_2/function_callbacks_structs_by_value_generated_test.dart
index 38714fd..bebd119 100644
--- a/tests/ffi_2/function_callbacks_structs_by_value_generated_test.dart
+++ b/tests/ffi_2/function_callbacks_structs_by_value_generated_test.dart
@@ -193,6 +193,56 @@
passStructAlignmentInt64, 0),
passStructAlignmentInt64AfterCallback),
CallbackTest.withCheck(
+ "PassStruct8BytesNestedIntx10",
+ Pointer.fromFunction<PassStruct8BytesNestedIntx10Type>(
+ passStruct8BytesNestedIntx10, 0),
+ passStruct8BytesNestedIntx10AfterCallback),
+ CallbackTest.withCheck(
+ "PassStruct8BytesNestedFloatx10",
+ Pointer.fromFunction<PassStruct8BytesNestedFloatx10Type>(
+ passStruct8BytesNestedFloatx10, 0.0),
+ passStruct8BytesNestedFloatx10AfterCallback),
+ CallbackTest.withCheck(
+ "PassStruct8BytesNestedFloat2x10",
+ Pointer.fromFunction<PassStruct8BytesNestedFloat2x10Type>(
+ passStruct8BytesNestedFloat2x10, 0.0),
+ passStruct8BytesNestedFloat2x10AfterCallback),
+ CallbackTest.withCheck(
+ "PassStruct8BytesNestedMixedx10",
+ Pointer.fromFunction<PassStruct8BytesNestedMixedx10Type>(
+ passStruct8BytesNestedMixedx10, 0.0),
+ passStruct8BytesNestedMixedx10AfterCallback),
+ CallbackTest.withCheck(
+ "PassStruct16BytesNestedIntx2",
+ Pointer.fromFunction<PassStruct16BytesNestedIntx2Type>(
+ passStruct16BytesNestedIntx2, 0),
+ passStruct16BytesNestedIntx2AfterCallback),
+ CallbackTest.withCheck(
+ "PassStruct32BytesNestedIntx2",
+ Pointer.fromFunction<PassStruct32BytesNestedIntx2Type>(
+ passStruct32BytesNestedIntx2, 0),
+ passStruct32BytesNestedIntx2AfterCallback),
+ CallbackTest.withCheck(
+ "PassStructNestedIntStructAlignmentInt16",
+ Pointer.fromFunction<PassStructNestedIntStructAlignmentInt16Type>(
+ passStructNestedIntStructAlignmentInt16, 0),
+ passStructNestedIntStructAlignmentInt16AfterCallback),
+ CallbackTest.withCheck(
+ "PassStructNestedIntStructAlignmentInt32",
+ Pointer.fromFunction<PassStructNestedIntStructAlignmentInt32Type>(
+ passStructNestedIntStructAlignmentInt32, 0),
+ passStructNestedIntStructAlignmentInt32AfterCallback),
+ CallbackTest.withCheck(
+ "PassStructNestedIntStructAlignmentInt64",
+ Pointer.fromFunction<PassStructNestedIntStructAlignmentInt64Type>(
+ passStructNestedIntStructAlignmentInt64, 0),
+ passStructNestedIntStructAlignmentInt64AfterCallback),
+ CallbackTest.withCheck(
+ "PassStructNestedIrregularEvenBiggerx4",
+ Pointer.fromFunction<PassStructNestedIrregularEvenBiggerx4Type>(
+ passStructNestedIrregularEvenBiggerx4, 0.0),
+ passStructNestedIrregularEvenBiggerx4AfterCallback),
+ CallbackTest.withCheck(
"ReturnStruct1ByteInt",
Pointer.fromFunction<ReturnStruct1ByteIntType>(returnStruct1ByteInt),
returnStruct1ByteIntAfterCallback),
@@ -342,6 +392,56 @@
Pointer.fromFunction<ReturnStructAlignmentInt64Type>(
returnStructAlignmentInt64),
returnStructAlignmentInt64AfterCallback),
+ CallbackTest.withCheck(
+ "ReturnStruct8BytesNestedInt",
+ Pointer.fromFunction<ReturnStruct8BytesNestedIntType>(
+ returnStruct8BytesNestedInt),
+ returnStruct8BytesNestedIntAfterCallback),
+ CallbackTest.withCheck(
+ "ReturnStruct8BytesNestedFloat",
+ Pointer.fromFunction<ReturnStruct8BytesNestedFloatType>(
+ returnStruct8BytesNestedFloat),
+ returnStruct8BytesNestedFloatAfterCallback),
+ CallbackTest.withCheck(
+ "ReturnStruct8BytesNestedFloat2",
+ Pointer.fromFunction<ReturnStruct8BytesNestedFloat2Type>(
+ returnStruct8BytesNestedFloat2),
+ returnStruct8BytesNestedFloat2AfterCallback),
+ CallbackTest.withCheck(
+ "ReturnStruct8BytesNestedMixed",
+ Pointer.fromFunction<ReturnStruct8BytesNestedMixedType>(
+ returnStruct8BytesNestedMixed),
+ returnStruct8BytesNestedMixedAfterCallback),
+ CallbackTest.withCheck(
+ "ReturnStruct16BytesNestedInt",
+ Pointer.fromFunction<ReturnStruct16BytesNestedIntType>(
+ returnStruct16BytesNestedInt),
+ returnStruct16BytesNestedIntAfterCallback),
+ CallbackTest.withCheck(
+ "ReturnStruct32BytesNestedInt",
+ Pointer.fromFunction<ReturnStruct32BytesNestedIntType>(
+ returnStruct32BytesNestedInt),
+ returnStruct32BytesNestedIntAfterCallback),
+ CallbackTest.withCheck(
+ "ReturnStructNestedIntStructAlignmentInt16",
+ Pointer.fromFunction<ReturnStructNestedIntStructAlignmentInt16Type>(
+ returnStructNestedIntStructAlignmentInt16),
+ returnStructNestedIntStructAlignmentInt16AfterCallback),
+ CallbackTest.withCheck(
+ "ReturnStructNestedIntStructAlignmentInt32",
+ Pointer.fromFunction<ReturnStructNestedIntStructAlignmentInt32Type>(
+ returnStructNestedIntStructAlignmentInt32),
+ returnStructNestedIntStructAlignmentInt32AfterCallback),
+ CallbackTest.withCheck(
+ "ReturnStructNestedIntStructAlignmentInt64",
+ Pointer.fromFunction<ReturnStructNestedIntStructAlignmentInt64Type>(
+ returnStructNestedIntStructAlignmentInt64),
+ returnStructNestedIntStructAlignmentInt64AfterCallback),
+ CallbackTest.withCheck(
+ "ReturnStructNestedIrregularEvenBigger",
+ Pointer.fromFunction<ReturnStructNestedIrregularEvenBiggerType>(
+ returnStructNestedIrregularEvenBigger),
+ returnStructNestedIrregularEvenBiggerAfterCallback),
];
typedef PassStruct1ByteIntx10Type = Int64 Function(
Struct1ByteInt,
@@ -4256,6 +4356,1053 @@
Expect.equals(-2, result);
}
+typedef PassStruct8BytesNestedIntx10Type = Int64 Function(
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt);
+
+// Global variables to be able to test inputs after callback returned.
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a0 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a1 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a2 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a3 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a4 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a5 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a6 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a7 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a8 = Struct8BytesNestedInt();
+Struct8BytesNestedInt passStruct8BytesNestedIntx10_a9 = Struct8BytesNestedInt();
+
+// Result variable also global, so we can delete it after the callback.
+int passStruct8BytesNestedIntx10Result = 0;
+
+int passStruct8BytesNestedIntx10CalculateResult() {
+ int result = 0;
+
+ result += passStruct8BytesNestedIntx10_a0.a0.a0;
+ result += passStruct8BytesNestedIntx10_a0.a0.a1;
+ result += passStruct8BytesNestedIntx10_a0.a1.a0;
+ result += passStruct8BytesNestedIntx10_a0.a1.a1;
+ result += passStruct8BytesNestedIntx10_a1.a0.a0;
+ result += passStruct8BytesNestedIntx10_a1.a0.a1;
+ result += passStruct8BytesNestedIntx10_a1.a1.a0;
+ result += passStruct8BytesNestedIntx10_a1.a1.a1;
+ result += passStruct8BytesNestedIntx10_a2.a0.a0;
+ result += passStruct8BytesNestedIntx10_a2.a0.a1;
+ result += passStruct8BytesNestedIntx10_a2.a1.a0;
+ result += passStruct8BytesNestedIntx10_a2.a1.a1;
+ result += passStruct8BytesNestedIntx10_a3.a0.a0;
+ result += passStruct8BytesNestedIntx10_a3.a0.a1;
+ result += passStruct8BytesNestedIntx10_a3.a1.a0;
+ result += passStruct8BytesNestedIntx10_a3.a1.a1;
+ result += passStruct8BytesNestedIntx10_a4.a0.a0;
+ result += passStruct8BytesNestedIntx10_a4.a0.a1;
+ result += passStruct8BytesNestedIntx10_a4.a1.a0;
+ result += passStruct8BytesNestedIntx10_a4.a1.a1;
+ result += passStruct8BytesNestedIntx10_a5.a0.a0;
+ result += passStruct8BytesNestedIntx10_a5.a0.a1;
+ result += passStruct8BytesNestedIntx10_a5.a1.a0;
+ result += passStruct8BytesNestedIntx10_a5.a1.a1;
+ result += passStruct8BytesNestedIntx10_a6.a0.a0;
+ result += passStruct8BytesNestedIntx10_a6.a0.a1;
+ result += passStruct8BytesNestedIntx10_a6.a1.a0;
+ result += passStruct8BytesNestedIntx10_a6.a1.a1;
+ result += passStruct8BytesNestedIntx10_a7.a0.a0;
+ result += passStruct8BytesNestedIntx10_a7.a0.a1;
+ result += passStruct8BytesNestedIntx10_a7.a1.a0;
+ result += passStruct8BytesNestedIntx10_a7.a1.a1;
+ result += passStruct8BytesNestedIntx10_a8.a0.a0;
+ result += passStruct8BytesNestedIntx10_a8.a0.a1;
+ result += passStruct8BytesNestedIntx10_a8.a1.a0;
+ result += passStruct8BytesNestedIntx10_a8.a1.a1;
+ result += passStruct8BytesNestedIntx10_a9.a0.a0;
+ result += passStruct8BytesNestedIntx10_a9.a0.a1;
+ result += passStruct8BytesNestedIntx10_a9.a1.a0;
+ result += passStruct8BytesNestedIntx10_a9.a1.a1;
+
+ passStruct8BytesNestedIntx10Result = result;
+
+ return result;
+}
+
+/// Simple nested struct. No alignment gaps on any architectures.
+/// 10 arguments exhaust registers on all platforms.
+int passStruct8BytesNestedIntx10(
+ Struct8BytesNestedInt a0,
+ Struct8BytesNestedInt a1,
+ Struct8BytesNestedInt a2,
+ Struct8BytesNestedInt a3,
+ Struct8BytesNestedInt a4,
+ Struct8BytesNestedInt a5,
+ Struct8BytesNestedInt a6,
+ Struct8BytesNestedInt a7,
+ Struct8BytesNestedInt a8,
+ Struct8BytesNestedInt a9) {
+ print(
+ "passStruct8BytesNestedIntx10(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9})");
+
+ // In legacy mode, possibly return null.
+ if (a0.a0.a0 == 84) {
+ print("returning null!");
+ return null;
+ }
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+ print("throwing!");
+ throw Exception("PassStruct8BytesNestedIntx10 throwing on purpuse!");
+ }
+
+ passStruct8BytesNestedIntx10_a0 = a0;
+ passStruct8BytesNestedIntx10_a1 = a1;
+ passStruct8BytesNestedIntx10_a2 = a2;
+ passStruct8BytesNestedIntx10_a3 = a3;
+ passStruct8BytesNestedIntx10_a4 = a4;
+ passStruct8BytesNestedIntx10_a5 = a5;
+ passStruct8BytesNestedIntx10_a6 = a6;
+ passStruct8BytesNestedIntx10_a7 = a7;
+ passStruct8BytesNestedIntx10_a8 = a8;
+ passStruct8BytesNestedIntx10_a9 = a9;
+
+ final result = passStruct8BytesNestedIntx10CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passStruct8BytesNestedIntx10AfterCallback() {
+ final result = passStruct8BytesNestedIntx10CalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.equals(20, result);
+}
+
+typedef PassStruct8BytesNestedFloatx10Type = Float Function(
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat);
+
+// Global variables to be able to test inputs after callback returned.
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a0 =
+ Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a1 =
+ Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a2 =
+ Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a3 =
+ Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a4 =
+ Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a5 =
+ Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a6 =
+ Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a7 =
+ Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a8 =
+ Struct8BytesNestedFloat();
+Struct8BytesNestedFloat passStruct8BytesNestedFloatx10_a9 =
+ Struct8BytesNestedFloat();
+
+// Result variable also global, so we can delete it after the callback.
+double passStruct8BytesNestedFloatx10Result = 0.0;
+
+double passStruct8BytesNestedFloatx10CalculateResult() {
+ double result = 0;
+
+ result += passStruct8BytesNestedFloatx10_a0.a0.a0;
+ result += passStruct8BytesNestedFloatx10_a0.a1.a0;
+ result += passStruct8BytesNestedFloatx10_a1.a0.a0;
+ result += passStruct8BytesNestedFloatx10_a1.a1.a0;
+ result += passStruct8BytesNestedFloatx10_a2.a0.a0;
+ result += passStruct8BytesNestedFloatx10_a2.a1.a0;
+ result += passStruct8BytesNestedFloatx10_a3.a0.a0;
+ result += passStruct8BytesNestedFloatx10_a3.a1.a0;
+ result += passStruct8BytesNestedFloatx10_a4.a0.a0;
+ result += passStruct8BytesNestedFloatx10_a4.a1.a0;
+ result += passStruct8BytesNestedFloatx10_a5.a0.a0;
+ result += passStruct8BytesNestedFloatx10_a5.a1.a0;
+ result += passStruct8BytesNestedFloatx10_a6.a0.a0;
+ result += passStruct8BytesNestedFloatx10_a6.a1.a0;
+ result += passStruct8BytesNestedFloatx10_a7.a0.a0;
+ result += passStruct8BytesNestedFloatx10_a7.a1.a0;
+ result += passStruct8BytesNestedFloatx10_a8.a0.a0;
+ result += passStruct8BytesNestedFloatx10_a8.a1.a0;
+ result += passStruct8BytesNestedFloatx10_a9.a0.a0;
+ result += passStruct8BytesNestedFloatx10_a9.a1.a0;
+
+ passStruct8BytesNestedFloatx10Result = result;
+
+ return result;
+}
+
+/// Simple nested struct. No alignment gaps on any architectures.
+/// 10 arguments exhaust fpu registers on all platforms.
+double passStruct8BytesNestedFloatx10(
+ Struct8BytesNestedFloat a0,
+ Struct8BytesNestedFloat a1,
+ Struct8BytesNestedFloat a2,
+ Struct8BytesNestedFloat a3,
+ Struct8BytesNestedFloat a4,
+ Struct8BytesNestedFloat a5,
+ Struct8BytesNestedFloat a6,
+ Struct8BytesNestedFloat a7,
+ Struct8BytesNestedFloat a8,
+ Struct8BytesNestedFloat a9) {
+ print(
+ "passStruct8BytesNestedFloatx10(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9})");
+
+ // In legacy mode, possibly return null.
+ if (a0.a0.a0 == 84) {
+ print("returning null!");
+ return null;
+ }
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+ print("throwing!");
+ throw Exception("PassStruct8BytesNestedFloatx10 throwing on purpuse!");
+ }
+
+ passStruct8BytesNestedFloatx10_a0 = a0;
+ passStruct8BytesNestedFloatx10_a1 = a1;
+ passStruct8BytesNestedFloatx10_a2 = a2;
+ passStruct8BytesNestedFloatx10_a3 = a3;
+ passStruct8BytesNestedFloatx10_a4 = a4;
+ passStruct8BytesNestedFloatx10_a5 = a5;
+ passStruct8BytesNestedFloatx10_a6 = a6;
+ passStruct8BytesNestedFloatx10_a7 = a7;
+ passStruct8BytesNestedFloatx10_a8 = a8;
+ passStruct8BytesNestedFloatx10_a9 = a9;
+
+ final result = passStruct8BytesNestedFloatx10CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passStruct8BytesNestedFloatx10AfterCallback() {
+ final result = passStruct8BytesNestedFloatx10CalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.approxEquals(10.0, result);
+}
+
+typedef PassStruct8BytesNestedFloat2x10Type = Float Function(
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2);
+
+// Global variables to be able to test inputs after callback returned.
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a0 =
+ Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a1 =
+ Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a2 =
+ Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a3 =
+ Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a4 =
+ Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a5 =
+ Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a6 =
+ Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a7 =
+ Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a8 =
+ Struct8BytesNestedFloat2();
+Struct8BytesNestedFloat2 passStruct8BytesNestedFloat2x10_a9 =
+ Struct8BytesNestedFloat2();
+
+// Result variable also global, so we can delete it after the callback.
+double passStruct8BytesNestedFloat2x10Result = 0.0;
+
+double passStruct8BytesNestedFloat2x10CalculateResult() {
+ double result = 0;
+
+ result += passStruct8BytesNestedFloat2x10_a0.a0.a0;
+ result += passStruct8BytesNestedFloat2x10_a0.a1;
+ result += passStruct8BytesNestedFloat2x10_a1.a0.a0;
+ result += passStruct8BytesNestedFloat2x10_a1.a1;
+ result += passStruct8BytesNestedFloat2x10_a2.a0.a0;
+ result += passStruct8BytesNestedFloat2x10_a2.a1;
+ result += passStruct8BytesNestedFloat2x10_a3.a0.a0;
+ result += passStruct8BytesNestedFloat2x10_a3.a1;
+ result += passStruct8BytesNestedFloat2x10_a4.a0.a0;
+ result += passStruct8BytesNestedFloat2x10_a4.a1;
+ result += passStruct8BytesNestedFloat2x10_a5.a0.a0;
+ result += passStruct8BytesNestedFloat2x10_a5.a1;
+ result += passStruct8BytesNestedFloat2x10_a6.a0.a0;
+ result += passStruct8BytesNestedFloat2x10_a6.a1;
+ result += passStruct8BytesNestedFloat2x10_a7.a0.a0;
+ result += passStruct8BytesNestedFloat2x10_a7.a1;
+ result += passStruct8BytesNestedFloat2x10_a8.a0.a0;
+ result += passStruct8BytesNestedFloat2x10_a8.a1;
+ result += passStruct8BytesNestedFloat2x10_a9.a0.a0;
+ result += passStruct8BytesNestedFloat2x10_a9.a1;
+
+ passStruct8BytesNestedFloat2x10Result = result;
+
+ return result;
+}
+
+/// Simple nested struct. No alignment gaps on any architectures.
+/// 10 arguments exhaust fpu registers on all platforms.
+/// The nesting is irregular, testing homogenous float rules on arm and arm64,
+/// and the fpu register usage on x64.
+double passStruct8BytesNestedFloat2x10(
+ Struct8BytesNestedFloat2 a0,
+ Struct8BytesNestedFloat2 a1,
+ Struct8BytesNestedFloat2 a2,
+ Struct8BytesNestedFloat2 a3,
+ Struct8BytesNestedFloat2 a4,
+ Struct8BytesNestedFloat2 a5,
+ Struct8BytesNestedFloat2 a6,
+ Struct8BytesNestedFloat2 a7,
+ Struct8BytesNestedFloat2 a8,
+ Struct8BytesNestedFloat2 a9) {
+ print(
+ "passStruct8BytesNestedFloat2x10(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9})");
+
+ // In legacy mode, possibly return null.
+ if (a0.a0.a0 == 84) {
+ print("returning null!");
+ return null;
+ }
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+ print("throwing!");
+ throw Exception("PassStruct8BytesNestedFloat2x10 throwing on purpuse!");
+ }
+
+ passStruct8BytesNestedFloat2x10_a0 = a0;
+ passStruct8BytesNestedFloat2x10_a1 = a1;
+ passStruct8BytesNestedFloat2x10_a2 = a2;
+ passStruct8BytesNestedFloat2x10_a3 = a3;
+ passStruct8BytesNestedFloat2x10_a4 = a4;
+ passStruct8BytesNestedFloat2x10_a5 = a5;
+ passStruct8BytesNestedFloat2x10_a6 = a6;
+ passStruct8BytesNestedFloat2x10_a7 = a7;
+ passStruct8BytesNestedFloat2x10_a8 = a8;
+ passStruct8BytesNestedFloat2x10_a9 = a9;
+
+ final result = passStruct8BytesNestedFloat2x10CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passStruct8BytesNestedFloat2x10AfterCallback() {
+ final result = passStruct8BytesNestedFloat2x10CalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.approxEquals(10.0, result);
+}
+
+typedef PassStruct8BytesNestedMixedx10Type = Double Function(
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed);
+
+// Global variables to be able to test inputs after callback returned.
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a0 =
+ Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a1 =
+ Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a2 =
+ Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a3 =
+ Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a4 =
+ Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a5 =
+ Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a6 =
+ Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a7 =
+ Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a8 =
+ Struct8BytesNestedMixed();
+Struct8BytesNestedMixed passStruct8BytesNestedMixedx10_a9 =
+ Struct8BytesNestedMixed();
+
+// Result variable also global, so we can delete it after the callback.
+double passStruct8BytesNestedMixedx10Result = 0.0;
+
+double passStruct8BytesNestedMixedx10CalculateResult() {
+ double result = 0;
+
+ result += passStruct8BytesNestedMixedx10_a0.a0.a0;
+ result += passStruct8BytesNestedMixedx10_a0.a0.a1;
+ result += passStruct8BytesNestedMixedx10_a0.a1.a0;
+ result += passStruct8BytesNestedMixedx10_a1.a0.a0;
+ result += passStruct8BytesNestedMixedx10_a1.a0.a1;
+ result += passStruct8BytesNestedMixedx10_a1.a1.a0;
+ result += passStruct8BytesNestedMixedx10_a2.a0.a0;
+ result += passStruct8BytesNestedMixedx10_a2.a0.a1;
+ result += passStruct8BytesNestedMixedx10_a2.a1.a0;
+ result += passStruct8BytesNestedMixedx10_a3.a0.a0;
+ result += passStruct8BytesNestedMixedx10_a3.a0.a1;
+ result += passStruct8BytesNestedMixedx10_a3.a1.a0;
+ result += passStruct8BytesNestedMixedx10_a4.a0.a0;
+ result += passStruct8BytesNestedMixedx10_a4.a0.a1;
+ result += passStruct8BytesNestedMixedx10_a4.a1.a0;
+ result += passStruct8BytesNestedMixedx10_a5.a0.a0;
+ result += passStruct8BytesNestedMixedx10_a5.a0.a1;
+ result += passStruct8BytesNestedMixedx10_a5.a1.a0;
+ result += passStruct8BytesNestedMixedx10_a6.a0.a0;
+ result += passStruct8BytesNestedMixedx10_a6.a0.a1;
+ result += passStruct8BytesNestedMixedx10_a6.a1.a0;
+ result += passStruct8BytesNestedMixedx10_a7.a0.a0;
+ result += passStruct8BytesNestedMixedx10_a7.a0.a1;
+ result += passStruct8BytesNestedMixedx10_a7.a1.a0;
+ result += passStruct8BytesNestedMixedx10_a8.a0.a0;
+ result += passStruct8BytesNestedMixedx10_a8.a0.a1;
+ result += passStruct8BytesNestedMixedx10_a8.a1.a0;
+ result += passStruct8BytesNestedMixedx10_a9.a0.a0;
+ result += passStruct8BytesNestedMixedx10_a9.a0.a1;
+ result += passStruct8BytesNestedMixedx10_a9.a1.a0;
+
+ passStruct8BytesNestedMixedx10Result = result;
+
+ return result;
+}
+
+/// Simple nested struct. No alignment gaps on any architectures.
+/// 10 arguments exhaust all registers on all platforms.
+double passStruct8BytesNestedMixedx10(
+ Struct8BytesNestedMixed a0,
+ Struct8BytesNestedMixed a1,
+ Struct8BytesNestedMixed a2,
+ Struct8BytesNestedMixed a3,
+ Struct8BytesNestedMixed a4,
+ Struct8BytesNestedMixed a5,
+ Struct8BytesNestedMixed a6,
+ Struct8BytesNestedMixed a7,
+ Struct8BytesNestedMixed a8,
+ Struct8BytesNestedMixed a9) {
+ print(
+ "passStruct8BytesNestedMixedx10(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9})");
+
+ // In legacy mode, possibly return null.
+ if (a0.a0.a0 == 84) {
+ print("returning null!");
+ return null;
+ }
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+ print("throwing!");
+ throw Exception("PassStruct8BytesNestedMixedx10 throwing on purpuse!");
+ }
+
+ passStruct8BytesNestedMixedx10_a0 = a0;
+ passStruct8BytesNestedMixedx10_a1 = a1;
+ passStruct8BytesNestedMixedx10_a2 = a2;
+ passStruct8BytesNestedMixedx10_a3 = a3;
+ passStruct8BytesNestedMixedx10_a4 = a4;
+ passStruct8BytesNestedMixedx10_a5 = a5;
+ passStruct8BytesNestedMixedx10_a6 = a6;
+ passStruct8BytesNestedMixedx10_a7 = a7;
+ passStruct8BytesNestedMixedx10_a8 = a8;
+ passStruct8BytesNestedMixedx10_a9 = a9;
+
+ final result = passStruct8BytesNestedMixedx10CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passStruct8BytesNestedMixedx10AfterCallback() {
+ final result = passStruct8BytesNestedMixedx10CalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.approxEquals(15.0, result);
+}
+
+typedef PassStruct16BytesNestedIntx2Type = Int64 Function(
+ Struct16BytesNestedInt, Struct16BytesNestedInt);
+
+// Global variables to be able to test inputs after callback returned.
+Struct16BytesNestedInt passStruct16BytesNestedIntx2_a0 =
+ Struct16BytesNestedInt();
+Struct16BytesNestedInt passStruct16BytesNestedIntx2_a1 =
+ Struct16BytesNestedInt();
+
+// Result variable also global, so we can delete it after the callback.
+int passStruct16BytesNestedIntx2Result = 0;
+
+int passStruct16BytesNestedIntx2CalculateResult() {
+ int result = 0;
+
+ result += passStruct16BytesNestedIntx2_a0.a0.a0.a0;
+ result += passStruct16BytesNestedIntx2_a0.a0.a0.a1;
+ result += passStruct16BytesNestedIntx2_a0.a0.a1.a0;
+ result += passStruct16BytesNestedIntx2_a0.a0.a1.a1;
+ result += passStruct16BytesNestedIntx2_a0.a1.a0.a0;
+ result += passStruct16BytesNestedIntx2_a0.a1.a0.a1;
+ result += passStruct16BytesNestedIntx2_a0.a1.a1.a0;
+ result += passStruct16BytesNestedIntx2_a0.a1.a1.a1;
+ result += passStruct16BytesNestedIntx2_a1.a0.a0.a0;
+ result += passStruct16BytesNestedIntx2_a1.a0.a0.a1;
+ result += passStruct16BytesNestedIntx2_a1.a0.a1.a0;
+ result += passStruct16BytesNestedIntx2_a1.a0.a1.a1;
+ result += passStruct16BytesNestedIntx2_a1.a1.a0.a0;
+ result += passStruct16BytesNestedIntx2_a1.a1.a0.a1;
+ result += passStruct16BytesNestedIntx2_a1.a1.a1.a0;
+ result += passStruct16BytesNestedIntx2_a1.a1.a1.a1;
+
+ passStruct16BytesNestedIntx2Result = result;
+
+ return result;
+}
+
+/// Deeper nested struct to test recursive member access.
+int passStruct16BytesNestedIntx2(
+ Struct16BytesNestedInt a0, Struct16BytesNestedInt a1) {
+ print("passStruct16BytesNestedIntx2(${a0}, ${a1})");
+
+ // In legacy mode, possibly return null.
+ if (a0.a0.a0.a0 == 84) {
+ print("returning null!");
+ return null;
+ }
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0.a0.a0 == 42 || a0.a0.a0.a0 == 84) {
+ print("throwing!");
+ throw Exception("PassStruct16BytesNestedIntx2 throwing on purpuse!");
+ }
+
+ passStruct16BytesNestedIntx2_a0 = a0;
+ passStruct16BytesNestedIntx2_a1 = a1;
+
+ final result = passStruct16BytesNestedIntx2CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passStruct16BytesNestedIntx2AfterCallback() {
+ final result = passStruct16BytesNestedIntx2CalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.equals(8, result);
+}
+
+typedef PassStruct32BytesNestedIntx2Type = Int64 Function(
+ Struct32BytesNestedInt, Struct32BytesNestedInt);
+
+// Global variables to be able to test inputs after callback returned.
+Struct32BytesNestedInt passStruct32BytesNestedIntx2_a0 =
+ Struct32BytesNestedInt();
+Struct32BytesNestedInt passStruct32BytesNestedIntx2_a1 =
+ Struct32BytesNestedInt();
+
+// Result variable also global, so we can delete it after the callback.
+int passStruct32BytesNestedIntx2Result = 0;
+
+int passStruct32BytesNestedIntx2CalculateResult() {
+ int result = 0;
+
+ result += passStruct32BytesNestedIntx2_a0.a0.a0.a0.a0;
+ result += passStruct32BytesNestedIntx2_a0.a0.a0.a0.a1;
+ result += passStruct32BytesNestedIntx2_a0.a0.a0.a1.a0;
+ result += passStruct32BytesNestedIntx2_a0.a0.a0.a1.a1;
+ result += passStruct32BytesNestedIntx2_a0.a0.a1.a0.a0;
+ result += passStruct32BytesNestedIntx2_a0.a0.a1.a0.a1;
+ result += passStruct32BytesNestedIntx2_a0.a0.a1.a1.a0;
+ result += passStruct32BytesNestedIntx2_a0.a0.a1.a1.a1;
+ result += passStruct32BytesNestedIntx2_a0.a1.a0.a0.a0;
+ result += passStruct32BytesNestedIntx2_a0.a1.a0.a0.a1;
+ result += passStruct32BytesNestedIntx2_a0.a1.a0.a1.a0;
+ result += passStruct32BytesNestedIntx2_a0.a1.a0.a1.a1;
+ result += passStruct32BytesNestedIntx2_a0.a1.a1.a0.a0;
+ result += passStruct32BytesNestedIntx2_a0.a1.a1.a0.a1;
+ result += passStruct32BytesNestedIntx2_a0.a1.a1.a1.a0;
+ result += passStruct32BytesNestedIntx2_a0.a1.a1.a1.a1;
+ result += passStruct32BytesNestedIntx2_a1.a0.a0.a0.a0;
+ result += passStruct32BytesNestedIntx2_a1.a0.a0.a0.a1;
+ result += passStruct32BytesNestedIntx2_a1.a0.a0.a1.a0;
+ result += passStruct32BytesNestedIntx2_a1.a0.a0.a1.a1;
+ result += passStruct32BytesNestedIntx2_a1.a0.a1.a0.a0;
+ result += passStruct32BytesNestedIntx2_a1.a0.a1.a0.a1;
+ result += passStruct32BytesNestedIntx2_a1.a0.a1.a1.a0;
+ result += passStruct32BytesNestedIntx2_a1.a0.a1.a1.a1;
+ result += passStruct32BytesNestedIntx2_a1.a1.a0.a0.a0;
+ result += passStruct32BytesNestedIntx2_a1.a1.a0.a0.a1;
+ result += passStruct32BytesNestedIntx2_a1.a1.a0.a1.a0;
+ result += passStruct32BytesNestedIntx2_a1.a1.a0.a1.a1;
+ result += passStruct32BytesNestedIntx2_a1.a1.a1.a0.a0;
+ result += passStruct32BytesNestedIntx2_a1.a1.a1.a0.a1;
+ result += passStruct32BytesNestedIntx2_a1.a1.a1.a1.a0;
+ result += passStruct32BytesNestedIntx2_a1.a1.a1.a1.a1;
+
+ passStruct32BytesNestedIntx2Result = result;
+
+ return result;
+}
+
+/// Even deeper nested struct to test recursive member access.
+int passStruct32BytesNestedIntx2(
+ Struct32BytesNestedInt a0, Struct32BytesNestedInt a1) {
+ print("passStruct32BytesNestedIntx2(${a0}, ${a1})");
+
+ // In legacy mode, possibly return null.
+ if (a0.a0.a0.a0.a0 == 84) {
+ print("returning null!");
+ return null;
+ }
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0.a0.a0.a0 == 42 || a0.a0.a0.a0.a0 == 84) {
+ print("throwing!");
+ throw Exception("PassStruct32BytesNestedIntx2 throwing on purpuse!");
+ }
+
+ passStruct32BytesNestedIntx2_a0 = a0;
+ passStruct32BytesNestedIntx2_a1 = a1;
+
+ final result = passStruct32BytesNestedIntx2CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passStruct32BytesNestedIntx2AfterCallback() {
+ final result = passStruct32BytesNestedIntx2CalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.equals(16, result);
+}
+
+typedef PassStructNestedIntStructAlignmentInt16Type = Int64 Function(
+ StructNestedIntStructAlignmentInt16);
+
+// Global variables to be able to test inputs after callback returned.
+StructNestedIntStructAlignmentInt16 passStructNestedIntStructAlignmentInt16_a0 =
+ StructNestedIntStructAlignmentInt16();
+
+// Result variable also global, so we can delete it after the callback.
+int passStructNestedIntStructAlignmentInt16Result = 0;
+
+int passStructNestedIntStructAlignmentInt16CalculateResult() {
+ int result = 0;
+
+ result += passStructNestedIntStructAlignmentInt16_a0.a0.a0;
+ result += passStructNestedIntStructAlignmentInt16_a0.a0.a1;
+ result += passStructNestedIntStructAlignmentInt16_a0.a0.a2;
+ result += passStructNestedIntStructAlignmentInt16_a0.a1.a0;
+ result += passStructNestedIntStructAlignmentInt16_a0.a1.a1;
+ result += passStructNestedIntStructAlignmentInt16_a0.a1.a2;
+
+ passStructNestedIntStructAlignmentInt16Result = result;
+
+ return result;
+}
+
+/// Test alignment and padding of nested struct with 16 byte int.
+int passStructNestedIntStructAlignmentInt16(
+ StructNestedIntStructAlignmentInt16 a0) {
+ print("passStructNestedIntStructAlignmentInt16(${a0})");
+
+ // In legacy mode, possibly return null.
+ if (a0.a0.a0 == 84) {
+ print("returning null!");
+ return null;
+ }
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+ print("throwing!");
+ throw Exception(
+ "PassStructNestedIntStructAlignmentInt16 throwing on purpuse!");
+ }
+
+ passStructNestedIntStructAlignmentInt16_a0 = a0;
+
+ final result = passStructNestedIntStructAlignmentInt16CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passStructNestedIntStructAlignmentInt16AfterCallback() {
+ final result = passStructNestedIntStructAlignmentInt16CalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.equals(3, result);
+}
+
+typedef PassStructNestedIntStructAlignmentInt32Type = Int64 Function(
+ StructNestedIntStructAlignmentInt32);
+
+// Global variables to be able to test inputs after callback returned.
+StructNestedIntStructAlignmentInt32 passStructNestedIntStructAlignmentInt32_a0 =
+ StructNestedIntStructAlignmentInt32();
+
+// Result variable also global, so we can delete it after the callback.
+int passStructNestedIntStructAlignmentInt32Result = 0;
+
+int passStructNestedIntStructAlignmentInt32CalculateResult() {
+ int result = 0;
+
+ result += passStructNestedIntStructAlignmentInt32_a0.a0.a0;
+ result += passStructNestedIntStructAlignmentInt32_a0.a0.a1;
+ result += passStructNestedIntStructAlignmentInt32_a0.a0.a2;
+ result += passStructNestedIntStructAlignmentInt32_a0.a1.a0;
+ result += passStructNestedIntStructAlignmentInt32_a0.a1.a1;
+ result += passStructNestedIntStructAlignmentInt32_a0.a1.a2;
+
+ passStructNestedIntStructAlignmentInt32Result = result;
+
+ return result;
+}
+
+/// Test alignment and padding of nested struct with 32 byte int.
+int passStructNestedIntStructAlignmentInt32(
+ StructNestedIntStructAlignmentInt32 a0) {
+ print("passStructNestedIntStructAlignmentInt32(${a0})");
+
+ // In legacy mode, possibly return null.
+ if (a0.a0.a0 == 84) {
+ print("returning null!");
+ return null;
+ }
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+ print("throwing!");
+ throw Exception(
+ "PassStructNestedIntStructAlignmentInt32 throwing on purpuse!");
+ }
+
+ passStructNestedIntStructAlignmentInt32_a0 = a0;
+
+ final result = passStructNestedIntStructAlignmentInt32CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passStructNestedIntStructAlignmentInt32AfterCallback() {
+ final result = passStructNestedIntStructAlignmentInt32CalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.equals(3, result);
+}
+
+typedef PassStructNestedIntStructAlignmentInt64Type = Int64 Function(
+ StructNestedIntStructAlignmentInt64);
+
+// Global variables to be able to test inputs after callback returned.
+StructNestedIntStructAlignmentInt64 passStructNestedIntStructAlignmentInt64_a0 =
+ StructNestedIntStructAlignmentInt64();
+
+// Result variable also global, so we can delete it after the callback.
+int passStructNestedIntStructAlignmentInt64Result = 0;
+
+int passStructNestedIntStructAlignmentInt64CalculateResult() {
+ int result = 0;
+
+ result += passStructNestedIntStructAlignmentInt64_a0.a0.a0;
+ result += passStructNestedIntStructAlignmentInt64_a0.a0.a1;
+ result += passStructNestedIntStructAlignmentInt64_a0.a0.a2;
+ result += passStructNestedIntStructAlignmentInt64_a0.a1.a0;
+ result += passStructNestedIntStructAlignmentInt64_a0.a1.a1;
+ result += passStructNestedIntStructAlignmentInt64_a0.a1.a2;
+
+ passStructNestedIntStructAlignmentInt64Result = result;
+
+ return result;
+}
+
+/// Test alignment and padding of nested struct with 64 byte int.
+int passStructNestedIntStructAlignmentInt64(
+ StructNestedIntStructAlignmentInt64 a0) {
+ print("passStructNestedIntStructAlignmentInt64(${a0})");
+
+ // In legacy mode, possibly return null.
+ if (a0.a0.a0 == 84) {
+ print("returning null!");
+ return null;
+ }
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+ print("throwing!");
+ throw Exception(
+ "PassStructNestedIntStructAlignmentInt64 throwing on purpuse!");
+ }
+
+ passStructNestedIntStructAlignmentInt64_a0 = a0;
+
+ final result = passStructNestedIntStructAlignmentInt64CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passStructNestedIntStructAlignmentInt64AfterCallback() {
+ final result = passStructNestedIntStructAlignmentInt64CalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.equals(3, result);
+}
+
+typedef PassStructNestedIrregularEvenBiggerx4Type = Double Function(
+ StructNestedIrregularEvenBigger,
+ StructNestedIrregularEvenBigger,
+ StructNestedIrregularEvenBigger,
+ StructNestedIrregularEvenBigger);
+
+// Global variables to be able to test inputs after callback returned.
+StructNestedIrregularEvenBigger passStructNestedIrregularEvenBiggerx4_a0 =
+ StructNestedIrregularEvenBigger();
+StructNestedIrregularEvenBigger passStructNestedIrregularEvenBiggerx4_a1 =
+ StructNestedIrregularEvenBigger();
+StructNestedIrregularEvenBigger passStructNestedIrregularEvenBiggerx4_a2 =
+ StructNestedIrregularEvenBigger();
+StructNestedIrregularEvenBigger passStructNestedIrregularEvenBiggerx4_a3 =
+ StructNestedIrregularEvenBigger();
+
+// Result variable also global, so we can delete it after the callback.
+double passStructNestedIrregularEvenBiggerx4Result = 0.0;
+
+double passStructNestedIrregularEvenBiggerx4CalculateResult() {
+ double result = 0;
+
+ result += passStructNestedIrregularEvenBiggerx4_a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a1.a0.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a1.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a2;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a3.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a3.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a4;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a5.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a5.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a1.a0.a6;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a1.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a1.a1.a0.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a1.a1.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a1.a2;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a1.a3;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a1.a0.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a1.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a2;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a3.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a3.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a4;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a5.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a5.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a2.a0.a6;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a2.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a2.a1.a0.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a2.a1.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a2.a2;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a2.a3;
+ result += passStructNestedIrregularEvenBiggerx4_a0.a3;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a1.a0.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a1.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a2;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a3.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a3.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a4;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a5.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a5.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a1.a0.a6;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a1.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a1.a1.a0.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a1.a1.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a1.a2;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a1.a3;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a1.a0.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a1.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a2;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a3.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a3.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a4;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a5.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a5.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a2.a0.a6;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a2.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a2.a1.a0.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a2.a1.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a2.a2;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a2.a3;
+ result += passStructNestedIrregularEvenBiggerx4_a1.a3;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a1.a0.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a1.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a2;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a3.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a3.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a4;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a5.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a5.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a1.a0.a6;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a1.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a1.a1.a0.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a1.a1.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a1.a2;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a1.a3;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a1.a0.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a1.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a2;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a3.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a3.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a4;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a5.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a5.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a2.a0.a6;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a2.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a2.a1.a0.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a2.a1.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a2.a2;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a2.a3;
+ result += passStructNestedIrregularEvenBiggerx4_a2.a3;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a1.a0.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a1.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a2;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a3.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a3.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a4;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a5.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a5.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a1.a0.a6;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a1.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a1.a1.a0.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a1.a1.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a1.a2;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a1.a3;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a1.a0.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a1.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a2;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a3.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a3.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a4;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a5.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a5.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a2.a0.a6;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a2.a1.a0.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a2.a1.a0.a1;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a2.a1.a1.a0;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a2.a2;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a2.a3;
+ result += passStructNestedIrregularEvenBiggerx4_a3.a3;
+
+ passStructNestedIrregularEvenBiggerx4Result = result;
+
+ return result;
+}
+
+/// Return big irregular struct as smoke test.
+double passStructNestedIrregularEvenBiggerx4(
+ StructNestedIrregularEvenBigger a0,
+ StructNestedIrregularEvenBigger a1,
+ StructNestedIrregularEvenBigger a2,
+ StructNestedIrregularEvenBigger a3) {
+ print("passStructNestedIrregularEvenBiggerx4(${a0}, ${a1}, ${a2}, ${a3})");
+
+ // In legacy mode, possibly return null.
+ if (a0.a0 == 84) {
+ print("returning null!");
+ return null;
+ }
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0 == 42 || a0.a0 == 84) {
+ print("throwing!");
+ throw Exception(
+ "PassStructNestedIrregularEvenBiggerx4 throwing on purpuse!");
+ }
+
+ passStructNestedIrregularEvenBiggerx4_a0 = a0;
+ passStructNestedIrregularEvenBiggerx4_a1 = a1;
+ passStructNestedIrregularEvenBiggerx4_a2 = a2;
+ passStructNestedIrregularEvenBiggerx4_a3 = a3;
+
+ final result = passStructNestedIrregularEvenBiggerx4CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passStructNestedIrregularEvenBiggerx4AfterCallback() {
+ final result = passStructNestedIrregularEvenBiggerx4CalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.approxEquals(1572.0, result);
+}
+
typedef ReturnStruct1ByteIntType = Struct1ByteInt Function(Int8);
// Global variables to be able to test inputs after callback returned.
@@ -6914,3 +8061,697 @@
free(returnStructAlignmentInt64Result.addressOf);
}
+
+typedef ReturnStruct8BytesNestedIntType = Struct8BytesNestedInt Function(
+ Struct4BytesHomogeneousInt16, Struct4BytesHomogeneousInt16);
+
+// Global variables to be able to test inputs after callback returned.
+Struct4BytesHomogeneousInt16 returnStruct8BytesNestedInt_a0 =
+ Struct4BytesHomogeneousInt16();
+Struct4BytesHomogeneousInt16 returnStruct8BytesNestedInt_a1 =
+ Struct4BytesHomogeneousInt16();
+
+// Result variable also global, so we can delete it after the callback.
+Struct8BytesNestedInt returnStruct8BytesNestedIntResult =
+ Struct8BytesNestedInt();
+
+Struct8BytesNestedInt returnStruct8BytesNestedIntCalculateResult() {
+ Struct8BytesNestedInt result = allocate<Struct8BytesNestedInt>().ref;
+
+ result.a0.a0 = returnStruct8BytesNestedInt_a0.a0;
+ result.a0.a1 = returnStruct8BytesNestedInt_a0.a1;
+ result.a1.a0 = returnStruct8BytesNestedInt_a1.a0;
+ result.a1.a1 = returnStruct8BytesNestedInt_a1.a1;
+
+ returnStruct8BytesNestedIntResult = result;
+
+ return result;
+}
+
+/// Simple nested struct.
+Struct8BytesNestedInt returnStruct8BytesNestedInt(
+ Struct4BytesHomogeneousInt16 a0, Struct4BytesHomogeneousInt16 a1) {
+ print("returnStruct8BytesNestedInt(${a0}, ${a1})");
+
+ // In legacy mode, possibly return null.
+ if (a0.a0 == 84) {
+ print("returning null!");
+ return null;
+ }
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0 == 42 || a0.a0 == 84) {
+ print("throwing!");
+ throw Exception("ReturnStruct8BytesNestedInt throwing on purpuse!");
+ }
+
+ returnStruct8BytesNestedInt_a0 = a0;
+ returnStruct8BytesNestedInt_a1 = a1;
+
+ final result = returnStruct8BytesNestedIntCalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void returnStruct8BytesNestedIntAfterCallback() {
+ free(returnStruct8BytesNestedIntResult.addressOf);
+
+ final result = returnStruct8BytesNestedIntCalculateResult();
+
+ print("after callback result = $result");
+
+ free(returnStruct8BytesNestedIntResult.addressOf);
+}
+
+typedef ReturnStruct8BytesNestedFloatType = Struct8BytesNestedFloat Function(
+ Struct4BytesFloat, Struct4BytesFloat);
+
+// Global variables to be able to test inputs after callback returned.
+Struct4BytesFloat returnStruct8BytesNestedFloat_a0 = Struct4BytesFloat();
+Struct4BytesFloat returnStruct8BytesNestedFloat_a1 = Struct4BytesFloat();
+
+// Result variable also global, so we can delete it after the callback.
+Struct8BytesNestedFloat returnStruct8BytesNestedFloatResult =
+ Struct8BytesNestedFloat();
+
+Struct8BytesNestedFloat returnStruct8BytesNestedFloatCalculateResult() {
+ Struct8BytesNestedFloat result = allocate<Struct8BytesNestedFloat>().ref;
+
+ result.a0.a0 = returnStruct8BytesNestedFloat_a0.a0;
+ result.a1.a0 = returnStruct8BytesNestedFloat_a1.a0;
+
+ returnStruct8BytesNestedFloatResult = result;
+
+ return result;
+}
+
+/// Simple nested struct with floats.
+Struct8BytesNestedFloat returnStruct8BytesNestedFloat(
+ Struct4BytesFloat a0, Struct4BytesFloat a1) {
+ print("returnStruct8BytesNestedFloat(${a0}, ${a1})");
+
+ // In legacy mode, possibly return null.
+ if (a0.a0 == 84) {
+ print("returning null!");
+ return null;
+ }
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0 == 42 || a0.a0 == 84) {
+ print("throwing!");
+ throw Exception("ReturnStruct8BytesNestedFloat throwing on purpuse!");
+ }
+
+ returnStruct8BytesNestedFloat_a0 = a0;
+ returnStruct8BytesNestedFloat_a1 = a1;
+
+ final result = returnStruct8BytesNestedFloatCalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void returnStruct8BytesNestedFloatAfterCallback() {
+ free(returnStruct8BytesNestedFloatResult.addressOf);
+
+ final result = returnStruct8BytesNestedFloatCalculateResult();
+
+ print("after callback result = $result");
+
+ free(returnStruct8BytesNestedFloatResult.addressOf);
+}
+
+typedef ReturnStruct8BytesNestedFloat2Type = Struct8BytesNestedFloat2 Function(
+ Struct4BytesFloat, Float);
+
+// Global variables to be able to test inputs after callback returned.
+Struct4BytesFloat returnStruct8BytesNestedFloat2_a0 = Struct4BytesFloat();
+double returnStruct8BytesNestedFloat2_a1 = 0.0;
+
+// Result variable also global, so we can delete it after the callback.
+Struct8BytesNestedFloat2 returnStruct8BytesNestedFloat2Result =
+ Struct8BytesNestedFloat2();
+
+Struct8BytesNestedFloat2 returnStruct8BytesNestedFloat2CalculateResult() {
+ Struct8BytesNestedFloat2 result = allocate<Struct8BytesNestedFloat2>().ref;
+
+ result.a0.a0 = returnStruct8BytesNestedFloat2_a0.a0;
+ result.a1 = returnStruct8BytesNestedFloat2_a1;
+
+ returnStruct8BytesNestedFloat2Result = result;
+
+ return result;
+}
+
+/// The nesting is irregular, testing homogenous float rules on arm and arm64,
+/// and the fpu register usage on x64.
+Struct8BytesNestedFloat2 returnStruct8BytesNestedFloat2(
+ Struct4BytesFloat a0, double a1) {
+ print("returnStruct8BytesNestedFloat2(${a0}, ${a1})");
+
+ // In legacy mode, possibly return null.
+ if (a0.a0 == 84) {
+ print("returning null!");
+ return null;
+ }
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0 == 42 || a0.a0 == 84) {
+ print("throwing!");
+ throw Exception("ReturnStruct8BytesNestedFloat2 throwing on purpuse!");
+ }
+
+ returnStruct8BytesNestedFloat2_a0 = a0;
+ returnStruct8BytesNestedFloat2_a1 = a1;
+
+ final result = returnStruct8BytesNestedFloat2CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void returnStruct8BytesNestedFloat2AfterCallback() {
+ free(returnStruct8BytesNestedFloat2Result.addressOf);
+
+ final result = returnStruct8BytesNestedFloat2CalculateResult();
+
+ print("after callback result = $result");
+
+ free(returnStruct8BytesNestedFloat2Result.addressOf);
+}
+
+typedef ReturnStruct8BytesNestedMixedType = Struct8BytesNestedMixed Function(
+ Struct4BytesHomogeneousInt16, Struct4BytesFloat);
+
+// Global variables to be able to test inputs after callback returned.
+Struct4BytesHomogeneousInt16 returnStruct8BytesNestedMixed_a0 =
+ Struct4BytesHomogeneousInt16();
+Struct4BytesFloat returnStruct8BytesNestedMixed_a1 = Struct4BytesFloat();
+
+// Result variable also global, so we can delete it after the callback.
+Struct8BytesNestedMixed returnStruct8BytesNestedMixedResult =
+ Struct8BytesNestedMixed();
+
+Struct8BytesNestedMixed returnStruct8BytesNestedMixedCalculateResult() {
+ Struct8BytesNestedMixed result = allocate<Struct8BytesNestedMixed>().ref;
+
+ result.a0.a0 = returnStruct8BytesNestedMixed_a0.a0;
+ result.a0.a1 = returnStruct8BytesNestedMixed_a0.a1;
+ result.a1.a0 = returnStruct8BytesNestedMixed_a1.a0;
+
+ returnStruct8BytesNestedMixedResult = result;
+
+ return result;
+}
+
+/// Simple nested struct with mixed members.
+Struct8BytesNestedMixed returnStruct8BytesNestedMixed(
+ Struct4BytesHomogeneousInt16 a0, Struct4BytesFloat a1) {
+ print("returnStruct8BytesNestedMixed(${a0}, ${a1})");
+
+ // In legacy mode, possibly return null.
+ if (a0.a0 == 84) {
+ print("returning null!");
+ return null;
+ }
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0 == 42 || a0.a0 == 84) {
+ print("throwing!");
+ throw Exception("ReturnStruct8BytesNestedMixed throwing on purpuse!");
+ }
+
+ returnStruct8BytesNestedMixed_a0 = a0;
+ returnStruct8BytesNestedMixed_a1 = a1;
+
+ final result = returnStruct8BytesNestedMixedCalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void returnStruct8BytesNestedMixedAfterCallback() {
+ free(returnStruct8BytesNestedMixedResult.addressOf);
+
+ final result = returnStruct8BytesNestedMixedCalculateResult();
+
+ print("after callback result = $result");
+
+ free(returnStruct8BytesNestedMixedResult.addressOf);
+}
+
+typedef ReturnStruct16BytesNestedIntType = Struct16BytesNestedInt Function(
+ Struct8BytesNestedInt, Struct8BytesNestedInt);
+
+// Global variables to be able to test inputs after callback returned.
+Struct8BytesNestedInt returnStruct16BytesNestedInt_a0 = Struct8BytesNestedInt();
+Struct8BytesNestedInt returnStruct16BytesNestedInt_a1 = Struct8BytesNestedInt();
+
+// Result variable also global, so we can delete it after the callback.
+Struct16BytesNestedInt returnStruct16BytesNestedIntResult =
+ Struct16BytesNestedInt();
+
+Struct16BytesNestedInt returnStruct16BytesNestedIntCalculateResult() {
+ Struct16BytesNestedInt result = allocate<Struct16BytesNestedInt>().ref;
+
+ result.a0.a0.a0 = returnStruct16BytesNestedInt_a0.a0.a0;
+ result.a0.a0.a1 = returnStruct16BytesNestedInt_a0.a0.a1;
+ result.a0.a1.a0 = returnStruct16BytesNestedInt_a0.a1.a0;
+ result.a0.a1.a1 = returnStruct16BytesNestedInt_a0.a1.a1;
+ result.a1.a0.a0 = returnStruct16BytesNestedInt_a1.a0.a0;
+ result.a1.a0.a1 = returnStruct16BytesNestedInt_a1.a0.a1;
+ result.a1.a1.a0 = returnStruct16BytesNestedInt_a1.a1.a0;
+ result.a1.a1.a1 = returnStruct16BytesNestedInt_a1.a1.a1;
+
+ returnStruct16BytesNestedIntResult = result;
+
+ return result;
+}
+
+/// Deeper nested struct to test recursive member access.
+Struct16BytesNestedInt returnStruct16BytesNestedInt(
+ Struct8BytesNestedInt a0, Struct8BytesNestedInt a1) {
+ print("returnStruct16BytesNestedInt(${a0}, ${a1})");
+
+ // In legacy mode, possibly return null.
+ if (a0.a0.a0 == 84) {
+ print("returning null!");
+ return null;
+ }
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0.a0 == 42 || a0.a0.a0 == 84) {
+ print("throwing!");
+ throw Exception("ReturnStruct16BytesNestedInt throwing on purpuse!");
+ }
+
+ returnStruct16BytesNestedInt_a0 = a0;
+ returnStruct16BytesNestedInt_a1 = a1;
+
+ final result = returnStruct16BytesNestedIntCalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void returnStruct16BytesNestedIntAfterCallback() {
+ free(returnStruct16BytesNestedIntResult.addressOf);
+
+ final result = returnStruct16BytesNestedIntCalculateResult();
+
+ print("after callback result = $result");
+
+ free(returnStruct16BytesNestedIntResult.addressOf);
+}
+
+typedef ReturnStruct32BytesNestedIntType = Struct32BytesNestedInt Function(
+ Struct16BytesNestedInt, Struct16BytesNestedInt);
+
+// Global variables to be able to test inputs after callback returned.
+Struct16BytesNestedInt returnStruct32BytesNestedInt_a0 =
+ Struct16BytesNestedInt();
+Struct16BytesNestedInt returnStruct32BytesNestedInt_a1 =
+ Struct16BytesNestedInt();
+
+// Result variable also global, so we can delete it after the callback.
+Struct32BytesNestedInt returnStruct32BytesNestedIntResult =
+ Struct32BytesNestedInt();
+
+Struct32BytesNestedInt returnStruct32BytesNestedIntCalculateResult() {
+ Struct32BytesNestedInt result = allocate<Struct32BytesNestedInt>().ref;
+
+ result.a0.a0.a0.a0 = returnStruct32BytesNestedInt_a0.a0.a0.a0;
+ result.a0.a0.a0.a1 = returnStruct32BytesNestedInt_a0.a0.a0.a1;
+ result.a0.a0.a1.a0 = returnStruct32BytesNestedInt_a0.a0.a1.a0;
+ result.a0.a0.a1.a1 = returnStruct32BytesNestedInt_a0.a0.a1.a1;
+ result.a0.a1.a0.a0 = returnStruct32BytesNestedInt_a0.a1.a0.a0;
+ result.a0.a1.a0.a1 = returnStruct32BytesNestedInt_a0.a1.a0.a1;
+ result.a0.a1.a1.a0 = returnStruct32BytesNestedInt_a0.a1.a1.a0;
+ result.a0.a1.a1.a1 = returnStruct32BytesNestedInt_a0.a1.a1.a1;
+ result.a1.a0.a0.a0 = returnStruct32BytesNestedInt_a1.a0.a0.a0;
+ result.a1.a0.a0.a1 = returnStruct32BytesNestedInt_a1.a0.a0.a1;
+ result.a1.a0.a1.a0 = returnStruct32BytesNestedInt_a1.a0.a1.a0;
+ result.a1.a0.a1.a1 = returnStruct32BytesNestedInt_a1.a0.a1.a1;
+ result.a1.a1.a0.a0 = returnStruct32BytesNestedInt_a1.a1.a0.a0;
+ result.a1.a1.a0.a1 = returnStruct32BytesNestedInt_a1.a1.a0.a1;
+ result.a1.a1.a1.a0 = returnStruct32BytesNestedInt_a1.a1.a1.a0;
+ result.a1.a1.a1.a1 = returnStruct32BytesNestedInt_a1.a1.a1.a1;
+
+ returnStruct32BytesNestedIntResult = result;
+
+ return result;
+}
+
+/// Even deeper nested struct to test recursive member access.
+Struct32BytesNestedInt returnStruct32BytesNestedInt(
+ Struct16BytesNestedInt a0, Struct16BytesNestedInt a1) {
+ print("returnStruct32BytesNestedInt(${a0}, ${a1})");
+
+ // In legacy mode, possibly return null.
+ if (a0.a0.a0.a0 == 84) {
+ print("returning null!");
+ return null;
+ }
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0.a0.a0 == 42 || a0.a0.a0.a0 == 84) {
+ print("throwing!");
+ throw Exception("ReturnStruct32BytesNestedInt throwing on purpuse!");
+ }
+
+ returnStruct32BytesNestedInt_a0 = a0;
+ returnStruct32BytesNestedInt_a1 = a1;
+
+ final result = returnStruct32BytesNestedIntCalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void returnStruct32BytesNestedIntAfterCallback() {
+ free(returnStruct32BytesNestedIntResult.addressOf);
+
+ final result = returnStruct32BytesNestedIntCalculateResult();
+
+ print("after callback result = $result");
+
+ free(returnStruct32BytesNestedIntResult.addressOf);
+}
+
+typedef ReturnStructNestedIntStructAlignmentInt16Type
+ = StructNestedIntStructAlignmentInt16 Function(
+ StructAlignmentInt16, StructAlignmentInt16);
+
+// Global variables to be able to test inputs after callback returned.
+StructAlignmentInt16 returnStructNestedIntStructAlignmentInt16_a0 =
+ StructAlignmentInt16();
+StructAlignmentInt16 returnStructNestedIntStructAlignmentInt16_a1 =
+ StructAlignmentInt16();
+
+// Result variable also global, so we can delete it after the callback.
+StructNestedIntStructAlignmentInt16
+ returnStructNestedIntStructAlignmentInt16Result =
+ StructNestedIntStructAlignmentInt16();
+
+StructNestedIntStructAlignmentInt16
+ returnStructNestedIntStructAlignmentInt16CalculateResult() {
+ StructNestedIntStructAlignmentInt16 result =
+ allocate<StructNestedIntStructAlignmentInt16>().ref;
+
+ result.a0.a0 = returnStructNestedIntStructAlignmentInt16_a0.a0;
+ result.a0.a1 = returnStructNestedIntStructAlignmentInt16_a0.a1;
+ result.a0.a2 = returnStructNestedIntStructAlignmentInt16_a0.a2;
+ result.a1.a0 = returnStructNestedIntStructAlignmentInt16_a1.a0;
+ result.a1.a1 = returnStructNestedIntStructAlignmentInt16_a1.a1;
+ result.a1.a2 = returnStructNestedIntStructAlignmentInt16_a1.a2;
+
+ returnStructNestedIntStructAlignmentInt16Result = result;
+
+ return result;
+}
+
+/// Test alignment and padding of nested struct with 16 byte int.
+StructNestedIntStructAlignmentInt16 returnStructNestedIntStructAlignmentInt16(
+ StructAlignmentInt16 a0, StructAlignmentInt16 a1) {
+ print("returnStructNestedIntStructAlignmentInt16(${a0}, ${a1})");
+
+ // In legacy mode, possibly return null.
+ if (a0.a0 == 84) {
+ print("returning null!");
+ return null;
+ }
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0 == 42 || a0.a0 == 84) {
+ print("throwing!");
+ throw Exception(
+ "ReturnStructNestedIntStructAlignmentInt16 throwing on purpuse!");
+ }
+
+ returnStructNestedIntStructAlignmentInt16_a0 = a0;
+ returnStructNestedIntStructAlignmentInt16_a1 = a1;
+
+ final result = returnStructNestedIntStructAlignmentInt16CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void returnStructNestedIntStructAlignmentInt16AfterCallback() {
+ free(returnStructNestedIntStructAlignmentInt16Result.addressOf);
+
+ final result = returnStructNestedIntStructAlignmentInt16CalculateResult();
+
+ print("after callback result = $result");
+
+ free(returnStructNestedIntStructAlignmentInt16Result.addressOf);
+}
+
+typedef ReturnStructNestedIntStructAlignmentInt32Type
+ = StructNestedIntStructAlignmentInt32 Function(
+ StructAlignmentInt32, StructAlignmentInt32);
+
+// Global variables to be able to test inputs after callback returned.
+StructAlignmentInt32 returnStructNestedIntStructAlignmentInt32_a0 =
+ StructAlignmentInt32();
+StructAlignmentInt32 returnStructNestedIntStructAlignmentInt32_a1 =
+ StructAlignmentInt32();
+
+// Result variable also global, so we can delete it after the callback.
+StructNestedIntStructAlignmentInt32
+ returnStructNestedIntStructAlignmentInt32Result =
+ StructNestedIntStructAlignmentInt32();
+
+StructNestedIntStructAlignmentInt32
+ returnStructNestedIntStructAlignmentInt32CalculateResult() {
+ StructNestedIntStructAlignmentInt32 result =
+ allocate<StructNestedIntStructAlignmentInt32>().ref;
+
+ result.a0.a0 = returnStructNestedIntStructAlignmentInt32_a0.a0;
+ result.a0.a1 = returnStructNestedIntStructAlignmentInt32_a0.a1;
+ result.a0.a2 = returnStructNestedIntStructAlignmentInt32_a0.a2;
+ result.a1.a0 = returnStructNestedIntStructAlignmentInt32_a1.a0;
+ result.a1.a1 = returnStructNestedIntStructAlignmentInt32_a1.a1;
+ result.a1.a2 = returnStructNestedIntStructAlignmentInt32_a1.a2;
+
+ returnStructNestedIntStructAlignmentInt32Result = result;
+
+ return result;
+}
+
+/// Test alignment and padding of nested struct with 32 byte int.
+StructNestedIntStructAlignmentInt32 returnStructNestedIntStructAlignmentInt32(
+ StructAlignmentInt32 a0, StructAlignmentInt32 a1) {
+ print("returnStructNestedIntStructAlignmentInt32(${a0}, ${a1})");
+
+ // In legacy mode, possibly return null.
+ if (a0.a0 == 84) {
+ print("returning null!");
+ return null;
+ }
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0 == 42 || a0.a0 == 84) {
+ print("throwing!");
+ throw Exception(
+ "ReturnStructNestedIntStructAlignmentInt32 throwing on purpuse!");
+ }
+
+ returnStructNestedIntStructAlignmentInt32_a0 = a0;
+ returnStructNestedIntStructAlignmentInt32_a1 = a1;
+
+ final result = returnStructNestedIntStructAlignmentInt32CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void returnStructNestedIntStructAlignmentInt32AfterCallback() {
+ free(returnStructNestedIntStructAlignmentInt32Result.addressOf);
+
+ final result = returnStructNestedIntStructAlignmentInt32CalculateResult();
+
+ print("after callback result = $result");
+
+ free(returnStructNestedIntStructAlignmentInt32Result.addressOf);
+}
+
+typedef ReturnStructNestedIntStructAlignmentInt64Type
+ = StructNestedIntStructAlignmentInt64 Function(
+ StructAlignmentInt64, StructAlignmentInt64);
+
+// Global variables to be able to test inputs after callback returned.
+StructAlignmentInt64 returnStructNestedIntStructAlignmentInt64_a0 =
+ StructAlignmentInt64();
+StructAlignmentInt64 returnStructNestedIntStructAlignmentInt64_a1 =
+ StructAlignmentInt64();
+
+// Result variable also global, so we can delete it after the callback.
+StructNestedIntStructAlignmentInt64
+ returnStructNestedIntStructAlignmentInt64Result =
+ StructNestedIntStructAlignmentInt64();
+
+StructNestedIntStructAlignmentInt64
+ returnStructNestedIntStructAlignmentInt64CalculateResult() {
+ StructNestedIntStructAlignmentInt64 result =
+ allocate<StructNestedIntStructAlignmentInt64>().ref;
+
+ result.a0.a0 = returnStructNestedIntStructAlignmentInt64_a0.a0;
+ result.a0.a1 = returnStructNestedIntStructAlignmentInt64_a0.a1;
+ result.a0.a2 = returnStructNestedIntStructAlignmentInt64_a0.a2;
+ result.a1.a0 = returnStructNestedIntStructAlignmentInt64_a1.a0;
+ result.a1.a1 = returnStructNestedIntStructAlignmentInt64_a1.a1;
+ result.a1.a2 = returnStructNestedIntStructAlignmentInt64_a1.a2;
+
+ returnStructNestedIntStructAlignmentInt64Result = result;
+
+ return result;
+}
+
+/// Test alignment and padding of nested struct with 64 byte int.
+StructNestedIntStructAlignmentInt64 returnStructNestedIntStructAlignmentInt64(
+ StructAlignmentInt64 a0, StructAlignmentInt64 a1) {
+ print("returnStructNestedIntStructAlignmentInt64(${a0}, ${a1})");
+
+ // In legacy mode, possibly return null.
+ if (a0.a0 == 84) {
+ print("returning null!");
+ return null;
+ }
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0 == 42 || a0.a0 == 84) {
+ print("throwing!");
+ throw Exception(
+ "ReturnStructNestedIntStructAlignmentInt64 throwing on purpuse!");
+ }
+
+ returnStructNestedIntStructAlignmentInt64_a0 = a0;
+ returnStructNestedIntStructAlignmentInt64_a1 = a1;
+
+ final result = returnStructNestedIntStructAlignmentInt64CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void returnStructNestedIntStructAlignmentInt64AfterCallback() {
+ free(returnStructNestedIntStructAlignmentInt64Result.addressOf);
+
+ final result = returnStructNestedIntStructAlignmentInt64CalculateResult();
+
+ print("after callback result = $result");
+
+ free(returnStructNestedIntStructAlignmentInt64Result.addressOf);
+}
+
+typedef ReturnStructNestedIrregularEvenBiggerType
+ = StructNestedIrregularEvenBigger Function(Uint64,
+ StructNestedIrregularBigger, StructNestedIrregularBigger, Double);
+
+// Global variables to be able to test inputs after callback returned.
+int returnStructNestedIrregularEvenBigger_a0 = 0;
+StructNestedIrregularBigger returnStructNestedIrregularEvenBigger_a1 =
+ StructNestedIrregularBigger();
+StructNestedIrregularBigger returnStructNestedIrregularEvenBigger_a2 =
+ StructNestedIrregularBigger();
+double returnStructNestedIrregularEvenBigger_a3 = 0.0;
+
+// Result variable also global, so we can delete it after the callback.
+StructNestedIrregularEvenBigger returnStructNestedIrregularEvenBiggerResult =
+ StructNestedIrregularEvenBigger();
+
+StructNestedIrregularEvenBigger
+ returnStructNestedIrregularEvenBiggerCalculateResult() {
+ StructNestedIrregularEvenBigger result =
+ allocate<StructNestedIrregularEvenBigger>().ref;
+
+ result.a0 = returnStructNestedIrregularEvenBigger_a0;
+ result.a1.a0.a0 = returnStructNestedIrregularEvenBigger_a1.a0.a0;
+ result.a1.a0.a1.a0.a0 = returnStructNestedIrregularEvenBigger_a1.a0.a1.a0.a0;
+ result.a1.a0.a1.a0.a1 = returnStructNestedIrregularEvenBigger_a1.a0.a1.a0.a1;
+ result.a1.a0.a1.a1.a0 = returnStructNestedIrregularEvenBigger_a1.a0.a1.a1.a0;
+ result.a1.a0.a2 = returnStructNestedIrregularEvenBigger_a1.a0.a2;
+ result.a1.a0.a3.a0.a0 = returnStructNestedIrregularEvenBigger_a1.a0.a3.a0.a0;
+ result.a1.a0.a3.a1 = returnStructNestedIrregularEvenBigger_a1.a0.a3.a1;
+ result.a1.a0.a4 = returnStructNestedIrregularEvenBigger_a1.a0.a4;
+ result.a1.a0.a5.a0.a0 = returnStructNestedIrregularEvenBigger_a1.a0.a5.a0.a0;
+ result.a1.a0.a5.a1.a0 = returnStructNestedIrregularEvenBigger_a1.a0.a5.a1.a0;
+ result.a1.a0.a6 = returnStructNestedIrregularEvenBigger_a1.a0.a6;
+ result.a1.a1.a0.a0 = returnStructNestedIrregularEvenBigger_a1.a1.a0.a0;
+ result.a1.a1.a0.a1 = returnStructNestedIrregularEvenBigger_a1.a1.a0.a1;
+ result.a1.a1.a1.a0 = returnStructNestedIrregularEvenBigger_a1.a1.a1.a0;
+ result.a1.a2 = returnStructNestedIrregularEvenBigger_a1.a2;
+ result.a1.a3 = returnStructNestedIrregularEvenBigger_a1.a3;
+ result.a2.a0.a0 = returnStructNestedIrregularEvenBigger_a2.a0.a0;
+ result.a2.a0.a1.a0.a0 = returnStructNestedIrregularEvenBigger_a2.a0.a1.a0.a0;
+ result.a2.a0.a1.a0.a1 = returnStructNestedIrregularEvenBigger_a2.a0.a1.a0.a1;
+ result.a2.a0.a1.a1.a0 = returnStructNestedIrregularEvenBigger_a2.a0.a1.a1.a0;
+ result.a2.a0.a2 = returnStructNestedIrregularEvenBigger_a2.a0.a2;
+ result.a2.a0.a3.a0.a0 = returnStructNestedIrregularEvenBigger_a2.a0.a3.a0.a0;
+ result.a2.a0.a3.a1 = returnStructNestedIrregularEvenBigger_a2.a0.a3.a1;
+ result.a2.a0.a4 = returnStructNestedIrregularEvenBigger_a2.a0.a4;
+ result.a2.a0.a5.a0.a0 = returnStructNestedIrregularEvenBigger_a2.a0.a5.a0.a0;
+ result.a2.a0.a5.a1.a0 = returnStructNestedIrregularEvenBigger_a2.a0.a5.a1.a0;
+ result.a2.a0.a6 = returnStructNestedIrregularEvenBigger_a2.a0.a6;
+ result.a2.a1.a0.a0 = returnStructNestedIrregularEvenBigger_a2.a1.a0.a0;
+ result.a2.a1.a0.a1 = returnStructNestedIrregularEvenBigger_a2.a1.a0.a1;
+ result.a2.a1.a1.a0 = returnStructNestedIrregularEvenBigger_a2.a1.a1.a0;
+ result.a2.a2 = returnStructNestedIrregularEvenBigger_a2.a2;
+ result.a2.a3 = returnStructNestedIrregularEvenBigger_a2.a3;
+ result.a3 = returnStructNestedIrregularEvenBigger_a3;
+
+ returnStructNestedIrregularEvenBiggerResult = result;
+
+ return result;
+}
+
+/// Return big irregular struct as smoke test.
+StructNestedIrregularEvenBigger returnStructNestedIrregularEvenBigger(int a0,
+ StructNestedIrregularBigger a1, StructNestedIrregularBigger a2, double a3) {
+ print("returnStructNestedIrregularEvenBigger(${a0}, ${a1}, ${a2}, ${a3})");
+
+ // In legacy mode, possibly return null.
+ if (a0 == 84) {
+ print("returning null!");
+ return null;
+ }
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0 == 42 || a0 == 84) {
+ print("throwing!");
+ throw Exception(
+ "ReturnStructNestedIrregularEvenBigger throwing on purpuse!");
+ }
+
+ returnStructNestedIrregularEvenBigger_a0 = a0;
+ returnStructNestedIrregularEvenBigger_a1 = a1;
+ returnStructNestedIrregularEvenBigger_a2 = a2;
+ returnStructNestedIrregularEvenBigger_a3 = a3;
+
+ final result = returnStructNestedIrregularEvenBiggerCalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void returnStructNestedIrregularEvenBiggerAfterCallback() {
+ free(returnStructNestedIrregularEvenBiggerResult.addressOf);
+
+ final result = returnStructNestedIrregularEvenBiggerCalculateResult();
+
+ print("after callback result = $result");
+
+ free(returnStructNestedIrregularEvenBiggerResult.addressOf);
+}
diff --git a/tests/ffi_2/function_callbacks_structs_by_value_test.dart b/tests/ffi_2/function_callbacks_structs_by_value_test.dart
index 98ab518..23c9946 100644
--- a/tests/ffi_2/function_callbacks_structs_by_value_test.dart
+++ b/tests/ffi_2/function_callbacks_structs_by_value_test.dart
@@ -18,6 +18,7 @@
for (int i = 0; i < 10; i++) {
recursiveTest(10);
recursiveTest(11);
+ testCopyLogic();
}
}
@@ -69,3 +70,65 @@
Struct20BytesHomogeneousInt32 struct, Pointer callbackAddress),
Struct20BytesHomogeneousInt32 Function(int recursionCounter,
Struct20BytesHomogeneousInt32, Pointer)>("PassStructRecursive");
+
+Struct8BytesNestedInt typedDataBackedStruct = Struct8BytesNestedInt();
+bool typedDataBackedStructSet = false;
+void _receiveStructByValue(Struct8BytesNestedInt struct) {
+ typedDataBackedStruct = struct;
+ typedDataBackedStructSet = true;
+}
+
+final _receiveStructByValuePointer =
+ Pointer.fromFunction<Void Function(Struct8BytesNestedInt)>(
+ _receiveStructByValue);
+
+final _invokeReceiveStructByValue = ffiTestFunctions.lookupFunction<
+ Void Function(
+ Pointer<NativeFunction<Void Function(Struct8BytesNestedInt)>>),
+ void Function(
+ Pointer<NativeFunction<Void Function(Struct8BytesNestedInt)>>)>(
+ "CallbackWithStruct");
+
+void testCopyLogic() {
+ _invokeReceiveStructByValue(_receiveStructByValuePointer);
+ Expect.isTrue(typedDataBackedStructSet);
+
+ final pointerBackedStruct = allocate<Struct8BytesNestedInt>().ref;
+
+ void reset() {
+ pointerBackedStruct.a0.a0 = 1;
+ pointerBackedStruct.a0.a1 = 2;
+ pointerBackedStruct.a1.a0 = 3;
+ pointerBackedStruct.a1.a1 = 4;
+ typedDataBackedStruct.a0.a0 = 5;
+ typedDataBackedStruct.a0.a1 = 6;
+ typedDataBackedStruct.a1.a0 = 7;
+ typedDataBackedStruct.a1.a1 = 8;
+ }
+
+ // Pointer -> Pointer.
+ reset();
+ pointerBackedStruct.a1 = pointerBackedStruct.a0;
+ Expect.equals(1, pointerBackedStruct.a1.a0);
+ Expect.equals(2, pointerBackedStruct.a1.a1);
+
+ // Pointer -> TypedData.
+ reset();
+ typedDataBackedStruct.a1 = pointerBackedStruct.a0;
+ Expect.equals(1, typedDataBackedStruct.a1.a0);
+ Expect.equals(2, typedDataBackedStruct.a1.a1);
+
+ // TypedData -> Pointer.
+ reset();
+ pointerBackedStruct.a1 = typedDataBackedStruct.a0;
+ Expect.equals(5, pointerBackedStruct.a1.a0);
+ Expect.equals(6, pointerBackedStruct.a1.a1);
+
+ // TypedData -> TypedData.
+ reset();
+ typedDataBackedStruct.a1 = typedDataBackedStruct.a0;
+ Expect.equals(5, typedDataBackedStruct.a1.a0);
+ Expect.equals(6, typedDataBackedStruct.a1.a1);
+
+ free(pointerBackedStruct.addressOf);
+}
diff --git a/tests/ffi_2/function_structs_by_value_generated_test.dart b/tests/ffi_2/function_structs_by_value_generated_test.dart
index dfb17cd..d8ed635 100644
--- a/tests/ffi_2/function_structs_by_value_generated_test.dart
+++ b/tests/ffi_2/function_structs_by_value_generated_test.dart
@@ -52,6 +52,16 @@
testPassStructAlignmentInt16();
testPassStructAlignmentInt32();
testPassStructAlignmentInt64();
+ testPassStruct8BytesNestedIntx10();
+ testPassStruct8BytesNestedFloatx10();
+ testPassStruct8BytesNestedFloat2x10();
+ testPassStruct8BytesNestedMixedx10();
+ testPassStruct16BytesNestedIntx2();
+ testPassStruct32BytesNestedIntx2();
+ testPassStructNestedIntStructAlignmentInt16();
+ testPassStructNestedIntStructAlignmentInt32();
+ testPassStructNestedIntStructAlignmentInt64();
+ testPassStructNestedIrregularEvenBiggerx4();
testReturnStruct1ByteInt();
testReturnStruct3BytesHomogeneousUint8();
testReturnStruct3BytesInt2ByteAligned();
@@ -82,6 +92,16 @@
testReturnStructAlignmentInt16();
testReturnStructAlignmentInt32();
testReturnStructAlignmentInt64();
+ testReturnStruct8BytesNestedInt();
+ testReturnStruct8BytesNestedFloat();
+ testReturnStruct8BytesNestedFloat2();
+ testReturnStruct8BytesNestedMixed();
+ testReturnStruct16BytesNestedInt();
+ testReturnStruct32BytesNestedInt();
+ testReturnStructNestedIntStructAlignmentInt16();
+ testReturnStructNestedIntStructAlignmentInt32();
+ testReturnStructNestedIntStructAlignmentInt64();
+ testReturnStructNestedIrregularEvenBigger();
}
}
@@ -129,6 +149,13 @@
String toString() => "(${a0}, ${a1})";
}
+class Struct4BytesFloat extends Struct {
+ @Float()
+ double a0;
+
+ String toString() => "(${a0})";
+}
+
class Struct7BytesHomogeneousUint8 extends Struct {
@Uint8()
int a0;
@@ -876,6 +903,129 @@
String toString() => "(${a0}, ${a1}, ${a2})";
}
+class Struct8BytesNestedInt extends Struct {
+ Struct4BytesHomogeneousInt16 a0;
+
+ Struct4BytesHomogeneousInt16 a1;
+
+ String toString() => "(${a0}, ${a1})";
+}
+
+class Struct8BytesNestedFloat extends Struct {
+ Struct4BytesFloat a0;
+
+ Struct4BytesFloat a1;
+
+ String toString() => "(${a0}, ${a1})";
+}
+
+class Struct8BytesNestedFloat2 extends Struct {
+ Struct4BytesFloat a0;
+
+ @Float()
+ double a1;
+
+ String toString() => "(${a0}, ${a1})";
+}
+
+class Struct8BytesNestedMixed extends Struct {
+ Struct4BytesHomogeneousInt16 a0;
+
+ Struct4BytesFloat a1;
+
+ String toString() => "(${a0}, ${a1})";
+}
+
+class Struct16BytesNestedInt extends Struct {
+ Struct8BytesNestedInt a0;
+
+ Struct8BytesNestedInt a1;
+
+ String toString() => "(${a0}, ${a1})";
+}
+
+class Struct32BytesNestedInt extends Struct {
+ Struct16BytesNestedInt a0;
+
+ Struct16BytesNestedInt a1;
+
+ String toString() => "(${a0}, ${a1})";
+}
+
+class StructNestedIntStructAlignmentInt16 extends Struct {
+ StructAlignmentInt16 a0;
+
+ StructAlignmentInt16 a1;
+
+ String toString() => "(${a0}, ${a1})";
+}
+
+class StructNestedIntStructAlignmentInt32 extends Struct {
+ StructAlignmentInt32 a0;
+
+ StructAlignmentInt32 a1;
+
+ String toString() => "(${a0}, ${a1})";
+}
+
+class StructNestedIntStructAlignmentInt64 extends Struct {
+ StructAlignmentInt64 a0;
+
+ StructAlignmentInt64 a1;
+
+ String toString() => "(${a0}, ${a1})";
+}
+
+class StructNestedIrregularBig extends Struct {
+ @Uint16()
+ int a0;
+
+ Struct8BytesNestedMixed a1;
+
+ @Uint16()
+ int a2;
+
+ Struct8BytesNestedFloat2 a3;
+
+ @Uint16()
+ int a4;
+
+ Struct8BytesNestedFloat a5;
+
+ @Uint16()
+ int a6;
+
+ String toString() => "(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6})";
+}
+
+class StructNestedIrregularBigger extends Struct {
+ StructNestedIrregularBig a0;
+
+ Struct8BytesNestedMixed a1;
+
+ @Float()
+ double a2;
+
+ @Double()
+ double a3;
+
+ String toString() => "(${a0}, ${a1}, ${a2}, ${a3})";
+}
+
+class StructNestedIrregularEvenBigger extends Struct {
+ @Uint64()
+ int a0;
+
+ StructNestedIrregularBigger a1;
+
+ StructNestedIrregularBigger a2;
+
+ @Double()
+ double a3;
+
+ String toString() => "(${a0}, ${a1}, ${a2}, ${a3})";
+}
+
final passStruct1ByteIntx10 = ffiTestFunctions.lookupFunction<
Int64 Function(
Struct1ByteInt,
@@ -3660,6 +3810,691 @@
free(a0.addressOf);
}
+final passStruct8BytesNestedIntx10 = ffiTestFunctions.lookupFunction<
+ Int64 Function(
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt),
+ int Function(
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt,
+ Struct8BytesNestedInt)>("PassStruct8BytesNestedIntx10");
+
+/// Simple nested struct. No alignment gaps on any architectures.
+/// 10 arguments exhaust registers on all platforms.
+void testPassStruct8BytesNestedIntx10() {
+ Struct8BytesNestedInt a0 = allocate<Struct8BytesNestedInt>().ref;
+ Struct8BytesNestedInt a1 = allocate<Struct8BytesNestedInt>().ref;
+ Struct8BytesNestedInt a2 = allocate<Struct8BytesNestedInt>().ref;
+ Struct8BytesNestedInt a3 = allocate<Struct8BytesNestedInt>().ref;
+ Struct8BytesNestedInt a4 = allocate<Struct8BytesNestedInt>().ref;
+ Struct8BytesNestedInt a5 = allocate<Struct8BytesNestedInt>().ref;
+ Struct8BytesNestedInt a6 = allocate<Struct8BytesNestedInt>().ref;
+ Struct8BytesNestedInt a7 = allocate<Struct8BytesNestedInt>().ref;
+ Struct8BytesNestedInt a8 = allocate<Struct8BytesNestedInt>().ref;
+ Struct8BytesNestedInt a9 = allocate<Struct8BytesNestedInt>().ref;
+
+ a0.a0.a0 = -1;
+ a0.a0.a1 = 2;
+ a0.a1.a0 = -3;
+ a0.a1.a1 = 4;
+ a1.a0.a0 = -5;
+ a1.a0.a1 = 6;
+ a1.a1.a0 = -7;
+ a1.a1.a1 = 8;
+ a2.a0.a0 = -9;
+ a2.a0.a1 = 10;
+ a2.a1.a0 = -11;
+ a2.a1.a1 = 12;
+ a3.a0.a0 = -13;
+ a3.a0.a1 = 14;
+ a3.a1.a0 = -15;
+ a3.a1.a1 = 16;
+ a4.a0.a0 = -17;
+ a4.a0.a1 = 18;
+ a4.a1.a0 = -19;
+ a4.a1.a1 = 20;
+ a5.a0.a0 = -21;
+ a5.a0.a1 = 22;
+ a5.a1.a0 = -23;
+ a5.a1.a1 = 24;
+ a6.a0.a0 = -25;
+ a6.a0.a1 = 26;
+ a6.a1.a0 = -27;
+ a6.a1.a1 = 28;
+ a7.a0.a0 = -29;
+ a7.a0.a1 = 30;
+ a7.a1.a0 = -31;
+ a7.a1.a1 = 32;
+ a8.a0.a0 = -33;
+ a8.a0.a1 = 34;
+ a8.a1.a0 = -35;
+ a8.a1.a1 = 36;
+ a9.a0.a0 = -37;
+ a9.a0.a1 = 38;
+ a9.a1.a0 = -39;
+ a9.a1.a1 = 40;
+
+ final result =
+ passStruct8BytesNestedIntx10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+ print("result = $result");
+
+ Expect.equals(20, result);
+
+ free(a0.addressOf);
+ free(a1.addressOf);
+ free(a2.addressOf);
+ free(a3.addressOf);
+ free(a4.addressOf);
+ free(a5.addressOf);
+ free(a6.addressOf);
+ free(a7.addressOf);
+ free(a8.addressOf);
+ free(a9.addressOf);
+}
+
+final passStruct8BytesNestedFloatx10 = ffiTestFunctions.lookupFunction<
+ Float Function(
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat),
+ double Function(
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat,
+ Struct8BytesNestedFloat)>("PassStruct8BytesNestedFloatx10");
+
+/// Simple nested struct. No alignment gaps on any architectures.
+/// 10 arguments exhaust fpu registers on all platforms.
+void testPassStruct8BytesNestedFloatx10() {
+ Struct8BytesNestedFloat a0 = allocate<Struct8BytesNestedFloat>().ref;
+ Struct8BytesNestedFloat a1 = allocate<Struct8BytesNestedFloat>().ref;
+ Struct8BytesNestedFloat a2 = allocate<Struct8BytesNestedFloat>().ref;
+ Struct8BytesNestedFloat a3 = allocate<Struct8BytesNestedFloat>().ref;
+ Struct8BytesNestedFloat a4 = allocate<Struct8BytesNestedFloat>().ref;
+ Struct8BytesNestedFloat a5 = allocate<Struct8BytesNestedFloat>().ref;
+ Struct8BytesNestedFloat a6 = allocate<Struct8BytesNestedFloat>().ref;
+ Struct8BytesNestedFloat a7 = allocate<Struct8BytesNestedFloat>().ref;
+ Struct8BytesNestedFloat a8 = allocate<Struct8BytesNestedFloat>().ref;
+ Struct8BytesNestedFloat a9 = allocate<Struct8BytesNestedFloat>().ref;
+
+ a0.a0.a0 = -1.0;
+ a0.a1.a0 = 2.0;
+ a1.a0.a0 = -3.0;
+ a1.a1.a0 = 4.0;
+ a2.a0.a0 = -5.0;
+ a2.a1.a0 = 6.0;
+ a3.a0.a0 = -7.0;
+ a3.a1.a0 = 8.0;
+ a4.a0.a0 = -9.0;
+ a4.a1.a0 = 10.0;
+ a5.a0.a0 = -11.0;
+ a5.a1.a0 = 12.0;
+ a6.a0.a0 = -13.0;
+ a6.a1.a0 = 14.0;
+ a7.a0.a0 = -15.0;
+ a7.a1.a0 = 16.0;
+ a8.a0.a0 = -17.0;
+ a8.a1.a0 = 18.0;
+ a9.a0.a0 = -19.0;
+ a9.a1.a0 = 20.0;
+
+ final result =
+ passStruct8BytesNestedFloatx10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+ print("result = $result");
+
+ Expect.approxEquals(10.0, result);
+
+ free(a0.addressOf);
+ free(a1.addressOf);
+ free(a2.addressOf);
+ free(a3.addressOf);
+ free(a4.addressOf);
+ free(a5.addressOf);
+ free(a6.addressOf);
+ free(a7.addressOf);
+ free(a8.addressOf);
+ free(a9.addressOf);
+}
+
+final passStruct8BytesNestedFloat2x10 = ffiTestFunctions.lookupFunction<
+ Float Function(
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2),
+ double Function(
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2,
+ Struct8BytesNestedFloat2)>("PassStruct8BytesNestedFloat2x10");
+
+/// Simple nested struct. No alignment gaps on any architectures.
+/// 10 arguments exhaust fpu registers on all platforms.
+/// The nesting is irregular, testing homogenous float rules on arm and arm64,
+/// and the fpu register usage on x64.
+void testPassStruct8BytesNestedFloat2x10() {
+ Struct8BytesNestedFloat2 a0 = allocate<Struct8BytesNestedFloat2>().ref;
+ Struct8BytesNestedFloat2 a1 = allocate<Struct8BytesNestedFloat2>().ref;
+ Struct8BytesNestedFloat2 a2 = allocate<Struct8BytesNestedFloat2>().ref;
+ Struct8BytesNestedFloat2 a3 = allocate<Struct8BytesNestedFloat2>().ref;
+ Struct8BytesNestedFloat2 a4 = allocate<Struct8BytesNestedFloat2>().ref;
+ Struct8BytesNestedFloat2 a5 = allocate<Struct8BytesNestedFloat2>().ref;
+ Struct8BytesNestedFloat2 a6 = allocate<Struct8BytesNestedFloat2>().ref;
+ Struct8BytesNestedFloat2 a7 = allocate<Struct8BytesNestedFloat2>().ref;
+ Struct8BytesNestedFloat2 a8 = allocate<Struct8BytesNestedFloat2>().ref;
+ Struct8BytesNestedFloat2 a9 = allocate<Struct8BytesNestedFloat2>().ref;
+
+ a0.a0.a0 = -1.0;
+ a0.a1 = 2.0;
+ a1.a0.a0 = -3.0;
+ a1.a1 = 4.0;
+ a2.a0.a0 = -5.0;
+ a2.a1 = 6.0;
+ a3.a0.a0 = -7.0;
+ a3.a1 = 8.0;
+ a4.a0.a0 = -9.0;
+ a4.a1 = 10.0;
+ a5.a0.a0 = -11.0;
+ a5.a1 = 12.0;
+ a6.a0.a0 = -13.0;
+ a6.a1 = 14.0;
+ a7.a0.a0 = -15.0;
+ a7.a1 = 16.0;
+ a8.a0.a0 = -17.0;
+ a8.a1 = 18.0;
+ a9.a0.a0 = -19.0;
+ a9.a1 = 20.0;
+
+ final result =
+ passStruct8BytesNestedFloat2x10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+ print("result = $result");
+
+ Expect.approxEquals(10.0, result);
+
+ free(a0.addressOf);
+ free(a1.addressOf);
+ free(a2.addressOf);
+ free(a3.addressOf);
+ free(a4.addressOf);
+ free(a5.addressOf);
+ free(a6.addressOf);
+ free(a7.addressOf);
+ free(a8.addressOf);
+ free(a9.addressOf);
+}
+
+final passStruct8BytesNestedMixedx10 = ffiTestFunctions.lookupFunction<
+ Double Function(
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed),
+ double Function(
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed,
+ Struct8BytesNestedMixed)>("PassStruct8BytesNestedMixedx10");
+
+/// Simple nested struct. No alignment gaps on any architectures.
+/// 10 arguments exhaust all registers on all platforms.
+void testPassStruct8BytesNestedMixedx10() {
+ Struct8BytesNestedMixed a0 = allocate<Struct8BytesNestedMixed>().ref;
+ Struct8BytesNestedMixed a1 = allocate<Struct8BytesNestedMixed>().ref;
+ Struct8BytesNestedMixed a2 = allocate<Struct8BytesNestedMixed>().ref;
+ Struct8BytesNestedMixed a3 = allocate<Struct8BytesNestedMixed>().ref;
+ Struct8BytesNestedMixed a4 = allocate<Struct8BytesNestedMixed>().ref;
+ Struct8BytesNestedMixed a5 = allocate<Struct8BytesNestedMixed>().ref;
+ Struct8BytesNestedMixed a6 = allocate<Struct8BytesNestedMixed>().ref;
+ Struct8BytesNestedMixed a7 = allocate<Struct8BytesNestedMixed>().ref;
+ Struct8BytesNestedMixed a8 = allocate<Struct8BytesNestedMixed>().ref;
+ Struct8BytesNestedMixed a9 = allocate<Struct8BytesNestedMixed>().ref;
+
+ a0.a0.a0 = -1;
+ a0.a0.a1 = 2;
+ a0.a1.a0 = -3.0;
+ a1.a0.a0 = 4;
+ a1.a0.a1 = -5;
+ a1.a1.a0 = 6.0;
+ a2.a0.a0 = -7;
+ a2.a0.a1 = 8;
+ a2.a1.a0 = -9.0;
+ a3.a0.a0 = 10;
+ a3.a0.a1 = -11;
+ a3.a1.a0 = 12.0;
+ a4.a0.a0 = -13;
+ a4.a0.a1 = 14;
+ a4.a1.a0 = -15.0;
+ a5.a0.a0 = 16;
+ a5.a0.a1 = -17;
+ a5.a1.a0 = 18.0;
+ a6.a0.a0 = -19;
+ a6.a0.a1 = 20;
+ a6.a1.a0 = -21.0;
+ a7.a0.a0 = 22;
+ a7.a0.a1 = -23;
+ a7.a1.a0 = 24.0;
+ a8.a0.a0 = -25;
+ a8.a0.a1 = 26;
+ a8.a1.a0 = -27.0;
+ a9.a0.a0 = 28;
+ a9.a0.a1 = -29;
+ a9.a1.a0 = 30.0;
+
+ final result =
+ passStruct8BytesNestedMixedx10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+ print("result = $result");
+
+ Expect.approxEquals(15.0, result);
+
+ free(a0.addressOf);
+ free(a1.addressOf);
+ free(a2.addressOf);
+ free(a3.addressOf);
+ free(a4.addressOf);
+ free(a5.addressOf);
+ free(a6.addressOf);
+ free(a7.addressOf);
+ free(a8.addressOf);
+ free(a9.addressOf);
+}
+
+final passStruct16BytesNestedIntx2 = ffiTestFunctions.lookupFunction<
+ Int64 Function(Struct16BytesNestedInt, Struct16BytesNestedInt),
+ int Function(Struct16BytesNestedInt,
+ Struct16BytesNestedInt)>("PassStruct16BytesNestedIntx2");
+
+/// Deeper nested struct to test recursive member access.
+void testPassStruct16BytesNestedIntx2() {
+ Struct16BytesNestedInt a0 = allocate<Struct16BytesNestedInt>().ref;
+ Struct16BytesNestedInt a1 = allocate<Struct16BytesNestedInt>().ref;
+
+ a0.a0.a0.a0 = -1;
+ a0.a0.a0.a1 = 2;
+ a0.a0.a1.a0 = -3;
+ a0.a0.a1.a1 = 4;
+ a0.a1.a0.a0 = -5;
+ a0.a1.a0.a1 = 6;
+ a0.a1.a1.a0 = -7;
+ a0.a1.a1.a1 = 8;
+ a1.a0.a0.a0 = -9;
+ a1.a0.a0.a1 = 10;
+ a1.a0.a1.a0 = -11;
+ a1.a0.a1.a1 = 12;
+ a1.a1.a0.a0 = -13;
+ a1.a1.a0.a1 = 14;
+ a1.a1.a1.a0 = -15;
+ a1.a1.a1.a1 = 16;
+
+ final result = passStruct16BytesNestedIntx2(a0, a1);
+
+ print("result = $result");
+
+ Expect.equals(8, result);
+
+ free(a0.addressOf);
+ free(a1.addressOf);
+}
+
+final passStruct32BytesNestedIntx2 = ffiTestFunctions.lookupFunction<
+ Int64 Function(Struct32BytesNestedInt, Struct32BytesNestedInt),
+ int Function(Struct32BytesNestedInt,
+ Struct32BytesNestedInt)>("PassStruct32BytesNestedIntx2");
+
+/// Even deeper nested struct to test recursive member access.
+void testPassStruct32BytesNestedIntx2() {
+ Struct32BytesNestedInt a0 = allocate<Struct32BytesNestedInt>().ref;
+ Struct32BytesNestedInt a1 = allocate<Struct32BytesNestedInt>().ref;
+
+ a0.a0.a0.a0.a0 = -1;
+ a0.a0.a0.a0.a1 = 2;
+ a0.a0.a0.a1.a0 = -3;
+ a0.a0.a0.a1.a1 = 4;
+ a0.a0.a1.a0.a0 = -5;
+ a0.a0.a1.a0.a1 = 6;
+ a0.a0.a1.a1.a0 = -7;
+ a0.a0.a1.a1.a1 = 8;
+ a0.a1.a0.a0.a0 = -9;
+ a0.a1.a0.a0.a1 = 10;
+ a0.a1.a0.a1.a0 = -11;
+ a0.a1.a0.a1.a1 = 12;
+ a0.a1.a1.a0.a0 = -13;
+ a0.a1.a1.a0.a1 = 14;
+ a0.a1.a1.a1.a0 = -15;
+ a0.a1.a1.a1.a1 = 16;
+ a1.a0.a0.a0.a0 = -17;
+ a1.a0.a0.a0.a1 = 18;
+ a1.a0.a0.a1.a0 = -19;
+ a1.a0.a0.a1.a1 = 20;
+ a1.a0.a1.a0.a0 = -21;
+ a1.a0.a1.a0.a1 = 22;
+ a1.a0.a1.a1.a0 = -23;
+ a1.a0.a1.a1.a1 = 24;
+ a1.a1.a0.a0.a0 = -25;
+ a1.a1.a0.a0.a1 = 26;
+ a1.a1.a0.a1.a0 = -27;
+ a1.a1.a0.a1.a1 = 28;
+ a1.a1.a1.a0.a0 = -29;
+ a1.a1.a1.a0.a1 = 30;
+ a1.a1.a1.a1.a0 = -31;
+ a1.a1.a1.a1.a1 = 32;
+
+ final result = passStruct32BytesNestedIntx2(a0, a1);
+
+ print("result = $result");
+
+ Expect.equals(16, result);
+
+ free(a0.addressOf);
+ free(a1.addressOf);
+}
+
+final passStructNestedIntStructAlignmentInt16 = ffiTestFunctions.lookupFunction<
+ Int64 Function(StructNestedIntStructAlignmentInt16),
+ int Function(StructNestedIntStructAlignmentInt16)>(
+ "PassStructNestedIntStructAlignmentInt16");
+
+/// Test alignment and padding of nested struct with 16 byte int.
+void testPassStructNestedIntStructAlignmentInt16() {
+ StructNestedIntStructAlignmentInt16 a0 =
+ allocate<StructNestedIntStructAlignmentInt16>().ref;
+
+ a0.a0.a0 = -1;
+ a0.a0.a1 = 2;
+ a0.a0.a2 = -3;
+ a0.a1.a0 = 4;
+ a0.a1.a1 = -5;
+ a0.a1.a2 = 6;
+
+ final result = passStructNestedIntStructAlignmentInt16(a0);
+
+ print("result = $result");
+
+ Expect.equals(3, result);
+
+ free(a0.addressOf);
+}
+
+final passStructNestedIntStructAlignmentInt32 = ffiTestFunctions.lookupFunction<
+ Int64 Function(StructNestedIntStructAlignmentInt32),
+ int Function(StructNestedIntStructAlignmentInt32)>(
+ "PassStructNestedIntStructAlignmentInt32");
+
+/// Test alignment and padding of nested struct with 32 byte int.
+void testPassStructNestedIntStructAlignmentInt32() {
+ StructNestedIntStructAlignmentInt32 a0 =
+ allocate<StructNestedIntStructAlignmentInt32>().ref;
+
+ a0.a0.a0 = -1;
+ a0.a0.a1 = 2;
+ a0.a0.a2 = -3;
+ a0.a1.a0 = 4;
+ a0.a1.a1 = -5;
+ a0.a1.a2 = 6;
+
+ final result = passStructNestedIntStructAlignmentInt32(a0);
+
+ print("result = $result");
+
+ Expect.equals(3, result);
+
+ free(a0.addressOf);
+}
+
+final passStructNestedIntStructAlignmentInt64 = ffiTestFunctions.lookupFunction<
+ Int64 Function(StructNestedIntStructAlignmentInt64),
+ int Function(StructNestedIntStructAlignmentInt64)>(
+ "PassStructNestedIntStructAlignmentInt64");
+
+/// Test alignment and padding of nested struct with 64 byte int.
+void testPassStructNestedIntStructAlignmentInt64() {
+ StructNestedIntStructAlignmentInt64 a0 =
+ allocate<StructNestedIntStructAlignmentInt64>().ref;
+
+ a0.a0.a0 = -1;
+ a0.a0.a1 = 2;
+ a0.a0.a2 = -3;
+ a0.a1.a0 = 4;
+ a0.a1.a1 = -5;
+ a0.a1.a2 = 6;
+
+ final result = passStructNestedIntStructAlignmentInt64(a0);
+
+ print("result = $result");
+
+ Expect.equals(3, result);
+
+ free(a0.addressOf);
+}
+
+final passStructNestedIrregularEvenBiggerx4 = ffiTestFunctions.lookupFunction<
+ Double Function(
+ StructNestedIrregularEvenBigger,
+ StructNestedIrregularEvenBigger,
+ StructNestedIrregularEvenBigger,
+ StructNestedIrregularEvenBigger),
+ double Function(
+ StructNestedIrregularEvenBigger,
+ StructNestedIrregularEvenBigger,
+ StructNestedIrregularEvenBigger,
+ StructNestedIrregularEvenBigger)>(
+ "PassStructNestedIrregularEvenBiggerx4");
+
+/// Return big irregular struct as smoke test.
+void testPassStructNestedIrregularEvenBiggerx4() {
+ StructNestedIrregularEvenBigger a0 =
+ allocate<StructNestedIrregularEvenBigger>().ref;
+ StructNestedIrregularEvenBigger a1 =
+ allocate<StructNestedIrregularEvenBigger>().ref;
+ StructNestedIrregularEvenBigger a2 =
+ allocate<StructNestedIrregularEvenBigger>().ref;
+ StructNestedIrregularEvenBigger a3 =
+ allocate<StructNestedIrregularEvenBigger>().ref;
+
+ a0.a0 = 1;
+ a0.a1.a0.a0 = 2;
+ a0.a1.a0.a1.a0.a0 = -3;
+ a0.a1.a0.a1.a0.a1 = 4;
+ a0.a1.a0.a1.a1.a0 = -5.0;
+ a0.a1.a0.a2 = 6;
+ a0.a1.a0.a3.a0.a0 = -7.0;
+ a0.a1.a0.a3.a1 = 8.0;
+ a0.a1.a0.a4 = 9;
+ a0.a1.a0.a5.a0.a0 = 10.0;
+ a0.a1.a0.a5.a1.a0 = -11.0;
+ a0.a1.a0.a6 = 12;
+ a0.a1.a1.a0.a0 = -13;
+ a0.a1.a1.a0.a1 = 14;
+ a0.a1.a1.a1.a0 = -15.0;
+ a0.a1.a2 = 16.0;
+ a0.a1.a3 = -17.0;
+ a0.a2.a0.a0 = 18;
+ a0.a2.a0.a1.a0.a0 = -19;
+ a0.a2.a0.a1.a0.a1 = 20;
+ a0.a2.a0.a1.a1.a0 = -21.0;
+ a0.a2.a0.a2 = 22;
+ a0.a2.a0.a3.a0.a0 = -23.0;
+ a0.a2.a0.a3.a1 = 24.0;
+ a0.a2.a0.a4 = 25;
+ a0.a2.a0.a5.a0.a0 = 26.0;
+ a0.a2.a0.a5.a1.a0 = -27.0;
+ a0.a2.a0.a6 = 28;
+ a0.a2.a1.a0.a0 = -29;
+ a0.a2.a1.a0.a1 = 30;
+ a0.a2.a1.a1.a0 = -31.0;
+ a0.a2.a2 = 32.0;
+ a0.a2.a3 = -33.0;
+ a0.a3 = 34.0;
+ a1.a0 = 35;
+ a1.a1.a0.a0 = 36;
+ a1.a1.a0.a1.a0.a0 = -37;
+ a1.a1.a0.a1.a0.a1 = 38;
+ a1.a1.a0.a1.a1.a0 = -39.0;
+ a1.a1.a0.a2 = 40;
+ a1.a1.a0.a3.a0.a0 = -41.0;
+ a1.a1.a0.a3.a1 = 42.0;
+ a1.a1.a0.a4 = 43;
+ a1.a1.a0.a5.a0.a0 = 44.0;
+ a1.a1.a0.a5.a1.a0 = -45.0;
+ a1.a1.a0.a6 = 46;
+ a1.a1.a1.a0.a0 = -47;
+ a1.a1.a1.a0.a1 = 48;
+ a1.a1.a1.a1.a0 = -49.0;
+ a1.a1.a2 = 50.0;
+ a1.a1.a3 = -51.0;
+ a1.a2.a0.a0 = 52;
+ a1.a2.a0.a1.a0.a0 = -53;
+ a1.a2.a0.a1.a0.a1 = 54;
+ a1.a2.a0.a1.a1.a0 = -55.0;
+ a1.a2.a0.a2 = 56;
+ a1.a2.a0.a3.a0.a0 = -57.0;
+ a1.a2.a0.a3.a1 = 58.0;
+ a1.a2.a0.a4 = 59;
+ a1.a2.a0.a5.a0.a0 = 60.0;
+ a1.a2.a0.a5.a1.a0 = -61.0;
+ a1.a2.a0.a6 = 62;
+ a1.a2.a1.a0.a0 = -63;
+ a1.a2.a1.a0.a1 = 64;
+ a1.a2.a1.a1.a0 = -65.0;
+ a1.a2.a2 = 66.0;
+ a1.a2.a3 = -67.0;
+ a1.a3 = 68.0;
+ a2.a0 = 69;
+ a2.a1.a0.a0 = 70;
+ a2.a1.a0.a1.a0.a0 = -71;
+ a2.a1.a0.a1.a0.a1 = 72;
+ a2.a1.a0.a1.a1.a0 = -73.0;
+ a2.a1.a0.a2 = 74;
+ a2.a1.a0.a3.a0.a0 = -75.0;
+ a2.a1.a0.a3.a1 = 76.0;
+ a2.a1.a0.a4 = 77;
+ a2.a1.a0.a5.a0.a0 = 78.0;
+ a2.a1.a0.a5.a1.a0 = -79.0;
+ a2.a1.a0.a6 = 80;
+ a2.a1.a1.a0.a0 = -81;
+ a2.a1.a1.a0.a1 = 82;
+ a2.a1.a1.a1.a0 = -83.0;
+ a2.a1.a2 = 84.0;
+ a2.a1.a3 = -85.0;
+ a2.a2.a0.a0 = 86;
+ a2.a2.a0.a1.a0.a0 = -87;
+ a2.a2.a0.a1.a0.a1 = 88;
+ a2.a2.a0.a1.a1.a0 = -89.0;
+ a2.a2.a0.a2 = 90;
+ a2.a2.a0.a3.a0.a0 = -91.0;
+ a2.a2.a0.a3.a1 = 92.0;
+ a2.a2.a0.a4 = 93;
+ a2.a2.a0.a5.a0.a0 = 94.0;
+ a2.a2.a0.a5.a1.a0 = -95.0;
+ a2.a2.a0.a6 = 96;
+ a2.a2.a1.a0.a0 = -97;
+ a2.a2.a1.a0.a1 = 98;
+ a2.a2.a1.a1.a0 = -99.0;
+ a2.a2.a2 = 100.0;
+ a2.a2.a3 = -101.0;
+ a2.a3 = 102.0;
+ a3.a0 = 103;
+ a3.a1.a0.a0 = 104;
+ a3.a1.a0.a1.a0.a0 = -105;
+ a3.a1.a0.a1.a0.a1 = 106;
+ a3.a1.a0.a1.a1.a0 = -107.0;
+ a3.a1.a0.a2 = 108;
+ a3.a1.a0.a3.a0.a0 = -109.0;
+ a3.a1.a0.a3.a1 = 110.0;
+ a3.a1.a0.a4 = 111;
+ a3.a1.a0.a5.a0.a0 = 112.0;
+ a3.a1.a0.a5.a1.a0 = -113.0;
+ a3.a1.a0.a6 = 114;
+ a3.a1.a1.a0.a0 = -115;
+ a3.a1.a1.a0.a1 = 116;
+ a3.a1.a1.a1.a0 = -117.0;
+ a3.a1.a2 = 118.0;
+ a3.a1.a3 = -119.0;
+ a3.a2.a0.a0 = 120;
+ a3.a2.a0.a1.a0.a0 = -121;
+ a3.a2.a0.a1.a0.a1 = 122;
+ a3.a2.a0.a1.a1.a0 = -123.0;
+ a3.a2.a0.a2 = 124;
+ a3.a2.a0.a3.a0.a0 = -125.0;
+ a3.a2.a0.a3.a1 = 126.0;
+ a3.a2.a0.a4 = 127;
+ a3.a2.a0.a5.a0.a0 = 128.0;
+ a3.a2.a0.a5.a1.a0 = -129.0;
+ a3.a2.a0.a6 = 130;
+ a3.a2.a1.a0.a0 = -131;
+ a3.a2.a1.a0.a1 = 132;
+ a3.a2.a1.a1.a0 = -133.0;
+ a3.a2.a2 = 134.0;
+ a3.a2.a3 = -135.0;
+ a3.a3 = 136.0;
+
+ final result = passStructNestedIrregularEvenBiggerx4(a0, a1, a2, a3);
+
+ print("result = $result");
+
+ Expect.approxEquals(1572.0, result);
+
+ free(a0.addressOf);
+ free(a1.addressOf);
+ free(a2.addressOf);
+ free(a3.addressOf);
+}
+
final returnStruct1ByteInt = ffiTestFunctions.lookupFunction<
Struct1ByteInt Function(Int8),
Struct1ByteInt Function(int)>("ReturnStruct1ByteInt");
@@ -5319,3 +6154,396 @@
Expect.equals(a1, result.a1);
Expect.equals(a2, result.a2);
}
+
+final returnStruct8BytesNestedInt = ffiTestFunctions.lookupFunction<
+ Struct8BytesNestedInt Function(
+ Struct4BytesHomogeneousInt16, Struct4BytesHomogeneousInt16),
+ Struct8BytesNestedInt Function(Struct4BytesHomogeneousInt16,
+ Struct4BytesHomogeneousInt16)>("ReturnStruct8BytesNestedInt");
+
+/// Simple nested struct.
+void testReturnStruct8BytesNestedInt() {
+ Struct4BytesHomogeneousInt16 a0 =
+ allocate<Struct4BytesHomogeneousInt16>().ref;
+ Struct4BytesHomogeneousInt16 a1 =
+ allocate<Struct4BytesHomogeneousInt16>().ref;
+
+ a0.a0 = -1;
+ a0.a1 = 2;
+ a1.a0 = -3;
+ a1.a1 = 4;
+
+ final result = returnStruct8BytesNestedInt(a0, a1);
+
+ print("result = $result");
+
+ Expect.equals(a0.a0, result.a0.a0);
+ Expect.equals(a0.a1, result.a0.a1);
+ Expect.equals(a1.a0, result.a1.a0);
+ Expect.equals(a1.a1, result.a1.a1);
+
+ free(a0.addressOf);
+ free(a1.addressOf);
+}
+
+final returnStruct8BytesNestedFloat = ffiTestFunctions.lookupFunction<
+ Struct8BytesNestedFloat Function(Struct4BytesFloat, Struct4BytesFloat),
+ Struct8BytesNestedFloat Function(
+ Struct4BytesFloat, Struct4BytesFloat)>("ReturnStruct8BytesNestedFloat");
+
+/// Simple nested struct with floats.
+void testReturnStruct8BytesNestedFloat() {
+ Struct4BytesFloat a0 = allocate<Struct4BytesFloat>().ref;
+ Struct4BytesFloat a1 = allocate<Struct4BytesFloat>().ref;
+
+ a0.a0 = -1.0;
+ a1.a0 = 2.0;
+
+ final result = returnStruct8BytesNestedFloat(a0, a1);
+
+ print("result = $result");
+
+ Expect.approxEquals(a0.a0, result.a0.a0);
+ Expect.approxEquals(a1.a0, result.a1.a0);
+
+ free(a0.addressOf);
+ free(a1.addressOf);
+}
+
+final returnStruct8BytesNestedFloat2 = ffiTestFunctions.lookupFunction<
+ Struct8BytesNestedFloat2 Function(Struct4BytesFloat, Float),
+ Struct8BytesNestedFloat2 Function(
+ Struct4BytesFloat, double)>("ReturnStruct8BytesNestedFloat2");
+
+/// The nesting is irregular, testing homogenous float rules on arm and arm64,
+/// and the fpu register usage on x64.
+void testReturnStruct8BytesNestedFloat2() {
+ Struct4BytesFloat a0 = allocate<Struct4BytesFloat>().ref;
+ double a1;
+
+ a0.a0 = -1.0;
+ a1 = 2.0;
+
+ final result = returnStruct8BytesNestedFloat2(a0, a1);
+
+ print("result = $result");
+
+ Expect.approxEquals(a0.a0, result.a0.a0);
+ Expect.approxEquals(a1, result.a1);
+
+ free(a0.addressOf);
+}
+
+final returnStruct8BytesNestedMixed = ffiTestFunctions.lookupFunction<
+ Struct8BytesNestedMixed Function(
+ Struct4BytesHomogeneousInt16, Struct4BytesFloat),
+ Struct8BytesNestedMixed Function(Struct4BytesHomogeneousInt16,
+ Struct4BytesFloat)>("ReturnStruct8BytesNestedMixed");
+
+/// Simple nested struct with mixed members.
+void testReturnStruct8BytesNestedMixed() {
+ Struct4BytesHomogeneousInt16 a0 =
+ allocate<Struct4BytesHomogeneousInt16>().ref;
+ Struct4BytesFloat a1 = allocate<Struct4BytesFloat>().ref;
+
+ a0.a0 = -1;
+ a0.a1 = 2;
+ a1.a0 = -3.0;
+
+ final result = returnStruct8BytesNestedMixed(a0, a1);
+
+ print("result = $result");
+
+ Expect.equals(a0.a0, result.a0.a0);
+ Expect.equals(a0.a1, result.a0.a1);
+ Expect.approxEquals(a1.a0, result.a1.a0);
+
+ free(a0.addressOf);
+ free(a1.addressOf);
+}
+
+final returnStruct16BytesNestedInt = ffiTestFunctions.lookupFunction<
+ Struct16BytesNestedInt Function(
+ Struct8BytesNestedInt, Struct8BytesNestedInt),
+ Struct16BytesNestedInt Function(Struct8BytesNestedInt,
+ Struct8BytesNestedInt)>("ReturnStruct16BytesNestedInt");
+
+/// Deeper nested struct to test recursive member access.
+void testReturnStruct16BytesNestedInt() {
+ Struct8BytesNestedInt a0 = allocate<Struct8BytesNestedInt>().ref;
+ Struct8BytesNestedInt a1 = allocate<Struct8BytesNestedInt>().ref;
+
+ a0.a0.a0 = -1;
+ a0.a0.a1 = 2;
+ a0.a1.a0 = -3;
+ a0.a1.a1 = 4;
+ a1.a0.a0 = -5;
+ a1.a0.a1 = 6;
+ a1.a1.a0 = -7;
+ a1.a1.a1 = 8;
+
+ final result = returnStruct16BytesNestedInt(a0, a1);
+
+ print("result = $result");
+
+ Expect.equals(a0.a0.a0, result.a0.a0.a0);
+ Expect.equals(a0.a0.a1, result.a0.a0.a1);
+ Expect.equals(a0.a1.a0, result.a0.a1.a0);
+ Expect.equals(a0.a1.a1, result.a0.a1.a1);
+ Expect.equals(a1.a0.a0, result.a1.a0.a0);
+ Expect.equals(a1.a0.a1, result.a1.a0.a1);
+ Expect.equals(a1.a1.a0, result.a1.a1.a0);
+ Expect.equals(a1.a1.a1, result.a1.a1.a1);
+
+ free(a0.addressOf);
+ free(a1.addressOf);
+}
+
+final returnStruct32BytesNestedInt = ffiTestFunctions.lookupFunction<
+ Struct32BytesNestedInt Function(
+ Struct16BytesNestedInt, Struct16BytesNestedInt),
+ Struct32BytesNestedInt Function(Struct16BytesNestedInt,
+ Struct16BytesNestedInt)>("ReturnStruct32BytesNestedInt");
+
+/// Even deeper nested struct to test recursive member access.
+void testReturnStruct32BytesNestedInt() {
+ Struct16BytesNestedInt a0 = allocate<Struct16BytesNestedInt>().ref;
+ Struct16BytesNestedInt a1 = allocate<Struct16BytesNestedInt>().ref;
+
+ a0.a0.a0.a0 = -1;
+ a0.a0.a0.a1 = 2;
+ a0.a0.a1.a0 = -3;
+ a0.a0.a1.a1 = 4;
+ a0.a1.a0.a0 = -5;
+ a0.a1.a0.a1 = 6;
+ a0.a1.a1.a0 = -7;
+ a0.a1.a1.a1 = 8;
+ a1.a0.a0.a0 = -9;
+ a1.a0.a0.a1 = 10;
+ a1.a0.a1.a0 = -11;
+ a1.a0.a1.a1 = 12;
+ a1.a1.a0.a0 = -13;
+ a1.a1.a0.a1 = 14;
+ a1.a1.a1.a0 = -15;
+ a1.a1.a1.a1 = 16;
+
+ final result = returnStruct32BytesNestedInt(a0, a1);
+
+ print("result = $result");
+
+ Expect.equals(a0.a0.a0.a0, result.a0.a0.a0.a0);
+ Expect.equals(a0.a0.a0.a1, result.a0.a0.a0.a1);
+ Expect.equals(a0.a0.a1.a0, result.a0.a0.a1.a0);
+ Expect.equals(a0.a0.a1.a1, result.a0.a0.a1.a1);
+ Expect.equals(a0.a1.a0.a0, result.a0.a1.a0.a0);
+ Expect.equals(a0.a1.a0.a1, result.a0.a1.a0.a1);
+ Expect.equals(a0.a1.a1.a0, result.a0.a1.a1.a0);
+ Expect.equals(a0.a1.a1.a1, result.a0.a1.a1.a1);
+ Expect.equals(a1.a0.a0.a0, result.a1.a0.a0.a0);
+ Expect.equals(a1.a0.a0.a1, result.a1.a0.a0.a1);
+ Expect.equals(a1.a0.a1.a0, result.a1.a0.a1.a0);
+ Expect.equals(a1.a0.a1.a1, result.a1.a0.a1.a1);
+ Expect.equals(a1.a1.a0.a0, result.a1.a1.a0.a0);
+ Expect.equals(a1.a1.a0.a1, result.a1.a1.a0.a1);
+ Expect.equals(a1.a1.a1.a0, result.a1.a1.a1.a0);
+ Expect.equals(a1.a1.a1.a1, result.a1.a1.a1.a1);
+
+ free(a0.addressOf);
+ free(a1.addressOf);
+}
+
+final returnStructNestedIntStructAlignmentInt16 =
+ ffiTestFunctions.lookupFunction<
+ StructNestedIntStructAlignmentInt16 Function(
+ StructAlignmentInt16, StructAlignmentInt16),
+ StructNestedIntStructAlignmentInt16 Function(StructAlignmentInt16,
+ StructAlignmentInt16)>("ReturnStructNestedIntStructAlignmentInt16");
+
+/// Test alignment and padding of nested struct with 16 byte int.
+void testReturnStructNestedIntStructAlignmentInt16() {
+ StructAlignmentInt16 a0 = allocate<StructAlignmentInt16>().ref;
+ StructAlignmentInt16 a1 = allocate<StructAlignmentInt16>().ref;
+
+ a0.a0 = -1;
+ a0.a1 = 2;
+ a0.a2 = -3;
+ a1.a0 = 4;
+ a1.a1 = -5;
+ a1.a2 = 6;
+
+ final result = returnStructNestedIntStructAlignmentInt16(a0, a1);
+
+ print("result = $result");
+
+ Expect.equals(a0.a0, result.a0.a0);
+ Expect.equals(a0.a1, result.a0.a1);
+ Expect.equals(a0.a2, result.a0.a2);
+ Expect.equals(a1.a0, result.a1.a0);
+ Expect.equals(a1.a1, result.a1.a1);
+ Expect.equals(a1.a2, result.a1.a2);
+
+ free(a0.addressOf);
+ free(a1.addressOf);
+}
+
+final returnStructNestedIntStructAlignmentInt32 =
+ ffiTestFunctions.lookupFunction<
+ StructNestedIntStructAlignmentInt32 Function(
+ StructAlignmentInt32, StructAlignmentInt32),
+ StructNestedIntStructAlignmentInt32 Function(StructAlignmentInt32,
+ StructAlignmentInt32)>("ReturnStructNestedIntStructAlignmentInt32");
+
+/// Test alignment and padding of nested struct with 32 byte int.
+void testReturnStructNestedIntStructAlignmentInt32() {
+ StructAlignmentInt32 a0 = allocate<StructAlignmentInt32>().ref;
+ StructAlignmentInt32 a1 = allocate<StructAlignmentInt32>().ref;
+
+ a0.a0 = -1;
+ a0.a1 = 2;
+ a0.a2 = -3;
+ a1.a0 = 4;
+ a1.a1 = -5;
+ a1.a2 = 6;
+
+ final result = returnStructNestedIntStructAlignmentInt32(a0, a1);
+
+ print("result = $result");
+
+ Expect.equals(a0.a0, result.a0.a0);
+ Expect.equals(a0.a1, result.a0.a1);
+ Expect.equals(a0.a2, result.a0.a2);
+ Expect.equals(a1.a0, result.a1.a0);
+ Expect.equals(a1.a1, result.a1.a1);
+ Expect.equals(a1.a2, result.a1.a2);
+
+ free(a0.addressOf);
+ free(a1.addressOf);
+}
+
+final returnStructNestedIntStructAlignmentInt64 =
+ ffiTestFunctions.lookupFunction<
+ StructNestedIntStructAlignmentInt64 Function(
+ StructAlignmentInt64, StructAlignmentInt64),
+ StructNestedIntStructAlignmentInt64 Function(StructAlignmentInt64,
+ StructAlignmentInt64)>("ReturnStructNestedIntStructAlignmentInt64");
+
+/// Test alignment and padding of nested struct with 64 byte int.
+void testReturnStructNestedIntStructAlignmentInt64() {
+ StructAlignmentInt64 a0 = allocate<StructAlignmentInt64>().ref;
+ StructAlignmentInt64 a1 = allocate<StructAlignmentInt64>().ref;
+
+ a0.a0 = -1;
+ a0.a1 = 2;
+ a0.a2 = -3;
+ a1.a0 = 4;
+ a1.a1 = -5;
+ a1.a2 = 6;
+
+ final result = returnStructNestedIntStructAlignmentInt64(a0, a1);
+
+ print("result = $result");
+
+ Expect.equals(a0.a0, result.a0.a0);
+ Expect.equals(a0.a1, result.a0.a1);
+ Expect.equals(a0.a2, result.a0.a2);
+ Expect.equals(a1.a0, result.a1.a0);
+ Expect.equals(a1.a1, result.a1.a1);
+ Expect.equals(a1.a2, result.a1.a2);
+
+ free(a0.addressOf);
+ free(a1.addressOf);
+}
+
+final returnStructNestedIrregularEvenBigger = ffiTestFunctions.lookupFunction<
+ StructNestedIrregularEvenBigger Function(Uint64,
+ StructNestedIrregularBigger, StructNestedIrregularBigger, Double),
+ StructNestedIrregularEvenBigger Function(
+ int,
+ StructNestedIrregularBigger,
+ StructNestedIrregularBigger,
+ double)>("ReturnStructNestedIrregularEvenBigger");
+
+/// Return big irregular struct as smoke test.
+void testReturnStructNestedIrregularEvenBigger() {
+ int a0;
+ StructNestedIrregularBigger a1 = allocate<StructNestedIrregularBigger>().ref;
+ StructNestedIrregularBigger a2 = allocate<StructNestedIrregularBigger>().ref;
+ double a3;
+
+ a0 = 1;
+ a1.a0.a0 = 2;
+ a1.a0.a1.a0.a0 = -3;
+ a1.a0.a1.a0.a1 = 4;
+ a1.a0.a1.a1.a0 = -5.0;
+ a1.a0.a2 = 6;
+ a1.a0.a3.a0.a0 = -7.0;
+ a1.a0.a3.a1 = 8.0;
+ a1.a0.a4 = 9;
+ a1.a0.a5.a0.a0 = 10.0;
+ a1.a0.a5.a1.a0 = -11.0;
+ a1.a0.a6 = 12;
+ a1.a1.a0.a0 = -13;
+ a1.a1.a0.a1 = 14;
+ a1.a1.a1.a0 = -15.0;
+ a1.a2 = 16.0;
+ a1.a3 = -17.0;
+ a2.a0.a0 = 18;
+ a2.a0.a1.a0.a0 = -19;
+ a2.a0.a1.a0.a1 = 20;
+ a2.a0.a1.a1.a0 = -21.0;
+ a2.a0.a2 = 22;
+ a2.a0.a3.a0.a0 = -23.0;
+ a2.a0.a3.a1 = 24.0;
+ a2.a0.a4 = 25;
+ a2.a0.a5.a0.a0 = 26.0;
+ a2.a0.a5.a1.a0 = -27.0;
+ a2.a0.a6 = 28;
+ a2.a1.a0.a0 = -29;
+ a2.a1.a0.a1 = 30;
+ a2.a1.a1.a0 = -31.0;
+ a2.a2 = 32.0;
+ a2.a3 = -33.0;
+ a3 = 34.0;
+
+ final result = returnStructNestedIrregularEvenBigger(a0, a1, a2, a3);
+
+ print("result = $result");
+
+ Expect.equals(a0, result.a0);
+ Expect.equals(a1.a0.a0, result.a1.a0.a0);
+ Expect.equals(a1.a0.a1.a0.a0, result.a1.a0.a1.a0.a0);
+ Expect.equals(a1.a0.a1.a0.a1, result.a1.a0.a1.a0.a1);
+ Expect.approxEquals(a1.a0.a1.a1.a0, result.a1.a0.a1.a1.a0);
+ Expect.equals(a1.a0.a2, result.a1.a0.a2);
+ Expect.approxEquals(a1.a0.a3.a0.a0, result.a1.a0.a3.a0.a0);
+ Expect.approxEquals(a1.a0.a3.a1, result.a1.a0.a3.a1);
+ Expect.equals(a1.a0.a4, result.a1.a0.a4);
+ Expect.approxEquals(a1.a0.a5.a0.a0, result.a1.a0.a5.a0.a0);
+ Expect.approxEquals(a1.a0.a5.a1.a0, result.a1.a0.a5.a1.a0);
+ Expect.equals(a1.a0.a6, result.a1.a0.a6);
+ Expect.equals(a1.a1.a0.a0, result.a1.a1.a0.a0);
+ Expect.equals(a1.a1.a0.a1, result.a1.a1.a0.a1);
+ Expect.approxEquals(a1.a1.a1.a0, result.a1.a1.a1.a0);
+ Expect.approxEquals(a1.a2, result.a1.a2);
+ Expect.approxEquals(a1.a3, result.a1.a3);
+ Expect.equals(a2.a0.a0, result.a2.a0.a0);
+ Expect.equals(a2.a0.a1.a0.a0, result.a2.a0.a1.a0.a0);
+ Expect.equals(a2.a0.a1.a0.a1, result.a2.a0.a1.a0.a1);
+ Expect.approxEquals(a2.a0.a1.a1.a0, result.a2.a0.a1.a1.a0);
+ Expect.equals(a2.a0.a2, result.a2.a0.a2);
+ Expect.approxEquals(a2.a0.a3.a0.a0, result.a2.a0.a3.a0.a0);
+ Expect.approxEquals(a2.a0.a3.a1, result.a2.a0.a3.a1);
+ Expect.equals(a2.a0.a4, result.a2.a0.a4);
+ Expect.approxEquals(a2.a0.a5.a0.a0, result.a2.a0.a5.a0.a0);
+ Expect.approxEquals(a2.a0.a5.a1.a0, result.a2.a0.a5.a1.a0);
+ Expect.equals(a2.a0.a6, result.a2.a0.a6);
+ Expect.equals(a2.a1.a0.a0, result.a2.a1.a0.a0);
+ Expect.equals(a2.a1.a0.a1, result.a2.a1.a0.a1);
+ Expect.approxEquals(a2.a1.a1.a0, result.a2.a1.a1.a0);
+ Expect.approxEquals(a2.a2, result.a2.a2);
+ Expect.approxEquals(a2.a3, result.a2.a3);
+ Expect.approxEquals(a3, result.a3);
+
+ free(a1.addressOf);
+ free(a2.addressOf);
+}
diff --git a/tests/ffi_2/generator/c_types.dart b/tests/ffi_2/generator/c_types.dart
index f2948fa..56736bf5 100644
--- a/tests/ffi_2/generator/c_types.dart
+++ b/tests/ffi_2/generator/c_types.dart
@@ -140,11 +140,19 @@
/// To disambiguate same size structs.
final String suffix;
+ /// To override names.
+ final String overrideName;
+
StructType(List<CType> memberTypes)
: this.members = generateMemberNames(memberTypes),
- this.suffix = "";
+ this.suffix = "",
+ this.overrideName = "";
StructType.disambiguate(List<CType> memberTypes, this.suffix)
- : this.members = generateMemberNames(memberTypes);
+ : this.members = generateMemberNames(memberTypes),
+ this.overrideName = "";
+ StructType.override(List<CType> memberTypes, this.overrideName)
+ : this.members = generateMemberNames(memberTypes),
+ this.suffix = "";
List<CType> get memberTypes => members.map((a) => a.type).toList();
@@ -191,6 +199,9 @@
String get name {
String result = "Struct";
+ if (overrideName != "") {
+ return result + overrideName;
+ }
if (hasSize) {
result += "${size}Byte" + (size != 1 ? "s" : "");
}
diff --git a/tests/ffi_2/generator/structs_by_value_tests_configuration.dart b/tests/ffi_2/generator/structs_by_value_tests_configuration.dart
index 46f5108..718caea 100644
--- a/tests/ffi_2/generator/structs_by_value_tests_configuration.dart
+++ b/tests/ffi_2/generator/structs_by_value_tests_configuration.dart
@@ -255,6 +255,41 @@
int64,
"""
Test alignment and padding of 64 byte int within struct."""),
+ FunctionType(List.filled(10, struct8bytesNestedInt), int64, """
+Simple nested struct. No alignment gaps on any architectures.
+10 arguments exhaust registers on all platforms."""),
+ FunctionType(List.filled(10, struct8bytesNestedFloat), float, """
+Simple nested struct. No alignment gaps on any architectures.
+10 arguments exhaust fpu registers on all platforms."""),
+ FunctionType(List.filled(10, struct8bytesNestedFloat2), float, """
+Simple nested struct. No alignment gaps on any architectures.
+10 arguments exhaust fpu registers on all platforms.
+The nesting is irregular, testing homogenous float rules on arm and arm64,
+and the fpu register usage on x64."""),
+ FunctionType(List.filled(10, struct8bytesNestedMixed), double_, """
+Simple nested struct. No alignment gaps on any architectures.
+10 arguments exhaust all registers on all platforms."""),
+ FunctionType(List.filled(2, struct16bytesNestedInt), int64, """
+Deeper nested struct to test recursive member access."""),
+ FunctionType(List.filled(2, struct32bytesNestedInt), int64, """
+Even deeper nested struct to test recursive member access."""),
+ FunctionType(
+ [structNestedAlignmentInt16],
+ int64,
+ """
+Test alignment and padding of nested struct with 16 byte int."""),
+ FunctionType(
+ [structNestedAlignmentInt32],
+ int64,
+ """
+Test alignment and padding of nested struct with 32 byte int."""),
+ FunctionType(
+ [structNestedAlignmentInt64],
+ int64,
+ """
+Test alignment and padding of nested struct with 64 byte int."""),
+ FunctionType(List.filled(4, structNestedEvenBigger), double_, """
+Return big irregular struct as smoke test."""),
FunctionType(struct1byteInt.memberTypes, struct1byteInt, """
Smallest struct with data."""),
FunctionType(struct3bytesInt.memberTypes, struct3bytesInt, """
@@ -358,6 +393,31 @@
Test alignment and padding of 32 byte int within struct."""),
FunctionType(structAlignmentInt64.memberTypes, structAlignmentInt64, """
Test alignment and padding of 64 byte int within struct."""),
+ FunctionType(struct8bytesNestedInt.memberTypes, struct8bytesNestedInt, """
+Simple nested struct."""),
+ FunctionType(struct8bytesNestedFloat.memberTypes, struct8bytesNestedFloat, """
+Simple nested struct with floats."""),
+ FunctionType(
+ struct8bytesNestedFloat2.memberTypes, struct8bytesNestedFloat2, """
+The nesting is irregular, testing homogenous float rules on arm and arm64,
+and the fpu register usage on x64."""),
+ FunctionType(struct8bytesNestedMixed.memberTypes, struct8bytesNestedMixed, """
+Simple nested struct with mixed members."""),
+ FunctionType(struct16bytesNestedInt.memberTypes, struct16bytesNestedInt, """
+Deeper nested struct to test recursive member access."""),
+ FunctionType(struct32bytesNestedInt.memberTypes, struct32bytesNestedInt, """
+Even deeper nested struct to test recursive member access."""),
+ FunctionType(
+ structNestedAlignmentInt16.memberTypes, structNestedAlignmentInt16, """
+Test alignment and padding of nested struct with 16 byte int."""),
+ FunctionType(
+ structNestedAlignmentInt32.memberTypes, structNestedAlignmentInt32, """
+Test alignment and padding of nested struct with 32 byte int."""),
+ FunctionType(
+ structNestedAlignmentInt64.memberTypes, structNestedAlignmentInt64, """
+Test alignment and padding of nested struct with 64 byte int."""),
+ FunctionType(structNestedEvenBigger.memberTypes, structNestedEvenBigger, """
+Return big irregular struct as smoke test."""),
];
final structs = [
@@ -366,6 +426,7 @@
struct3bytesInt,
struct3bytesInt2,
struct4bytesInt,
+ struct4bytesFloat,
struct7bytesInt,
struct7bytesInt2,
struct8bytesInt,
@@ -387,6 +448,18 @@
structAlignmentInt16,
structAlignmentInt32,
structAlignmentInt64,
+ struct8bytesNestedInt,
+ struct8bytesNestedFloat,
+ struct8bytesNestedFloat2,
+ struct8bytesNestedMixed,
+ struct16bytesNestedInt,
+ struct32bytesNestedInt,
+ structNestedAlignmentInt16,
+ structNestedAlignmentInt32,
+ structNestedAlignmentInt64,
+ structNestedBig,
+ structNestedBigger,
+ structNestedEvenBigger,
];
/// Using empty structs is undefined behavior in C.
@@ -396,6 +469,7 @@
final struct3bytesInt = StructType(List.filled(3, uint8));
final struct3bytesInt2 = StructType.disambiguate([int16, int8], "2ByteAligned");
final struct4bytesInt = StructType([int16, int16]);
+final struct4bytesFloat = StructType([float]);
final struct7bytesInt = StructType(List.filled(7, uint8));
final struct7bytesInt2 =
StructType.disambiguate([int32, int16, int8], "4ByteAligned");
@@ -444,3 +518,39 @@
final structAlignmentInt16 = StructType([int8, int16, int8]);
final structAlignmentInt32 = StructType([int8, int32, int8]);
final structAlignmentInt64 = StructType([int8, int64, int8]);
+
+final struct8bytesNestedInt = StructType([struct4bytesInt, struct4bytesInt]);
+final struct8bytesNestedFloat =
+ StructType([struct4bytesFloat, struct4bytesFloat]);
+final struct8bytesNestedFloat2 =
+ StructType.disambiguate([struct4bytesFloat, float], "2");
+final struct8bytesNestedMixed =
+ StructType([struct4bytesInt, struct4bytesFloat]);
+
+final struct16bytesNestedInt =
+ StructType([struct8bytesNestedInt, struct8bytesNestedInt]);
+final struct32bytesNestedInt =
+ StructType([struct16bytesNestedInt, struct16bytesNestedInt]);
+
+final structNestedAlignmentInt16 = StructType.disambiguate(
+ List.filled(2, structAlignmentInt16), structAlignmentInt16.name);
+final structNestedAlignmentInt32 = StructType.disambiguate(
+ List.filled(2, structAlignmentInt32), structAlignmentInt32.name);
+final structNestedAlignmentInt64 = StructType.disambiguate(
+ List.filled(2, structAlignmentInt64), structAlignmentInt64.name);
+
+final structNestedBig = StructType.override([
+ uint16,
+ struct8bytesNestedMixed,
+ uint16,
+ struct8bytesNestedFloat2,
+ uint16,
+ struct8bytesNestedFloat,
+ uint16
+], "NestedIrregularBig");
+final structNestedBigger = StructType.override(
+ [structNestedBig, struct8bytesNestedMixed, float, double_],
+ "NestedIrregularBigger");
+final structNestedEvenBigger = StructType.override(
+ [uint64, structNestedBigger, structNestedBigger, double_],
+ "NestedIrregularEvenBigger");
diff --git a/tests/ffi_2/structs_nested_test.dart b/tests/ffi_2/structs_nested_test.dart
new file mode 100644
index 0000000..7083044
--- /dev/null
+++ b/tests/ffi_2/structs_nested_test.dart
@@ -0,0 +1,105 @@
+// Copyright (c) 2020, 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.
+//
+// This tests the non trampoline aspects of nested structs.
+//
+// SharedObjects=ffi_test_functions
+
+import 'dart:ffi';
+
+import "package:expect/expect.dart";
+import "package:ffi/ffi.dart";
+
+import 'dylib_utils.dart';
+
+final ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions");
+void main() {
+ for (int i = 0; i < 10; ++i) {
+ testSizeOf();
+ testAllocate();
+ testRead();
+ testWrite();
+ testCopy();
+ }
+}
+
+class Struct4BytesHomogeneousInt16 extends Struct {
+ @Int16()
+ int a0;
+
+ @Int16()
+ int a1;
+
+ String toString() => "(${a0}, ${a1})";
+}
+
+class Struct8BytesNestedInt extends Struct {
+ Struct4BytesHomogeneousInt16 a0;
+
+ Struct4BytesHomogeneousInt16 a1;
+
+ String toString() => "(${a0}, ${a1})";
+}
+
+void testSizeOf() {
+ print(sizeOf<Struct8BytesNestedInt>());
+ Expect.equals(8, sizeOf<Struct8BytesNestedInt>());
+}
+
+void testAllocate() {
+ final p = allocate<Struct8BytesNestedInt>();
+ Expect.type<Pointer<Struct8BytesNestedInt>>(p);
+ print(p);
+ free(p);
+}
+
+/// Test that reading does not segfault, even uninitialized.
+void testRead() {
+ print("read");
+ final p = allocate<Struct8BytesNestedInt>();
+ print(p);
+ print(p.ref.runtimeType);
+ print(p.ref.addressOf);
+ print(p.ref.addressOf.address);
+ print(p.ref.a0.runtimeType);
+ print(p.ref.a0.addressOf);
+ print(p.ref.a0.a0);
+ free(p);
+ print("read");
+}
+
+void testWrite() {
+ print("write");
+ final p = allocate<Struct8BytesNestedInt>(count: 2);
+ p[0].a0.a0 = 12;
+ p[0].a0.a1 = 13;
+ p[0].a1.a0 = 14;
+ p[0].a1.a1 = 15;
+ p[1].a0.a0 = 16;
+ p[1].a0.a1 = 17;
+ p[1].a1.a0 = 18;
+ p[1].a1.a1 = 19;
+ Expect.equals(12, p[0].a0.a0);
+ Expect.equals(13, p[0].a0.a1);
+ Expect.equals(14, p[0].a1.a0);
+ Expect.equals(15, p[0].a1.a1);
+ Expect.equals(16, p[1].a0.a0);
+ Expect.equals(17, p[1].a0.a1);
+ Expect.equals(18, p[1].a1.a0);
+ Expect.equals(19, p[1].a1.a1);
+ free(p);
+ print("written");
+}
+
+void testCopy() {
+ print("copy");
+ final p = allocate<Struct8BytesNestedInt>();
+ p.ref.a0.a0 = 12;
+ p.ref.a0.a1 = 13;
+ p.ref.a1 = p.ref.a0;
+ Expect.equals(12, p.ref.a1.a0);
+ Expect.equals(13, p.ref.a1.a1);
+ free(p);
+ print("copied");
+}
diff --git a/tests/ffi_2/vmspecific_static_checks_test.dart b/tests/ffi_2/vmspecific_static_checks_test.dart
index dfa52a9..69efa84 100644
--- a/tests/ffi_2/vmspecific_static_checks_test.dart
+++ b/tests/ffi_2/vmspecific_static_checks_test.dart
@@ -526,3 +526,7 @@
Pointer.fromFunction<EmptyStruct Function()>(//# 1105: compile-time error
_returnEmptyStruct); //# 1105: compile-time error
}
+
+class HasNestedEmptyStruct extends Struct {
+ EmptyStruct nestedEmptyStruct; //# 1106: compile-time error
+}
diff --git a/tools/VERSION b/tools/VERSION
index f676ec5..f214593 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 12
PATCH 0
-PRERELEASE 167
+PRERELEASE 168
PRERELEASE_PATCH 0
\ No newline at end of file