[vm/ffi] Variable length inline arrays

Adds a new `@Array.variable()` to specify that the last element of
structs is a variable length inline array.

This CL does not add any checks for passing structs with variable
length inline arrays by value or directly calling them with
`AllocatorAlloc.call`. Instead, the implementation defaults to what
C does, allocate as if there are 0 elements in the variable length
inline array.

TEST=tests/ffi/*

CoreLibraryReviewExempt: VM only
Closes: https://github.com/dart-lang/sdk/issues/55964
Change-Id: I524d8a1d710b1a744b392e05fa884908c3ff1f12
Cq-Include-Trybots: dart/try:vm-aot-android-release-arm64c-try,vm-aot-android-release-arm_x64-try,vm-aot-asan-linux-release-x64-try,vm-aot-linux-debug-x64-try,vm-aot-linux-debug-x64c-try,vm-aot-mac-release-arm64-try,vm-aot-mac-release-x64-try,vm-aot-msan-linux-release-x64-try,vm-aot-obfuscate-linux-release-x64-try,vm-aot-optimization-level-linux-release-x64-try,vm-aot-tsan-linux-release-x64-try,vm-aot-ubsan-linux-release-x64-try,vm-aot-win-debug-arm64-try,vm-aot-win-debug-x64-try,vm-aot-win-debug-x64c-try,vm-appjit-linux-debug-x64-try,vm-asan-linux-release-arm64-try,vm-asan-linux-release-x64-try,vm-checked-mac-release-arm64-try,vm-eager-optimization-linux-release-ia32-try,vm-eager-optimization-linux-release-x64-try,vm-ffi-android-debug-arm-try,vm-ffi-android-debug-arm64c-try,vm-ffi-qemu-linux-release-arm-try,vm-ffi-qemu-linux-release-riscv64-try,vm-fuchsia-release-arm64-try,vm-fuchsia-release-x64-try,vm-linux-debug-ia32-try,vm-linux-debug-x64-try,vm-linux-debug-x64c-try,vm-mac-debug-arm64-try,vm-mac-debug-x64-try,vm-msan-linux-release-arm64-try,vm-msan-linux-release-x64-try,vm-reload-linux-debug-x64-try,vm-reload-rollback-linux-debug-x64-try,vm-tsan-linux-release-arm64-try,vm-tsan-linux-release-x64-try,vm-ubsan-linux-release-arm64-try,vm-ubsan-linux-release-x64-try,vm-win-debug-arm64-try,vm-win-debug-x64-try,vm-win-debug-x64c-try,vm-win-release-ia32-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/371960
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Daco Harkes <dacoharkes@google.com>
Reviewed-by: Lasse Nielsen <lrn@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
Daco Harkes
2024-08-06 07:50:49 +00:00
committed by Commit Queue
parent 2fe6ffe8c3
commit 965234ccbe
44 changed files with 1886 additions and 273 deletions
+1
View File
@@ -248,6 +248,7 @@ if (is_fuchsia) {
"tests/ffi/has_symbol_test.dart",
"tests/ffi/inline_array_multi_dimensional_test.dart",
"tests/ffi/inline_array_test.dart",
"tests/ffi/inline_array_variable_length_test.dart",
"tests/ffi/invoke_callback_after_suspension_test.dart",
"tests/ffi/isolate_local_function_callbacks_test.dart",
"tests/ffi/msan_test.dart",
@@ -6654,6 +6654,19 @@ Message _withArgumentsFfiStructGeneric(String string, String name) {
);
}
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Code<Null> codeFfiVariableLengthArrayNotLast =
messageFfiVariableLengthArrayNotLast;
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const MessageCode messageFfiVariableLengthArrayNotLast = const MessageCode(
"FfiVariableLengthArrayNotLast",
problemMessage:
r"""Variable length 'Array's must only occur as the last field of Structs.""",
correctionMessage:
r"""Try adjusting the arguments in the 'Array' annotation.""",
);
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Template<Message Function(String name)>
templateFieldAlreadyInitializedAtDeclaration =
@@ -1816,6 +1816,8 @@ FfiCode.PACKED_ANNOTATION_ALIGNMENT:
status: noFix
FfiCode.SIZE_ANNOTATION_DIMENSIONS:
status: noFix
FfiCode.VARIABLE_LENGTH_ARRAY_NOT_LAST:
status: noFix
FfiCode.SUBTYPE_OF_STRUCT_CLASS_IN_EXTENDS:
status: hasFix
FfiCode.SUBTYPE_OF_STRUCT_CLASS_IN_IMPLEMENTS:
@@ -496,6 +496,13 @@ class FfiCode extends AnalyzerErrorCode {
uniqueName: 'SUBTYPE_OF_STRUCT_CLASS_IN_WITH',
);
/// No parameters.
static const FfiCode VARIABLE_LENGTH_ARRAY_NOT_LAST = FfiCode(
'VARIABLE_LENGTH_ARRAY_NOT_LAST',
"Variable length 'Array's must only occur as the last field of Structs.",
correctionMessage: "Try adjusting the arguments in the 'Array' annotation.",
);
/// Initialize a newly created error code to have the given [name].
const FfiCode(
String name,
@@ -644,6 +644,7 @@ const List<ErrorCode> errorCodeValues = [
FfiCode.SUBTYPE_OF_STRUCT_CLASS_IN_EXTENDS,
FfiCode.SUBTYPE_OF_STRUCT_CLASS_IN_IMPLEMENTS,
FfiCode.SUBTYPE_OF_STRUCT_CLASS_IN_WITH,
FfiCode.VARIABLE_LENGTH_ARRAY_NOT_LAST,
HintCode.DEPRECATED_COLON_FOR_DEFAULT_VALUE,
HintCode.DEPRECATED_MEMBER_USE,
HintCode.DEPRECATED_MEMBER_USE_FROM_SAME_PACKAGE,
@@ -499,8 +499,14 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
arguments: ['T', 'Native'],
);
} else {
_checkFfiNativeField(errorNode, declarationElement, metadata,
ffiSignature, annotationValue);
_checkFfiNativeField(
errorNode,
declarationElement,
metadata,
ffiSignature,
annotationValue,
false,
);
}
}
@@ -515,6 +521,7 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
NodeList<Annotation> metadata,
DartType ffiSignature,
DartObject annotationValue,
bool allowVariableLength,
) {
DartType type;
@@ -574,7 +581,11 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
} else if (ffiSignature.isArray) {
// Array fields need an `@Array` size annotation.
_validateSizeOfAnnotation(
errorToken, metadata, ffiSignature.arrayDimensions);
errorToken,
metadata,
ffiSignature.arrayDimensions,
allowVariableLength,
);
} else if (ffiSignature.isHandle || ffiSignature.isNativeFunction) {
_errorReporter.atToken(
errorToken,
@@ -1466,7 +1477,19 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
);
}
var arrayDimensions = declaredType.arrayDimensions;
_validateSizeOfAnnotation(fieldType, annotations, arrayDimensions);
var fieldElement = node.fields.variables.first.declaredElement;
var lastElement = (fieldElement?.enclosingElement as ClassElement?)
?.fields
.reversed
.where((field) => !field.isStatic)
.firstOrNull;
var isLastField = fieldElement == lastElement;
_validateSizeOfAnnotation(
fieldType,
annotations,
arrayDimensions,
isLastField,
);
} else if (declaredType.isCompoundSubtype) {
var clazz = (declaredType as InterfaceType).element;
if (clazz.isEmptyStruct) {
@@ -1903,8 +1926,12 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
/// Validate that the [annotations] include exactly one size annotation. If
/// an error is produced that cannot be associated with an annotation,
/// associate it with the [errorEntity].
void _validateSizeOfAnnotation(SyntacticEntity errorEntity,
NodeList<Annotation> annotations, int arrayDimensions) {
void _validateSizeOfAnnotation(
SyntacticEntity errorEntity,
NodeList<Annotation> annotations,
int arrayDimensions,
bool allowVariableLength,
) {
var ffiSizeAnnotations =
annotations.where((annotation) => annotation.isArray).toList();
@@ -1928,7 +1955,8 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
// Check number of dimensions.
var annotation = ffiSizeAnnotations.first;
var dimensions = annotation.elementAnnotation?.arraySizeDimensions ?? [];
var (dimensions, variableLength) =
annotation.elementAnnotation?.arraySizeDimensions ?? (<int>[], false);
var annotationDimensions = dimensions.length;
if (annotationDimensions != arrayDimensions) {
_errorReporter.atNode(
@@ -1937,7 +1965,16 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
);
}
// Check dimensions are positive
if (variableLength) {
if (!allowVariableLength) {
_errorReporter.atNode(
annotation,
FfiCode.VARIABLE_LENGTH_ARRAY_NOT_LAST,
);
}
}
// Check dimensions are positive.
List<AstNode>? getArgumentNodes() {
var arguments = annotation.arguments?.arguments;
if (arguments != null && arguments.length == 1) {
@@ -1950,6 +1987,9 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
}
for (int i = 0; i < dimensions.length; i++) {
if (i == 0 && variableLength) {
continue; // First dimension is variable.
}
if (dimensions[i] <= 0) {
AstNode errorNode = annotation;
var argumentNodes = getArgumentNodes();
@@ -2032,10 +2072,13 @@ extension on Annotation {
}
extension on ElementAnnotation {
List<int> get arraySizeDimensions {
(List<int>, bool) get arraySizeDimensions {
assert(isArray);
var value = computeConstantValue();
var variableLength =
value?.getField('variableLength')?.toBoolValue() ?? false;
// Element of `@Array.multi([1, 2, 3])`.
var listField = value?.getField('dimensions');
if (listField != null) {
@@ -2045,7 +2088,7 @@ extension on ElementAnnotation {
.whereType<int>()
.toList();
if (listValues != null) {
return listValues;
return ([if (variableLength) 0, ...listValues], variableLength);
}
}
@@ -2064,7 +2107,7 @@ extension on ElementAnnotation {
result.add(dimensionValue);
}
}
return result;
return (result, variableLength);
}
bool get isArray {
@@ -895,6 +895,13 @@ final class Array<T extends NativeType> extends _Compound {
int dimension5]) = _ArraySize<T>;
const factory Array.multi(List<int> dimensions) = _ArraySize<T>.multi;
@Since('3.6')
const factory Array.variable() = _ArraySize<T>.variable;
@Since('3.6')
const factory Array.variableMulti(List<int> dimensions) =
_ArraySize<T>.variableMulti;
}
final class _ArraySize<T extends NativeType> implements Array<T> {
@@ -906,16 +913,44 @@ final class _ArraySize<T extends NativeType> implements Array<T> {
final List<int>? dimensions;
const _ArraySize(this.dimension1,
[this.dimension2, this.dimension3, this.dimension4, this.dimension5])
: dimensions = null;
final bool variableLength;
const _ArraySize(
this.dimension1, [
this.dimension2,
this.dimension3,
this.dimension4,
this.dimension5,
]) : dimensions = null,
variableLength = false;
const _ArraySize.multi(this.dimensions)
: dimension1 = null,
dimension2 = null,
dimension3 = null,
dimension4 = null,
dimension5 = null;
dimension5 = null,
variableLength = false;
static const variableLengthLength = 0;
const _ArraySize.variable([
this.dimension2,
this.dimension3,
this.dimension4,
this.dimension5,
]) : dimension1 = variableLengthLength,
dimensions = null,
variableLength = true;
const _ArraySize.variableMulti(List<int> nestedDimensions)
: dimensions = nestedDimensions,
dimension1 = null,
dimension2 = null,
dimension3 = null,
dimension4 = null,
dimension5 = null,
variableLength = true;
}
extension StructPointer<T extends Struct> on Pointer<T> {
+70
View File
@@ -20543,6 +20543,17 @@ FfiCode:
external Array<Uint8> a0;
}
```
If this is a variable length inline array, change the annotation to `Array.variable()`:
```dart
import 'dart:ffi';
final class MyStruct extends Struct {
@Array.variable()
external Array<Uint8> a0;
}
```
NON_SIZED_TYPE_ARGUMENT:
problemMessage: "The type '{1}' isn't a valid type argument for '{0}'. The type argument must be a native integer, 'Float', 'Double', 'Pointer', or subtype of 'Struct', 'Union', or 'AbiSpecificInteger'."
correctionMessage: "Try using a native integer, 'Float', 'Double', 'Pointer', or subtype of 'Struct', 'Union', or 'AbiSpecificInteger'."
@@ -20666,6 +20677,65 @@ FfiCode:
external Pointer<Uint8> notEmpty;
}
```
VARIABLE_LENGTH_ARRAY_NOT_LAST:
problemMessage: "Variable length 'Array's must only occur as the last field of Structs."
correctionMessage: "Try adjusting the arguments in the 'Array' annotation."
comment: No parameters.
documentation: |-
#### Description
The analyzer produces this diagnostic when a variable length inline `Array`
is not the last member of a `Struct`.
For more information about FFI, see [C interop using dart:ffi][ffi].
#### Example
The following code produces this diagnostic because the field `a0` has a
type with three nested arrays, but only two dimensions are given in the
`Array` annotation:
```dart
import 'dart:ffi';
final class C extends Struct {
[!@Array.variable()!]
external Array<Uint8> a0;
@Uint8()
external int a1;
}
```
#### Common fixes
Move the variable length inline `Array` to be the last field in the struct.
```dart
import 'dart:ffi';
final class C extends Struct {
@Uint8()
external int a1;
@Array.variable()
external Array<Uint8> a0;
}
```
If the inline array has a fixed size, annotate it with the size:
```dart
import 'dart:ffi';
final class C extends Struct {
@Array(10)
external Array<Uint8> a0;
@Uint8()
external int a1;
}
```
SIZE_ANNOTATION_DIMENSIONS:
problemMessage: "'Array's must have an 'Array' annotation that matches the dimensions."
correctionMessage: "Try adjusting the arguments in the 'Array' annotation."
@@ -15726,6 +15726,17 @@ final class MyStruct extends Struct {
}
```
If this is a variable length inline array, change the annotation to `Array.variable()`:
```dart
import 'dart:ffi';
final class MyStruct extends Struct {
@Array.variable()
external Array<Uint8> a0;
}
```
### non_sized_type_argument
_The type '{1}' isn't a valid type argument for '{0}'. The type argument must be
@@ -23674,6 +23685,65 @@ enum E {
}
```
### variable_length_array_not_last
_Variable length 'Array's must only occur as the last field of Structs._
#### Description
The analyzer produces this diagnostic when a variable length inline `Array`
is not the last member of a `Struct`.
For more information about FFI, see [C interop using dart:ffi][ffi].
#### Example
The following code produces this diagnostic because the field `a0` has a
type with three nested arrays, but only two dimensions are given in the
`Array` annotation:
```dart
import 'dart:ffi';
final class C extends Struct {
[!@Array.variable()!]
external Array<Uint8> a0;
@Uint8()
external int a1;
}
```
#### Common fixes
Move the variable length inline `Array` to be the last field in the struct.
```dart
import 'dart:ffi';
final class C extends Struct {
@Uint8()
external int a1;
@Array.variable()
external Array<Uint8> a0;
}
```
If the inline array has a fixed size, annotate it with the size:
```dart
import 'dart:ffi';
final class C extends Struct {
@Array(10)
external Array<Uint8> a0;
@Uint8()
external int a1;
}
```
### variable_pattern_keyword_in_declaration_context
_Variable patterns in declaration context can't specify 'var' or 'final'
+1
View File
@@ -422,6 +422,7 @@ FfiStructAnnotation/analyzerCode: Fail
FfiStructGeneric/analyzerCode: Fail
FfiTypeInvalid/analyzerCode: Fail
FfiTypeMismatch/analyzerCode: Fail
FfiVariableLengthArrayNotLast/analyzerCode: Fail
FieldInitializedOutsideDeclaringClass/part_wrapped_script1: Fail
FieldInitializedOutsideDeclaringClass/script1: Fail
FieldInitializerOutsideConstructor/part_wrapped_script1: Fail
+6
View File
@@ -5014,6 +5014,12 @@ FfiSizeAnnotationDimensions:
problemMessage: "Field '#name' must have an 'Array' annotation that matches the dimensions."
external: test/ffi_test.dart
FfiVariableLengthArrayNotLast:
# Used by dart:ffi
problemMessage: "Variable length 'Array's must only occur as the last field of Structs."
correctionMessage: "Try adjusting the arguments in the 'Array' annotation."
external: test/ffi_test.dart
FfiStructGeneric:
# Used by dart:ffi
problemMessage: "#string '#name' should not be generic."
@@ -17,6 +17,7 @@ annotate
api
apis
argument(s)
array's
assigning
augment
augmentation
@@ -9,9 +9,9 @@ final class StructInlineArray extends ffi::Struct {
synthetic constructor •() → self::StructInlineArray
: super ffi::Struct::•()
;
@#C3
@#C4
external get a0() → ffi::Array<ffi::Uint8>;
@#C3
@#C4
external set a0(synthesized ffi::Array<ffi::Uint8> #externalFieldValue) → void;
}
static method main() → dynamic {}
@@ -19,7 +19,8 @@ static method main() → dynamic {}
constants {
#C1 = 8
#C2 = null
#C3 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C1, dimension2:#C2, dimension3:#C2, dimension4:#C2, dimension5:#C2, dimensions:#C2}
#C3 = false
#C4 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C1, dimension2:#C2, dimension3:#C2, dimension4:#C2, dimension5:#C2, dimensions:#C2, variableLength:#C3}
}
@@ -9,9 +9,9 @@ final class StructInlineArray extends ffi::Struct {
synthetic constructor •() → self::StructInlineArray
: super ffi::Struct::•()
;
@#C3
@#C4
external get a0() → ffi::Array<ffi::Uint8>;
@#C3
@#C4
external set a0(synthesized ffi::Array<ffi::Uint8> #externalFieldValue) → void;
}
static method main() → dynamic {}
@@ -19,7 +19,8 @@ static method main() → dynamic {}
constants {
#C1 = 8
#C2 = null
#C3 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C1, dimension2:#C2, dimension3:#C2, dimension4:#C2, dimension5:#C2, dimensions:#C2}
#C3 = false
#C4 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C1, dimension2:#C2, dimension3:#C2, dimension4:#C2, dimension5:#C2, dimensions:#C2, variableLength:#C3}
}
@@ -18,6 +18,6 @@ static method main() → dynamic
Extra constant evaluation status:
Evaluated: ConstructorInvocation @ org-dartlang-testcase:///ffi_struct_inline_array.dart:10:4 -> InstanceConstant(const _ArraySize<NativeType>{_ArraySize.dimension1: 8, _ArraySize.dimension2: null, _ArraySize.dimension3: null, _ArraySize.dimension4: null, _ArraySize.dimension5: null, _ArraySize.dimensions: null})
Evaluated: ConstructorInvocation @ org-dartlang-testcase:///ffi_struct_inline_array.dart:10:4 -> InstanceConstant(const _ArraySize<NativeType>{_ArraySize.dimension1: 8, _ArraySize.dimension2: null, _ArraySize.dimension3: null, _ArraySize.dimension4: null, _ArraySize.dimension5: null, _ArraySize.dimensions: null})
Evaluated: ConstructorInvocation @ org-dartlang-testcase:///ffi_struct_inline_array.dart:10:4 -> InstanceConstant(const _ArraySize<NativeType>{_ArraySize.dimension1: 8, _ArraySize.dimension2: null, _ArraySize.dimension3: null, _ArraySize.dimension4: null, _ArraySize.dimension5: null, _ArraySize.dimensions: null, _ArraySize.variableLength: false})
Evaluated: ConstructorInvocation @ org-dartlang-testcase:///ffi_struct_inline_array.dart:10:4 -> InstanceConstant(const _ArraySize<NativeType>{_ArraySize.dimension1: 8, _ArraySize.dimension2: null, _ArraySize.dimension3: null, _ArraySize.dimension4: null, _ArraySize.dimension5: null, _ArraySize.dimensions: null, _ArraySize.variableLength: false})
Extra constant evaluation: evaluated: 2, effectively constant: 2
@@ -18,18 +18,18 @@ final class StructInlineArray extends ffi::Struct {
constructor #fromTypedData(synthesized typ::TypedData #typedData, synthesized core::int #offset, synthesized core::int #sizeInBytes) → self::StructInlineArray
: super ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C9
@#C10
get a0() → ffi::Array<ffi::Uint8>
return new ffi::Array::_<ffi::Uint8>(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::StructInlineArray::a0#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #C3, #C10);
@#C9
return new ffi::Array::_<ffi::Uint8>(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::StructInlineArray::a0#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #C3, #C11);
@#C10
set a0(synthesized ffi::Array<ffi::Uint8> #externalFieldValue) → void
return ffi::_memCopy(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::StructInlineArray::a0#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #externalFieldValue.{ffi::_Compound::_typedDataBase}{core::Object}, #externalFieldValue.{ffi::_Compound::_offsetInBytes}{core::int}, #C11.{core::List::[]}(ffi::_abi()){(core::int) → core::int});
@#C13
return ffi::_memCopy(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::StructInlineArray::a0#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #externalFieldValue.{ffi::_Compound::_typedDataBase}{core::Object}, #externalFieldValue.{ffi::_Compound::_offsetInBytes}{core::int}, #C12.{core::List::[]}(ffi::_abi()){(core::int) → core::int});
@#C14
static get a0#offsetOf() → core::int
return #C15.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
@#C13
return #C16.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
@#C14
static get #sizeOf() → core::int
return #C11.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
return #C12.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
}
static method main() → dynamic {}
@@ -42,13 +42,14 @@ constants {
#C6 = null
#C7 = ffi::_FfiStructLayout {fieldTypes:#C5, packing:#C6}
#C8 = core::pragma {name:#C1, options:#C7}
#C9 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C3, dimension2:#C6, dimension3:#C6, dimension4:#C6, dimension5:#C6, dimensions:#C6}
#C10 = <core::int>[]
#C11 = <core::int>[#C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3]
#C12 = "vm:prefer-inline"
#C13 = core::pragma {name:#C12, options:#C6}
#C14 = 0
#C15 = <core::int>[#C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14]
#C9 = false
#C10 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C3, dimension2:#C6, dimension3:#C6, dimension4:#C6, dimension5:#C6, dimensions:#C6, variableLength:#C9}
#C11 = <core::int>[]
#C12 = <core::int>[#C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3]
#C13 = "vm:prefer-inline"
#C14 = core::pragma {name:#C13, options:#C6}
#C15 = 0
#C16 = <core::int>[#C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15]
}
@@ -10,25 +10,26 @@ final class StructInlineArrayMultiDimensional extends ffi::Struct {
synthetic constructor •() → self::StructInlineArrayMultiDimensional
: super ffi::Struct::•()
;
@#C3
@#C4
external get a0() → ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>>;
@#C3
@#C4
external set a0(synthesized ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>> #externalFieldValue) → void;
}
static method main() → dynamic {
final ffi::Pointer<self::StructInlineArrayMultiDimensional> pointer = ffi::AllocatorAlloc|call<self::StructInlineArrayMultiDimensional>(#C4);
final ffi::Pointer<self::StructInlineArrayMultiDimensional> pointer = ffi::AllocatorAlloc|call<self::StructInlineArrayMultiDimensional>(#C5);
final self::StructInlineArrayMultiDimensional struct = ffi::StructPointer|get#ref<self::StructInlineArrayMultiDimensional>(pointer);
final ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>> array = struct.{self::StructInlineArrayMultiDimensional::a0}{ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>>};
final ffi::Array<ffi::Array<ffi::Uint8>> subArray = ffi::ArrayArray|[]<ffi::Array<ffi::Uint8>>(array, 0);
ffi::ArrayArray|[]=<ffi::Array<ffi::Uint8>>(array, 1, subArray);
#C4.{all::CallocAllocator::free}(pointer){(ffi::Pointer<ffi::NativeType>) → void};
#C5.{all::CallocAllocator::free}(pointer){(ffi::Pointer<ffi::NativeType>) → void};
}
constants {
#C1 = 2
#C2 = null
#C3 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C1, dimension2:#C1, dimension3:#C1, dimension4:#C2, dimension5:#C2, dimensions:#C2}
#C4 = all::CallocAllocator {}
#C3 = false
#C4 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C1, dimension2:#C1, dimension3:#C1, dimension4:#C2, dimension5:#C2, dimensions:#C2, variableLength:#C3}
#C5 = all::CallocAllocator {}
}
@@ -10,25 +10,26 @@ final class StructInlineArrayMultiDimensional extends ffi::Struct {
synthetic constructor •() → self::StructInlineArrayMultiDimensional
: super ffi::Struct::•()
;
@#C3
@#C4
external get a0() → ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>>;
@#C3
@#C4
external set a0(synthesized ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>> #externalFieldValue) → void;
}
static method main() → dynamic {
final ffi::Pointer<self::StructInlineArrayMultiDimensional> pointer = ffi::AllocatorAlloc|call<self::StructInlineArrayMultiDimensional>(#C4);
final ffi::Pointer<self::StructInlineArrayMultiDimensional> pointer = ffi::AllocatorAlloc|call<self::StructInlineArrayMultiDimensional>(#C5);
final self::StructInlineArrayMultiDimensional struct = ffi::StructPointer|get#ref<self::StructInlineArrayMultiDimensional>(pointer);
final ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>> array = struct.{self::StructInlineArrayMultiDimensional::a0}{ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>>};
final ffi::Array<ffi::Array<ffi::Uint8>> subArray = ffi::ArrayArray|[]<ffi::Array<ffi::Uint8>>(array, 0);
ffi::ArrayArray|[]=<ffi::Array<ffi::Uint8>>(array, 1, subArray);
#C4.{all::CallocAllocator::free}(pointer){(ffi::Pointer<ffi::NativeType>) → void};
#C5.{all::CallocAllocator::free}(pointer){(ffi::Pointer<ffi::NativeType>) → void};
}
constants {
#C1 = 2
#C2 = null
#C3 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C1, dimension2:#C1, dimension3:#C1, dimension4:#C2, dimension5:#C2, dimensions:#C2}
#C4 = all::CallocAllocator {}
#C3 = false
#C4 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C1, dimension2:#C1, dimension3:#C1, dimension4:#C2, dimension5:#C2, dimensions:#C2, variableLength:#C3}
#C5 = all::CallocAllocator {}
}
@@ -18,6 +18,6 @@ static method main() → dynamic
Extra constant evaluation status:
Evaluated: ConstructorInvocation @ org-dartlang-testcase:///ffi_struct_inline_array_multi_dimensional.dart:10:4 -> InstanceConstant(const _ArraySize<NativeType>{_ArraySize.dimension1: 2, _ArraySize.dimension2: 2, _ArraySize.dimension3: 2, _ArraySize.dimension4: null, _ArraySize.dimension5: null, _ArraySize.dimensions: null})
Evaluated: ConstructorInvocation @ org-dartlang-testcase:///ffi_struct_inline_array_multi_dimensional.dart:10:4 -> InstanceConstant(const _ArraySize<NativeType>{_ArraySize.dimension1: 2, _ArraySize.dimension2: 2, _ArraySize.dimension3: 2, _ArraySize.dimension4: null, _ArraySize.dimension5: null, _ArraySize.dimensions: null})
Evaluated: ConstructorInvocation @ org-dartlang-testcase:///ffi_struct_inline_array_multi_dimensional.dart:10:4 -> InstanceConstant(const _ArraySize<NativeType>{_ArraySize.dimension1: 2, _ArraySize.dimension2: 2, _ArraySize.dimension3: 2, _ArraySize.dimension4: null, _ArraySize.dimension5: null, _ArraySize.dimensions: null, _ArraySize.variableLength: false})
Evaluated: ConstructorInvocation @ org-dartlang-testcase:///ffi_struct_inline_array_multi_dimensional.dart:10:4 -> InstanceConstant(const _ArraySize<NativeType>{_ArraySize.dimension1: 2, _ArraySize.dimension2: 2, _ArraySize.dimension3: 2, _ArraySize.dimension4: null, _ArraySize.dimension5: null, _ArraySize.dimensions: null, _ArraySize.variableLength: false})
Extra constant evaluation: evaluated: 2, effectively constant: 2
@@ -19,28 +19,28 @@ final class StructInlineArrayMultiDimensional extends ffi::Struct {
constructor #fromTypedData(synthesized typ::TypedData #typedData, synthesized core::int #offset, synthesized core::int #sizeInBytes) → self::StructInlineArrayMultiDimensional
: super ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C10
@#C11
get a0() → ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>>
return new ffi::Array::_<ffi::Array<ffi::Array<ffi::Uint8>>>(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::StructInlineArrayMultiDimensional::a0#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #C9, #C11);
@#C10
return new ffi::Array::_<ffi::Array<ffi::Array<ffi::Uint8>>>(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::StructInlineArrayMultiDimensional::a0#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #C9, #C12);
@#C11
set a0(synthesized ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>> #externalFieldValue) → void
return ffi::_memCopy(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::StructInlineArrayMultiDimensional::a0#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #externalFieldValue.{ffi::_Compound::_typedDataBase}{core::Object}, #externalFieldValue.{ffi::_Compound::_offsetInBytes}{core::int}, #C12.{core::List::[]}(ffi::_abi()){(core::int) → core::int});
@#C14
return ffi::_memCopy(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::StructInlineArrayMultiDimensional::a0#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #externalFieldValue.{ffi::_Compound::_typedDataBase}{core::Object}, #externalFieldValue.{ffi::_Compound::_offsetInBytes}{core::int}, #C13.{core::List::[]}(ffi::_abi()){(core::int) → core::int});
@#C15
static get a0#offsetOf() → core::int
return #C16.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
@#C14
return #C17.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
@#C15
static get #sizeOf() → core::int
return #C12.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
return #C13.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
}
static method main() → dynamic {
final ffi::Pointer<self::StructInlineArrayMultiDimensional> pointer = #C17.{ffi::Allocator::allocate}<self::StructInlineArrayMultiDimensional>(self::StructInlineArrayMultiDimensional::#sizeOf){(core::int, {alignment: core::int?}) → ffi::Pointer<self::StructInlineArrayMultiDimensional>};
final self::StructInlineArrayMultiDimensional struct = new self::StructInlineArrayMultiDimensional::#fromTypedDataBase(pointer!, #C15);
final ffi::Pointer<self::StructInlineArrayMultiDimensional> pointer = #C18.{ffi::Allocator::allocate}<self::StructInlineArrayMultiDimensional>(self::StructInlineArrayMultiDimensional::#sizeOf){(core::int, {alignment: core::int?}) → ffi::Pointer<self::StructInlineArrayMultiDimensional>};
final self::StructInlineArrayMultiDimensional struct = new self::StructInlineArrayMultiDimensional::#fromTypedDataBase(pointer!, #C16);
final ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>> array = struct.{self::StructInlineArrayMultiDimensional::a0}{ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>>};
final ffi::Array<ffi::Array<ffi::Uint8>> subArray = block {
synthesized ffi::Array<dynamic> #array = array!;
synthesized core::int #index = 0!;
#array.{ffi::Array::_checkIndex}(#index){(core::int) → void};
synthesized core::int #singleElementSize = #C18;
synthesized core::int #singleElementSize = #C19;
synthesized core::int #elementSize = #singleElementSize.{core::num::*}(#array.{ffi::Array::_nestedDimensionsFlattened}{core::int}){(core::num) → core::num};
synthesized core::int #offset = #elementSize.{core::num::*}(#index){(core::num) → core::num};
} =>new ffi::Array::_<ffi::Array<ffi::Uint8>>(#array.{ffi::_Compound::_typedDataBase}{core::Object}, #array.{ffi::_Compound::_offsetInBytes}{core::int}.{core::num::+}(#offset){(core::num) → core::num}, #array.{ffi::Array::_nestedDimensionsFirst}{core::int}, #array.{ffi::Array::_nestedDimensionsRest}{core::List<core::int>});
@@ -48,12 +48,12 @@ static method main() → dynamic {
synthesized ffi::Array<dynamic> #array = array!;
synthesized core::int #index = 1!;
#array.{ffi::Array::_checkIndex}(#index){(core::int) → void};
synthesized core::int #singleElementSize = #C18;
synthesized core::int #singleElementSize = #C19;
synthesized core::int #elementSize = #singleElementSize.{core::num::*}(#array.{ffi::Array::_nestedDimensionsFlattened}{core::int}){(core::num) → core::num};
synthesized core::int #offset = #elementSize.{core::num::*}(#index){(core::num) → core::num};
synthesized ffi::Array<dynamic> #value = subArray!;
} =>ffi::_memCopy(#array.{ffi::_Compound::_typedDataBase}{core::Object}, #array.{ffi::_Compound::_offsetInBytes}{core::int}.{core::num::+}(#offset){(core::num) → core::num}, #value.{ffi::_Compound::_typedDataBase}{core::Object}, #value.{ffi::_Compound::_offsetInBytes}{core::int}, #elementSize);
#C17.{all::CallocAllocator::free}(pointer){(ffi::Pointer<ffi::NativeType>) → void};
#C18.{all::CallocAllocator::free}(pointer){(ffi::Pointer<ffi::NativeType>) → void};
}
constants {
@@ -66,15 +66,16 @@ constants {
#C7 = ffi::_FfiStructLayout {fieldTypes:#C5, packing:#C6}
#C8 = core::pragma {name:#C1, options:#C7}
#C9 = 2
#C10 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C9, dimension2:#C9, dimension3:#C9, dimension4:#C6, dimension5:#C6, dimensions:#C6}
#C11 = <core::int>[#C9, #C9]
#C12 = <core::int>[#C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3]
#C13 = "vm:prefer-inline"
#C14 = core::pragma {name:#C13, options:#C6}
#C15 = 0
#C16 = <core::int>[#C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15]
#C17 = all::CallocAllocator {}
#C18 = 1
#C10 = false
#C11 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C9, dimension2:#C9, dimension3:#C9, dimension4:#C6, dimension5:#C6, dimensions:#C6, variableLength:#C10}
#C12 = <core::int>[#C9, #C9]
#C13 = <core::int>[#C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3]
#C14 = "vm:prefer-inline"
#C15 = core::pragma {name:#C14, options:#C6}
#C16 = 0
#C17 = <core::int>[#C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16, #C16]
#C18 = all::CallocAllocator {}
#C19 = 1
}
Extra constant evaluation status:
@@ -13,6 +13,7 @@ import 'package:front_end/src/codes/cfe_codes.dart'
show
messageFfiLeafCallMustNotReturnHandle,
messageFfiLeafCallMustNotTakeHandle,
messageFfiVariableLengthArrayNotLast,
messageNonPositiveArrayDimensions,
templateFfiSizeAnnotation,
templateFfiSizeAnnotationDimensions,
@@ -223,6 +224,7 @@ class FfiTransformer extends Transformer {
final Field arraySizeDimension4Field;
final Field arraySizeDimension5Field;
final Field arraySizeDimensionsField;
final Field arraySizeVariableLengthField;
final Class pointerClass;
final Class compoundClass;
final Class structClass;
@@ -413,6 +415,8 @@ class FfiTransformer extends Transformer {
index.getField('dart:ffi', '_ArraySize', 'dimension5'),
arraySizeDimensionsField =
index.getField('dart:ffi', '_ArraySize', 'dimensions'),
arraySizeVariableLengthField =
index.getField('dart:ffi', '_ArraySize', 'variableLength'),
pointerClass = index.getClass('dart:ffi', 'Pointer'),
compoundClass = index.getClass('dart:ffi', '_Compound'),
structClass = index.getClass('dart:ffi', 'Struct'),
@@ -985,9 +989,14 @@ class FfiTransformer extends Transformer {
/// matching its [type].
///
/// Throws an [FfiStaticTypeError] otherwise.
List<int> ensureArraySizeAnnotation(Member node, DartType type) {
List<int> ensureArraySizeAnnotation(
Member node,
DartType type,
bool allowVariableLength,
) {
final sizeAnnotations = getArraySizeAnnotations(node);
List<int> dimensions;
bool variableLength;
var success = true;
if (sizeAnnotations.length == 1) {
@@ -996,7 +1005,8 @@ class FfiTransformer extends Transformer {
assert(singleElementType is InvalidType);
throw FfiStaticTypeError();
} else {
dimensions = sizeAnnotations.single;
dimensions = sizeAnnotations.single.$1;
variableLength = sizeAnnotations.single.$2;
if (arrayDimensions(type) != dimensions.length) {
diagnosticReporter.report(
templateFfiSizeAnnotationDimensions.withArguments(node.name.text),
@@ -1004,6 +1014,17 @@ class FfiTransformer extends Transformer {
node.name.text.length,
node.fileUri);
}
if (variableLength) {
if (!allowVariableLength) {
diagnosticReporter.report(
messageFfiVariableLengthArrayNotLast,
node.fileOffset,
node.name.text.length,
node.fileUri,
);
}
return dimensions; // Variable length single dimension.
}
for (var dimension in dimensions) {
if (dimension <= 0) {
diagnosticReporter.report(messageNonPositiveArrayDimensions,
@@ -1028,7 +1049,7 @@ class FfiTransformer extends Transformer {
return dimensions;
}
Iterable<List<int>> getArraySizeAnnotations(Member node) {
Iterable<(List<int>, bool)> getArraySizeAnnotations(Member node) {
return node.annotations
.whereType<ConstantExpression>()
.map((e) => e.constant)
@@ -1038,16 +1059,18 @@ class FfiTransformer extends Transformer {
}
/// Reads the dimensions from a constant instance of `_ArraySize`.
List<int> _arraySize(InstanceConstant constant) {
(List<int>, bool) _arraySize(InstanceConstant constant) {
final variableLength =
(constant.fieldValues[arraySizeVariableLengthField.fieldReference]
as BoolConstant)
.value;
final dimensions =
constant.fieldValues[arraySizeDimensionsField.fieldReference];
if (dimensions != null) {
if (dimensions is ListConstant) {
final result = dimensions.entries
.whereType<IntConstant>()
.map((e) => e.value)
.toList();
return result;
final result =
dimensions.entries.whereType<IntConstant>().map((e) => e.value);
return ([if (variableLength) 0, ...result], variableLength);
}
}
final dimensionFields = [
@@ -1062,7 +1085,7 @@ class FfiTransformer extends Transformer {
.whereType<IntConstant>()
.map((c) => c.value)
.toList();
return result;
return (result, variableLength);
}
/// Returns the number of dimensions of `Array`.
@@ -442,6 +442,7 @@ class _FfiDefinitionTransformer extends FfiTransformer {
bool success = true;
final membersWithAnnotations =
_compoundFieldMembers(node, includeSetters: false);
final lastField = membersWithAnnotations.lastOrNull;
for (final Member f in membersWithAnnotations) {
if (f is Field) {
if (f.initializer is! NullLiteral) {
@@ -485,7 +486,8 @@ class _FfiDefinitionTransformer extends FfiTransformer {
if (isArrayType(type)) {
try {
ensureNativeTypeValid(type, f, allowInlineArray: true);
ensureArraySizeAnnotation(f, type);
final isLastField = f == lastField;
ensureArraySizeAnnotation(f, type, isLastField);
} on FfiStaticTypeError {
// It's OK to swallow the exception because the diagnostics issued will
// cause compilation to fail. By continuing, we can report more
@@ -674,7 +676,7 @@ class _FfiDefinitionTransformer extends FfiTransformer {
if (isArrayType(dartType)) {
final sizeAnnotations = getArraySizeAnnotations(m).toList();
if (sizeAnnotations.length == 1) {
final arrayDimensions = sizeAnnotations.single;
final arrayDimensions = sizeAnnotations.single.$1;
if (this.arrayDimensions(dartType) == arrayDimensions.length) {
final elementType = arraySingleElementType(dartType);
if (elementType is! InterfaceType) {
@@ -764,7 +764,7 @@ class FfiNativeTransformer extends FfiTransformer {
// Array types must have an @Array annotation denoting its size.
if (isArrayType(ffiType)) {
final dimensions = ensureArraySizeAnnotation(node, ffiType);
final dimensions = ensureArraySizeAnnotation(node, ffiType, false);
return (
ffiType,
NativeTypeCfe.withoutLayout(this, dartType, arrayDimensions: dimensions)
@@ -60,31 +60,31 @@ final class WCharArrayStruct extends ffi::Struct {
constructor #fromTypedData(synthesized typ::TypedData #typedData, synthesized core::int #offset, synthesized core::int #sizeInBytes) → self::WCharArrayStruct
: super ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C83
@#C84
get a0() → ffi::Array<self::WChar>
return new ffi::Array::_<self::WChar>(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::WCharArrayStruct::a0#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #C78, #C84);
@#C83
return new ffi::Array::_<self::WChar>(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::WCharArrayStruct::a0#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #C78, #C85);
@#C84
set a0(synthesized ffi::Array<self::WChar> #externalFieldValue) → void
return ffi::_memCopy(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::WCharArrayStruct::a0#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #externalFieldValue.{ffi::_Compound::_typedDataBase}{core::Object}, #externalFieldValue.{ffi::_Compound::_offsetInBytes}{core::int}, #C87.{core::List::[]}(ffi::_abi()){(core::int) → core::int});
return ffi::_memCopy(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::WCharArrayStruct::a0#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #externalFieldValue.{ffi::_Compound::_typedDataBase}{core::Object}, #externalFieldValue.{ffi::_Compound::_offsetInBytes}{core::int}, #C88.{core::List::[]}(ffi::_abi()){(core::int) → core::int});
@#C67
static get a0#offsetOf() → core::int
return #C75.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
@#C67
static get #sizeOf() → core::int
return #C87.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
return #C88.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
}
class _DummyAllocator extends core::Object implements ffi::Allocator /*hasConstConstructor*/ {
const constructor •() → self::_DummyAllocator
: super core::Object::•()
;
@#C88
@#C89
method allocate<T extends ffi::NativeType>(core::int byteCount, {core::int? alignment = #C66}) → ffi::Pointer<self::_DummyAllocator::allocate::T> {
return ffi::Pointer::fromAddress<self::_DummyAllocator::allocate::T>(0);
}
@#C88
@#C89
method free(ffi::Pointer<ffi::NativeType> pointer) → void {}
}
static const field self::_DummyAllocator noAlloc = #C89;
static const field self::_DummyAllocator noAlloc = #C90;
static method main() → void {
self::testSizeOf();
self::testStoreLoad();
@@ -97,29 +97,29 @@ static method testSizeOf() → void {
core::print(size);
}
static method testStoreLoad() → void {
final ffi::Pointer<self::WChar> p = #C89.{ffi::Allocator::allocate}<self::WChar>(self::WChar::#sizeOf){(core::int, {alignment: core::int?}) → ffi::Pointer<self::WChar>};
final ffi::Pointer<self::WChar> p = #C90.{ffi::Allocator::allocate}<self::WChar>(self::WChar::#sizeOf){(core::int, {alignment: core::int?}) → ffi::Pointer<self::WChar>};
ffi::_storeAbiSpecificInt<self::WChar>(p, #C1, 10);
core::print(ffi::_loadAbiSpecificInt<self::WChar>(p, #C1));
#C89.{self::_DummyAllocator::free}(p){(ffi::Pointer<ffi::NativeType>) → void};
#C90.{self::_DummyAllocator::free}(p){(ffi::Pointer<ffi::NativeType>) → void};
}
static method testStoreLoadIndexed() → void {
final ffi::Pointer<self::WChar> p = #C89.{ffi::Allocator::allocate}<self::WChar>(2.{core::num::*}(self::WChar::#sizeOf){(core::num) → core::num}){(core::int, {alignment: core::int?}) → ffi::Pointer<self::WChar>};
final ffi::Pointer<self::WChar> p = #C90.{ffi::Allocator::allocate}<self::WChar>(2.{core::num::*}(self::WChar::#sizeOf){(core::num) → core::num}){(core::int, {alignment: core::int?}) → ffi::Pointer<self::WChar>};
ffi::_storeAbiSpecificIntAtIndex<self::WChar>(p, #C1, 0, 10);
ffi::_storeAbiSpecificIntAtIndex<self::WChar>(p, #C1, 1, 3);
core::print(ffi::_loadAbiSpecificIntAtIndex<self::WChar>(p, #C1, 0));
core::print(ffi::_loadAbiSpecificIntAtIndex<self::WChar>(p, #C1, 1));
#C89.{self::_DummyAllocator::free}(p){(ffi::Pointer<ffi::NativeType>) → void};
#C90.{self::_DummyAllocator::free}(p){(ffi::Pointer<ffi::NativeType>) → void};
}
static method testStruct() → void {
final ffi::Pointer<self::WCharStruct> p = #C89.{ffi::Allocator::allocate}<self::WCharStruct>(self::WCharStruct::#sizeOf){(core::int, {alignment: core::int?}) → ffi::Pointer<self::WCharStruct>};
final ffi::Pointer<self::WCharStruct> p = #C90.{ffi::Allocator::allocate}<self::WCharStruct>(self::WCharStruct::#sizeOf){(core::int, {alignment: core::int?}) → ffi::Pointer<self::WCharStruct>};
new self::WCharStruct::#fromTypedDataBase(p!, #C1).{self::WCharStruct::a0} = 1;
core::print(new self::WCharStruct::#fromTypedDataBase(p!, #C1).{self::WCharStruct::a0}{core::int});
new self::WCharStruct::#fromTypedDataBase(p!, #C1).{self::WCharStruct::a0} = 2;
core::print(new self::WCharStruct::#fromTypedDataBase(p!, #C1).{self::WCharStruct::a0}{core::int});
#C89.{self::_DummyAllocator::free}(p){(ffi::Pointer<ffi::NativeType>) → void};
#C90.{self::_DummyAllocator::free}(p){(ffi::Pointer<ffi::NativeType>) → void};
}
static method testInlineArray() → void {
final ffi::Pointer<self::WCharArrayStruct> p = #C89.{ffi::Allocator::allocate}<self::WCharArrayStruct>(self::WCharArrayStruct::#sizeOf){(core::int, {alignment: core::int?}) → ffi::Pointer<self::WCharArrayStruct>};
final ffi::Pointer<self::WCharArrayStruct> p = #C90.{ffi::Allocator::allocate}<self::WCharArrayStruct>(self::WCharArrayStruct::#sizeOf){(core::int, {alignment: core::int?}) → ffi::Pointer<self::WCharArrayStruct>};
final ffi::Array<self::WChar> array = new self::WCharArrayStruct::#fromTypedDataBase(p!, #C1).{self::WCharArrayStruct::a0}{ffi::Array<self::WChar>};
for (core::int i = 0; i.{core::num::<}(100){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::num) → core::int}) {
block {
@@ -135,7 +135,7 @@ static method testInlineArray() → void {
#array.{ffi::Array::_checkIndex}(#index){(core::int) → void};
} =>ffi::_loadAbiSpecificIntAtIndex<self::WChar>(#array.{ffi::_Compound::_typedDataBase}{core::Object}, #array.{ffi::_Compound::_offsetInBytes}{core::int}, #index));
}
#C89.{self::_DummyAllocator::free}(p){(ffi::Pointer<ffi::NativeType>) → void};
#C90.{self::_DummyAllocator::free}(p){(ffi::Pointer<ffi::NativeType>) → void};
}
constants {
#C1 = 0
@@ -220,11 +220,12 @@ constants {
#C80 = <core::Type>[#C79]
#C81 = ffi::_FfiStructLayout {fieldTypes:#C80, packing:#C66}
#C82 = core::pragma {name:#C69, options:#C81}
#C83 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C78, dimension2:#C66, dimension3:#C66, dimension4:#C66, dimension5:#C66, dimensions:#C66}
#C84 = <core::int>[]
#C85 = 400
#C86 = 200
#C87 = <core::int>[#C85, #C85, #C85, #C85, #C85, #C85, #C85, #C85, #C85, #C85, #C85, #C85, #C85, #C85, #C85, #C85, #C85, #C85, #C85, #C86, #C86, #C86]
#C88 = core::_Override {}
#C89 = self::_DummyAllocator {}
#C83 = false
#C84 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C78, dimension2:#C66, dimension3:#C66, dimension4:#C66, dimension5:#C66, dimensions:#C66, variableLength:#C83}
#C85 = <core::int>[]
#C86 = 400
#C87 = 200
#C88 = <core::int>[#C86, #C86, #C86, #C86, #C86, #C86, #C86, #C86, #C86, #C86, #C86, #C86, #C86, #C86, #C86, #C86, #C86, #C86, #C86, #C87, #C87, #C87]
#C89 = core::_Override {}
#C90 = self::_DummyAllocator {}
}
@@ -60,31 +60,31 @@ final class IncompleteArrayStruct extends ffi::Struct {
constructor #fromTypedData(synthesized typ::TypedData #typedData, synthesized core::int #offset, synthesized core::int #sizeInBytes) → self::IncompleteArrayStruct
: super ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C46
@#C47
get a0() → ffi::Array<self::Incomplete>
return new ffi::Array::_<self::Incomplete>(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::IncompleteArrayStruct::a0#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #C41, #C47);
@#C46
return new ffi::Array::_<self::Incomplete>(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::IncompleteArrayStruct::a0#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #C41, #C48);
@#C47
set a0(synthesized ffi::Array<self::Incomplete> #externalFieldValue) → void
return ffi::_memCopy(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::IncompleteArrayStruct::a0#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #externalFieldValue.{ffi::_Compound::_typedDataBase}{core::Object}, #externalFieldValue.{ffi::_Compound::_offsetInBytes}{core::int}, ffi::_checkAbiSpecificIntegerMapping<core::int>(#C49.{core::List::[]}(ffi::_abi()){(core::int) → core::int?}));
return ffi::_memCopy(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::IncompleteArrayStruct::a0#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #externalFieldValue.{ffi::_Compound::_typedDataBase}{core::Object}, #externalFieldValue.{ffi::_Compound::_offsetInBytes}{core::int}, ffi::_checkAbiSpecificIntegerMapping<core::int>(#C50.{core::List::[]}(ffi::_abi()){(core::int) → core::int?}));
@#C29
static get a0#offsetOf() → core::int
return #C38.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
@#C29
static get #sizeOf() → core::int
return ffi::_checkAbiSpecificIntegerMapping<core::int>(#C49.{core::List::[]}(ffi::_abi()){(core::int) → core::int?});
return ffi::_checkAbiSpecificIntegerMapping<core::int>(#C50.{core::List::[]}(ffi::_abi()){(core::int) → core::int?});
}
class _DummyAllocator extends core::Object implements ffi::Allocator /*hasConstConstructor*/ {
const constructor •() → self::_DummyAllocator
: super core::Object::•()
;
@#C50
@#C51
method allocate<T extends ffi::NativeType>(core::int byteCount, {core::int? alignment = #C23}) → ffi::Pointer<self::_DummyAllocator::allocate::T> {
return ffi::Pointer::fromAddress<self::_DummyAllocator::allocate::T>(0);
}
@#C50
@#C51
method free(ffi::Pointer<ffi::NativeType> pointer) → void {}
}
static const field self::_DummyAllocator noAlloc = #C51;
static const field self::_DummyAllocator noAlloc = #C52;
static method main() → void {
self::testSizeOf();
self::testStoreLoad();
@@ -97,29 +97,29 @@ static method testSizeOf() → void {
core::print(size);
}
static method testStoreLoad() → void {
final ffi::Pointer<self::Incomplete> p = #C51.{ffi::Allocator::allocate}<self::Incomplete>(self::Incomplete::#sizeOf){(core::int, {alignment: core::int?}) → ffi::Pointer<self::Incomplete>};
final ffi::Pointer<self::Incomplete> p = #C52.{ffi::Allocator::allocate}<self::Incomplete>(self::Incomplete::#sizeOf){(core::int, {alignment: core::int?}) → ffi::Pointer<self::Incomplete>};
ffi::_storeAbiSpecificInt<self::Incomplete>(p, #C4, 10);
core::print(ffi::_loadAbiSpecificInt<self::Incomplete>(p, #C4));
#C51.{self::_DummyAllocator::free}(p){(ffi::Pointer<ffi::NativeType>) → void};
#C52.{self::_DummyAllocator::free}(p){(ffi::Pointer<ffi::NativeType>) → void};
}
static method testStoreLoadIndexed() → void {
final ffi::Pointer<self::Incomplete> p = #C51.{ffi::Allocator::allocate}<self::Incomplete>(2.{core::num::*}(self::Incomplete::#sizeOf){(core::num) → core::num}){(core::int, {alignment: core::int?}) → ffi::Pointer<self::Incomplete>};
final ffi::Pointer<self::Incomplete> p = #C52.{ffi::Allocator::allocate}<self::Incomplete>(2.{core::num::*}(self::Incomplete::#sizeOf){(core::num) → core::num}){(core::int, {alignment: core::int?}) → ffi::Pointer<self::Incomplete>};
ffi::_storeAbiSpecificIntAtIndex<self::Incomplete>(p, #C4, 0, 10);
ffi::_storeAbiSpecificIntAtIndex<self::Incomplete>(p, #C4, 1, 3);
core::print(ffi::_loadAbiSpecificIntAtIndex<self::Incomplete>(p, #C4, 0));
core::print(ffi::_loadAbiSpecificIntAtIndex<self::Incomplete>(p, #C4, 1));
#C51.{self::_DummyAllocator::free}(p){(ffi::Pointer<ffi::NativeType>) → void};
#C52.{self::_DummyAllocator::free}(p){(ffi::Pointer<ffi::NativeType>) → void};
}
static method testStruct() → void {
final ffi::Pointer<self::IncompleteStruct> p = #C51.{ffi::Allocator::allocate}<self::IncompleteStruct>(self::IncompleteStruct::#sizeOf){(core::int, {alignment: core::int?}) → ffi::Pointer<self::IncompleteStruct>};
final ffi::Pointer<self::IncompleteStruct> p = #C52.{ffi::Allocator::allocate}<self::IncompleteStruct>(self::IncompleteStruct::#sizeOf){(core::int, {alignment: core::int?}) → ffi::Pointer<self::IncompleteStruct>};
new self::IncompleteStruct::#fromTypedDataBase(p!, #C4).{self::IncompleteStruct::a0} = 1;
core::print(new self::IncompleteStruct::#fromTypedDataBase(p!, #C4).{self::IncompleteStruct::a0}{core::int});
new self::IncompleteStruct::#fromTypedDataBase(p!, #C4).{self::IncompleteStruct::a0} = 2;
core::print(new self::IncompleteStruct::#fromTypedDataBase(p!, #C4).{self::IncompleteStruct::a0}{core::int});
#C51.{self::_DummyAllocator::free}(p){(ffi::Pointer<ffi::NativeType>) → void};
#C52.{self::_DummyAllocator::free}(p){(ffi::Pointer<ffi::NativeType>) → void};
}
static method testInlineArray() → void {
final ffi::Pointer<self::IncompleteArrayStruct> p = #C51.{ffi::Allocator::allocate}<self::IncompleteArrayStruct>(self::IncompleteArrayStruct::#sizeOf){(core::int, {alignment: core::int?}) → ffi::Pointer<self::IncompleteArrayStruct>};
final ffi::Pointer<self::IncompleteArrayStruct> p = #C52.{ffi::Allocator::allocate}<self::IncompleteArrayStruct>(self::IncompleteArrayStruct::#sizeOf){(core::int, {alignment: core::int?}) → ffi::Pointer<self::IncompleteArrayStruct>};
final ffi::Array<self::Incomplete> array = new self::IncompleteArrayStruct::#fromTypedDataBase(p!, #C4).{self::IncompleteArrayStruct::a0}{ffi::Array<self::Incomplete>};
for (core::int i = 0; i.{core::num::<}(100){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::num) → core::int}) {
block {
@@ -135,7 +135,7 @@ static method testInlineArray() → void {
#array.{ffi::Array::_checkIndex}(#index){(core::int) → void};
} =>ffi::_loadAbiSpecificIntAtIndex<self::Incomplete>(#array.{ffi::_Compound::_typedDataBase}{core::Object}, #array.{ffi::_Compound::_offsetInBytes}{core::int}, #index));
}
#C51.{self::_DummyAllocator::free}(p){(ffi::Pointer<ffi::NativeType>) → void};
#C52.{self::_DummyAllocator::free}(p){(ffi::Pointer<ffi::NativeType>) → void};
}
constants {
#C1 = 3
@@ -183,10 +183,11 @@ constants {
#C43 = <core::Type>[#C42]
#C44 = ffi::_FfiStructLayout {fieldTypes:#C43, packing:#C23}
#C45 = core::pragma {name:#C32, options:#C44}
#C46 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C41, dimension2:#C23, dimension3:#C23, dimension4:#C23, dimension5:#C23, dimensions:#C23}
#C47 = <core::int>[]
#C48 = 400
#C49 = <core::int?>[#C23, #C23, #C23, #C23, #C23, #C23, #C23, #C23, #C23, #C23, #C23, #C48, #C48, #C48, #C48, #C23, #C23, #C23, #C23, #C23, #C23, #C23]
#C50 = core::_Override {}
#C51 = self::_DummyAllocator {}
#C46 = false
#C47 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C41, dimension2:#C23, dimension3:#C23, dimension4:#C23, dimension5:#C23, dimensions:#C23, variableLength:#C46}
#C48 = <core::int>[]
#C49 = 400
#C50 = <core::int?>[#C23, #C23, #C23, #C23, #C23, #C23, #C23, #C23, #C23, #C23, #C23, #C49, #C49, #C49, #C49, #C23, #C23, #C23, #C23, #C23, #C23, #C23]
#C51 = core::_Override {}
#C52 = self::_DummyAllocator {}
}
@@ -17,20 +17,20 @@ final class MyStruct extends ffi::Struct {
constructor #fromTypedData(synthesized typ::TypedData #typedData, synthesized core::int #offset, synthesized core::int #sizeInBytes) → self::MyStruct
: super ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C9
@#C10
get a() → ffi::Array<ffi::Int8>
return new ffi::Array::_<ffi::Int8>(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyStruct::a#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #C3, #C10);
@#C9
return new ffi::Array::_<ffi::Int8>(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyStruct::a#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #C3, #C11);
@#C10
set a(synthesized ffi::Array<ffi::Int8> #externalFieldValue) → void
return ffi::_memCopy(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyStruct::a#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #externalFieldValue.{ffi::_Compound::_typedDataBase}{core::Object}, #externalFieldValue.{ffi::_Compound::_offsetInBytes}{core::int}, #C11.{core::List::[]}(ffi::_abi()){(core::int) → core::int});
@#C13
return ffi::_memCopy(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyStruct::a#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #externalFieldValue.{ffi::_Compound::_typedDataBase}{core::Object}, #externalFieldValue.{ffi::_Compound::_offsetInBytes}{core::int}, #C12.{core::List::[]}(ffi::_abi()){(core::int) → core::int});
@#C14
static get a#offsetOf() → core::int
return #C15.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
@#C13
return #C16.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
@#C14
static get #sizeOf() → core::int
return #C11.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
return #C12.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
}
@#C18
@#C19
final class MyUnion extends ffi::Union {
synthetic constructor •() → self::MyUnion
: super ffi::Union::•()
@@ -41,43 +41,43 @@ final class MyUnion extends ffi::Union {
constructor #fromTypedData(synthesized typ::TypedData #typedData, synthesized core::int #offset, synthesized core::int #sizeInBytes) → self::MyUnion
: super ffi::Union::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C19
@#C20
get a() → core::int
return ffi::_loadInt8(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyUnion::a#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num});
@#C19
@#C20
set a(synthesized core::int #externalFieldValue) → void
return ffi::_storeInt8(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyUnion::a#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #externalFieldValue);
@#C13
@#C14
static get a#offsetOf() → core::int
return #C15.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
@#C13
return #C16.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
@#C14
static get #sizeOf() → core::int
return #C21.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
return #C22.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
}
static method main() → void {
final self::MyStruct myStruct = new self::MyStruct::#fromTypedDataBase(typ::Uint8List::•(self::MyStruct::#sizeOf), #C14);
final self::MyStruct myStruct = new self::MyStruct::#fromTypedDataBase(typ::Uint8List::•(self::MyStruct::#sizeOf), #C15);
self::myNative#C(myStruct);
final self::MyUnion myUnion = new self::MyUnion::#fromTypedDataBase(typ::Uint8List::•(self::MyUnion::#sizeOf), #C14);
final self::MyUnion myUnion = new self::MyUnion::#fromTypedDataBase(typ::Uint8List::•(self::MyUnion::#sizeOf), #C15);
self::myNative2#C(myUnion);
self::myNative3#C(myStruct.{self::MyStruct::a}{ffi::Array<ffi::Int8>});
}
@#C27
@#C29
@#C28
@#C30
external static method myNative(ffi::Pointer<self::MyStruct> pointer) → void;
@#C32
@#C33
@#C34
external static method myNative2(ffi::Pointer<self::MyUnion> pointer) → void;
@#C36
@#C37
@#C38
external static method myNative3(ffi::Pointer<ffi::Int8> pointer) → void;
@#C27
@#C29
@#C28
@#C30
external static method myNative#C(ffi::_Compound pointer) → void;
@#C32
@#C33
@#C34
external static method myNative2#C(ffi::_Compound pointer) → void;
@#C36
@#C37
@#C38
external static method myNative3#C(ffi::_Compound pointer) → void;
constants {
#C1 = "vm:ffi:struct-fields"
@@ -88,33 +88,34 @@ constants {
#C6 = null
#C7 = ffi::_FfiStructLayout {fieldTypes:#C5, packing:#C6}
#C8 = core::pragma {name:#C1, options:#C7}
#C9 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C3, dimension2:#C6, dimension3:#C6, dimension4:#C6, dimension5:#C6, dimensions:#C6}
#C10 = <core::int>[]
#C11 = <core::int>[#C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3]
#C12 = "vm:prefer-inline"
#C13 = core::pragma {name:#C12, options:#C6}
#C14 = 0
#C15 = <core::int>[#C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14]
#C16 = <core::Type>[#C2]
#C17 = ffi::_FfiStructLayout {fieldTypes:#C16, packing:#C6}
#C18 = core::pragma {name:#C1, options:#C17}
#C19 = ffi::Int8 {}
#C20 = 1
#C21 = <core::int>[#C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20]
#C22 = "cfe:ffi:native-marker"
#C23 = "myNative"
#C24 = "#lib"
#C25 = true
#C26 = ffi::Native<(ffi::Pointer<self::MyStruct>) → ffi::Void> {symbol:#C23, assetId:#C24, isLeaf:#C25}
#C27 = core::pragma {name:#C22, options:#C26}
#C28 = "vm:ffi:native"
#C29 = core::pragma {name:#C28, options:#C26}
#C30 = "myNative2"
#C31 = ffi::Native<(ffi::Pointer<self::MyUnion>) → ffi::Void> {symbol:#C30, assetId:#C24, isLeaf:#C25}
#C32 = core::pragma {name:#C22, options:#C31}
#C33 = core::pragma {name:#C28, options:#C31}
#C34 = "myNative3"
#C35 = ffi::Native<(ffi::Pointer<ffi::Int8>) → ffi::Void> {symbol:#C34, assetId:#C24, isLeaf:#C25}
#C36 = core::pragma {name:#C22, options:#C35}
#C37 = core::pragma {name:#C28, options:#C35}
#C9 = false
#C10 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C3, dimension2:#C6, dimension3:#C6, dimension4:#C6, dimension5:#C6, dimensions:#C6, variableLength:#C9}
#C11 = <core::int>[]
#C12 = <core::int>[#C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3]
#C13 = "vm:prefer-inline"
#C14 = core::pragma {name:#C13, options:#C6}
#C15 = 0
#C16 = <core::int>[#C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15, #C15]
#C17 = <core::Type>[#C2]
#C18 = ffi::_FfiStructLayout {fieldTypes:#C17, packing:#C6}
#C19 = core::pragma {name:#C1, options:#C18}
#C20 = ffi::Int8 {}
#C21 = 1
#C22 = <core::int>[#C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21]
#C23 = "cfe:ffi:native-marker"
#C24 = "myNative"
#C25 = "#lib"
#C26 = true
#C27 = ffi::Native<(ffi::Pointer<self::MyStruct>) → ffi::Void> {symbol:#C24, assetId:#C25, isLeaf:#C26}
#C28 = core::pragma {name:#C23, options:#C27}
#C29 = "vm:ffi:native"
#C30 = core::pragma {name:#C29, options:#C27}
#C31 = "myNative2"
#C32 = ffi::Native<(ffi::Pointer<self::MyUnion>) → ffi::Void> {symbol:#C31, assetId:#C25, isLeaf:#C26}
#C33 = core::pragma {name:#C23, options:#C32}
#C34 = core::pragma {name:#C29, options:#C32}
#C35 = "myNative3"
#C36 = ffi::Native<(ffi::Pointer<ffi::Int8>) → ffi::Void> {symbol:#C35, assetId:#C25, isLeaf:#C26}
#C37 = core::pragma {name:#C23, options:#C36}
#C38 = core::pragma {name:#C29, options:#C36}
}
@@ -29,35 +29,35 @@ final class MyStruct extends ffi::Struct {
@#C11
set b(synthesized core::int #externalFieldValue) → void
return ffi::_storeInt8(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyStruct::b#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #externalFieldValue);
@#C12
@#C13
get array() → ffi::Array<ffi::Int8>
return new ffi::Array::_<ffi::Int8>(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyStruct::array#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #C3, #C13);
@#C12
return new ffi::Array::_<ffi::Int8>(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyStruct::array#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #C3, #C14);
@#C13
set array(synthesized ffi::Array<ffi::Int8> #externalFieldValue) → void
return ffi::_memCopy(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyStruct::array#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #externalFieldValue.{ffi::_Compound::_typedDataBase}{core::Object}, #externalFieldValue.{ffi::_Compound::_offsetInBytes}{core::int}, #C14.{core::List::[]}(ffi::_abi()){(core::int) → core::int});
@#C12
return ffi::_memCopy(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyStruct::array#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #externalFieldValue.{ffi::_Compound::_typedDataBase}{core::Object}, #externalFieldValue.{ffi::_Compound::_offsetInBytes}{core::int}, #C15.{core::List::[]}(ffi::_abi()){(core::int) → core::int});
@#C13
get array2() → ffi::Array<ffi::UnsignedLong>
return new ffi::Array::_<ffi::UnsignedLong>(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyStruct::array2#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #C3, #C13);
@#C12
return new ffi::Array::_<ffi::UnsignedLong>(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyStruct::array2#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #C3, #C14);
@#C13
set array2(synthesized ffi::Array<ffi::UnsignedLong> #externalFieldValue) → void
return ffi::_memCopy(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyStruct::array2#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #externalFieldValue.{ffi::_Compound::_typedDataBase}{core::Object}, #externalFieldValue.{ffi::_Compound::_offsetInBytes}{core::int}, #C17.{core::List::[]}(ffi::_abi()){(core::int) → core::int});
@#C19
return ffi::_memCopy(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyStruct::array2#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #externalFieldValue.{ffi::_Compound::_typedDataBase}{core::Object}, #externalFieldValue.{ffi::_Compound::_offsetInBytes}{core::int}, #C18.{core::List::[]}(ffi::_abi()){(core::int) → core::int});
@#C20
static get a#offsetOf() → core::int
return #C21.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
@#C19
return #C22.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
@#C20
static get b#offsetOf() → core::int
return #C23.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
@#C19
return #C24.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
@#C20
static get array#offsetOf() → core::int
return #C25.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
@#C19
return #C26.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
@#C20
static get array2#offsetOf() → core::int
return #C28.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
@#C19
return #C29.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
@#C20
static get #sizeOf() → core::int
return #C31.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
return #C32.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
}
@#C34
@#C35
final class MyUnion extends ffi::Union {
synthetic constructor •() → self::MyUnion
: super ffi::Union::•()
@@ -80,41 +80,41 @@ final class MyUnion extends ffi::Union {
@#C11
set b(synthesized core::int #externalFieldValue) → void
return ffi::_storeInt8(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyUnion::b#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #externalFieldValue);
@#C19
@#C20
static get a#offsetOf() → core::int
return #C21.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
@#C19
return #C22.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
@#C20
static get b#offsetOf() → core::int
return #C21.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
@#C19
return #C22.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
@#C20
static get #sizeOf() → core::int
return #C23.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
return #C24.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
}
static method main() → void {
final self::MyStruct myStruct = new self::MyStruct::#fromTypedDataBase(typ::Uint8List::•(self::MyStruct::#sizeOf), #C20);
final self::MyStruct myStruct = new self::MyStruct::#fromTypedDataBase(typ::Uint8List::•(self::MyStruct::#sizeOf), #C21);
self::myNative#CC( block {
synthesized ffi::_Compound pointer#value = myStruct;
} =>new ffi::_Compound::_fromTypedDataBase(pointer#value.{ffi::_Compound::_typedDataBase}{core::Object}, pointer#value.{ffi::_Compound::_offsetInBytes}{core::int}.{core::num::+}(self::MyStruct::a#offsetOf){(core::num) → core::num}), block {
synthesized ffi::_Compound pointer2#value = myStruct;
} =>new ffi::_Compound::_fromTypedDataBase(pointer2#value.{ffi::_Compound::_typedDataBase}{core::Object}, pointer2#value.{ffi::_Compound::_offsetInBytes}{core::int}.{core::num::+}(self::MyStruct::b#offsetOf){(core::num) → core::num}));
final self::MyUnion myUnion = new self::MyUnion::#fromTypedDataBase(typ::Uint8List::•(self::MyUnion::#sizeOf), #C20);
final self::MyUnion myUnion = new self::MyUnion::#fromTypedDataBase(typ::Uint8List::•(self::MyUnion::#sizeOf), #C21);
self::myNative#CC(myUnion, myUnion);
self::myNative#CC( block {
synthesized ffi::_Compound pointer#value = myStruct.{self::MyStruct::array}{ffi::Array<ffi::Int8>};
} =>new ffi::_Compound::_fromTypedDataBase(pointer#value.{ffi::_Compound::_typedDataBase}{core::Object}, pointer#value.{ffi::_Compound::_offsetInBytes}{core::int}.{core::num::+}(#C22.{core::num::*}(3){(core::num) → core::num}){(core::num) → core::num}), block {
} =>new ffi::_Compound::_fromTypedDataBase(pointer#value.{ffi::_Compound::_typedDataBase}{core::Object}, pointer#value.{ffi::_Compound::_offsetInBytes}{core::int}.{core::num::+}(#C23.{core::num::*}(3){(core::num) → core::num}){(core::num) → core::num}), block {
synthesized ffi::_Compound pointer2#value = myStruct.{self::MyStruct::array}{ffi::Array<ffi::Int8>};
} =>new ffi::_Compound::_fromTypedDataBase(pointer2#value.{ffi::_Compound::_typedDataBase}{core::Object}, pointer2#value.{ffi::_Compound::_offsetInBytes}{core::int}.{core::num::+}(#C22.{core::num::*}(4){(core::num) → core::num}){(core::num) → core::num}));
} =>new ffi::_Compound::_fromTypedDataBase(pointer2#value.{ffi::_Compound::_typedDataBase}{core::Object}, pointer2#value.{ffi::_Compound::_offsetInBytes}{core::int}.{core::num::+}(#C23.{core::num::*}(4){(core::num) → core::num}){(core::num) → core::num}));
self::myNative#CC( block {
synthesized ffi::_Compound pointer#value = myStruct.{self::MyStruct::array2}{ffi::Array<ffi::UnsignedLong>};
} =>new ffi::_Compound::_fromTypedDataBase(pointer#value.{ffi::_Compound::_typedDataBase}{core::Object}, pointer#value.{ffi::_Compound::_offsetInBytes}{core::int}.{core::num::+}(ffi::UnsignedLong::#sizeOf.{core::num::*}(3){(core::num) → core::num}){(core::num) → core::num}), block {
synthesized ffi::_Compound pointer2#value = myStruct.{self::MyStruct::array2}{ffi::Array<ffi::UnsignedLong>};
} =>new ffi::_Compound::_fromTypedDataBase(pointer2#value.{ffi::_Compound::_typedDataBase}{core::Object}, pointer2#value.{ffi::_Compound::_offsetInBytes}{core::int}.{core::num::+}(ffi::UnsignedLong::#sizeOf.{core::num::*}(4){(core::num) → core::num}){(core::num) → core::num}));
}
@#C40
@#C42
@#C41
@#C43
external static method myNative(ffi::Pointer<ffi::Int8> pointer, ffi::Pointer<ffi::Int8> pointer2) → void;
@#C40
@#C42
@#C41
@#C43
external static method myNative#CC(ffi::_Compound pointer, ffi::_Compound pointer2) → void;
constants {
#C1 = "vm:ffi:struct-fields"
@@ -128,35 +128,36 @@ constants {
#C9 = ffi::_FfiStructLayout {fieldTypes:#C7, packing:#C8}
#C10 = core::pragma {name:#C1, options:#C9}
#C11 = ffi::Int8 {}
#C12 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C3, dimension2:#C8, dimension3:#C8, dimension4:#C8, dimension5:#C8, dimensions:#C8}
#C13 = <core::int>[]
#C14 = <core::int>[#C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3]
#C15 = 40
#C16 = 80
#C17 = <core::int>[#C15, #C16, #C15, #C16, #C16, #C16, #C16, #C16, #C15, #C16, #C16, #C15, #C16, #C15, #C16, #C15, #C16, #C16, #C16, #C15, #C15, #C15]
#C18 = "vm:prefer-inline"
#C19 = core::pragma {name:#C18, options:#C8}
#C20 = 0
#C21 = <core::int>[#C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20, #C20]
#C22 = 1
#C23 = <core::int>[#C22, #C22, #C22, #C22, #C22, #C22, #C22, #C22, #C22, #C22, #C22, #C22, #C22, #C22, #C22, #C22, #C22, #C22, #C22, #C22, #C22, #C22]
#C24 = 2
#C25 = <core::int>[#C24, #C24, #C24, #C24, #C24, #C24, #C24, #C24, #C24, #C24, #C24, #C24, #C24, #C24, #C24, #C24, #C24, #C24, #C24, #C24, #C24, #C24]
#C26 = 12
#C27 = 16
#C28 = <core::int>[#C26, #C27, #C26, #C27, #C27, #C27, #C27, #C27, #C26, #C27, #C27, #C26, #C27, #C26, #C27, #C26, #C27, #C27, #C27, #C26, #C26, #C26]
#C29 = 52
#C30 = 96
#C31 = <core::int>[#C29, #C30, #C29, #C30, #C30, #C30, #C30, #C30, #C29, #C30, #C30, #C29, #C30, #C29, #C30, #C29, #C30, #C30, #C30, #C29, #C29, #C29]
#C32 = <core::Type>[#C2, #C2]
#C33 = ffi::_FfiStructLayout {fieldTypes:#C32, packing:#C8}
#C34 = core::pragma {name:#C1, options:#C33}
#C35 = "cfe:ffi:native-marker"
#C36 = "myNative"
#C37 = "#lib"
#C38 = true
#C39 = ffi::Native<(ffi::Pointer<ffi::Int8>, ffi::Pointer<ffi::Int8>) → ffi::Void> {symbol:#C36, assetId:#C37, isLeaf:#C38}
#C40 = core::pragma {name:#C35, options:#C39}
#C41 = "vm:ffi:native"
#C42 = core::pragma {name:#C41, options:#C39}
#C12 = false
#C13 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C3, dimension2:#C8, dimension3:#C8, dimension4:#C8, dimension5:#C8, dimensions:#C8, variableLength:#C12}
#C14 = <core::int>[]
#C15 = <core::int>[#C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3, #C3]
#C16 = 40
#C17 = 80
#C18 = <core::int>[#C16, #C17, #C16, #C17, #C17, #C17, #C17, #C17, #C16, #C17, #C17, #C16, #C17, #C16, #C17, #C16, #C17, #C17, #C17, #C16, #C16, #C16]
#C19 = "vm:prefer-inline"
#C20 = core::pragma {name:#C19, options:#C8}
#C21 = 0
#C22 = <core::int>[#C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21, #C21]
#C23 = 1
#C24 = <core::int>[#C23, #C23, #C23, #C23, #C23, #C23, #C23, #C23, #C23, #C23, #C23, #C23, #C23, #C23, #C23, #C23, #C23, #C23, #C23, #C23, #C23, #C23]
#C25 = 2
#C26 = <core::int>[#C25, #C25, #C25, #C25, #C25, #C25, #C25, #C25, #C25, #C25, #C25, #C25, #C25, #C25, #C25, #C25, #C25, #C25, #C25, #C25, #C25, #C25]
#C27 = 12
#C28 = 16
#C29 = <core::int>[#C27, #C28, #C27, #C28, #C28, #C28, #C28, #C28, #C27, #C28, #C28, #C27, #C28, #C27, #C28, #C27, #C28, #C28, #C28, #C27, #C27, #C27]
#C30 = 52
#C31 = 96
#C32 = <core::int>[#C30, #C31, #C30, #C31, #C31, #C31, #C31, #C31, #C30, #C31, #C31, #C30, #C31, #C30, #C31, #C30, #C31, #C31, #C31, #C30, #C30, #C30]
#C33 = <core::Type>[#C2, #C2]
#C34 = ffi::_FfiStructLayout {fieldTypes:#C33, packing:#C8}
#C35 = core::pragma {name:#C1, options:#C34}
#C36 = "cfe:ffi:native-marker"
#C37 = "myNative"
#C38 = "#lib"
#C39 = true
#C40 = ffi::Native<(ffi::Pointer<ffi::Int8>, ffi::Pointer<ffi::Int8>) → ffi::Void> {symbol:#C37, assetId:#C38, isLeaf:#C39}
#C41 = core::pragma {name:#C36, options:#C40}
#C42 = "vm:ffi:native"
#C43 = core::pragma {name:#C42, options:#C40}
}
@@ -184,7 +184,7 @@ constants {
#C39 = 1
#C40 = 2
#C41 = 3
#C42 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C39, dimension2:#C40, dimension3:#C41, dimension4:#C4, dimension5:#C4, dimensions:#C4}
#C42 = ffi::_ArraySize<ffi::NativeType> {dimension1:#C39, dimension2:#C40, dimension3:#C41, dimension4:#C4, dimension5:#C4, dimensions:#C4, variableLength:#C24}
#C43 = "manyNumbers"
#C44 = ffi::Native<ffi::Array<ffi::Array<ffi::Array<ffi::Double>>>> {symbol:#C43, assetId:#C23, isLeaf:#C24}
#C45 = core::pragma {name:#C21, options:#C44}
@@ -578,6 +578,26 @@ struct StructInlineArrayInt {
wchar_t a0[10];
};
struct StructInlineArrayVariable {
uint32_t a0;
uint8_t a1[];
};
struct StructInlineArrayVariableNested {
uint32_t a0;
uint8_t a1[][2][2];
};
struct StructInlineArrayVariableNestedDeep {
uint32_t a0;
uint8_t a1[][2][2][2][2][2][2];
};
struct StructInlineArrayVariableAlign {
uint8_t a0;
uint32_t a1[];
};
// Used for testing structs and unions by value.
// Smallest struct with data.
// 10 struct arguments will exhaust available registers.
@@ -4894,6 +4914,73 @@ PassPointerStruct12BytesHomogeneousInt32(Struct12BytesHomogeneousInt32* a0) {
return result;
}
// Used for testing structs and unions by value.
// Variable length array
DART_EXPORT int64_t
PassPointerStructInlineArrayVariable(StructInlineArrayVariable* a0) {
std::cout << "PassPointerStructInlineArrayVariable"
<< "((" << a0->a0 << ", [" << static_cast<int>(a0->a1[0]) << ", "
<< static_cast<int>(a0->a1[1]) << ", "
<< static_cast<int>(a0->a1[2]) << ", "
<< static_cast<int>(a0->a1[3]) << ", "
<< static_cast<int>(a0->a1[4]) << ", "
<< static_cast<int>(a0->a1[5]) << ", "
<< static_cast<int>(a0->a1[6]) << ", "
<< static_cast<int>(a0->a1[7]) << ", "
<< static_cast<int>(a0->a1[8]) << ", "
<< static_cast<int>(a0->a1[9]) << "]))"
<< "\n";
int64_t result = 0;
result += a0->a0;
result += a0->a1[0];
result += a0->a1[1];
result += a0->a1[2];
result += a0->a1[3];
result += a0->a1[4];
result += a0->a1[5];
result += a0->a1[6];
result += a0->a1[7];
result += a0->a1[8];
result += a0->a1[9];
std::cout << "result = " << result << "\n";
return result;
}
// Used for testing structs and unions by value.
// Variable length array with variable length element having more alignment than
// the rest of the struct.
DART_EXPORT int64_t
PassPointerStructInlineArrayVariableAlign(StructInlineArrayVariableAlign* a0) {
std::cout << "PassPointerStructInlineArrayVariableAlign"
<< "((" << static_cast<int>(a0->a0) << ", [" << a0->a1[0] << ", "
<< a0->a1[1] << ", " << a0->a1[2] << ", " << a0->a1[3] << ", "
<< a0->a1[4] << ", " << a0->a1[5] << ", " << a0->a1[6] << ", "
<< a0->a1[7] << ", " << a0->a1[8] << ", " << a0->a1[9] << "]))"
<< "\n";
int64_t result = 0;
result += a0->a0;
result += a0->a1[0];
result += a0->a1[1];
result += a0->a1[2];
result += a0->a1[3];
result += a0->a1[4];
result += a0->a1[5];
result += a0->a1[6];
result += a0->a1[7];
result += a0->a1[8];
result += a0->a1[9];
std::cout << "result = " << result << "\n";
return result;
}
// Used for testing structs and unions by value.
// Smallest struct with data.
DART_EXPORT Struct1ByteInt ReturnStruct1ByteInt(int8_t a0) {
@@ -12630,8 +12717,9 @@ DART_EXPORT intptr_t TestPassInt64x7Struct12BytesHomogeneousInt32(
DART_EXPORT intptr_t TestPassPointerStruct12BytesHomogeneousInt32(
// NOLINTNEXTLINE(whitespace/parens)
int64_t (*f)(Struct12BytesHomogeneousInt32* a0)) {
Struct12BytesHomogeneousInt32 a0_value = {};
Struct12BytesHomogeneousInt32* a0 = &a0_value;
Struct12BytesHomogeneousInt32* a0 =
static_cast<Struct12BytesHomogeneousInt32*>(
calloc(1, sizeof(Struct12BytesHomogeneousInt32)));
a0->a0 = -1;
a0->a1 = 2;
@@ -12661,6 +12749,120 @@ DART_EXPORT intptr_t TestPassPointerStruct12BytesHomogeneousInt32(
CHECK_EQ(0, result);
free(a0);
return 0;
}
// Used for testing structs and unions by value.
// Variable length array
DART_EXPORT intptr_t TestPassPointerStructInlineArrayVariable(
// NOLINTNEXTLINE(whitespace/parens)
int64_t (*f)(StructInlineArrayVariable* a0)) {
StructInlineArrayVariable* a0 = static_cast<StructInlineArrayVariable*>(
calloc(1, sizeof(StructInlineArrayVariable) + 10 * sizeof(uint8_t)));
a0->a0 = 1;
a0->a1[0] = 2;
a0->a1[1] = 3;
a0->a1[2] = 4;
a0->a1[3] = 5;
a0->a1[4] = 6;
a0->a1[5] = 7;
a0->a1[6] = 8;
a0->a1[7] = 9;
a0->a1[8] = 10;
a0->a1[9] = 11;
std::cout << "Calling TestPassPointerStructInlineArrayVariable("
<< "((" << a0->a0 << ", [" << static_cast<int>(a0->a1[0]) << ", "
<< static_cast<int>(a0->a1[1]) << ", "
<< static_cast<int>(a0->a1[2]) << ", "
<< static_cast<int>(a0->a1[3]) << ", "
<< static_cast<int>(a0->a1[4]) << ", "
<< static_cast<int>(a0->a1[5]) << ", "
<< static_cast<int>(a0->a1[6]) << ", "
<< static_cast<int>(a0->a1[7]) << ", "
<< static_cast<int>(a0->a1[8]) << ", "
<< static_cast<int>(a0->a1[9]) << "]))"
<< ")\n";
int64_t result = f(a0);
std::cout << "result = " << result << "\n";
CHECK_EQ(66, result);
// Pass argument that will make the Dart callback throw.
a0->a0 = 42;
result = f(a0);
CHECK_EQ(0, result);
// Pass argument that will make the Dart callback return null.
a0->a0 = 84;
result = f(a0);
CHECK_EQ(0, result);
free(a0);
return 0;
}
// Used for testing structs and unions by value.
// Variable length array with variable length element having more alignment than
// the rest of the struct.
DART_EXPORT intptr_t TestPassPointerStructInlineArrayVariableAlign(
// NOLINTNEXTLINE(whitespace/parens)
int64_t (*f)(StructInlineArrayVariableAlign* a0)) {
StructInlineArrayVariableAlign* a0 =
static_cast<StructInlineArrayVariableAlign*>(calloc(
1, sizeof(StructInlineArrayVariableAlign) + 10 * sizeof(uint32_t)));
a0->a0 = 1;
a0->a1[0] = 2;
a0->a1[1] = 3;
a0->a1[2] = 4;
a0->a1[3] = 5;
a0->a1[4] = 6;
a0->a1[5] = 7;
a0->a1[6] = 8;
a0->a1[7] = 9;
a0->a1[8] = 10;
a0->a1[9] = 11;
std::cout << "Calling TestPassPointerStructInlineArrayVariableAlign("
<< "((" << static_cast<int>(a0->a0) << ", [" << a0->a1[0] << ", "
<< a0->a1[1] << ", " << a0->a1[2] << ", " << a0->a1[3] << ", "
<< a0->a1[4] << ", " << a0->a1[5] << ", " << a0->a1[6] << ", "
<< a0->a1[7] << ", " << a0->a1[8] << ", " << a0->a1[9] << "]))"
<< ")\n";
int64_t result = f(a0);
std::cout << "result = " << result << "\n";
CHECK_EQ(66, result);
// Pass argument that will make the Dart callback throw.
a0->a0 = 42;
result = f(a0);
CHECK_EQ(0, result);
// Pass argument that will make the Dart callback return null.
a0->a0 = 84;
result = f(a0);
CHECK_EQ(0, result);
free(a0);
return 0;
}
@@ -21287,8 +21489,9 @@ DART_EXPORT void TestAsyncPassInt64x7Struct12BytesHomogeneousInt32(
DART_EXPORT void TestAsyncPassPointerStruct12BytesHomogeneousInt32(
// NOLINTNEXTLINE(whitespace/parens)
void (*f)(Struct12BytesHomogeneousInt32* a0)) {
Struct12BytesHomogeneousInt32 a0_value = {};
Struct12BytesHomogeneousInt32* a0 = &a0_value;
Struct12BytesHomogeneousInt32* a0 =
static_cast<Struct12BytesHomogeneousInt32*>(
calloc(1, sizeof(Struct12BytesHomogeneousInt32)));
a0->a0 = -1;
a0->a1 = 2;
@@ -21299,6 +21502,80 @@ DART_EXPORT void TestAsyncPassPointerStruct12BytesHomogeneousInt32(
<< ")\n";
f(a0);
free(a0);
}
// Used for testing structs and unions by value.
// Variable length array
DART_EXPORT void TestAsyncPassPointerStructInlineArrayVariable(
// NOLINTNEXTLINE(whitespace/parens)
void (*f)(StructInlineArrayVariable* a0)) {
StructInlineArrayVariable* a0 = static_cast<StructInlineArrayVariable*>(
calloc(1, sizeof(StructInlineArrayVariable) + 10 * sizeof(uint8_t)));
a0->a0 = 1;
a0->a1[0] = 2;
a0->a1[1] = 3;
a0->a1[2] = 4;
a0->a1[3] = 5;
a0->a1[4] = 6;
a0->a1[5] = 7;
a0->a1[6] = 8;
a0->a1[7] = 9;
a0->a1[8] = 10;
a0->a1[9] = 11;
std::cout << "Calling TestAsyncPassPointerStructInlineArrayVariable("
<< "((" << a0->a0 << ", [" << static_cast<int>(a0->a1[0]) << ", "
<< static_cast<int>(a0->a1[1]) << ", "
<< static_cast<int>(a0->a1[2]) << ", "
<< static_cast<int>(a0->a1[3]) << ", "
<< static_cast<int>(a0->a1[4]) << ", "
<< static_cast<int>(a0->a1[5]) << ", "
<< static_cast<int>(a0->a1[6]) << ", "
<< static_cast<int>(a0->a1[7]) << ", "
<< static_cast<int>(a0->a1[8]) << ", "
<< static_cast<int>(a0->a1[9]) << "]))"
<< ")\n";
f(a0);
free(a0);
}
// Used for testing structs and unions by value.
// Variable length array with variable length element having more alignment than
// the rest of the struct.
DART_EXPORT void TestAsyncPassPointerStructInlineArrayVariableAlign(
// NOLINTNEXTLINE(whitespace/parens)
void (*f)(StructInlineArrayVariableAlign* a0)) {
StructInlineArrayVariableAlign* a0 =
static_cast<StructInlineArrayVariableAlign*>(calloc(
1, sizeof(StructInlineArrayVariableAlign) + 10 * sizeof(uint32_t)));
a0->a0 = 1;
a0->a1[0] = 2;
a0->a1[1] = 3;
a0->a1[2] = 4;
a0->a1[3] = 5;
a0->a1[4] = 6;
a0->a1[5] = 7;
a0->a1[6] = 8;
a0->a1[7] = 9;
a0->a1[8] = 10;
a0->a1[9] = 11;
std::cout << "Calling TestAsyncPassPointerStructInlineArrayVariableAlign("
<< "((" << static_cast<int>(a0->a0) << ", [" << a0->a1[0] << ", "
<< a0->a1[1] << ", " << a0->a1[2] << ", " << a0->a1[3] << ", "
<< a0->a1[4] << ", " << a0->a1[5] << ", " << a0->a1[6] << ", "
<< a0->a1[7] << ", " << a0->a1[8] << ", " << a0->a1[9] << "]))"
<< ")\n";
f(a0);
free(a0);
}
// Used for testing structs and unions by value.
+5
View File
@@ -344,7 +344,12 @@ final class Array<T extends NativeType> extends _Compound {
List<int> get _nestedDimensionsRest =>
_nestedDimensionsRestCache ??= _nestedDimensions.sublist(1);
static const _variableLengthLength = 0;
void _checkIndex(int index) {
if (_size == _variableLengthLength) {
return;
}
if (index < 0 || index >= _size) {
throw RangeError.range(index, 0, _size - 1);
}
+140 -6
View File
@@ -88,7 +88,7 @@ final class Pointer<T extends NativeType> implements SizedNativeType {
/// A fixed-sized array of [T]s.
@Since('2.13')
final class Array<T extends NativeType> extends _Compound {
/// Const constructor to specify [Array] dimensions in [Struct]s.
/// Annotation to specify [Array] dimensions in [Struct]s.
///
/// ```dart
/// final class MyStruct extends Struct {
@@ -107,7 +107,7 @@ final class Array<T extends NativeType> extends _Compound {
int dimension4,
int dimension5]) = _ArraySize<T>;
/// Const constructor to specify [Array] dimensions in [Struct]s.
/// Annotation to specify [Array] dimensions in [Struct]s.
///
/// ```dart
/// final class MyStruct extends Struct {
@@ -121,6 +121,97 @@ final class Array<T extends NativeType> extends _Compound {
///
/// Do not invoke in normal code.
const factory Array.multi(List<int> dimensions) = _ArraySize<T>.multi;
/// Annotation to specify a variable length [Array] in [Struct]s.
///
/// Can only be used on the last field of a struct. The last field of the
/// struct is _not_ taken into account in [sizeOf]. Using an
/// [AllocatorAlloc.call] will _not_ allocate any backing storage for the
/// variable length array. Instead use [Allocator.allocate] and calculate the
/// required number of bytes manually.
///
/// ```dart
/// import 'dart:ffi';
/// import 'package:ffi/ffi.dart';
///
/// final class MyStruct extends Struct {
/// @Size()
/// external int length;
///
/// @Array.variable()
/// external Array<Uint8> inlineArray;
///
/// static Pointer<MyStruct> allocate(Allocator allocator, int length) {
/// final lengthInBytes = sizeOf<MyStruct>() + sizeOf<Uint8>() * length;
/// final result = allocator.allocate<MyStruct>(lengthInBytes);
/// result.ref.length = length;
/// return result;
/// }
/// }
///
/// void main() {
/// final myStruct = MyStruct.allocate(calloc, 10);
/// }
/// ```
///
/// The variable lenght is always the outermost dimension of the array.
///
/// ```dart
/// import 'dart:ffi';
/// import 'package:ffi/ffi.dart';
///
/// final class MyStruct extends Struct {
/// @Size()
/// external int length;
///
/// @Array.variable(10, 10)
/// external Array<Array<Array<Uint8>>> inlineArray;
///
/// static Pointer<MyStruct> allocate(Allocator allocator, int length) {
/// final lengthInBytes = sizeOf<MyStruct>() + sizeOf<Uint8>() * length * 100;
/// final result = allocator.allocate<MyStruct>(lengthInBytes);
/// result.ref.length = length;
/// return result;
/// }
/// }
/// ```
///
/// Accessing variable length inline arrays of structs passed by value in FFI
/// calls and callbacks is undefined behavior. Accessing variable length
/// inline arrays in structs passed by value is undefined behavior in C.
///
/// For more information about variable length inline arrays in C, please
/// refer to: https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html.
///
/// Do not invoke in normal code.
@Since('3.6')
const factory Array.variable([
int dimension2,
int dimension3,
int dimension4,
int dimension5,
]) = _ArraySize<T>.variable;
/// Annotation to a variable length [Array] in [Struct]s.
///
/// ```dart
/// final class MyStruct extends Struct {
/// @Array.variableMulti([2, 2])
/// external Array<Array<Array<Uint8>>> threeDimensionalInlineArray;
/// }
///
/// final class MyStruct2 extends Struct {
/// @Array.variableMulti([2, 2, 2, 2, 2, 2, 2])
/// external Array<Array<Array<Array<Array<Array<Array<Array<Uint8>>>>>>>> eightDimensionalInlineArray;
/// }
/// ```
///
/// The variable lenght is always the outermost dimension of the array.
///
/// Do not invoke in normal code.
@Since('3.6')
const factory Array.variableMulti(List<int> dimensions) =
_ArraySize<T>.variableMulti;
}
final class _ArraySize<T extends NativeType> implements Array<T> {
@@ -132,16 +223,59 @@ final class _ArraySize<T extends NativeType> implements Array<T> {
final List<int>? dimensions;
const _ArraySize(this.dimension1,
[this.dimension2, this.dimension3, this.dimension4, this.dimension5])
: dimensions = null;
// When `true`, [dimension1] is [variableLengthLength], or [dimensions]
// should be prepended with [variableLengthLength].
final bool variableLength;
const _ArraySize(
this.dimension1, [
this.dimension2,
this.dimension3,
this.dimension4,
this.dimension5,
]) : dimensions = null,
variableLength = false;
const _ArraySize.multi(this.dimensions)
: dimension1 = null,
dimension2 = null,
dimension3 = null,
dimension4 = null,
dimension5 = null;
dimension5 = null,
variableLength = false;
// Inline arrays in C of length 0 are undefined.
//
// GNU uses 0 to signal variable length arrays.
// https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html
//
// Some Windows APIs use an inline array length of 1 for variable length
// inline arrays.
// https://devblogs.microsoft.com/oldnewthing/20040826-00/?p=38043
// However, this is perfectly valid C code.
//
// We follow the GNU standard here. This follows the behavior of structs
// with variable length arrays when malloc'ed and passed by value (the
// variable length array is ignored).
static const variableLengthLength = 0;
const _ArraySize.variable([
this.dimension2,
this.dimension3,
this.dimension4,
this.dimension5,
]) : dimension1 = variableLengthLength,
dimensions = null,
variableLength = true;
const _ArraySize.variableMulti(List<int> nestedDimensions)
: dimensions = nestedDimensions, // Should be `[0, ...nestedDimensions]`.
dimension1 = null,
dimension2 = null,
dimension3 = null,
dimension4 = null,
dimension5 = null,
variableLength = true;
}
/// Extension on [Pointer] specialized for the type argument [NativeFunction].
@@ -382,6 +382,16 @@ final testCases = [
Pointer.fromFunction<PassPointerStruct12BytesHomogeneousInt32Type>(
passPointerStruct12BytesHomogeneousInt32, 0),
noChecks),
CallbackTest.withCheck(
"PassPointerStructInlineArrayVariable",
Pointer.fromFunction<PassPointerStructInlineArrayVariableType>(
passPointerStructInlineArrayVariable, 0),
noChecks),
CallbackTest.withCheck(
"PassPointerStructInlineArrayVariableAlign",
Pointer.fromFunction<PassPointerStructInlineArrayVariableAlignType>(
passPointerStructInlineArrayVariableAlign, 0),
noChecks),
CallbackTest.withCheck(
"ReturnStruct1ByteInt",
Pointer.fromFunction<ReturnStruct1ByteIntType>(returnStruct1ByteInt),
@@ -7970,6 +7980,109 @@ int passPointerStruct12BytesHomogeneousInt32(
return result;
}
typedef PassPointerStructInlineArrayVariableType = Int64 Function(
Pointer<StructInlineArrayVariable>);
// Global variables to be able to test inputs after callback returned.
Pointer<StructInlineArrayVariable> passPointerStructInlineArrayVariable_a0 =
nullptr;
// Result variable also global, so we can delete it after the callback.
int passPointerStructInlineArrayVariableResult = 0;
int passPointerStructInlineArrayVariableCalculateResult() {
int result = 0;
result += passPointerStructInlineArrayVariable_a0.ref.a0;
result += passPointerStructInlineArrayVariable_a0.ref.a1[0];
result += passPointerStructInlineArrayVariable_a0.ref.a1[1];
result += passPointerStructInlineArrayVariable_a0.ref.a1[2];
result += passPointerStructInlineArrayVariable_a0.ref.a1[3];
result += passPointerStructInlineArrayVariable_a0.ref.a1[4];
result += passPointerStructInlineArrayVariable_a0.ref.a1[5];
result += passPointerStructInlineArrayVariable_a0.ref.a1[6];
result += passPointerStructInlineArrayVariable_a0.ref.a1[7];
result += passPointerStructInlineArrayVariable_a0.ref.a1[8];
result += passPointerStructInlineArrayVariable_a0.ref.a1[9];
passPointerStructInlineArrayVariableResult = result;
return result;
}
/// Variable length array
int passPointerStructInlineArrayVariable(
Pointer<StructInlineArrayVariable> a0) {
print("passPointerStructInlineArrayVariable(${a0})");
// Possibly throw.
if (a0.ref.a0 == 42 || a0.ref.a0 == 84) {
print("throwing!");
throw Exception(
"PassPointerStructInlineArrayVariable throwing on purpose!");
}
passPointerStructInlineArrayVariable_a0 = a0;
final result = passPointerStructInlineArrayVariableCalculateResult();
print("result = $result");
return result;
}
typedef PassPointerStructInlineArrayVariableAlignType = Int64 Function(
Pointer<StructInlineArrayVariableAlign>);
// Global variables to be able to test inputs after callback returned.
Pointer<StructInlineArrayVariableAlign>
passPointerStructInlineArrayVariableAlign_a0 = nullptr;
// Result variable also global, so we can delete it after the callback.
int passPointerStructInlineArrayVariableAlignResult = 0;
int passPointerStructInlineArrayVariableAlignCalculateResult() {
int result = 0;
result += passPointerStructInlineArrayVariableAlign_a0.ref.a0;
result += passPointerStructInlineArrayVariableAlign_a0.ref.a1[0];
result += passPointerStructInlineArrayVariableAlign_a0.ref.a1[1];
result += passPointerStructInlineArrayVariableAlign_a0.ref.a1[2];
result += passPointerStructInlineArrayVariableAlign_a0.ref.a1[3];
result += passPointerStructInlineArrayVariableAlign_a0.ref.a1[4];
result += passPointerStructInlineArrayVariableAlign_a0.ref.a1[5];
result += passPointerStructInlineArrayVariableAlign_a0.ref.a1[6];
result += passPointerStructInlineArrayVariableAlign_a0.ref.a1[7];
result += passPointerStructInlineArrayVariableAlign_a0.ref.a1[8];
result += passPointerStructInlineArrayVariableAlign_a0.ref.a1[9];
passPointerStructInlineArrayVariableAlignResult = result;
return result;
}
/// Variable length array with variable length element having more alignment than
/// the rest of the struct.
int passPointerStructInlineArrayVariableAlign(
Pointer<StructInlineArrayVariableAlign> a0) {
print("passPointerStructInlineArrayVariableAlign(${a0})");
// Possibly throw.
if (a0.ref.a0 == 42 || a0.ref.a0 == 84) {
print("throwing!");
throw Exception(
"PassPointerStructInlineArrayVariableAlign throwing on purpose!");
}
passPointerStructInlineArrayVariableAlign_a0 = a0;
final result = passPointerStructInlineArrayVariableAlignCalculateResult();
print("result = $result");
return result;
}
typedef ReturnStruct1ByteIntType = Struct1ByteInt Function(Int8);
// Global variables to be able to test inputs after callback returned.
@@ -457,6 +457,19 @@ final testCases = [
passPointerStruct12BytesHomogeneousInt32,
exceptionalReturn: 0),
noChecks),
CallbackTest.withCheck(
"PassPointerStructInlineArrayVariable",
NativeCallable<PassPointerStructInlineArrayVariableType>.isolateLocal(
passPointerStructInlineArrayVariable,
exceptionalReturn: 0),
noChecks),
CallbackTest.withCheck(
"PassPointerStructInlineArrayVariableAlign",
NativeCallable<
PassPointerStructInlineArrayVariableAlignType>.isolateLocal(
passPointerStructInlineArrayVariableAlign,
exceptionalReturn: 0),
noChecks),
CallbackTest.withCheck(
"ReturnStruct1ByteInt",
NativeCallable<ReturnStruct1ByteIntType>.isolateLocal(
@@ -8055,6 +8068,109 @@ int passPointerStruct12BytesHomogeneousInt32(
return result;
}
typedef PassPointerStructInlineArrayVariableType = Int64 Function(
Pointer<StructInlineArrayVariable>);
// Global variables to be able to test inputs after callback returned.
Pointer<StructInlineArrayVariable> passPointerStructInlineArrayVariable_a0 =
nullptr;
// Result variable also global, so we can delete it after the callback.
int passPointerStructInlineArrayVariableResult = 0;
int passPointerStructInlineArrayVariableCalculateResult() {
int result = 0;
result += passPointerStructInlineArrayVariable_a0.ref.a0;
result += passPointerStructInlineArrayVariable_a0.ref.a1[0];
result += passPointerStructInlineArrayVariable_a0.ref.a1[1];
result += passPointerStructInlineArrayVariable_a0.ref.a1[2];
result += passPointerStructInlineArrayVariable_a0.ref.a1[3];
result += passPointerStructInlineArrayVariable_a0.ref.a1[4];
result += passPointerStructInlineArrayVariable_a0.ref.a1[5];
result += passPointerStructInlineArrayVariable_a0.ref.a1[6];
result += passPointerStructInlineArrayVariable_a0.ref.a1[7];
result += passPointerStructInlineArrayVariable_a0.ref.a1[8];
result += passPointerStructInlineArrayVariable_a0.ref.a1[9];
passPointerStructInlineArrayVariableResult = result;
return result;
}
/// Variable length array
int passPointerStructInlineArrayVariable(
Pointer<StructInlineArrayVariable> a0) {
print("passPointerStructInlineArrayVariable(${a0})");
// Possibly throw.
if (a0.ref.a0 == 42 || a0.ref.a0 == 84) {
print("throwing!");
throw Exception(
"PassPointerStructInlineArrayVariable throwing on purpose!");
}
passPointerStructInlineArrayVariable_a0 = a0;
final result = passPointerStructInlineArrayVariableCalculateResult();
print("result = $result");
return result;
}
typedef PassPointerStructInlineArrayVariableAlignType = Int64 Function(
Pointer<StructInlineArrayVariableAlign>);
// Global variables to be able to test inputs after callback returned.
Pointer<StructInlineArrayVariableAlign>
passPointerStructInlineArrayVariableAlign_a0 = nullptr;
// Result variable also global, so we can delete it after the callback.
int passPointerStructInlineArrayVariableAlignResult = 0;
int passPointerStructInlineArrayVariableAlignCalculateResult() {
int result = 0;
result += passPointerStructInlineArrayVariableAlign_a0.ref.a0;
result += passPointerStructInlineArrayVariableAlign_a0.ref.a1[0];
result += passPointerStructInlineArrayVariableAlign_a0.ref.a1[1];
result += passPointerStructInlineArrayVariableAlign_a0.ref.a1[2];
result += passPointerStructInlineArrayVariableAlign_a0.ref.a1[3];
result += passPointerStructInlineArrayVariableAlign_a0.ref.a1[4];
result += passPointerStructInlineArrayVariableAlign_a0.ref.a1[5];
result += passPointerStructInlineArrayVariableAlign_a0.ref.a1[6];
result += passPointerStructInlineArrayVariableAlign_a0.ref.a1[7];
result += passPointerStructInlineArrayVariableAlign_a0.ref.a1[8];
result += passPointerStructInlineArrayVariableAlign_a0.ref.a1[9];
passPointerStructInlineArrayVariableAlignResult = result;
return result;
}
/// Variable length array with variable length element having more alignment than
/// the rest of the struct.
int passPointerStructInlineArrayVariableAlign(
Pointer<StructInlineArrayVariableAlign> a0) {
print("passPointerStructInlineArrayVariableAlign(${a0})");
// Possibly throw.
if (a0.ref.a0 == 42 || a0.ref.a0 == 84) {
print("throwing!");
throw Exception(
"PassPointerStructInlineArrayVariableAlign throwing on purpose!");
}
passPointerStructInlineArrayVariableAlign_a0 = a0;
final result = passPointerStructInlineArrayVariableAlignCalculateResult();
print("result = $result");
return result;
}
typedef ReturnStruct1ByteIntType = Struct1ByteInt Function(Int8);
// Global variables to be able to test inputs after callback returned.
@@ -93,6 +93,8 @@ void main() {
testPassWCharStructInlineArrayIntUintPtrx2LongUnsignedLeaf();
testPassInt64x7Struct12BytesHomogeneousInt32Leaf();
testPassPointerStruct12BytesHomogeneousInt32Leaf();
testPassPointerStructInlineArrayVariableLeaf();
testPassPointerStructInlineArrayVariableAlignLeaf();
}
}
@@ -5509,7 +5511,8 @@ final passPointerStruct12BytesHomogeneousInt32Leaf =
/// Passing a pointer to a struct
void testPassPointerStruct12BytesHomogeneousInt32Leaf() {
final a0 = calloc<Struct12BytesHomogeneousInt32>();
final a0 = calloc.allocate<Struct12BytesHomogeneousInt32>(
sizeOf<Struct12BytesHomogeneousInt32>());
a0.ref.a0 = -1;
a0.ref.a1 = 2;
@@ -5523,3 +5526,70 @@ void testPassPointerStruct12BytesHomogeneousInt32Leaf() {
calloc.free(a0);
}
final passPointerStructInlineArrayVariableLeaf =
ffiTestFunctions.lookupFunction<
Int64 Function(Pointer<StructInlineArrayVariable>),
int Function(Pointer<StructInlineArrayVariable>)>(
"PassPointerStructInlineArrayVariable",
isLeaf: true);
/// Variable length array
void testPassPointerStructInlineArrayVariableLeaf() {
final a0 = calloc.allocate<StructInlineArrayVariable>(
sizeOf<StructInlineArrayVariable>() + 10 * sizeOf<Uint8>());
a0.ref.a0 = 1;
a0.ref.a1[0] = 2;
a0.ref.a1[1] = 3;
a0.ref.a1[2] = 4;
a0.ref.a1[3] = 5;
a0.ref.a1[4] = 6;
a0.ref.a1[5] = 7;
a0.ref.a1[6] = 8;
a0.ref.a1[7] = 9;
a0.ref.a1[8] = 10;
a0.ref.a1[9] = 11;
final result = passPointerStructInlineArrayVariableLeaf(a0);
print("result = $result");
Expect.equals(66, result);
calloc.free(a0);
}
final passPointerStructInlineArrayVariableAlignLeaf =
ffiTestFunctions.lookupFunction<
Int64 Function(Pointer<StructInlineArrayVariableAlign>),
int Function(Pointer<StructInlineArrayVariableAlign>)>(
"PassPointerStructInlineArrayVariableAlign",
isLeaf: true);
/// Variable length array with variable length element having more alignment than
/// the rest of the struct.
void testPassPointerStructInlineArrayVariableAlignLeaf() {
final a0 = calloc.allocate<StructInlineArrayVariableAlign>(
sizeOf<StructInlineArrayVariableAlign>() + 10 * sizeOf<Uint32>());
a0.ref.a0 = 1;
a0.ref.a1[0] = 2;
a0.ref.a1[1] = 3;
a0.ref.a1[2] = 4;
a0.ref.a1[3] = 5;
a0.ref.a1[4] = 6;
a0.ref.a1[5] = 7;
a0.ref.a1[6] = 8;
a0.ref.a1[7] = 9;
a0.ref.a1[8] = 10;
a0.ref.a1[9] = 11;
final result = passPointerStructInlineArrayVariableAlignLeaf(a0);
print("result = $result");
Expect.equals(66, result);
calloc.free(a0);
}
@@ -96,6 +96,8 @@ void main() {
testPassWCharStructInlineArrayIntUintPtrx2LongUnsignedNativeLeaf();
testPassInt64x7Struct12BytesHomogeneousInt32NativeLeaf();
testPassPointerStruct12BytesHomogeneousInt32NativeLeaf();
testPassPointerStructInlineArrayVariableNativeLeaf();
testPassPointerStructInlineArrayVariableAlignNativeLeaf();
}
}
@@ -5471,7 +5473,8 @@ external int passPointerStruct12BytesHomogeneousInt32NativeLeaf(
/// Passing a pointer to a struct
void testPassPointerStruct12BytesHomogeneousInt32NativeLeaf() {
final a0 = calloc<Struct12BytesHomogeneousInt32>();
final a0 = calloc.allocate<Struct12BytesHomogeneousInt32>(
sizeOf<Struct12BytesHomogeneousInt32>());
a0.ref.a0 = -1;
a0.ref.a1 = 2;
@@ -5485,3 +5488,66 @@ void testPassPointerStruct12BytesHomogeneousInt32NativeLeaf() {
calloc.free(a0);
}
@Native<Int64 Function(Pointer<StructInlineArrayVariable>)>(
symbol: 'PassPointerStructInlineArrayVariable', isLeaf: true)
external int passPointerStructInlineArrayVariableNativeLeaf(
Pointer<StructInlineArrayVariable> a0);
/// Variable length array
void testPassPointerStructInlineArrayVariableNativeLeaf() {
final a0 = calloc.allocate<StructInlineArrayVariable>(
sizeOf<StructInlineArrayVariable>() + 10 * sizeOf<Uint8>());
a0.ref.a0 = 1;
a0.ref.a1[0] = 2;
a0.ref.a1[1] = 3;
a0.ref.a1[2] = 4;
a0.ref.a1[3] = 5;
a0.ref.a1[4] = 6;
a0.ref.a1[5] = 7;
a0.ref.a1[6] = 8;
a0.ref.a1[7] = 9;
a0.ref.a1[8] = 10;
a0.ref.a1[9] = 11;
final result = passPointerStructInlineArrayVariableNativeLeaf(a0);
print("result = $result");
Expect.equals(66, result);
calloc.free(a0);
}
@Native<Int64 Function(Pointer<StructInlineArrayVariableAlign>)>(
symbol: 'PassPointerStructInlineArrayVariableAlign', isLeaf: true)
external int passPointerStructInlineArrayVariableAlignNativeLeaf(
Pointer<StructInlineArrayVariableAlign> a0);
/// Variable length array with variable length element having more alignment than
/// the rest of the struct.
void testPassPointerStructInlineArrayVariableAlignNativeLeaf() {
final a0 = calloc.allocate<StructInlineArrayVariableAlign>(
sizeOf<StructInlineArrayVariableAlign>() + 10 * sizeOf<Uint32>());
a0.ref.a0 = 1;
a0.ref.a1[0] = 2;
a0.ref.a1[1] = 3;
a0.ref.a1[2] = 4;
a0.ref.a1[3] = 5;
a0.ref.a1[4] = 6;
a0.ref.a1[5] = 7;
a0.ref.a1[6] = 8;
a0.ref.a1[7] = 9;
a0.ref.a1[8] = 10;
a0.ref.a1[9] = 11;
final result = passPointerStructInlineArrayVariableAlignNativeLeaf(a0);
print("result = $result");
Expect.equals(66, result);
calloc.free(a0);
}
@@ -96,6 +96,8 @@ void main() {
testPassWCharStructInlineArrayIntUintPtrx2LongUnsignedNative();
testPassInt64x7Struct12BytesHomogeneousInt32Native();
testPassPointerStruct12BytesHomogeneousInt32Native();
testPassPointerStructInlineArrayVariableNative();
testPassPointerStructInlineArrayVariableAlignNative();
}
}
@@ -5481,7 +5483,8 @@ external int passPointerStruct12BytesHomogeneousInt32Native(
/// Passing a pointer to a struct
void testPassPointerStruct12BytesHomogeneousInt32Native() {
final a0 = calloc<Struct12BytesHomogeneousInt32>();
final a0 = calloc.allocate<Struct12BytesHomogeneousInt32>(
sizeOf<Struct12BytesHomogeneousInt32>());
a0.ref.a0 = -1;
a0.ref.a1 = 2;
@@ -5495,3 +5498,66 @@ void testPassPointerStruct12BytesHomogeneousInt32Native() {
calloc.free(a0);
}
@Native<Int64 Function(Pointer<StructInlineArrayVariable>)>(
symbol: 'PassPointerStructInlineArrayVariable')
external int passPointerStructInlineArrayVariableNative(
Pointer<StructInlineArrayVariable> a0);
/// Variable length array
void testPassPointerStructInlineArrayVariableNative() {
final a0 = calloc.allocate<StructInlineArrayVariable>(
sizeOf<StructInlineArrayVariable>() + 10 * sizeOf<Uint8>());
a0.ref.a0 = 1;
a0.ref.a1[0] = 2;
a0.ref.a1[1] = 3;
a0.ref.a1[2] = 4;
a0.ref.a1[3] = 5;
a0.ref.a1[4] = 6;
a0.ref.a1[5] = 7;
a0.ref.a1[6] = 8;
a0.ref.a1[7] = 9;
a0.ref.a1[8] = 10;
a0.ref.a1[9] = 11;
final result = passPointerStructInlineArrayVariableNative(a0);
print("result = $result");
Expect.equals(66, result);
calloc.free(a0);
}
@Native<Int64 Function(Pointer<StructInlineArrayVariableAlign>)>(
symbol: 'PassPointerStructInlineArrayVariableAlign')
external int passPointerStructInlineArrayVariableAlignNative(
Pointer<StructInlineArrayVariableAlign> a0);
/// Variable length array with variable length element having more alignment than
/// the rest of the struct.
void testPassPointerStructInlineArrayVariableAlignNative() {
final a0 = calloc.allocate<StructInlineArrayVariableAlign>(
sizeOf<StructInlineArrayVariableAlign>() + 10 * sizeOf<Uint32>());
a0.ref.a0 = 1;
a0.ref.a1[0] = 2;
a0.ref.a1[1] = 3;
a0.ref.a1[2] = 4;
a0.ref.a1[3] = 5;
a0.ref.a1[4] = 6;
a0.ref.a1[5] = 7;
a0.ref.a1[6] = 8;
a0.ref.a1[7] = 9;
a0.ref.a1[8] = 10;
a0.ref.a1[9] = 11;
final result = passPointerStructInlineArrayVariableAlignNative(a0);
print("result = $result");
Expect.equals(66, result);
calloc.free(a0);
}
@@ -93,6 +93,8 @@ void main() {
testPassWCharStructInlineArrayIntUintPtrx2LongUnsigned();
testPassInt64x7Struct12BytesHomogeneousInt32();
testPassPointerStruct12BytesHomogeneousInt32();
testPassPointerStructInlineArrayVariable();
testPassPointerStructInlineArrayVariableAlign();
}
}
@@ -5430,7 +5432,8 @@ final passPointerStruct12BytesHomogeneousInt32 =
/// Passing a pointer to a struct
void testPassPointerStruct12BytesHomogeneousInt32() {
final a0 = calloc<Struct12BytesHomogeneousInt32>();
final a0 = calloc.allocate<Struct12BytesHomogeneousInt32>(
sizeOf<Struct12BytesHomogeneousInt32>());
a0.ref.a0 = -1;
a0.ref.a1 = 2;
@@ -5444,3 +5447,67 @@ void testPassPointerStruct12BytesHomogeneousInt32() {
calloc.free(a0);
}
final passPointerStructInlineArrayVariable = ffiTestFunctions.lookupFunction<
Int64 Function(Pointer<StructInlineArrayVariable>),
int Function(Pointer<StructInlineArrayVariable>)>(
"PassPointerStructInlineArrayVariable");
/// Variable length array
void testPassPointerStructInlineArrayVariable() {
final a0 = calloc.allocate<StructInlineArrayVariable>(
sizeOf<StructInlineArrayVariable>() + 10 * sizeOf<Uint8>());
a0.ref.a0 = 1;
a0.ref.a1[0] = 2;
a0.ref.a1[1] = 3;
a0.ref.a1[2] = 4;
a0.ref.a1[3] = 5;
a0.ref.a1[4] = 6;
a0.ref.a1[5] = 7;
a0.ref.a1[6] = 8;
a0.ref.a1[7] = 9;
a0.ref.a1[8] = 10;
a0.ref.a1[9] = 11;
final result = passPointerStructInlineArrayVariable(a0);
print("result = $result");
Expect.equals(66, result);
calloc.free(a0);
}
final passPointerStructInlineArrayVariableAlign =
ffiTestFunctions.lookupFunction<
Int64 Function(Pointer<StructInlineArrayVariableAlign>),
int Function(Pointer<StructInlineArrayVariableAlign>)>(
"PassPointerStructInlineArrayVariableAlign");
/// Variable length array with variable length element having more alignment than
/// the rest of the struct.
void testPassPointerStructInlineArrayVariableAlign() {
final a0 = calloc.allocate<StructInlineArrayVariableAlign>(
sizeOf<StructInlineArrayVariableAlign>() + 10 * sizeOf<Uint32>());
a0.ref.a0 = 1;
a0.ref.a1[0] = 2;
a0.ref.a1[1] = 3;
a0.ref.a1[2] = 4;
a0.ref.a1[3] = 5;
a0.ref.a1[4] = 6;
a0.ref.a1[5] = 7;
a0.ref.a1[6] = 8;
a0.ref.a1[7] = 9;
a0.ref.a1[8] = 10;
a0.ref.a1[9] = 11;
final result = passPointerStructInlineArrayVariableAlign(a0);
print("result = $result");
Expect.equals(66, result);
calloc.free(a0);
}
@@ -1277,3 +1277,43 @@ final class StructInlineArrayInt extends Struct {
String toString() => "(${[for (var i0 = 0; i0 < 10; i0 += 1) a0[i0]]})";
}
final class StructInlineArrayVariable extends Struct {
@Uint32()
external int a0;
@Array.variable()
external Array<Uint8> a1;
String toString() => "(${a0}, ${a1})";
}
final class StructInlineArrayVariableNested extends Struct {
@Uint32()
external int a0;
@Array.variable(2, 2)
external Array<Array<Array<Uint8>>> a1;
String toString() => "(${a0}, ${a1})";
}
final class StructInlineArrayVariableNestedDeep extends Struct {
@Uint32()
external int a0;
@Array.variableMulti([2, 2, 2, 2, 2, 2])
external Array<Array<Array<Array<Array<Array<Array<Uint8>>>>>>> a1;
String toString() => "(${a0}, ${a1})";
}
final class StructInlineArrayVariableAlign extends Struct {
@Uint8()
external int a0;
@Array.variable()
external Array<Uint32> a1;
String toString() => "(${a0}, ${a1})";
}
+22 -1
View File
@@ -247,7 +247,8 @@ class Member {
String postFix = "";
if (type is FixedLengthArrayType) {
final dimensions = (type as FixedLengthArrayType).dimensions;
postFix = "[${dimensions.join("][")}]";
postFix =
"[${dimensions.map((d) => d == 0 ? '' : d.toString()).join("][")}]";
}
return "${type.cType} $name$postFix;";
}
@@ -479,6 +480,26 @@ class FixedLengthArrayType extends CType {
bool get isOnlyBool => elementType.isOnlyBool;
}
class VariableLengthArrayType extends FixedLengthArrayType {
VariableLengthArrayType(
CType elementType,
) : super(elementType, 0);
factory VariableLengthArrayType.multi(
CType elementType, List<int> fixedDimensions) {
final nestedArray =
FixedLengthArrayType.multi(elementType, fixedDimensions);
return VariableLengthArrayType(nestedArray);
}
String get dartStructFieldAnnotation {
if (dimensions.length > 5) {
return "@Array.variableMulti([${dimensions.skip(1).join(", ")}])";
}
return "@Array.variable(${dimensions.skip(1).join(", ")})";
}
}
class FunctionType extends CType {
final List<Member> arguments;
final int? varArgsIndex;
@@ -457,6 +457,21 @@ Struct stradles last argument register"""),
int64,
"""
Passing a pointer to a struct"""),
FunctionType(
[
PointerType(structVariableLengthArray),
],
int64,
"""
Variable length array"""),
FunctionType(
[
PointerType(structVariableLengthArray4),
],
int64,
"""
Variable length array with variable length element having more alignment than
the rest of the struct."""),
];
/// Functions that return a struct by value.
@@ -711,6 +726,10 @@ final compounds = [
union16bytesFloat,
union16bytesFloat2,
structArrayWChar,
structVariableLengthArray,
structVariableLengthArray2,
structVariableLengthArray3,
structVariableLengthArray4,
];
/// Function signatures for variadic argument tests.
@@ -1061,3 +1080,35 @@ final union16bytesFloat2 =
/// This struct contains an AbiSpecificInt type.
final structArrayWChar = StructType([FixedLengthArrayType(wchar, 10)]);
final structVariableLengthArray = StructType.override(
[
uint32,
VariableLengthArrayType(uint8),
],
"InlineArrayVariable",
);
final structVariableLengthArray2 = StructType.override(
[
uint32,
VariableLengthArrayType.multi(uint8, [2, 2]),
],
"InlineArrayVariableNested",
);
final structVariableLengthArray3 = StructType.override(
[
uint32,
VariableLengthArrayType.multi(uint8, [2, 2, 2, 2, 2, 2]),
],
"InlineArrayVariableNestedDeep",
);
final structVariableLengthArray4 = StructType.override(
[
uint8,
VariableLengthArrayType(uint32),
],
"InlineArrayVariableAlign",
);
@@ -76,8 +76,13 @@ extension on CType {
return this_.members.take(1).toList().coutExpression("$variableName.");
case FixedLengthArrayType:
case VariableLengthArrayType:
final this_ = this as FixedLengthArrayType;
final indices = [for (var i = 0; i < this_.length; i += 1) i];
final int length = switch (this_) {
VariableLengthArrayType _ => _variableLengthLength,
FixedLengthArrayType _ => this_.length,
};
final indices = [for (var i = 0; i < length; i += 1) i];
String result = '<< "["';
result += indices
@@ -144,8 +149,13 @@ extension on CType {
.addToResultStatements("$variableName.${member.name}", isDart);
case FixedLengthArrayType:
case VariableLengthArrayType:
final this_ = this as FixedLengthArrayType;
final indices = [for (var i = 0; i < this_.length; i += 1) i];
final int length = switch (this_) {
VariableLengthArrayType _ => _variableLengthLength,
FixedLengthArrayType _ => this_.length,
};
final indices = [for (var i = 0; i < length; i += 1) i];
return indices
.map((i) => this_.elementType
.addToResultStatements("$variableName[$i]", isDart))
@@ -192,8 +202,13 @@ extension on CType {
.assignValueStatements(a, "$variableName.${member.name}", isDart);
case FixedLengthArrayType:
case VariableLengthArrayType:
final this_ = this as FixedLengthArrayType;
final indices = [for (var i = 0; i < this_.length; i += 1) i];
final int length = switch (this_) {
VariableLengthArrayType _ => _variableLengthLength,
FixedLengthArrayType _ => this_.length,
};
final indices = [for (var i = 0; i < length; i += 1) i];
return indices
.map((i) => this_.elementType
.assignValueStatements(a, "$variableName[$i]", isDart))
@@ -301,8 +316,14 @@ extension on CType {
final pointerTo = this_.pointerTo;
switch (pointerTo) {
case StructType _:
final lastMember = pointerTo.memberTypes.last;
final String extraBytes = switch (lastMember) {
VariableLengthArrayType _ =>
'+ $_variableLengthLength * sizeOf<${lastMember.elementType.dartCType}>()',
_ => '',
};
return '''
final ${variableName} = calloc<${pointerTo.dartType}>();
final ${variableName} = calloc.allocate<${pointerTo.dartType}>(sizeOf<${pointerTo.dartType}>() $extraBytes);
''';
}
return "\n";
@@ -408,15 +429,33 @@ extension on CType {
final pointerTo = this_.pointerTo;
switch (pointerTo) {
case StructType _:
final lastMember = pointerTo.memberTypes.last;
final String extraBytes = switch (lastMember) {
VariableLengthArrayType _ =>
'+ $_variableLengthLength * sizeof(${lastMember.elementType.cType})',
_ => '',
};
return '''
${pointerTo.cType} ${variableName}_value = {};
${cType} ${variableName} = &${variableName}_value;
${cType} ${variableName} = static_cast<${cType}>(calloc(1, sizeof(${pointerTo.cType}) $extraBytes));
''';
}
}
throw Exception("Not implemented for ${this.runtimeType}");
}
String cFreeStatements(String variableName) {
switch (this.runtimeType) {
case FundamentalType:
case StructType:
case UnionType:
return "";
case PointerType:
return 'free(${variableName});';
}
throw Exception("Not implemented for ${this.runtimeType}");
}
}
extension on List<Member> {
@@ -425,6 +464,10 @@ extension on List<Member> {
return map((m) => m.type.cAllocateStatements("$namePrefix${m.name}"))
.join();
}
String cFreeStatements([String namePrefix = ""]) {
return map((m) => m.type.cFreeStatements("$namePrefix${m.name}")).join();
}
}
extension on CType {
@@ -597,7 +640,8 @@ extension CompositeTypeGenerator on CompositeType {
dartFields += "${member.dartStructField()}\n\n";
}
String toStringBody = members.map((m) {
if (m.type is FixedLengthArrayType) {
if (m.type is FixedLengthArrayType &&
m.type is! VariableLengthArrayType) {
int dimensionNumber = 0;
String inlineFor = "";
String read = m.name;
@@ -993,6 +1037,7 @@ $varArgsUnpack
String get cCallbackCode {
final a = ArgumentValueAssigner();
final argumentAllocations = arguments.cAllocateStatements();
final argumentFrees = arguments.cFreeStatements();
final assignValues = arguments.assignValueStatements(a, false);
final argumentString = [
@@ -1062,6 +1107,8 @@ $varArgsUnpack
$expectsZero
$argumentFrees
return 0;
}
@@ -1071,6 +1118,7 @@ $varArgsUnpack
String get cAsyncCallbackCode {
final a = ArgumentValueAssigner();
final argumentAllocations = arguments.cAllocateStatements();
final argumentFrees = arguments.cFreeStatements();
final assignValues = arguments.assignValueStatements(a, false);
final argumentString = [
@@ -1095,6 +1143,8 @@ $varArgsUnpack
std::cout << "Calling TestAsync$cName(" ${arguments.coutExpression()} << ")\\n";
f($argumentNames);
$argumentFrees
}
""";
@@ -1469,3 +1519,6 @@ void main(List<String> arguments) async {
writeC(),
]);
}
// This test uses this number of elements for variable length arrays.
const _variableLengthLength = 10;
@@ -0,0 +1,94 @@
// Copyright (c) 2024, 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 'package:ffi/ffi.dart';
// Reuse compound definitions.
import 'function_structs_by_value_generated_compounds.dart';
void main() {
testInlineArray();
testInlineArrayNested();
testInlineArrayNestedDeep();
testSizeOf();
}
void testInlineArray() {
const length = 10;
final lengthInBytes =
sizeOf<StructInlineArrayVariable>() + sizeOf<Uint8>() * length;
final pointer = calloc.allocate<StructInlineArrayVariable>(lengthInBytes);
pointer.ref.a0 = length;
final struct = pointer.ref;
for (int i = 0; i < length; i++) {
struct.a1[i] = i;
}
var sum = 0;
for (int i = 0; i < length; i++) {
sum += struct.a1[i];
}
calloc.free(pointer);
Expect.equals(45, sum);
}
void testInlineArrayNested() {
const length = 10;
final lengthInBytes = sizeOf<StructInlineArrayVariableNested>() +
sizeOf<Uint8>() * length * 2 * 2;
final pointer =
calloc.allocate<StructInlineArrayVariableNested>(lengthInBytes);
pointer.ref.a0 = length;
final struct = pointer.ref;
for (int i = 0; i < length; i++) {
struct.a1[i][0][0] = i;
}
var sum = 0;
for (int i = 0; i < length; i++) {
sum += struct.a1[i][0][0];
}
calloc.free(pointer);
Expect.equals(45, sum);
}
void testInlineArrayNestedDeep() {
const length = 10;
final lengthInBytes = sizeOf<StructInlineArrayVariableNestedDeep>() +
sizeOf<Uint8>() * length * 2 * 2 * 2 * 2 * 2 * 2;
final pointer =
calloc.allocate<StructInlineArrayVariableNestedDeep>(lengthInBytes);
pointer.ref.a0 = length;
final struct = pointer.ref;
for (int i = 0; i < length; i++) {
struct.a1[i][0][0][1][1][0][0] = i;
}
var sum = 0;
for (int i = 0; i < length; i++) {
sum += struct.a1[i][0][0][1][1][0][0];
}
calloc.free(pointer);
Expect.equals(45, sum);
}
final class Foo extends Struct {
@Int8()
external int field0;
}
final class Foo2 extends Struct {
@Int8()
external int field0;
@Array<Uint32>.variable()
external Array<Uint32> field1;
}
void testSizeOf() {
Expect.equals(1, sizeOf<Foo>());
Expect.equals(4, sizeOf<Foo2>());
}
@@ -382,6 +382,16 @@ final testCases = [
Pointer.fromFunction<PassPointerStruct12BytesHomogeneousInt32Type>(
passPointerStruct12BytesHomogeneousInt32),
noChecksAsync),
AsyncCallbackTest(
"PassPointerStructInlineArrayVariable",
Pointer.fromFunction<PassPointerStructInlineArrayVariableType>(
passPointerStructInlineArrayVariable),
noChecksAsync),
AsyncCallbackTest(
"PassPointerStructInlineArrayVariableAlign",
Pointer.fromFunction<PassPointerStructInlineArrayVariableAlignType>(
passPointerStructInlineArrayVariableAlign),
noChecksAsync),
AsyncCallbackTest(
"ReturnStruct1ByteInt",
Pointer.fromFunction<ReturnStruct1ByteIntType>(returnStruct1ByteInt),
@@ -5167,6 +5177,77 @@ Future<void> passPointerStruct12BytesHomogeneousInt32AfterCallback() async {
Expect.approxEquals(-2, result);
}
typedef PassPointerStructInlineArrayVariableType = Void Function(
Pointer<StructInlineArrayVariable>);
// Global variable that stores the result.
final PassPointerStructInlineArrayVariableResult = Completer<double>();
/// Variable length array
void passPointerStructInlineArrayVariable(
Pointer<StructInlineArrayVariable> a0) {
print("passPointerStructInlineArrayVariable(${a0})");
double result = 0;
result += a0.ref.a0;
result += a0.ref.a1[0];
result += a0.ref.a1[1];
result += a0.ref.a1[2];
result += a0.ref.a1[3];
result += a0.ref.a1[4];
result += a0.ref.a1[5];
result += a0.ref.a1[6];
result += a0.ref.a1[7];
result += a0.ref.a1[8];
result += a0.ref.a1[9];
print("result = $result");
PassPointerStructInlineArrayVariableResult.complete(result);
}
Future<void> passPointerStructInlineArrayVariableAfterCallback() async {
final result = await PassPointerStructInlineArrayVariableResult.future;
print("after callback result = $result");
Expect.approxEquals(66, result);
}
typedef PassPointerStructInlineArrayVariableAlignType = Void Function(
Pointer<StructInlineArrayVariableAlign>);
// Global variable that stores the result.
final PassPointerStructInlineArrayVariableAlignResult = Completer<double>();
/// Variable length array with variable length element having more alignment than
/// the rest of the struct.
void passPointerStructInlineArrayVariableAlign(
Pointer<StructInlineArrayVariableAlign> a0) {
print("passPointerStructInlineArrayVariableAlign(${a0})");
double result = 0;
result += a0.ref.a0;
result += a0.ref.a1[0];
result += a0.ref.a1[1];
result += a0.ref.a1[2];
result += a0.ref.a1[3];
result += a0.ref.a1[4];
result += a0.ref.a1[5];
result += a0.ref.a1[6];
result += a0.ref.a1[7];
result += a0.ref.a1[8];
result += a0.ref.a1[9];
print("result = $result");
PassPointerStructInlineArrayVariableAlignResult.complete(result);
}
Future<void> passPointerStructInlineArrayVariableAlignAfterCallback() async {
final result = await PassPointerStructInlineArrayVariableAlignResult.future;
print("after callback result = $result");
Expect.approxEquals(66, result);
}
typedef ReturnStruct1ByteIntType = Void Function(Int8);
// Global variable that stores the result.
@@ -0,0 +1,74 @@
// Copyright (c) 2024, 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.
//
// Dart test program for testing dart:ffi extra checks
//
// SharedObjects=ffi_test_dynamic_library ffi_test_functions
import 'dart:ffi';
void main() {}
final class TestStruct1 extends Struct {
/**/ @Array.variable()
// ^^^^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.SIZE_ANNOTATION_DIMENSIONS
external Array<Array<Uint8>> a0;
// ^
// [cfe] Field 'a0' must have an 'Array' annotation that matches the dimensions.
}
final class TestStruct2 extends Struct {
/**/ @Array.variable()
// ^^^^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.VARIABLE_LENGTH_ARRAY_NOT_LAST
external Array<Uint8> a0;
// ^
// [cfe] Variable length 'Array's must only occur as the last field of Structs.
@Uint8()
external int a1;
}
final class TestStruct3 extends Struct {
// This should be a Array.variable() not an `@Array(0)`.
@Array(0)
// ^
// [analyzer] COMPILE_TIME_ERROR.NON_POSITIVE_ARRAY_DIMENSION
external Array<Uint8> a0;
// ^^
// [cfe] Array dimensions must be positive numbers.
}
final class TestStruct4 extends Struct {
/**/ @Array.variable(1, 2)
// ^^^^^^^^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.SIZE_ANNOTATION_DIMENSIONS
external Array<Array<Uint8>> a0;
// ^
// [cfe] Field 'a0' must have an 'Array' annotation that matches the dimensions.
}
final class TestStruct5 extends Struct {
/**/ @Array.variableMulti([1, 2])
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.SIZE_ANNOTATION_DIMENSIONS
external Array<Array<Uint8>> a0;
// ^
// [cfe] Field 'a0' must have an 'Array' annotation that matches the dimensions.
}
final class TestStruct6 extends Struct {
@Array.variableMulti([1, 2])
external Array<Array<Array<Uint8>>> a0;
}
final class TestStruct7 extends Struct {
/**/ @Array.variableMulti([1, 2])
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.SIZE_ANNOTATION_DIMENSIONS
external Array<Array<Array<Array<Uint8>>>> a0;
// ^
// [cfe] Field 'a0' must have an 'Array' annotation that matches the dimensions.
}