Version 2.13.0-153.0.dev
Merge commit '77dc74bcd57b8c3b0d1a67737275fb2206e46aeb' into 'dev'
diff --git a/BUILD.gn b/BUILD.gn
index 5d3846c..c25c176 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -23,6 +23,7 @@
testonly = true
}
deps = [
+ ":analysis_server",
":create_sdk",
":dart2js",
":dartanalyzer",
@@ -30,9 +31,6 @@
":runtime",
":samples",
]
- if (dart_target_arch != "arm") {
- deps += [ ":analysis_server" ]
- }
}
group("runtime") {
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index 32821ea2..f1ac8c2 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -271,15 +271,12 @@
cflags += [ "-fcolor-diagnostics" ]
}
- # C++11 compiler flags setup.
+ # C++ standard compiler flags setup.
# ---------------------------
if (is_win) {
- # Up-to-date toolchain MSVC doesn't support c++11 flag any longer.
- cc_std = [ "/std:c++14" ]
- } else if (is_fuchsia) {
- cc_std = [ "-std=c++17" ]
+ cc_std = [ "/std:c++17" ]
} else {
- cc_std = [ "-std=c++11" ]
+ cc_std = [ "-std=c++17" ]
}
cflags_cc += cc_std
cflags_objcc += cc_std
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 c2b2f95..0850c7c 100644
--- a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
@@ -3904,6 +3904,68 @@
}
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<Message Function(String name)> templateFfiPackedAnnotation =
+ const Template<Message Function(String name)>(
+ messageTemplate:
+ r"""Struct '#name' must have at most one 'Packed' annotation.""",
+ withArguments: _withArgumentsFfiPackedAnnotation);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String name)> codeFfiPackedAnnotation =
+ const Code<Message Function(String name)>(
+ "FfiPackedAnnotation",
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsFfiPackedAnnotation(String name) {
+ if (name.isEmpty) throw 'No name provided';
+ name = demangleMixinApplicationName(name);
+ return new Message(codeFfiPackedAnnotation,
+ message:
+ """Struct '${name}' must have at most one 'Packed' annotation.""",
+ arguments: {'name': name});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Null> codeFfiPackedAnnotationAlignment =
+ messageFfiPackedAnnotationAlignment;
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const MessageCode messageFfiPackedAnnotationAlignment = const MessageCode(
+ "FfiPackedAnnotationAlignment",
+ message: r"""Only packing to 1, 2, 4, 8, and 16 bytes is supported.""");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+ Message Function(
+ String name,
+ String
+ name2)> templateFfiPackedNestingNonPacked = const Template<
+ Message Function(String name, String name2)>(
+ messageTemplate:
+ r"""Nesting the non-packed or less tightly packed struct '#name' in a packed struct '#name2' is not supported.""",
+ withArguments: _withArgumentsFfiPackedNestingNonPacked);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String name, String name2)>
+ codeFfiPackedNestingNonPacked =
+ const Code<Message Function(String name, String name2)>(
+ "FfiPackedNestingNonPacked",
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsFfiPackedNestingNonPacked(String name, String name2) {
+ if (name.isEmpty) throw 'No name provided';
+ name = demangleMixinApplicationName(name);
+ if (name2.isEmpty) throw 'No name provided';
+ name2 = demangleMixinApplicationName(name2);
+ return new Message(codeFfiPackedNestingNonPacked,
+ message:
+ """Nesting the non-packed or less tightly packed struct '${name}' in a packed struct '${name2}' is not supported.""",
+ arguments: {'name': name, 'name2': name2});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Template<Message Function(String name)> templateFfiSizeAnnotation =
const Template<Message Function(String name)>(
messageTemplate:
diff --git a/pkg/analysis_server/lib/lsp_protocol/protocol_custom_generated.dart b/pkg/analysis_server/lib/lsp_protocol/protocol_custom_generated.dart
index d55238b..c56e6e9 100644
--- a/pkg/analysis_server/lib/lsp_protocol/protocol_custom_generated.dart
+++ b/pkg/analysis_server/lib/lsp_protocol/protocol_custom_generated.dart
@@ -7,11 +7,7 @@
// "pkg/analysis_server/tool/lsp_spec/generate_all.dart".
// ignore_for_file: annotate_overrides
-// ignore_for_file: deprecated_member_use
-// ignore_for_file: deprecated_member_use_from_same_package
-// ignore_for_file: unnecessary_brace_in_string_interps
// ignore_for_file: unnecessary_parenthesis
-// ignore_for_file: unused_import
// ignore_for_file: unused_shown_name
import 'dart:core' hide deprecated;
diff --git a/pkg/analysis_server/lib/lsp_protocol/protocol_generated.dart b/pkg/analysis_server/lib/lsp_protocol/protocol_generated.dart
index d3c551b..6559e10 100644
--- a/pkg/analysis_server/lib/lsp_protocol/protocol_generated.dart
+++ b/pkg/analysis_server/lib/lsp_protocol/protocol_generated.dart
@@ -7,11 +7,7 @@
// "pkg/analysis_server/tool/lsp_spec/generate_all.dart".
// ignore_for_file: annotate_overrides
-// ignore_for_file: deprecated_member_use
-// ignore_for_file: deprecated_member_use_from_same_package
-// ignore_for_file: unnecessary_brace_in_string_interps
// ignore_for_file: unnecessary_parenthesis
-// ignore_for_file: unused_import
// ignore_for_file: unused_shown_name
import 'dart:core' hide deprecated;
@@ -9537,7 +9533,7 @@
item != null
? TextDocumentContentChangeEvent2.fromJson(item)
: null)
- : (throw '''${item} was not one of (TextDocumentContentChangeEvent1, TextDocumentContentChangeEvent2)''')))
+ : (throw '''$item was not one of (TextDocumentContentChangeEvent1, TextDocumentContentChangeEvent2)''')))
?.cast<Either2<TextDocumentContentChangeEvent1, TextDocumentContentChangeEvent2>>()
?.toList();
return DidChangeTextDocumentParams(
@@ -30675,7 +30671,7 @@
: (TextEdit.canParse(item, nullLspJsonReporter)
? Either3<SnippetTextEdit, AnnotatedTextEdit, TextEdit>.t3(
item != null ? TextEdit.fromJson(item) : null)
- : (throw '''${item} was not one of (SnippetTextEdit, AnnotatedTextEdit, TextEdit)'''))))
+ : (throw '''$item was not one of (SnippetTextEdit, AnnotatedTextEdit, TextEdit)'''))))
?.cast<Either3<SnippetTextEdit, AnnotatedTextEdit, TextEdit>>()
?.toList();
return TextDocumentEdit(textDocument: textDocument, edits: edits);
@@ -33645,7 +33641,7 @@
item != null ? TextDocumentEdit.fromJson(item) : null)
: (CreateFile.canParse(item, nullLspJsonReporter)
? Either4<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>.t2(item != null ? CreateFile.fromJson(item) : null)
- : (RenameFile.canParse(item, nullLspJsonReporter) ? Either4<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>.t3(item != null ? RenameFile.fromJson(item) : null) : (DeleteFile.canParse(item, nullLspJsonReporter) ? Either4<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>.t4(item != null ? DeleteFile.fromJson(item) : null) : (item == null ? null : (throw '''${item} was not one of (TextDocumentEdit, CreateFile, RenameFile, DeleteFile)'''))))))
+ : (RenameFile.canParse(item, nullLspJsonReporter) ? Either4<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>.t3(item != null ? RenameFile.fromJson(item) : null) : (DeleteFile.canParse(item, nullLspJsonReporter) ? Either4<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>.t4(item != null ? DeleteFile.fromJson(item) : null) : (item == null ? null : (throw '''$item was not one of (TextDocumentEdit, CreateFile, RenameFile, DeleteFile)'''))))))
?.cast<Either4<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>>()
?.toList())
: (json['documentChanges'] == null ? null : (throw '''${json['documentChanges']} was not one of (List<TextDocumentEdit>, List<Either4<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>>)''')));
diff --git a/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart b/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart
index 1e17989..0836959 100644
--- a/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart
+++ b/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:dart_style/dart_style.dart';
+import 'package:meta/meta.dart';
import 'typescript.dart';
import 'typescript_parser.dart';
@@ -463,7 +464,7 @@
void _writeFromJsonCode(
IndentableStringBuffer buffer, TypeBase type, String valueCode,
- {bool allowsNull}) {
+ {bool allowsNull, bool requiresBracesInInterpolation = false}) {
type = resolveTypeAlias(type);
if (_isSimpleType(type)) {
@@ -493,7 +494,9 @@
_writeFromJsonCodeForLiteralUnion(buffer, type, valueCode,
allowsNull: allowsNull);
} else if (type is UnionType) {
- _writeFromJsonCodeForUnion(buffer, type, valueCode, allowsNull: allowsNull);
+ _writeFromJsonCodeForUnion(buffer, type, valueCode,
+ allowsNull: allowsNull,
+ requiresBracesInInterpolation: requiresBracesInInterpolation);
} else {
buffer.write('$valueCode');
}
@@ -513,7 +516,7 @@
void _writeFromJsonCodeForUnion(
IndentableStringBuffer buffer, UnionType union, String valueCode,
- {bool allowsNull}) {
+ {bool allowsNull, @required bool requiresBracesInInterpolation}) {
// Write a check against each type, eg.:
// x is y ? new Either.tx(x) : (...)
var hasIncompleteCondition = false;
@@ -532,7 +535,9 @@
// The code to construct a value with this "side" of the union.
buffer.write('${union.dartTypeWithTypeArgs}.t${i + 1}(');
_writeFromJsonCode(buffer, type, valueCode,
- allowsNull: allowsNull); // Call recursively!
+ allowsNull: allowsNull,
+ requiresBracesInInterpolation:
+ requiresBracesInInterpolation); // Call recursively!
buffer.write(')');
// If we output the type condition at the top, prepare for the next condition.
@@ -551,8 +556,10 @@
buffer.write('$valueCode == null ? null : (');
unclosedParens++;
}
+ var interpolation =
+ requiresBracesInInterpolation ? '\${$valueCode}' : '\$$valueCode';
buffer.write(
- "throw '''\${$valueCode} was not one of (${union.types.map((t) => t.dartTypeWithTypeArgs).join(', ')})'''");
+ "throw '''$interpolation was not one of (${union.types.map((t) => t.dartTypeWithTypeArgs).join(', ')})'''");
}
buffer.write(')' * unclosedParens);
}
@@ -578,7 +585,8 @@
for (final field in allFields) {
buffer.writeIndented('final ${field.name} = ');
_writeFromJsonCode(buffer, field.type, "json['${field.name}']",
- allowsNull: field.allowsNull || field.allowsUndefined);
+ allowsNull: field.allowsNull || field.allowsUndefined,
+ requiresBracesInInterpolation: true);
buffer.writeln(';');
}
buffer
diff --git a/pkg/analysis_server/tool/lsp_spec/generate_all.dart b/pkg/analysis_server/tool/lsp_spec/generate_all.dart
index ace8ef1..3475f3a 100644
--- a/pkg/analysis_server/tool/lsp_spec/generate_all.dart
+++ b/pkg/analysis_server/tool/lsp_spec/generate_all.dart
@@ -160,11 +160,7 @@
// "pkg/analysis_server/tool/lsp_spec/generate_all.dart".
// ignore_for_file: annotate_overrides
-// ignore_for_file: deprecated_member_use
-// ignore_for_file: deprecated_member_use_from_same_package
-// ignore_for_file: unnecessary_brace_in_string_interps
// ignore_for_file: unnecessary_parenthesis
-// ignore_for_file: unused_import
// ignore_for_file: unused_shown_name
import 'dart:core' hide deprecated;
diff --git a/pkg/front_end/lib/src/api_unstable/vm.dart b/pkg/front_end/lib/src/api_unstable/vm.dart
index 80da869..bf01283 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 @@
LocatedMessage,
messageFfiExceptionalReturnNull,
messageFfiExpectedConstant,
+ messageFfiPackedAnnotationAlignment,
noLength,
templateFfiDartTypeMismatch,
templateFfiEmptyStruct,
@@ -64,6 +65,8 @@
templateFfiFieldNoAnnotation,
templateFfiFieldNull,
templateFfiNotStatic,
+ templateFfiPackedAnnotation,
+ templateFfiPackedNestingNonPacked,
templateFfiSizeAnnotation,
templateFfiSizeAnnotationDimensions,
templateFfiStructGeneric,
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index 7d556ba..d25dfc3 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -329,6 +329,9 @@
FfiFieldNoAnnotation/analyzerCode: Fail
FfiFieldNull/analyzerCode: Fail
FfiNotStatic/analyzerCode: Fail
+FfiPackedAnnotation/analyzerCode: Fail
+FfiPackedAnnotationAlignment/analyzerCode: Fail
+FfiPackedNestingNonPacked/analyzerCode: Fail
FfiSizeAnnotation/analyzerCode: Fail
FfiSizeAnnotationDimensions/analyzerCode: Fail
FfiStructAnnotation/analyzerCode: Fail
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index abab3a0..31a8357 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -4279,6 +4279,21 @@
template: "Class '#name' cannot be extended or implemented."
external: test/ffi_test.dart
+FfiPackedAnnotation:
+ # Used by dart:ffi
+ template: "Struct '#name' must have at most one 'Packed' annotation."
+ external: test/ffi_test.dart
+
+FfiPackedAnnotationAlignment:
+ # Used by dart:ffi
+ template: "Only packing to 1, 2, 4, 8, and 16 bytes is supported."
+ external: test/ffi_test.dart
+
+FfiPackedNestingNonPacked:
+ # Used by dart:ffi
+ template: "Nesting the non-packed or less tightly packed struct '#name' in a packed struct '#name2' is not supported."
+ external: test/ffi_test.dart
+
FfiSizeAnnotation:
# Used by dart:ffi
template: "Field '#name' must have exactly one 'Array' annotation."
diff --git a/pkg/front_end/test/fasta/testing/suite.dart b/pkg/front_end/test/fasta/testing/suite.dart
index d4cc164..8702311 100644
--- a/pkg/front_end/test/fasta/testing/suite.dart
+++ b/pkg/front_end/test/fasta/testing/suite.dart
@@ -162,6 +162,8 @@
import 'package:vm/transformations/type_flow/transformer.dart' as type_flow;
import 'package:vm/transformations/pragma.dart' as type_flow;
+import '../../testing_utils.dart' show checkEnvironment;
+
import '../../utils/kernel_chain.dart'
show
ComponentResult,
@@ -376,13 +378,11 @@
steps.add(new MatchExpectation(
fullCompile ? "$fullPrefix.expect" : "$outlinePrefix.expect",
serializeFirst: false,
- isLastMatchStep: updateExpectations));
- if (!updateExpectations) {
- steps.add(new MatchExpectation(
- fullCompile ? "$fullPrefix.expect" : "$outlinePrefix.expect",
- serializeFirst: true,
- isLastMatchStep: true));
- }
+ isLastMatchStep: false));
+ steps.add(new MatchExpectation(
+ fullCompile ? "$fullPrefix.expect" : "$outlinePrefix.expect",
+ serializeFirst: true,
+ isLastMatchStep: true));
}
steps.add(const TypeCheck());
steps.add(const EnsureNoErrors());
@@ -723,6 +723,23 @@
static Future<FastaContext> create(
Chain suite, Map<String, String> environment) async {
+ const Set<String> knownEnvironmentKeys = {
+ "enableExtensionMethods",
+ "enableNonNullable",
+ "soundNullSafety",
+ "onlyCrashes",
+ "ignoreExpectations",
+ UPDATE_EXPECTATIONS,
+ UPDATE_COMMENTS,
+ "skipVm",
+ "semiFuzz",
+ "verify",
+ KERNEL_TEXT_SERIALIZATION,
+ "platformBinaries",
+ ENABLE_FULL_COMPILE,
+ };
+ checkEnvironment(environment, knownEnvironmentKeys);
+
String resolvedExecutable = Platform.environment['resolvedExecutable'] ??
Platform.resolvedExecutable;
Uri vm = Uri.base.resolveUri(new Uri.file(resolvedExecutable));
diff --git a/pkg/front_end/test/spell_checking_list_common.txt b/pkg/front_end/test/spell_checking_list_common.txt
index 25e141b..cb51c68 100644
--- a/pkg/front_end/test/spell_checking_list_common.txt
+++ b/pkg/front_end/test/spell_checking_list_common.txt
@@ -2113,6 +2113,8 @@
package
packaged
packages
+packed
+packing
pad
padded
padding
@@ -3042,6 +3044,7 @@
thumb
thunk
thus
+tightly
time
timeline
times
diff --git a/pkg/front_end/test/testing_utils.dart b/pkg/front_end/test/testing_utils.dart
index 6781673..502ac99 100644
--- a/pkg/front_end/test/testing_utils.dart
+++ b/pkg/front_end/test/testing_utils.dart
@@ -43,7 +43,9 @@
Set<String> environmentKeys = environment.keys.toSet();
environmentKeys.removeAll(knownEnvironmentKeys);
if (environmentKeys.isNotEmpty) {
- throw "Unknown environment(s) given: ${environmentKeys.toList()}.\n"
- "Knows about ${knownEnvironmentKeys.toList()}";
+ throw "Unknown environment(s) given:"
+ "\n - ${environmentKeys.join("\n- ")}\n"
+ "Knows about these environment(s):"
+ "\n - ${knownEnvironmentKeys.join("\n - ")}";
}
}
diff --git a/pkg/front_end/test/utils/kernel_chain.dart b/pkg/front_end/test/utils/kernel_chain.dart
index f7b52fc..f3d539c 100644
--- a/pkg/front_end/test/utils/kernel_chain.dart
+++ b/pkg/front_end/test/utils/kernel_chain.dart
@@ -84,7 +84,9 @@
expectationSet["ExpectationFileMissing"];
Future<Result<O>> match<O>(String suffix, String actual, Uri uri, O output,
- {Expectation onMismatch}) async {
+ {Expectation onMismatch, bool overwriteUpdateExpectationsWith}) async {
+ bool updateExpectations =
+ overwriteUpdateExpectationsWith ?? this.updateExpectations;
actual = actual.trim();
if (actual.isNotEmpty) {
actual += "\n";
@@ -338,7 +340,8 @@
return context.match<ComponentResult>(suffix, actual, uri, result,
onMismatch: serializeFirst
? context.expectationFileMismatchSerialized
- : context.expectationFileMismatch);
+ : context.expectationFileMismatch,
+ overwriteUpdateExpectationsWith: serializeFirst ? false : null);
}
}
diff --git a/pkg/front_end/testcases/general/ffi_sample.dart.weak.transformed.expect b/pkg/front_end/testcases/general/ffi_sample.dart.weak.transformed.expect
index eefcad1..4e3bcdc 100644
--- a/pkg/front_end/testcases/general/ffi_sample.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/general/ffi_sample.dart.weak.transformed.expect
@@ -6,9 +6,9 @@
import "dart:ffi";
import "package:ffi/ffi.dart";
-@#C6
+@#C7
class Coordinate extends ffi::Struct {
- static final field core::int* #sizeOf = (#C9).{core::List::[]}(ffi::_abi());
+ static final field core::int* #sizeOf = (#C10).{core::List::[]}(ffi::_abi());
@#C12
constructor #fromTypedDataBase(dynamic #pointer) → dynamic
: super ffi::Struct::_fromPointer(#pointer)
@@ -51,14 +51,14 @@
#C2 = TypeLiteralConstant(ffi::Double)
#C3 = TypeLiteralConstant(ffi::Pointer<ffi::NativeType>)
#C4 = <core::Type>[#C2, #C2, #C3]
- #C5 = ffi::_FfiStructLayout {fieldTypes:#C4}
- #C6 = core::pragma {name:#C1, options:#C5}
- #C7 = 24
- #C8 = 20
- #C9 = <core::int*>[#C7, #C8, #C7]
- #C10 = "vm:entry-point"
- #C11 = null
- #C12 = core::pragma {name:#C10, options:#C11}
+ #C5 = null
+ #C6 = ffi::_FfiStructLayout {fieldTypes:#C4, packing:#C5}
+ #C7 = core::pragma {name:#C1, options:#C6}
+ #C8 = 24
+ #C9 = 20
+ #C10 = <core::int*>[#C8, #C9, #C8]
+ #C11 = "vm:entry-point"
+ #C12 = core::pragma {name:#C11, options:#C5}
#C13 = 0
#C14 = <core::int*>[#C13, #C13, #C13]
#C15 = 8
diff --git a/pkg/front_end/testcases/incremental/ffi_01.yaml.world.1.expect b/pkg/front_end/testcases/incremental/ffi_01.yaml.world.1.expect
index b112423..5946f0f 100644
--- a/pkg/front_end/testcases/incremental/ffi_01.yaml.world.1.expect
+++ b/pkg/front_end/testcases/incremental/ffi_01.yaml.world.1.expect
@@ -3,9 +3,9 @@
import "dart:ffi";
- @#C6
+ @#C7
class Coordinate extends dart.ffi::Struct {
- static final field dart.core::int* #sizeOf = (#C9).{dart.core::List::[]}(dart.ffi::_abi());
+ static final field dart.core::int* #sizeOf = (#C10).{dart.core::List::[]}(dart.ffi::_abi());
@#C12
constructor #fromTypedDataBase(dynamic #pointer) → dynamic
: super dart.ffi::Struct::_fromPointer(#pointer)
@@ -54,14 +54,14 @@
#C2 = TypeLiteralConstant(dart.ffi::Double)
#C3 = TypeLiteralConstant(dart.ffi::Pointer<dart.ffi::NativeType>)
#C4 = <dart.core::Type>[#C2, #C2, #C3]
- #C5 = dart.ffi::_FfiStructLayout {fieldTypes:#C4}
- #C6 = dart.core::pragma {name:#C1, options:#C5}
- #C7 = 24
- #C8 = 20
- #C9 = <dart.core::int*>[#C7, #C8, #C7]
- #C10 = "vm:entry-point"
- #C11 = null
- #C12 = dart.core::pragma {name:#C10, options:#C11}
+ #C5 = null
+ #C6 = dart.ffi::_FfiStructLayout {fieldTypes:#C4, packing:#C5}
+ #C7 = dart.core::pragma {name:#C1, options:#C6}
+ #C8 = 24
+ #C9 = 20
+ #C10 = <dart.core::int*>[#C8, #C9, #C8]
+ #C11 = "vm:entry-point"
+ #C12 = dart.core::pragma {name:#C11, options:#C5}
#C13 = 0
#C14 = <dart.core::int*>[#C13, #C13, #C13]
#C15 = 8
diff --git a/pkg/front_end/testcases/incremental/ffi_01.yaml.world.2.expect b/pkg/front_end/testcases/incremental/ffi_01.yaml.world.2.expect
index 406e797..7095982 100644
--- a/pkg/front_end/testcases/incremental/ffi_01.yaml.world.2.expect
+++ b/pkg/front_end/testcases/incremental/ffi_01.yaml.world.2.expect
@@ -3,9 +3,9 @@
import "dart:ffi";
- @#C6
+ @#C7
class Coordinate extends dart.ffi::Struct {
- static final field dart.core::int* #sizeOf = (#C9).{dart.core::List::[]}(dart.ffi::_abi());
+ static final field dart.core::int* #sizeOf = (#C10).{dart.core::List::[]}(dart.ffi::_abi());
@#C12
constructor #fromTypedDataBase(dynamic #pointer) → dynamic
: super dart.ffi::Struct::_fromPointer(#pointer)
@@ -58,14 +58,14 @@
#C2 = TypeLiteralConstant(dart.ffi::Double)
#C3 = TypeLiteralConstant(dart.ffi::Pointer<dart.ffi::NativeType>)
#C4 = <dart.core::Type>[#C2, #C2, #C3]
- #C5 = dart.ffi::_FfiStructLayout {fieldTypes:#C4}
- #C6 = dart.core::pragma {name:#C1, options:#C5}
- #C7 = 24
- #C8 = 20
- #C9 = <dart.core::int*>[#C7, #C8, #C7]
- #C10 = "vm:entry-point"
- #C11 = null
- #C12 = dart.core::pragma {name:#C10, options:#C11}
+ #C5 = null
+ #C6 = dart.ffi::_FfiStructLayout {fieldTypes:#C4, packing:#C5}
+ #C7 = dart.core::pragma {name:#C1, options:#C6}
+ #C8 = 24
+ #C9 = 20
+ #C10 = <dart.core::int*>[#C8, #C9, #C8]
+ #C11 = "vm:entry-point"
+ #C12 = dart.core::pragma {name:#C11, options:#C5}
#C13 = 0
#C14 = <dart.core::int*>[#C13, #C13, #C13]
#C15 = 8
diff --git a/pkg/front_end/testcases/incremental/ffi_02.yaml.world.1.expect b/pkg/front_end/testcases/incremental/ffi_02.yaml.world.1.expect
index 29633d6..1bd3802 100644
--- a/pkg/front_end/testcases/incremental/ffi_02.yaml.world.1.expect
+++ b/pkg/front_end/testcases/incremental/ffi_02.yaml.world.1.expect
@@ -3,9 +3,9 @@
import "dart:ffi";
- @#C6
+ @#C7
class Coordinate extends dart.ffi::Struct {
- static final field dart.core::int* #sizeOf = (#C9).{dart.core::List::[]}(dart.ffi::_abi());
+ static final field dart.core::int* #sizeOf = (#C10).{dart.core::List::[]}(dart.ffi::_abi());
@#C12
constructor #fromTypedDataBase(dynamic #pointer) → dynamic
: super dart.ffi::Struct::_fromPointer(#pointer)
@@ -55,14 +55,14 @@
#C2 = TypeLiteralConstant(dart.ffi::Double)
#C3 = TypeLiteralConstant(dart.ffi::Pointer<dart.ffi::NativeType>)
#C4 = <dart.core::Type>[#C2, #C2, #C3]
- #C5 = dart.ffi::_FfiStructLayout {fieldTypes:#C4}
- #C6 = dart.core::pragma {name:#C1, options:#C5}
- #C7 = 24
- #C8 = 20
- #C9 = <dart.core::int*>[#C7, #C8, #C7]
- #C10 = "vm:entry-point"
- #C11 = null
- #C12 = dart.core::pragma {name:#C10, options:#C11}
+ #C5 = null
+ #C6 = dart.ffi::_FfiStructLayout {fieldTypes:#C4, packing:#C5}
+ #C7 = dart.core::pragma {name:#C1, options:#C6}
+ #C8 = 24
+ #C9 = 20
+ #C10 = <dart.core::int*>[#C8, #C9, #C8]
+ #C11 = "vm:entry-point"
+ #C12 = dart.core::pragma {name:#C11, options:#C5}
#C13 = 0
#C14 = <dart.core::int*>[#C13, #C13, #C13]
#C15 = 8
diff --git a/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.1.expect b/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.1.expect
index b112423..5946f0f 100644
--- a/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.1.expect
+++ b/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.1.expect
@@ -3,9 +3,9 @@
import "dart:ffi";
- @#C6
+ @#C7
class Coordinate extends dart.ffi::Struct {
- static final field dart.core::int* #sizeOf = (#C9).{dart.core::List::[]}(dart.ffi::_abi());
+ static final field dart.core::int* #sizeOf = (#C10).{dart.core::List::[]}(dart.ffi::_abi());
@#C12
constructor #fromTypedDataBase(dynamic #pointer) → dynamic
: super dart.ffi::Struct::_fromPointer(#pointer)
@@ -54,14 +54,14 @@
#C2 = TypeLiteralConstant(dart.ffi::Double)
#C3 = TypeLiteralConstant(dart.ffi::Pointer<dart.ffi::NativeType>)
#C4 = <dart.core::Type>[#C2, #C2, #C3]
- #C5 = dart.ffi::_FfiStructLayout {fieldTypes:#C4}
- #C6 = dart.core::pragma {name:#C1, options:#C5}
- #C7 = 24
- #C8 = 20
- #C9 = <dart.core::int*>[#C7, #C8, #C7]
- #C10 = "vm:entry-point"
- #C11 = null
- #C12 = dart.core::pragma {name:#C10, options:#C11}
+ #C5 = null
+ #C6 = dart.ffi::_FfiStructLayout {fieldTypes:#C4, packing:#C5}
+ #C7 = dart.core::pragma {name:#C1, options:#C6}
+ #C8 = 24
+ #C9 = 20
+ #C10 = <dart.core::int*>[#C8, #C9, #C8]
+ #C11 = "vm:entry-point"
+ #C12 = dart.core::pragma {name:#C11, options:#C5}
#C13 = 0
#C14 = <dart.core::int*>[#C13, #C13, #C13]
#C15 = 8
diff --git a/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.2.expect b/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.2.expect
index 5a4e426..9d3a106 100644
--- a/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.2.expect
+++ b/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.2.expect
@@ -3,9 +3,9 @@
import "dart:ffi";
- @#C6
+ @#C7
class Coordinate extends dart.ffi::Struct {
- static final field dart.core::int* #sizeOf = (#C9).{dart.core::List::[]}(dart.ffi::_abi());
+ static final field dart.core::int* #sizeOf = (#C10).{dart.core::List::[]}(dart.ffi::_abi());
@#C12
constructor #fromTypedDataBase(dynamic #pointer) → dynamic
: super dart.ffi::Struct::_fromPointer(#pointer)
@@ -55,14 +55,14 @@
#C2 = TypeLiteralConstant(dart.ffi::Double)
#C3 = TypeLiteralConstant(dart.ffi::Pointer<dart.ffi::NativeType>)
#C4 = <dart.core::Type>[#C2, #C2, #C3]
- #C5 = dart.ffi::_FfiStructLayout {fieldTypes:#C4}
- #C6 = dart.core::pragma {name:#C1, options:#C5}
- #C7 = 24
- #C8 = 20
- #C9 = <dart.core::int*>[#C7, #C8, #C7]
- #C10 = "vm:entry-point"
- #C11 = null
- #C12 = dart.core::pragma {name:#C10, options:#C11}
+ #C5 = null
+ #C6 = dart.ffi::_FfiStructLayout {fieldTypes:#C4, packing:#C5}
+ #C7 = dart.core::pragma {name:#C1, options:#C6}
+ #C8 = 24
+ #C9 = 20
+ #C10 = <dart.core::int*>[#C8, #C9, #C8]
+ #C11 = "vm:entry-point"
+ #C12 = dart.core::pragma {name:#C11, options:#C5}
#C13 = 0
#C14 = <dart.core::int*>[#C13, #C13, #C13]
#C15 = 8
diff --git a/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.3.expect b/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.3.expect
index 3d3c99a..9094ac3 100644
--- a/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.3.expect
+++ b/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.3.expect
@@ -3,9 +3,9 @@
import "dart:ffi";
- @#C6
+ @#C7
class Coordinate extends dart.ffi::Struct {
- static final field dart.core::int* #sizeOf = (#C9).{dart.core::List::[]}(dart.ffi::_abi());
+ static final field dart.core::int* #sizeOf = (#C10).{dart.core::List::[]}(dart.ffi::_abi());
@#C12
constructor #fromTypedDataBase(dynamic #pointer) → dynamic
: super dart.ffi::Struct::_fromPointer(#pointer)
@@ -56,14 +56,14 @@
#C2 = TypeLiteralConstant(dart.ffi::Double)
#C3 = TypeLiteralConstant(dart.ffi::Pointer<dart.ffi::NativeType>)
#C4 = <dart.core::Type>[#C2, #C2, #C3]
- #C5 = dart.ffi::_FfiStructLayout {fieldTypes:#C4}
- #C6 = dart.core::pragma {name:#C1, options:#C5}
- #C7 = 24
- #C8 = 20
- #C9 = <dart.core::int*>[#C7, #C8, #C7]
- #C10 = "vm:entry-point"
- #C11 = null
- #C12 = dart.core::pragma {name:#C10, options:#C11}
+ #C5 = null
+ #C6 = dart.ffi::_FfiStructLayout {fieldTypes:#C4, packing:#C5}
+ #C7 = dart.core::pragma {name:#C1, options:#C6}
+ #C8 = 24
+ #C9 = 20
+ #C10 = <dart.core::int*>[#C8, #C9, #C8]
+ #C11 = "vm:entry-point"
+ #C12 = dart.core::pragma {name:#C11, options:#C5}
#C13 = 0
#C14 = <dart.core::int*>[#C13, #C13, #C13]
#C15 = 8
diff --git a/pkg/front_end/testcases/nnbd/ffi_sample.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_sample.dart.strong.transformed.expect
index e039c5f..ff9d534 100644
--- a/pkg/front_end/testcases/nnbd/ffi_sample.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_sample.dart.strong.transformed.expect
@@ -6,9 +6,9 @@
import "dart:ffi";
import "package:ffi/ffi.dart";
-@#C6
+@#C7
class Coordinate extends ffi::Struct {
- static final field core::int* #sizeOf = (#C9).{core::List::[]}(ffi::_abi())/*isLegacy*/;
+ static final field core::int* #sizeOf = (#C10).{core::List::[]}(ffi::_abi())/*isLegacy*/;
@#C12
constructor #fromTypedDataBase(dynamic #pointer) → dynamic
: super ffi::Struct::_fromPointer(#pointer)
@@ -44,14 +44,14 @@
#C2 = TypeLiteralConstant(ffi::Double)
#C3 = TypeLiteralConstant(ffi::Pointer<ffi::NativeType>)
#C4 = <core::Type>[#C2, #C2, #C3]
- #C5 = ffi::_FfiStructLayout {fieldTypes:#C4}
- #C6 = core::pragma {name:#C1, options:#C5}
- #C7 = 24
- #C8 = 20
- #C9 = <core::int*>[#C7, #C8, #C7]
- #C10 = "vm:entry-point"
- #C11 = null
- #C12 = core::pragma {name:#C10, options:#C11}
+ #C5 = null
+ #C6 = ffi::_FfiStructLayout {fieldTypes:#C4, packing:#C5}
+ #C7 = core::pragma {name:#C1, options:#C6}
+ #C8 = 24
+ #C9 = 20
+ #C10 = <core::int*>[#C8, #C9, #C8]
+ #C11 = "vm:entry-point"
+ #C12 = core::pragma {name:#C11, options:#C5}
#C13 = ffi::Double {}
#C14 = 0
#C15 = <core::int*>[#C14, #C14, #C14]
diff --git a/pkg/front_end/testcases/nnbd/ffi_sample.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_sample.dart.weak.transformed.expect
index e039c5f..ff9d534 100644
--- a/pkg/front_end/testcases/nnbd/ffi_sample.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_sample.dart.weak.transformed.expect
@@ -6,9 +6,9 @@
import "dart:ffi";
import "package:ffi/ffi.dart";
-@#C6
+@#C7
class Coordinate extends ffi::Struct {
- static final field core::int* #sizeOf = (#C9).{core::List::[]}(ffi::_abi())/*isLegacy*/;
+ static final field core::int* #sizeOf = (#C10).{core::List::[]}(ffi::_abi())/*isLegacy*/;
@#C12
constructor #fromTypedDataBase(dynamic #pointer) → dynamic
: super ffi::Struct::_fromPointer(#pointer)
@@ -44,14 +44,14 @@
#C2 = TypeLiteralConstant(ffi::Double)
#C3 = TypeLiteralConstant(ffi::Pointer<ffi::NativeType>)
#C4 = <core::Type>[#C2, #C2, #C3]
- #C5 = ffi::_FfiStructLayout {fieldTypes:#C4}
- #C6 = core::pragma {name:#C1, options:#C5}
- #C7 = 24
- #C8 = 20
- #C9 = <core::int*>[#C7, #C8, #C7]
- #C10 = "vm:entry-point"
- #C11 = null
- #C12 = core::pragma {name:#C10, options:#C11}
+ #C5 = null
+ #C6 = ffi::_FfiStructLayout {fieldTypes:#C4, packing:#C5}
+ #C7 = core::pragma {name:#C1, options:#C6}
+ #C8 = 24
+ #C9 = 20
+ #C10 = <core::int*>[#C8, #C9, #C8]
+ #C11 = "vm:entry-point"
+ #C12 = core::pragma {name:#C11, options:#C5}
#C13 = ffi::Double {}
#C14 = 0
#C15 = <core::int*>[#C14, #C14, #C14]
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.transformed.expect
index 7610147..a943907 100644
--- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.transformed.expect
@@ -8,9 +8,9 @@
import "dart:ffi";
import "package:ffi/ffi.dart";
-@#C7
+@#C8
class StructInlineArray extends ffi::Struct {
- static final field core::int* #sizeOf = (#C8).{core::List::[]}(ffi::_abi())/*isLegacy*/;
+ static final field core::int* #sizeOf = (#C9).{core::List::[]}(ffi::_abi())/*isLegacy*/;
synthetic constructor •() → self::StructInlineArray
: super ffi::Struct::•()
;
@@ -23,10 +23,10 @@
return new ffi::Array::_<ffi::Uint8>( block {
core::Object #typedDataBase = this.{ffi::Struct::_addressOf};
core::int #offset = (#C14).{core::List::[]}(ffi::_abi());
- } =>#typedDataBase is ffi::Pointer<dynamic> ?{core::Object} ffi::_fromAddress<ffi::Uint8>(#typedDataBase.{ffi::Pointer::address}.{core::num::+}(#offset)) : let typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in #typedData.{typ::TypedData::buffer}.{typ::ByteBuffer::asUint8List}(#typedData.{typ::TypedData::offsetInBytes}.{core::num::+}(#offset), (#C8).{core::List::[]}(ffi::_abi())), #C3, #C15);
+ } =>#typedDataBase is ffi::Pointer<dynamic> ?{core::Object} ffi::_fromAddress<ffi::Uint8>(#typedDataBase.{ffi::Pointer::address}.{core::num::+}(#offset)) : let typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in #typedData.{typ::TypedData::buffer}.{typ::ByteBuffer::asUint8List}(#typedData.{typ::TypedData::offsetInBytes}.{core::num::+}(#offset), (#C9).{core::List::[]}(ffi::_abi())), #C3, #C15);
@#C12
set a0(ffi::Array<ffi::Uint8> #externalFieldValue) → void
- return ffi::_memCopy(this.{ffi::Struct::_addressOf}, (#C14).{core::List::[]}(ffi::_abi()), #externalFieldValue.{ffi::Array::_typedDataBase}, #C13, (#C8).{core::List::[]}(ffi::_abi()));
+ return ffi::_memCopy(this.{ffi::Struct::_addressOf}, (#C14).{core::List::[]}(ffi::_abi()), #externalFieldValue.{ffi::Array::_typedDataBase}, #C13, (#C9).{core::List::[]}(ffi::_abi()));
}
static method main() → dynamic {}
@@ -36,13 +36,13 @@
#C3 = 8
#C4 = ffi::_FfiInlineArray {elementType:#C2, length:#C3}
#C5 = <core::Type>[#C4]
- #C6 = ffi::_FfiStructLayout {fieldTypes:#C5}
- #C7 = core::pragma {name:#C1, options:#C6}
- #C8 = <core::int*>[#C3, #C3, #C3]
- #C9 = "vm:entry-point"
- #C10 = null
- #C11 = core::pragma {name:#C9, options:#C10}
- #C12 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C3, dimension2:#C10, dimension3:#C10, dimension4:#C10, dimension5:#C10, dimensions:#C10}
+ #C6 = null
+ #C7 = ffi::_FfiStructLayout {fieldTypes:#C5, packing:#C6}
+ #C8 = core::pragma {name:#C1, options:#C7}
+ #C9 = <core::int*>[#C3, #C3, #C3]
+ #C10 = "vm:entry-point"
+ #C11 = core::pragma {name:#C10, options:#C6}
+ #C12 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C3, dimension2:#C6, dimension3:#C6, dimension4:#C6, dimension5:#C6, dimensions:#C6}
#C13 = 0
#C14 = <core::int*>[#C13, #C13, #C13]
#C15 = <core::int*>[]
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.transformed.expect
index 2b04c25..77bed60 100644
--- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.transformed.expect
@@ -8,9 +8,9 @@
import "dart:ffi";
import "package:ffi/ffi.dart";
-@#C7
+@#C8
class StructInlineArray extends ffi::Struct {
- static final field core::int* #sizeOf = (#C8).{core::List::[]}(ffi::_abi())/*isLegacy*/;
+ static final field core::int* #sizeOf = (#C9).{core::List::[]}(ffi::_abi())/*isLegacy*/;
synthetic constructor •() → self::StructInlineArray
: super ffi::Struct::•()
;
@@ -23,10 +23,10 @@
return new ffi::Array::_<ffi::Uint8>( block {
core::Object #typedDataBase = this.{ffi::Struct::_addressOf};
core::int #offset = (#C14).{core::List::[]}(ffi::_abi());
- } =>#typedDataBase is ffi::Pointer<dynamic> ?{core::Object} ffi::_fromAddress<ffi::Uint8>(#typedDataBase.{ffi::Pointer::address}.{core::num::+}(#offset)) : let typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in #typedData.{typ::TypedData::buffer}.{typ::ByteBuffer::asUint8List}(#typedData.{typ::TypedData::offsetInBytes}.{core::num::+}(#offset), (#C8).{core::List::[]}(ffi::_abi())), #C3, #C15);
+ } =>#typedDataBase is ffi::Pointer<dynamic> ?{core::Object} ffi::_fromAddress<ffi::Uint8>(#typedDataBase.{ffi::Pointer::address}.{core::num::+}(#offset)) : let typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in #typedData.{typ::TypedData::buffer}.{typ::ByteBuffer::asUint8List}(#typedData.{typ::TypedData::offsetInBytes}.{core::num::+}(#offset), (#C9).{core::List::[]}(ffi::_abi())), #C3, #C15);
@#C12
set a0(ffi::Array<ffi::Uint8> #externalFieldValue) → void
- return ffi::_memCopy(this.{ffi::Struct::_addressOf}, (#C14).{core::List::[]}(ffi::_abi()), #externalFieldValue.{ffi::Array::_typedDataBase}, #C13, (#C8).{core::List::[]}(ffi::_abi()));
+ return ffi::_memCopy(this.{ffi::Struct::_addressOf}, (#C14).{core::List::[]}(ffi::_abi()), #externalFieldValue.{ffi::Array::_typedDataBase}, #C13, (#C9).{core::List::[]}(ffi::_abi()));
}
static method main() → dynamic {}
@@ -36,13 +36,13 @@
#C3 = 8
#C4 = ffi::_FfiInlineArray {elementType:#C2, length:#C3}
#C5 = <core::Type>[#C4]
- #C6 = ffi::_FfiStructLayout {fieldTypes:#C5}
- #C7 = core::pragma {name:#C1, options:#C6}
- #C8 = <core::int*>[#C3, #C3, #C3]
- #C9 = "vm:entry-point"
- #C10 = null
- #C11 = core::pragma {name:#C9, options:#C10}
- #C12 = ffi::_ArraySize<ffi::NativeType*> {dimension1:#C3, dimension2:#C10, dimension3:#C10, dimension4:#C10, dimension5:#C10, dimensions:#C10}
+ #C6 = null
+ #C7 = ffi::_FfiStructLayout {fieldTypes:#C5, packing:#C6}
+ #C8 = core::pragma {name:#C1, options:#C7}
+ #C9 = <core::int*>[#C3, #C3, #C3]
+ #C10 = "vm:entry-point"
+ #C11 = core::pragma {name:#C10, options:#C6}
+ #C12 = ffi::_ArraySize<ffi::NativeType*> {dimension1:#C3, dimension2:#C6, dimension3:#C6, dimension4:#C6, dimension5:#C6, dimensions:#C6}
#C13 = 0
#C14 = <core::int*>[#C13, #C13, #C13]
#C15 = <core::int*>[]
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.transformed.expect
index 7f11381..6ccde5b 100644
--- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.transformed.expect
@@ -8,9 +8,9 @@
import "dart:ffi";
import "package:ffi/ffi.dart";
-@#C7
+@#C8
class StructInlineArrayMultiDimensional extends ffi::Struct {
- static final field core::int* #sizeOf = (#C8).{core::List::[]}(ffi::_abi())/*isLegacy*/;
+ static final field core::int* #sizeOf = (#C9).{core::List::[]}(ffi::_abi())/*isLegacy*/;
synthetic constructor •() → self::StructInlineArrayMultiDimensional
: super ffi::Struct::•()
;
@@ -23,10 +23,10 @@
return new ffi::Array::_<ffi::Array<ffi::Array<ffi::Uint8>>>( block {
core::Object #typedDataBase = this.{ffi::Struct::_addressOf};
core::int #offset = (#C15).{core::List::[]}(ffi::_abi());
- } =>#typedDataBase is ffi::Pointer<dynamic> ?{core::Object} ffi::_fromAddress<ffi::Array<ffi::Array<ffi::Uint8>>>(#typedDataBase.{ffi::Pointer::address}.{core::num::+}(#offset)) : let typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in #typedData.{typ::TypedData::buffer}.{typ::ByteBuffer::asUint8List}(#typedData.{typ::TypedData::offsetInBytes}.{core::num::+}(#offset), (#C8).{core::List::[]}(ffi::_abi())), #C12, #C16);
+ } =>#typedDataBase is ffi::Pointer<dynamic> ?{core::Object} ffi::_fromAddress<ffi::Array<ffi::Array<ffi::Uint8>>>(#typedDataBase.{ffi::Pointer::address}.{core::num::+}(#offset)) : let typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in #typedData.{typ::TypedData::buffer}.{typ::ByteBuffer::asUint8List}(#typedData.{typ::TypedData::offsetInBytes}.{core::num::+}(#offset), (#C9).{core::List::[]}(ffi::_abi())), #C12, #C16);
@#C13
set a0(ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>> #externalFieldValue) → void
- return ffi::_memCopy(this.{ffi::Struct::_addressOf}, (#C15).{core::List::[]}(ffi::_abi()), #externalFieldValue.{ffi::Array::_typedDataBase}, #C14, (#C8).{core::List::[]}(ffi::_abi()));
+ return ffi::_memCopy(this.{ffi::Struct::_addressOf}, (#C15).{core::List::[]}(ffi::_abi()), #externalFieldValue.{ffi::Array::_typedDataBase}, #C14, (#C9).{core::List::[]}(ffi::_abi()));
}
static method main() → dynamic {
final ffi::Pointer<self::StructInlineArrayMultiDimensional> pointer = (#C17).{ffi::Allocator::allocate}<self::StructInlineArrayMultiDimensional>(self::StructInlineArrayMultiDimensional::#sizeOf);
@@ -60,14 +60,14 @@
#C3 = 8
#C4 = ffi::_FfiInlineArray {elementType:#C2, length:#C3}
#C5 = <core::Type>[#C4]
- #C6 = ffi::_FfiStructLayout {fieldTypes:#C5}
- #C7 = core::pragma {name:#C1, options:#C6}
- #C8 = <core::int*>[#C3, #C3, #C3]
- #C9 = "vm:entry-point"
- #C10 = null
- #C11 = core::pragma {name:#C9, options:#C10}
+ #C6 = null
+ #C7 = ffi::_FfiStructLayout {fieldTypes:#C5, packing:#C6}
+ #C8 = core::pragma {name:#C1, options:#C7}
+ #C9 = <core::int*>[#C3, #C3, #C3]
+ #C10 = "vm:entry-point"
+ #C11 = core::pragma {name:#C10, options:#C6}
#C12 = 2
- #C13 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C12, dimension2:#C12, dimension3:#C12, dimension4:#C10, dimension5:#C10, dimensions:#C10}
+ #C13 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C12, dimension2:#C12, dimension3:#C12, dimension4:#C6, dimension5:#C6, dimensions:#C6}
#C14 = 0
#C15 = <core::int*>[#C14, #C14, #C14]
#C16 = <core::int*>[#C12, #C12]
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.transformed.expect
index 18b2371..2147809 100644
--- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.transformed.expect
@@ -8,9 +8,9 @@
import "dart:ffi";
import "package:ffi/ffi.dart";
-@#C7
+@#C8
class StructInlineArrayMultiDimensional extends ffi::Struct {
- static final field core::int* #sizeOf = (#C8).{core::List::[]}(ffi::_abi())/*isLegacy*/;
+ static final field core::int* #sizeOf = (#C9).{core::List::[]}(ffi::_abi())/*isLegacy*/;
synthetic constructor •() → self::StructInlineArrayMultiDimensional
: super ffi::Struct::•()
;
@@ -23,10 +23,10 @@
return new ffi::Array::_<ffi::Array<ffi::Array<ffi::Uint8>>>( block {
core::Object #typedDataBase = this.{ffi::Struct::_addressOf};
core::int #offset = (#C15).{core::List::[]}(ffi::_abi());
- } =>#typedDataBase is ffi::Pointer<dynamic> ?{core::Object} ffi::_fromAddress<ffi::Array<ffi::Array<ffi::Uint8>>>(#typedDataBase.{ffi::Pointer::address}.{core::num::+}(#offset)) : let typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in #typedData.{typ::TypedData::buffer}.{typ::ByteBuffer::asUint8List}(#typedData.{typ::TypedData::offsetInBytes}.{core::num::+}(#offset), (#C8).{core::List::[]}(ffi::_abi())), #C12, #C16);
+ } =>#typedDataBase is ffi::Pointer<dynamic> ?{core::Object} ffi::_fromAddress<ffi::Array<ffi::Array<ffi::Uint8>>>(#typedDataBase.{ffi::Pointer::address}.{core::num::+}(#offset)) : let typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in #typedData.{typ::TypedData::buffer}.{typ::ByteBuffer::asUint8List}(#typedData.{typ::TypedData::offsetInBytes}.{core::num::+}(#offset), (#C9).{core::List::[]}(ffi::_abi())), #C12, #C16);
@#C13
set a0(ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>> #externalFieldValue) → void
- return ffi::_memCopy(this.{ffi::Struct::_addressOf}, (#C15).{core::List::[]}(ffi::_abi()), #externalFieldValue.{ffi::Array::_typedDataBase}, #C14, (#C8).{core::List::[]}(ffi::_abi()));
+ return ffi::_memCopy(this.{ffi::Struct::_addressOf}, (#C15).{core::List::[]}(ffi::_abi()), #externalFieldValue.{ffi::Array::_typedDataBase}, #C14, (#C9).{core::List::[]}(ffi::_abi()));
}
static method main() → dynamic {
final ffi::Pointer<self::StructInlineArrayMultiDimensional> pointer = (#C17).{ffi::Allocator::allocate}<self::StructInlineArrayMultiDimensional>(self::StructInlineArrayMultiDimensional::#sizeOf);
@@ -60,14 +60,14 @@
#C3 = 8
#C4 = ffi::_FfiInlineArray {elementType:#C2, length:#C3}
#C5 = <core::Type>[#C4]
- #C6 = ffi::_FfiStructLayout {fieldTypes:#C5}
- #C7 = core::pragma {name:#C1, options:#C6}
- #C8 = <core::int*>[#C3, #C3, #C3]
- #C9 = "vm:entry-point"
- #C10 = null
- #C11 = core::pragma {name:#C9, options:#C10}
+ #C6 = null
+ #C7 = ffi::_FfiStructLayout {fieldTypes:#C5, packing:#C6}
+ #C8 = core::pragma {name:#C1, options:#C7}
+ #C9 = <core::int*>[#C3, #C3, #C3]
+ #C10 = "vm:entry-point"
+ #C11 = core::pragma {name:#C10, options:#C6}
#C12 = 2
- #C13 = ffi::_ArraySize<ffi::NativeType*> {dimension1:#C12, dimension2:#C12, dimension3:#C12, dimension4:#C10, dimension5:#C10, dimensions:#C10}
+ #C13 = ffi::_ArraySize<ffi::NativeType*> {dimension1:#C12, dimension2:#C12, dimension3:#C12, dimension4:#C6, dimension5:#C6, dimensions:#C6}
#C14 = 0
#C15 = <core::int*>[#C14, #C14, #C14]
#C16 = <core::int*>[#C12, #C12]
diff --git a/pkg/front_end/testing.json b/pkg/front_end/testing.json
index 5ccfe48..ef56494 100644
--- a/pkg/front_end/testing.json
+++ b/pkg/front_end/testing.json
@@ -134,7 +134,8 @@
"path": "testcases/",
"status": "testcases/weak.status",
"pattern": [
- "\\.dart$"
+ "\\.dart$",
+ "\\.crash_dart$"
],
"exclude": [
"/testcases/.*_part[0-9]*\\.dart$",
diff --git a/pkg/vm/lib/transformations/ffi.dart b/pkg/vm/lib/transformations/ffi.dart
index 7f1d707..1551f46 100644
--- a/pkg/vm/lib/transformations/ffi.dart
+++ b/pkg/vm/lib/transformations/ffi.dart
@@ -182,6 +182,11 @@
NativeType.kPointer,
];
+const List<NativeType> unalignedLoadsStores = [
+ NativeType.kFloat,
+ NativeType.kDouble,
+];
+
/// [FfiTransformer] contains logic which is shared between
/// _FfiUseSiteTransformer and _FfiDefinitionTransformer.
class FfiTransformer extends Transformer {
@@ -225,9 +230,12 @@
final Class structClass;
final Class ffiStructLayoutClass;
final Field ffiStructLayoutTypesField;
+ final Field ffiStructLayoutPackingField;
final Class ffiInlineArrayClass;
final Field ffiInlineArrayElementTypeField;
final Field ffiInlineArrayLengthField;
+ final Class packedClass;
+ final Field packedMemberAlignmentField;
final Procedure allocateMethod;
final Procedure allocatorAllocateMethod;
final Procedure castMethod;
@@ -260,7 +268,9 @@
final Procedure pointerFromFunctionProcedure;
final Procedure nativeCallbackFunctionProcedure;
final Map<NativeType, Procedure> loadMethods;
+ final Map<NativeType, Procedure> loadUnalignedMethods;
final Map<NativeType, Procedure> storeMethods;
+ final Map<NativeType, Procedure> storeUnalignedMethods;
final Map<NativeType, Procedure> elementAtMethods;
final Procedure memCopy;
final Procedure allocationTearoff;
@@ -319,11 +329,16 @@
ffiStructLayoutClass = index.getClass('dart:ffi', '_FfiStructLayout'),
ffiStructLayoutTypesField =
index.getMember('dart:ffi', '_FfiStructLayout', 'fieldTypes'),
+ ffiStructLayoutPackingField =
+ index.getMember('dart:ffi', '_FfiStructLayout', 'packing'),
ffiInlineArrayClass = index.getClass('dart:ffi', '_FfiInlineArray'),
ffiInlineArrayElementTypeField =
index.getMember('dart:ffi', '_FfiInlineArray', 'elementType'),
ffiInlineArrayLengthField =
index.getMember('dart:ffi', '_FfiInlineArray', 'length'),
+ packedClass = index.getClass('dart:ffi', 'Packed'),
+ packedMemberAlignmentField =
+ index.getMember('dart:ffi', 'Packed', 'memberAlignment'),
allocateMethod = index.getMember('dart:ffi', 'AllocatorAlloc', 'call'),
allocatorAllocateMethod =
index.getMember('dart:ffi', 'Allocator', 'allocate'),
@@ -379,10 +394,20 @@
final name = nativeTypeClassNames[t.index];
return index.getTopLevelMember('dart:ffi', "_load$name");
}),
+ loadUnalignedMethods =
+ Map.fromIterable(unalignedLoadsStores, value: (t) {
+ final name = nativeTypeClassNames[t.index];
+ return index.getTopLevelMember('dart:ffi', "_load${name}Unaligned");
+ }),
storeMethods = Map.fromIterable(optimizedTypes, value: (t) {
final name = nativeTypeClassNames[t.index];
return index.getTopLevelMember('dart:ffi', "_store$name");
}),
+ storeUnalignedMethods =
+ Map.fromIterable(unalignedLoadsStores, value: (t) {
+ final name = nativeTypeClassNames[t.index];
+ return index.getTopLevelMember('dart:ffi', "_store${name}Unaligned");
+ }),
elementAtMethods = Map.fromIterable(optimizedTypes, value: (t) {
final name = nativeTypeClassNames[t.index];
return index.getTopLevelMember('dart:ffi', "_elementAt$name");
diff --git a/pkg/vm/lib/transformations/ffi_definitions.dart b/pkg/vm/lib/transformations/ffi_definitions.dart
index 2de1c0b..522510b 100644
--- a/pkg/vm/lib/transformations/ffi_definitions.dart
+++ b/pkg/vm/lib/transformations/ffi_definitions.dart
@@ -8,6 +8,7 @@
import 'package:front_end/src/api_unstable/vm.dart'
show
+ messageFfiPackedAnnotationAlignment,
templateFfiEmptyStruct,
templateFfiFieldAnnotation,
templateFfiFieldNull,
@@ -15,6 +16,8 @@
templateFfiFieldNoAnnotation,
templateFfiTypeMismatch,
templateFfiFieldInitializer,
+ templateFfiPackedAnnotation,
+ templateFfiPackedNestingNonPacked,
templateFfiSizeAnnotation,
templateFfiSizeAnnotationDimensions,
templateFfiStructGeneric;
@@ -177,13 +180,13 @@
return node;
}
- _checkStructClass(node);
+ final packing = _checkStructClass(node);
final indexedClass = currentLibraryIndex?.lookupIndexedClass(node.name);
_checkConstructors(node, indexedClass);
indexedStructClasses[node] = indexedClass;
- fieldsValid[node] = _checkFieldAnnotations(node);
+ fieldsValid[node] = _checkFieldAnnotations(node, packing);
return node;
}
@@ -197,7 +200,8 @@
}
}
- void _checkStructClass(Class node) {
+ /// Returns packing if any.
+ int _checkStructClass(Class node) {
if (node.typeParameters.length > 0) {
diagnosticReporter.report(
templateFfiStructGeneric.withArguments(node.name),
@@ -209,8 +213,30 @@
if (node.supertype?.classNode != structClass) {
// Not a struct, but extends a struct. The error will be emitted by
// _FfiUseSiteTransformer.
- return;
+ return null;
}
+
+ final packingAnnotations = _getPackedAnnotations(node);
+ if (packingAnnotations.length > 1) {
+ diagnosticReporter.report(
+ templateFfiPackedAnnotation.withArguments(node.name),
+ node.fileOffset,
+ node.name.length,
+ node.location.file);
+ }
+ if (packingAnnotations.isNotEmpty) {
+ final packing = packingAnnotations.first;
+ if (!(packing == 1 ||
+ packing == 2 ||
+ packing == 4 ||
+ packing == 8 ||
+ packing == 16)) {
+ diagnosticReporter.report(messageFfiPackedAnnotationAlignment,
+ node.fileOffset, node.name.length, node.location.file);
+ }
+ return packing;
+ }
+ return null;
}
/// Returns members of [node] that correspond to struct fields.
@@ -242,7 +268,7 @@
return p.function.positionalParameters.single.type;
}
- bool _checkFieldAnnotations(Class node) {
+ bool _checkFieldAnnotations(Class node, int packing) {
bool success = true;
structClassDependencies[node] = {};
final membersWithAnnotations = _structFieldMembers(node)
@@ -284,6 +310,7 @@
if (isStructSubtype(type)) {
final clazz = (type as InterfaceType).classNode;
structClassDependencies[node].add(clazz);
+ _checkPacking(node, packing, clazz, f);
} else if (isArrayType(type)) {
final sizeAnnotations = _getArraySizeAnnotations(f);
if (sizeAnnotations.length == 1) {
@@ -291,6 +318,7 @@
if (isStructSubtype(singleElementType)) {
final clazz = (singleElementType as InterfaceType).classNode;
structClassDependencies[node].add(clazz);
+ _checkPacking(node, packing, clazz, f);
}
if (arrayDimensions(type) != sizeAnnotations.single.length) {
diagnosticReporter.report(
@@ -342,6 +370,35 @@
return success;
}
+ void _checkPacking(Class outerClass, int outerClassPacking, Class fieldClass,
+ Member errorNode) {
+ if (outerClassPacking == null) {
+ // Outer struct has no packing, nesting anything is fine.
+ return;
+ }
+
+ final fieldPackingAnnotations = _getPackedAnnotations(fieldClass);
+ bool error = false;
+ if (fieldPackingAnnotations.isEmpty) {
+ // Outer struct has packing but inner one doesn't.
+ error = true;
+ } else {
+ final fieldPacking = fieldPackingAnnotations.first;
+ if (fieldPacking > outerClassPacking) {
+ // Outer struct has stricter packing than the inner.
+ error = true;
+ }
+ }
+ if (error) {
+ diagnosticReporter.report(
+ templateFfiPackedNestingNonPacked.withArguments(
+ fieldClass.name, outerClass.name),
+ errorNode.fileOffset,
+ errorNode.name.text.length,
+ errorNode.fileUri);
+ }
+ }
+
void _checkConstructors(Class node, IndexedClass indexedClass) {
final toRemove = <Initializer>[];
@@ -441,22 +498,27 @@
}
}
- _annoteStructWithFields(node, types);
+ final packingAnnotations = _getPackedAnnotations(node);
+ final packing =
+ (!packingAnnotations.isEmpty) ? packingAnnotations.first : null;
+
+ _annoteStructWithFields(node, types, packing);
if (types.isEmpty) {
diagnosticReporter.report(templateFfiEmptyStruct.withArguments(node.name),
node.fileOffset, node.name.length, node.location.file);
emptyStructs.add(node);
}
- final structType = StructNativeTypeCfe(node, types);
+ final structType = StructNativeTypeCfe(node, types, packing: packing);
structCache[node] = structType;
final structLayout = structType.layout;
+ final unalignedAccess = packing != null;
for (final i in fields.keys) {
final fieldOffsets = structLayout
.map((Abi abi, StructLayout v) => MapEntry(abi, v.offsets[i]));
final methods = _generateMethodsForField(
- fields[i], types[i], fieldOffsets, indexedClass);
+ fields[i], types[i], fieldOffsets, unalignedAccess, indexedClass);
methods.forEach((p) => node.addProcedure(p));
}
@@ -469,7 +531,11 @@
.map((Abi abi, StructLayout v) => MapEntry(abi, v.offsets[i]));
Procedure getter = getters[i];
getter.function.body = types[i].generateGetterStatement(
- getter.function.returnType, getter.fileOffset, fieldOffsets, this);
+ getter.function.returnType,
+ getter.fileOffset,
+ fieldOffsets,
+ unalignedAccess,
+ this);
getter.isExternal = false;
}
@@ -481,6 +547,7 @@
setter.function.positionalParameters.single.type,
setter.fileOffset,
fieldOffsets,
+ unalignedAccess,
setter.function.positionalParameters.single,
this);
setter.isExternal = false;
@@ -489,7 +556,9 @@
return structLayout.map((k, v) => MapEntry(k, v.size));
}
- void _annoteStructWithFields(Class node, List<NativeTypeCfe> types) {
+ // packing is `int?`.
+ void _annoteStructWithFields(
+ Class node, List<NativeTypeCfe> types, int packing) {
List<Constant> constants =
types.map((t) => t.generateConstant(this)).toList();
@@ -499,19 +568,21 @@
pragmaOptions.getterReference:
InstanceConstant(ffiStructLayoutClass.reference, [], {
ffiStructLayoutTypesField.getterReference: ListConstant(
- InterfaceType(typeClass, Nullability.nonNullable), constants)
+ InterfaceType(typeClass, Nullability.nonNullable), constants),
+ ffiStructLayoutPackingField.getterReference:
+ packing == null ? NullConstant() : IntConstant(packing)
})
}),
InterfaceType(pragmaClass, Nullability.nonNullable, [])));
}
List<Procedure> _generateMethodsForField(Field field, NativeTypeCfe type,
- Map<Abi, int> offsets, IndexedClass indexedClass) {
+ Map<Abi, int> offsets, bool unalignedAccess, IndexedClass indexedClass) {
// TODO(johnniwinther): Avoid passing [indexedClass]. When compiling
// incrementally, [field] should already carry the references from
// [indexedClass].
final getterStatement = type.generateGetterStatement(
- field.type, field.fileOffset, offsets, this);
+ field.type, field.fileOffset, offsets, unalignedAccess, this);
Reference getterReference =
indexedClass?.lookupGetterReference(field.name) ??
field.getterReference;
@@ -533,8 +604,8 @@
final VariableDeclaration argument =
VariableDeclaration('#v', type: field.type)
..fileOffset = field.fileOffset;
- final setterStatement = type.generateSetterStatement(
- field.type, field.fileOffset, offsets, argument, this);
+ final setterStatement = type.generateSetterStatement(field.type,
+ field.fileOffset, offsets, unalignedAccess, argument, this);
setter = Procedure(
field.name,
ProcedureKind.Setter,
@@ -633,6 +704,17 @@
.toList();
return result;
}
+
+ Iterable<int> _getPackedAnnotations(Class node) {
+ return node.annotations
+ .whereType<ConstantExpression>()
+ .map((expr) => expr.constant)
+ .whereType<InstanceConstant>()
+ .where((e) => e.classNode == packedClass)
+ .map((e) => e.fieldValues.values.single)
+ .whereType<IntConstant>()
+ .map((e) => e.value);
+ }
}
/// The layout of a `Struct` in one [Abi].
@@ -704,7 +786,7 @@
///
/// Takes [transformer] to be able to lookup classes and methods.
ReturnStatement generateGetterStatement(DartType dartType, int fileOffset,
- Map<Abi, int> offsets, FfiTransformer transformer);
+ Map<Abi, int> offsets, bool unalignedAccess, FfiTransformer transformer);
/// Generates the return statement for a struct field setter with this type.
///
@@ -713,6 +795,7 @@
DartType dartType,
int fileOffset,
Map<Abi, int> offsets,
+ bool unalignedAccess,
VariableDeclaration argument,
FfiTransformer transformer);
}
@@ -741,16 +824,37 @@
Constant generateConstant(FfiTransformer transformer) =>
TypeLiteralConstant(InterfaceType(clazz, Nullability.nonNullable));
+ bool get isFloat =>
+ nativeType == NativeType.kFloat || nativeType == NativeType.kDouble;
+
+ bool isUnaligned(Map<Abi, int> offsets) {
+ final alignments = alignment;
+ for (final abi in offsets.keys) {
+ final offset = offsets[abi];
+ final alignment = alignments[abi];
+ if (offset % alignment != 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/// Sample output for `int get x =>`:
///
/// ```
/// _loadInt8(_addressOf, offset);
/// ```
@override
- ReturnStatement generateGetterStatement(DartType dartType, int fileOffset,
- Map<Abi, int> offsets, FfiTransformer transformer) =>
+ ReturnStatement generateGetterStatement(
+ DartType dartType,
+ int fileOffset,
+ Map<Abi, int> offsets,
+ bool unalignedAccess,
+ FfiTransformer transformer) =>
ReturnStatement(StaticInvocation(
- transformer.loadMethods[nativeType],
+ (unalignedAccess && isFloat
+ ? transformer.loadUnalignedMethods
+ : transformer.loadMethods)[nativeType],
Arguments([
PropertyGet(ThisExpression(), transformer.addressOfField.name,
transformer.addressOfField)
@@ -769,10 +873,13 @@
DartType dartType,
int fileOffset,
Map<Abi, int> offsets,
+ bool unalignedAccess,
VariableDeclaration argument,
FfiTransformer transformer) =>
ReturnStatement(StaticInvocation(
- transformer.storeMethods[nativeType],
+ (unalignedAccess && isFloat
+ ? transformer.storeUnalignedMethods
+ : transformer.storeMethods)[nativeType],
Arguments([
PropertyGet(ThisExpression(), transformer.addressOfField.name,
transformer.addressOfField)
@@ -803,8 +910,12 @@
/// _fromAddress<Int8>(_loadIntPtr(_addressOf, offset));
/// ```
@override
- ReturnStatement generateGetterStatement(DartType dartType, int fileOffset,
- Map<Abi, int> offsets, FfiTransformer transformer) =>
+ ReturnStatement generateGetterStatement(
+ DartType dartType,
+ int fileOffset,
+ Map<Abi, int> offsets,
+ bool unalignedAccess,
+ FfiTransformer transformer) =>
ReturnStatement(StaticInvocation(
transformer.fromAddressInternal,
Arguments([
@@ -832,6 +943,7 @@
DartType dartType,
int fileOffset,
Map<Abi, int> offsets,
+ bool unalignedAccess,
VariableDeclaration argument,
FfiTransformer transformer) =>
ReturnStatement(StaticInvocation(
@@ -853,23 +965,31 @@
final List<NativeTypeCfe> members;
+ // Nullable int.
+ final int packing;
+
final Map<Abi, StructLayout> layout;
- factory StructNativeTypeCfe(Class clazz, List<NativeTypeCfe> members) {
- final layout = Map.fromEntries(
- Abi.values.map((abi) => MapEntry(abi, _calculateLayout(members, abi))));
- return StructNativeTypeCfe._(clazz, members, layout);
+ factory StructNativeTypeCfe(Class clazz, List<NativeTypeCfe> members,
+ {int packing}) {
+ final layout = Map.fromEntries(Abi.values
+ .map((abi) => MapEntry(abi, _calculateLayout(members, packing, abi))));
+ return StructNativeTypeCfe._(clazz, members, packing, layout);
}
// Keep consistent with runtime/vm/compiler/ffi/native_type.cc
// NativeCompoundType::FromNativeTypes.
- static StructLayout _calculateLayout(List<NativeTypeCfe> types, Abi abi) {
+ static StructLayout _calculateLayout(
+ List<NativeTypeCfe> types, int packing, Abi abi) {
int offset = 0;
final offsets = <int>[];
int structAlignment = 1;
for (int i = 0; i < types.length; i++) {
final int size = types[i].size[abi];
- final int alignment = types[i].alignment[abi];
+ int alignment = types[i].alignment[abi];
+ if (packing != null && packing < alignment) {
+ alignment = packing;
+ }
offset = _alignOffset(offset, alignment);
offsets.add(offset);
offset += size;
@@ -879,7 +999,7 @@
return StructLayout(size, structAlignment, offsets);
}
- StructNativeTypeCfe._(this.clazz, this.members, this.layout);
+ StructNativeTypeCfe._(this.clazz, this.members, this.packing, this.layout);
@override
Map<Abi, int> get size =>
@@ -902,7 +1022,7 @@
/// ```
@override
ReturnStatement generateGetterStatement(DartType dartType, int fileOffset,
- Map<Abi, int> offsets, FfiTransformer transformer) {
+ Map<Abi, int> offsets, bool unalignedAccess, FfiTransformer transformer) {
final constructor = clazz.constructors
.firstWhere((c) => c.name == Name("#fromTypedDataBase"));
@@ -931,6 +1051,7 @@
DartType dartType,
int fileOffset,
Map<Abi, int> offsets,
+ bool unalignedAccess,
VariableDeclaration argument,
FfiTransformer transformer) =>
ReturnStatement(StaticInvocation(
@@ -1012,7 +1133,7 @@
/// ```
@override
ReturnStatement generateGetterStatement(DartType dartType, int fileOffset,
- Map<Abi, int> offsets, FfiTransformer transformer) {
+ Map<Abi, int> offsets, bool unalignedAccess, FfiTransformer transformer) {
InterfaceType typeArgument =
(dartType as InterfaceType).typeArguments.single as InterfaceType;
return ReturnStatement(ConstructorInvocation(
@@ -1044,6 +1165,7 @@
DartType dartType,
int fileOffset,
Map<Abi, int> offsets,
+ bool unalignedAccess,
VariableDeclaration argument,
FfiTransformer transformer) =>
ReturnStatement(StaticInvocation(
diff --git a/runtime/bin/ffi_test/ffi_test_functions.cc b/runtime/bin/ffi_test/ffi_test_functions.cc
index 69a0e51..49ab86f 100644
--- a/runtime/bin/ffi_test/ffi_test_functions.cc
+++ b/runtime/bin/ffi_test/ffi_test_functions.cc
@@ -1113,4 +1113,15 @@
return my_struct->someValue;
}
+#pragma pack(push, 1)
+struct Struct3BytesPackedIntCopy {
+ int8_t a0;
+ int16_t a1;
+};
+#pragma pack(pop)
+
+DART_EXPORT uint64_t SizeOfStruct3BytesPackedInt() {
+ return sizeof(Struct3BytesPackedIntCopy);
+}
+
} // namespace dart
diff --git a/runtime/bin/ffi_test/ffi_test_functions_generated.cc b/runtime/bin/ffi_test/ffi_test_functions_generated.cc
index a352c54..6d3a2cf 100644
--- a/runtime/bin/ffi_test/ffi_test_functions_generated.cc
+++ b/runtime/bin/ffi_test/ffi_test_functions_generated.cc
@@ -467,6 +467,57 @@
Struct1ByteInt a0[2][2];
};
+#pragma pack(push, 1)
+struct Struct3BytesPackedInt {
+ int8_t a0;
+ int16_t a1;
+};
+#pragma pack(pop)
+
+#pragma pack(push, 1)
+struct Struct3BytesPackedIntMembersAligned {
+ int8_t a0;
+ int16_t a1;
+};
+#pragma pack(pop)
+
+#pragma pack(push, 1)
+struct Struct5BytesPackedMixed {
+ float a0;
+ uint8_t a1;
+};
+#pragma pack(pop)
+
+struct StructNestedAlignmentStruct5BytesPackedMixed {
+ uint8_t a0;
+ Struct5BytesPackedMixed a1;
+};
+
+struct Struct6BytesInlineArrayInt {
+ Struct3BytesPackedIntMembersAligned a0[2];
+};
+
+#pragma pack(push, 1)
+struct Struct8BytesPackedInt {
+ uint8_t a0;
+ uint32_t a1;
+ uint8_t a2;
+ uint8_t a3;
+ uint8_t a4;
+};
+#pragma pack(pop)
+
+#pragma pack(push, 1)
+struct Struct9BytesPackedMixed {
+ uint8_t a0;
+ double a1;
+};
+#pragma pack(pop)
+
+struct Struct15BytesInlineArrayMixed {
+ Struct5BytesPackedMixed a0[3];
+};
+
// Used for testing structs by value.
// Smallest struct with data.
// 10 struct arguments will exhaust available registers.
@@ -3996,6 +4047,306 @@
}
// Used for testing structs by value.
+// Small struct with mis-aligned member.
+DART_EXPORT int64_t PassStruct3BytesPackedIntx10(Struct3BytesPackedInt a0,
+ Struct3BytesPackedInt a1,
+ Struct3BytesPackedInt a2,
+ Struct3BytesPackedInt a3,
+ Struct3BytesPackedInt a4,
+ Struct3BytesPackedInt a5,
+ Struct3BytesPackedInt a6,
+ Struct3BytesPackedInt a7,
+ Struct3BytesPackedInt a8,
+ Struct3BytesPackedInt a9) {
+ std::cout << "PassStruct3BytesPackedIntx10"
+ << "((" << static_cast<int>(a0.a0) << ", " << a0.a1 << "), ("
+ << static_cast<int>(a1.a0) << ", " << a1.a1 << "), ("
+ << static_cast<int>(a2.a0) << ", " << a2.a1 << "), ("
+ << static_cast<int>(a3.a0) << ", " << a3.a1 << "), ("
+ << static_cast<int>(a4.a0) << ", " << a4.a1 << "), ("
+ << static_cast<int>(a5.a0) << ", " << a5.a1 << "), ("
+ << static_cast<int>(a6.a0) << ", " << a6.a1 << "), ("
+ << static_cast<int>(a7.a0) << ", " << a7.a1 << "), ("
+ << static_cast<int>(a8.a0) << ", " << a8.a1 << "), ("
+ << static_cast<int>(a9.a0) << ", " << a9.a1 << "))"
+ << "\n";
+
+ int64_t result = 0;
+
+ result += a0.a0;
+ result += a0.a1;
+ result += a1.a0;
+ result += a1.a1;
+ result += a2.a0;
+ result += a2.a1;
+ result += a3.a0;
+ result += a3.a1;
+ result += a4.a0;
+ result += a4.a1;
+ result += a5.a0;
+ result += a5.a1;
+ result += a6.a0;
+ result += a6.a1;
+ result += a7.a0;
+ result += a7.a1;
+ result += a8.a0;
+ result += a8.a1;
+ result += a9.a0;
+ result += a9.a1;
+
+ std::cout << "result = " << result << "\n";
+
+ return result;
+}
+
+// Used for testing structs by value.
+// Struct with mis-aligned member.
+DART_EXPORT int64_t PassStruct8BytesPackedIntx10(Struct8BytesPackedInt a0,
+ Struct8BytesPackedInt a1,
+ Struct8BytesPackedInt a2,
+ Struct8BytesPackedInt a3,
+ Struct8BytesPackedInt a4,
+ Struct8BytesPackedInt a5,
+ Struct8BytesPackedInt a6,
+ Struct8BytesPackedInt a7,
+ Struct8BytesPackedInt a8,
+ Struct8BytesPackedInt a9) {
+ std::cout << "PassStruct8BytesPackedIntx10"
+ << "((" << static_cast<int>(a0.a0) << ", " << a0.a1 << ", "
+ << static_cast<int>(a0.a2) << ", " << static_cast<int>(a0.a3)
+ << ", " << static_cast<int>(a0.a4) << "), ("
+ << static_cast<int>(a1.a0) << ", " << a1.a1 << ", "
+ << static_cast<int>(a1.a2) << ", " << static_cast<int>(a1.a3)
+ << ", " << static_cast<int>(a1.a4) << "), ("
+ << static_cast<int>(a2.a0) << ", " << a2.a1 << ", "
+ << static_cast<int>(a2.a2) << ", " << static_cast<int>(a2.a3)
+ << ", " << static_cast<int>(a2.a4) << "), ("
+ << static_cast<int>(a3.a0) << ", " << a3.a1 << ", "
+ << static_cast<int>(a3.a2) << ", " << static_cast<int>(a3.a3)
+ << ", " << static_cast<int>(a3.a4) << "), ("
+ << static_cast<int>(a4.a0) << ", " << a4.a1 << ", "
+ << static_cast<int>(a4.a2) << ", " << static_cast<int>(a4.a3)
+ << ", " << static_cast<int>(a4.a4) << "), ("
+ << static_cast<int>(a5.a0) << ", " << a5.a1 << ", "
+ << static_cast<int>(a5.a2) << ", " << static_cast<int>(a5.a3)
+ << ", " << static_cast<int>(a5.a4) << "), ("
+ << static_cast<int>(a6.a0) << ", " << a6.a1 << ", "
+ << static_cast<int>(a6.a2) << ", " << static_cast<int>(a6.a3)
+ << ", " << static_cast<int>(a6.a4) << "), ("
+ << static_cast<int>(a7.a0) << ", " << a7.a1 << ", "
+ << static_cast<int>(a7.a2) << ", " << static_cast<int>(a7.a3)
+ << ", " << static_cast<int>(a7.a4) << "), ("
+ << static_cast<int>(a8.a0) << ", " << a8.a1 << ", "
+ << static_cast<int>(a8.a2) << ", " << static_cast<int>(a8.a3)
+ << ", " << static_cast<int>(a8.a4) << "), ("
+ << static_cast<int>(a9.a0) << ", " << a9.a1 << ", "
+ << static_cast<int>(a9.a2) << ", " << static_cast<int>(a9.a3)
+ << ", " << static_cast<int>(a9.a4) << "))"
+ << "\n";
+
+ int64_t result = 0;
+
+ result += a0.a0;
+ result += a0.a1;
+ result += a0.a2;
+ result += a0.a3;
+ result += a0.a4;
+ result += a1.a0;
+ result += a1.a1;
+ result += a1.a2;
+ result += a1.a3;
+ result += a1.a4;
+ result += a2.a0;
+ result += a2.a1;
+ result += a2.a2;
+ result += a2.a3;
+ result += a2.a4;
+ result += a3.a0;
+ result += a3.a1;
+ result += a3.a2;
+ result += a3.a3;
+ result += a3.a4;
+ result += a4.a0;
+ result += a4.a1;
+ result += a4.a2;
+ result += a4.a3;
+ result += a4.a4;
+ result += a5.a0;
+ result += a5.a1;
+ result += a5.a2;
+ result += a5.a3;
+ result += a5.a4;
+ result += a6.a0;
+ result += a6.a1;
+ result += a6.a2;
+ result += a6.a3;
+ result += a6.a4;
+ result += a7.a0;
+ result += a7.a1;
+ result += a7.a2;
+ result += a7.a3;
+ result += a7.a4;
+ result += a8.a0;
+ result += a8.a1;
+ result += a8.a2;
+ result += a8.a3;
+ result += a8.a4;
+ result += a9.a0;
+ result += a9.a1;
+ result += a9.a2;
+ result += a9.a3;
+ result += a9.a4;
+
+ std::cout << "result = " << result << "\n";
+
+ return result;
+}
+
+// Used for testing structs by value.
+// Struct with mis-aligned member.
+// Tests backfilling of CPU and FPU registers.
+DART_EXPORT double PassStruct9BytesPackedMixedx10DoubleInt32(
+ Struct9BytesPackedMixed a0,
+ Struct9BytesPackedMixed a1,
+ Struct9BytesPackedMixed a2,
+ Struct9BytesPackedMixed a3,
+ Struct9BytesPackedMixed a4,
+ Struct9BytesPackedMixed a5,
+ Struct9BytesPackedMixed a6,
+ Struct9BytesPackedMixed a7,
+ Struct9BytesPackedMixed a8,
+ Struct9BytesPackedMixed a9,
+ double a10,
+ int32_t a11) {
+ std::cout << "PassStruct9BytesPackedMixedx10DoubleInt32"
+ << "((" << static_cast<int>(a0.a0) << ", " << a0.a1 << "), ("
+ << static_cast<int>(a1.a0) << ", " << a1.a1 << "), ("
+ << static_cast<int>(a2.a0) << ", " << a2.a1 << "), ("
+ << static_cast<int>(a3.a0) << ", " << a3.a1 << "), ("
+ << static_cast<int>(a4.a0) << ", " << a4.a1 << "), ("
+ << static_cast<int>(a5.a0) << ", " << a5.a1 << "), ("
+ << static_cast<int>(a6.a0) << ", " << a6.a1 << "), ("
+ << static_cast<int>(a7.a0) << ", " << a7.a1 << "), ("
+ << static_cast<int>(a8.a0) << ", " << a8.a1 << "), ("
+ << static_cast<int>(a9.a0) << ", " << a9.a1 << "), " << a10 << ", "
+ << a11 << ")"
+ << "\n";
+
+ double result = 0;
+
+ result += a0.a0;
+ result += a0.a1;
+ result += a1.a0;
+ result += a1.a1;
+ result += a2.a0;
+ result += a2.a1;
+ result += a3.a0;
+ result += a3.a1;
+ result += a4.a0;
+ result += a4.a1;
+ result += a5.a0;
+ result += a5.a1;
+ result += a6.a0;
+ result += a6.a1;
+ result += a7.a0;
+ result += a7.a1;
+ result += a8.a0;
+ result += a8.a1;
+ result += a9.a0;
+ result += a9.a1;
+ result += a10;
+ result += a11;
+
+ std::cout << "result = " << result << "\n";
+
+ return result;
+}
+
+// Used for testing structs by value.
+// This packed struct happens to have only aligned members.
+DART_EXPORT double PassStruct5BytesPackedMixed(Struct5BytesPackedMixed a0) {
+ std::cout << "PassStruct5BytesPackedMixed"
+ << "((" << a0.a0 << ", " << static_cast<int>(a0.a1) << "))"
+ << "\n";
+
+ double result = 0;
+
+ result += a0.a0;
+ result += a0.a1;
+
+ std::cout << "result = " << result << "\n";
+
+ return result;
+}
+
+// Used for testing structs by value.
+// Check alignment of packed struct in non-packed struct.
+DART_EXPORT double PassStructNestedAlignmentStruct5BytesPackedMixed(
+ StructNestedAlignmentStruct5BytesPackedMixed a0) {
+ std::cout << "PassStructNestedAlignmentStruct5BytesPackedMixed"
+ << "((" << static_cast<int>(a0.a0) << ", (" << a0.a1.a0 << ", "
+ << static_cast<int>(a0.a1.a1) << ")))"
+ << "\n";
+
+ double result = 0;
+
+ result += a0.a0;
+ result += a0.a1.a0;
+ result += a0.a1.a1;
+
+ std::cout << "result = " << result << "\n";
+
+ return result;
+}
+
+// Used for testing structs by value.
+// Check alignment of packed struct array in non-packed struct.
+DART_EXPORT double PassStruct6BytesInlineArrayInt(
+ Struct6BytesInlineArrayInt a0) {
+ std::cout << "PassStruct6BytesInlineArrayInt"
+ << "(([(" << static_cast<int>(a0.a0[0].a0) << ", " << a0.a0[0].a1
+ << "), (" << static_cast<int>(a0.a0[1].a0) << ", " << a0.a0[1].a1
+ << ")]))"
+ << "\n";
+
+ double result = 0;
+
+ result += a0.a0[0].a0;
+ result += a0.a0[0].a1;
+ result += a0.a0[1].a0;
+ result += a0.a0[1].a1;
+
+ std::cout << "result = " << result << "\n";
+
+ return result;
+}
+
+// Used for testing structs by value.
+// Check alignment of packed struct array in non-packed struct.
+DART_EXPORT double PassStruct15BytesInlineArrayMixed(
+ Struct15BytesInlineArrayMixed a0) {
+ std::cout << "PassStruct15BytesInlineArrayMixed"
+ << "(([(" << a0.a0[0].a0 << ", " << static_cast<int>(a0.a0[0].a1)
+ << "), (" << a0.a0[1].a0 << ", " << static_cast<int>(a0.a0[1].a1)
+ << "), (" << a0.a0[2].a0 << ", " << static_cast<int>(a0.a0[2].a1)
+ << ")]))"
+ << "\n";
+
+ double result = 0;
+
+ result += a0.a0[0].a0;
+ result += a0.a0[0].a1;
+ result += a0.a0[1].a0;
+ result += a0.a0[1].a1;
+ result += a0.a0[2].a0;
+ result += a0.a0[2].a1;
+
+ 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"
@@ -4932,6 +5283,78 @@
}
// Used for testing structs by value.
+// Small struct with mis-aligned member.
+DART_EXPORT Struct3BytesPackedInt ReturnStruct3BytesPackedInt(int8_t a0,
+ int16_t a1) {
+ std::cout << "ReturnStruct3BytesPackedInt"
+ << "(" << static_cast<int>(a0) << ", " << a1 << ")"
+ << "\n";
+
+ Struct3BytesPackedInt result;
+
+ result.a0 = a0;
+ result.a1 = a1;
+
+ std::cout << "result = "
+ << "(" << static_cast<int>(result.a0) << ", " << result.a1 << ")"
+ << "\n";
+
+ return result;
+}
+
+// Used for testing structs by value.
+// Struct with mis-aligned member.
+DART_EXPORT Struct8BytesPackedInt ReturnStruct8BytesPackedInt(uint8_t a0,
+ uint32_t a1,
+ uint8_t a2,
+ uint8_t a3,
+ uint8_t a4) {
+ std::cout << "ReturnStruct8BytesPackedInt"
+ << "(" << static_cast<int>(a0) << ", " << a1 << ", "
+ << static_cast<int>(a2) << ", " << static_cast<int>(a3) << ", "
+ << static_cast<int>(a4) << ")"
+ << "\n";
+
+ Struct8BytesPackedInt result;
+
+ result.a0 = a0;
+ result.a1 = a1;
+ result.a2 = a2;
+ result.a3 = a3;
+ result.a4 = a4;
+
+ std::cout << "result = "
+ << "(" << static_cast<int>(result.a0) << ", " << result.a1 << ", "
+ << static_cast<int>(result.a2) << ", "
+ << static_cast<int>(result.a3) << ", "
+ << static_cast<int>(result.a4) << ")"
+ << "\n";
+
+ return result;
+}
+
+// Used for testing structs by value.
+// Struct with mis-aligned member.
+// Tests backfilling of CPU and FPU registers.
+DART_EXPORT Struct9BytesPackedMixed ReturnStruct9BytesPackedMixed(uint8_t a0,
+ double a1) {
+ std::cout << "ReturnStruct9BytesPackedMixed"
+ << "(" << static_cast<int>(a0) << ", " << a1 << ")"
+ << "\n";
+
+ Struct9BytesPackedMixed result;
+
+ result.a0 = a0;
+ result.a1 = a1;
+
+ std::cout << "result = "
+ << "(" << static_cast<int>(result.a0) << ", " << result.a1 << ")"
+ << "\n";
+
+ return result;
+}
+
+// Used for testing structs by value.
// Test that a struct passed in as argument can be returned.
// Especially for ffi callbacks.
// Struct is passed in int registers in most ABIs.
@@ -10367,6 +10790,471 @@
}
// Used for testing structs by value.
+// Small struct with mis-aligned member.
+DART_EXPORT intptr_t TestPassStruct3BytesPackedIntx10(
+ // NOLINTNEXTLINE(whitespace/parens)
+ int64_t (*f)(Struct3BytesPackedInt a0,
+ Struct3BytesPackedInt a1,
+ Struct3BytesPackedInt a2,
+ Struct3BytesPackedInt a3,
+ Struct3BytesPackedInt a4,
+ Struct3BytesPackedInt a5,
+ Struct3BytesPackedInt a6,
+ Struct3BytesPackedInt a7,
+ Struct3BytesPackedInt a8,
+ Struct3BytesPackedInt a9)) {
+ Struct3BytesPackedInt a0;
+ Struct3BytesPackedInt a1;
+ Struct3BytesPackedInt a2;
+ Struct3BytesPackedInt a3;
+ Struct3BytesPackedInt a4;
+ Struct3BytesPackedInt a5;
+ Struct3BytesPackedInt a6;
+ Struct3BytesPackedInt a7;
+ Struct3BytesPackedInt a8;
+ Struct3BytesPackedInt a9;
+
+ a0.a0 = -1;
+ a0.a1 = 2;
+ a1.a0 = -3;
+ a1.a1 = 4;
+ a2.a0 = -5;
+ a2.a1 = 6;
+ a3.a0 = -7;
+ a3.a1 = 8;
+ a4.a0 = -9;
+ a4.a1 = 10;
+ a5.a0 = -11;
+ a5.a1 = 12;
+ a6.a0 = -13;
+ a6.a1 = 14;
+ a7.a0 = -15;
+ a7.a1 = 16;
+ a8.a0 = -17;
+ a8.a1 = 18;
+ a9.a0 = -19;
+ a9.a1 = 20;
+
+ std::cout << "Calling TestPassStruct3BytesPackedIntx10("
+ << "((" << static_cast<int>(a0.a0) << ", " << a0.a1 << "), ("
+ << static_cast<int>(a1.a0) << ", " << a1.a1 << "), ("
+ << static_cast<int>(a2.a0) << ", " << a2.a1 << "), ("
+ << static_cast<int>(a3.a0) << ", " << a3.a1 << "), ("
+ << static_cast<int>(a4.a0) << ", " << a4.a1 << "), ("
+ << static_cast<int>(a5.a0) << ", " << a5.a1 << "), ("
+ << static_cast<int>(a6.a0) << ", " << a6.a1 << "), ("
+ << static_cast<int>(a7.a0) << ", " << a7.a1 << "), ("
+ << static_cast<int>(a8.a0) << ", " << a8.a1 << "), ("
+ << static_cast<int>(a9.a0) << ", " << a9.a1 << "))"
+ << ")\n";
+
+ int64_t result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+ std::cout << "result = " << result << "\n";
+
+ CHECK_EQ(10, result);
+
+ // Pass argument that will make the Dart callback throw.
+ 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 = 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.
+// Struct with mis-aligned member.
+DART_EXPORT intptr_t TestPassStruct8BytesPackedIntx10(
+ // NOLINTNEXTLINE(whitespace/parens)
+ int64_t (*f)(Struct8BytesPackedInt a0,
+ Struct8BytesPackedInt a1,
+ Struct8BytesPackedInt a2,
+ Struct8BytesPackedInt a3,
+ Struct8BytesPackedInt a4,
+ Struct8BytesPackedInt a5,
+ Struct8BytesPackedInt a6,
+ Struct8BytesPackedInt a7,
+ Struct8BytesPackedInt a8,
+ Struct8BytesPackedInt a9)) {
+ Struct8BytesPackedInt a0;
+ Struct8BytesPackedInt a1;
+ Struct8BytesPackedInt a2;
+ Struct8BytesPackedInt a3;
+ Struct8BytesPackedInt a4;
+ Struct8BytesPackedInt a5;
+ Struct8BytesPackedInt a6;
+ Struct8BytesPackedInt a7;
+ Struct8BytesPackedInt a8;
+ Struct8BytesPackedInt a9;
+
+ a0.a0 = 1;
+ a0.a1 = 2;
+ a0.a2 = 3;
+ a0.a3 = 4;
+ a0.a4 = 5;
+ a1.a0 = 6;
+ a1.a1 = 7;
+ a1.a2 = 8;
+ a1.a3 = 9;
+ a1.a4 = 10;
+ a2.a0 = 11;
+ a2.a1 = 12;
+ a2.a2 = 13;
+ a2.a3 = 14;
+ a2.a4 = 15;
+ a3.a0 = 16;
+ a3.a1 = 17;
+ a3.a2 = 18;
+ a3.a3 = 19;
+ a3.a4 = 20;
+ a4.a0 = 21;
+ a4.a1 = 22;
+ a4.a2 = 23;
+ a4.a3 = 24;
+ a4.a4 = 25;
+ a5.a0 = 26;
+ a5.a1 = 27;
+ a5.a2 = 28;
+ a5.a3 = 29;
+ a5.a4 = 30;
+ a6.a0 = 31;
+ a6.a1 = 32;
+ a6.a2 = 33;
+ a6.a3 = 34;
+ a6.a4 = 35;
+ a7.a0 = 36;
+ a7.a1 = 37;
+ a7.a2 = 38;
+ a7.a3 = 39;
+ a7.a4 = 40;
+ a8.a0 = 41;
+ a8.a1 = 42;
+ a8.a2 = 43;
+ a8.a3 = 44;
+ a8.a4 = 45;
+ a9.a0 = 46;
+ a9.a1 = 47;
+ a9.a2 = 48;
+ a9.a3 = 49;
+ a9.a4 = 50;
+
+ std::cout << "Calling TestPassStruct8BytesPackedIntx10("
+ << "((" << static_cast<int>(a0.a0) << ", " << a0.a1 << ", "
+ << static_cast<int>(a0.a2) << ", " << static_cast<int>(a0.a3)
+ << ", " << static_cast<int>(a0.a4) << "), ("
+ << static_cast<int>(a1.a0) << ", " << a1.a1 << ", "
+ << static_cast<int>(a1.a2) << ", " << static_cast<int>(a1.a3)
+ << ", " << static_cast<int>(a1.a4) << "), ("
+ << static_cast<int>(a2.a0) << ", " << a2.a1 << ", "
+ << static_cast<int>(a2.a2) << ", " << static_cast<int>(a2.a3)
+ << ", " << static_cast<int>(a2.a4) << "), ("
+ << static_cast<int>(a3.a0) << ", " << a3.a1 << ", "
+ << static_cast<int>(a3.a2) << ", " << static_cast<int>(a3.a3)
+ << ", " << static_cast<int>(a3.a4) << "), ("
+ << static_cast<int>(a4.a0) << ", " << a4.a1 << ", "
+ << static_cast<int>(a4.a2) << ", " << static_cast<int>(a4.a3)
+ << ", " << static_cast<int>(a4.a4) << "), ("
+ << static_cast<int>(a5.a0) << ", " << a5.a1 << ", "
+ << static_cast<int>(a5.a2) << ", " << static_cast<int>(a5.a3)
+ << ", " << static_cast<int>(a5.a4) << "), ("
+ << static_cast<int>(a6.a0) << ", " << a6.a1 << ", "
+ << static_cast<int>(a6.a2) << ", " << static_cast<int>(a6.a3)
+ << ", " << static_cast<int>(a6.a4) << "), ("
+ << static_cast<int>(a7.a0) << ", " << a7.a1 << ", "
+ << static_cast<int>(a7.a2) << ", " << static_cast<int>(a7.a3)
+ << ", " << static_cast<int>(a7.a4) << "), ("
+ << static_cast<int>(a8.a0) << ", " << a8.a1 << ", "
+ << static_cast<int>(a8.a2) << ", " << static_cast<int>(a8.a3)
+ << ", " << static_cast<int>(a8.a4) << "), ("
+ << static_cast<int>(a9.a0) << ", " << a9.a1 << ", "
+ << static_cast<int>(a9.a2) << ", " << static_cast<int>(a9.a3)
+ << ", " << static_cast<int>(a9.a4) << "))"
+ << ")\n";
+
+ int64_t result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+ std::cout << "result = " << result << "\n";
+
+ CHECK_EQ(1275, result);
+
+ // Pass argument that will make the Dart callback throw.
+ 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 = 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.
+// Struct with mis-aligned member.
+// Tests backfilling of CPU and FPU registers.
+DART_EXPORT intptr_t TestPassStruct9BytesPackedMixedx10DoubleInt32(
+ // NOLINTNEXTLINE(whitespace/parens)
+ double (*f)(Struct9BytesPackedMixed a0,
+ Struct9BytesPackedMixed a1,
+ Struct9BytesPackedMixed a2,
+ Struct9BytesPackedMixed a3,
+ Struct9BytesPackedMixed a4,
+ Struct9BytesPackedMixed a5,
+ Struct9BytesPackedMixed a6,
+ Struct9BytesPackedMixed a7,
+ Struct9BytesPackedMixed a8,
+ Struct9BytesPackedMixed a9,
+ double a10,
+ int32_t a11)) {
+ Struct9BytesPackedMixed a0;
+ Struct9BytesPackedMixed a1;
+ Struct9BytesPackedMixed a2;
+ Struct9BytesPackedMixed a3;
+ Struct9BytesPackedMixed a4;
+ Struct9BytesPackedMixed a5;
+ Struct9BytesPackedMixed a6;
+ Struct9BytesPackedMixed a7;
+ Struct9BytesPackedMixed a8;
+ Struct9BytesPackedMixed a9;
+ double a10;
+ int32_t a11;
+
+ a0.a0 = 1;
+ a0.a1 = 2.0;
+ a1.a0 = 3;
+ a1.a1 = 4.0;
+ a2.a0 = 5;
+ a2.a1 = 6.0;
+ a3.a0 = 7;
+ a3.a1 = 8.0;
+ a4.a0 = 9;
+ a4.a1 = 10.0;
+ a5.a0 = 11;
+ a5.a1 = 12.0;
+ a6.a0 = 13;
+ a6.a1 = 14.0;
+ a7.a0 = 15;
+ a7.a1 = 16.0;
+ a8.a0 = 17;
+ a8.a1 = 18.0;
+ a9.a0 = 19;
+ a9.a1 = 20.0;
+ a10 = -21.0;
+ a11 = 22;
+
+ std::cout << "Calling TestPassStruct9BytesPackedMixedx10DoubleInt32("
+ << "((" << static_cast<int>(a0.a0) << ", " << a0.a1 << "), ("
+ << static_cast<int>(a1.a0) << ", " << a1.a1 << "), ("
+ << static_cast<int>(a2.a0) << ", " << a2.a1 << "), ("
+ << static_cast<int>(a3.a0) << ", " << a3.a1 << "), ("
+ << static_cast<int>(a4.a0) << ", " << a4.a1 << "), ("
+ << static_cast<int>(a5.a0) << ", " << a5.a1 << "), ("
+ << static_cast<int>(a6.a0) << ", " << a6.a1 << "), ("
+ << static_cast<int>(a7.a0) << ", " << a7.a1 << "), ("
+ << static_cast<int>(a8.a0) << ", " << a8.a1 << "), ("
+ << static_cast<int>(a9.a0) << ", " << a9.a1 << "), " << a10 << ", "
+ << a11 << ")"
+ << ")\n";
+
+ double result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11);
+
+ std::cout << "result = " << result << "\n";
+
+ CHECK_APPROX(211.0, result);
+
+ // Pass argument that will make the Dart callback throw.
+ a0.a0 = 42;
+
+ result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11);
+
+ CHECK_APPROX(0.0, result);
+
+ // Pass argument that will make the Dart callback return null.
+ a0.a0 = 84;
+
+ result = f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11);
+
+ CHECK_APPROX(0.0, result);
+
+ return 0;
+}
+
+// Used for testing structs by value.
+// This packed struct happens to have only aligned members.
+DART_EXPORT intptr_t TestPassStruct5BytesPackedMixed(
+ // NOLINTNEXTLINE(whitespace/parens)
+ double (*f)(Struct5BytesPackedMixed a0)) {
+ Struct5BytesPackedMixed a0;
+
+ a0.a0 = -1.0;
+ a0.a1 = 2;
+
+ std::cout << "Calling TestPassStruct5BytesPackedMixed("
+ << "((" << a0.a0 << ", " << static_cast<int>(a0.a1) << "))"
+ << ")\n";
+
+ double result = f(a0);
+
+ std::cout << "result = " << result << "\n";
+
+ CHECK_APPROX(1.0, result);
+
+ // Pass argument that will make the Dart callback throw.
+ a0.a0 = 42;
+
+ result = f(a0);
+
+ CHECK_APPROX(0.0, result);
+
+ // Pass argument that will make the Dart callback return null.
+ a0.a0 = 84;
+
+ result = f(a0);
+
+ CHECK_APPROX(0.0, result);
+
+ return 0;
+}
+
+// Used for testing structs by value.
+// Check alignment of packed struct in non-packed struct.
+DART_EXPORT intptr_t TestPassStructNestedAlignmentStruct5BytesPackedMixed(
+ // NOLINTNEXTLINE(whitespace/parens)
+ double (*f)(StructNestedAlignmentStruct5BytesPackedMixed a0)) {
+ StructNestedAlignmentStruct5BytesPackedMixed a0;
+
+ a0.a0 = 1;
+ a0.a1.a0 = 2.0;
+ a0.a1.a1 = 3;
+
+ std::cout << "Calling TestPassStructNestedAlignmentStruct5BytesPackedMixed("
+ << "((" << static_cast<int>(a0.a0) << ", (" << a0.a1.a0 << ", "
+ << static_cast<int>(a0.a1.a1) << ")))"
+ << ")\n";
+
+ double result = f(a0);
+
+ std::cout << "result = " << result << "\n";
+
+ CHECK_APPROX(6.0, result);
+
+ // Pass argument that will make the Dart callback throw.
+ a0.a0 = 42;
+
+ result = f(a0);
+
+ CHECK_APPROX(0.0, result);
+
+ // Pass argument that will make the Dart callback return null.
+ a0.a0 = 84;
+
+ result = f(a0);
+
+ CHECK_APPROX(0.0, result);
+
+ return 0;
+}
+
+// Used for testing structs by value.
+// Check alignment of packed struct array in non-packed struct.
+DART_EXPORT intptr_t TestPassStruct6BytesInlineArrayInt(
+ // NOLINTNEXTLINE(whitespace/parens)
+ double (*f)(Struct6BytesInlineArrayInt a0)) {
+ Struct6BytesInlineArrayInt a0;
+
+ a0.a0[0].a0 = -1;
+ a0.a0[0].a1 = 2;
+ a0.a0[1].a0 = -3;
+ a0.a0[1].a1 = 4;
+
+ std::cout << "Calling TestPassStruct6BytesInlineArrayInt("
+ << "(([(" << static_cast<int>(a0.a0[0].a0) << ", " << a0.a0[0].a1
+ << "), (" << static_cast<int>(a0.a0[1].a0) << ", " << a0.a0[1].a1
+ << ")]))"
+ << ")\n";
+
+ double result = f(a0);
+
+ std::cout << "result = " << result << "\n";
+
+ CHECK_APPROX(2.0, result);
+
+ // Pass argument that will make the Dart callback throw.
+ a0.a0[0].a0 = 42;
+
+ result = f(a0);
+
+ CHECK_APPROX(0.0, result);
+
+ // Pass argument that will make the Dart callback return null.
+ a0.a0[0].a0 = 84;
+
+ result = f(a0);
+
+ CHECK_APPROX(0.0, result);
+
+ return 0;
+}
+
+// Used for testing structs by value.
+// Check alignment of packed struct array in non-packed struct.
+DART_EXPORT intptr_t TestPassStruct15BytesInlineArrayMixed(
+ // NOLINTNEXTLINE(whitespace/parens)
+ double (*f)(Struct15BytesInlineArrayMixed a0)) {
+ Struct15BytesInlineArrayMixed a0;
+
+ a0.a0[0].a0 = -1.0;
+ a0.a0[0].a1 = 2;
+ a0.a0[1].a0 = -3.0;
+ a0.a0[1].a1 = 4;
+ a0.a0[2].a0 = -5.0;
+ a0.a0[2].a1 = 6;
+
+ std::cout << "Calling TestPassStruct15BytesInlineArrayMixed("
+ << "(([(" << a0.a0[0].a0 << ", " << static_cast<int>(a0.a0[0].a1)
+ << "), (" << a0.a0[1].a0 << ", " << static_cast<int>(a0.a0[1].a1)
+ << "), (" << a0.a0[2].a0 << ", " << static_cast<int>(a0.a0[2].a1)
+ << ")]))"
+ << ")\n";
+
+ double result = f(a0);
+
+ std::cout << "result = " << result << "\n";
+
+ CHECK_APPROX(3.0, result);
+
+ // Pass argument that will make the Dart callback throw.
+ a0.a0[0].a0 = 42;
+
+ result = f(a0);
+
+ CHECK_APPROX(0.0, result);
+
+ // Pass argument that will make the Dart callback return null.
+ a0.a0[0].a0 = 84;
+
+ result = f(a0);
+
+ 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)
@@ -12527,6 +13415,157 @@
}
// Used for testing structs by value.
+// Small struct with mis-aligned member.
+DART_EXPORT intptr_t TestReturnStruct3BytesPackedInt(
+ // NOLINTNEXTLINE(whitespace/parens)
+ Struct3BytesPackedInt (*f)(int8_t a0, int16_t a1)) {
+ int8_t a0;
+ int16_t a1;
+
+ a0 = -1;
+ a1 = 2;
+
+ std::cout << "Calling TestReturnStruct3BytesPackedInt("
+ << "(" << static_cast<int>(a0) << ", " << a1 << ")"
+ << ")\n";
+
+ Struct3BytesPackedInt result = f(a0, a1);
+
+ std::cout << "result = "
+ << "(" << static_cast<int>(result.a0) << ", " << result.a1 << ")"
+ << "\n";
+
+ CHECK_EQ(a0, result.a0);
+ CHECK_EQ(a1, result.a1);
+
+ // Pass argument that will make the Dart callback throw.
+ a0 = 42;
+
+ result = f(a0, a1);
+
+ CHECK_EQ(0, result.a0);
+ CHECK_EQ(0, result.a1);
+
+ // Pass argument that will make the Dart callback return null.
+ a0 = 84;
+
+ result = f(a0, a1);
+
+ CHECK_EQ(0, result.a0);
+ CHECK_EQ(0, result.a1);
+
+ return 0;
+}
+
+// Used for testing structs by value.
+// Struct with mis-aligned member.
+DART_EXPORT intptr_t TestReturnStruct8BytesPackedInt(
+ // NOLINTNEXTLINE(whitespace/parens)
+ Struct8BytesPackedInt (
+ *f)(uint8_t a0, uint32_t a1, uint8_t a2, uint8_t a3, uint8_t a4)) {
+ uint8_t a0;
+ uint32_t a1;
+ uint8_t a2;
+ uint8_t a3;
+ uint8_t a4;
+
+ a0 = 1;
+ a1 = 2;
+ a2 = 3;
+ a3 = 4;
+ a4 = 5;
+
+ std::cout << "Calling TestReturnStruct8BytesPackedInt("
+ << "(" << static_cast<int>(a0) << ", " << a1 << ", "
+ << static_cast<int>(a2) << ", " << static_cast<int>(a3) << ", "
+ << static_cast<int>(a4) << ")"
+ << ")\n";
+
+ Struct8BytesPackedInt result = f(a0, a1, a2, a3, a4);
+
+ std::cout << "result = "
+ << "(" << static_cast<int>(result.a0) << ", " << result.a1 << ", "
+ << static_cast<int>(result.a2) << ", "
+ << static_cast<int>(result.a3) << ", "
+ << static_cast<int>(result.a4) << ")"
+ << "\n";
+
+ CHECK_EQ(a0, result.a0);
+ CHECK_EQ(a1, result.a1);
+ CHECK_EQ(a2, result.a2);
+ CHECK_EQ(a3, result.a3);
+ CHECK_EQ(a4, result.a4);
+
+ // Pass argument that will make the Dart callback throw.
+ a0 = 42;
+
+ result = f(a0, a1, a2, a3, a4);
+
+ CHECK_EQ(0, result.a0);
+ CHECK_EQ(0, result.a1);
+ CHECK_EQ(0, result.a2);
+ CHECK_EQ(0, result.a3);
+ CHECK_EQ(0, result.a4);
+
+ // Pass argument that will make the Dart callback return null.
+ a0 = 84;
+
+ result = f(a0, a1, a2, a3, a4);
+
+ CHECK_EQ(0, result.a0);
+ CHECK_EQ(0, result.a1);
+ CHECK_EQ(0, result.a2);
+ CHECK_EQ(0, result.a3);
+ CHECK_EQ(0, result.a4);
+
+ return 0;
+}
+
+// Used for testing structs by value.
+// Struct with mis-aligned member.
+// Tests backfilling of CPU and FPU registers.
+DART_EXPORT intptr_t TestReturnStruct9BytesPackedMixed(
+ // NOLINTNEXTLINE(whitespace/parens)
+ Struct9BytesPackedMixed (*f)(uint8_t a0, double a1)) {
+ uint8_t a0;
+ double a1;
+
+ a0 = 1;
+ a1 = 2.0;
+
+ std::cout << "Calling TestReturnStruct9BytesPackedMixed("
+ << "(" << static_cast<int>(a0) << ", " << a1 << ")"
+ << ")\n";
+
+ Struct9BytesPackedMixed result = f(a0, a1);
+
+ std::cout << "result = "
+ << "(" << static_cast<int>(result.a0) << ", " << result.a1 << ")"
+ << "\n";
+
+ CHECK_EQ(a0, result.a0);
+ CHECK_APPROX(a1, result.a1);
+
+ // Pass argument that will make the Dart callback throw.
+ a0 = 42;
+
+ result = f(a0, a1);
+
+ CHECK_EQ(0, result.a0);
+ CHECK_APPROX(0.0, result.a1);
+
+ // Pass argument that will make the Dart callback return null.
+ a0 = 84;
+
+ result = f(a0, a1);
+
+ CHECK_EQ(0, result.a0);
+ CHECK_APPROX(0.0, result.a1);
+
+ return 0;
+}
+
+// Used for testing structs by value.
// Test that a struct passed in as argument can be returned.
// Especially for ffi callbacks.
// Struct is passed in int registers in most ABIs.
diff --git a/runtime/observatory/tests/service/allocations_test.dart b/runtime/observatory/tests/service/allocations_test.dart
index 6a7d002..bc98c0e 100644
--- a/runtime/observatory/tests/service/allocations_test.dart
+++ b/runtime/observatory/tests/service/allocations_test.dart
@@ -8,6 +8,7 @@
import 'package:test/test.dart';
import 'test_helper.dart';
+@pragma('vm:entry-point')
class Foo {}
// Prevent TFA from removing this static field to ensure the objects are kept
diff --git a/runtime/observatory_2/tests/service_2/allocations_test.dart b/runtime/observatory_2/tests/service_2/allocations_test.dart
index 3fb6ce4..2732498 100644
--- a/runtime/observatory_2/tests/service_2/allocations_test.dart
+++ b/runtime/observatory_2/tests/service_2/allocations_test.dart
@@ -8,6 +8,7 @@
import 'package:test/test.dart';
import 'test_helper.dart';
+@pragma('vm:entry-point')
class Foo {}
// Prevent TFA from removing this static field to ensure the objects are kept
diff --git a/runtime/platform/globals.h b/runtime/platform/globals.h
index 407304e..52201dc 100644
--- a/runtime/platform/globals.h
+++ b/runtime/platform/globals.h
@@ -20,7 +20,13 @@
// from the way the Dart project expects it: DEBUG indicating a debug build.
#if !defined(NDEBUG) && !defined(DEBUG)
#define DEBUG
-#endif // !NDEBUG && !DEBUG
+#endif // !NDEBUG && !DEBUG \
+#else
+// Since <cassert> uses NDEBUG to signify that assert() macros should be turned
+// off, we'll define it when DEBUG is _not_ set.
+#if !defined(DEBUG)
+#define NDEBUG
+#endif
#endif // GOOGLE3
// __STDC_FORMAT_MACROS has to be defined before including <inttypes.h> to
@@ -85,6 +91,8 @@
#include <string.h>
#include <sys/types.h>
+#include <cassert> // For assert() in constant expressions.
+
#if defined(_WIN32)
#include "platform/floating_point_win.h"
#endif // defined(_WIN32)
@@ -136,12 +144,6 @@
#define DEBUG_ONLY(code)
#endif // defined(DEBUG)
-#if defined(DEBUG)
-#define UNLESS_DEBUG(code)
-#else // defined(DEBUG)
-#define UNLESS_DEBUG(code) code
-#endif // defined(DEBUG)
-
namespace dart {
struct simd128_value_t {
diff --git a/runtime/platform/utils.h b/runtime/platform/utils.h
index eecab24..788dd2a 100644
--- a/runtime/platform/utils.h
+++ b/runtime/platform/utils.h
@@ -57,7 +57,7 @@
}
template <typename T>
- static inline bool IsPowerOfTwo(T x) {
+ static constexpr bool IsPowerOfTwo(T x) {
return ((x & (x - 1)) == 0) && (x != 0);
}
@@ -73,13 +73,13 @@
}
template <typename T>
- static inline bool IsAligned(T x, intptr_t n) {
- ASSERT(IsPowerOfTwo(n));
+ static constexpr bool IsAligned(T x, intptr_t n) {
+ assert(IsPowerOfTwo(n));
return (x & (n - 1)) == 0;
}
template <typename T>
- static inline bool IsAligned(T* x, intptr_t n) {
+ static constexpr bool IsAligned(T* x, intptr_t n) {
return IsAligned(reinterpret_cast<uword>(x), n);
}
diff --git a/runtime/tests/vm/dart/regress_45270_test.dart b/runtime/tests/vm/dart/regress_45270_test.dart
new file mode 100644
index 0000000..867e2bd
--- /dev/null
+++ b/runtime/tests/vm/dart/regress_45270_test.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2021, 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.
+//
+// Regression test for https://github.com/dart-lang/sdk/issues/45270
+
+void main() {
+ // Function.apply.
+ test<int?>(42);
+ test<int?>(null);
+
+ // Dynamic closure calls.
+ test2<int?>(42);
+ test2<int?>(null);
+}
+
+void test<T>(T value) {
+ final f = (T inner) {
+ print('f inner=$inner T=$T');
+ };
+ Function.apply(f, [value]);
+}
+
+void test2<T>(T value) {
+ dynamic f = (T inner) {
+ print('f inner=$inner T=$T');
+ };
+ f(value);
+}
diff --git a/runtime/vm/bitfield.h b/runtime/vm/bitfield.h
index 4a4f45f..b982d83 100644
--- a/runtime/vm/bitfield.h
+++ b/runtime/vm/bitfield.h
@@ -7,7 +7,6 @@
#include <type_traits>
-#include "platform/assert.h"
#include "platform/globals.h"
namespace dart {
@@ -52,8 +51,8 @@
static constexpr int bitsize() { return size; }
// Returns an S with the bit field value encoded.
- static UNLESS_DEBUG(constexpr) S encode(T value) {
- DEBUG_ASSERT(is_valid(value));
+ static constexpr S encode(T value) {
+ assert(is_valid(value));
return encode_unchecked(value);
}
@@ -62,29 +61,28 @@
// Ensure we slide down the sign bit if the value in the bit field is signed
// and negative. We use 64-bit ints inside the expression since we can have
// both cases: sizeof(S) > sizeof(T) or sizeof(S) < sizeof(T).
- return static_cast<T>(
- (sign_extend
- ? (static_cast<int64_t>(static_cast<uint64_t>(value)
- << (64 - (size + position))) >>
- (64 - size))
- : ((static_cast<typename std::make_unsigned<S>::type>(value) >>
- position) &
- mask())));
+ if constexpr (sign_extend) {
+ auto const u = static_cast<uint64_t>(value);
+ return static_cast<T>((static_cast<int64_t>(u << (64 - kNextBit))) >>
+ (64 - size));
+ } else {
+ auto const u = static_cast<typename std::make_unsigned<S>::type>(value);
+ return static_cast<T>((u >> position) & mask());
+ }
}
// Returns an S with the bit field value encoded based on the
// original value. Only the bits corresponding to this bit field
// will be changed.
- static UNLESS_DEBUG(constexpr) S update(T value, S original) {
- DEBUG_ASSERT(is_valid(value));
- return encode_unchecked(value) | (~mask_in_place() & original);
+ static constexpr S update(T value, S original) {
+ return encode(value) | (~mask_in_place() & original);
}
private:
// Returns an S with the bit field value encoded.
static constexpr S encode_unchecked(T value) {
- return (static_cast<typename std::make_unsigned<S>::type>(value) & mask())
- << position;
+ auto const u = static_cast<typename std::make_unsigned<S>::type>(value);
+ return (u & mask()) << position;
}
};
diff --git a/runtime/vm/bitfield_test.cc b/runtime/vm/bitfield_test.cc
index e90a257..e2d40f0 100644
--- a/runtime/vm/bitfield_test.cc
+++ b/runtime/vm/bitfield_test.cc
@@ -70,4 +70,16 @@
TestSignExtendedBitField<int32_t>();
}
+#if defined(DEBUG)
+#define DEBUG_CRASH "Crash"
+#else
+#define DEBUG_CRASH "Pass"
+#endif
+
+VM_UNIT_TEST_CASE_WITH_EXPECTATION(BitFields_Assert, DEBUG_CRASH) {
+ class F : public BitField<uint32_t, uint32_t, 0, 8, /*sign_extend=*/false> {};
+ const uint32_t value = F::encode(kMaxUint32);
+ EXPECT_EQ(kMaxUint8, value);
+}
+
} // namespace dart
diff --git a/runtime/vm/compiler/assembler/assembler_ia32.cc b/runtime/vm/compiler/assembler/assembler_ia32.cc
index b33e519..56c4f3e 100644
--- a/runtime/vm/compiler/assembler/assembler_ia32.cc
+++ b/runtime/vm/compiler/assembler/assembler_ia32.cc
@@ -1763,20 +1763,19 @@
}
void Assembler::LoadFromOffset(Register reg,
- Register base,
- int32_t offset,
+ const Address& address,
OperandSize type) {
switch (type) {
case kByte:
- return movsxb(reg, Address(base, offset));
+ return movsxb(reg, address);
case kUnsignedByte:
- return movzxb(reg, Address(base, offset));
+ return movzxb(reg, address);
case kTwoBytes:
- return movsxw(reg, Address(base, offset));
+ return movsxw(reg, address);
case kUnsignedTwoBytes:
- return movzxw(reg, Address(base, offset));
+ return movzxw(reg, address);
case kFourBytes:
- return movl(reg, Address(base, offset));
+ return movl(reg, address);
default:
UNREACHABLE();
break;
diff --git a/runtime/vm/compiler/assembler/assembler_ia32.h b/runtime/vm/compiler/assembler/assembler_ia32.h
index 1573131..fecfe5a1 100644
--- a/runtime/vm/compiler/assembler/assembler_ia32.h
+++ b/runtime/vm/compiler/assembler/assembler_ia32.h
@@ -584,29 +584,40 @@
j(ZERO, label, distance);
}
- void LoadFromOffset(Register reg,
+ // Arch-specific LoadFromOffset to choose the right operation for [sz].
+ void LoadFromOffset(Register dst,
+ const Address& address,
+ OperandSize sz = kFourBytes);
+ void LoadFromOffset(Register dst,
Register base,
int32_t offset,
- OperandSize type = kFourBytes);
- void LoadField(Register dst, FieldAddress address) { movl(dst, address); }
+ OperandSize sz = kFourBytes) {
+ LoadFromOffset(dst, Address(base, offset), sz);
+ }
+ void LoadField(Register dst,
+ const FieldAddress& address,
+ OperandSize sz = kFourBytes) {
+ LoadFromOffset(dst, address, sz);
+ }
void LoadFieldFromOffset(Register reg,
Register base,
int32_t offset,
- OperandSize type = kFourBytes) {
- LoadFromOffset(reg, base, offset - kHeapObjectTag, type);
+ OperandSize sz = kFourBytes) {
+ LoadFromOffset(reg, FieldAddress(base, offset), sz);
}
void LoadCompressedFieldFromOffset(Register reg,
Register base,
int32_t offset,
- OperandSize type = kFourBytes) {
- LoadFieldFromOffset(reg, base, offset, type);
+ OperandSize sz = kFourBytes) {
+ LoadFieldFromOffset(reg, base, offset, sz);
}
- void LoadIndexedFieldFromOffset(Register reg,
- Register base,
- int32_t offset,
- Register index,
- ScaleFactor scale) {
- LoadField(reg, FieldAddress(base, index, scale, offset));
+ void LoadIndexedPayload(Register dst,
+ Register base,
+ int32_t payload_offset,
+ Register index,
+ ScaleFactor scale,
+ OperandSize sz = kFourBytes) {
+ LoadFromOffset(dst, FieldAddress(base, index, scale, payload_offset), sz);
}
void LoadFromStack(Register dst, intptr_t depth);
void StoreToStack(Register src, intptr_t depth);
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index 0c2dcff..139eaeb 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -5431,6 +5431,67 @@
env());
}
+LocationSummary* InstantiateTypeArgumentsInstr::MakeLocationSummary(
+ Zone* zone,
+ bool opt) const {
+ const intptr_t kNumInputs = 3;
+ const intptr_t kNumTemps = 0;
+ LocationSummary* locs = new (zone)
+ LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kCall);
+ locs->set_in(0, Location::RegisterLocation(
+ InstantiationABI::kInstantiatorTypeArgumentsReg));
+ locs->set_in(1, Location::RegisterLocation(
+ InstantiationABI::kFunctionTypeArgumentsReg));
+ locs->set_in(2, Location::RegisterLocation(
+ InstantiationABI::kUninstantiatedTypeArgumentsReg));
+ locs->set_out(
+ 0, Location::RegisterLocation(InstantiationABI::kResultTypeArgumentsReg));
+ return locs;
+}
+
+void InstantiateTypeArgumentsInstr::EmitNativeCode(
+ FlowGraphCompiler* compiler) {
+ // We should never try and instantiate a TAV known at compile time to be null,
+ // so we can use a null value below for the dynamic case.
+ ASSERT(!type_arguments()->BindsToConstant() ||
+ !type_arguments()->BoundConstant().IsNull());
+ const auto& type_args =
+ type_arguments()->BindsToConstant()
+ ? TypeArguments::Cast(type_arguments()->BoundConstant())
+ : Object::null_type_arguments();
+ const intptr_t len = type_args.Length();
+ const bool can_function_type_args_be_null =
+ function_type_arguments()->CanBe(Object::null_object());
+
+ compiler::Label type_arguments_instantiated;
+ if (type_args.IsNull()) {
+ // Currently we only create dynamic InstantiateTypeArguments instructions
+ // in cases where we know the type argument is uninstantiated at runtime,
+ // so there are no extra checks needed to call the stub successfully.
+ } else if (type_args.IsRawWhenInstantiatedFromRaw(len) &&
+ can_function_type_args_be_null) {
+ // If both the instantiator and function type arguments are null and if the
+ // type argument vector instantiated from null becomes a vector of dynamic,
+ // then use null as the type arguments.
+ compiler::Label non_null_type_args;
+ __ LoadObject(InstantiationABI::kResultTypeArgumentsReg,
+ Object::null_object());
+ __ CompareRegisters(InstantiationABI::kInstantiatorTypeArgumentsReg,
+ InstantiationABI::kResultTypeArgumentsReg);
+ if (!function_type_arguments()->BindsToConstant()) {
+ __ BranchIf(NOT_EQUAL, &non_null_type_args);
+ __ CompareRegisters(InstantiationABI::kFunctionTypeArgumentsReg,
+ InstantiationABI::kResultTypeArgumentsReg);
+ }
+ __ BranchIf(EQUAL, &type_arguments_instantiated);
+ __ Bind(&non_null_type_args);
+ }
+
+ compiler->GenerateStubCall(source(), GetStub(), UntaggedPcDescriptors::kOther,
+ locs(), deopt_id());
+ __ Bind(&type_arguments_instantiated);
+}
+
LocationSummary* DeoptimizeInstr::MakeLocationSummary(Zone* zone,
bool opt) const {
return new (zone) LocationSummary(zone, 0, 0, LocationSummary::kNoCall);
diff --git a/runtime/vm/compiler/backend/il_arm.cc b/runtime/vm/compiler/backend/il_arm.cc
index 0655206..02b811a 100644
--- a/runtime/vm/compiler/backend/il_arm.cc
+++ b/runtime/vm/compiler/backend/il_arm.cc
@@ -3456,69 +3456,6 @@
__ Bind(&done);
}
-LocationSummary* InstantiateTypeArgumentsInstr::MakeLocationSummary(
- Zone* zone,
- bool opt) const {
- const intptr_t kNumInputs = 3;
- const intptr_t kNumTemps = 0;
- LocationSummary* locs = new (zone)
- LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kCall);
- locs->set_in(0, Location::RegisterLocation(
- InstantiationABI::kInstantiatorTypeArgumentsReg));
- locs->set_in(1, Location::RegisterLocation(
- InstantiationABI::kFunctionTypeArgumentsReg));
- locs->set_in(2, Location::RegisterLocation(
- InstantiationABI::kUninstantiatedTypeArgumentsReg));
- locs->set_out(
- 0, Location::RegisterLocation(InstantiationABI::kResultTypeArgumentsReg));
- return locs;
-}
-
-void InstantiateTypeArgumentsInstr::EmitNativeCode(
- FlowGraphCompiler* compiler) {
- // We should never try and instantiate a TAV known at compile time to be null,
- // so we can use a null value below for the dynamic case.
- ASSERT(!type_arguments()->BindsToConstant() ||
- !type_arguments()->BoundConstant().IsNull());
- const auto& type_args =
- type_arguments()->BindsToConstant()
- ? TypeArguments::Cast(type_arguments()->BoundConstant())
- : Object::null_type_arguments();
- const intptr_t len = type_args.Length();
- const bool can_function_type_args_be_null =
- function_type_arguments()->CanBe(Object::null_object());
-
- compiler::Label type_arguments_instantiated;
- if (type_args.IsNull()) {
- // Currently we only create dynamic InstantiateTypeArguments instructions
- // in cases where we know the type argument is uninstantiated at runtime,
- // so there are no extra checks needed to call the stub successfully.
- } else if (type_args.IsRawWhenInstantiatedFromRaw(len) &&
- can_function_type_args_be_null) {
- // If both the instantiator and function type arguments are null and if the
- // type argument vector instantiated from null becomes a vector of dynamic,
- // then use null as the type arguments.
- //
- // 'instantiator_type_args_reg' is a TypeArguments object (or null).
- // 'function_type_args_reg' is a TypeArguments object (or null).
- const Register instantiator_type_args_reg = locs()->in(0).reg();
- const Register function_type_args_reg = locs()->in(1).reg();
- const Register result_reg = locs()->out(0).reg();
- ASSERT(result_reg != instantiator_type_args_reg &&
- result_reg != function_type_args_reg);
- __ LoadObject(result_reg, Object::null_object());
- __ cmp(instantiator_type_args_reg, compiler::Operand(result_reg));
- if (!function_type_arguments()->BindsToConstant()) {
- __ cmp(function_type_args_reg, compiler::Operand(result_reg), EQ);
- }
- __ b(&type_arguments_instantiated, EQ);
- }
- // Lookup cache in stub before calling runtime.
- compiler->GenerateStubCall(source(), GetStub(), UntaggedPcDescriptors::kOther,
- locs());
- __ Bind(&type_arguments_instantiated);
-}
-
LocationSummary* AllocateUninitializedContextInstr::MakeLocationSummary(
Zone* zone,
bool opt) const {
diff --git a/runtime/vm/compiler/backend/il_arm64.cc b/runtime/vm/compiler/backend/il_arm64.cc
index a4d8689..170d8e6 100644
--- a/runtime/vm/compiler/backend/il_arm64.cc
+++ b/runtime/vm/compiler/backend/il_arm64.cc
@@ -3031,72 +3031,6 @@
__ Bind(&done);
}
-LocationSummary* InstantiateTypeArgumentsInstr::MakeLocationSummary(
- Zone* zone,
- bool opt) const {
- const intptr_t kNumInputs = 3;
- const intptr_t kNumTemps = 0;
- LocationSummary* locs = new (zone)
- LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kCall);
- locs->set_in(0, Location::RegisterLocation(
- InstantiationABI::kInstantiatorTypeArgumentsReg));
- locs->set_in(1, Location::RegisterLocation(
- InstantiationABI::kFunctionTypeArgumentsReg));
- locs->set_in(2, Location::RegisterLocation(
- InstantiationABI::kUninstantiatedTypeArgumentsReg));
- locs->set_out(
- 0, Location::RegisterLocation(InstantiationABI::kResultTypeArgumentsReg));
- return locs;
-}
-
-void InstantiateTypeArgumentsInstr::EmitNativeCode(
- FlowGraphCompiler* compiler) {
- // We should never try and instantiate a TAV known at compile time to be null,
- // so we can use a null value below for the dynamic case.
- ASSERT(!type_arguments()->BindsToConstant() ||
- !type_arguments()->BoundConstant().IsNull());
- const auto& type_args =
- type_arguments()->BindsToConstant()
- ? TypeArguments::Cast(type_arguments()->BoundConstant())
- : Object::null_type_arguments();
- const intptr_t len = type_args.Length();
- const bool can_function_type_args_be_null =
- function_type_arguments()->CanBe(Object::null_object());
-
- compiler::Label type_arguments_instantiated;
- if (type_args.IsNull()) {
- // Currently we only create dynamic InstantiateTypeArguments instructions
- // in cases where we know the type argument is uninstantiated at runtime,
- // so there are no extra checks needed to call the stub successfully.
- } else if (type_args.IsRawWhenInstantiatedFromRaw(len) &&
- can_function_type_args_be_null) {
- // If both the instantiator and function type arguments are null and if the
- // type argument vector instantiated from null becomes a vector of dynamic,
- // then use null as the type arguments.
- compiler::Label non_null_type_args;
- // 'instantiator_type_args_reg' is a TypeArguments object (or null).
- // 'function_type_args_reg' is a TypeArguments object (or null).
- const Register instantiator_type_args_reg = locs()->in(0).reg();
- const Register function_type_args_reg = locs()->in(1).reg();
- const Register result_reg = locs()->out(0).reg();
- ASSERT(result_reg != instantiator_type_args_reg &&
- result_reg != function_type_args_reg);
- __ LoadObject(result_reg, Object::null_object());
- __ CompareRegisters(instantiator_type_args_reg, result_reg);
- if (!function_type_arguments()->BindsToConstant()) {
- __ b(&non_null_type_args, NE);
- __ CompareRegisters(function_type_args_reg, result_reg);
- }
- __ b(&type_arguments_instantiated, EQ);
- __ Bind(&non_null_type_args);
- }
- // Lookup cache in stub before calling runtime.
-
- compiler->GenerateStubCall(source(), GetStub(), UntaggedPcDescriptors::kOther,
- locs());
- __ Bind(&type_arguments_instantiated);
-}
-
LocationSummary* AllocateUninitializedContextInstr::MakeLocationSummary(
Zone* zone,
bool opt) const {
diff --git a/runtime/vm/compiler/backend/il_ia32.cc b/runtime/vm/compiler/backend/il_ia32.cc
index 2b8fd43..3381064 100644
--- a/runtime/vm/compiler/backend/il_ia32.cc
+++ b/runtime/vm/compiler/backend/il_ia32.cc
@@ -2782,71 +2782,6 @@
__ Bind(&done);
}
-LocationSummary* InstantiateTypeArgumentsInstr::MakeLocationSummary(
- Zone* zone,
- bool opt) const {
- const intptr_t kNumInputs = 3;
- const intptr_t kNumTemps = 0;
- LocationSummary* locs = new (zone)
- LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kCall);
- locs->set_in(0, Location::RegisterLocation(
- InstantiationABI::kInstantiatorTypeArgumentsReg));
- locs->set_in(1, Location::RegisterLocation(
- InstantiationABI::kFunctionTypeArgumentsReg));
- locs->set_in(2, Location::RegisterLocation(
- InstantiationABI::kUninstantiatedTypeArgumentsReg));
- locs->set_out(
- 0, Location::RegisterLocation(InstantiationABI::kResultTypeArgumentsReg));
- return locs;
-}
-
-void InstantiateTypeArgumentsInstr::EmitNativeCode(
- FlowGraphCompiler* compiler) {
- // We should never try and instantiate a TAV known at compile time to be null,
- // so we can use a null value below for the dynamic case.
- ASSERT(!type_arguments()->BindsToConstant() ||
- !type_arguments()->BoundConstant().IsNull());
- const auto& type_args =
- type_arguments()->BindsToConstant()
- ? TypeArguments::Cast(type_arguments()->BoundConstant())
- : Object::null_type_arguments();
- const intptr_t len = type_args.Length();
- const bool can_function_type_args_be_null =
- function_type_arguments()->CanBe(Object::null_object());
-
- compiler::Label type_arguments_instantiated;
- if (type_args.IsNull()) {
- // Currently we only create dynamic InstantiateTypeArguments instructions
- // in cases where we know the type argument is uninstantiated at runtime,
- // so there are no extra checks needed to call the stub successfully.
- } else if (type_args.IsRawWhenInstantiatedFromRaw(len) &&
- can_function_type_args_be_null) {
- // If both the instantiator and function type arguments are null and if the
- // type argument vector instantiated from null becomes a vector of dynamic,
- // then use null as the type arguments.
- compiler::Label non_null_type_args;
- // 'instantiator_type_args_reg' is a TypeArguments object (or null).
- // 'function_type_args_reg' is a TypeArguments object (or null).
- const Register instantiator_type_args_reg = locs()->in(0).reg();
- const Register function_type_args_reg = locs()->in(1).reg();
- const Register result_reg = locs()->out(0).reg();
- ASSERT(result_reg != instantiator_type_args_reg &&
- result_reg != function_type_args_reg);
- __ LoadObject(result_reg, Object::null_object());
- __ cmpl(instantiator_type_args_reg, result_reg);
- if (!function_type_arguments()->BindsToConstant()) {
- __ j(NOT_EQUAL, &non_null_type_args, compiler::Assembler::kNearJump);
- __ cmpl(function_type_args_reg, result_reg);
- }
- __ j(EQUAL, &type_arguments_instantiated, compiler::Assembler::kNearJump);
- __ Bind(&non_null_type_args);
- }
- // Lookup cache in stub before calling runtime.
- compiler->GenerateStubCall(source(), GetStub(), UntaggedPcDescriptors::kOther,
- locs());
- __ Bind(&type_arguments_instantiated);
-}
-
LocationSummary* AllocateUninitializedContextInstr::MakeLocationSummary(
Zone* zone,
bool opt) const {
diff --git a/runtime/vm/compiler/backend/il_x64.cc b/runtime/vm/compiler/backend/il_x64.cc
index 574395a..f755d79 100644
--- a/runtime/vm/compiler/backend/il_x64.cc
+++ b/runtime/vm/compiler/backend/il_x64.cc
@@ -3115,71 +3115,6 @@
__ Bind(&done);
}
-LocationSummary* InstantiateTypeArgumentsInstr::MakeLocationSummary(
- Zone* zone,
- bool opt) const {
- const intptr_t kNumInputs = 3;
- const intptr_t kNumTemps = 0;
- LocationSummary* locs = new (zone)
- LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kCall);
- locs->set_in(0, Location::RegisterLocation(
- InstantiationABI::kInstantiatorTypeArgumentsReg));
- locs->set_in(1, Location::RegisterLocation(
- InstantiationABI::kFunctionTypeArgumentsReg));
- locs->set_in(2, Location::RegisterLocation(
- InstantiationABI::kUninstantiatedTypeArgumentsReg));
- locs->set_out(
- 0, Location::RegisterLocation(InstantiationABI::kResultTypeArgumentsReg));
- return locs;
-}
-
-void InstantiateTypeArgumentsInstr::EmitNativeCode(
- FlowGraphCompiler* compiler) {
- // We should never try and instantiate a TAV known at compile time to be null,
- // so we can use a null value below for the dynamic case.
- ASSERT(!type_arguments()->BindsToConstant() ||
- !type_arguments()->BoundConstant().IsNull());
- const auto& type_args =
- type_arguments()->BindsToConstant()
- ? TypeArguments::Cast(type_arguments()->BoundConstant())
- : Object::null_type_arguments();
- const intptr_t len = type_args.Length();
- const bool can_function_type_args_be_null =
- function_type_arguments()->CanBe(Object::null_object());
-
- compiler::Label type_arguments_instantiated;
- if (type_args.IsNull()) {
- // Currently we only create dynamic InstantiateTypeArguments instructions
- // in cases where we know the type argument is uninstantiated at runtime,
- // so there are no extra checks needed to call the stub successfully.
- } else if (type_args.IsRawWhenInstantiatedFromRaw(len) &&
- can_function_type_args_be_null) {
- // If both the instantiator and function type arguments are null and if the
- // type argument vector instantiated from null becomes a vector of dynamic,
- // then use null as the type arguments.
- compiler::Label non_null_type_args;
- // 'instantiator_type_args_reg' is a TypeArguments object (or null).
- // 'function_type_args_reg' is a TypeArguments object (or null).
- const Register instantiator_type_args_reg = locs()->in(0).reg();
- const Register function_type_args_reg = locs()->in(1).reg();
- const Register result_reg = locs()->out(0).reg();
- ASSERT(result_reg != instantiator_type_args_reg &&
- result_reg != function_type_args_reg);
- __ LoadObject(result_reg, Object::null_object());
- __ cmpq(instantiator_type_args_reg, result_reg);
- if (!function_type_arguments()->BindsToConstant()) {
- __ j(NOT_EQUAL, &non_null_type_args, compiler::Assembler::kNearJump);
- __ cmpq(function_type_args_reg, result_reg);
- }
- __ j(EQUAL, &type_arguments_instantiated, compiler::Assembler::kNearJump);
- __ Bind(&non_null_type_args);
- }
- // Lookup cache in stub before calling runtime.
- compiler->GenerateStubCall(source(), GetStub(), UntaggedPcDescriptors::kOther,
- locs());
- __ Bind(&type_arguments_instantiated);
-}
-
LocationSummary* AllocateUninitializedContextInstr::MakeLocationSummary(
Zone* zone,
bool opt) const {
diff --git a/runtime/vm/compiler/ffi/native_type.cc b/runtime/vm/compiler/ffi/native_type.cc
index 19a2ace..cd4cbbb 100644
--- a/runtime/vm/compiler/ffi/native_type.cc
+++ b/runtime/vm/compiler/ffi/native_type.cc
@@ -399,12 +399,21 @@
.Equals(Symbols::FfiStructLayout()));
ASSERT(String::Handle(zone, library.url()).Equals(Symbols::DartFfi()));
const auto& struct_layout_fields = Array::Handle(zone, clazz.fields());
- ASSERT(struct_layout_fields.Length() == 1);
- const auto& field =
+ ASSERT(struct_layout_fields.Length() == 2);
+ const auto& types_field =
Field::Handle(zone, Field::RawCast(struct_layout_fields.At(0)));
- ASSERT(String::Handle(zone, field.name()).Equals(Symbols::FfiFieldTypes()));
+ ASSERT(String::Handle(zone, types_field.name())
+ .Equals(Symbols::FfiFieldTypes()));
const auto& field_types =
- Array::Handle(zone, Array::RawCast(struct_layout.GetField(field)));
+ Array::Handle(zone, Array::RawCast(struct_layout.GetField(types_field)));
+ const auto& packed_field =
+ Field::Handle(zone, Field::RawCast(struct_layout_fields.At(1)));
+ ASSERT(String::Handle(zone, packed_field.name())
+ .Equals(Symbols::FfiFieldPacking()));
+ const auto& packed_value = Integer::Handle(
+ zone, Integer::RawCast(struct_layout.GetField(packed_field)));
+ const intptr_t member_packing =
+ packed_value.IsNull() ? kMaxInt32 : packed_value.AsInt64Value();
auto& field_instance = Instance::Handle(zone);
auto& field_type = AbstractType::Handle(zone);
@@ -445,7 +454,8 @@
}
}
- return NativeCompoundType::FromNativeTypes(zone, field_native_types);
+ return NativeCompoundType::FromNativeTypes(zone, field_native_types,
+ member_packing);
}
#endif
@@ -736,23 +746,32 @@
}
#endif // !defined(DART_PRECOMPILED_RUNTIME)
-bool NativePrimitiveType::ContainsUnalignedMembers() const {
+bool NativePrimitiveType::ContainsUnalignedMembers(intptr_t offset) const {
+ return offset % AlignmentInBytesField() != 0;
+}
+
+bool NativeArrayType::ContainsUnalignedMembers(intptr_t offset) const {
+ const intptr_t element_size = element_type_.SizeInBytes();
+ // We're only checking the first two elements of the array.
+ //
+ // If the element size is divisible by the alignment of the largest type
+ // contained within the element type, the alignment of all elements is the
+ // same. If not, either the first or the second element is unaligned.
+ const intptr_t max_check = 2;
+ for (intptr_t i = 0; i < Utils::Minimum(length_, max_check); i++) {
+ const intptr_t element_offset = i * element_size;
+ if (element_type_.ContainsUnalignedMembers(offset + element_offset)) {
+ return true;
+ }
+ }
return false;
}
-bool NativeArrayType::ContainsUnalignedMembers() const {
- return element_type_.ContainsUnalignedMembers();
-}
-
-bool NativeCompoundType::ContainsUnalignedMembers() const {
+bool NativeCompoundType::ContainsUnalignedMembers(intptr_t offset) const {
for (intptr_t i = 0; i < members_.length(); i++) {
const auto& member = *members_.At(i);
const intptr_t member_offset = member_offsets_.At(i);
- const intptr_t member_alignment = member.AlignmentInBytesField();
- if (member_offset % member_alignment != 0) {
- return true;
- }
- if (member.ContainsUnalignedMembers()) {
+ if (member.ContainsUnalignedMembers(offset + member_offset)) {
return true;
}
}
diff --git a/runtime/vm/compiler/ffi/native_type.h b/runtime/vm/compiler/ffi/native_type.h
index 8fbcbc5..e7a156e 100644
--- a/runtime/vm/compiler/ffi/native_type.h
+++ b/runtime/vm/compiler/ffi/native_type.h
@@ -98,7 +98,7 @@
#endif // !defined(DART_PRECOMPILED_RUNTIME)
// True iff any members are misaligned recursively due to packing.
- virtual bool ContainsUnalignedMembers() const = 0;
+ virtual bool ContainsUnalignedMembers(intptr_t offset = 0) const = 0;
#if !defined(DART_PRECOMPILED_RUNTIME) && !defined(FFI_UNIT_TESTS)
// NativeTypes which are available as unboxed Representations.
@@ -188,7 +188,7 @@
virtual bool ContainsOnlyFloats(Range range) const;
#endif // !defined(DART_PRECOMPILED_RUNTIME)
- virtual bool ContainsUnalignedMembers() const;
+ virtual bool ContainsUnalignedMembers(intptr_t offset = 0) const;
#if !defined(DART_PRECOMPILED_RUNTIME) && !defined(FFI_UNIT_TESTS)
virtual bool IsExpressibleAsRepresentation() const;
@@ -239,7 +239,7 @@
virtual bool ContainsOnlyFloats(Range range) const;
#endif // !defined(DART_PRECOMPILED_RUNTIME)
- virtual bool ContainsUnalignedMembers() const;
+ virtual bool ContainsUnalignedMembers(intptr_t offset = 0) const;
virtual bool Equals(const NativeType& other) const;
@@ -298,7 +298,7 @@
intptr_t NumberOfWordSizeChunksNotOnlyFloat() const;
#endif // !defined(DART_PRECOMPILED_RUNTIME)
- virtual bool ContainsUnalignedMembers() const;
+ virtual bool ContainsUnalignedMembers(intptr_t offset = 0) const;
// Whether this type has only same-size floating point members.
//
diff --git a/runtime/vm/compiler/ffi/native_type_test.cc b/runtime/vm/compiler/ffi/native_type_test.cc
index d6d5ece..63cc7b4 100644
--- a/runtime/vm/compiler/ffi/native_type_test.cc
+++ b/runtime/vm/compiler/ffi/native_type_test.cc
@@ -214,6 +214,61 @@
EXPECT(struct_type.ContainsUnalignedMembers());
}
+UNIT_TEST_CASE_WITH_ZONE(NativeCompoundType_packed_array) {
+ const auto& uint8_type = *new (Z) NativePrimitiveType(kUint8);
+ const auto& uint16_type = *new (Z) NativePrimitiveType(kUint16);
+
+ auto& inner_members = *new (Z) NativeTypes(Z, 2);
+ inner_members.Add(&uint16_type);
+ inner_members.Add(&uint8_type);
+ const intptr_t packing = 1;
+ const auto& inner_struct_type =
+ NativeCompoundType::FromNativeTypes(Z, inner_members, packing);
+
+ EXPECT_EQ(3, inner_struct_type.SizeInBytes());
+ // Non-windows x64 considers this struct as all members aligned, even though
+ // its size is not a multiple of its individual member alignment.
+ EXPECT(!inner_struct_type.ContainsUnalignedMembers());
+
+ const auto& array_type = *new (Z) NativeArrayType(inner_struct_type, 2);
+
+ auto& members = *new (Z) NativeTypes(Z, 1);
+ members.Add(&array_type);
+ const auto& struct_type = NativeCompoundType::FromNativeTypes(Z, members);
+
+ EXPECT_EQ(6, struct_type.SizeInBytes());
+ // Non-windows x64 passes this as a struct with unaligned members, because
+ // the second element of the array contains unaligned members.
+ EXPECT(struct_type.ContainsUnalignedMembers());
+}
+
+UNIT_TEST_CASE_WITH_ZONE(NativeCompoundType_packed_nested) {
+ const auto& uint8_type = *new (Z) NativePrimitiveType(kUint8);
+ const auto& uint32_type = *new (Z) NativePrimitiveType(kUint32);
+
+ auto& inner_members = *new (Z) NativeTypes(Z, 2);
+ inner_members.Add(&uint32_type);
+ inner_members.Add(&uint8_type);
+ const intptr_t packing = 1;
+ const auto& inner_struct_type =
+ NativeCompoundType::FromNativeTypes(Z, inner_members, packing);
+
+ EXPECT_EQ(5, inner_struct_type.SizeInBytes());
+ // Non-windows x64 considers this struct as all members aligned, even though
+ // its size is not a multiple of its individual member alignment.
+ EXPECT(!inner_struct_type.ContainsUnalignedMembers());
+
+ auto& members = *new (Z) NativeTypes(Z, 2);
+ members.Add(&uint8_type);
+ members.Add(&inner_struct_type);
+ const auto& struct_type = NativeCompoundType::FromNativeTypes(Z, members);
+
+ EXPECT_EQ(6, struct_type.SizeInBytes());
+ // Non-windows x64 passes this as a struct with unaligned members, even
+ // though the nested struct itself has all its members aligned in isolation.
+ EXPECT(struct_type.ContainsUnalignedMembers());
+}
+
} // namespace ffi
} // namespace compiler
} // namespace dart
diff --git a/runtime/vm/compiler/ffi/recognized_method.cc b/runtime/vm/compiler/ffi/recognized_method.cc
index f13f041..6900a83 100644
--- a/runtime/vm/compiler/ffi/recognized_method.cc
+++ b/runtime/vm/compiler/ffi/recognized_method.cc
@@ -57,6 +57,31 @@
CLASS_LIST_FFI_NUMERIC(LOAD_STORE)
LOAD_STORE(Pointer)
#undef LOAD_STORE
+ case MethodRecognizer::kFfiLoadFloatUnaligned:
+ case MethodRecognizer::kFfiStoreFloatUnaligned:
+ return kFfiFloatCid;
+ case MethodRecognizer::kFfiLoadDoubleUnaligned:
+ case MethodRecognizer::kFfiStoreDoubleUnaligned:
+ return kFfiDoubleCid;
+ default:
+ UNREACHABLE();
+ }
+}
+
+AlignmentType RecognizedMethodAlignment(MethodRecognizer::Kind kind) {
+ switch (kind) {
+#define LOAD_STORE(type) \
+ case MethodRecognizer::kFfiLoad##type: \
+ case MethodRecognizer::kFfiStore##type: \
+ return kAlignedAccess;
+ CLASS_LIST_FFI_NUMERIC(LOAD_STORE)
+ LOAD_STORE(Pointer)
+#undef LOAD_STORE
+ case MethodRecognizer::kFfiLoadFloatUnaligned:
+ case MethodRecognizer::kFfiStoreFloatUnaligned:
+ case MethodRecognizer::kFfiLoadDoubleUnaligned:
+ case MethodRecognizer::kFfiStoreDoubleUnaligned:
+ return kUnalignedAccess;
default:
UNREACHABLE();
}
diff --git a/runtime/vm/compiler/ffi/recognized_method.h b/runtime/vm/compiler/ffi/recognized_method.h
index 8cbfca8..993306e 100644
--- a/runtime/vm/compiler/ffi/recognized_method.h
+++ b/runtime/vm/compiler/ffi/recognized_method.h
@@ -11,6 +11,7 @@
#include <platform/globals.h>
+#include "vm/compiler/backend/il.h"
#include "vm/compiler/method_recognizer.h"
namespace dart {
@@ -25,6 +26,8 @@
// Returns the kFFi<type>Cid for the recognized load/store method [kind].
classid_t RecognizedMethodTypeArgCid(MethodRecognizer::Kind kind);
+AlignmentType RecognizedMethodAlignment(MethodRecognizer::Kind kind);
+
} // namespace ffi
} // namespace compiler
diff --git a/runtime/vm/compiler/frontend/base_flow_graph_builder.cc b/runtime/vm/compiler/frontend/base_flow_graph_builder.cc
index a106f1e..7527b84 100644
--- a/runtime/vm/compiler/frontend/base_flow_graph_builder.cc
+++ b/runtime/vm/compiler/frontend/base_flow_graph_builder.cc
@@ -370,7 +370,8 @@
Fragment BaseFlowGraphBuilder::LoadIndexed(classid_t class_id,
intptr_t index_scale,
- bool index_unboxed) {
+ bool index_unboxed,
+ AlignmentType alignment) {
Value* index = Pop();
// A C pointer if index_unboxed, otherwise a boxed Dart value.
Value* array = Pop();
@@ -379,7 +380,7 @@
// all cases.
LoadIndexedInstr* instr = new (Z)
LoadIndexedInstr(array, index, index_unboxed, index_scale, class_id,
- kAlignedAccess, DeoptId::kNone, InstructionSource());
+ alignment, DeoptId::kNone, InstructionSource());
Push(instr);
return Fragment(instr);
}
@@ -636,10 +637,8 @@
Fragment BaseFlowGraphBuilder::StoreIndexedTypedData(classid_t class_id,
intptr_t index_scale,
- bool index_unboxed) {
- // We use C behavior when dereferencing pointers, we assume alignment.
- const AlignmentType alignment = kAlignedAccess;
-
+ bool index_unboxed,
+ AlignmentType alignment) {
Value* value = Pop();
Value* index = Pop();
Value* c_pointer = Pop();
diff --git a/runtime/vm/compiler/frontend/base_flow_graph_builder.h b/runtime/vm/compiler/frontend/base_flow_graph_builder.h
index 50e198a..e55d218 100644
--- a/runtime/vm/compiler/frontend/base_flow_graph_builder.h
+++ b/runtime/vm/compiler/frontend/base_flow_graph_builder.h
@@ -167,7 +167,8 @@
// Pass true for index_unboxed if indexing into external typed data.
Fragment LoadIndexed(classid_t class_id,
intptr_t index_scale = compiler::target::kWordSize,
- bool index_unboxed = false);
+ bool index_unboxed = false,
+ AlignmentType alignment = kAlignedAccess);
Fragment LoadUntagged(intptr_t offset);
Fragment StoreUntagged(intptr_t offset);
@@ -213,7 +214,8 @@
// Takes a [class_id] valid for StoreIndexed.
Fragment StoreIndexedTypedData(classid_t class_id,
intptr_t index_scale,
- bool index_unboxed);
+ bool index_unboxed,
+ AlignmentType alignment = kAlignedAccess);
// Sign-extends kUnboxedInt32 and zero-extends kUnboxedUint32.
Fragment Box(Representation from);
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index 7437768..3d38bf7 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -831,7 +831,9 @@
case MethodRecognizer::kFfiLoadUint64:
case MethodRecognizer::kFfiLoadIntPtr:
case MethodRecognizer::kFfiLoadFloat:
+ case MethodRecognizer::kFfiLoadFloatUnaligned:
case MethodRecognizer::kFfiLoadDouble:
+ case MethodRecognizer::kFfiLoadDoubleUnaligned:
case MethodRecognizer::kFfiLoadPointer:
case MethodRecognizer::kFfiStoreInt8:
case MethodRecognizer::kFfiStoreInt16:
@@ -843,7 +845,9 @@
case MethodRecognizer::kFfiStoreUint64:
case MethodRecognizer::kFfiStoreIntPtr:
case MethodRecognizer::kFfiStoreFloat:
+ case MethodRecognizer::kFfiStoreFloatUnaligned:
case MethodRecognizer::kFfiStoreDouble:
+ case MethodRecognizer::kFfiStoreDoubleUnaligned:
case MethodRecognizer::kFfiStorePointer:
case MethodRecognizer::kFfiFromAddress:
case MethodRecognizer::kFfiGetAddress:
@@ -1288,10 +1292,14 @@
case MethodRecognizer::kFfiLoadUint64:
case MethodRecognizer::kFfiLoadIntPtr:
case MethodRecognizer::kFfiLoadFloat:
+ case MethodRecognizer::kFfiLoadFloatUnaligned:
case MethodRecognizer::kFfiLoadDouble:
+ case MethodRecognizer::kFfiLoadDoubleUnaligned:
case MethodRecognizer::kFfiLoadPointer: {
const classid_t ffi_type_arg_cid =
compiler::ffi::RecognizedMethodTypeArgCid(kind);
+ const AlignmentType alignment =
+ compiler::ffi::RecognizedMethodAlignment(kind);
const classid_t typed_data_cid =
compiler::ffi::ElementTypedDataCid(ffi_type_arg_cid);
const auto& native_rep = compiler::ffi::NativeType::FromTypedDataClassId(
@@ -1314,10 +1322,13 @@
body += LoadLocal(arg_offset_not_null);
body += UnboxTruncate(kUnboxedFfiIntPtr);
body += LoadIndexed(typed_data_cid, /*index_scale=*/1,
- /*index_unboxed=*/true);
+ /*index_unboxed=*/true, alignment);
if (kind == MethodRecognizer::kFfiLoadFloat ||
- kind == MethodRecognizer::kFfiLoadDouble) {
- if (kind == MethodRecognizer::kFfiLoadFloat) {
+ kind == MethodRecognizer::kFfiLoadFloatUnaligned ||
+ kind == MethodRecognizer::kFfiLoadDouble ||
+ kind == MethodRecognizer::kFfiLoadDoubleUnaligned) {
+ if (kind == MethodRecognizer::kFfiLoadFloat ||
+ kind == MethodRecognizer::kFfiLoadFloatUnaligned) {
body += FloatToDouble();
}
body += Box(kUnboxedDouble);
@@ -1369,10 +1380,14 @@
case MethodRecognizer::kFfiStoreUint64:
case MethodRecognizer::kFfiStoreIntPtr:
case MethodRecognizer::kFfiStoreFloat:
+ case MethodRecognizer::kFfiStoreFloatUnaligned:
case MethodRecognizer::kFfiStoreDouble:
+ case MethodRecognizer::kFfiStoreDoubleUnaligned:
case MethodRecognizer::kFfiStorePointer: {
const classid_t ffi_type_arg_cid =
compiler::ffi::RecognizedMethodTypeArgCid(kind);
+ const AlignmentType alignment =
+ compiler::ffi::RecognizedMethodAlignment(kind);
const classid_t typed_data_cid =
compiler::ffi::ElementTypedDataCid(ffi_type_arg_cid);
const auto& native_rep = compiler::ffi::NativeType::FromTypedDataClassId(
@@ -1439,16 +1454,19 @@
body += LoadUntagged(compiler::target::Pointer::data_field_offset());
body += ConvertUntaggedToUnboxed(kUnboxedFfiIntPtr);
} else if (kind == MethodRecognizer::kFfiStoreFloat ||
- kind == MethodRecognizer::kFfiStoreDouble) {
+ kind == MethodRecognizer::kFfiStoreFloatUnaligned ||
+ kind == MethodRecognizer::kFfiStoreDouble ||
+ kind == MethodRecognizer::kFfiStoreDoubleUnaligned) {
body += UnboxTruncate(kUnboxedDouble);
- if (kind == MethodRecognizer::kFfiStoreFloat) {
+ if (kind == MethodRecognizer::kFfiStoreFloat ||
+ kind == MethodRecognizer::kFfiStoreFloatUnaligned) {
body += DoubleToFloat();
}
} else {
body += UnboxTruncate(native_rep.AsRepresentationOverApprox(zone_));
}
body += StoreIndexedTypedData(typed_data_cid, /*index_scale=*/1,
- /*index_unboxed=*/true);
+ /*index_unboxed=*/true, alignment);
body += Drop(); // Drop [arg_value].
body += Drop(); // Drop [arg_offset].
body += NullConstant();
diff --git a/runtime/vm/compiler/recognized_methods_list.h b/runtime/vm/compiler/recognized_methods_list.h
index c53fc34..e9b014d 100644
--- a/runtime/vm/compiler/recognized_methods_list.h
+++ b/runtime/vm/compiler/recognized_methods_list.h
@@ -176,7 +176,9 @@
V(::, _loadUint64, FfiLoadUint64, 0x0505fdcc) \
V(::, _loadIntPtr, FfiLoadIntPtr, 0xebd9b43e) \
V(::, _loadFloat, FfiLoadFloat, 0xf8d9845d) \
+ V(::, _loadFloatUnaligned, FfiLoadFloatUnaligned, 0xc8c8dfff) \
V(::, _loadDouble, FfiLoadDouble, 0xf70cc619) \
+ V(::, _loadDoubleUnaligned, FfiLoadDoubleUnaligned, 0xc99ebd39) \
V(::, _loadPointer, FfiLoadPointer, 0x4e79d0fc) \
V(::, _storeInt8, FfiStoreInt8, 0xdf50af0c) \
V(::, _storeInt16, FfiStoreInt16, 0xd84df332) \
@@ -188,7 +190,9 @@
V(::, _storeUint64, FfiStoreUint64, 0xe2d93239) \
V(::, _storeIntPtr, FfiStoreIntPtr, 0x080266a8) \
V(::, _storeFloat, FfiStoreFloat, 0x6484f07e) \
+ V(::, _storeFloatUnaligned, FfiStoreFloatUnaligned, 0x600a9203) \
V(::, _storeDouble, FfiStoreDouble, 0x42998c64) \
+ V(::, _storeDoubleUnaligned, FfiStoreDoubleUnaligned, 0x3dced75b) \
V(::, _storePointer, FfiStorePointer, 0xea6b7751) \
V(::, _fromAddress, FfiFromAddress, 0xfd8cb1cc) \
V(Pointer, get:address, FfiGetAddress, 0x7cde87be) \
diff --git a/runtime/vm/compiler/stub_code_compiler.cc b/runtime/vm/compiler/stub_code_compiler.cc
index 03356ae..b41e1f9 100644
--- a/runtime/vm/compiler/stub_code_compiler.cc
+++ b/runtime/vm/compiler/stub_code_compiler.cc
@@ -376,12 +376,14 @@
/*null_safety=*/true);
}
-// Version of Instance::NullIsAssignableTo() used when the destination type is
-// not known at compile time. Must be kept in sync.
+// Version of Instance::NullIsAssignableTo(other, inst_tav, fun_tav) used when
+// the destination type was not known at compile time. Must be kept in sync.
//
// Inputs:
// - TypeTestABI::kInstanceReg: Object to check for assignability.
// - TypeTestABI::kDstTypeReg: Destination type.
+// - TypeTestABI::kInstantiatorTypeArgumentsReg: Instantiator TAV.
+// - TypeTestABI::kFunctionTypeArgumentsReg: Function TAV.
//
// Non-preserved non-output scratch registers:
// - TypeTestABI::kScratchReg (only on non-IA32 architectures)
@@ -396,85 +398,120 @@
// The only case where the original value of kSubtypeTestCacheReg is needed
// after the stub call is on IA32, where it's currently preserved on the stack
// before calling the stub (as it's also CODE_REG on that architecture), so we
- // both use it as a scratch and clobber it for the return value.
- const Register scratch1_reg = TypeTestABI::kSubtypeTestCacheReg;
+ // both use it as a scratch to hold the current type to inspect and also
+ // clobber it for the return value.
+ const Register kCurrentTypeReg = TypeTestABI::kSubtypeTestCacheReg;
// We reuse the first scratch register as the output register because we're
- // always guaranteed to have a type in it (starting with kDstType), and all
- // non-Smi ObjectPtrs are non-zero values.
- const Register output_reg = scratch1_reg;
+ // always guaranteed to have a type in it (starting with the contents of
+ // kDstTypeReg), and all non-Smi ObjectPtrs are non-zero values.
+ const Register kOutputReg = kCurrentTypeReg;
#if defined(TARGET_ARCH_IA32)
// The remaining scratch registers are preserved and restored before exit on
// IA32. Because we have few registers to choose from (which are all used in
// TypeTestABI), use specific TestTypeABI registers.
- const Register scratch2_reg = TypeTestABI::kFunctionTypeArgumentsReg;
+ const Register kScratchReg = TypeTestABI::kFunctionTypeArgumentsReg;
// Preserve non-output scratch registers.
- __ PushRegister(scratch2_reg);
+ __ PushRegister(kScratchReg);
#else
- const Register scratch2_reg = TypeTestABI::kScratchReg;
+ const Register kScratchReg = TypeTestABI::kScratchReg;
#endif
- static_assert(scratch1_reg != scratch2_reg,
+ static_assert(kCurrentTypeReg != kScratchReg,
"code assumes distinct scratch registers");
compiler::Label is_assignable, done;
// Initialize the first scratch register (and thus the output register) with
// the destination type. We do this before the check to ensure the output
// register has a non-zero value if !null_safety and kInstanceReg is not null.
- __ MoveRegister(scratch1_reg, TypeTestABI::kDstTypeReg);
+ __ MoveRegister(kCurrentTypeReg, TypeTestABI::kDstTypeReg);
__ CompareObject(TypeTestABI::kInstanceReg, Object::null_object());
if (null_safety) {
compiler::Label check_null_assignable;
// Skip checking the type if not null.
- __ BranchIf(NOT_EQUAL, &done, compiler::Assembler::kNearJump);
+ __ BranchIf(NOT_EQUAL, &done);
__ Bind(&check_null_assignable);
// scratch1_reg: Current type to check.
- EnsureIsTypeOrFunctionTypeOrTypeParameter(assembler, scratch1_reg,
- scratch2_reg);
+ EnsureIsTypeOrFunctionTypeOrTypeParameter(assembler, kCurrentTypeReg,
+ kScratchReg);
compiler::Label is_not_type;
- __ CompareClassId(scratch1_reg, kTypeCid, scratch2_reg);
+ __ CompareClassId(kCurrentTypeReg, kTypeCid, kScratchReg);
__ BranchIf(NOT_EQUAL, &is_not_type, compiler::Assembler::kNearJump);
__ CompareTypeNullabilityWith(
- scratch1_reg, static_cast<int8_t>(Nullability::kNonNullable));
- __ BranchIf(NOT_EQUAL, &is_assignable, compiler::Assembler::kNearJump);
+ kCurrentTypeReg, static_cast<int8_t>(Nullability::kNonNullable));
+ __ BranchIf(NOT_EQUAL, &is_assignable);
// FutureOr is a special case because it may have the non-nullable bit set,
// but FutureOr<T> functions as the union of T and Future<T>, so it must be
// unwrapped to see if T is nullable.
__ LoadField(
- scratch2_reg,
- compiler::FieldAddress(scratch1_reg,
+ kScratchReg,
+ compiler::FieldAddress(kCurrentTypeReg,
compiler::target::Type::type_class_id_offset()));
- __ SmiUntag(scratch2_reg);
- __ CompareImmediate(scratch2_reg, kFutureOrCid);
- __ BranchIf(NOT_EQUAL, &done, compiler::Assembler::kNearJump);
- __ LoadField(scratch2_reg,
- compiler::FieldAddress(
- scratch1_reg, compiler::target::Type::arguments_offset()));
- __ CompareObject(scratch2_reg, Object::null_object());
+ __ SmiUntag(kScratchReg);
+ __ CompareImmediate(kScratchReg, kFutureOrCid);
+ __ BranchIf(NOT_EQUAL, &done);
+ __ LoadField(kScratchReg, compiler::FieldAddress(
+ kCurrentTypeReg,
+ compiler::target::Type::arguments_offset()));
+ __ CompareObject(kScratchReg, Object::null_object());
// If the arguments are null, then unwrapping gives the dynamic type,
// which can take null.
- __ BranchIf(EQUAL, &is_assignable, compiler::Assembler::kNearJump);
+ __ BranchIf(EQUAL, &is_assignable);
__ LoadField(
- scratch1_reg,
+ kCurrentTypeReg,
compiler::FieldAddress(
- scratch2_reg, compiler::target::TypeArguments::type_at_offset(0)));
+ kScratchReg, compiler::target::TypeArguments::type_at_offset(0)));
__ Jump(&check_null_assignable, compiler::Assembler::kNearJump);
__ Bind(&is_not_type);
- // Null is assignable to a type parameter only if it is nullable.
+ // Null is assignable to a type parameter only if it is nullable or if the
+ // instantiation is nullable.
__ LoadFieldFromOffset(
- scratch2_reg, scratch1_reg,
+ kScratchReg, kCurrentTypeReg,
compiler::target::TypeParameter::nullability_offset(), kByte);
- __ CompareImmediate(scratch2_reg,
+ __ CompareImmediate(kScratchReg,
static_cast<int8_t>(Nullability::kNonNullable));
- __ BranchIf(EQUAL, &done, compiler::Assembler::kNearJump);
+ __ BranchIf(NOT_EQUAL, &is_assignable);
+
+ // Don't set kScratchReg in here as on IA32, that's the function TAV reg.
+ auto handle_case = [&](Register tav) {
+ // We can reuse kCurrentTypeReg to hold the index because we no longer
+ // need the type parameter afterwards.
+ auto const kIndexReg = kCurrentTypeReg;
+ // If the TAV is null, resolving gives the (nullable) dynamic type.
+ __ CompareObject(tav, NullObject());
+ __ BranchIf(EQUAL, &is_assignable, Assembler::kNearJump);
+ // Resolve the type parameter to its instantiated type and loop.
+ __ LoadFieldFromOffset(kIndexReg, kCurrentTypeReg,
+ target::TypeParameter::index_offset(), kTwoBytes);
+ __ LoadIndexedPayload(kCurrentTypeReg, tav,
+ target::TypeArguments::types_offset(), kIndexReg,
+ TIMES_WORD_SIZE);
+ __ Jump(&check_null_assignable);
+ };
+
+ Label function_type_param;
+ __ LoadFieldFromOffset(
+ kScratchReg, kCurrentTypeReg,
+ target::TypeParameter::parameterized_class_id_offset(),
+ kUnsignedTwoBytes);
+ __ CompareImmediate(kScratchReg, kFunctionCid);
+ __ BranchIf(EQUAL, &function_type_param, Assembler::kNearJump);
+ handle_case(TypeTestABI::kInstantiatorTypeArgumentsReg);
+ __ Bind(&function_type_param);
+#if defined(TARGET_ARCH_IA32)
+ // Function TAV is on top of stack because we're using that register as
+ // kScratchReg.
+ __ LoadFromStack(TypeTestABI::kFunctionTypeArgumentsReg, 0);
+#endif
+ handle_case(TypeTestABI::kFunctionTypeArgumentsReg);
} else {
// Null in non-null-safe mode is always assignable.
__ BranchIf(NOT_EQUAL, &done, compiler::Assembler::kNearJump);
}
__ Bind(&is_assignable);
- __ LoadImmediate(output_reg, 0);
+ __ LoadImmediate(kOutputReg, 0);
__ Bind(&done);
#if defined(TARGET_ARCH_IA32)
// Restore preserved scratch registers.
- __ PopRegister(scratch2_reg);
+ __ PopRegister(kScratchReg);
#endif
__ Ret();
}
diff --git a/runtime/vm/image_snapshot.cc b/runtime/vm/image_snapshot.cc
index 6147404..25ace1d 100644
--- a/runtime/vm/image_snapshot.cc
+++ b/runtime/vm/image_snapshot.cc
@@ -557,7 +557,7 @@
}
}
-static UNLESS_DEBUG(constexpr) const uword kReadOnlyGCBits =
+static constexpr uword kReadOnlyGCBits =
UntaggedObject::OldBit::encode(true) |
UntaggedObject::OldAndNotMarkedBit::encode(false) |
UntaggedObject::OldAndNotRememberedBit::encode(true) |
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index c47b47e..a0af589 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -8241,7 +8241,8 @@
// If the argument type is the top type, no need to check.
if (type.IsTopTypeForSubtyping()) return true;
if (argument.IsNull()) {
- return Instance::NullIsAssignableTo(type);
+ return Instance::NullIsAssignableTo(type, instantiator_type_args,
+ function_type_args);
}
return argument.IsAssignableTo(type, instantiator_type_args,
function_type_args);
@@ -8304,6 +8305,11 @@
if (!check_argument(argument, type, instantiator_type_arguments,
function_type_arguments)) {
auto& name = String::Handle(zone, ParameterNameAt(param_index));
+ if (!type.IsInstantiated()) {
+ type = type.InstantiateFrom(instantiator_type_arguments,
+ function_type_arguments, kAllFree,
+ Heap::kNew);
+ }
return ThrowTypeError(token_pos(), argument, type, name);
}
break;
@@ -18821,9 +18827,28 @@
return NullIsAssignableTo(
AbstractType::Handle(thread->zone(), other.UnwrapFutureOr()));
}
+ // Since the TAVs are not available, for non-nullable type parameters
+ // this returns a conservative approximation of "not assignable" .
return false;
}
+// Must be kept in sync with GenerateNullIsAssignableToType in
+// stub_code_compiler.cc if any changes are made.
+bool Instance::NullIsAssignableTo(
+ const AbstractType& other,
+ const TypeArguments& other_instantiator_type_arguments,
+ const TypeArguments& other_function_type_arguments) {
+ // Do checks that don't require instantiation first.
+ if (NullIsAssignableTo(other)) return true;
+ if (!other.IsTypeParameter()) return false;
+ const auto& type = AbstractType::Handle(other.InstantiateFrom(
+ other_instantiator_type_arguments, other_function_type_arguments,
+ kAllFree, Heap::kNew));
+ // At runtime, uses of TypeRef should not occur.
+ ASSERT(!type.IsTypeRef());
+ return NullIsAssignableTo(type);
+}
+
bool Instance::RuntimeTypeIsSubtypeOf(
const AbstractType& other,
const TypeArguments& other_instantiator_type_arguments,
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index d603fdc..0708f3d 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -7111,9 +7111,18 @@
// Return true if the null instance can be assigned to a variable of [other]
// type. Return false if null cannot be assigned or we cannot tell (if
- // [other] is a type parameter in NNBD strong mode).
+ // [other] is a type parameter in NNBD strong mode). Only used for checks at
+ // compile time.
static bool NullIsAssignableTo(const AbstractType& other);
+ // Return true if the null instance can be assigned to a variable of [other]
+ // type. Return false if null cannot be assigned. Used for checks at runtime,
+ // when the instantiator and function type argument vectors are available.
+ static bool NullIsAssignableTo(
+ const AbstractType& other,
+ const TypeArguments& other_instantiator_type_arguments,
+ const TypeArguments& other_function_type_arguments);
+
bool IsValidNativeIndex(int index) const {
return ((index >= 0) && (index < clazz()->untag()->num_native_fields_));
}
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 79fa9df..a4f0b8a 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -153,7 +153,7 @@
static constexpr intptr_t kMaxSizeTag =
kMaxSizeTagInUnitsOfAlignment * kObjectAlignment;
- static UNLESS_DEBUG(constexpr) uword encode(intptr_t size) {
+ static constexpr uword encode(intptr_t size) {
return SizeBits::encode(SizeToTagValue(size));
}
@@ -161,11 +161,11 @@
return TagValueToSize(SizeBits::decode(tag));
}
- static UNLESS_DEBUG(constexpr) uword update(intptr_t size, uword tag) {
+ static constexpr uword update(intptr_t size, uword tag) {
return SizeBits::update(SizeToTagValue(size), tag);
}
- static UNLESS_DEBUG(constexpr) bool SizeFits(intptr_t size) {
+ static constexpr bool SizeFits(intptr_t size) {
DEBUG_ASSERT(Utils::IsAligned(size, kObjectAlignment));
return (size <= kMaxSizeTag);
}
@@ -175,8 +175,8 @@
class SizeBits
: public BitField<uword, intptr_t, kSizeTagPos, kSizeTagSize> {};
- static UNLESS_DEBUG(constexpr) intptr_t SizeToTagValue(intptr_t size) {
- DEBUG_ASSERT(Utils::IsAligned(size, kObjectAlignment));
+ static constexpr intptr_t SizeToTagValue(intptr_t size) {
+ assert(Utils::IsAligned(size, kObjectAlignment));
return !SizeFits(size) ? 0 : (size >> kObjectAlignmentLog2);
}
static constexpr intptr_t TagValueToSize(intptr_t value) {
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index ff0ad5e..6688a92 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -125,6 +125,7 @@
V(FfiDouble, "Double") \
V(FfiDynamicLibrary, "DynamicLibrary") \
V(FfiElementType, "elementType") \
+ V(FfiFieldPacking, "packing") \
V(FfiFieldTypes, "fieldTypes") \
V(FfiFloat, "Float") \
V(FfiInt16, "Int16") \
diff --git a/sdk/BUILD.gn b/sdk/BUILD.gn
index b6091dc..aad3f5a 100644
--- a/sdk/BUILD.gn
+++ b/sdk/BUILD.gn
@@ -115,6 +115,10 @@
# Snapshots that go under bin/snapshots
_platform_sdk_snapshots = [
[
+ "analysis_server",
+ "../utils/analysis_server",
+ ],
+ [
"dartanalyzer",
"../utils/dartanalyzer:generate_dartanalyzer_snapshot",
],
@@ -151,12 +155,6 @@
"../utils/kernel-service:kernel-service_snapshot",
] ]
}
-if (dart_target_arch != "arm") {
- _platform_sdk_snapshots += [ [
- "analysis_server",
- "../utils/analysis_server",
- ] ]
-}
_full_sdk_snapshots = _platform_sdk_snapshots + [
[
diff --git a/sdk/lib/_internal/vm/lib/ffi_patch.dart b/sdk/lib/_internal/vm/lib/ffi_patch.dart
index df878fa..75a6e51 100644
--- a/sdk/lib/_internal/vm/lib/ffi_patch.dart
+++ b/sdk/lib/_internal/vm/lib/ffi_patch.dart
@@ -260,6 +260,14 @@
native "Ffi_loadDouble";
@pragma("vm:recognized", "other")
+double _loadFloatUnaligned(Object typedDataBase, int offsetInBytes)
+ native "Ffi_loadFloatUnaligned";
+
+@pragma("vm:recognized", "other")
+double _loadDoubleUnaligned(Object typedDataBase, int offsetInBytes)
+ native "Ffi_loadDoubleUnaligned";
+
+@pragma("vm:recognized", "other")
Pointer<S> _loadPointer<S extends NativeType>(
Object typedDataBase, int offsetInBytes) native "Ffi_loadPointer";
@@ -308,6 +316,14 @@
native "Ffi_storeDouble";
@pragma("vm:recognized", "other")
+void _storeFloatUnaligned(Object typedDataBase, int offsetInBytes, double value)
+ native "Ffi_storeFloatUnaligned";
+
+@pragma("vm:recognized", "other")
+void _storeDoubleUnaligned(Object typedDataBase, int offsetInBytes,
+ double value) native "Ffi_storeDoubleUnaligned";
+
+@pragma("vm:recognized", "other")
void _storePointer<S extends NativeType>(Object typedDataBase,
int offsetInBytes, Pointer<S> value) native "Ffi_storePointer";
diff --git a/sdk/lib/_internal/vm/lib/ffi_struct_patch.dart b/sdk/lib/_internal/vm/lib/ffi_struct_patch.dart
index 3391e88..1dd4726 100644
--- a/sdk/lib/_internal/vm/lib/ffi_struct_patch.dart
+++ b/sdk/lib/_internal/vm/lib/ffi_struct_patch.dart
@@ -16,7 +16,10 @@
@pragma("vm:entry-point")
final List<Object> fieldTypes;
- const _FfiStructLayout(this.fieldTypes);
+ @pragma("vm:entry-point")
+ final int? packing;
+
+ const _FfiStructLayout(this.fieldTypes, this.packing);
}
@pragma("vm:entry-point")
diff --git a/sdk/lib/ffi/struct.dart b/sdk/lib/ffi/struct.dart
index 5d32708..ebe326a 100644
--- a/sdk/lib/ffi/struct.dart
+++ b/sdk/lib/ffi/struct.dart
@@ -58,3 +58,13 @@
Struct._fromPointer(this._addressOf);
}
+
+/// Annotation to specify on `Struct` subtypes to indicate that its members
+/// need to be packed.
+///
+/// Valid values for [memberAlignment] are 1, 2, 4, 8, and 16.
+class Packed {
+ final int memberAlignment;
+
+ const Packed(this.memberAlignment);
+}
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 4cdd74b..09648be 100644
--- a/tests/ffi/function_callbacks_structs_by_value_generated_test.dart
+++ b/tests/ffi/function_callbacks_structs_by_value_generated_test.dart
@@ -285,6 +285,42 @@
passUint8Struct4BytesInlineArrayMultiDimensionalIn, 0),
passUint8Struct4BytesInlineArrayMultiDimensionalInAfterCallback),
CallbackTest.withCheck(
+ "PassStruct3BytesPackedIntx10",
+ Pointer.fromFunction<PassStruct3BytesPackedIntx10Type>(
+ passStruct3BytesPackedIntx10, 0),
+ passStruct3BytesPackedIntx10AfterCallback),
+ CallbackTest.withCheck(
+ "PassStruct8BytesPackedIntx10",
+ Pointer.fromFunction<PassStruct8BytesPackedIntx10Type>(
+ passStruct8BytesPackedIntx10, 0),
+ passStruct8BytesPackedIntx10AfterCallback),
+ CallbackTest.withCheck(
+ "PassStruct9BytesPackedMixedx10DoubleInt32",
+ Pointer.fromFunction<PassStruct9BytesPackedMixedx10DoubleInt32Type>(
+ passStruct9BytesPackedMixedx10DoubleInt32, 0.0),
+ passStruct9BytesPackedMixedx10DoubleInt32AfterCallback),
+ CallbackTest.withCheck(
+ "PassStruct5BytesPackedMixed",
+ Pointer.fromFunction<PassStruct5BytesPackedMixedType>(
+ passStruct5BytesPackedMixed, 0.0),
+ passStruct5BytesPackedMixedAfterCallback),
+ CallbackTest.withCheck(
+ "PassStructNestedAlignmentStruct5BytesPackedMixed",
+ Pointer.fromFunction<
+ PassStructNestedAlignmentStruct5BytesPackedMixedType>(
+ passStructNestedAlignmentStruct5BytesPackedMixed, 0.0),
+ passStructNestedAlignmentStruct5BytesPackedMixedAfterCallback),
+ CallbackTest.withCheck(
+ "PassStruct6BytesInlineArrayInt",
+ Pointer.fromFunction<PassStruct6BytesInlineArrayIntType>(
+ passStruct6BytesInlineArrayInt, 0.0),
+ passStruct6BytesInlineArrayIntAfterCallback),
+ CallbackTest.withCheck(
+ "PassStruct15BytesInlineArrayMixed",
+ Pointer.fromFunction<PassStruct15BytesInlineArrayMixedType>(
+ passStruct15BytesInlineArrayMixed, 0.0),
+ passStruct15BytesInlineArrayMixedAfterCallback),
+ CallbackTest.withCheck(
"ReturnStruct1ByteInt",
Pointer.fromFunction<ReturnStruct1ByteIntType>(returnStruct1ByteInt),
returnStruct1ByteIntAfterCallback),
@@ -392,6 +428,21 @@
returnStruct1024BytesHomogeneousUint64),
returnStruct1024BytesHomogeneousUint64AfterCallback),
CallbackTest.withCheck(
+ "ReturnStruct3BytesPackedInt",
+ Pointer.fromFunction<ReturnStruct3BytesPackedIntType>(
+ returnStruct3BytesPackedInt),
+ returnStruct3BytesPackedIntAfterCallback),
+ CallbackTest.withCheck(
+ "ReturnStruct8BytesPackedInt",
+ Pointer.fromFunction<ReturnStruct8BytesPackedIntType>(
+ returnStruct8BytesPackedInt),
+ returnStruct8BytesPackedIntAfterCallback),
+ CallbackTest.withCheck(
+ "ReturnStruct9BytesPackedMixed",
+ Pointer.fromFunction<ReturnStruct9BytesPackedMixedType>(
+ returnStruct9BytesPackedMixed),
+ returnStruct9BytesPackedMixedAfterCallback),
+ CallbackTest.withCheck(
"ReturnStructArgumentStruct1ByteInt",
Pointer.fromFunction<ReturnStructArgumentStruct1ByteIntType>(
returnStructArgumentStruct1ByteInt),
@@ -6210,6 +6261,585 @@
Expect.equals(5, result);
}
+typedef PassStruct3BytesPackedIntx10Type = Int64 Function(
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt);
+
+// Global variables to be able to test inputs after callback returned.
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a0 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a1 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a2 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a3 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a4 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a5 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a6 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a7 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a8 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a9 = Struct3BytesPackedInt();
+
+// Result variable also global, so we can delete it after the callback.
+int passStruct3BytesPackedIntx10Result = 0;
+
+int passStruct3BytesPackedIntx10CalculateResult() {
+ int result = 0;
+
+ result += passStruct3BytesPackedIntx10_a0.a0;
+ result += passStruct3BytesPackedIntx10_a0.a1;
+ result += passStruct3BytesPackedIntx10_a1.a0;
+ result += passStruct3BytesPackedIntx10_a1.a1;
+ result += passStruct3BytesPackedIntx10_a2.a0;
+ result += passStruct3BytesPackedIntx10_a2.a1;
+ result += passStruct3BytesPackedIntx10_a3.a0;
+ result += passStruct3BytesPackedIntx10_a3.a1;
+ result += passStruct3BytesPackedIntx10_a4.a0;
+ result += passStruct3BytesPackedIntx10_a4.a1;
+ result += passStruct3BytesPackedIntx10_a5.a0;
+ result += passStruct3BytesPackedIntx10_a5.a1;
+ result += passStruct3BytesPackedIntx10_a6.a0;
+ result += passStruct3BytesPackedIntx10_a6.a1;
+ result += passStruct3BytesPackedIntx10_a7.a0;
+ result += passStruct3BytesPackedIntx10_a7.a1;
+ result += passStruct3BytesPackedIntx10_a8.a0;
+ result += passStruct3BytesPackedIntx10_a8.a1;
+ result += passStruct3BytesPackedIntx10_a9.a0;
+ result += passStruct3BytesPackedIntx10_a9.a1;
+
+ passStruct3BytesPackedIntx10Result = result;
+
+ return result;
+}
+
+/// Small struct with mis-aligned member.
+int passStruct3BytesPackedIntx10(
+ Struct3BytesPackedInt a0,
+ Struct3BytesPackedInt a1,
+ Struct3BytesPackedInt a2,
+ Struct3BytesPackedInt a3,
+ Struct3BytesPackedInt a4,
+ Struct3BytesPackedInt a5,
+ Struct3BytesPackedInt a6,
+ Struct3BytesPackedInt a7,
+ Struct3BytesPackedInt a8,
+ Struct3BytesPackedInt a9) {
+ print(
+ "passStruct3BytesPackedIntx10(${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 == 42 || a0.a0 == 84) {
+ print("throwing!");
+ throw Exception("PassStruct3BytesPackedIntx10 throwing on purpose!");
+ }
+
+ passStruct3BytesPackedIntx10_a0 = a0;
+ passStruct3BytesPackedIntx10_a1 = a1;
+ passStruct3BytesPackedIntx10_a2 = a2;
+ passStruct3BytesPackedIntx10_a3 = a3;
+ passStruct3BytesPackedIntx10_a4 = a4;
+ passStruct3BytesPackedIntx10_a5 = a5;
+ passStruct3BytesPackedIntx10_a6 = a6;
+ passStruct3BytesPackedIntx10_a7 = a7;
+ passStruct3BytesPackedIntx10_a8 = a8;
+ passStruct3BytesPackedIntx10_a9 = a9;
+
+ final result = passStruct3BytesPackedIntx10CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passStruct3BytesPackedIntx10AfterCallback() {
+ final result = passStruct3BytesPackedIntx10CalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.equals(10, result);
+}
+
+typedef PassStruct8BytesPackedIntx10Type = Int64 Function(
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt);
+
+// Global variables to be able to test inputs after callback returned.
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a0 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a1 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a2 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a3 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a4 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a5 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a6 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a7 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a8 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a9 = Struct8BytesPackedInt();
+
+// Result variable also global, so we can delete it after the callback.
+int passStruct8BytesPackedIntx10Result = 0;
+
+int passStruct8BytesPackedIntx10CalculateResult() {
+ int result = 0;
+
+ result += passStruct8BytesPackedIntx10_a0.a0;
+ result += passStruct8BytesPackedIntx10_a0.a1;
+ result += passStruct8BytesPackedIntx10_a0.a2;
+ result += passStruct8BytesPackedIntx10_a0.a3;
+ result += passStruct8BytesPackedIntx10_a0.a4;
+ result += passStruct8BytesPackedIntx10_a1.a0;
+ result += passStruct8BytesPackedIntx10_a1.a1;
+ result += passStruct8BytesPackedIntx10_a1.a2;
+ result += passStruct8BytesPackedIntx10_a1.a3;
+ result += passStruct8BytesPackedIntx10_a1.a4;
+ result += passStruct8BytesPackedIntx10_a2.a0;
+ result += passStruct8BytesPackedIntx10_a2.a1;
+ result += passStruct8BytesPackedIntx10_a2.a2;
+ result += passStruct8BytesPackedIntx10_a2.a3;
+ result += passStruct8BytesPackedIntx10_a2.a4;
+ result += passStruct8BytesPackedIntx10_a3.a0;
+ result += passStruct8BytesPackedIntx10_a3.a1;
+ result += passStruct8BytesPackedIntx10_a3.a2;
+ result += passStruct8BytesPackedIntx10_a3.a3;
+ result += passStruct8BytesPackedIntx10_a3.a4;
+ result += passStruct8BytesPackedIntx10_a4.a0;
+ result += passStruct8BytesPackedIntx10_a4.a1;
+ result += passStruct8BytesPackedIntx10_a4.a2;
+ result += passStruct8BytesPackedIntx10_a4.a3;
+ result += passStruct8BytesPackedIntx10_a4.a4;
+ result += passStruct8BytesPackedIntx10_a5.a0;
+ result += passStruct8BytesPackedIntx10_a5.a1;
+ result += passStruct8BytesPackedIntx10_a5.a2;
+ result += passStruct8BytesPackedIntx10_a5.a3;
+ result += passStruct8BytesPackedIntx10_a5.a4;
+ result += passStruct8BytesPackedIntx10_a6.a0;
+ result += passStruct8BytesPackedIntx10_a6.a1;
+ result += passStruct8BytesPackedIntx10_a6.a2;
+ result += passStruct8BytesPackedIntx10_a6.a3;
+ result += passStruct8BytesPackedIntx10_a6.a4;
+ result += passStruct8BytesPackedIntx10_a7.a0;
+ result += passStruct8BytesPackedIntx10_a7.a1;
+ result += passStruct8BytesPackedIntx10_a7.a2;
+ result += passStruct8BytesPackedIntx10_a7.a3;
+ result += passStruct8BytesPackedIntx10_a7.a4;
+ result += passStruct8BytesPackedIntx10_a8.a0;
+ result += passStruct8BytesPackedIntx10_a8.a1;
+ result += passStruct8BytesPackedIntx10_a8.a2;
+ result += passStruct8BytesPackedIntx10_a8.a3;
+ result += passStruct8BytesPackedIntx10_a8.a4;
+ result += passStruct8BytesPackedIntx10_a9.a0;
+ result += passStruct8BytesPackedIntx10_a9.a1;
+ result += passStruct8BytesPackedIntx10_a9.a2;
+ result += passStruct8BytesPackedIntx10_a9.a3;
+ result += passStruct8BytesPackedIntx10_a9.a4;
+
+ passStruct8BytesPackedIntx10Result = result;
+
+ return result;
+}
+
+/// Struct with mis-aligned member.
+int passStruct8BytesPackedIntx10(
+ Struct8BytesPackedInt a0,
+ Struct8BytesPackedInt a1,
+ Struct8BytesPackedInt a2,
+ Struct8BytesPackedInt a3,
+ Struct8BytesPackedInt a4,
+ Struct8BytesPackedInt a5,
+ Struct8BytesPackedInt a6,
+ Struct8BytesPackedInt a7,
+ Struct8BytesPackedInt a8,
+ Struct8BytesPackedInt a9) {
+ print(
+ "passStruct8BytesPackedIntx10(${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 == 42 || a0.a0 == 84) {
+ print("throwing!");
+ throw Exception("PassStruct8BytesPackedIntx10 throwing on purpose!");
+ }
+
+ passStruct8BytesPackedIntx10_a0 = a0;
+ passStruct8BytesPackedIntx10_a1 = a1;
+ passStruct8BytesPackedIntx10_a2 = a2;
+ passStruct8BytesPackedIntx10_a3 = a3;
+ passStruct8BytesPackedIntx10_a4 = a4;
+ passStruct8BytesPackedIntx10_a5 = a5;
+ passStruct8BytesPackedIntx10_a6 = a6;
+ passStruct8BytesPackedIntx10_a7 = a7;
+ passStruct8BytesPackedIntx10_a8 = a8;
+ passStruct8BytesPackedIntx10_a9 = a9;
+
+ final result = passStruct8BytesPackedIntx10CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passStruct8BytesPackedIntx10AfterCallback() {
+ final result = passStruct8BytesPackedIntx10CalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.equals(1275, result);
+}
+
+typedef PassStruct9BytesPackedMixedx10DoubleInt32Type = Double Function(
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Double,
+ Int32);
+
+// Global variables to be able to test inputs after callback returned.
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a0 =
+ Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a1 =
+ Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a2 =
+ Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a3 =
+ Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a4 =
+ Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a5 =
+ Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a6 =
+ Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a7 =
+ Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a8 =
+ Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a9 =
+ Struct9BytesPackedMixed();
+double passStruct9BytesPackedMixedx10DoubleInt32_a10 = 0.0;
+int passStruct9BytesPackedMixedx10DoubleInt32_a11 = 0;
+
+// Result variable also global, so we can delete it after the callback.
+double passStruct9BytesPackedMixedx10DoubleInt32Result = 0.0;
+
+double passStruct9BytesPackedMixedx10DoubleInt32CalculateResult() {
+ double result = 0;
+
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a0.a0;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a0.a1;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a1.a0;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a1.a1;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a2.a0;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a2.a1;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a3.a0;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a3.a1;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a4.a0;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a4.a1;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a5.a0;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a5.a1;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a6.a0;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a6.a1;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a7.a0;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a7.a1;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a8.a0;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a8.a1;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a9.a0;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a9.a1;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a10;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a11;
+
+ passStruct9BytesPackedMixedx10DoubleInt32Result = result;
+
+ return result;
+}
+
+/// Struct with mis-aligned member.
+/// Tests backfilling of CPU and FPU registers.
+double passStruct9BytesPackedMixedx10DoubleInt32(
+ Struct9BytesPackedMixed a0,
+ Struct9BytesPackedMixed a1,
+ Struct9BytesPackedMixed a2,
+ Struct9BytesPackedMixed a3,
+ Struct9BytesPackedMixed a4,
+ Struct9BytesPackedMixed a5,
+ Struct9BytesPackedMixed a6,
+ Struct9BytesPackedMixed a7,
+ Struct9BytesPackedMixed a8,
+ Struct9BytesPackedMixed a9,
+ double a10,
+ int a11) {
+ print(
+ "passStruct9BytesPackedMixedx10DoubleInt32(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9}, ${a10}, ${a11})");
+
+ // 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(
+ "PassStruct9BytesPackedMixedx10DoubleInt32 throwing on purpose!");
+ }
+
+ passStruct9BytesPackedMixedx10DoubleInt32_a0 = a0;
+ passStruct9BytesPackedMixedx10DoubleInt32_a1 = a1;
+ passStruct9BytesPackedMixedx10DoubleInt32_a2 = a2;
+ passStruct9BytesPackedMixedx10DoubleInt32_a3 = a3;
+ passStruct9BytesPackedMixedx10DoubleInt32_a4 = a4;
+ passStruct9BytesPackedMixedx10DoubleInt32_a5 = a5;
+ passStruct9BytesPackedMixedx10DoubleInt32_a6 = a6;
+ passStruct9BytesPackedMixedx10DoubleInt32_a7 = a7;
+ passStruct9BytesPackedMixedx10DoubleInt32_a8 = a8;
+ passStruct9BytesPackedMixedx10DoubleInt32_a9 = a9;
+ passStruct9BytesPackedMixedx10DoubleInt32_a10 = a10;
+ passStruct9BytesPackedMixedx10DoubleInt32_a11 = a11;
+
+ final result = passStruct9BytesPackedMixedx10DoubleInt32CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passStruct9BytesPackedMixedx10DoubleInt32AfterCallback() {
+ final result = passStruct9BytesPackedMixedx10DoubleInt32CalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.approxEquals(211.0, result);
+}
+
+typedef PassStruct5BytesPackedMixedType = Double Function(
+ Struct5BytesPackedMixed);
+
+// Global variables to be able to test inputs after callback returned.
+Struct5BytesPackedMixed passStruct5BytesPackedMixed_a0 =
+ Struct5BytesPackedMixed();
+
+// Result variable also global, so we can delete it after the callback.
+double passStruct5BytesPackedMixedResult = 0.0;
+
+double passStruct5BytesPackedMixedCalculateResult() {
+ double result = 0;
+
+ result += passStruct5BytesPackedMixed_a0.a0;
+ result += passStruct5BytesPackedMixed_a0.a1;
+
+ passStruct5BytesPackedMixedResult = result;
+
+ return result;
+}
+
+/// This packed struct happens to have only aligned members.
+double passStruct5BytesPackedMixed(Struct5BytesPackedMixed a0) {
+ print("passStruct5BytesPackedMixed(${a0})");
+
+ // 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("PassStruct5BytesPackedMixed throwing on purpose!");
+ }
+
+ passStruct5BytesPackedMixed_a0 = a0;
+
+ final result = passStruct5BytesPackedMixedCalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passStruct5BytesPackedMixedAfterCallback() {
+ final result = passStruct5BytesPackedMixedCalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.approxEquals(1.0, result);
+}
+
+typedef PassStructNestedAlignmentStruct5BytesPackedMixedType = Double Function(
+ StructNestedAlignmentStruct5BytesPackedMixed);
+
+// Global variables to be able to test inputs after callback returned.
+StructNestedAlignmentStruct5BytesPackedMixed
+ passStructNestedAlignmentStruct5BytesPackedMixed_a0 =
+ StructNestedAlignmentStruct5BytesPackedMixed();
+
+// Result variable also global, so we can delete it after the callback.
+double passStructNestedAlignmentStruct5BytesPackedMixedResult = 0.0;
+
+double passStructNestedAlignmentStruct5BytesPackedMixedCalculateResult() {
+ double result = 0;
+
+ result += passStructNestedAlignmentStruct5BytesPackedMixed_a0.a0;
+ result += passStructNestedAlignmentStruct5BytesPackedMixed_a0.a1.a0;
+ result += passStructNestedAlignmentStruct5BytesPackedMixed_a0.a1.a1;
+
+ passStructNestedAlignmentStruct5BytesPackedMixedResult = result;
+
+ return result;
+}
+
+/// Check alignment of packed struct in non-packed struct.
+double passStructNestedAlignmentStruct5BytesPackedMixed(
+ StructNestedAlignmentStruct5BytesPackedMixed a0) {
+ print("passStructNestedAlignmentStruct5BytesPackedMixed(${a0})");
+
+ // 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(
+ "PassStructNestedAlignmentStruct5BytesPackedMixed throwing on purpose!");
+ }
+
+ passStructNestedAlignmentStruct5BytesPackedMixed_a0 = a0;
+
+ final result =
+ passStructNestedAlignmentStruct5BytesPackedMixedCalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passStructNestedAlignmentStruct5BytesPackedMixedAfterCallback() {
+ final result =
+ passStructNestedAlignmentStruct5BytesPackedMixedCalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.approxEquals(6.0, result);
+}
+
+typedef PassStruct6BytesInlineArrayIntType = Double Function(
+ Struct6BytesInlineArrayInt);
+
+// Global variables to be able to test inputs after callback returned.
+Struct6BytesInlineArrayInt passStruct6BytesInlineArrayInt_a0 =
+ Struct6BytesInlineArrayInt();
+
+// Result variable also global, so we can delete it after the callback.
+double passStruct6BytesInlineArrayIntResult = 0.0;
+
+double passStruct6BytesInlineArrayIntCalculateResult() {
+ double result = 0;
+
+ result += passStruct6BytesInlineArrayInt_a0.a0[0].a0;
+ result += passStruct6BytesInlineArrayInt_a0.a0[0].a1;
+ result += passStruct6BytesInlineArrayInt_a0.a0[1].a0;
+ result += passStruct6BytesInlineArrayInt_a0.a0[1].a1;
+
+ passStruct6BytesInlineArrayIntResult = result;
+
+ return result;
+}
+
+/// Check alignment of packed struct array in non-packed struct.
+double passStruct6BytesInlineArrayInt(Struct6BytesInlineArrayInt a0) {
+ print("passStruct6BytesInlineArrayInt(${a0})");
+
+ // In legacy mode, possibly return null.
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0[0].a0 == 42 || a0.a0[0].a0 == 84) {
+ print("throwing!");
+ throw Exception("PassStruct6BytesInlineArrayInt throwing on purpose!");
+ }
+
+ passStruct6BytesInlineArrayInt_a0 = a0;
+
+ final result = passStruct6BytesInlineArrayIntCalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passStruct6BytesInlineArrayIntAfterCallback() {
+ final result = passStruct6BytesInlineArrayIntCalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.approxEquals(2.0, result);
+}
+
+typedef PassStruct15BytesInlineArrayMixedType = Double Function(
+ Struct15BytesInlineArrayMixed);
+
+// Global variables to be able to test inputs after callback returned.
+Struct15BytesInlineArrayMixed passStruct15BytesInlineArrayMixed_a0 =
+ Struct15BytesInlineArrayMixed();
+
+// Result variable also global, so we can delete it after the callback.
+double passStruct15BytesInlineArrayMixedResult = 0.0;
+
+double passStruct15BytesInlineArrayMixedCalculateResult() {
+ double result = 0;
+
+ result += passStruct15BytesInlineArrayMixed_a0.a0[0].a0;
+ result += passStruct15BytesInlineArrayMixed_a0.a0[0].a1;
+ result += passStruct15BytesInlineArrayMixed_a0.a0[1].a0;
+ result += passStruct15BytesInlineArrayMixed_a0.a0[1].a1;
+ result += passStruct15BytesInlineArrayMixed_a0.a0[2].a0;
+ result += passStruct15BytesInlineArrayMixed_a0.a0[2].a1;
+
+ passStruct15BytesInlineArrayMixedResult = result;
+
+ return result;
+}
+
+/// Check alignment of packed struct array in non-packed struct.
+double passStruct15BytesInlineArrayMixed(Struct15BytesInlineArrayMixed a0) {
+ print("passStruct15BytesInlineArrayMixed(${a0})");
+
+ // In legacy mode, possibly return null.
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0[0].a0 == 42 || a0.a0[0].a0 == 84) {
+ print("throwing!");
+ throw Exception("PassStruct15BytesInlineArrayMixed throwing on purpose!");
+ }
+
+ passStruct15BytesInlineArrayMixed_a0 = a0;
+
+ final result = passStruct15BytesInlineArrayMixedCalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passStruct15BytesInlineArrayMixedAfterCallback() {
+ final result = passStruct15BytesInlineArrayMixedCalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.approxEquals(3.0, result);
+}
+
typedef ReturnStruct1ByteIntType = Struct1ByteInt Function(Int8);
// Global variables to be able to test inputs after callback returned.
@@ -8283,6 +8913,182 @@
calloc.free(returnStruct1024BytesHomogeneousUint64ResultPointer);
}
+typedef ReturnStruct3BytesPackedIntType = Struct3BytesPackedInt Function(
+ Int8, Int16);
+
+// Global variables to be able to test inputs after callback returned.
+int returnStruct3BytesPackedInt_a0 = 0;
+int returnStruct3BytesPackedInt_a1 = 0;
+
+// Result variable also global, so we can delete it after the callback.
+Pointer<Struct3BytesPackedInt> returnStruct3BytesPackedIntResultPointer =
+ nullptr;
+
+Struct3BytesPackedInt returnStruct3BytesPackedIntCalculateResult() {
+ final resultPointer = calloc<Struct3BytesPackedInt>();
+ final result = resultPointer.ref;
+
+ result.a0 = returnStruct3BytesPackedInt_a0;
+ result.a1 = returnStruct3BytesPackedInt_a1;
+
+ returnStruct3BytesPackedIntResultPointer = resultPointer;
+
+ return result;
+}
+
+/// Small struct with mis-aligned member.
+Struct3BytesPackedInt returnStruct3BytesPackedInt(int a0, int a1) {
+ print("returnStruct3BytesPackedInt(${a0}, ${a1})");
+
+ // In legacy mode, possibly return null.
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0 == 42 || a0 == 84) {
+ print("throwing!");
+ throw Exception("ReturnStruct3BytesPackedInt throwing on purpose!");
+ }
+
+ returnStruct3BytesPackedInt_a0 = a0;
+ returnStruct3BytesPackedInt_a1 = a1;
+
+ final result = returnStruct3BytesPackedIntCalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void returnStruct3BytesPackedIntAfterCallback() {
+ calloc.free(returnStruct3BytesPackedIntResultPointer);
+
+ final result = returnStruct3BytesPackedIntCalculateResult();
+
+ print("after callback result = $result");
+
+ calloc.free(returnStruct3BytesPackedIntResultPointer);
+}
+
+typedef ReturnStruct8BytesPackedIntType = Struct8BytesPackedInt Function(
+ Uint8, Uint32, Uint8, Uint8, Uint8);
+
+// Global variables to be able to test inputs after callback returned.
+int returnStruct8BytesPackedInt_a0 = 0;
+int returnStruct8BytesPackedInt_a1 = 0;
+int returnStruct8BytesPackedInt_a2 = 0;
+int returnStruct8BytesPackedInt_a3 = 0;
+int returnStruct8BytesPackedInt_a4 = 0;
+
+// Result variable also global, so we can delete it after the callback.
+Pointer<Struct8BytesPackedInt> returnStruct8BytesPackedIntResultPointer =
+ nullptr;
+
+Struct8BytesPackedInt returnStruct8BytesPackedIntCalculateResult() {
+ final resultPointer = calloc<Struct8BytesPackedInt>();
+ final result = resultPointer.ref;
+
+ result.a0 = returnStruct8BytesPackedInt_a0;
+ result.a1 = returnStruct8BytesPackedInt_a1;
+ result.a2 = returnStruct8BytesPackedInt_a2;
+ result.a3 = returnStruct8BytesPackedInt_a3;
+ result.a4 = returnStruct8BytesPackedInt_a4;
+
+ returnStruct8BytesPackedIntResultPointer = resultPointer;
+
+ return result;
+}
+
+/// Struct with mis-aligned member.
+Struct8BytesPackedInt returnStruct8BytesPackedInt(
+ int a0, int a1, int a2, int a3, int a4) {
+ print("returnStruct8BytesPackedInt(${a0}, ${a1}, ${a2}, ${a3}, ${a4})");
+
+ // In legacy mode, possibly return null.
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0 == 42 || a0 == 84) {
+ print("throwing!");
+ throw Exception("ReturnStruct8BytesPackedInt throwing on purpose!");
+ }
+
+ returnStruct8BytesPackedInt_a0 = a0;
+ returnStruct8BytesPackedInt_a1 = a1;
+ returnStruct8BytesPackedInt_a2 = a2;
+ returnStruct8BytesPackedInt_a3 = a3;
+ returnStruct8BytesPackedInt_a4 = a4;
+
+ final result = returnStruct8BytesPackedIntCalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void returnStruct8BytesPackedIntAfterCallback() {
+ calloc.free(returnStruct8BytesPackedIntResultPointer);
+
+ final result = returnStruct8BytesPackedIntCalculateResult();
+
+ print("after callback result = $result");
+
+ calloc.free(returnStruct8BytesPackedIntResultPointer);
+}
+
+typedef ReturnStruct9BytesPackedMixedType = Struct9BytesPackedMixed Function(
+ Uint8, Double);
+
+// Global variables to be able to test inputs after callback returned.
+int returnStruct9BytesPackedMixed_a0 = 0;
+double returnStruct9BytesPackedMixed_a1 = 0.0;
+
+// Result variable also global, so we can delete it after the callback.
+Pointer<Struct9BytesPackedMixed> returnStruct9BytesPackedMixedResultPointer =
+ nullptr;
+
+Struct9BytesPackedMixed returnStruct9BytesPackedMixedCalculateResult() {
+ final resultPointer = calloc<Struct9BytesPackedMixed>();
+ final result = resultPointer.ref;
+
+ result.a0 = returnStruct9BytesPackedMixed_a0;
+ result.a1 = returnStruct9BytesPackedMixed_a1;
+
+ returnStruct9BytesPackedMixedResultPointer = resultPointer;
+
+ return result;
+}
+
+/// Struct with mis-aligned member.
+/// Tests backfilling of CPU and FPU registers.
+Struct9BytesPackedMixed returnStruct9BytesPackedMixed(int a0, double a1) {
+ print("returnStruct9BytesPackedMixed(${a0}, ${a1})");
+
+ // In legacy mode, possibly return null.
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0 == 42 || a0 == 84) {
+ print("throwing!");
+ throw Exception("ReturnStruct9BytesPackedMixed throwing on purpose!");
+ }
+
+ returnStruct9BytesPackedMixed_a0 = a0;
+ returnStruct9BytesPackedMixed_a1 = a1;
+
+ final result = returnStruct9BytesPackedMixedCalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void returnStruct9BytesPackedMixedAfterCallback() {
+ calloc.free(returnStruct9BytesPackedMixedResultPointer);
+
+ final result = returnStruct9BytesPackedMixedCalculateResult();
+
+ print("after callback result = $result");
+
+ calloc.free(returnStruct9BytesPackedMixedResultPointer);
+}
+
typedef ReturnStructArgumentStruct1ByteIntType = Struct1ByteInt Function(
Struct1ByteInt);
diff --git a/tests/ffi/function_structs_by_value_generated_test.dart b/tests/ffi/function_structs_by_value_generated_test.dart
index ef59018..d52c0d3 100644
--- a/tests/ffi/function_structs_by_value_generated_test.dart
+++ b/tests/ffi/function_structs_by_value_generated_test.dart
@@ -70,6 +70,13 @@
testPassStructStruct16BytesMixed3x10();
testPassUint8Struct32BytesInlineArrayMultiDimensionalI();
testPassUint8Struct4BytesInlineArrayMultiDimensionalIn();
+ testPassStruct3BytesPackedIntx10();
+ testPassStruct8BytesPackedIntx10();
+ testPassStruct9BytesPackedMixedx10DoubleInt32();
+ testPassStruct5BytesPackedMixed();
+ testPassStructNestedAlignmentStruct5BytesPackedMixed();
+ testPassStruct6BytesInlineArrayInt();
+ testPassStruct15BytesInlineArrayMixed();
testReturnStruct1ByteInt();
testReturnStruct3BytesHomogeneousUint8();
testReturnStruct3BytesInt2ByteAligned();
@@ -92,6 +99,9 @@
testReturnStruct32BytesHomogeneousDouble();
testReturnStruct40BytesHomogeneousDouble();
testReturnStruct1024BytesHomogeneousUint64();
+ testReturnStruct3BytesPackedInt();
+ testReturnStruct8BytesPackedInt();
+ testReturnStruct9BytesPackedMixed();
testReturnStructArgumentStruct1ByteInt();
testReturnStructArgumentInt32x8Struct1ByteInt();
testReturnStructArgumentStruct8BytesHomogeneousFloat();
@@ -1186,6 +1196,93 @@
]})";
}
+@Packed(1)
+class Struct3BytesPackedInt extends Struct {
+ @Int8()
+ external int a0;
+
+ @Int16()
+ external int a1;
+
+ String toString() => "(${a0}, ${a1})";
+}
+
+@Packed(1)
+class Struct3BytesPackedIntMembersAligned extends Struct {
+ @Int8()
+ external int a0;
+
+ @Int16()
+ external int a1;
+
+ String toString() => "(${a0}, ${a1})";
+}
+
+@Packed(1)
+class Struct5BytesPackedMixed extends Struct {
+ @Float()
+ external double a0;
+
+ @Uint8()
+ external int a1;
+
+ String toString() => "(${a0}, ${a1})";
+}
+
+class StructNestedAlignmentStruct5BytesPackedMixed extends Struct {
+ @Uint8()
+ external int a0;
+
+ external Struct5BytesPackedMixed a1;
+
+ String toString() => "(${a0}, ${a1})";
+}
+
+class Struct6BytesInlineArrayInt extends Struct {
+ @Array(2)
+ external Array<Struct3BytesPackedIntMembersAligned> a0;
+
+ String toString() => "(${[for (var i0 = 0; i0 < 2; i0 += 1) a0[i0]]})";
+}
+
+@Packed(1)
+class Struct8BytesPackedInt extends Struct {
+ @Uint8()
+ external int a0;
+
+ @Uint32()
+ external int a1;
+
+ @Uint8()
+ external int a2;
+
+ @Uint8()
+ external int a3;
+
+ @Uint8()
+ external int a4;
+
+ String toString() => "(${a0}, ${a1}, ${a2}, ${a3}, ${a4})";
+}
+
+@Packed(1)
+class Struct9BytesPackedMixed extends Struct {
+ @Uint8()
+ external int a0;
+
+ @Double()
+ external double a1;
+
+ String toString() => "(${a0}, ${a1})";
+}
+
+class Struct15BytesInlineArrayMixed extends Struct {
+ @Array(3)
+ external Array<Struct5BytesPackedMixed> a0;
+
+ String toString() => "(${[for (var i0 = 0; i0 < 3; i0 += 1) a0[i0]]})";
+}
+
final passStruct1ByteIntx10 = ffiTestFunctions.lookupFunction<
Int64 Function(
Struct1ByteInt,
@@ -5423,6 +5520,402 @@
calloc.free(a1Pointer);
}
+final passStruct3BytesPackedIntx10 = ffiTestFunctions.lookupFunction<
+ Int64 Function(
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt),
+ int Function(
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt)>("PassStruct3BytesPackedIntx10");
+
+/// Small struct with mis-aligned member.
+void testPassStruct3BytesPackedIntx10() {
+ final a0Pointer = calloc<Struct3BytesPackedInt>();
+ final Struct3BytesPackedInt a0 = a0Pointer.ref;
+ final a1Pointer = calloc<Struct3BytesPackedInt>();
+ final Struct3BytesPackedInt a1 = a1Pointer.ref;
+ final a2Pointer = calloc<Struct3BytesPackedInt>();
+ final Struct3BytesPackedInt a2 = a2Pointer.ref;
+ final a3Pointer = calloc<Struct3BytesPackedInt>();
+ final Struct3BytesPackedInt a3 = a3Pointer.ref;
+ final a4Pointer = calloc<Struct3BytesPackedInt>();
+ final Struct3BytesPackedInt a4 = a4Pointer.ref;
+ final a5Pointer = calloc<Struct3BytesPackedInt>();
+ final Struct3BytesPackedInt a5 = a5Pointer.ref;
+ final a6Pointer = calloc<Struct3BytesPackedInt>();
+ final Struct3BytesPackedInt a6 = a6Pointer.ref;
+ final a7Pointer = calloc<Struct3BytesPackedInt>();
+ final Struct3BytesPackedInt a7 = a7Pointer.ref;
+ final a8Pointer = calloc<Struct3BytesPackedInt>();
+ final Struct3BytesPackedInt a8 = a8Pointer.ref;
+ final a9Pointer = calloc<Struct3BytesPackedInt>();
+ final Struct3BytesPackedInt a9 = a9Pointer.ref;
+
+ a0.a0 = -1;
+ a0.a1 = 2;
+ a1.a0 = -3;
+ a1.a1 = 4;
+ a2.a0 = -5;
+ a2.a1 = 6;
+ a3.a0 = -7;
+ a3.a1 = 8;
+ a4.a0 = -9;
+ a4.a1 = 10;
+ a5.a0 = -11;
+ a5.a1 = 12;
+ a6.a0 = -13;
+ a6.a1 = 14;
+ a7.a0 = -15;
+ a7.a1 = 16;
+ a8.a0 = -17;
+ a8.a1 = 18;
+ a9.a0 = -19;
+ a9.a1 = 20;
+
+ final result =
+ passStruct3BytesPackedIntx10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+ print("result = $result");
+
+ Expect.equals(10, result);
+
+ calloc.free(a0Pointer);
+ calloc.free(a1Pointer);
+ calloc.free(a2Pointer);
+ calloc.free(a3Pointer);
+ calloc.free(a4Pointer);
+ calloc.free(a5Pointer);
+ calloc.free(a6Pointer);
+ calloc.free(a7Pointer);
+ calloc.free(a8Pointer);
+ calloc.free(a9Pointer);
+}
+
+final passStruct8BytesPackedIntx10 = ffiTestFunctions.lookupFunction<
+ Int64 Function(
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt),
+ int Function(
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt)>("PassStruct8BytesPackedIntx10");
+
+/// Struct with mis-aligned member.
+void testPassStruct8BytesPackedIntx10() {
+ final a0Pointer = calloc<Struct8BytesPackedInt>();
+ final Struct8BytesPackedInt a0 = a0Pointer.ref;
+ final a1Pointer = calloc<Struct8BytesPackedInt>();
+ final Struct8BytesPackedInt a1 = a1Pointer.ref;
+ final a2Pointer = calloc<Struct8BytesPackedInt>();
+ final Struct8BytesPackedInt a2 = a2Pointer.ref;
+ final a3Pointer = calloc<Struct8BytesPackedInt>();
+ final Struct8BytesPackedInt a3 = a3Pointer.ref;
+ final a4Pointer = calloc<Struct8BytesPackedInt>();
+ final Struct8BytesPackedInt a4 = a4Pointer.ref;
+ final a5Pointer = calloc<Struct8BytesPackedInt>();
+ final Struct8BytesPackedInt a5 = a5Pointer.ref;
+ final a6Pointer = calloc<Struct8BytesPackedInt>();
+ final Struct8BytesPackedInt a6 = a6Pointer.ref;
+ final a7Pointer = calloc<Struct8BytesPackedInt>();
+ final Struct8BytesPackedInt a7 = a7Pointer.ref;
+ final a8Pointer = calloc<Struct8BytesPackedInt>();
+ final Struct8BytesPackedInt a8 = a8Pointer.ref;
+ final a9Pointer = calloc<Struct8BytesPackedInt>();
+ final Struct8BytesPackedInt a9 = a9Pointer.ref;
+
+ a0.a0 = 1;
+ a0.a1 = 2;
+ a0.a2 = 3;
+ a0.a3 = 4;
+ a0.a4 = 5;
+ a1.a0 = 6;
+ a1.a1 = 7;
+ a1.a2 = 8;
+ a1.a3 = 9;
+ a1.a4 = 10;
+ a2.a0 = 11;
+ a2.a1 = 12;
+ a2.a2 = 13;
+ a2.a3 = 14;
+ a2.a4 = 15;
+ a3.a0 = 16;
+ a3.a1 = 17;
+ a3.a2 = 18;
+ a3.a3 = 19;
+ a3.a4 = 20;
+ a4.a0 = 21;
+ a4.a1 = 22;
+ a4.a2 = 23;
+ a4.a3 = 24;
+ a4.a4 = 25;
+ a5.a0 = 26;
+ a5.a1 = 27;
+ a5.a2 = 28;
+ a5.a3 = 29;
+ a5.a4 = 30;
+ a6.a0 = 31;
+ a6.a1 = 32;
+ a6.a2 = 33;
+ a6.a3 = 34;
+ a6.a4 = 35;
+ a7.a0 = 36;
+ a7.a1 = 37;
+ a7.a2 = 38;
+ a7.a3 = 39;
+ a7.a4 = 40;
+ a8.a0 = 41;
+ a8.a1 = 42;
+ a8.a2 = 43;
+ a8.a3 = 44;
+ a8.a4 = 45;
+ a9.a0 = 46;
+ a9.a1 = 47;
+ a9.a2 = 48;
+ a9.a3 = 49;
+ a9.a4 = 50;
+
+ final result =
+ passStruct8BytesPackedIntx10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+ print("result = $result");
+
+ Expect.equals(1275, result);
+
+ calloc.free(a0Pointer);
+ calloc.free(a1Pointer);
+ calloc.free(a2Pointer);
+ calloc.free(a3Pointer);
+ calloc.free(a4Pointer);
+ calloc.free(a5Pointer);
+ calloc.free(a6Pointer);
+ calloc.free(a7Pointer);
+ calloc.free(a8Pointer);
+ calloc.free(a9Pointer);
+}
+
+final passStruct9BytesPackedMixedx10DoubleInt32 =
+ ffiTestFunctions.lookupFunction<
+ Double Function(
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Double,
+ Int32),
+ double Function(
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ double,
+ int)>("PassStruct9BytesPackedMixedx10DoubleInt32");
+
+/// Struct with mis-aligned member.
+/// Tests backfilling of CPU and FPU registers.
+void testPassStruct9BytesPackedMixedx10DoubleInt32() {
+ final a0Pointer = calloc<Struct9BytesPackedMixed>();
+ final Struct9BytesPackedMixed a0 = a0Pointer.ref;
+ final a1Pointer = calloc<Struct9BytesPackedMixed>();
+ final Struct9BytesPackedMixed a1 = a1Pointer.ref;
+ final a2Pointer = calloc<Struct9BytesPackedMixed>();
+ final Struct9BytesPackedMixed a2 = a2Pointer.ref;
+ final a3Pointer = calloc<Struct9BytesPackedMixed>();
+ final Struct9BytesPackedMixed a3 = a3Pointer.ref;
+ final a4Pointer = calloc<Struct9BytesPackedMixed>();
+ final Struct9BytesPackedMixed a4 = a4Pointer.ref;
+ final a5Pointer = calloc<Struct9BytesPackedMixed>();
+ final Struct9BytesPackedMixed a5 = a5Pointer.ref;
+ final a6Pointer = calloc<Struct9BytesPackedMixed>();
+ final Struct9BytesPackedMixed a6 = a6Pointer.ref;
+ final a7Pointer = calloc<Struct9BytesPackedMixed>();
+ final Struct9BytesPackedMixed a7 = a7Pointer.ref;
+ final a8Pointer = calloc<Struct9BytesPackedMixed>();
+ final Struct9BytesPackedMixed a8 = a8Pointer.ref;
+ final a9Pointer = calloc<Struct9BytesPackedMixed>();
+ final Struct9BytesPackedMixed a9 = a9Pointer.ref;
+ double a10;
+ int a11;
+
+ a0.a0 = 1;
+ a0.a1 = 2.0;
+ a1.a0 = 3;
+ a1.a1 = 4.0;
+ a2.a0 = 5;
+ a2.a1 = 6.0;
+ a3.a0 = 7;
+ a3.a1 = 8.0;
+ a4.a0 = 9;
+ a4.a1 = 10.0;
+ a5.a0 = 11;
+ a5.a1 = 12.0;
+ a6.a0 = 13;
+ a6.a1 = 14.0;
+ a7.a0 = 15;
+ a7.a1 = 16.0;
+ a8.a0 = 17;
+ a8.a1 = 18.0;
+ a9.a0 = 19;
+ a9.a1 = 20.0;
+ a10 = -21.0;
+ a11 = 22;
+
+ final result = passStruct9BytesPackedMixedx10DoubleInt32(
+ a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11);
+
+ print("result = $result");
+
+ Expect.approxEquals(211.0, result);
+
+ calloc.free(a0Pointer);
+ calloc.free(a1Pointer);
+ calloc.free(a2Pointer);
+ calloc.free(a3Pointer);
+ calloc.free(a4Pointer);
+ calloc.free(a5Pointer);
+ calloc.free(a6Pointer);
+ calloc.free(a7Pointer);
+ calloc.free(a8Pointer);
+ calloc.free(a9Pointer);
+}
+
+final passStruct5BytesPackedMixed = ffiTestFunctions.lookupFunction<
+ Double Function(Struct5BytesPackedMixed),
+ double Function(Struct5BytesPackedMixed)>("PassStruct5BytesPackedMixed");
+
+/// This packed struct happens to have only aligned members.
+void testPassStruct5BytesPackedMixed() {
+ final a0Pointer = calloc<Struct5BytesPackedMixed>();
+ final Struct5BytesPackedMixed a0 = a0Pointer.ref;
+
+ a0.a0 = -1.0;
+ a0.a1 = 2;
+
+ final result = passStruct5BytesPackedMixed(a0);
+
+ print("result = $result");
+
+ Expect.approxEquals(1.0, result);
+
+ calloc.free(a0Pointer);
+}
+
+final passStructNestedAlignmentStruct5BytesPackedMixed =
+ ffiTestFunctions.lookupFunction<
+ Double Function(StructNestedAlignmentStruct5BytesPackedMixed),
+ double Function(StructNestedAlignmentStruct5BytesPackedMixed)>(
+ "PassStructNestedAlignmentStruct5BytesPackedMixed");
+
+/// Check alignment of packed struct in non-packed struct.
+void testPassStructNestedAlignmentStruct5BytesPackedMixed() {
+ final a0Pointer = calloc<StructNestedAlignmentStruct5BytesPackedMixed>();
+ final StructNestedAlignmentStruct5BytesPackedMixed a0 = a0Pointer.ref;
+
+ a0.a0 = 1;
+ a0.a1.a0 = 2.0;
+ a0.a1.a1 = 3;
+
+ final result = passStructNestedAlignmentStruct5BytesPackedMixed(a0);
+
+ print("result = $result");
+
+ Expect.approxEquals(6.0, result);
+
+ calloc.free(a0Pointer);
+}
+
+final passStruct6BytesInlineArrayInt = ffiTestFunctions.lookupFunction<
+ Double Function(Struct6BytesInlineArrayInt),
+ double Function(
+ Struct6BytesInlineArrayInt)>("PassStruct6BytesInlineArrayInt");
+
+/// Check alignment of packed struct array in non-packed struct.
+void testPassStruct6BytesInlineArrayInt() {
+ final a0Pointer = calloc<Struct6BytesInlineArrayInt>();
+ final Struct6BytesInlineArrayInt a0 = a0Pointer.ref;
+
+ a0.a0[0].a0 = -1;
+ a0.a0[0].a1 = 2;
+ a0.a0[1].a0 = -3;
+ a0.a0[1].a1 = 4;
+
+ final result = passStruct6BytesInlineArrayInt(a0);
+
+ print("result = $result");
+
+ Expect.approxEquals(2.0, result);
+
+ calloc.free(a0Pointer);
+}
+
+final passStruct15BytesInlineArrayMixed = ffiTestFunctions.lookupFunction<
+ Double Function(Struct15BytesInlineArrayMixed),
+ double Function(
+ Struct15BytesInlineArrayMixed)>("PassStruct15BytesInlineArrayMixed");
+
+/// Check alignment of packed struct array in non-packed struct.
+void testPassStruct15BytesInlineArrayMixed() {
+ final a0Pointer = calloc<Struct15BytesInlineArrayMixed>();
+ final Struct15BytesInlineArrayMixed a0 = a0Pointer.ref;
+
+ a0.a0[0].a0 = -1.0;
+ a0.a0[0].a1 = 2;
+ a0.a0[1].a0 = -3.0;
+ a0.a0[1].a1 = 4;
+ a0.a0[2].a0 = -5.0;
+ a0.a0[2].a1 = 6;
+
+ final result = passStruct15BytesInlineArrayMixed(a0);
+
+ print("result = $result");
+
+ Expect.approxEquals(3.0, result);
+
+ calloc.free(a0Pointer);
+}
+
final returnStruct1ByteInt = ffiTestFunctions.lookupFunction<
Struct1ByteInt Function(Int8),
Struct1ByteInt Function(int)>("ReturnStruct1ByteInt");
@@ -6841,6 +7334,78 @@
Expect.equals(a127, result.a127);
}
+final returnStruct3BytesPackedInt = ffiTestFunctions.lookupFunction<
+ Struct3BytesPackedInt Function(Int8, Int16),
+ Struct3BytesPackedInt Function(int, int)>("ReturnStruct3BytesPackedInt");
+
+/// Small struct with mis-aligned member.
+void testReturnStruct3BytesPackedInt() {
+ int a0;
+ int a1;
+
+ a0 = -1;
+ a1 = 2;
+
+ final result = returnStruct3BytesPackedInt(a0, a1);
+
+ print("result = $result");
+
+ Expect.equals(a0, result.a0);
+ Expect.equals(a1, result.a1);
+}
+
+final returnStruct8BytesPackedInt = ffiTestFunctions.lookupFunction<
+ Struct8BytesPackedInt Function(Uint8, Uint32, Uint8, Uint8, Uint8),
+ Struct8BytesPackedInt Function(
+ int, int, int, int, int)>("ReturnStruct8BytesPackedInt");
+
+/// Struct with mis-aligned member.
+void testReturnStruct8BytesPackedInt() {
+ int a0;
+ int a1;
+ int a2;
+ int a3;
+ int a4;
+
+ a0 = 1;
+ a1 = 2;
+ a2 = 3;
+ a3 = 4;
+ a4 = 5;
+
+ final result = returnStruct8BytesPackedInt(a0, a1, a2, a3, a4);
+
+ print("result = $result");
+
+ Expect.equals(a0, result.a0);
+ Expect.equals(a1, result.a1);
+ Expect.equals(a2, result.a2);
+ Expect.equals(a3, result.a3);
+ Expect.equals(a4, result.a4);
+}
+
+final returnStruct9BytesPackedMixed = ffiTestFunctions.lookupFunction<
+ Struct9BytesPackedMixed Function(Uint8, Double),
+ Struct9BytesPackedMixed Function(
+ int, double)>("ReturnStruct9BytesPackedMixed");
+
+/// Struct with mis-aligned member.
+/// Tests backfilling of CPU and FPU registers.
+void testReturnStruct9BytesPackedMixed() {
+ int a0;
+ double a1;
+
+ a0 = 1;
+ a1 = 2.0;
+
+ final result = returnStruct9BytesPackedMixed(a0, a1);
+
+ print("result = $result");
+
+ Expect.equals(a0, result.a0);
+ Expect.approxEquals(a1, result.a1);
+}
+
final returnStructArgumentStruct1ByteInt = ffiTestFunctions.lookupFunction<
Struct1ByteInt Function(Struct1ByteInt),
Struct1ByteInt Function(
diff --git a/tests/ffi/generator/c_types.dart b/tests/ffi/generator/c_types.dart
index e94b35b..df3b44b 100644
--- a/tests/ffi/generator/c_types.dart
+++ b/tests/ffi/generator/c_types.dart
@@ -155,20 +155,24 @@
class StructType extends CType {
final List<Member> members;
+ final int? packing;
+
/// To disambiguate same size structs.
final String suffix;
/// To override names.
final String overrideName;
- StructType(List<CType> memberTypes)
+ StructType(List<CType> memberTypes, {int? this.packing})
: this.members = generateMemberNames(memberTypes),
this.suffix = "",
this.overrideName = "";
- StructType.disambiguate(List<CType> memberTypes, this.suffix)
+ StructType.disambiguate(List<CType> memberTypes, this.suffix,
+ {int? this.packing})
: this.members = generateMemberNames(memberTypes),
this.overrideName = "";
- StructType.override(List<CType> memberTypes, this.overrideName)
+ StructType.override(List<CType> memberTypes, this.overrideName,
+ {int? this.packing})
: this.members = generateMemberNames(memberTypes),
this.suffix = "";
@@ -183,9 +187,19 @@
!memberTypes.map((e) => e.hasSize).contains(false) && !hasPadding;
int get size => memberTypes.fold(0, (int acc, e) => acc + e.size);
- /// Rough approximation, to not redo all ABI logic here.
- bool get hasPadding =>
- members.length < 2 ? false : members[0].type.size < members[1].type.size;
+ bool get hasPacking => packing != null;
+
+ bool get hasPadding {
+ if (members.length < 2) {
+ return false;
+ }
+ if (packing == 1) {
+ return false;
+ }
+
+ /// Rough approximation, to not redo all ABI logic here.
+ return members[0].type.size < members[1].type.size;
+ }
bool get hasNestedStructs =>
members.map((e) => e.type is StructType).contains(true);
@@ -217,6 +231,12 @@
if (hasSize) {
result += "${size}Byte" + (size != 1 ? "s" : "");
}
+ if (hasPacking) {
+ result += "Packed";
+ if (packing! > 1) {
+ result += "$packing";
+ }
+ }
if (hasNestedStructs) {
result += "Nested";
}
diff --git a/tests/ffi/generator/structs_by_value_tests_configuration.dart b/tests/ffi/generator/structs_by_value_tests_configuration.dart
index ebc6a94..1ad3c11 100644
--- a/tests/ffi/generator/structs_by_value_tests_configuration.dart
+++ b/tests/ffi/generator/structs_by_value_tests_configuration.dart
@@ -328,6 +328,36 @@
uint32,
"""
Test struct in multi dimensional inline array."""),
+ FunctionType(List.filled(10, struct3bytesPacked), int64, """
+Small struct with mis-aligned member."""),
+ FunctionType(List.filled(10, struct8bytesPacked), int64, """
+Struct with mis-aligned member."""),
+ FunctionType(
+ [...List.filled(10, struct9bytesPacked), double_, int32],
+ double_,
+ """
+Struct with mis-aligned member.
+Tests backfilling of CPU and FPU registers."""),
+ FunctionType(
+ [struct5bytesPacked],
+ double_,
+ """
+This packed struct happens to have only aligned members."""),
+ FunctionType(
+ [struct6bytesPacked],
+ double_,
+ """
+Check alignment of packed struct in non-packed struct."""),
+ FunctionType(
+ [struct6bytesPacked2],
+ double_,
+ """
+Check alignment of packed struct array in non-packed struct."""),
+ FunctionType(
+ [struct15bytesPacked],
+ double_,
+ """
+Check alignment of packed struct array in non-packed struct."""),
FunctionType(struct1byteInt.memberTypes, struct1byteInt, """
Smallest struct with data."""),
FunctionType(struct3bytesInt.memberTypes, struct3bytesInt, """
@@ -383,6 +413,13 @@
Return value too big to go in FPU registers on arm64."""),
FunctionType(struct1024bytesInt.memberTypes, struct1024bytesInt, """
Test 1kb struct."""),
+ FunctionType(struct3bytesPacked.memberTypes, struct3bytesPacked, """
+Small struct with mis-aligned member."""),
+ FunctionType(struct8bytesPacked.memberTypes, struct8bytesPacked, """
+Struct with mis-aligned member."""),
+ FunctionType(struct9bytesPacked.memberTypes, struct9bytesPacked, """
+Struct with mis-aligned member.
+Tests backfilling of CPU and FPU registers."""),
FunctionType(
[struct1byteInt],
struct1byteInt,
@@ -529,6 +566,14 @@
struct32bytesInlineArrayMultiDimesional,
struct64bytesInlineArrayMultiDimesional,
structMultiDimensionalStruct,
+ struct3bytesPacked,
+ struct3bytesPackedMembersAligned,
+ struct5bytesPacked,
+ struct6bytesPacked,
+ struct6bytesPacked2,
+ struct8bytesPacked,
+ struct9bytesPacked,
+ struct15bytesPacked,
];
final struct1byteInt = StructType([int8]);
@@ -672,3 +717,27 @@
final structMultiDimensionalStruct = StructType([
FixedLengthArrayType.multi(struct1byteInt, [2, 2])
]);
+
+final struct3bytesPacked = StructType([int8, int16], packing: 1);
+
+final struct3bytesPackedMembersAligned =
+ StructType.disambiguate([int8, int16], "MembersAligned", packing: 1);
+
+final struct5bytesPacked = StructType([float, uint8], packing: 1);
+
+/// The float in the nested struct is not aligned.
+final struct6bytesPacked = StructType([uint8, struct5bytesPacked]);
+
+/// The second element in the array has a nested misaligned int16.
+final struct6bytesPacked2 =
+ StructType([FixedLengthArrayType(struct3bytesPackedMembersAligned, 2)]);
+
+final struct8bytesPacked =
+ StructType([uint8, uint32, uint8, uint8, uint8], packing: 1);
+
+final struct9bytesPacked = StructType([uint8, double_], packing: 1);
+
+/// The float in the nested struct is aligned in the first element in the
+/// inline array, but not in the subsequent ones.
+final struct15bytesPacked =
+ StructType([FixedLengthArrayType(struct5bytesPacked, 3)]);
diff --git a/tests/ffi/generator/structs_by_value_tests_generator.dart b/tests/ffi/generator/structs_by_value_tests_generator.dart
index 51f51ab..c66dbea 100644
--- a/tests/ffi/generator/structs_by_value_tests_generator.dart
+++ b/tests/ffi/generator/structs_by_value_tests_generator.dart
@@ -455,6 +455,7 @@
extension on StructType {
String dartClass(bool nnbd) {
+ final packingAnnotation = hasPacking ? "@Packed(${packing})" : "";
String dartFields = "";
for (final member in members) {
dartFields += "${member.dartStructField(nnbd)}\n\n";
@@ -477,6 +478,7 @@
return "\$\{${m.name}\}";
}).join(", ");
return """
+ $packingAnnotation
class $name extends Struct {
$dartFields
@@ -486,14 +488,19 @@
}
String get cDefinition {
+ final packingPragmaPush =
+ hasPacking ? "#pragma pack(push, ${packing})" : "";
+ final packingPragmaPop = hasPacking ? "#pragma pack(pop)" : "";
String cFields = "";
for (final member in members) {
cFields += " ${member.cStructField}\n";
}
return """
+ $packingPragmaPush
struct $name {
$cFields
};
+ $packingPragmaPop
""";
}
diff --git a/tests/ffi/structs_packed_test.dart b/tests/ffi/structs_packed_test.dart
new file mode 100644
index 0000000..cec7703
--- /dev/null
+++ b/tests/ffi/structs_packed_test.dart
@@ -0,0 +1,40 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+//
+// SharedObjects=ffi_test_functions
+
+import 'dart:ffi';
+
+import "package:expect/expect.dart";
+
+import 'dylib_utils.dart';
+
+// Reuse struct definitions.
+import 'function_structs_by_value_generated_test.dart';
+
+void main() {
+ testSizeOfC();
+ testSizeOfDart();
+}
+
+final ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions");
+
+final sizeOfStruct3BytesPackedInt =
+ ffiTestFunctions.lookupFunction<Uint64 Function(), int Function()>(
+ "SizeOfStruct3BytesPackedInt");
+
+void testSizeOfC() {
+ Expect.equals(3, sizeOfStruct3BytesPackedInt());
+}
+
+void testSizeOfDart() {
+ // No packing needed to get to 3 bytes.
+ Expect.equals(3, sizeOf<Struct3BytesHomogeneousUint8>());
+
+ // Contents 3 bytes, but alignment forces it to be 4 bytes.
+ Expect.equals(4, sizeOf<Struct3BytesInt2ByteAligned>());
+
+ // Alignment gets the same content back to 3 bytes.
+ Expect.equals(3, sizeOf<Struct3BytesPackedInt>());
+}
diff --git a/tests/ffi/vmspecific_static_checks_test.dart b/tests/ffi/vmspecific_static_checks_test.dart
index 746b238..e70f9963 100644
--- a/tests/ffi/vmspecific_static_checks_test.dart
+++ b/tests/ffi/vmspecific_static_checks_test.dart
@@ -703,3 +703,59 @@
external Pointer<Uint8> notEmpty;
}
+
+@Packed(1)
+class TestStruct1600 extends Struct {
+ external Pointer<Uint8> notEmpty;
+}
+
+@Packed(1)
+@Packed(1) //# 1601: compile-time error
+class TestStruct1601 extends Struct {
+ external Pointer<Uint8> notEmpty;
+}
+
+@Packed(3) //# 1602: compile-time error
+class TestStruct1602 extends Struct {
+ external Pointer<Uint8> notEmpty;
+}
+
+class TestStruct1603 extends Struct {
+ external Pointer<Uint8> notEmpty;
+}
+
+@Packed(1)
+class TestStruct1603Packed extends Struct {
+ external Pointer<Uint8> notEmpty;
+
+ external TestStruct1603 nestedNotPacked; //# 1603: compile-time error
+}
+
+@Packed(8)
+class TestStruct1604 extends Struct {
+ external Pointer<Uint8> notEmpty;
+}
+
+@Packed(1)
+class TestStruct1604Packed extends Struct {
+ external Pointer<Uint8> notEmpty;
+
+ external TestStruct1604 nestedLooselyPacked; //# 1604: compile-time error
+}
+
+@Packed(1)
+class TestStruct1605Packed extends Struct {
+ external Pointer<Uint8> notEmpty;
+
+ @Array(2) //# 1605: compile-time error
+ external Array<TestStruct1603> nestedNotPacked; //# 1605: compile-time error
+}
+
+@Packed(1)
+class TestStruct1606Packed extends Struct {
+ external Pointer<Uint8> notEmpty;
+
+ @Array(2) //# 1606: compile-time error
+ external Array<TestStruct1604> //# 1606: compile-time error
+ nestedLooselyPacked; //# 1606: 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 d4650fe..c063d7f 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
@@ -285,6 +285,42 @@
passUint8Struct4BytesInlineArrayMultiDimensionalIn, 0),
passUint8Struct4BytesInlineArrayMultiDimensionalInAfterCallback),
CallbackTest.withCheck(
+ "PassStruct3BytesPackedIntx10",
+ Pointer.fromFunction<PassStruct3BytesPackedIntx10Type>(
+ passStruct3BytesPackedIntx10, 0),
+ passStruct3BytesPackedIntx10AfterCallback),
+ CallbackTest.withCheck(
+ "PassStruct8BytesPackedIntx10",
+ Pointer.fromFunction<PassStruct8BytesPackedIntx10Type>(
+ passStruct8BytesPackedIntx10, 0),
+ passStruct8BytesPackedIntx10AfterCallback),
+ CallbackTest.withCheck(
+ "PassStruct9BytesPackedMixedx10DoubleInt32",
+ Pointer.fromFunction<PassStruct9BytesPackedMixedx10DoubleInt32Type>(
+ passStruct9BytesPackedMixedx10DoubleInt32, 0.0),
+ passStruct9BytesPackedMixedx10DoubleInt32AfterCallback),
+ CallbackTest.withCheck(
+ "PassStruct5BytesPackedMixed",
+ Pointer.fromFunction<PassStruct5BytesPackedMixedType>(
+ passStruct5BytesPackedMixed, 0.0),
+ passStruct5BytesPackedMixedAfterCallback),
+ CallbackTest.withCheck(
+ "PassStructNestedAlignmentStruct5BytesPackedMixed",
+ Pointer.fromFunction<
+ PassStructNestedAlignmentStruct5BytesPackedMixedType>(
+ passStructNestedAlignmentStruct5BytesPackedMixed, 0.0),
+ passStructNestedAlignmentStruct5BytesPackedMixedAfterCallback),
+ CallbackTest.withCheck(
+ "PassStruct6BytesInlineArrayInt",
+ Pointer.fromFunction<PassStruct6BytesInlineArrayIntType>(
+ passStruct6BytesInlineArrayInt, 0.0),
+ passStruct6BytesInlineArrayIntAfterCallback),
+ CallbackTest.withCheck(
+ "PassStruct15BytesInlineArrayMixed",
+ Pointer.fromFunction<PassStruct15BytesInlineArrayMixedType>(
+ passStruct15BytesInlineArrayMixed, 0.0),
+ passStruct15BytesInlineArrayMixedAfterCallback),
+ CallbackTest.withCheck(
"ReturnStruct1ByteInt",
Pointer.fromFunction<ReturnStruct1ByteIntType>(returnStruct1ByteInt),
returnStruct1ByteIntAfterCallback),
@@ -392,6 +428,21 @@
returnStruct1024BytesHomogeneousUint64),
returnStruct1024BytesHomogeneousUint64AfterCallback),
CallbackTest.withCheck(
+ "ReturnStruct3BytesPackedInt",
+ Pointer.fromFunction<ReturnStruct3BytesPackedIntType>(
+ returnStruct3BytesPackedInt),
+ returnStruct3BytesPackedIntAfterCallback),
+ CallbackTest.withCheck(
+ "ReturnStruct8BytesPackedInt",
+ Pointer.fromFunction<ReturnStruct8BytesPackedIntType>(
+ returnStruct8BytesPackedInt),
+ returnStruct8BytesPackedIntAfterCallback),
+ CallbackTest.withCheck(
+ "ReturnStruct9BytesPackedMixed",
+ Pointer.fromFunction<ReturnStruct9BytesPackedMixedType>(
+ returnStruct9BytesPackedMixed),
+ returnStruct9BytesPackedMixedAfterCallback),
+ CallbackTest.withCheck(
"ReturnStructArgumentStruct1ByteInt",
Pointer.fromFunction<ReturnStructArgumentStruct1ByteIntType>(
returnStructArgumentStruct1ByteInt),
@@ -6410,6 +6461,613 @@
Expect.equals(5, result);
}
+typedef PassStruct3BytesPackedIntx10Type = Int64 Function(
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt);
+
+// Global variables to be able to test inputs after callback returned.
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a0 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a1 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a2 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a3 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a4 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a5 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a6 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a7 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a8 = Struct3BytesPackedInt();
+Struct3BytesPackedInt passStruct3BytesPackedIntx10_a9 = Struct3BytesPackedInt();
+
+// Result variable also global, so we can delete it after the callback.
+int passStruct3BytesPackedIntx10Result = 0;
+
+int passStruct3BytesPackedIntx10CalculateResult() {
+ int result = 0;
+
+ result += passStruct3BytesPackedIntx10_a0.a0;
+ result += passStruct3BytesPackedIntx10_a0.a1;
+ result += passStruct3BytesPackedIntx10_a1.a0;
+ result += passStruct3BytesPackedIntx10_a1.a1;
+ result += passStruct3BytesPackedIntx10_a2.a0;
+ result += passStruct3BytesPackedIntx10_a2.a1;
+ result += passStruct3BytesPackedIntx10_a3.a0;
+ result += passStruct3BytesPackedIntx10_a3.a1;
+ result += passStruct3BytesPackedIntx10_a4.a0;
+ result += passStruct3BytesPackedIntx10_a4.a1;
+ result += passStruct3BytesPackedIntx10_a5.a0;
+ result += passStruct3BytesPackedIntx10_a5.a1;
+ result += passStruct3BytesPackedIntx10_a6.a0;
+ result += passStruct3BytesPackedIntx10_a6.a1;
+ result += passStruct3BytesPackedIntx10_a7.a0;
+ result += passStruct3BytesPackedIntx10_a7.a1;
+ result += passStruct3BytesPackedIntx10_a8.a0;
+ result += passStruct3BytesPackedIntx10_a8.a1;
+ result += passStruct3BytesPackedIntx10_a9.a0;
+ result += passStruct3BytesPackedIntx10_a9.a1;
+
+ passStruct3BytesPackedIntx10Result = result;
+
+ return result;
+}
+
+/// Small struct with mis-aligned member.
+int passStruct3BytesPackedIntx10(
+ Struct3BytesPackedInt a0,
+ Struct3BytesPackedInt a1,
+ Struct3BytesPackedInt a2,
+ Struct3BytesPackedInt a3,
+ Struct3BytesPackedInt a4,
+ Struct3BytesPackedInt a5,
+ Struct3BytesPackedInt a6,
+ Struct3BytesPackedInt a7,
+ Struct3BytesPackedInt a8,
+ Struct3BytesPackedInt a9) {
+ print(
+ "passStruct3BytesPackedIntx10(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9})");
+
+ // 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("PassStruct3BytesPackedIntx10 throwing on purpose!");
+ }
+
+ passStruct3BytesPackedIntx10_a0 = a0;
+ passStruct3BytesPackedIntx10_a1 = a1;
+ passStruct3BytesPackedIntx10_a2 = a2;
+ passStruct3BytesPackedIntx10_a3 = a3;
+ passStruct3BytesPackedIntx10_a4 = a4;
+ passStruct3BytesPackedIntx10_a5 = a5;
+ passStruct3BytesPackedIntx10_a6 = a6;
+ passStruct3BytesPackedIntx10_a7 = a7;
+ passStruct3BytesPackedIntx10_a8 = a8;
+ passStruct3BytesPackedIntx10_a9 = a9;
+
+ final result = passStruct3BytesPackedIntx10CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passStruct3BytesPackedIntx10AfterCallback() {
+ final result = passStruct3BytesPackedIntx10CalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.equals(10, result);
+}
+
+typedef PassStruct8BytesPackedIntx10Type = Int64 Function(
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt);
+
+// Global variables to be able to test inputs after callback returned.
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a0 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a1 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a2 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a3 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a4 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a5 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a6 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a7 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a8 = Struct8BytesPackedInt();
+Struct8BytesPackedInt passStruct8BytesPackedIntx10_a9 = Struct8BytesPackedInt();
+
+// Result variable also global, so we can delete it after the callback.
+int passStruct8BytesPackedIntx10Result = 0;
+
+int passStruct8BytesPackedIntx10CalculateResult() {
+ int result = 0;
+
+ result += passStruct8BytesPackedIntx10_a0.a0;
+ result += passStruct8BytesPackedIntx10_a0.a1;
+ result += passStruct8BytesPackedIntx10_a0.a2;
+ result += passStruct8BytesPackedIntx10_a0.a3;
+ result += passStruct8BytesPackedIntx10_a0.a4;
+ result += passStruct8BytesPackedIntx10_a1.a0;
+ result += passStruct8BytesPackedIntx10_a1.a1;
+ result += passStruct8BytesPackedIntx10_a1.a2;
+ result += passStruct8BytesPackedIntx10_a1.a3;
+ result += passStruct8BytesPackedIntx10_a1.a4;
+ result += passStruct8BytesPackedIntx10_a2.a0;
+ result += passStruct8BytesPackedIntx10_a2.a1;
+ result += passStruct8BytesPackedIntx10_a2.a2;
+ result += passStruct8BytesPackedIntx10_a2.a3;
+ result += passStruct8BytesPackedIntx10_a2.a4;
+ result += passStruct8BytesPackedIntx10_a3.a0;
+ result += passStruct8BytesPackedIntx10_a3.a1;
+ result += passStruct8BytesPackedIntx10_a3.a2;
+ result += passStruct8BytesPackedIntx10_a3.a3;
+ result += passStruct8BytesPackedIntx10_a3.a4;
+ result += passStruct8BytesPackedIntx10_a4.a0;
+ result += passStruct8BytesPackedIntx10_a4.a1;
+ result += passStruct8BytesPackedIntx10_a4.a2;
+ result += passStruct8BytesPackedIntx10_a4.a3;
+ result += passStruct8BytesPackedIntx10_a4.a4;
+ result += passStruct8BytesPackedIntx10_a5.a0;
+ result += passStruct8BytesPackedIntx10_a5.a1;
+ result += passStruct8BytesPackedIntx10_a5.a2;
+ result += passStruct8BytesPackedIntx10_a5.a3;
+ result += passStruct8BytesPackedIntx10_a5.a4;
+ result += passStruct8BytesPackedIntx10_a6.a0;
+ result += passStruct8BytesPackedIntx10_a6.a1;
+ result += passStruct8BytesPackedIntx10_a6.a2;
+ result += passStruct8BytesPackedIntx10_a6.a3;
+ result += passStruct8BytesPackedIntx10_a6.a4;
+ result += passStruct8BytesPackedIntx10_a7.a0;
+ result += passStruct8BytesPackedIntx10_a7.a1;
+ result += passStruct8BytesPackedIntx10_a7.a2;
+ result += passStruct8BytesPackedIntx10_a7.a3;
+ result += passStruct8BytesPackedIntx10_a7.a4;
+ result += passStruct8BytesPackedIntx10_a8.a0;
+ result += passStruct8BytesPackedIntx10_a8.a1;
+ result += passStruct8BytesPackedIntx10_a8.a2;
+ result += passStruct8BytesPackedIntx10_a8.a3;
+ result += passStruct8BytesPackedIntx10_a8.a4;
+ result += passStruct8BytesPackedIntx10_a9.a0;
+ result += passStruct8BytesPackedIntx10_a9.a1;
+ result += passStruct8BytesPackedIntx10_a9.a2;
+ result += passStruct8BytesPackedIntx10_a9.a3;
+ result += passStruct8BytesPackedIntx10_a9.a4;
+
+ passStruct8BytesPackedIntx10Result = result;
+
+ return result;
+}
+
+/// Struct with mis-aligned member.
+int passStruct8BytesPackedIntx10(
+ Struct8BytesPackedInt a0,
+ Struct8BytesPackedInt a1,
+ Struct8BytesPackedInt a2,
+ Struct8BytesPackedInt a3,
+ Struct8BytesPackedInt a4,
+ Struct8BytesPackedInt a5,
+ Struct8BytesPackedInt a6,
+ Struct8BytesPackedInt a7,
+ Struct8BytesPackedInt a8,
+ Struct8BytesPackedInt a9) {
+ print(
+ "passStruct8BytesPackedIntx10(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9})");
+
+ // 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("PassStruct8BytesPackedIntx10 throwing on purpose!");
+ }
+
+ passStruct8BytesPackedIntx10_a0 = a0;
+ passStruct8BytesPackedIntx10_a1 = a1;
+ passStruct8BytesPackedIntx10_a2 = a2;
+ passStruct8BytesPackedIntx10_a3 = a3;
+ passStruct8BytesPackedIntx10_a4 = a4;
+ passStruct8BytesPackedIntx10_a5 = a5;
+ passStruct8BytesPackedIntx10_a6 = a6;
+ passStruct8BytesPackedIntx10_a7 = a7;
+ passStruct8BytesPackedIntx10_a8 = a8;
+ passStruct8BytesPackedIntx10_a9 = a9;
+
+ final result = passStruct8BytesPackedIntx10CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passStruct8BytesPackedIntx10AfterCallback() {
+ final result = passStruct8BytesPackedIntx10CalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.equals(1275, result);
+}
+
+typedef PassStruct9BytesPackedMixedx10DoubleInt32Type = Double Function(
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Double,
+ Int32);
+
+// Global variables to be able to test inputs after callback returned.
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a0 =
+ Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a1 =
+ Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a2 =
+ Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a3 =
+ Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a4 =
+ Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a5 =
+ Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a6 =
+ Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a7 =
+ Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a8 =
+ Struct9BytesPackedMixed();
+Struct9BytesPackedMixed passStruct9BytesPackedMixedx10DoubleInt32_a9 =
+ Struct9BytesPackedMixed();
+double passStruct9BytesPackedMixedx10DoubleInt32_a10 = 0.0;
+int passStruct9BytesPackedMixedx10DoubleInt32_a11 = 0;
+
+// Result variable also global, so we can delete it after the callback.
+double passStruct9BytesPackedMixedx10DoubleInt32Result = 0.0;
+
+double passStruct9BytesPackedMixedx10DoubleInt32CalculateResult() {
+ double result = 0;
+
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a0.a0;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a0.a1;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a1.a0;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a1.a1;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a2.a0;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a2.a1;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a3.a0;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a3.a1;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a4.a0;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a4.a1;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a5.a0;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a5.a1;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a6.a0;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a6.a1;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a7.a0;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a7.a1;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a8.a0;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a8.a1;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a9.a0;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a9.a1;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a10;
+ result += passStruct9BytesPackedMixedx10DoubleInt32_a11;
+
+ passStruct9BytesPackedMixedx10DoubleInt32Result = result;
+
+ return result;
+}
+
+/// Struct with mis-aligned member.
+/// Tests backfilling of CPU and FPU registers.
+double passStruct9BytesPackedMixedx10DoubleInt32(
+ Struct9BytesPackedMixed a0,
+ Struct9BytesPackedMixed a1,
+ Struct9BytesPackedMixed a2,
+ Struct9BytesPackedMixed a3,
+ Struct9BytesPackedMixed a4,
+ Struct9BytesPackedMixed a5,
+ Struct9BytesPackedMixed a6,
+ Struct9BytesPackedMixed a7,
+ Struct9BytesPackedMixed a8,
+ Struct9BytesPackedMixed a9,
+ double a10,
+ int a11) {
+ print(
+ "passStruct9BytesPackedMixedx10DoubleInt32(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9}, ${a10}, ${a11})");
+
+ // 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(
+ "PassStruct9BytesPackedMixedx10DoubleInt32 throwing on purpose!");
+ }
+
+ passStruct9BytesPackedMixedx10DoubleInt32_a0 = a0;
+ passStruct9BytesPackedMixedx10DoubleInt32_a1 = a1;
+ passStruct9BytesPackedMixedx10DoubleInt32_a2 = a2;
+ passStruct9BytesPackedMixedx10DoubleInt32_a3 = a3;
+ passStruct9BytesPackedMixedx10DoubleInt32_a4 = a4;
+ passStruct9BytesPackedMixedx10DoubleInt32_a5 = a5;
+ passStruct9BytesPackedMixedx10DoubleInt32_a6 = a6;
+ passStruct9BytesPackedMixedx10DoubleInt32_a7 = a7;
+ passStruct9BytesPackedMixedx10DoubleInt32_a8 = a8;
+ passStruct9BytesPackedMixedx10DoubleInt32_a9 = a9;
+ passStruct9BytesPackedMixedx10DoubleInt32_a10 = a10;
+ passStruct9BytesPackedMixedx10DoubleInt32_a11 = a11;
+
+ final result = passStruct9BytesPackedMixedx10DoubleInt32CalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passStruct9BytesPackedMixedx10DoubleInt32AfterCallback() {
+ final result = passStruct9BytesPackedMixedx10DoubleInt32CalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.approxEquals(211.0, result);
+}
+
+typedef PassStruct5BytesPackedMixedType = Double Function(
+ Struct5BytesPackedMixed);
+
+// Global variables to be able to test inputs after callback returned.
+Struct5BytesPackedMixed passStruct5BytesPackedMixed_a0 =
+ Struct5BytesPackedMixed();
+
+// Result variable also global, so we can delete it after the callback.
+double passStruct5BytesPackedMixedResult = 0.0;
+
+double passStruct5BytesPackedMixedCalculateResult() {
+ double result = 0;
+
+ result += passStruct5BytesPackedMixed_a0.a0;
+ result += passStruct5BytesPackedMixed_a0.a1;
+
+ passStruct5BytesPackedMixedResult = result;
+
+ return result;
+}
+
+/// This packed struct happens to have only aligned members.
+double passStruct5BytesPackedMixed(Struct5BytesPackedMixed a0) {
+ print("passStruct5BytesPackedMixed(${a0})");
+
+ // 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("PassStruct5BytesPackedMixed throwing on purpose!");
+ }
+
+ passStruct5BytesPackedMixed_a0 = a0;
+
+ final result = passStruct5BytesPackedMixedCalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passStruct5BytesPackedMixedAfterCallback() {
+ final result = passStruct5BytesPackedMixedCalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.approxEquals(1.0, result);
+}
+
+typedef PassStructNestedAlignmentStruct5BytesPackedMixedType = Double Function(
+ StructNestedAlignmentStruct5BytesPackedMixed);
+
+// Global variables to be able to test inputs after callback returned.
+StructNestedAlignmentStruct5BytesPackedMixed
+ passStructNestedAlignmentStruct5BytesPackedMixed_a0 =
+ StructNestedAlignmentStruct5BytesPackedMixed();
+
+// Result variable also global, so we can delete it after the callback.
+double passStructNestedAlignmentStruct5BytesPackedMixedResult = 0.0;
+
+double passStructNestedAlignmentStruct5BytesPackedMixedCalculateResult() {
+ double result = 0;
+
+ result += passStructNestedAlignmentStruct5BytesPackedMixed_a0.a0;
+ result += passStructNestedAlignmentStruct5BytesPackedMixed_a0.a1.a0;
+ result += passStructNestedAlignmentStruct5BytesPackedMixed_a0.a1.a1;
+
+ passStructNestedAlignmentStruct5BytesPackedMixedResult = result;
+
+ return result;
+}
+
+/// Check alignment of packed struct in non-packed struct.
+double passStructNestedAlignmentStruct5BytesPackedMixed(
+ StructNestedAlignmentStruct5BytesPackedMixed a0) {
+ print("passStructNestedAlignmentStruct5BytesPackedMixed(${a0})");
+
+ // 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(
+ "PassStructNestedAlignmentStruct5BytesPackedMixed throwing on purpose!");
+ }
+
+ passStructNestedAlignmentStruct5BytesPackedMixed_a0 = a0;
+
+ final result =
+ passStructNestedAlignmentStruct5BytesPackedMixedCalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passStructNestedAlignmentStruct5BytesPackedMixedAfterCallback() {
+ final result =
+ passStructNestedAlignmentStruct5BytesPackedMixedCalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.approxEquals(6.0, result);
+}
+
+typedef PassStruct6BytesInlineArrayIntType = Double Function(
+ Struct6BytesInlineArrayInt);
+
+// Global variables to be able to test inputs after callback returned.
+Struct6BytesInlineArrayInt passStruct6BytesInlineArrayInt_a0 =
+ Struct6BytesInlineArrayInt();
+
+// Result variable also global, so we can delete it after the callback.
+double passStruct6BytesInlineArrayIntResult = 0.0;
+
+double passStruct6BytesInlineArrayIntCalculateResult() {
+ double result = 0;
+
+ result += passStruct6BytesInlineArrayInt_a0.a0[0].a0;
+ result += passStruct6BytesInlineArrayInt_a0.a0[0].a1;
+ result += passStruct6BytesInlineArrayInt_a0.a0[1].a0;
+ result += passStruct6BytesInlineArrayInt_a0.a0[1].a1;
+
+ passStruct6BytesInlineArrayIntResult = result;
+
+ return result;
+}
+
+/// Check alignment of packed struct array in non-packed struct.
+double passStruct6BytesInlineArrayInt(Struct6BytesInlineArrayInt a0) {
+ print("passStruct6BytesInlineArrayInt(${a0})");
+
+ // In legacy mode, possibly return null.
+ if (a0.a0[0].a0 == 84) {
+ print("returning null!");
+ return null;
+ }
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0[0].a0 == 42 || a0.a0[0].a0 == 84) {
+ print("throwing!");
+ throw Exception("PassStruct6BytesInlineArrayInt throwing on purpose!");
+ }
+
+ passStruct6BytesInlineArrayInt_a0 = a0;
+
+ final result = passStruct6BytesInlineArrayIntCalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passStruct6BytesInlineArrayIntAfterCallback() {
+ final result = passStruct6BytesInlineArrayIntCalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.approxEquals(2.0, result);
+}
+
+typedef PassStruct15BytesInlineArrayMixedType = Double Function(
+ Struct15BytesInlineArrayMixed);
+
+// Global variables to be able to test inputs after callback returned.
+Struct15BytesInlineArrayMixed passStruct15BytesInlineArrayMixed_a0 =
+ Struct15BytesInlineArrayMixed();
+
+// Result variable also global, so we can delete it after the callback.
+double passStruct15BytesInlineArrayMixedResult = 0.0;
+
+double passStruct15BytesInlineArrayMixedCalculateResult() {
+ double result = 0;
+
+ result += passStruct15BytesInlineArrayMixed_a0.a0[0].a0;
+ result += passStruct15BytesInlineArrayMixed_a0.a0[0].a1;
+ result += passStruct15BytesInlineArrayMixed_a0.a0[1].a0;
+ result += passStruct15BytesInlineArrayMixed_a0.a0[1].a1;
+ result += passStruct15BytesInlineArrayMixed_a0.a0[2].a0;
+ result += passStruct15BytesInlineArrayMixed_a0.a0[2].a1;
+
+ passStruct15BytesInlineArrayMixedResult = result;
+
+ return result;
+}
+
+/// Check alignment of packed struct array in non-packed struct.
+double passStruct15BytesInlineArrayMixed(Struct15BytesInlineArrayMixed a0) {
+ print("passStruct15BytesInlineArrayMixed(${a0})");
+
+ // In legacy mode, possibly return null.
+ if (a0.a0[0].a0 == 84) {
+ print("returning null!");
+ return null;
+ }
+
+ // In both nnbd and legacy mode, possibly throw.
+ if (a0.a0[0].a0 == 42 || a0.a0[0].a0 == 84) {
+ print("throwing!");
+ throw Exception("PassStruct15BytesInlineArrayMixed throwing on purpose!");
+ }
+
+ passStruct15BytesInlineArrayMixed_a0 = a0;
+
+ final result = passStruct15BytesInlineArrayMixedCalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void passStruct15BytesInlineArrayMixedAfterCallback() {
+ final result = passStruct15BytesInlineArrayMixedCalculateResult();
+
+ print("after callback result = $result");
+
+ Expect.approxEquals(3.0, result);
+}
+
typedef ReturnStruct1ByteIntType = Struct1ByteInt Function(Int8);
// Global variables to be able to test inputs after callback returned.
@@ -8571,6 +9229,194 @@
calloc.free(returnStruct1024BytesHomogeneousUint64ResultPointer);
}
+typedef ReturnStruct3BytesPackedIntType = Struct3BytesPackedInt Function(
+ Int8, Int16);
+
+// Global variables to be able to test inputs after callback returned.
+int returnStruct3BytesPackedInt_a0 = 0;
+int returnStruct3BytesPackedInt_a1 = 0;
+
+// Result variable also global, so we can delete it after the callback.
+Pointer<Struct3BytesPackedInt> returnStruct3BytesPackedIntResultPointer =
+ nullptr;
+
+Struct3BytesPackedInt returnStruct3BytesPackedIntCalculateResult() {
+ final resultPointer = calloc<Struct3BytesPackedInt>();
+ final result = resultPointer.ref;
+
+ result.a0 = returnStruct3BytesPackedInt_a0;
+ result.a1 = returnStruct3BytesPackedInt_a1;
+
+ returnStruct3BytesPackedIntResultPointer = resultPointer;
+
+ return result;
+}
+
+/// Small struct with mis-aligned member.
+Struct3BytesPackedInt returnStruct3BytesPackedInt(int a0, int a1) {
+ print("returnStruct3BytesPackedInt(${a0}, ${a1})");
+
+ // 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("ReturnStruct3BytesPackedInt throwing on purpose!");
+ }
+
+ returnStruct3BytesPackedInt_a0 = a0;
+ returnStruct3BytesPackedInt_a1 = a1;
+
+ final result = returnStruct3BytesPackedIntCalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void returnStruct3BytesPackedIntAfterCallback() {
+ calloc.free(returnStruct3BytesPackedIntResultPointer);
+
+ final result = returnStruct3BytesPackedIntCalculateResult();
+
+ print("after callback result = $result");
+
+ calloc.free(returnStruct3BytesPackedIntResultPointer);
+}
+
+typedef ReturnStruct8BytesPackedIntType = Struct8BytesPackedInt Function(
+ Uint8, Uint32, Uint8, Uint8, Uint8);
+
+// Global variables to be able to test inputs after callback returned.
+int returnStruct8BytesPackedInt_a0 = 0;
+int returnStruct8BytesPackedInt_a1 = 0;
+int returnStruct8BytesPackedInt_a2 = 0;
+int returnStruct8BytesPackedInt_a3 = 0;
+int returnStruct8BytesPackedInt_a4 = 0;
+
+// Result variable also global, so we can delete it after the callback.
+Pointer<Struct8BytesPackedInt> returnStruct8BytesPackedIntResultPointer =
+ nullptr;
+
+Struct8BytesPackedInt returnStruct8BytesPackedIntCalculateResult() {
+ final resultPointer = calloc<Struct8BytesPackedInt>();
+ final result = resultPointer.ref;
+
+ result.a0 = returnStruct8BytesPackedInt_a0;
+ result.a1 = returnStruct8BytesPackedInt_a1;
+ result.a2 = returnStruct8BytesPackedInt_a2;
+ result.a3 = returnStruct8BytesPackedInt_a3;
+ result.a4 = returnStruct8BytesPackedInt_a4;
+
+ returnStruct8BytesPackedIntResultPointer = resultPointer;
+
+ return result;
+}
+
+/// Struct with mis-aligned member.
+Struct8BytesPackedInt returnStruct8BytesPackedInt(
+ int a0, int a1, int a2, int a3, int a4) {
+ print("returnStruct8BytesPackedInt(${a0}, ${a1}, ${a2}, ${a3}, ${a4})");
+
+ // 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("ReturnStruct8BytesPackedInt throwing on purpose!");
+ }
+
+ returnStruct8BytesPackedInt_a0 = a0;
+ returnStruct8BytesPackedInt_a1 = a1;
+ returnStruct8BytesPackedInt_a2 = a2;
+ returnStruct8BytesPackedInt_a3 = a3;
+ returnStruct8BytesPackedInt_a4 = a4;
+
+ final result = returnStruct8BytesPackedIntCalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void returnStruct8BytesPackedIntAfterCallback() {
+ calloc.free(returnStruct8BytesPackedIntResultPointer);
+
+ final result = returnStruct8BytesPackedIntCalculateResult();
+
+ print("after callback result = $result");
+
+ calloc.free(returnStruct8BytesPackedIntResultPointer);
+}
+
+typedef ReturnStruct9BytesPackedMixedType = Struct9BytesPackedMixed Function(
+ Uint8, Double);
+
+// Global variables to be able to test inputs after callback returned.
+int returnStruct9BytesPackedMixed_a0 = 0;
+double returnStruct9BytesPackedMixed_a1 = 0.0;
+
+// Result variable also global, so we can delete it after the callback.
+Pointer<Struct9BytesPackedMixed> returnStruct9BytesPackedMixedResultPointer =
+ nullptr;
+
+Struct9BytesPackedMixed returnStruct9BytesPackedMixedCalculateResult() {
+ final resultPointer = calloc<Struct9BytesPackedMixed>();
+ final result = resultPointer.ref;
+
+ result.a0 = returnStruct9BytesPackedMixed_a0;
+ result.a1 = returnStruct9BytesPackedMixed_a1;
+
+ returnStruct9BytesPackedMixedResultPointer = resultPointer;
+
+ return result;
+}
+
+/// Struct with mis-aligned member.
+/// Tests backfilling of CPU and FPU registers.
+Struct9BytesPackedMixed returnStruct9BytesPackedMixed(int a0, double a1) {
+ print("returnStruct9BytesPackedMixed(${a0}, ${a1})");
+
+ // 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("ReturnStruct9BytesPackedMixed throwing on purpose!");
+ }
+
+ returnStruct9BytesPackedMixed_a0 = a0;
+ returnStruct9BytesPackedMixed_a1 = a1;
+
+ final result = returnStruct9BytesPackedMixedCalculateResult();
+
+ print("result = $result");
+
+ return result;
+}
+
+void returnStruct9BytesPackedMixedAfterCallback() {
+ calloc.free(returnStruct9BytesPackedMixedResultPointer);
+
+ final result = returnStruct9BytesPackedMixedCalculateResult();
+
+ print("after callback result = $result");
+
+ calloc.free(returnStruct9BytesPackedMixedResultPointer);
+}
+
typedef ReturnStructArgumentStruct1ByteIntType = Struct1ByteInt Function(
Struct1ByteInt);
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 91747de..48c7b59 100644
--- a/tests/ffi_2/function_structs_by_value_generated_test.dart
+++ b/tests/ffi_2/function_structs_by_value_generated_test.dart
@@ -70,6 +70,13 @@
testPassStructStruct16BytesMixed3x10();
testPassUint8Struct32BytesInlineArrayMultiDimensionalI();
testPassUint8Struct4BytesInlineArrayMultiDimensionalIn();
+ testPassStruct3BytesPackedIntx10();
+ testPassStruct8BytesPackedIntx10();
+ testPassStruct9BytesPackedMixedx10DoubleInt32();
+ testPassStruct5BytesPackedMixed();
+ testPassStructNestedAlignmentStruct5BytesPackedMixed();
+ testPassStruct6BytesInlineArrayInt();
+ testPassStruct15BytesInlineArrayMixed();
testReturnStruct1ByteInt();
testReturnStruct3BytesHomogeneousUint8();
testReturnStruct3BytesInt2ByteAligned();
@@ -92,6 +99,9 @@
testReturnStruct32BytesHomogeneousDouble();
testReturnStruct40BytesHomogeneousDouble();
testReturnStruct1024BytesHomogeneousUint64();
+ testReturnStruct3BytesPackedInt();
+ testReturnStruct8BytesPackedInt();
+ testReturnStruct9BytesPackedMixed();
testReturnStructArgumentStruct1ByteInt();
testReturnStructArgumentInt32x8Struct1ByteInt();
testReturnStructArgumentStruct8BytesHomogeneousFloat();
@@ -1186,6 +1196,93 @@
]})";
}
+@Packed(1)
+class Struct3BytesPackedInt extends Struct {
+ @Int8()
+ int a0;
+
+ @Int16()
+ int a1;
+
+ String toString() => "(${a0}, ${a1})";
+}
+
+@Packed(1)
+class Struct3BytesPackedIntMembersAligned extends Struct {
+ @Int8()
+ int a0;
+
+ @Int16()
+ int a1;
+
+ String toString() => "(${a0}, ${a1})";
+}
+
+@Packed(1)
+class Struct5BytesPackedMixed extends Struct {
+ @Float()
+ double a0;
+
+ @Uint8()
+ int a1;
+
+ String toString() => "(${a0}, ${a1})";
+}
+
+class StructNestedAlignmentStruct5BytesPackedMixed extends Struct {
+ @Uint8()
+ int a0;
+
+ Struct5BytesPackedMixed a1;
+
+ String toString() => "(${a0}, ${a1})";
+}
+
+class Struct6BytesInlineArrayInt extends Struct {
+ @Array(2)
+ Array<Struct3BytesPackedIntMembersAligned> a0;
+
+ String toString() => "(${[for (var i0 = 0; i0 < 2; i0 += 1) a0[i0]]})";
+}
+
+@Packed(1)
+class Struct8BytesPackedInt extends Struct {
+ @Uint8()
+ int a0;
+
+ @Uint32()
+ int a1;
+
+ @Uint8()
+ int a2;
+
+ @Uint8()
+ int a3;
+
+ @Uint8()
+ int a4;
+
+ String toString() => "(${a0}, ${a1}, ${a2}, ${a3}, ${a4})";
+}
+
+@Packed(1)
+class Struct9BytesPackedMixed extends Struct {
+ @Uint8()
+ int a0;
+
+ @Double()
+ double a1;
+
+ String toString() => "(${a0}, ${a1})";
+}
+
+class Struct15BytesInlineArrayMixed extends Struct {
+ @Array(3)
+ Array<Struct5BytesPackedMixed> a0;
+
+ String toString() => "(${[for (var i0 = 0; i0 < 3; i0 += 1) a0[i0]]})";
+}
+
final passStruct1ByteIntx10 = ffiTestFunctions.lookupFunction<
Int64 Function(
Struct1ByteInt,
@@ -5423,6 +5520,402 @@
calloc.free(a1Pointer);
}
+final passStruct3BytesPackedIntx10 = ffiTestFunctions.lookupFunction<
+ Int64 Function(
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt),
+ int Function(
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt,
+ Struct3BytesPackedInt)>("PassStruct3BytesPackedIntx10");
+
+/// Small struct with mis-aligned member.
+void testPassStruct3BytesPackedIntx10() {
+ final a0Pointer = calloc<Struct3BytesPackedInt>();
+ final Struct3BytesPackedInt a0 = a0Pointer.ref;
+ final a1Pointer = calloc<Struct3BytesPackedInt>();
+ final Struct3BytesPackedInt a1 = a1Pointer.ref;
+ final a2Pointer = calloc<Struct3BytesPackedInt>();
+ final Struct3BytesPackedInt a2 = a2Pointer.ref;
+ final a3Pointer = calloc<Struct3BytesPackedInt>();
+ final Struct3BytesPackedInt a3 = a3Pointer.ref;
+ final a4Pointer = calloc<Struct3BytesPackedInt>();
+ final Struct3BytesPackedInt a4 = a4Pointer.ref;
+ final a5Pointer = calloc<Struct3BytesPackedInt>();
+ final Struct3BytesPackedInt a5 = a5Pointer.ref;
+ final a6Pointer = calloc<Struct3BytesPackedInt>();
+ final Struct3BytesPackedInt a6 = a6Pointer.ref;
+ final a7Pointer = calloc<Struct3BytesPackedInt>();
+ final Struct3BytesPackedInt a7 = a7Pointer.ref;
+ final a8Pointer = calloc<Struct3BytesPackedInt>();
+ final Struct3BytesPackedInt a8 = a8Pointer.ref;
+ final a9Pointer = calloc<Struct3BytesPackedInt>();
+ final Struct3BytesPackedInt a9 = a9Pointer.ref;
+
+ a0.a0 = -1;
+ a0.a1 = 2;
+ a1.a0 = -3;
+ a1.a1 = 4;
+ a2.a0 = -5;
+ a2.a1 = 6;
+ a3.a0 = -7;
+ a3.a1 = 8;
+ a4.a0 = -9;
+ a4.a1 = 10;
+ a5.a0 = -11;
+ a5.a1 = 12;
+ a6.a0 = -13;
+ a6.a1 = 14;
+ a7.a0 = -15;
+ a7.a1 = 16;
+ a8.a0 = -17;
+ a8.a1 = 18;
+ a9.a0 = -19;
+ a9.a1 = 20;
+
+ final result =
+ passStruct3BytesPackedIntx10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+ print("result = $result");
+
+ Expect.equals(10, result);
+
+ calloc.free(a0Pointer);
+ calloc.free(a1Pointer);
+ calloc.free(a2Pointer);
+ calloc.free(a3Pointer);
+ calloc.free(a4Pointer);
+ calloc.free(a5Pointer);
+ calloc.free(a6Pointer);
+ calloc.free(a7Pointer);
+ calloc.free(a8Pointer);
+ calloc.free(a9Pointer);
+}
+
+final passStruct8BytesPackedIntx10 = ffiTestFunctions.lookupFunction<
+ Int64 Function(
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt),
+ int Function(
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt,
+ Struct8BytesPackedInt)>("PassStruct8BytesPackedIntx10");
+
+/// Struct with mis-aligned member.
+void testPassStruct8BytesPackedIntx10() {
+ final a0Pointer = calloc<Struct8BytesPackedInt>();
+ final Struct8BytesPackedInt a0 = a0Pointer.ref;
+ final a1Pointer = calloc<Struct8BytesPackedInt>();
+ final Struct8BytesPackedInt a1 = a1Pointer.ref;
+ final a2Pointer = calloc<Struct8BytesPackedInt>();
+ final Struct8BytesPackedInt a2 = a2Pointer.ref;
+ final a3Pointer = calloc<Struct8BytesPackedInt>();
+ final Struct8BytesPackedInt a3 = a3Pointer.ref;
+ final a4Pointer = calloc<Struct8BytesPackedInt>();
+ final Struct8BytesPackedInt a4 = a4Pointer.ref;
+ final a5Pointer = calloc<Struct8BytesPackedInt>();
+ final Struct8BytesPackedInt a5 = a5Pointer.ref;
+ final a6Pointer = calloc<Struct8BytesPackedInt>();
+ final Struct8BytesPackedInt a6 = a6Pointer.ref;
+ final a7Pointer = calloc<Struct8BytesPackedInt>();
+ final Struct8BytesPackedInt a7 = a7Pointer.ref;
+ final a8Pointer = calloc<Struct8BytesPackedInt>();
+ final Struct8BytesPackedInt a8 = a8Pointer.ref;
+ final a9Pointer = calloc<Struct8BytesPackedInt>();
+ final Struct8BytesPackedInt a9 = a9Pointer.ref;
+
+ a0.a0 = 1;
+ a0.a1 = 2;
+ a0.a2 = 3;
+ a0.a3 = 4;
+ a0.a4 = 5;
+ a1.a0 = 6;
+ a1.a1 = 7;
+ a1.a2 = 8;
+ a1.a3 = 9;
+ a1.a4 = 10;
+ a2.a0 = 11;
+ a2.a1 = 12;
+ a2.a2 = 13;
+ a2.a3 = 14;
+ a2.a4 = 15;
+ a3.a0 = 16;
+ a3.a1 = 17;
+ a3.a2 = 18;
+ a3.a3 = 19;
+ a3.a4 = 20;
+ a4.a0 = 21;
+ a4.a1 = 22;
+ a4.a2 = 23;
+ a4.a3 = 24;
+ a4.a4 = 25;
+ a5.a0 = 26;
+ a5.a1 = 27;
+ a5.a2 = 28;
+ a5.a3 = 29;
+ a5.a4 = 30;
+ a6.a0 = 31;
+ a6.a1 = 32;
+ a6.a2 = 33;
+ a6.a3 = 34;
+ a6.a4 = 35;
+ a7.a0 = 36;
+ a7.a1 = 37;
+ a7.a2 = 38;
+ a7.a3 = 39;
+ a7.a4 = 40;
+ a8.a0 = 41;
+ a8.a1 = 42;
+ a8.a2 = 43;
+ a8.a3 = 44;
+ a8.a4 = 45;
+ a9.a0 = 46;
+ a9.a1 = 47;
+ a9.a2 = 48;
+ a9.a3 = 49;
+ a9.a4 = 50;
+
+ final result =
+ passStruct8BytesPackedIntx10(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+
+ print("result = $result");
+
+ Expect.equals(1275, result);
+
+ calloc.free(a0Pointer);
+ calloc.free(a1Pointer);
+ calloc.free(a2Pointer);
+ calloc.free(a3Pointer);
+ calloc.free(a4Pointer);
+ calloc.free(a5Pointer);
+ calloc.free(a6Pointer);
+ calloc.free(a7Pointer);
+ calloc.free(a8Pointer);
+ calloc.free(a9Pointer);
+}
+
+final passStruct9BytesPackedMixedx10DoubleInt32 =
+ ffiTestFunctions.lookupFunction<
+ Double Function(
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Double,
+ Int32),
+ double Function(
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ Struct9BytesPackedMixed,
+ double,
+ int)>("PassStruct9BytesPackedMixedx10DoubleInt32");
+
+/// Struct with mis-aligned member.
+/// Tests backfilling of CPU and FPU registers.
+void testPassStruct9BytesPackedMixedx10DoubleInt32() {
+ final a0Pointer = calloc<Struct9BytesPackedMixed>();
+ final Struct9BytesPackedMixed a0 = a0Pointer.ref;
+ final a1Pointer = calloc<Struct9BytesPackedMixed>();
+ final Struct9BytesPackedMixed a1 = a1Pointer.ref;
+ final a2Pointer = calloc<Struct9BytesPackedMixed>();
+ final Struct9BytesPackedMixed a2 = a2Pointer.ref;
+ final a3Pointer = calloc<Struct9BytesPackedMixed>();
+ final Struct9BytesPackedMixed a3 = a3Pointer.ref;
+ final a4Pointer = calloc<Struct9BytesPackedMixed>();
+ final Struct9BytesPackedMixed a4 = a4Pointer.ref;
+ final a5Pointer = calloc<Struct9BytesPackedMixed>();
+ final Struct9BytesPackedMixed a5 = a5Pointer.ref;
+ final a6Pointer = calloc<Struct9BytesPackedMixed>();
+ final Struct9BytesPackedMixed a6 = a6Pointer.ref;
+ final a7Pointer = calloc<Struct9BytesPackedMixed>();
+ final Struct9BytesPackedMixed a7 = a7Pointer.ref;
+ final a8Pointer = calloc<Struct9BytesPackedMixed>();
+ final Struct9BytesPackedMixed a8 = a8Pointer.ref;
+ final a9Pointer = calloc<Struct9BytesPackedMixed>();
+ final Struct9BytesPackedMixed a9 = a9Pointer.ref;
+ double a10;
+ int a11;
+
+ a0.a0 = 1;
+ a0.a1 = 2.0;
+ a1.a0 = 3;
+ a1.a1 = 4.0;
+ a2.a0 = 5;
+ a2.a1 = 6.0;
+ a3.a0 = 7;
+ a3.a1 = 8.0;
+ a4.a0 = 9;
+ a4.a1 = 10.0;
+ a5.a0 = 11;
+ a5.a1 = 12.0;
+ a6.a0 = 13;
+ a6.a1 = 14.0;
+ a7.a0 = 15;
+ a7.a1 = 16.0;
+ a8.a0 = 17;
+ a8.a1 = 18.0;
+ a9.a0 = 19;
+ a9.a1 = 20.0;
+ a10 = -21.0;
+ a11 = 22;
+
+ final result = passStruct9BytesPackedMixedx10DoubleInt32(
+ a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11);
+
+ print("result = $result");
+
+ Expect.approxEquals(211.0, result);
+
+ calloc.free(a0Pointer);
+ calloc.free(a1Pointer);
+ calloc.free(a2Pointer);
+ calloc.free(a3Pointer);
+ calloc.free(a4Pointer);
+ calloc.free(a5Pointer);
+ calloc.free(a6Pointer);
+ calloc.free(a7Pointer);
+ calloc.free(a8Pointer);
+ calloc.free(a9Pointer);
+}
+
+final passStruct5BytesPackedMixed = ffiTestFunctions.lookupFunction<
+ Double Function(Struct5BytesPackedMixed),
+ double Function(Struct5BytesPackedMixed)>("PassStruct5BytesPackedMixed");
+
+/// This packed struct happens to have only aligned members.
+void testPassStruct5BytesPackedMixed() {
+ final a0Pointer = calloc<Struct5BytesPackedMixed>();
+ final Struct5BytesPackedMixed a0 = a0Pointer.ref;
+
+ a0.a0 = -1.0;
+ a0.a1 = 2;
+
+ final result = passStruct5BytesPackedMixed(a0);
+
+ print("result = $result");
+
+ Expect.approxEquals(1.0, result);
+
+ calloc.free(a0Pointer);
+}
+
+final passStructNestedAlignmentStruct5BytesPackedMixed =
+ ffiTestFunctions.lookupFunction<
+ Double Function(StructNestedAlignmentStruct5BytesPackedMixed),
+ double Function(StructNestedAlignmentStruct5BytesPackedMixed)>(
+ "PassStructNestedAlignmentStruct5BytesPackedMixed");
+
+/// Check alignment of packed struct in non-packed struct.
+void testPassStructNestedAlignmentStruct5BytesPackedMixed() {
+ final a0Pointer = calloc<StructNestedAlignmentStruct5BytesPackedMixed>();
+ final StructNestedAlignmentStruct5BytesPackedMixed a0 = a0Pointer.ref;
+
+ a0.a0 = 1;
+ a0.a1.a0 = 2.0;
+ a0.a1.a1 = 3;
+
+ final result = passStructNestedAlignmentStruct5BytesPackedMixed(a0);
+
+ print("result = $result");
+
+ Expect.approxEquals(6.0, result);
+
+ calloc.free(a0Pointer);
+}
+
+final passStruct6BytesInlineArrayInt = ffiTestFunctions.lookupFunction<
+ Double Function(Struct6BytesInlineArrayInt),
+ double Function(
+ Struct6BytesInlineArrayInt)>("PassStruct6BytesInlineArrayInt");
+
+/// Check alignment of packed struct array in non-packed struct.
+void testPassStruct6BytesInlineArrayInt() {
+ final a0Pointer = calloc<Struct6BytesInlineArrayInt>();
+ final Struct6BytesInlineArrayInt a0 = a0Pointer.ref;
+
+ a0.a0[0].a0 = -1;
+ a0.a0[0].a1 = 2;
+ a0.a0[1].a0 = -3;
+ a0.a0[1].a1 = 4;
+
+ final result = passStruct6BytesInlineArrayInt(a0);
+
+ print("result = $result");
+
+ Expect.approxEquals(2.0, result);
+
+ calloc.free(a0Pointer);
+}
+
+final passStruct15BytesInlineArrayMixed = ffiTestFunctions.lookupFunction<
+ Double Function(Struct15BytesInlineArrayMixed),
+ double Function(
+ Struct15BytesInlineArrayMixed)>("PassStruct15BytesInlineArrayMixed");
+
+/// Check alignment of packed struct array in non-packed struct.
+void testPassStruct15BytesInlineArrayMixed() {
+ final a0Pointer = calloc<Struct15BytesInlineArrayMixed>();
+ final Struct15BytesInlineArrayMixed a0 = a0Pointer.ref;
+
+ a0.a0[0].a0 = -1.0;
+ a0.a0[0].a1 = 2;
+ a0.a0[1].a0 = -3.0;
+ a0.a0[1].a1 = 4;
+ a0.a0[2].a0 = -5.0;
+ a0.a0[2].a1 = 6;
+
+ final result = passStruct15BytesInlineArrayMixed(a0);
+
+ print("result = $result");
+
+ Expect.approxEquals(3.0, result);
+
+ calloc.free(a0Pointer);
+}
+
final returnStruct1ByteInt = ffiTestFunctions.lookupFunction<
Struct1ByteInt Function(Int8),
Struct1ByteInt Function(int)>("ReturnStruct1ByteInt");
@@ -6841,6 +7334,78 @@
Expect.equals(a127, result.a127);
}
+final returnStruct3BytesPackedInt = ffiTestFunctions.lookupFunction<
+ Struct3BytesPackedInt Function(Int8, Int16),
+ Struct3BytesPackedInt Function(int, int)>("ReturnStruct3BytesPackedInt");
+
+/// Small struct with mis-aligned member.
+void testReturnStruct3BytesPackedInt() {
+ int a0;
+ int a1;
+
+ a0 = -1;
+ a1 = 2;
+
+ final result = returnStruct3BytesPackedInt(a0, a1);
+
+ print("result = $result");
+
+ Expect.equals(a0, result.a0);
+ Expect.equals(a1, result.a1);
+}
+
+final returnStruct8BytesPackedInt = ffiTestFunctions.lookupFunction<
+ Struct8BytesPackedInt Function(Uint8, Uint32, Uint8, Uint8, Uint8),
+ Struct8BytesPackedInt Function(
+ int, int, int, int, int)>("ReturnStruct8BytesPackedInt");
+
+/// Struct with mis-aligned member.
+void testReturnStruct8BytesPackedInt() {
+ int a0;
+ int a1;
+ int a2;
+ int a3;
+ int a4;
+
+ a0 = 1;
+ a1 = 2;
+ a2 = 3;
+ a3 = 4;
+ a4 = 5;
+
+ final result = returnStruct8BytesPackedInt(a0, a1, a2, a3, a4);
+
+ print("result = $result");
+
+ Expect.equals(a0, result.a0);
+ Expect.equals(a1, result.a1);
+ Expect.equals(a2, result.a2);
+ Expect.equals(a3, result.a3);
+ Expect.equals(a4, result.a4);
+}
+
+final returnStruct9BytesPackedMixed = ffiTestFunctions.lookupFunction<
+ Struct9BytesPackedMixed Function(Uint8, Double),
+ Struct9BytesPackedMixed Function(
+ int, double)>("ReturnStruct9BytesPackedMixed");
+
+/// Struct with mis-aligned member.
+/// Tests backfilling of CPU and FPU registers.
+void testReturnStruct9BytesPackedMixed() {
+ int a0;
+ double a1;
+
+ a0 = 1;
+ a1 = 2.0;
+
+ final result = returnStruct9BytesPackedMixed(a0, a1);
+
+ print("result = $result");
+
+ Expect.equals(a0, result.a0);
+ Expect.approxEquals(a1, result.a1);
+}
+
final returnStructArgumentStruct1ByteInt = ffiTestFunctions.lookupFunction<
Struct1ByteInt Function(Struct1ByteInt),
Struct1ByteInt Function(
diff --git a/tests/ffi_2/generator/c_types.dart b/tests/ffi_2/generator/c_types.dart
index e94b35b..df3b44b 100644
--- a/tests/ffi_2/generator/c_types.dart
+++ b/tests/ffi_2/generator/c_types.dart
@@ -155,20 +155,24 @@
class StructType extends CType {
final List<Member> members;
+ final int? packing;
+
/// To disambiguate same size structs.
final String suffix;
/// To override names.
final String overrideName;
- StructType(List<CType> memberTypes)
+ StructType(List<CType> memberTypes, {int? this.packing})
: this.members = generateMemberNames(memberTypes),
this.suffix = "",
this.overrideName = "";
- StructType.disambiguate(List<CType> memberTypes, this.suffix)
+ StructType.disambiguate(List<CType> memberTypes, this.suffix,
+ {int? this.packing})
: this.members = generateMemberNames(memberTypes),
this.overrideName = "";
- StructType.override(List<CType> memberTypes, this.overrideName)
+ StructType.override(List<CType> memberTypes, this.overrideName,
+ {int? this.packing})
: this.members = generateMemberNames(memberTypes),
this.suffix = "";
@@ -183,9 +187,19 @@
!memberTypes.map((e) => e.hasSize).contains(false) && !hasPadding;
int get size => memberTypes.fold(0, (int acc, e) => acc + e.size);
- /// Rough approximation, to not redo all ABI logic here.
- bool get hasPadding =>
- members.length < 2 ? false : members[0].type.size < members[1].type.size;
+ bool get hasPacking => packing != null;
+
+ bool get hasPadding {
+ if (members.length < 2) {
+ return false;
+ }
+ if (packing == 1) {
+ return false;
+ }
+
+ /// Rough approximation, to not redo all ABI logic here.
+ return members[0].type.size < members[1].type.size;
+ }
bool get hasNestedStructs =>
members.map((e) => e.type is StructType).contains(true);
@@ -217,6 +231,12 @@
if (hasSize) {
result += "${size}Byte" + (size != 1 ? "s" : "");
}
+ if (hasPacking) {
+ result += "Packed";
+ if (packing! > 1) {
+ result += "$packing";
+ }
+ }
if (hasNestedStructs) {
result += "Nested";
}
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 d0a22b0..1ad3c11 100644
--- a/tests/ffi_2/generator/structs_by_value_tests_configuration.dart
+++ b/tests/ffi_2/generator/structs_by_value_tests_configuration.dart
@@ -313,7 +313,7 @@
FunctionType(
[
uint8,
- struct8bytesInlineArrayMultiDimesional,
+ struct32bytesInlineArrayMultiDimesional,
uint8,
struct8bytesInlineArrayMultiDimesional,
uint8,
@@ -328,6 +328,36 @@
uint32,
"""
Test struct in multi dimensional inline array."""),
+ FunctionType(List.filled(10, struct3bytesPacked), int64, """
+Small struct with mis-aligned member."""),
+ FunctionType(List.filled(10, struct8bytesPacked), int64, """
+Struct with mis-aligned member."""),
+ FunctionType(
+ [...List.filled(10, struct9bytesPacked), double_, int32],
+ double_,
+ """
+Struct with mis-aligned member.
+Tests backfilling of CPU and FPU registers."""),
+ FunctionType(
+ [struct5bytesPacked],
+ double_,
+ """
+This packed struct happens to have only aligned members."""),
+ FunctionType(
+ [struct6bytesPacked],
+ double_,
+ """
+Check alignment of packed struct in non-packed struct."""),
+ FunctionType(
+ [struct6bytesPacked2],
+ double_,
+ """
+Check alignment of packed struct array in non-packed struct."""),
+ FunctionType(
+ [struct15bytesPacked],
+ double_,
+ """
+Check alignment of packed struct array in non-packed struct."""),
FunctionType(struct1byteInt.memberTypes, struct1byteInt, """
Smallest struct with data."""),
FunctionType(struct3bytesInt.memberTypes, struct3bytesInt, """
@@ -383,6 +413,13 @@
Return value too big to go in FPU registers on arm64."""),
FunctionType(struct1024bytesInt.memberTypes, struct1024bytesInt, """
Test 1kb struct."""),
+ FunctionType(struct3bytesPacked.memberTypes, struct3bytesPacked, """
+Small struct with mis-aligned member."""),
+ FunctionType(struct8bytesPacked.memberTypes, struct8bytesPacked, """
+Struct with mis-aligned member."""),
+ FunctionType(struct9bytesPacked.memberTypes, struct9bytesPacked, """
+Struct with mis-aligned member.
+Tests backfilling of CPU and FPU registers."""),
FunctionType(
[struct1byteInt],
struct1byteInt,
@@ -529,6 +566,14 @@
struct32bytesInlineArrayMultiDimesional,
struct64bytesInlineArrayMultiDimesional,
structMultiDimensionalStruct,
+ struct3bytesPacked,
+ struct3bytesPackedMembersAligned,
+ struct5bytesPacked,
+ struct6bytesPacked,
+ struct6bytesPacked2,
+ struct8bytesPacked,
+ struct9bytesPacked,
+ struct15bytesPacked,
];
final struct1byteInt = StructType([int8]);
@@ -672,3 +717,27 @@
final structMultiDimensionalStruct = StructType([
FixedLengthArrayType.multi(struct1byteInt, [2, 2])
]);
+
+final struct3bytesPacked = StructType([int8, int16], packing: 1);
+
+final struct3bytesPackedMembersAligned =
+ StructType.disambiguate([int8, int16], "MembersAligned", packing: 1);
+
+final struct5bytesPacked = StructType([float, uint8], packing: 1);
+
+/// The float in the nested struct is not aligned.
+final struct6bytesPacked = StructType([uint8, struct5bytesPacked]);
+
+/// The second element in the array has a nested misaligned int16.
+final struct6bytesPacked2 =
+ StructType([FixedLengthArrayType(struct3bytesPackedMembersAligned, 2)]);
+
+final struct8bytesPacked =
+ StructType([uint8, uint32, uint8, uint8, uint8], packing: 1);
+
+final struct9bytesPacked = StructType([uint8, double_], packing: 1);
+
+/// The float in the nested struct is aligned in the first element in the
+/// inline array, but not in the subsequent ones.
+final struct15bytesPacked =
+ StructType([FixedLengthArrayType(struct5bytesPacked, 3)]);
diff --git a/tests/ffi_2/generator/structs_by_value_tests_generator.dart b/tests/ffi_2/generator/structs_by_value_tests_generator.dart
index 51f51ab..c66dbea 100644
--- a/tests/ffi_2/generator/structs_by_value_tests_generator.dart
+++ b/tests/ffi_2/generator/structs_by_value_tests_generator.dart
@@ -455,6 +455,7 @@
extension on StructType {
String dartClass(bool nnbd) {
+ final packingAnnotation = hasPacking ? "@Packed(${packing})" : "";
String dartFields = "";
for (final member in members) {
dartFields += "${member.dartStructField(nnbd)}\n\n";
@@ -477,6 +478,7 @@
return "\$\{${m.name}\}";
}).join(", ");
return """
+ $packingAnnotation
class $name extends Struct {
$dartFields
@@ -486,14 +488,19 @@
}
String get cDefinition {
+ final packingPragmaPush =
+ hasPacking ? "#pragma pack(push, ${packing})" : "";
+ final packingPragmaPop = hasPacking ? "#pragma pack(pop)" : "";
String cFields = "";
for (final member in members) {
cFields += " ${member.cStructField}\n";
}
return """
+ $packingPragmaPush
struct $name {
$cFields
};
+ $packingPragmaPop
""";
}
diff --git a/tests/ffi_2/structs_packed_test.dart b/tests/ffi_2/structs_packed_test.dart
new file mode 100644
index 0000000..cec7703
--- /dev/null
+++ b/tests/ffi_2/structs_packed_test.dart
@@ -0,0 +1,40 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+//
+// SharedObjects=ffi_test_functions
+
+import 'dart:ffi';
+
+import "package:expect/expect.dart";
+
+import 'dylib_utils.dart';
+
+// Reuse struct definitions.
+import 'function_structs_by_value_generated_test.dart';
+
+void main() {
+ testSizeOfC();
+ testSizeOfDart();
+}
+
+final ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions");
+
+final sizeOfStruct3BytesPackedInt =
+ ffiTestFunctions.lookupFunction<Uint64 Function(), int Function()>(
+ "SizeOfStruct3BytesPackedInt");
+
+void testSizeOfC() {
+ Expect.equals(3, sizeOfStruct3BytesPackedInt());
+}
+
+void testSizeOfDart() {
+ // No packing needed to get to 3 bytes.
+ Expect.equals(3, sizeOf<Struct3BytesHomogeneousUint8>());
+
+ // Contents 3 bytes, but alignment forces it to be 4 bytes.
+ Expect.equals(4, sizeOf<Struct3BytesInt2ByteAligned>());
+
+ // Alignment gets the same content back to 3 bytes.
+ Expect.equals(3, sizeOf<Struct3BytesPackedInt>());
+}
diff --git a/tests/ffi_2/vmspecific_static_checks_test.dart b/tests/ffi_2/vmspecific_static_checks_test.dart
index 978fb2d..3960181 100644
--- a/tests/ffi_2/vmspecific_static_checks_test.dart
+++ b/tests/ffi_2/vmspecific_static_checks_test.dart
@@ -701,3 +701,58 @@
Pointer<Uint8> notEmpty;
}
+
+@Packed(1)
+class TestStruct1600 extends Struct {
+ Pointer<Uint8> notEmpty;
+}
+
+@Packed(1)
+@Packed(1) //# 1601: compile-time error
+class TestStruct1601 extends Struct {
+ Pointer<Uint8> notEmpty;
+}
+
+@Packed(3) //# 1602: compile-time error
+class TestStruct1602 extends Struct {
+ Pointer<Uint8> notEmpty;
+}
+
+class TestStruct1603 extends Struct {
+ Pointer<Uint8> notEmpty;
+}
+
+@Packed(1)
+class TestStruct1603Packed extends Struct {
+ Pointer<Uint8> notEmpty;
+
+ TestStruct1603 nestedNotPacked; //# 1603: compile-time error
+}
+
+@Packed(8)
+class TestStruct1604 extends Struct {
+ Pointer<Uint8> notEmpty;
+}
+
+@Packed(1)
+class TestStruct1604Packed extends Struct {
+ Pointer<Uint8> notEmpty;
+
+ TestStruct1604 nestedLooselyPacked; //# 1604: compile-time error
+}
+
+@Packed(1)
+class TestStruct1605Packed extends Struct {
+ Pointer<Uint8> notEmpty;
+
+ @Array(2) //# 1605: compile-time error
+ Array<TestStruct1603> nestedNotPacked; //# 1605: compile-time error
+}
+
+@Packed(1)
+class TestStruct1606Packed extends Struct {
+ Pointer<Uint8> notEmpty;
+
+ @Array(2) //# 1606: compile-time error
+ Array<TestStruct1604> nestedLooselyPacked; //# 1606: compile-time error
+}
diff --git a/tools/VERSION b/tools/VERSION
index aa81756..78dca80 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 13
PATCH 0
-PRERELEASE 152
+PRERELEASE 153
PRERELEASE_PATCH 0
\ No newline at end of file