[vm/ffi] address of operator for FFI leaf calls

During FFI leaf calls, the Dart GC will not run. This means that we
can pass pointers into `TypedData` to FFI calls that take `Pointer`
arguments.

After this CL, we have three types of arguments that can flow into
`Pointer` argument in an FFI call:
* `Pointer`.
* `TypedData`: Any typed data including views.
* `_Compound`: A TypedData/Pointer and an offset in bytes.

The is only possible for `@Native external` functions, `asFunction`
does not support passing in `TypedData`. (See related GitHub issues
for discussion. TLDR: FFIgen should generate bindings without config.)

`.address` expressions on `TypedData` and `Array` elements do _not_
introduce bounds checks, even though `TypedData` and `Array` have
bounds information. E.g. `ffiNative(Uint8List(10)[20].address)` does
not throw.

Implementation details:

The CFE analyzes call-sites to `@Native external` functions. If the
arguments are `.address` expressions, it transforms the call site to
pass the compound or `TypedData`. If an additional offset needs to be
applied, the CFE constructs a new `_Compound` with the correct offset
in bytes.

The CFE then also creates a new `@Native external` function which have
`TypedData`s and `_Compound`s parameters. To avoid name clashes, these
functions are postfixed with `#` and `P`, `T`, or `C` for each Pointer
parameter.

TEST=pkg/vm/testcases/transformations/ffi/address_of_*

In the VM, `TypedData` arguments are passed as tagged values, and the
address is loaded inside the `FfiCallInstr`. `_Compound` arguments
turn into two IL definitions, one for the `TypedDataBase` (tagged),
and one for the offset in bytes (unboxed). The address is then loaded
inside the `FfiCallInstr` and the offset in bytes is applied.

Adding the offset in bytes required an extra temp register for ia32.
Also, it uncovered that the temp register in arm32 was conflicting
with the argument registers. However, TMP should suffice instead.

TEST=tests/ffi/address_of_array_generated_test.dart
TEST=tests/ffi/address_of_struct_generated_test.dart
TEST=tests/ffi/address_of_typeddata_generated_test.dart

Closes: https://github.com/dart-lang/sdk/issues/44589
Closes: https://github.com/dart-lang/sdk/issues/54771

CoreLibraryReviewExempt: VM only, unsupported in dart2wasm
Change-Id: I01fb428cfd6f9096a34689c2819c124a8003cb6b
Cq-Include-Trybots: dart/try:vm-aot-android-release-arm64c-try,vm-aot-android-release-arm_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-obfuscate-linux-release-x64-try,vm-aot-optimization-level-linux-release-x64-try,vm-aot-win-debug-arm64-try,vm-aot-win-debug-x64c-try,vm-aot-win-release-x64-try,vm-appjit-linux-debug-x64-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-arm64c-try,vm-ffi-qemu-linux-release-arm-try,vm-ffi-qemu-linux-release-riscv64-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-x64-try,vm-reload-linux-debug-x64-try,vm-reload-rollback-linux-debug-x64-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/+/360882
Reviewed-by: Jens Johansen <jensj@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Daco Harkes <dacoharkes@google.com>
Reviewed-by: Tess Strickland <sstrickl@google.com>
Reviewed-by: Lasse Nielsen <lrn@google.com>
This commit is contained in:
Daco Harkes 2024-04-25 10:06:16 +00:00 committed by Commit Queue
parent 8c1475c6c2
commit 4b66657b98
56 changed files with 7490 additions and 415 deletions

View file

@ -5900,6 +5900,28 @@ const MessageCode messageFfiAddressOfMustBeNative = const MessageCode(
r"""Argument to 'Native.addressOf' must be annotated with @Native.""",
);
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Code<Null> codeFfiAddressPosition = messageFfiAddressPosition;
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const MessageCode messageFfiAddressPosition = const MessageCode(
"FfiAddressPosition",
problemMessage:
r"""The '.address' expression can only be used as argument to a leaf native external call.""",
);
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Code<Null> codeFfiAddressReceiver = messageFfiAddressReceiver;
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const MessageCode messageFfiAddressReceiver = const MessageCode(
"FfiAddressReceiver",
problemMessage:
r"""The receiver of '.address' must be a concrete 'TypedData', a concrete 'TypedData' '[]', an 'Array', an 'Array' '[]', a Struct field, or a Union field.""",
correctionMessage:
r"""Change the receiver of '.address' to one of the allowed kinds.""",
);
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Template<Message Function(String string, String name)>
templateFfiCompoundImplementsFinalizable =

View file

@ -1743,6 +1743,12 @@ FfiCode.ABI_SPECIFIC_INTEGER_MAPPING_MISSING:
FfiCode.ABI_SPECIFIC_INTEGER_MAPPING_UNSUPPORTED:
status: noFix
since: ~2.16
FfiCode.ADDRESS_POSITION:
status: needsEvaluation
since: ~3.5
FfiCode.ADDRESS_RECEIVER:
status: needsEvaluation
since: ~3.5
FfiCode.ANNOTATION_ON_POINTER_FIELD:
status: needsFix
FfiCode.ARGUMENT_MUST_BE_A_CONSTANT:

View file

@ -65,6 +65,23 @@ class FfiCode extends AnalyzerErrorCode {
hasPublishedDocs: true,
);
/// No parameters.
static const FfiCode ADDRESS_POSITION = FfiCode(
'ADDRESS_POSITION',
"The '.address' expression can only be used as argument to a leaf native "
"external call.",
);
/// No parameters.
static const FfiCode ADDRESS_RECEIVER = FfiCode(
'ADDRESS_RECEIVER',
"The receiver of '.address' must be a concrete 'TypedData', a concrete "
"'TypedData' '[]', an 'Array', an 'Array' '[]', a Struct field, or a "
"Union field.",
correctionMessage:
"Change the receiver of '.address' to one of the allowed kinds.",
);
/// No parameters.
static const FfiCode ANNOTATION_ON_POINTER_FIELD = FfiCode(
'ANNOTATION_ON_POINTER_FIELD',

View file

@ -590,6 +590,8 @@ const List<ErrorCode> errorCodeValues = [
FfiCode.ABI_SPECIFIC_INTEGER_MAPPING_EXTRA,
FfiCode.ABI_SPECIFIC_INTEGER_MAPPING_MISSING,
FfiCode.ABI_SPECIFIC_INTEGER_MAPPING_UNSUPPORTED,
FfiCode.ADDRESS_POSITION,
FfiCode.ADDRESS_RECEIVER,
FfiCode.ANNOTATION_ON_POINTER_FIELD,
FfiCode.ARGUMENT_MUST_BE_A_CONSTANT,
FfiCode.ARGUMENT_MUST_BE_NATIVE,

View file

@ -35,6 +35,37 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
static const _nativeCallable = 'NativeCallable';
static const _opaqueClassName = 'Opaque';
static const _addressOfExtensionNames = {
..._addressOfCompoundExtensionNames,
..._addressOfPrimitiveExtensionNames,
..._addressOfTypedDataExtensionNames,
};
static const _addressOfCompoundExtensionNames = {
'ArrayAddress',
'StructAddress',
'UnionAddress',
};
static const _addressOfPrimitiveExtensionNames = {
'BoolAddress',
'DoubleAddress',
'IntAddress',
};
static const _addressOfTypedDataExtensionNames = {
'Float32ListAddress',
'Float64ListAddress',
'Int16ListAddress',
'Int32ListAddress',
'Int64ListAddress',
'Int8ListAddress',
'Uint16ListAddress',
'Uint32ListAddress',
'Uint64ListAddress',
'Uint8ListAddress',
};
static const Set<String> _primitiveIntegerNativeTypesFixedSize = {
'Int8',
'Int16',
@ -346,6 +377,10 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
if (element.name == 'ref') {
_validateRefPrefixedIdentifier(node);
}
} else if (enclosingElement.isAddressOfExtension) {
if (element.name == 'address') {
_validateAddressPrefixedIdentifier(node);
}
}
}
super.visitPrefixedIdentifier(node);
@ -361,6 +396,10 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
if (element.name == 'ref') {
_validateRefPropertyAccess(node);
}
} else if (enclosingElement.isAddressOfExtension) {
if (element.name == 'address') {
_validateAddressPropertyAccess(node);
}
}
}
super.visitPropertyAccess(node);
@ -986,6 +1025,84 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
}
}
/// Check that .address is only used in argument lists passed to native leaf
/// calls.
void _validateAddressPosition(Expression node, AstNode errorNode) {
final parent = node.parent;
final grandParent = parent?.parent;
if (parent is! ArgumentList ||
grandParent is! MethodInvocation ||
!grandParent.isNativeLeafInvocation) {
_errorReporter.atNode(
errorNode,
FfiCode.ADDRESS_POSITION,
);
}
}
void _validateAddressPrefixedIdentifier(PrefixedIdentifier node) {
final errorNode = node.identifier;
_validateAddressPosition(node, errorNode);
final extensionName = node.staticElement?.enclosingElement?.name;
final receiver = node.prefix;
_validateAddressReceiver(node, extensionName, receiver, errorNode);
}
void _validateAddressPropertyAccess(PropertyAccess node) {
final errorNode = node.propertyName;
_validateAddressPosition(node, errorNode);
final extensionName =
node.propertyName.staticElement?.enclosingElement?.name;
final receiver = node.target;
_validateAddressReceiver(node, extensionName, receiver, errorNode);
}
void _validateAddressReceiver(
Expression node,
String? extensionName,
Expression? receiver,
AstNode errorNode,
) {
if (_addressOfCompoundExtensionNames.contains(extensionName) ||
_addressOfTypedDataExtensionNames.contains(extensionName)) {
return; // Only primitives need their reciever checked.
}
if (receiver == null) {
return;
}
switch (receiver) {
case IndexExpression _:
// Array or TypedData element.
final arrayOrTypedData = receiver.target;
final type = arrayOrTypedData?.staticType;
if (type?.isArray ?? false) {
return;
}
if (type?.isTypedData ?? false) {
return;
}
case PrefixedIdentifier _:
// Struct or Union field.
final compound = receiver.prefix;
final type = compound.staticType;
if (type?.isCompoundSubtype ?? false) {
return;
}
case PropertyAccess _:
// Struct or Union field.
final compound = receiver.target;
final type = compound?.staticType;
if (type?.isCompoundSubtype ?? false) {
return;
}
default:
}
_errorReporter.atNode(
errorNode,
FfiCode.ADDRESS_RECEIVER,
);
}
void _validateAllocate(FunctionExpressionInvocation node) {
var typeArgumentTypes = node.typeArgumentTypes;
if (typeArgumentTypes == null || typeArgumentTypes.length != 1) {
@ -1959,6 +2076,22 @@ extension on ElementAnnotation {
// forwarding factory instead of the forwarded constructor.
}
/// @Native(isLeaf: true)
bool get isNativeLeaf {
final annotationValue = computeConstantValue();
final annotationType = annotationValue?.type; // Native<T>
if (annotationValue == null || annotationType is! InterfaceType) {
return false;
}
if (!annotationValue.isNative) {
return false;
}
return annotationValue
.getField(FfiVerifier._isLeafParamName)
?.toBoolValue() ??
false;
}
bool get isPacked {
final element = this.element;
return element is ConstructorElement &&
@ -1973,6 +2106,44 @@ extension on ElementAnnotation {
}
}
extension on FunctionElement {
/// @Native(isLeaf: true) external function.
bool get isNativeLeaf {
for (final annotation in metadata) {
if (annotation.isNativeLeaf) {
return true;
}
}
return false;
}
}
extension on MethodElement {
/// @Native(isLeaf: true) external function.
bool get isNativeLeaf {
for (final annotation in metadata) {
if (annotation.isNativeLeaf) {
return true;
}
}
return false;
}
}
extension on MethodInvocation {
/// Calls @Native(isLeaf: true) external function.
bool get isNativeLeafInvocation {
final element = methodName.staticElement;
if (element is FunctionElement) {
return element.isNativeLeaf;
}
if (element is MethodElement) {
return element.isNativeLeaf;
}
return false;
}
}
extension on DartObject {
bool get isDefaultAsset {
return switch (type) {
@ -2017,6 +2188,13 @@ extension on Element? {
return element is ClassElement && element.supertype.isAbiSpecificInteger;
}
bool get isAddressOfExtension {
final element = this;
return element is ExtensionElement &&
element.isFfiExtension &&
FfiVerifier._addressOfExtensionNames.contains(element.name);
}
/// Return `true` if this represents the extension `AllocatorAlloc`.
bool get isAllocatorExtension {
var element = this;

View file

@ -18697,6 +18697,15 @@ FfiCode:
const C();
}
```
ADDRESS_POSITION:
problemMessage: "The '.address' expression can only be used as argument to a leaf native external call."
hasPublishedDocs: false
comment: No parameters.
ADDRESS_RECEIVER:
problemMessage: "The receiver of '.address' must be a concrete 'TypedData', a concrete 'TypedData' '[]', an 'Array', an 'Array' '[]', a Struct field, or a Union field."
correctionMessage: Change the receiver of '.address' to one of the allowed kinds.
hasPublishedDocs: false
comment: No parameters.
ANNOTATION_ON_POINTER_FIELD:
problemMessage: "Fields in a struct class whose type is 'Pointer' shouldn't have any annotations."
correctionMessage: Try removing the annotation.

View file

@ -380,6 +380,8 @@ FastaUsageShort/analyzerCode: Fail
FastaUsageShort/example: Fail
FfiAbiSpecificIntegerInvalid/analyzerCode: Fail
FfiAbiSpecificIntegerMappingInvalid/analyzerCode: Fail
FfiAddressPosition/analyzerCode: Fail
FfiAddressReceiver/analyzerCode: Fail
FfiCompoundImplementsFinalizable/analyzerCode: Fail
FfiCreateOfStructOrUnion/analyzerCode: Fail
FfiDartTypeMismatch/analyzerCode: Fail

View file

@ -5039,6 +5039,17 @@ FfiAbiSpecificIntegerMappingInvalid:
problemMessage: "Classes extending 'AbiSpecificInteger' must have exactly one 'AbiSpecificIntegerMapping' annotation specifying the mapping from ABI to a NativeType integer with a fixed size."
external: test/ffi_test.dart
FfiAddressPosition:
# Used by dart:ffi
problemMessage: "The '.address' expression can only be used as argument to a leaf native external call."
external: test/ffi_test.dart
FfiAddressReceiver:
# Used by dart:ffi
problemMessage: "The receiver of '.address' must be a concrete 'TypedData', a concrete 'TypedData' '[]', an 'Array', an 'Array' '[]', a Struct field, or a Union field."
correctionMessage: "Change the receiver of '.address' to one of the allowed kinds."
external: test/ffi_test.dart
FfiCreateOfStructOrUnion:
# Used by dart:ffi
problemMessage: "Subclasses of 'Struct' and 'Union' are backed by native memory, and can't be instantiated by a generative constructor. Try allocating it via allocation, or load from a 'Pointer'."

View file

@ -143,6 +143,7 @@ tojs
trusttypes
type's
type3.#name
typeddata
typeof
u
unavailable

View file

@ -55,8 +55,10 @@ additionalExports = (ffi::Abi,
ffi::Allocator,
ffi::AllocatorAlloc,
ffi::Array,
ffi::ArrayAddress,
ffi::ArrayArray,
ffi::Bool,
ffi::BoolAddress,
ffi::BoolArray,
ffi::BoolPointer,
ffi::Char,
@ -65,28 +67,36 @@ additionalExports = (ffi::Abi,
ffi::Dart_NativeMessageHandler,
ffi::DefaultAsset,
ffi::Double,
ffi::DoubleAddress,
ffi::DoubleArray,
ffi::DoublePointer,
ffi::DynamicLibrary,
ffi::DynamicLibraryExtension,
ffi::Finalizable,
ffi::Float,
ffi::Float32ListAddress,
ffi::Float64ListAddress,
ffi::FloatArray,
ffi::FloatPointer,
ffi::Handle,
ffi::Int,
ffi::Int16,
ffi::Int16Array,
ffi::Int16ListAddress,
ffi::Int16Pointer,
ffi::Int32,
ffi::Int32Array,
ffi::Int32ListAddress,
ffi::Int32Pointer,
ffi::Int64,
ffi::Int64Array,
ffi::Int64ListAddress,
ffi::Int64Pointer,
ffi::Int8,
ffi::Int8Array,
ffi::Int8ListAddress,
ffi::Int8Pointer,
ffi::IntAddress,
ffi::IntPtr,
ffi::Long,
ffi::LongLong,
@ -109,22 +119,28 @@ additionalExports = (ffi::Abi,
ffi::Size,
ffi::SizedNativeType,
ffi::Struct,
ffi::StructAddress,
ffi::StructArray,
ffi::StructPointer,
ffi::Uint16,
ffi::Uint16Array,
ffi::Uint16ListAddress,
ffi::Uint16Pointer,
ffi::Uint32,
ffi::Uint32Array,
ffi::Uint32ListAddress,
ffi::Uint32Pointer,
ffi::Uint64,
ffi::Uint64Array,
ffi::Uint64ListAddress,
ffi::Uint64Pointer,
ffi::Uint8,
ffi::Uint8Array,
ffi::Uint8ListAddress,
ffi::Uint8Pointer,
ffi::UintPtr,
ffi::Union,
ffi::UnionAddress,
ffi::UnionArray,
ffi::UnionPointer,
ffi::UnsignedChar,
@ -150,8 +166,10 @@ additionalExports = (ffi::Abi,
ffi::Allocator,
ffi::AllocatorAlloc,
ffi::Array,
ffi::ArrayAddress,
ffi::ArrayArray,
ffi::Bool,
ffi::BoolAddress,
ffi::BoolArray,
ffi::BoolPointer,
ffi::Char,
@ -160,28 +178,36 @@ additionalExports = (ffi::Abi,
ffi::Dart_NativeMessageHandler,
ffi::DefaultAsset,
ffi::Double,
ffi::DoubleAddress,
ffi::DoubleArray,
ffi::DoublePointer,
ffi::DynamicLibrary,
ffi::DynamicLibraryExtension,
ffi::Finalizable,
ffi::Float,
ffi::Float32ListAddress,
ffi::Float64ListAddress,
ffi::FloatArray,
ffi::FloatPointer,
ffi::Handle,
ffi::Int,
ffi::Int16,
ffi::Int16Array,
ffi::Int16ListAddress,
ffi::Int16Pointer,
ffi::Int32,
ffi::Int32Array,
ffi::Int32ListAddress,
ffi::Int32Pointer,
ffi::Int64,
ffi::Int64Array,
ffi::Int64ListAddress,
ffi::Int64Pointer,
ffi::Int8,
ffi::Int8Array,
ffi::Int8ListAddress,
ffi::Int8Pointer,
ffi::IntAddress,
ffi::IntPtr,
ffi::Long,
ffi::LongLong,
@ -204,22 +230,28 @@ additionalExports = (ffi::Abi,
ffi::Size,
ffi::SizedNativeType,
ffi::Struct,
ffi::StructAddress,
ffi::StructArray,
ffi::StructPointer,
ffi::Uint16,
ffi::Uint16Array,
ffi::Uint16ListAddress,
ffi::Uint16Pointer,
ffi::Uint32,
ffi::Uint32Array,
ffi::Uint32ListAddress,
ffi::Uint32Pointer,
ffi::Uint64,
ffi::Uint64Array,
ffi::Uint64ListAddress,
ffi::Uint64Pointer,
ffi::Uint8,
ffi::Uint8Array,
ffi::Uint8ListAddress,
ffi::Uint8Pointer,
ffi::UintPtr,
ffi::Union,
ffi::UnionAddress,
ffi::UnionArray,
ffi::UnionPointer,
ffi::UnsignedChar,

View file

@ -55,8 +55,10 @@ additionalExports = (ffi::Abi,
ffi::Allocator,
ffi::AllocatorAlloc,
ffi::Array,
ffi::ArrayAddress,
ffi::ArrayArray,
ffi::Bool,
ffi::BoolAddress,
ffi::BoolArray,
ffi::BoolPointer,
ffi::Char,
@ -65,28 +67,36 @@ additionalExports = (ffi::Abi,
ffi::Dart_NativeMessageHandler,
ffi::DefaultAsset,
ffi::Double,
ffi::DoubleAddress,
ffi::DoubleArray,
ffi::DoublePointer,
ffi::DynamicLibrary,
ffi::DynamicLibraryExtension,
ffi::Finalizable,
ffi::Float,
ffi::Float32ListAddress,
ffi::Float64ListAddress,
ffi::FloatArray,
ffi::FloatPointer,
ffi::Handle,
ffi::Int,
ffi::Int16,
ffi::Int16Array,
ffi::Int16ListAddress,
ffi::Int16Pointer,
ffi::Int32,
ffi::Int32Array,
ffi::Int32ListAddress,
ffi::Int32Pointer,
ffi::Int64,
ffi::Int64Array,
ffi::Int64ListAddress,
ffi::Int64Pointer,
ffi::Int8,
ffi::Int8Array,
ffi::Int8ListAddress,
ffi::Int8Pointer,
ffi::IntAddress,
ffi::IntPtr,
ffi::Long,
ffi::LongLong,
@ -109,22 +119,28 @@ additionalExports = (ffi::Abi,
ffi::Size,
ffi::SizedNativeType,
ffi::Struct,
ffi::StructAddress,
ffi::StructArray,
ffi::StructPointer,
ffi::Uint16,
ffi::Uint16Array,
ffi::Uint16ListAddress,
ffi::Uint16Pointer,
ffi::Uint32,
ffi::Uint32Array,
ffi::Uint32ListAddress,
ffi::Uint32Pointer,
ffi::Uint64,
ffi::Uint64Array,
ffi::Uint64ListAddress,
ffi::Uint64Pointer,
ffi::Uint8,
ffi::Uint8Array,
ffi::Uint8ListAddress,
ffi::Uint8Pointer,
ffi::UintPtr,
ffi::Union,
ffi::UnionAddress,
ffi::UnionArray,
ffi::UnionPointer,
ffi::UnsignedChar,
@ -150,8 +166,10 @@ additionalExports = (ffi::Abi,
ffi::Allocator,
ffi::AllocatorAlloc,
ffi::Array,
ffi::ArrayAddress,
ffi::ArrayArray,
ffi::Bool,
ffi::BoolAddress,
ffi::BoolArray,
ffi::BoolPointer,
ffi::Char,
@ -160,28 +178,36 @@ additionalExports = (ffi::Abi,
ffi::Dart_NativeMessageHandler,
ffi::DefaultAsset,
ffi::Double,
ffi::DoubleAddress,
ffi::DoubleArray,
ffi::DoublePointer,
ffi::DynamicLibrary,
ffi::DynamicLibraryExtension,
ffi::Finalizable,
ffi::Float,
ffi::Float32ListAddress,
ffi::Float64ListAddress,
ffi::FloatArray,
ffi::FloatPointer,
ffi::Handle,
ffi::Int,
ffi::Int16,
ffi::Int16Array,
ffi::Int16ListAddress,
ffi::Int16Pointer,
ffi::Int32,
ffi::Int32Array,
ffi::Int32ListAddress,
ffi::Int32Pointer,
ffi::Int64,
ffi::Int64Array,
ffi::Int64ListAddress,
ffi::Int64Pointer,
ffi::Int8,
ffi::Int8Array,
ffi::Int8ListAddress,
ffi::Int8Pointer,
ffi::IntAddress,
ffi::IntPtr,
ffi::Long,
ffi::LongLong,
@ -204,22 +230,28 @@ additionalExports = (ffi::Abi,
ffi::Size,
ffi::SizedNativeType,
ffi::Struct,
ffi::StructAddress,
ffi::StructArray,
ffi::StructPointer,
ffi::Uint16,
ffi::Uint16Array,
ffi::Uint16ListAddress,
ffi::Uint16Pointer,
ffi::Uint32,
ffi::Uint32Array,
ffi::Uint32ListAddress,
ffi::Uint32Pointer,
ffi::Uint64,
ffi::Uint64Array,
ffi::Uint64ListAddress,
ffi::Uint64Pointer,
ffi::Uint8,
ffi::Uint8Array,
ffi::Uint8ListAddress,
ffi::Uint8Pointer,
ffi::UintPtr,
ffi::Union,
ffi::UnionAddress,
ffi::UnionArray,
ffi::UnionPointer,
ffi::UnsignedChar,

View file

@ -137,6 +137,37 @@ const List<NativeType> unalignedLoadsStores = [
NativeType.kDouble,
];
const List<String> addressOfExtensionsTypedData = [
'Float32List',
'Float64List',
'Int16List',
'Int32List',
'Int64List',
'Int8List',
'Uint16List',
'Uint32List',
'Uint64List',
'Uint8List',
];
const List<String> addressOfExtensionsCompound = [
'Array',
'Struct',
'Union',
];
const List<String> addressOfExtensionsPrimitive = [
'Bool',
'Double',
'Int',
];
const List<String> addressOfExtensions = [
...addressOfExtensionsCompound,
...addressOfExtensionsPrimitive,
...addressOfExtensionsTypedData,
];
enum FfiTypeCheckDirection {
// Passing a value from native code to Dart code.
nativeToDart,
@ -258,6 +289,7 @@ class FfiTransformer extends Transformer {
final Procedure arrayNestedDimensionsRest;
final Procedure structCreate;
final Procedure unionCreate;
final Constructor compoundFromTypedDataBase;
final Constructor structFromTypedDataBase;
final Constructor unionFromTypedDataBase;
final Constructor structFromTypedData;
@ -301,8 +333,13 @@ class FfiTransformer extends Transformer {
final Field nativeCallablePointerField;
final Procedure nativeAddressOf;
final Procedure nativePrivateAddressOf;
final List<Procedure> addressOfMethods;
final List<Procedure> addressOfMethodsCompound;
final List<Procedure> addressOfMethodsPrimitive;
final List<Procedure> addressOfMethodsTypedData;
final Class ffiCallClass;
final Field ffiCallIsLeafField;
final Field nativeIsLeafField;
late final InterfaceType nativeFieldWrapperClass1Type;
late final InterfaceType voidType;
@ -311,6 +348,7 @@ class FfiTransformer extends Transformer {
late final InterfaceType nativeTypeType;
// The Pointer type when instantiated to bounds.
late final InterfaceType pointerNativeTypeType;
late final InterfaceType compoundType;
/// Classes corresponding to [NativeType], indexed by [NativeType].
final Map<NativeType, Class> nativeTypesClasses;
@ -426,6 +464,8 @@ class FfiTransformer extends Transformer {
'dart:ffi', 'Array', 'get:_nestedDimensionsRest'),
structCreate = index.getProcedure('dart:ffi', 'Struct', 'create'),
unionCreate = index.getProcedure('dart:ffi', 'Union', 'create'),
compoundFromTypedDataBase =
index.getConstructor('dart:ffi', '_Compound', '_fromTypedDataBase'),
structFromTypedDataBase =
index.getConstructor('dart:ffi', 'Struct', '_fromTypedDataBase'),
unionFromTypedDataBase =
@ -591,8 +631,25 @@ class FfiTransformer extends Transformer {
index.getMember('dart:ffi', 'Native', 'addressOf') as Procedure,
nativePrivateAddressOf =
index.getMember('dart:ffi', 'Native', '_addressOf') as Procedure,
addressOfMethods = [
for (final name in addressOfExtensions)
index.getProcedure('dart:ffi', '${name}Address', 'get:address'),
],
addressOfMethodsPrimitive = [
for (final name in addressOfExtensionsPrimitive)
index.getProcedure('dart:ffi', '${name}Address', 'get:address'),
],
addressOfMethodsCompound = [
for (final name in addressOfExtensionsCompound)
index.getProcedure('dart:ffi', '${name}Address', 'get:address'),
],
addressOfMethodsTypedData = [
for (final name in addressOfExtensionsTypedData)
index.getProcedure('dart:ffi', '${name}Address', 'get:address'),
],
ffiCallClass = index.getClass('dart:ffi', '_FfiCall'),
ffiCallIsLeafField = index.getField('dart:ffi', '_FfiCall', 'isLeaf') {
ffiCallIsLeafField = index.getField('dart:ffi', '_FfiCall', 'isLeaf'),
nativeIsLeafField = index.getField('dart:ffi', 'Native', 'isLeaf') {
nativeFieldWrapperClass1Type = nativeFieldWrapperClass1Class.getThisType(
coreTypes, Nullability.nonNullable);
voidType = nativeTypesClasses[NativeType.kVoid]!
@ -606,6 +663,11 @@ class FfiTransformer extends Transformer {
intptrNativeTypeCfe =
NativeTypeCfe(this, InterfaceType(intptrClass, Nullability.nonNullable))
as AbiSpecificNativeTypeCfe;
compoundType = InterfaceType(
compoundClass,
Nullability.nonNullable,
const <DartType>[],
);
}
@override

View file

@ -62,7 +62,6 @@ class FfiNativeTransformer extends FfiTransformer {
final Field assetAssetField;
final Field nativeSymbolField;
final Field nativeAssetField;
final Field nativeIsLeafField;
final Field resolverField;
StringConstant? currentAsset;
@ -83,7 +82,6 @@ class FfiNativeTransformer extends FfiTransformer {
assetAssetField = index.getField('dart:ffi', 'DefaultAsset', 'id'),
nativeSymbolField = index.getField('dart:ffi', 'Native', 'symbol'),
nativeAssetField = index.getField('dart:ffi', 'Native', 'assetId'),
nativeIsLeafField = index.getField('dart:ffi', 'Native', 'isLeaf'),
resolverField = index.getField('dart:ffi', 'Native', '_ffi_resolver'),
super(index, coreTypes, hierarchy, diagnosticReporter,
referenceFromIndex);

View file

@ -5,11 +5,13 @@
import 'package:front_end/src/fasta/codes/fasta_codes.dart'
show
messageFfiAddressOfMustBeNative,
messageFfiAddressPosition,
messageFfiAddressReceiver,
messageFfiCreateOfStructOrUnion,
messageFfiExceptionalReturnNull,
messageFfiExpectedConstant,
templateFfiNativeCallableListenerReturnVoid,
templateFfiDartTypeMismatch,
templateFfiNativeCallableListenerReturnVoid,
templateFfiExpectedConstantArg,
templateFfiExpectedExceptionalReturn,
templateFfiExpectedNoExceptionalReturn,
@ -17,6 +19,7 @@ import 'package:front_end/src/fasta/codes/fasta_codes.dart'
templateFfiNotStatic;
import 'package:kernel/ast.dart';
import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
import 'package:kernel/clone.dart';
import 'package:kernel/constructor_tearoff_lowering.dart';
import 'package:kernel/core_types.dart';
import 'package:kernel/kernel.dart';
@ -138,6 +141,7 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
final target = node.target;
if (hierarchy.isSubclassOf(target.enclosingClass, compoundClass) &&
target.enclosingClass != arrayClass &&
target.enclosingClass != compoundClass &&
target.name != Name("#fromTypedDataBase") &&
target.name != Name("#fromTypedData")) {
diagnosticReporter.report(messageFfiCreateOfStructOrUnion,
@ -171,9 +175,9 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
return ConstructorInvocation(
constructors.firstWhere((c) => c.name == Name("#fromTypedData")),
Arguments([
(defaultExpression(node.arguments.positional.first) as Expression),
node.arguments.positional.first,
(positionalArguments.length >= 2
? defaultExpression(positionalArguments[1]) as Expression
? positionalArguments[1]
: ConstantExpression(IntConstant(0))),
// Length in bytes to check the typedData against.
sizeOfExpression,
@ -525,6 +529,18 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
return _transformCompoundCreate(node);
} else if (target == nativeAddressOf) {
return _replaceNativeAddressOf(node);
} else if (addressOfMethods.contains(target)) {
// The AST is visited recursively down. Handling of native invocations
// will inspect arguments for `<expr>.address` invocations. Any
// remaining invocations occur are places where `<expr>.address` is
// disallowed, so issue an error.
diagnosticReporter.report(
messageFfiAddressPosition, node.fileOffset, 1, node.location?.file);
} else {
final nativeAnnotation = memberGetNativeAnnotation(target);
if (nativeAnnotation != null && _isLeaf(nativeAnnotation)) {
return _replaceNativeCall(node, nativeAnnotation);
}
}
} on FfiStaticTypeError {
// It's OK to swallow the exception because the diagnostics issued will
@ -535,6 +551,12 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
return node;
}
bool _isLeaf(InstanceConstant native) {
return (native.fieldValues[nativeIsLeafField.fieldReference]
as BoolConstant)
.value;
}
/// Create Dart function which calls native code.
///
/// Adds a native effect invoking a compound constructors if this is used
@ -1340,24 +1362,8 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
StaticGet(:var targetReference) => targetReference.asMember,
_ => null,
};
Constant? nativeAnnotation;
if (potentiallyNativeTarget != null) {
for (final annotation in potentiallyNativeTarget.annotations) {
if (annotation
case ConstantExpression(constant: final InstanceConstant c)) {
if (c.classNode == coreTypes.pragmaClass) {
final name = c.fieldValues[coreTypes.pragmaName.fieldReference];
if (name is StringConstant &&
name.value == native.FfiNativeTransformer.nativeMarker) {
nativeAnnotation =
c.fieldValues[coreTypes.pragmaOptions.fieldReference]!;
break;
}
}
}
}
}
Constant? nativeAnnotation =
memberGetNativeAnnotation(potentiallyNativeTarget);
if (nativeAnnotation == null) {
diagnosticReporter.report(messageFfiAddressOfMustBeNative, arg.fileOffset,
@ -1376,6 +1382,347 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
Arguments([ConstantExpression(nativeAnnotation)], types: [nativeType]),
)..fileOffset = arg.fileOffset;
}
InstanceConstant? memberGetNativeAnnotation(Member? member) {
if (member == null) {
return null;
}
for (final annotation in member.annotations) {
if (annotation
case ConstantExpression(constant: final InstanceConstant c)) {
if (c.classNode == coreTypes.pragmaClass) {
final name = c.fieldValues[coreTypes.pragmaName.fieldReference];
if (name is StringConstant &&
name.value == native.FfiNativeTransformer.nativeMarker) {
return c.fieldValues[coreTypes.pragmaOptions.fieldReference]
as InstanceConstant;
}
}
}
}
return null;
}
StaticInvocation _replaceNativeCall(
StaticInvocation node,
InstanceConstant targetNativeAnnotation,
) {
final target = node.target;
if (targetNativeAnnotation.typeArguments.length != 1) {
return node;
}
final annotationType = targetNativeAnnotation.typeArguments.single;
if (annotationType is! FunctionType) {
return node;
}
final parameterTypes = [
for (final varDecl in target.function.positionalParameters) varDecl.type,
];
final numParams = parameterTypes.length;
String methodPostfix = '';
final newArguments = <Expression>[];
final newParameters = <VariableDeclaration>[];
bool isTransformed = false;
for (int i = 0; i < numParams; i++) {
final parameter = target.function.positionalParameters[i];
final parameterType = parameterTypes[i];
final argument = node.arguments.positional[i];
final (
postFix,
newType,
newArgument,
) = _replaceNativeCallParameterAndArgument(
parameter,
parameterType,
argument,
node.fileOffset,
);
methodPostfix += postFix;
if (postFix == 'C' || postFix == 'E' || postFix == 'T') {
isTransformed = true;
}
newParameters.add(VariableDeclaration(
parameter.name,
type: newType,
));
newArguments.add(newArgument);
}
if (!isTransformed) {
return node;
}
final newName = '${target.name.text}#$methodPostfix';
final Procedure newTarget;
final parent = target.parent;
final members = switch (parent) {
Library _ => parent.members,
Class _ => parent.members,
_ => throw UnimplementedError('Unexpected parent: ${parent}'),
};
final existingNewTarget = members
.whereType<Procedure>()
.where((element) => element.name.text == newName)
.firstOrNull;
if (existingNewTarget != null) {
newTarget = existingNewTarget;
} else {
final cloner = CloneProcedureWithoutBody();
newTarget = cloner.cloneProcedure(target, null);
newTarget.name = Name(newName);
newTarget.function.positionalParameters = newParameters;
setParents(newParameters, newTarget.function);
switch (parent) {
case Library _:
parent.addProcedure(newTarget);
case Class _:
parent.addProcedure(newTarget);
}
}
return StaticInvocation(
newTarget,
Arguments(newArguments),
);
}
/// Converts a single parameter with argument for [_replaceNativeCall].
(
/// '' for non-Pointer.
/// 'P' for Pointer.
/// 'T' for TypedData.
/// 'C' for _Compound (TypedData/Pointer and offset in bytes).
/// 'E' for errors.
String methodPostFix,
DartType parameterType,
Expression argument,
) _replaceNativeCallParameterAndArgument(
VariableDeclaration parameter,
DartType parameterType,
Expression argument,
int fileOffset,
) {
if (parameterType is! InterfaceType ||
parameterType.classNode != pointerClass) {
// Parameter is non-pointer. Keep unchanged.
return ('', parameterType, argument);
}
if (argument is! StaticInvocation ||
!addressOfMethods.contains(argument.target)) {
// The argument has type Pointer, but it's not produced by any of the
// `.address` getters.
// Argument must be a Pointer object. Keep unchanged.
return ('P', parameterType, argument);
}
if (addressOfMethodsTypedData.contains(argument.target)) {
final subExpression = argument.arguments.positional.single;
// Argument is `typedData.address`.
final typedDataType = InterfaceType(
typedDataClass,
Nullability.nonNullable,
const <DartType>[],
);
return ('T', typedDataType, subExpression);
}
final subExpression = argument.arguments.positional.single;
if (addressOfMethodsCompound.contains(argument.target)) {
// Argument is `structOrUnionOrArray.address`.
return ('C', compoundType, subExpression);
}
assert(addressOfMethodsPrimitive.contains(argument.target));
// Argument is an `expr.address` where `expr` is typed `bool`, `int`, or
// `double`. Analyze `expr` further.
switch (subExpression) {
case InstanceGet _:
// Look for `structOrUnion.member.address`.
final interfaceTarget = subExpression.interfaceTarget;
final enclosingClass = interfaceTarget.enclosingClass!;
final targetSuperClass = enclosingClass.superclass;
if (targetSuperClass == unionClass) {
// `expr` is a union member access.
// Union members have no additional offset. Pass in unchanged.
return ('C', compoundType, subExpression.receiver);
}
if (targetSuperClass == structClass) {
final getterName = interfaceTarget.name.text;
final offsetOfName = '$getterName#offsetOf';
final offsetGetter = enclosingClass.procedures
.firstWhere((e) => e.name.text == offsetOfName);
// `expr` is a struct member access. Struct members have an offset.
// Pass in a newly constructed `_Compound`, with adjusted offset.
return (
'C',
compoundType,
_generateCompoundOffsetBy(
subExpression.receiver,
StaticGet(offsetGetter),
fileOffset,
variableName: "${parameter.name}#value",
),
);
}
// Error, unrecognized getter.
case StaticInvocation _:
// Look for `array[index].address`.
// Extensions have already been desugared, so no enclosing extension.
final target = subExpression.target;
if (!target.isExtensionMember) break;
final positionalParameters = target.function.positionalParameters;
if (positionalParameters.length != 2) break;
final firstParamType = positionalParameters.first.type;
if (firstParamType is! InterfaceType) break;
if (firstParamType.classNode != arrayClass) break;
// Extensions have already been desugared, so original name is lost.
if (!target.name.text.endsWith('|[]')) break;
final DartType arrayElementType;
if (subExpression.arguments.types.isNotEmpty) {
// AbiSpecificInteger.
arrayElementType = subExpression.arguments.types.single;
} else {
arrayElementType = firstParamType.typeArguments.single;
}
final arrayElementSize =
inlineSizeOf(arrayElementType as InterfaceType)!;
// Array element. Pass in a newly constructed `_Compound`, with
// adjusted offset.
return (
'C',
compoundType,
_generateCompoundOffsetBy(
subExpression.arguments.positional[0],
multiply(
arrayElementSize,
subExpression.arguments.positional[1], // index.
),
fileOffset,
variableName: "${parameter.name}#value",
),
);
case InstanceInvocation _:
// Look for `typedData[index].address`
final receiverType =
staticTypeContext!.getExpressionType(subExpression.receiver);
final implementsTypedData = TypeEnvironment(coreTypes, hierarchy)
.isSubtypeOf(
receiverType,
InterfaceType(typedDataClass, Nullability.nonNullable),
SubtypeCheckMode.withNullabilities);
if (!implementsTypedData) break;
if (receiverType is! InterfaceType) break;
final classNode = receiverType.classNode;
final elementSizeInBytes = _typedDataElementSizeInBytes(classNode);
if (elementSizeInBytes == null) break;
// Typed Data element off. Pass in new _Compound with extra
// offset.
return (
'C',
compoundType,
ConstructorInvocation(
compoundFromTypedDataBase,
Arguments([
subExpression.receiver,
multiply(
ConstantExpression(IntConstant(elementSizeInBytes)),
subExpression.arguments.positional.first, // index.
),
]),
),
);
default:
}
diagnosticReporter.report(
messageFfiAddressReceiver,
argument.fileOffset,
1,
argument.location?.file,
);
// Pass nullptr to prevent cascading error messages.
return (
'E', // Error.
pointerVoidType,
StaticInvocation(
fromAddressInternal,
Arguments(
<Expression>[ConstantExpression(IntConstant(0))],
types: <DartType>[voidType],
),
),
);
}
int? _typedDataElementSizeInBytes(Class classNode) {
final name = classNode.name;
if (name.contains('8')) {
return 1;
} else if (name.contains('16')) {
return 2;
} else if (name.contains('32')) {
return 4;
} else if (name.contains('64')) {
return 8;
}
return null;
}
/// Returns:
///
/// ```
/// _Compound._fromTypedDataBase(
/// compound._typedDataBase,
/// compound._offsetInBytes + offsetInBytes,
/// )
/// ```
Expression _generateCompoundOffsetBy(
Expression compound,
Expression offsetInBytes,
int fileOffset, {
String variableName = "#compoundOffset",
}) {
final compoundType = InterfaceType(
compoundClass,
Nullability.nonNullable,
const <DartType>[],
);
final valueVar = VariableDeclaration(
variableName,
initializer: compound,
type: compoundType,
isSynthesized: true,
)..fileOffset = fileOffset;
final newArgument = BlockExpression(
Block([
valueVar,
]),
ConstructorInvocation(
compoundFromTypedDataBase,
Arguments([
getCompoundTypedDataBaseField(
VariableGet(valueVar),
fileOffset,
),
add(
getCompoundOffsetInBytesField(
VariableGet(valueVar),
fileOffset,
),
offsetInBytes,
),
]),
),
);
return newArgument;
}
}
extension<T extends Object> on List<T> {

View file

@ -0,0 +1,37 @@
// 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=3.5
import 'dart:ffi';
void main() {
// All of these pass a `_Compound`.
final myStruct = Struct.create<MyStruct>();
myNative(myStruct.address);
final myUnion = Union.create<MyUnion>();
myNative2(myUnion.address);
myNative3(myStruct.a.address);
}
@Native<Void Function(Pointer<MyStruct>)>(isLeaf: true)
external void myNative(Pointer<MyStruct> pointer);
final class MyStruct extends Struct {
@Array(10)
external Array<Int8> a;
}
@Native<Void Function(Pointer<MyUnion>)>(isLeaf: true)
external void myNative2(Pointer<MyUnion> pointer);
final class MyUnion extends Union {
@Int8()
external int a;
}
@Native<Void Function(Pointer<Int8>)>(isLeaf: true)
external void myNative3(Pointer<Int8> pointer);

View file

@ -0,0 +1,92 @@
library #lib;
import self as self;
import "dart:core" as core;
import "dart:ffi" as ffi;
import "dart:typed_data" as typ;
import "dart:ffi";
@#C8
final class MyStruct extends ffi::Struct {
constructor #fromTypedDataBase([@vm.inferred-arg-type.metadata=dart.typed_data::_Uint8List] synthesized core::Object #typedDataBase) → self::MyStruct
: super ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:1]
get a() → ffi::Array<ffi::Int8>
return new ffi::Array::_<ffi::Int8>([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] this.{ffi::_Compound::_typedDataBase}{core::Object}, [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] [@vm.inferred-type.metadata=dart.core::_Smi (value: 0)] self::MyStruct::a#offsetOf.{core::num::+}([@vm.direct-call.metadata=dart.ffi::_Compound._offsetInBytes] [@vm.inferred-type.metadata=int?] this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #C3, #C9);
[@vm.unboxing-info.metadata=()->i]
@#C11
static get a#offsetOf() → core::int
return #C13.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
[@vm.unboxing-info.metadata=()->i]
@#C11
static get #sizeOf() → core::int*
return #C14.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
}
@#C17
final class MyUnion extends ffi::Union {
constructor #fromTypedDataBase([@vm.inferred-arg-type.metadata=dart.typed_data::_Uint8List] synthesized core::Object #typedDataBase) → self::MyUnion
: super ffi::Union::_fromTypedDataBase(#typedDataBase)
;
[@vm.unboxing-info.metadata=()->i]
@#C11
static get #sizeOf() → core::int*
return #C19.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
}
static method main() → void {
final self::MyStruct myStruct = new self::MyStruct::#fromTypedDataBase([@vm.inferred-type.metadata=dart.typed_data::_Uint8List] typ::Uint8List::•([@vm.inferred-type.metadata=dart.core::_Smi (value: 10)] self::MyStruct::#sizeOf));
self::myNative#C(myStruct);
final self::MyUnion myUnion = new self::MyUnion::#fromTypedDataBase([@vm.inferred-type.metadata=dart.typed_data::_Uint8List] typ::Uint8List::•([@vm.inferred-type.metadata=dart.core::_Smi (value: 1)] self::MyUnion::#sizeOf));
self::myNative2#C(myUnion);
self::myNative3#C([@vm.direct-call.metadata=#lib::MyStruct.a] [@vm.inferred-type.metadata=dart.ffi::Array<dart.ffi::Int8>] myStruct.{self::MyStruct::a}{ffi::Array<ffi::Int8>});
}
@#C25
@#C27
external static method myNative#C([@vm.inferred-arg-type.metadata=#lib::MyStruct] ffi::_Compound pointer) → void;
@#C30
@#C31
external static method myNative2#C([@vm.inferred-arg-type.metadata=#lib::MyUnion] ffi::_Compound pointer) → void;
@#C34
@#C35
external static method myNative3#C([@vm.inferred-arg-type.metadata=dart.ffi::Array<dart.ffi::Int8>] ffi::_Compound pointer) → void;
constants {
#C1 = "vm:ffi:struct-fields"
#C2 = TypeLiteralConstant(ffi::Int8)
#C3 = 10
#C4 = ffi::_FfiInlineArray {elementType:#C2, length:#C3}
#C5 = <core::Type>[#C4]
#C6 = null
#C7 = ffi::_FfiStructLayout {fieldTypes:#C5, packing:#C6}
#C8 = core::pragma {name:#C1, options:#C7}
#C9 = <core::int*>[]
#C10 = "vm:prefer-inline"
#C11 = core::pragma {name:#C10, options:#C6}
#C12 = 0
#C13 = <core::int*>[#C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12, #C12]
#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 = <core::Type>[#C2]
#C16 = ffi::_FfiStructLayout {fieldTypes:#C15, packing:#C6}
#C17 = core::pragma {name:#C1, options:#C16}
#C18 = 1
#C19 = <core::int*>[#C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18]
#C20 = "cfe:ffi:native-marker"
#C21 = "myNative"
#C22 = "#lib"
#C23 = true
#C24 = ffi::Native<(ffi::Pointer<self::MyStruct>) → ffi::Void> {symbol:#C21, assetId:#C22, isLeaf:#C23}
#C25 = core::pragma {name:#C20, options:#C24}
#C26 = "vm:ffi:native"
#C27 = core::pragma {name:#C26, options:#C24}
#C28 = "myNative2"
#C29 = ffi::Native<(ffi::Pointer<self::MyUnion>) → ffi::Void> {symbol:#C28, assetId:#C22, isLeaf:#C23}
#C30 = core::pragma {name:#C20, options:#C29}
#C31 = core::pragma {name:#C26, options:#C29}
#C32 = "myNative3"
#C33 = ffi::Native<(ffi::Pointer<ffi::Int8>) → ffi::Void> {symbol:#C32, assetId:#C22, isLeaf:#C23}
#C34 = core::pragma {name:#C20, options:#C33}
#C35 = core::pragma {name:#C26, options:#C33}
}

View file

@ -0,0 +1,120 @@
library #lib;
import self as self;
import "dart:core" as core;
import "dart:ffi" as ffi;
import "dart:typed_data" as typ;
import "dart:ffi";
@#C8
final class MyStruct extends ffi::Struct {
synthetic constructor •() → self::MyStruct
: super ffi::Struct::•()
;
constructor #fromTypedDataBase(synthesized core::Object #typedDataBase, synthesized core::int #offsetInBytes) → self::MyStruct
: super ffi::Struct::_fromTypedDataBase(#typedDataBase, #offsetInBytes)
;
constructor #fromTypedData(synthesized typ::TypedData #typedData, synthesized core::int #offset, synthesized core::int #sizeInBytes) → self::MyStruct
: super ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C9
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
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
static get a#offsetOf() → core::int
return #C15.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
@#C13
static get #sizeOf() → core::int*
return #C11.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
}
@#C18
final class MyUnion extends ffi::Union {
synthetic constructor •() → self::MyUnion
: super ffi::Union::•()
;
constructor #fromTypedDataBase(synthesized core::Object #typedDataBase, synthesized core::int #offsetInBytes) → self::MyUnion
: super ffi::Union::_fromTypedDataBase(#typedDataBase, #offsetInBytes)
;
constructor #fromTypedData(synthesized typ::TypedData #typedData, synthesized core::int #offset, synthesized core::int #sizeInBytes) → self::MyUnion
: super ffi::Union::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C19
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
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
static get a#offsetOf() → core::int
return #C15.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
@#C13
static get #sizeOf() → core::int*
return #C21.{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);
self::myNative#C(myStruct);
final self::MyUnion myUnion = new self::MyUnion::#fromTypedDataBase(typ::Uint8List::•(self::MyUnion::#sizeOf), #C14);
self::myNative2#C(myUnion);
self::myNative3#C(myStruct.{self::MyStruct::a}{ffi::Array<ffi::Int8>});
}
@#C27
@#C29
external static method myNative(ffi::Pointer<self::MyStruct> pointer) → void;
@#C32
@#C33
external static method myNative2(ffi::Pointer<self::MyUnion> pointer) → void;
@#C36
@#C37
external static method myNative3(ffi::Pointer<ffi::Int8> pointer) → void;
@#C27
@#C29
external static method myNative#C(ffi::_Compound pointer) → void;
@#C32
@#C33
external static method myNative2#C(ffi::_Compound pointer) → void;
@#C36
@#C37
external static method myNative3#C(ffi::_Compound pointer) → void;
constants {
#C1 = "vm:ffi:struct-fields"
#C2 = TypeLiteralConstant(ffi::Int8)
#C3 = 10
#C4 = ffi::_FfiInlineArray {elementType:#C2, length:#C3}
#C5 = <core::Type>[#C4]
#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}
}

View file

@ -0,0 +1,67 @@
// 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=3.5
import 'dart:ffi';
void main() {
// This should create a `_Compound` with the right offset.
final myStruct = Struct.create<MyStruct>();
myNative(
myStruct.a.address,
myStruct.b.address,
);
// Unions do not need to create a view with an offset.
final myUnion = Union.create<MyUnion>();
myNative(
myUnion.a.address,
myUnion.b.address,
);
// This should create a `_Compound` with the right offset.
myNative(
myStruct.array[3].address,
myStruct.array[4].address,
);
// This should create a `_Compound` with the right offset.
myNative(
myStruct.array2[3].address,
myStruct.array2[4].address,
);
}
@Native<
Void Function(
Pointer<Int8>,
Pointer<Int8>,
)>(isLeaf: true)
external void myNative(
Pointer<Int8> pointer,
Pointer<Int8> pointer2,
);
final class MyStruct extends Struct {
@Int8()
external int a;
@Int8()
external int b;
@Array(10)
external Array<Int8> array;
@Array(10)
external Array<UnsignedLong> array2;
}
final class MyUnion extends Union {
@Int8()
external int a;
@Int8()
external int b;
}

View file

@ -0,0 +1,119 @@
library #lib;
import self as self;
import "dart:core" as core;
import "dart:ffi" as ffi;
import "dart:typed_data" as typ;
import "dart:ffi";
@#C10
final class MyStruct extends ffi::Struct {
constructor #fromTypedDataBase([@vm.inferred-arg-type.metadata=dart.typed_data::_Uint8List] synthesized core::Object #typedDataBase) → self::MyStruct
: super ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:1]
get array() → ffi::Array<ffi::Int8>
return new ffi::Array::_<ffi::Int8>([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] this.{ffi::_Compound::_typedDataBase}{core::Object}, [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] [@vm.inferred-type.metadata=dart.core::_Smi (value: 2)] self::MyStruct::array#offsetOf.{core::num::+}([@vm.direct-call.metadata=dart.ffi::_Compound._offsetInBytes] [@vm.inferred-type.metadata=int?] this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #C3, #C11);
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:2]
get array2() → ffi::Array<ffi::UnsignedLong>
return new ffi::Array::_<ffi::UnsignedLong>([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] this.{ffi::_Compound::_typedDataBase}{core::Object}, [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] [@vm.inferred-type.metadata=dart.core::_Smi] self::MyStruct::array2#offsetOf.{core::num::+}([@vm.direct-call.metadata=dart.ffi::_Compound._offsetInBytes] [@vm.inferred-type.metadata=int?] this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #C3, #C11);
[@vm.unboxing-info.metadata=()->i]
@#C13
static get a#offsetOf() → core::int
return #C15.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
[@vm.unboxing-info.metadata=()->i]
@#C13
static get b#offsetOf() → core::int
return #C17.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
[@vm.unboxing-info.metadata=()->i]
@#C13
static get array#offsetOf() → core::int
return #C19.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
[@vm.unboxing-info.metadata=()->i]
@#C13
static get array2#offsetOf() → core::int
return #C22.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
[@vm.unboxing-info.metadata=()->i]
@#C13
static get #sizeOf() → core::int*
return #C25.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
}
@#C28
final class MyUnion extends ffi::Union {
constructor #fromTypedDataBase([@vm.inferred-arg-type.metadata=dart.typed_data::_Uint8List] synthesized core::Object #typedDataBase) → self::MyUnion
: super ffi::Union::_fromTypedDataBase(#typedDataBase)
;
[@vm.unboxing-info.metadata=()->i]
@#C13
static get #sizeOf() → core::int*
return #C17.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
}
static method main() → void {
final self::MyStruct myStruct = new self::MyStruct::#fromTypedDataBase([@vm.inferred-type.metadata=dart.typed_data::_Uint8List] typ::Uint8List::•([@vm.inferred-type.metadata=dart.core::_Smi] self::MyStruct::#sizeOf));
self::myNative#CC( block {
synthesized ffi::_Compound pointer#value = myStruct;
} =>new ffi::_Compound::_fromTypedDataBase([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] pointer#value.{ffi::_Compound::_typedDataBase}{core::Object}, [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+??] [@vm.inferred-type.metadata=int (skip check)] [@vm.direct-call.metadata=dart.ffi::_Compound._offsetInBytes] [@vm.inferred-type.metadata=int?] pointer#value.{ffi::_Compound::_offsetInBytes}{core::int}.{core::num::+}([@vm.inferred-type.metadata=dart.core::_Smi (value: 0)] self::MyStruct::a#offsetOf){(core::num) → core::num}), block {
synthesized ffi::_Compound pointer2#value = myStruct;
} =>new ffi::_Compound::_fromTypedDataBase([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] pointer2#value.{ffi::_Compound::_typedDataBase}{core::Object}, [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+??] [@vm.inferred-type.metadata=int (skip check)] [@vm.direct-call.metadata=dart.ffi::_Compound._offsetInBytes] [@vm.inferred-type.metadata=int?] pointer2#value.{ffi::_Compound::_offsetInBytes}{core::int}.{core::num::+}([@vm.inferred-type.metadata=dart.core::_Smi (value: 1)] self::MyStruct::b#offsetOf){(core::num) → core::num}));
final self::MyUnion myUnion = new self::MyUnion::#fromTypedDataBase([@vm.inferred-type.metadata=dart.typed_data::_Uint8List] typ::Uint8List::•([@vm.inferred-type.metadata=dart.core::_Smi (value: 1)] self::MyUnion::#sizeOf));
self::myNative#CC(myUnion, myUnion);
self::myNative#CC( block {
synthesized ffi::_Compound pointer#value = [@vm.direct-call.metadata=#lib::MyStruct.array] [@vm.inferred-type.metadata=dart.ffi::Array<dart.ffi::Int8>] myStruct.{self::MyStruct::array}{ffi::Array<ffi::Int8>};
} =>new ffi::_Compound::_fromTypedDataBase([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] pointer#value.{ffi::_Compound::_typedDataBase}{core::Object}, [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+??] [@vm.inferred-type.metadata=int (skip check)] [@vm.direct-call.metadata=dart.ffi::_Compound._offsetInBytes] [@vm.inferred-type.metadata=int?] pointer#value.{ffi::_Compound::_offsetInBytes}{core::int}.{core::num::+}([@vm.direct-call.metadata=dart.core::_IntegerImplementation.*] [@vm.inferred-type.metadata=int (skip check)] #C16.{core::num::*}(3){(core::num) → core::num}){(core::num) → core::num}), block {
synthesized ffi::_Compound pointer2#value = [@vm.direct-call.metadata=#lib::MyStruct.array] [@vm.inferred-type.metadata=dart.ffi::Array<dart.ffi::Int8>] myStruct.{self::MyStruct::array}{ffi::Array<ffi::Int8>};
} =>new ffi::_Compound::_fromTypedDataBase([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] pointer2#value.{ffi::_Compound::_typedDataBase}{core::Object}, [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+??] [@vm.inferred-type.metadata=int (skip check)] [@vm.direct-call.metadata=dart.ffi::_Compound._offsetInBytes] [@vm.inferred-type.metadata=int?] pointer2#value.{ffi::_Compound::_offsetInBytes}{core::int}.{core::num::+}([@vm.direct-call.metadata=dart.core::_IntegerImplementation.*] [@vm.inferred-type.metadata=int (skip check)] #C16.{core::num::*}(4){(core::num) → core::num}){(core::num) → core::num}));
self::myNative#CC( block {
synthesized ffi::_Compound pointer#value = [@vm.direct-call.metadata=#lib::MyStruct.array2] [@vm.inferred-type.metadata=dart.ffi::Array<dart.ffi::UnsignedLong>] myStruct.{self::MyStruct::array2}{ffi::Array<ffi::UnsignedLong>};
} =>new ffi::_Compound::_fromTypedDataBase([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] pointer#value.{ffi::_Compound::_typedDataBase}{core::Object}, [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+??] [@vm.inferred-type.metadata=int (skip check)] [@vm.direct-call.metadata=dart.ffi::_Compound._offsetInBytes] [@vm.inferred-type.metadata=int?] pointer#value.{ffi::_Compound::_offsetInBytes}{core::int}.{core::num::+}([@vm.direct-call.metadata=dart.core::_IntegerImplementation.*] [@vm.inferred-type.metadata=int (skip check)] [@vm.inferred-type.metadata=dart.core::_Smi] ffi::UnsignedLong::#sizeOf.{core::num::*}(3){(core::num) → core::num}){(core::num) → core::num}), block {
synthesized ffi::_Compound pointer2#value = [@vm.direct-call.metadata=#lib::MyStruct.array2] [@vm.inferred-type.metadata=dart.ffi::Array<dart.ffi::UnsignedLong>] myStruct.{self::MyStruct::array2}{ffi::Array<ffi::UnsignedLong>};
} =>new ffi::_Compound::_fromTypedDataBase([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] pointer2#value.{ffi::_Compound::_typedDataBase}{core::Object}, [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+??] [@vm.inferred-type.metadata=int (skip check)] [@vm.direct-call.metadata=dart.ffi::_Compound._offsetInBytes] [@vm.inferred-type.metadata=int?] pointer2#value.{ffi::_Compound::_offsetInBytes}{core::int}.{core::num::+}([@vm.direct-call.metadata=dart.core::_IntegerImplementation.*] [@vm.inferred-type.metadata=int (skip check)] [@vm.inferred-type.metadata=dart.core::_Smi] ffi::UnsignedLong::#sizeOf.{core::num::*}(4){(core::num) → core::num}){(core::num) → core::num}));
}
@#C34
@#C36
external static method myNative#CC([@vm.inferred-arg-type.metadata=!] ffi::_Compound pointer, [@vm.inferred-arg-type.metadata=!] ffi::_Compound pointer2) → void;
constants {
#C1 = "vm:ffi:struct-fields"
#C2 = TypeLiteralConstant(ffi::Int8)
#C3 = 10
#C4 = ffi::_FfiInlineArray {elementType:#C2, length:#C3}
#C5 = TypeLiteralConstant(ffi::UnsignedLong)
#C6 = ffi::_FfiInlineArray {elementType:#C5, length:#C3}
#C7 = <core::Type>[#C2, #C2, #C4, #C6]
#C8 = null
#C9 = ffi::_FfiStructLayout {fieldTypes:#C7, packing:#C8}
#C10 = core::pragma {name:#C1, options:#C9}
#C11 = <core::int*>[]
#C12 = "vm:prefer-inline"
#C13 = core::pragma {name:#C12, options:#C8}
#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 = 1
#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 = 2
#C19 = <core::int*>[#C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18, #C18]
#C20 = 12
#C21 = 16
#C22 = <core::int*>[#C20, #C21, #C20, #C21, #C21, #C21, #C21, #C21, #C20, #C21, #C21, #C20, #C21, #C20, #C21, #C20, #C21, #C21, #C21, #C20, #C20, #C20]
#C23 = 52
#C24 = 96
#C25 = <core::int*>[#C23, #C24, #C23, #C24, #C24, #C24, #C24, #C24, #C23, #C24, #C24, #C23, #C24, #C23, #C24, #C23, #C24, #C24, #C24, #C23, #C23, #C23]
#C26 = <core::Type>[#C2, #C2]
#C27 = ffi::_FfiStructLayout {fieldTypes:#C26, packing:#C8}
#C28 = core::pragma {name:#C1, options:#C27}
#C29 = "cfe:ffi:native-marker"
#C30 = "myNative"
#C31 = "#lib"
#C32 = true
#C33 = ffi::Native<(ffi::Pointer<ffi::Int8>, ffi::Pointer<ffi::Int8>) → ffi::Void> {symbol:#C30, assetId:#C31, isLeaf:#C32}
#C34 = core::pragma {name:#C29, options:#C33}
#C35 = "vm:ffi:native"
#C36 = core::pragma {name:#C35, options:#C33}
}

View file

@ -0,0 +1,162 @@
library #lib;
import self as self;
import "dart:core" as core;
import "dart:ffi" as ffi;
import "dart:typed_data" as typ;
import "dart:ffi";
@#C10
final class MyStruct extends ffi::Struct {
synthetic constructor •() → self::MyStruct
: super ffi::Struct::•()
;
constructor #fromTypedDataBase(synthesized core::Object #typedDataBase, synthesized core::int #offsetInBytes) → self::MyStruct
: super ffi::Struct::_fromTypedDataBase(#typedDataBase, #offsetInBytes)
;
constructor #fromTypedData(synthesized typ::TypedData #typedData, synthesized core::int #offset, synthesized core::int #sizeInBytes) → self::MyStruct
: super ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C11
get a() → core::int
return ffi::_loadInt8(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyStruct::a#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num});
@#C11
set a(synthesized core::int #externalFieldValue) → void
return ffi::_storeInt8(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyStruct::a#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num}, #externalFieldValue);
@#C11
get b() → core::int
return ffi::_loadInt8(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyStruct::b#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num});
@#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
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
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
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
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
static get a#offsetOf() → core::int
return #C21.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
@#C19
static get b#offsetOf() → core::int
return #C23.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
@#C19
static get array#offsetOf() → core::int
return #C25.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
@#C19
static get array2#offsetOf() → core::int
return #C28.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
@#C19
static get #sizeOf() → core::int*
return #C31.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
}
@#C34
final class MyUnion extends ffi::Union {
synthetic constructor •() → self::MyUnion
: super ffi::Union::•()
;
constructor #fromTypedDataBase(synthesized core::Object #typedDataBase, synthesized core::int #offsetInBytes) → self::MyUnion
: super ffi::Union::_fromTypedDataBase(#typedDataBase, #offsetInBytes)
;
constructor #fromTypedData(synthesized typ::TypedData #typedData, synthesized core::int #offset, synthesized core::int #sizeInBytes) → self::MyUnion
: super ffi::Union::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C11
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});
@#C11
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);
@#C11
get b() → core::int
return ffi::_loadInt8(this.{ffi::_Compound::_typedDataBase}{core::Object}, self::MyUnion::b#offsetOf.{core::num::+}(this.{ffi::_Compound::_offsetInBytes}{core::int}){(core::num) → core::num});
@#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
static get a#offsetOf() → core::int
return #C21.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
@#C19
static get b#offsetOf() → core::int
return #C21.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
@#C19
static get #sizeOf() → core::int*
return #C23.{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);
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);
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 {
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}));
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
external static method myNative(ffi::Pointer<ffi::Int8> pointer, ffi::Pointer<ffi::Int8> pointer2) → void;
@#C40
@#C42
external static method myNative#CC(ffi::_Compound pointer, ffi::_Compound pointer2) → void;
constants {
#C1 = "vm:ffi:struct-fields"
#C2 = TypeLiteralConstant(ffi::Int8)
#C3 = 10
#C4 = ffi::_FfiInlineArray {elementType:#C2, length:#C3}
#C5 = TypeLiteralConstant(ffi::UnsignedLong)
#C6 = ffi::_FfiInlineArray {elementType:#C5, length:#C3}
#C7 = <core::Type>[#C2, #C2, #C4, #C6]
#C8 = null
#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}
}

View file

@ -0,0 +1,51 @@
// 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=3.5
import 'dart:ffi';
import 'dart:typed_data';
void main() {
// Invocation with no `.address`, should use the original native.
final pointer = nullptr.cast<Int8>();
myNative(
pointer,
pointer,
1,
);
// Invocations with `.address` should invoke a copy, but the same copy.
final typedData = Int8List(20);
myNative(
typedData.address,
typedData.address,
2,
);
myNative(
Int8List.sublistView(typedData, 4).address,
typedData.address,
3,
);
// And invocations with different arguments being TypedDataBase should
// have a different copy.
myNative(
pointer,
typedData.address,
4,
);
}
@Native<
Int8 Function(
Pointer<Int8>,
Pointer<Int8>,
Int8,
)>(isLeaf: true)
external int myNative(
Pointer<Int8> pointer,
Pointer<Int8> pointer2,
int nonPointer,
);

View file

@ -0,0 +1,42 @@
library #lib;
import self as self;
import "dart:ffi" as ffi;
import "dart:typed_data" as typ;
import "dart:core" as core;
import "dart:ffi";
import "dart:typed_data";
static method main() → void {
final ffi::Pointer<ffi::Int8> pointer = [@vm.direct-call.metadata=dart.ffi::Pointer.cast] [@vm.inferred-type.metadata=dart.ffi::Pointer (skip check)] [@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::nullptr.{ffi::Pointer::cast}<ffi::Int8>(){() → ffi::Pointer<ffi::Int8>};
self::myNative(pointer, pointer, 1);
final typ::Int8List typedData = [@vm.inferred-type.metadata=dart.typed_data::_Int8List] typ::Int8List::•(20);
self::myNative#TT(typedData, typedData, 2);
self::myNative#TT([@vm.inferred-type.metadata=dart.typed_data::_Int8ArrayView] typ::Int8List::sublistView(typedData), typedData, 3);
self::myNative#PT(pointer, typedData, 4);
}
[@vm.unboxing-info.metadata=(b,b,i)->i]
@#C6
@#C8
external static method myNative([@vm.inferred-arg-type.metadata=dart.ffi::Pointer] ffi::Pointer<ffi::Int8> pointer, [@vm.inferred-arg-type.metadata=dart.ffi::Pointer] ffi::Pointer<ffi::Int8> pointer2, [@vm.inferred-arg-type.metadata=dart.core::_Smi (value: 1)] core::int nonPointer) → core::int;
[@vm.unboxing-info.metadata=(b,b,i)->i]
@#C6
@#C8
external static method myNative#TT([@vm.inferred-arg-type.metadata=!] typ::TypedData pointer, [@vm.inferred-arg-type.metadata=dart.typed_data::_Int8List] typ::TypedData pointer2, [@vm.inferred-arg-type.metadata=dart.core::_Smi] core::int nonPointer) → core::int;
[@vm.unboxing-info.metadata=(b,b,i)->i]
@#C6
@#C8
external static method myNative#PT([@vm.inferred-arg-type.metadata=dart.ffi::Pointer] ffi::Pointer<ffi::Int8> pointer, [@vm.inferred-arg-type.metadata=dart.typed_data::_Int8List] typ::TypedData pointer2, [@vm.inferred-arg-type.metadata=dart.core::_Smi (value: 4)] core::int nonPointer) → core::int;
constants {
#C1 = "cfe:ffi:native-marker"
#C2 = "myNative"
#C3 = "#lib"
#C4 = true
#C5 = ffi::Native<(ffi::Pointer<ffi::Int8>, ffi::Pointer<ffi::Int8>, ffi::Int8) → ffi::Int8> {symbol:#C2, assetId:#C3, isLeaf:#C4}
#C6 = core::pragma {name:#C1, options:#C5}
#C7 = "vm:ffi:native"
#C8 = core::pragma {name:#C7, options:#C5}
}

View file

@ -0,0 +1,36 @@
library #lib;
import self as self;
import "dart:ffi" as ffi;
import "dart:typed_data" as typ;
import "dart:core" as core;
import "dart:ffi";
import "dart:typed_data";
static method main() → void {
final ffi::Pointer<ffi::Int8> pointer = ffi::nullptr.{ffi::Pointer::cast}<ffi::Int8>(){() → ffi::Pointer<ffi::Int8>};
self::myNative(pointer, pointer, 1);
final typ::Int8List typedData = typ::Int8List::•(20);
self::myNative#TT(typedData, typedData, 2);
self::myNative#TT(typ::Int8List::sublistView(typedData, 4), typedData, 3);
self::myNative#PT(pointer, typedData, 4);
}
@#C6
@#C8
external static method myNative(ffi::Pointer<ffi::Int8> pointer, ffi::Pointer<ffi::Int8> pointer2, core::int nonPointer) → core::int;
@#C6
@#C8
external static method myNative#TT(typ::TypedData pointer, typ::TypedData pointer2, core::int nonPointer) → core::int;
@#C6
@#C8
external static method myNative#PT(ffi::Pointer<ffi::Int8> pointer, typ::TypedData pointer2, core::int nonPointer) → core::int;
constants {
#C1 = "cfe:ffi:native-marker"
#C2 = "myNative"
#C3 = "#lib"
#C4 = true
#C5 = ffi::Native<(ffi::Pointer<ffi::Int8>, ffi::Pointer<ffi::Int8>, ffi::Int8) → ffi::Int8> {symbol:#C2, assetId:#C3, isLeaf:#C4}
#C6 = core::pragma {name:#C1, options:#C5}
#C7 = "vm:ffi:native"
#C8 = core::pragma {name:#C7, options:#C5}
}

View file

@ -0,0 +1,30 @@
// 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=3.5
import 'dart:ffi';
import 'dart:typed_data';
void main() {
// This needs to create a view on the underlying typed data.
// Or, create a `_Compound` with an offset.
final typedData = Int8List(20);
SomeClass.myNative(
typedData[3].address,
typedData[8].address,
);
}
final class SomeClass {
@Native<
Int8 Function(
Pointer<Int8>,
Pointer<Int8>,
)>(isLeaf: true)
external static int myNative(
Pointer<Int8> pointer,
Pointer<Int8> pointer2,
);
}

View file

@ -0,0 +1,31 @@
library #lib;
import self as self;
import "dart:core" as core;
import "dart:ffi" as ffi;
import "dart:typed_data" as typ;
import "dart:ffi";
import "dart:typed_data";
abstract final class SomeClass extends core::Object {
[@vm.unboxing-info.metadata=(b,b)->i]
@#C6
@#C8
external static method myNative#CC([@vm.inferred-arg-type.metadata=dart.ffi::_Compound] ffi::_Compound pointer, [@vm.inferred-arg-type.metadata=dart.ffi::_Compound] ffi::_Compound pointer2) → core::int;
}
static method main() → void {
final typ::Int8List typedData = [@vm.inferred-type.metadata=dart.typed_data::_Int8List] typ::Int8List::•(20);
self::SomeClass::myNative#CC(new ffi::_Compound::_fromTypedDataBase(typedData, [@vm.direct-call.metadata=dart.core::_IntegerImplementation.*] [@vm.inferred-type.metadata=int (skip check)] #C9.{core::num::*}(3){(core::num) → core::num}), new ffi::_Compound::_fromTypedDataBase(typedData, [@vm.direct-call.metadata=dart.core::_IntegerImplementation.*] [@vm.inferred-type.metadata=int (skip check)] #C9.{core::num::*}(8){(core::num) → core::num}));
}
constants {
#C1 = "cfe:ffi:native-marker"
#C2 = "myNative"
#C3 = "#lib"
#C4 = true
#C5 = ffi::Native<(ffi::Pointer<ffi::Int8>, ffi::Pointer<ffi::Int8>) → ffi::Int8> {symbol:#C2, assetId:#C3, isLeaf:#C4}
#C6 = core::pragma {name:#C1, options:#C5}
#C7 = "vm:ffi:native"
#C8 = core::pragma {name:#C7, options:#C5}
#C9 = 1
}

View file

@ -0,0 +1,35 @@
library #lib;
import self as self;
import "dart:core" as core;
import "dart:ffi" as ffi;
import "dart:typed_data" as typ;
import "dart:ffi";
import "dart:typed_data";
final class SomeClass extends core::Object {
synthetic constructor •() → self::SomeClass
: super core::Object::•()
;
@#C6
@#C8
external static method myNative(ffi::Pointer<ffi::Int8> pointer, ffi::Pointer<ffi::Int8> pointer2) → core::int;
@#C6
@#C8
external static method myNative#CC(ffi::_Compound pointer, ffi::_Compound pointer2) → core::int;
}
static method main() → void {
final typ::Int8List typedData = typ::Int8List::•(20);
self::SomeClass::myNative#CC(new ffi::_Compound::_fromTypedDataBase(typedData, #C9.{core::num::*}(3){(core::num) → core::num}), new ffi::_Compound::_fromTypedDataBase(typedData, #C9.{core::num::*}(8){(core::num) → core::num}));
}
constants {
#C1 = "cfe:ffi:native-marker"
#C2 = "myNative"
#C3 = "#lib"
#C4 = true
#C5 = ffi::Native<(ffi::Pointer<ffi::Int8>, ffi::Pointer<ffi::Int8>) → ffi::Int8> {symbol:#C2, assetId:#C3, isLeaf:#C4}
#C6 = core::pragma {name:#C1, options:#C5}
#C7 = "vm:ffi:native"
#C8 = core::pragma {name:#C7, options:#C5}
#C9 = 1
}

View file

@ -1087,6 +1087,7 @@ shared_library("ffi_test_functions") {
"ffi_test/ffi_test_fields.c",
"ffi_test/ffi_test_functions.cc",
"ffi_test/ffi_test_functions_generated.cc",
"ffi_test/ffi_test_functions_generated_2.cc",
"ffi_test/ffi_test_functions_vmspecific.cc",
]
if (is_win && current_cpu == "x64") {

File diff suppressed because it is too large Load diff

View file

@ -47,9 +47,15 @@ class Container {
Version(2, 13),
);
static const typedData = Container(
'TypedData',
Version(3, 5),
);
static const values = [
pointer,
array,
typedData,
];
}
@ -294,6 +300,41 @@ $since extension ${nativeType}Array on Array<$nativeType> {
}
""");
case Container.typedData:
if (typedListType != kDoNotEmit) {
buffer.write("""
$since extension ${typedListType}Address on $typedListType {
/// The memory address of the underlying data.
///
/// An expression of the form `expression.address` denoting this `address`
/// can only occurr as an entire argument expression in the invocation of
/// a leaf [Native] external function.
///
/// Example:
///
/// ```dart import:typed_data
/// @Native<Void Function(Pointer<$nativeType>)>(isLeaf: true)
/// external void myFunction(Pointer<$nativeType> pointer);
///
/// void main() {
/// final list = $typedListType(10);
/// myFunction(list.address);
/// }
/// ```
///
/// The expression before `.address` is evaluated like the left-hand-side of
/// an assignment, to something that gives access to the storage behind the
/// expression, which can be used both for reading and writing. The `.address`
/// then gives a native pointer to that storage.
///
/// The `.address` is evaluated just before calling into native code when
/// invoking a leaf [Native] external function. This ensures the Dart garbage
/// collector will not move the object that the address points in to.
external Pointer<$nativeType> get address;
}
""");
}
}
}
@ -375,6 +416,8 @@ extension ${nativeType}Array on Array<$nativeType> {
}
""");
case Container.typedData:
// Nothing to do, all rewritten in the CFE.
}
}

View file

@ -7351,7 +7351,7 @@ Representation FfiCallInstr::RequiredInputRepresentation(intptr_t idx) const {
// All input handles are passed as tagged values to FfiCallInstr and
// are given stack locations. FfiCallInstr then passes an untagged pointer
// to the handle on the stack (Dart_Handle) to the C function.
if (marshaller_.IsHandle(marshaller_.ArgumentIndex(idx))) {
if (marshaller_.IsHandleCType(marshaller_.ArgumentIndex(idx))) {
return kTagged;
}
return marshaller_.RepInFfiCall(idx);
@ -7396,13 +7396,14 @@ LocationSummary* FfiCallInstr::MakeLocationSummaryInternal(
marshaller_.contains_varargs()
? R13
: CallingConventions::kFirstNonArgumentRegister; // RAX
#else
const Register target_address = CallingConventions::kFirstNonArgumentRegister;
#endif
#define R(r) (1 << r)
ASSERT_EQUAL(temps & R(target_address), 0x0);
#undef R
summary->set_in(TargetAddressIndex(),
Location::RegisterLocation(target_address));
#else
summary->set_in(TargetAddressIndex(),
Location::RegisterLocation(
CallingConventions::kFirstNonArgumentRegister));
#endif
for (intptr_t i = 0, n = marshaller_.NumArgumentDefinitions(); i < n; ++i) {
summary->set_in(i, marshaller_.LocInFfiCall(i));
}
@ -7458,7 +7459,9 @@ void FfiCallInstr::EmitParamMoves(FlowGraphCompiler* compiler,
// First deal with moving all individual definitions passed in to the
// FfiCall to the right native location based on calling convention.
for (intptr_t i = 0; i < num_defs; i++) {
if (arg_target.IsPointerToMemory() && i == 1) {
if ((arg_target.IsPointerToMemory() ||
marshaller_.IsCompoundPointer(arg_index)) &&
i == 1) {
// The offset_in_bytes is not an argument for C, so don't move it.
// It is used as offset_in_bytes_loc below and moved there if
// necessary.
@ -7466,7 +7469,7 @@ void FfiCallInstr::EmitParamMoves(FlowGraphCompiler* compiler,
continue;
}
__ Comment(" def_index %" Pd, def_index);
const Location origin = rebase.Rebase(locs()->in(def_index));
Location origin = rebase.Rebase(locs()->in(def_index));
const Representation origin_rep = RequiredInputRepresentation(def_index);
// Find the native location where this individual definition should be
@ -7482,17 +7485,21 @@ void FfiCallInstr::EmitParamMoves(FlowGraphCompiler* compiler,
ConstantTemporaryAllocator temp_alloc(temp0);
if (origin.IsConstant()) {
__ Comment("origin.IsConstant()");
ASSERT(!marshaller_.IsHandle(arg_index));
ASSERT(!marshaller_.IsHandleCType(arg_index));
ASSERT(!marshaller_.IsTypedDataPointer(arg_index));
ASSERT(!marshaller_.IsCompoundPointer(arg_index));
compiler->EmitMoveConst(def_target, origin, origin_rep, &temp_alloc);
} else if (origin.IsPairLocation() &&
(origin.AsPairLocation()->At(0).IsConstant() ||
origin.AsPairLocation()->At(1).IsConstant())) {
// Note: half of the pair can be constant.
__ Comment("origin.IsPairLocation() and constant");
ASSERT(!marshaller_.IsHandle(arg_index));
ASSERT(!marshaller_.IsHandleCType(arg_index));
ASSERT(!marshaller_.IsTypedDataPointer(arg_index));
ASSERT(!marshaller_.IsCompoundPointer(arg_index));
compiler->EmitMoveConst(def_target, origin, origin_rep, &temp_alloc);
} else if (marshaller_.IsHandle(arg_index)) {
__ Comment("marshaller_.IsHandle(arg_index)");
} else if (marshaller_.IsHandleCType(arg_index)) {
__ Comment("marshaller_.IsHandleCType(arg_index)");
// Handles are passed into FfiCalls as Tagged values on the stack, and
// then we pass pointers to these handles to the native function here.
ASSERT(origin_rep == kTagged);
@ -7529,6 +7536,35 @@ void FfiCallInstr::EmitParamMoves(FlowGraphCompiler* compiler,
marshaller_.RequiredStackSpaceInBytes());
}
#endif
if (marshaller_.IsTypedDataPointer(arg_index) ||
marshaller_.IsCompoundPointer(arg_index)) {
// Unwrap typed data before move to native location.
__ Comment("Load typed data base address");
if (origin.IsStackSlot()) {
compiler->EmitMove(Location::RegisterLocation(temp0), origin,
&temp_alloc);
origin = Location::RegisterLocation(temp0);
}
ASSERT(origin.IsRegister());
__ LoadFromSlot(origin.reg(), origin.reg(), Slot::PointerBase_data());
if (marshaller_.IsCompoundPointer(arg_index)) {
__ Comment("Load offset in bytes");
const intptr_t offset_in_bytes_def_index = def_index + 1;
const Location offset_in_bytes_loc =
rebase.Rebase(locs()->in(offset_in_bytes_def_index));
Register offset_in_bytes_reg = kNoRegister;
if (offset_in_bytes_loc.IsRegister()) {
offset_in_bytes_reg = offset_in_bytes_loc.reg();
} else {
offset_in_bytes_reg = temp1;
NoTemporaryAllocator no_temp;
compiler->EmitMove(
Location::RegisterLocation(offset_in_bytes_reg),
offset_in_bytes_loc, &no_temp);
}
__ AddRegisters(origin.reg(), offset_in_bytes_reg);
}
}
compiler->EmitMoveToNative(def_target, origin, origin_rep, &temp_alloc);
}
def_index++;
@ -7910,7 +7946,7 @@ Representation FfiCallInstr::representation() const {
// Don't care, we're discarding the value.
return kTagged;
}
if (marshaller_.IsHandle(compiler::ffi::kResultIndex)) {
if (marshaller_.IsHandleCType(compiler::ffi::kResultIndex)) {
// The call returns a Dart_Handle, from which we need to extract the
// tagged pointer using LoadField with an appropriate slot.
return kUntagged;

View file

@ -1748,9 +1748,11 @@ void NativeCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
LocationSummary* FfiCallInstr::MakeLocationSummary(Zone* zone,
bool is_optimizing) const {
COMPILE_ASSERT(R(CallingConventions::kFfiAnyNonAbiRegister) <
R(CallingConventions::kSecondNonArgumentRegister));
return MakeLocationSummaryInternal(
zone, is_optimizing,
(R(R0) | R(CallingConventions::kFfiAnyNonAbiRegister) |
(R(CallingConventions::kFfiAnyNonAbiRegister) |
R(CallingConventions::kSecondNonArgumentRegister)));
}
@ -1760,13 +1762,12 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
const Register branch = locs()->in(TargetAddressIndex()).reg();
// The temps are indexed according to their register number.
const Register temp2 = locs()->temp(0).reg();
// For regular calls, this holds the FP for rebasing the original locations
// during EmitParamMoves.
// For leaf calls, this holds the SP used to restore the pre-aligned SP after
// the call.
const Register saved_fp_or_sp = locs()->temp(1).reg();
const Register temp1 = locs()->temp(2).reg();
const Register saved_fp_or_sp = locs()->temp(0).reg();
const Register temp1 = locs()->temp(1).reg();
// Ensure these are callee-saved register and are preserved across the call.
ASSERT(IsCalleeSavedRegister(saved_fp_or_sp));
@ -1791,7 +1792,7 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
UNIMPLEMENTED();
#endif
EmitParamMoves(compiler, is_leaf_ ? FPREG : saved_fp_or_sp, temp1, temp2);
EmitParamMoves(compiler, is_leaf_ ? FPREG : saved_fp_or_sp, temp1, TMP);
if (compiler::Assembler::EmittingComments()) {
__ Comment(is_leaf_ ? "Leaf Call" : "Call");
@ -1843,7 +1844,7 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
"NOTFP should be a reserved register");
__ blx(temp1);
if (marshaller_.IsHandle(compiler::ffi::kResultIndex)) {
if (marshaller_.IsHandleCType(compiler::ffi::kResultIndex)) {
__ Comment("Check Dart_Handle for Error.");
compiler::Label not_error;
ASSERT(temp1 != CallingConventions::kReturnReg);
@ -1882,7 +1883,7 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
}
}
EmitReturnMoves(compiler, temp1, temp2);
EmitReturnMoves(compiler, temp1, TMP);
if (is_leaf_) {
// Restore the pre-aligned SP.

View file

@ -1593,7 +1593,7 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
ASSERT(branch == R9);
__ blr(temp1);
if (marshaller_.IsHandle(compiler::ffi::kResultIndex)) {
if (marshaller_.IsHandleCType(compiler::ffi::kResultIndex)) {
__ Comment("Check Dart_Handle for Error.");
compiler::Label not_error;
__ ldr(temp1,

View file

@ -1198,9 +1198,11 @@ void NativeCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
LocationSummary* FfiCallInstr::MakeLocationSummary(Zone* zone,
bool is_optimizing) const {
COMPILE_ASSERT(R(CallingConventions::kSecondNonArgumentRegister) < R(EDX));
COMPILE_ASSERT(R(EDX) < R(CallingConventions::kFfiAnyNonAbiRegister));
return MakeLocationSummaryInternal(
zone, is_optimizing,
(R(CallingConventions::kSecondNonArgumentRegister) |
(R(CallingConventions::kSecondNonArgumentRegister) | R(EDX) |
R(CallingConventions::kFfiAnyNonAbiRegister)));
}
@ -1215,7 +1217,7 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
// during EmitParamMoves.
// For leaf calls, this holds the SP used to restore the pre-aligned SP after
// the call.
const Register saved_fp_or_sp = locs()->temp(1).reg();
const Register saved_fp_or_sp = locs()->temp(2).reg();
// Ensure these are callee-saved register and are preserved across the call.
ASSERT(IsCalleeSavedRegister(saved_fp_or_sp));
@ -1243,9 +1245,8 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
UNIMPLEMENTED();
#endif
// No second temp: PointerToMemoryLocation is not used for arguments in ia32.
EmitParamMoves(compiler, is_leaf_ ? FPREG : saved_fp_or_sp, temp,
kNoRegister);
locs()->temp(1).reg());
if (is_leaf_) {
// We store the pre-align SP at a fixed offset from the final SP.
@ -1306,7 +1307,7 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
ASSERT(branch == EAX);
__ call(temp);
if (marshaller_.IsHandle(compiler::ffi::kResultIndex)) {
if (marshaller_.IsHandleCType(compiler::ffi::kResultIndex)) {
__ Comment("Check Dart_Handle for Error.");
compiler::Label not_error;
__ movl(temp,

View file

@ -1749,7 +1749,7 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
__ jalr(temp1);
}
if (marshaller_.IsHandle(compiler::ffi::kResultIndex)) {
if (marshaller_.IsHandleCType(compiler::ffi::kResultIndex)) {
__ Comment("Check Dart_Handle for Error.");
ASSERT(temp1 != CallingConventions::kReturnReg);
ASSERT(temp2 != CallingConventions::kReturnReg);

View file

@ -1541,7 +1541,7 @@ void FfiCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
__ call(temp);
}
if (marshaller_.IsHandle(compiler::ffi::kResultIndex)) {
if (marshaller_.IsHandleCType(compiler::ffi::kResultIndex)) {
__ Comment("Check Dart_Handle for Error.");
compiler::Label not_error;
__ movq(temp,

View file

@ -92,23 +92,6 @@ const Function& CompilerState::StringBaseInterpolate() {
}
return *interpolate_;
}
const Class& CompilerState::TypedListClass() {
if (typed_list_class_ == nullptr) {
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
const Library& lib = Library::Handle(zone, Library::TypedDataLibrary());
const Class& cls = Class::ZoneHandle(
zone, lib.LookupClassAllowPrivate(Symbols::_TypedList()));
ASSERT(!cls.IsNull());
const Error& error = Error::Handle(zone, cls.EnsureIsFinalized(thread));
ASSERT(error.IsNull());
typed_list_class_ = &cls;
}
return *typed_list_class_;
}
#define DEFINE_TYPED_LIST_NATIVE_FUNCTION_GETTER(Upper, Lower) \
const Function& CompilerState::TypedListGet##Upper() { \
if (typed_list_get_##Lower##_ == nullptr) { \
@ -141,21 +124,30 @@ DEFINE_TYPED_LIST_NATIVE_FUNCTION_GETTER(Float64x2, float64x2)
#undef DEFINE_TYPED_LIST_NATIVE_FUNCTION_GETTER
const Class& CompilerState::CompoundClass() {
if (compound_class_ == nullptr) {
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
const auto& lib_ffi = Library::Handle(zone, Library::FfiLibrary());
const auto& cls = Class::Handle(
zone, lib_ffi.LookupClassAllowPrivate(Symbols::Compound()));
ASSERT(!cls.IsNull());
const Error& error = Error::Handle(zone, cls.EnsureIsFinalized(thread));
ASSERT(error.IsNull());
compound_class_ = &cls;
#define DEFINE_CLASS_GETTER(Lib, Upper, Lower, Symbol) \
const Class& CompilerState::Upper##Class() { \
if (Lower##_class_ == nullptr) { \
Thread* thread = Thread::Current(); \
Zone* zone = thread->zone(); \
const auto& lib = Library::Handle(zone, Library::Lib##Library()); \
const auto& cls = \
Class::Handle(zone, lib.LookupClassAllowPrivate(Symbols::Symbol())); \
ASSERT(!cls.IsNull()); \
const Error& error = Error::Handle(zone, cls.EnsureIsFinalized(thread)); \
ASSERT(error.IsNull()); \
Lower##_class_ = &cls; \
} \
return *Lower##_class_; \
}
return *compound_class_;
}
DEFINE_CLASS_GETTER(Ffi, Array, array, Array)
DEFINE_CLASS_GETTER(Ffi, Compound, compound, Compound)
DEFINE_CLASS_GETTER(Ffi, Struct, struct, Struct)
DEFINE_CLASS_GETTER(Ffi, Union, union, Union)
DEFINE_CLASS_GETTER(TypedData, TypedData, typed_data, TypedData)
DEFINE_CLASS_GETTER(TypedData, TypedList, typed_list, _TypedList)
#undef DEFINE_CLASS_GETTER
const Field& CompilerState::CompoundOffsetInBytesField() {
if (compound_offset_in_bytes_field_ == nullptr) {

View file

@ -105,7 +105,12 @@ class CompilerState : public ThreadStackResource {
const Function& TypedListGetFloat64x2();
const Function& TypedListSetFloat64x2();
const Class& ArrayClass();
const Class& CompoundClass();
const Class& StructClass();
const Class& TypedDataClass();
const Class& UnionClass();
const Field& CompoundOffsetInBytesField();
const Field& CompoundTypedDataBaseField();
@ -147,7 +152,11 @@ class CompilerState : public ThreadStackResource {
const Function* interpolate_ = nullptr;
const Function* interpolate_single_ = nullptr;
const Class* typed_list_class_ = nullptr;
const Class* array_class_ = nullptr;
const Class* compound_class_ = nullptr;
const Class* struct_class_ = nullptr;
const Class* typed_data_class_ = nullptr;
const Class* union_class_ = nullptr;
const Field* compound_offset_in_bytes_field_ = nullptr;
const Field* compound_typed_data_base_field_ = nullptr;
const Function* typed_list_get_float32_ = nullptr;

View file

@ -7,6 +7,7 @@
#include "platform/assert.h"
#include "platform/globals.h"
#include "vm/class_id.h"
#include "vm/compiler/compiler_state.h"
#include "vm/compiler/ffi/frame_rebase.h"
#include "vm/compiler/ffi/native_calling_convention.h"
#include "vm/compiler/ffi/native_location.h"
@ -18,6 +19,7 @@
#include "vm/raw_object.h"
#include "vm/stack_frame.h"
#include "vm/symbols.h"
#include "vm/tagged_pointer.h"
namespace dart {
@ -143,30 +145,101 @@ AbstractTypePtr BaseMarshaller::CType(intptr_t arg_index) const {
return c_signature_.ParameterTypeAt(real_arg_index);
}
AbstractTypePtr BaseMarshaller::DartType(intptr_t arg_index) const {
if (arg_index == kResultIndex) {
return dart_signature_.result_type();
}
const intptr_t real_arg_index = arg_index + dart_signature_params_start_at_;
ASSERT(!Array::Handle(dart_signature_.parameter_types()).IsNull());
ASSERT(real_arg_index <
Array::Handle(dart_signature_.parameter_types()).Length());
ASSERT(!AbstractType::Handle(dart_signature_.ParameterTypeAt(real_arg_index))
.IsNull());
return dart_signature_.ParameterTypeAt(real_arg_index);
}
bool BaseMarshaller::IsPointerCType(intptr_t arg_index) const {
return AbstractType::Handle(zone_, CType(arg_index)).type_class_id() ==
kPointerCid;
}
bool BaseMarshaller::IsPointerDartType(intptr_t arg_index) const {
return AbstractType::Handle(zone_, DartType(arg_index)).type_class_id() ==
kPointerCid;
}
bool BaseMarshaller::IsPointerPointer(intptr_t arg_index) const {
if (dart_signature_.parameter_types() == Array::null()) {
// TODO(https://dartbug.com/54173): BuildGraphOfSyncFfiCallback provides a
// function object with its type arguments not initialized.
return IsPointerCType(arg_index);
}
return IsPointerDartType(arg_index) && IsPointerCType(arg_index);
}
bool BaseMarshaller::IsTypedDataPointer(intptr_t arg_index) const {
if (!IsPointerCType(arg_index)) {
return false;
}
if (IsHandleCType(arg_index)) {
return false;
}
if (dart_signature_.parameter_types() == Array::null()) {
// TODO(https://dartbug.com/54173): BuildGraphOfSyncFfiCallback provides a
// function object with its type arguments not initialized. Change this
// to an assert when addressing that issue.
return false;
}
const auto& type = AbstractType::Handle(zone_, DartType(arg_index));
return type.type_class() ==
Thread::Current()->compiler_state().TypedDataClass().ptr();
}
static bool IsCompound(Zone* zone, const AbstractType& type) {
auto& compiler_state = Thread::Current()->compiler_state();
auto& cls = Class::Handle(zone, type.type_class());
if (cls.id() == compiler_state.CompoundClass().id() ||
cls.id() == compiler_state.ArrayClass().id()) {
return true;
}
cls ^= cls.SuperClass();
if (cls.id() == compiler_state.StructClass().id() ||
cls.id() == compiler_state.UnionClass().id()) {
return true;
}
return false;
}
bool BaseMarshaller::IsCompoundPointer(intptr_t arg_index) const {
if (!IsPointerCType(arg_index)) {
return false;
}
if (dart_signature_.parameter_types() == Array::null()) {
// TODO(https://dartbug.com/54173): BuildGraphOfSyncFfiCallback provides a
// function object with its type arguments not initialized.
return false;
}
const auto& dart_type = AbstractType::Handle(zone_, DartType(arg_index));
return IsCompound(this->zone_, dart_type);
}
bool BaseMarshaller::IsHandleCType(intptr_t arg_index) const {
return AbstractType::Handle(zone_, CType(arg_index)).type_class_id() ==
kFfiHandleCid;
}
bool BaseMarshaller::IsBool(intptr_t arg_index) const {
return AbstractType::Handle(zone_, CType(arg_index)).type_class_id() ==
kFfiBoolCid;
}
// Keep consistent with Function::FfiCSignatureReturnsStruct.
bool BaseMarshaller::IsCompound(intptr_t arg_index) const {
const auto& type = AbstractType::Handle(zone_, CType(arg_index));
if (IsFfiTypeClassId(type.type_class_id())) {
return false;
}
const auto& cls = Class::Handle(this->zone_, type.type_class());
const auto& superClass = Class::Handle(this->zone_, cls.SuperClass());
const bool is_abi_specific_int =
String::Handle(this->zone_, superClass.UserVisibleName())
.Equals(Symbols::AbiSpecificInteger());
if (is_abi_specific_int) {
return false;
}
#ifdef DEBUG
const bool is_struct =
String::Handle(this->zone_, superClass.UserVisibleName())
.Equals(Symbols::Struct());
const bool is_union =
String::Handle(this->zone_, superClass.UserVisibleName())
.Equals(Symbols::Union());
ASSERT(is_struct || is_union);
#endif
return true;
bool BaseMarshaller::IsCompoundCType(intptr_t arg_index) const {
const auto& c_type = AbstractType::Handle(zone_, CType(arg_index));
return IsCompound(this->zone_, c_type);
}
bool BaseMarshaller::ContainsHandles() const {
@ -189,6 +262,10 @@ intptr_t BaseMarshaller::NumDefinitions(intptr_t arg_index) const {
const auto& loc = Location(arg_index);
const auto& type = loc.payload_type();
if (IsCompoundPointer(arg_index)) {
// typed data base and offset.
return 2;
}
if (type.IsPrimitive()) {
// All non-struct arguments are 1 definition in IL. Even 64 bit values
// on 32 bit architectures.
@ -357,8 +434,8 @@ static Representation SelectRepresentationInIL(Zone* zone,
Representation BaseMarshaller::RepInDart(intptr_t arg_index) const {
// This should never be called on Pointers or Handles, which are specially
// handled during marshalling/unmarshalling.
ASSERT(!IsHandle(arg_index));
ASSERT(!IsPointer(arg_index));
ASSERT(!IsHandleCType(arg_index));
ASSERT(!IsPointerPointer(arg_index));
return Location(arg_index).payload_type().AsRepresentationOverApprox(zone_);
}
@ -368,12 +445,12 @@ Representation BaseMarshaller::RepInFfiCall(intptr_t def_index_global) const {
intptr_t arg_index = ArgumentIndex(def_index_global);
// Handled appropriately in the subclasses.
ASSERT(!IsHandle(arg_index));
ASSERT(!IsHandleCType(arg_index));
// The IL extracts the address stored in the Pointer object as an untagged
// pointer before passing it to C, and creates a new Pointer object to store
// the received untagged pointer when receiving a pointer from C.
if (IsPointer(arg_index)) return kUntagged;
if (IsPointerPointer(arg_index)) return kUntagged;
const auto& location = Location(arg_index);
if (location.container_type().IsPrimitive()) {
@ -402,7 +479,7 @@ static const intptr_t kOffsetInBytesIndex = 1;
Representation CallMarshaller::RepInFfiCall(intptr_t def_index_global) const {
intptr_t arg_index = ArgumentIndex(def_index_global);
if (IsHandle(arg_index)) {
if (IsHandleCType(arg_index)) {
// For FfiCall arguments, the FfiCall instruction takes a tagged pointer
// from the IL. (It then creates a handle on the stack and passes a
// pointer to the newly allocated handle to C.)
@ -420,7 +497,8 @@ Representation CallMarshaller::RepInFfiCall(intptr_t def_index_global) const {
return kTagged;
}
const auto& location = Location(arg_index);
if (location.IsPointerToMemory()) {
if (location.IsPointerToMemory() || IsCompoundPointer(arg_index) ||
IsTypedDataPointer(arg_index)) {
// For arguments, the compound data being passed as a pointer is first
// collected into a TypedData object by the IL, and that object is what is
// passed to the FfiCall instruction. (The machine code generated by
@ -435,6 +513,7 @@ Representation CallMarshaller::RepInFfiCall(intptr_t def_index_global) const {
return kTagged;
} else {
ASSERT_EQUAL(def_index_in_arg, kOffsetInBytesIndex);
ASSERT(!IsTypedDataPointer(arg_index));
return kUnboxedUword;
}
}
@ -444,7 +523,7 @@ Representation CallMarshaller::RepInFfiCall(intptr_t def_index_global) const {
Representation CallbackMarshaller::RepInFfiCall(
intptr_t def_index_global) const {
intptr_t arg_index = ArgumentIndex(def_index_global);
if (IsHandle(arg_index)) {
if (IsHandleCType(arg_index)) {
// Dart objects are passed to C as untagged pointers to newly created
// handles in the IL, and the ptr field of untagged pointers to handles are
// extracted when the IL receives handles from C code.
@ -533,7 +612,7 @@ Location CallMarshaller::LocInFfiCall(intptr_t def_index_global) const {
}
// Force all handles to be Stack locations.
if (IsHandle(arg_index)) {
if (IsHandleCType(arg_index)) {
return Location::RequiresStack();
}
@ -571,6 +650,16 @@ Location CallMarshaller::LocInFfiCall(intptr_t def_index_global) const {
}
}
if (IsCompoundPointer(arg_index)) {
const intptr_t def_index_in_arg =
def_index_global - FirstDefinitionIndex(arg_index);
if (def_index_in_arg == kOffsetInBytesIndex) {
// The typed data is passed in the location from the calling convention.
// The offset in bytes can be passed in any location.
return Location::Any();
}
}
if (loc.IsStack()) {
return ConvertToAnyLocation(loc.AsStack(), RepInFfiCall(def_index_global));
}
@ -589,7 +678,7 @@ Location CallMarshaller::LocInFfiCall(intptr_t def_index_global) const {
}
bool CallMarshaller::ReturnsCompound() const {
return IsCompound(compiler::ffi::kResultIndex);
return IsCompoundCType(compiler::ffi::kResultIndex);
}
intptr_t CallMarshaller::CompoundReturnSizeInBytes() const {

View file

@ -106,30 +106,47 @@ class BaseMarshaller : public ZoneAllocated {
// Recurses into VarArgs if needed.
AbstractTypePtr CType(intptr_t arg_index) const;
AbstractTypePtr DartType(intptr_t arg_index) const;
protected:
bool IsPointerDartType(intptr_t arg_index) const;
bool IsPointerCType(intptr_t arg_index) const;
public:
// The Dart and C Type is Pointer.
//
// Requires boxing or unboxing the Pointer object to int.
bool IsPointer(intptr_t arg_index) const {
if (IsHandle(arg_index)) {
return false;
}
return AbstractType::Handle(zone_, CType(arg_index)).type_class_id() ==
kPointerCid;
}
bool IsPointerPointer(intptr_t arg_index) const;
// The C type is Handle.
// The Dart type is TypedData and the C type is Pointer.
//
// Requires passing the typed data base in as tagged pointer.
//
// TODO(https://dartbug.com/55444): The typed data address load could be
// done in IL.
bool IsTypedDataPointer(intptr_t arg_index) const;
// The Dart type is a compound (for example an Array or a TypedData+offset),
// and the C type is Pointer.
//
// Requires passing in two definitions in IL: TypedDataBase + offset.
//
// TODO(https://dartbug.com/55444): The typed data address load could be
// done in IL.
bool IsCompoundPointer(intptr_t arg_index) const;
// The C type is Handle, the Dart type can be anything.
//
// Requires passing the pointer to the Dart object in a handle.
bool IsHandle(intptr_t arg_index) const {
return AbstractType::Handle(zone_, CType(arg_index)).type_class_id() ==
kFfiHandleCid;
}
bool IsBool(intptr_t arg_index) const {
return AbstractType::Handle(zone_, CType(arg_index)).type_class_id() ==
kFfiBoolCid;
}
bool IsHandleCType(intptr_t arg_index) const;
bool IsCompound(intptr_t arg_index) const;
// The Dart and C Types are boolean.
//
// Requires converting the boolean into an int in IL.
bool IsBool(intptr_t arg_index) const;
// The Dart and C Types are compound (pass by value).
bool IsCompoundCType(intptr_t arg_index) const;
// Treated as a null constant in Dart.
bool IsVoid(intptr_t arg_index) const {

View file

@ -5178,10 +5178,10 @@ Fragment FlowGraphBuilder::FfiCallbackConvertCompoundReturnToNative(
Fragment FlowGraphBuilder::FfiConvertPrimitiveToDart(
const compiler::ffi::BaseMarshaller& marshaller,
intptr_t arg_index) {
ASSERT(!marshaller.IsCompound(arg_index));
ASSERT(!marshaller.IsCompoundCType(arg_index));
Fragment body;
if (marshaller.IsPointer(arg_index)) {
if (marshaller.IsPointerPointer(arg_index)) {
Class& result_class =
Class::ZoneHandle(Z, IG->object_store()->ffi_pointer_class());
// This class might only be instantiated as a return type of ffi calls.
@ -5208,7 +5208,11 @@ Fragment FlowGraphBuilder::FfiConvertPrimitiveToDart(
StoreFieldInstr::Kind::kInitializing);
body += DropTemporary(&address); // address
body += LoadLocal(result);
} else if (marshaller.IsHandle(arg_index)) {
} else if (marshaller.IsTypedDataPointer(arg_index)) {
UNREACHABLE(); // Only supported for FFI call arguments.
} else if (marshaller.IsCompoundPointer(arg_index)) {
UNREACHABLE(); // Only supported for FFI call arguments.
} else if (marshaller.IsHandleCType(arg_index)) {
// The top of the stack is a Dart_Handle, so retrieve the tagged pointer
// out of it.
body += LoadNativeField(Slot::LocalHandle_ptr());
@ -5235,15 +5239,24 @@ Fragment FlowGraphBuilder::FfiConvertPrimitiveToDart(
Fragment FlowGraphBuilder::FfiConvertPrimitiveToNative(
const compiler::ffi::BaseMarshaller& marshaller,
intptr_t arg_index) {
ASSERT(!marshaller.IsCompound(arg_index));
intptr_t arg_index,
LocalVariable* variable) {
ASSERT(!marshaller.IsCompoundCType(arg_index));
Fragment body;
if (marshaller.IsPointer(arg_index)) {
if (marshaller.IsPointerPointer(arg_index)) {
// This can only be Pointer, so it is safe to load the data field.
body += LoadNativeField(Slot::PointerBase_data(),
InnerPointerAccess::kCannotBeInnerPointer);
} else if (marshaller.IsHandle(arg_index)) {
} else if (marshaller.IsTypedDataPointer(arg_index)) {
// Nothing to do. Unwrap in `FfiCallInstr::EmitNativeCode`.
} else if (marshaller.IsCompoundPointer(arg_index)) {
ASSERT(variable != nullptr);
body += LoadTypedDataBaseFromCompound();
body += LoadLocal(variable); // User-defined struct.
body += LoadOffsetInBytesFromCompound();
body += UnboxTruncate(kUnboxedWord);
} else if (marshaller.IsHandleCType(arg_index)) {
// FfiCallInstr specifies all handle locations as Stack, and will pass a
// pointer to the stack slot as the native handle argument. Therefore the
// only handles that need wrapping are function results.
@ -5430,7 +5443,7 @@ Fragment FlowGraphBuilder::FfiCallFunctionBody(
// catch our own null errors.
const intptr_t num_args = marshaller.num_args();
for (intptr_t i = 0; i < num_args; i++) {
if (marshaller.IsHandle(i)) {
if (marshaller.IsHandleCType(i)) {
continue;
}
body += LoadLocal(parsed_function_->ParameterVariable(
@ -5480,7 +5493,7 @@ Fragment FlowGraphBuilder::FfiCallFunctionBody(
// Unbox and push the arguments.
for (intptr_t i = 0; i < marshaller.num_args(); i++) {
if (marshaller.IsCompound(i)) {
if (marshaller.IsCompoundCType(i)) {
body += FfiCallConvertCompoundArgumentToNative(
parsed_function_->ParameterVariable(first_argument_parameter_offset +
i),
@ -5491,8 +5504,11 @@ Fragment FlowGraphBuilder::FfiCallFunctionBody(
// FfiCallInstr specifies all handle locations as Stack, and will pass a
// pointer to the stack slot as the native handle argument.
// Therefore we do not need to wrap handles.
if (!marshaller.IsHandle(i)) {
body += FfiConvertPrimitiveToNative(marshaller, i);
if (!marshaller.IsHandleCType(i)) {
body += FfiConvertPrimitiveToNative(
marshaller, i,
parsed_function_->ParameterVariable(
first_argument_parameter_offset + i));
}
}
}
@ -5516,7 +5532,7 @@ Fragment FlowGraphBuilder::FfiCallFunctionBody(
body += DropTemporary(&def);
}
if (marshaller.IsCompound(compiler::ffi::kResultIndex)) {
if (marshaller.IsCompoundCType(compiler::ffi::kResultIndex)) {
body += FfiCallConvertCompoundReturnToDart(marshaller,
compiler::ffi::kResultIndex);
} else {
@ -5585,7 +5601,7 @@ Fragment FlowGraphBuilder::LoadNativeArg(
defs->Add(def);
}
if (marshaller.IsCompound(arg_index)) {
if (marshaller.IsCompoundCType(arg_index)) {
fragment +=
FfiCallbackConvertCompoundArgumentToDart(marshaller, arg_index, defs);
} else {
@ -5665,13 +5681,13 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfSyncFfiCallback(
ICData::kNoRebind);
}
if (!marshaller.IsHandle(compiler::ffi::kResultIndex)) {
if (!marshaller.IsHandleCType(compiler::ffi::kResultIndex)) {
body += CheckNullOptimized(
String::ZoneHandle(Z, Symbols::New(H.thread(), "return_value")),
CheckNullInstr::kArgumentError);
}
if (marshaller.IsCompound(compiler::ffi::kResultIndex)) {
if (marshaller.IsCompoundCType(compiler::ffi::kResultIndex)) {
body += FfiCallbackConvertCompoundReturnToNative(
marshaller, compiler::ffi::kResultIndex);
} else {
@ -5694,16 +5710,16 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfSyncFfiCallback(
// The exceptional return is always null -- return nullptr instead.
ASSERT(function.FfiCallbackExceptionalReturn() == Object::null());
catch_body += UnboxedIntConstant(0, kUnboxedIntPtr);
} else if (marshaller.IsPointer(compiler::ffi::kResultIndex)) {
} else if (marshaller.IsPointerPointer(compiler::ffi::kResultIndex)) {
// The exceptional return is always null -- return nullptr instead.
ASSERT(function.FfiCallbackExceptionalReturn() == Object::null());
catch_body += UnboxedIntConstant(0, kUnboxedAddress);
catch_body += ConvertUnboxedToUntagged();
} else if (marshaller.IsHandle(compiler::ffi::kResultIndex)) {
} else if (marshaller.IsHandleCType(compiler::ffi::kResultIndex)) {
catch_body += UnhandledException();
catch_body +=
FfiConvertPrimitiveToNative(marshaller, compiler::ffi::kResultIndex);
} else if (marshaller.IsCompound(compiler::ffi::kResultIndex)) {
} else if (marshaller.IsCompoundCType(compiler::ffi::kResultIndex)) {
ASSERT(function.FfiCallbackExceptionalReturn() == Object::null());
// Manufacture empty result.
const intptr_t size =

View file

@ -335,9 +335,13 @@ class FlowGraphBuilder : public BaseFlowGraphBuilder {
// semantics of FFI argument translation.
//
// Works for FFI call arguments, and FFI callback return values.
//
// If `marshaller.IsCompoundPointer(arg_index)`, then [variable] must point to
// a valid LocalVariable.
Fragment FfiConvertPrimitiveToNative(
const compiler::ffi::BaseMarshaller& marshaller,
intptr_t arg_index);
intptr_t arg_index,
LocalVariable* variable = nullptr);
// Pops an unboxed native value, and pushes a Dart object, according to the
// semantics of FFI argument translation.

View file

@ -25,6 +25,7 @@ class ObjectPointerVisitor;
V(ApiError, "ApiError") \
V(ArgDescVar, ":arg_desc") \
V(ArgumentError, "ArgumentError") \
V(Array, "Array") \
V(StateError, "StateError") \
V(AssertionError, "_AssertionError") \
V(AssignIndexToken, "[]=") \
@ -254,6 +255,7 @@ class ObjectPointerVisitor;
V(Type, "Type") \
V(TypeArguments, "TypeArguments") \
V(TypeArgumentsParameter, ":type_arguments") \
V(TypedData, "TypedData") \
V(TypeError, "_TypeError") \
V(TypeParameters, "TypeParameters") \
V(TypeQuote, "type '") \

View file

@ -9,7 +9,7 @@ import 'dart:typed_data';
@pragma("vm:entry-point")
@patch
abstract final class _Compound implements NativeType {}
final class _Compound implements NativeType {}
@pragma("vm:entry-point")
@patch

View file

@ -1202,6 +1202,316 @@ extension BoolArray on Array<Bool> {
external void operator []=(int index, bool value);
}
@Since('3.5')
extension Int8ListAddress on Int8List {
/// The memory address of the underlying data.
///
/// An expression of the form `expression.address` denoting this `address`
/// can only occurr as an entire argument expression in the invocation of
/// a leaf [Native] external function.
///
/// Example:
///
/// ```dart import:typed_data
/// @Native<Void Function(Pointer<Int8>)>(isLeaf: true)
/// external void myFunction(Pointer<Int8> pointer);
///
/// void main() {
/// final list = Int8List(10);
/// myFunction(list.address);
/// }
/// ```
///
/// The expression before `.address` is evaluated like the left-hand-side of
/// an assignment, to something that gives access to the storage behind the
/// expression, which can be used both for reading and writing. The `.address`
/// then gives a native pointer to that storage.
///
/// The `.address` is evaluated just before calling into native code when
/// invoking a leaf [Native] external function. This ensures the Dart garbage
/// collector will not move the object that the address points in to.
external Pointer<Int8> get address;
}
@Since('3.5')
extension Int16ListAddress on Int16List {
/// The memory address of the underlying data.
///
/// An expression of the form `expression.address` denoting this `address`
/// can only occurr as an entire argument expression in the invocation of
/// a leaf [Native] external function.
///
/// Example:
///
/// ```dart import:typed_data
/// @Native<Void Function(Pointer<Int16>)>(isLeaf: true)
/// external void myFunction(Pointer<Int16> pointer);
///
/// void main() {
/// final list = Int16List(10);
/// myFunction(list.address);
/// }
/// ```
///
/// The expression before `.address` is evaluated like the left-hand-side of
/// an assignment, to something that gives access to the storage behind the
/// expression, which can be used both for reading and writing. The `.address`
/// then gives a native pointer to that storage.
///
/// The `.address` is evaluated just before calling into native code when
/// invoking a leaf [Native] external function. This ensures the Dart garbage
/// collector will not move the object that the address points in to.
external Pointer<Int16> get address;
}
@Since('3.5')
extension Int32ListAddress on Int32List {
/// The memory address of the underlying data.
///
/// An expression of the form `expression.address` denoting this `address`
/// can only occurr as an entire argument expression in the invocation of
/// a leaf [Native] external function.
///
/// Example:
///
/// ```dart import:typed_data
/// @Native<Void Function(Pointer<Int32>)>(isLeaf: true)
/// external void myFunction(Pointer<Int32> pointer);
///
/// void main() {
/// final list = Int32List(10);
/// myFunction(list.address);
/// }
/// ```
///
/// The expression before `.address` is evaluated like the left-hand-side of
/// an assignment, to something that gives access to the storage behind the
/// expression, which can be used both for reading and writing. The `.address`
/// then gives a native pointer to that storage.
///
/// The `.address` is evaluated just before calling into native code when
/// invoking a leaf [Native] external function. This ensures the Dart garbage
/// collector will not move the object that the address points in to.
external Pointer<Int32> get address;
}
@Since('3.5')
extension Int64ListAddress on Int64List {
/// The memory address of the underlying data.
///
/// An expression of the form `expression.address` denoting this `address`
/// can only occurr as an entire argument expression in the invocation of
/// a leaf [Native] external function.
///
/// Example:
///
/// ```dart import:typed_data
/// @Native<Void Function(Pointer<Int64>)>(isLeaf: true)
/// external void myFunction(Pointer<Int64> pointer);
///
/// void main() {
/// final list = Int64List(10);
/// myFunction(list.address);
/// }
/// ```
///
/// The expression before `.address` is evaluated like the left-hand-side of
/// an assignment, to something that gives access to the storage behind the
/// expression, which can be used both for reading and writing. The `.address`
/// then gives a native pointer to that storage.
///
/// The `.address` is evaluated just before calling into native code when
/// invoking a leaf [Native] external function. This ensures the Dart garbage
/// collector will not move the object that the address points in to.
external Pointer<Int64> get address;
}
@Since('3.5')
extension Uint8ListAddress on Uint8List {
/// The memory address of the underlying data.
///
/// An expression of the form `expression.address` denoting this `address`
/// can only occurr as an entire argument expression in the invocation of
/// a leaf [Native] external function.
///
/// Example:
///
/// ```dart import:typed_data
/// @Native<Void Function(Pointer<Uint8>)>(isLeaf: true)
/// external void myFunction(Pointer<Uint8> pointer);
///
/// void main() {
/// final list = Uint8List(10);
/// myFunction(list.address);
/// }
/// ```
///
/// The expression before `.address` is evaluated like the left-hand-side of
/// an assignment, to something that gives access to the storage behind the
/// expression, which can be used both for reading and writing. The `.address`
/// then gives a native pointer to that storage.
///
/// The `.address` is evaluated just before calling into native code when
/// invoking a leaf [Native] external function. This ensures the Dart garbage
/// collector will not move the object that the address points in to.
external Pointer<Uint8> get address;
}
@Since('3.5')
extension Uint16ListAddress on Uint16List {
/// The memory address of the underlying data.
///
/// An expression of the form `expression.address` denoting this `address`
/// can only occurr as an entire argument expression in the invocation of
/// a leaf [Native] external function.
///
/// Example:
///
/// ```dart import:typed_data
/// @Native<Void Function(Pointer<Uint16>)>(isLeaf: true)
/// external void myFunction(Pointer<Uint16> pointer);
///
/// void main() {
/// final list = Uint16List(10);
/// myFunction(list.address);
/// }
/// ```
///
/// The expression before `.address` is evaluated like the left-hand-side of
/// an assignment, to something that gives access to the storage behind the
/// expression, which can be used both for reading and writing. The `.address`
/// then gives a native pointer to that storage.
///
/// The `.address` is evaluated just before calling into native code when
/// invoking a leaf [Native] external function. This ensures the Dart garbage
/// collector will not move the object that the address points in to.
external Pointer<Uint16> get address;
}
@Since('3.5')
extension Uint32ListAddress on Uint32List {
/// The memory address of the underlying data.
///
/// An expression of the form `expression.address` denoting this `address`
/// can only occurr as an entire argument expression in the invocation of
/// a leaf [Native] external function.
///
/// Example:
///
/// ```dart import:typed_data
/// @Native<Void Function(Pointer<Uint32>)>(isLeaf: true)
/// external void myFunction(Pointer<Uint32> pointer);
///
/// void main() {
/// final list = Uint32List(10);
/// myFunction(list.address);
/// }
/// ```
///
/// The expression before `.address` is evaluated like the left-hand-side of
/// an assignment, to something that gives access to the storage behind the
/// expression, which can be used both for reading and writing. The `.address`
/// then gives a native pointer to that storage.
///
/// The `.address` is evaluated just before calling into native code when
/// invoking a leaf [Native] external function. This ensures the Dart garbage
/// collector will not move the object that the address points in to.
external Pointer<Uint32> get address;
}
@Since('3.5')
extension Uint64ListAddress on Uint64List {
/// The memory address of the underlying data.
///
/// An expression of the form `expression.address` denoting this `address`
/// can only occurr as an entire argument expression in the invocation of
/// a leaf [Native] external function.
///
/// Example:
///
/// ```dart import:typed_data
/// @Native<Void Function(Pointer<Uint64>)>(isLeaf: true)
/// external void myFunction(Pointer<Uint64> pointer);
///
/// void main() {
/// final list = Uint64List(10);
/// myFunction(list.address);
/// }
/// ```
///
/// The expression before `.address` is evaluated like the left-hand-side of
/// an assignment, to something that gives access to the storage behind the
/// expression, which can be used both for reading and writing. The `.address`
/// then gives a native pointer to that storage.
///
/// The `.address` is evaluated just before calling into native code when
/// invoking a leaf [Native] external function. This ensures the Dart garbage
/// collector will not move the object that the address points in to.
external Pointer<Uint64> get address;
}
@Since('3.5')
extension Float32ListAddress on Float32List {
/// The memory address of the underlying data.
///
/// An expression of the form `expression.address` denoting this `address`
/// can only occurr as an entire argument expression in the invocation of
/// a leaf [Native] external function.
///
/// Example:
///
/// ```dart import:typed_data
/// @Native<Void Function(Pointer<Float>)>(isLeaf: true)
/// external void myFunction(Pointer<Float> pointer);
///
/// void main() {
/// final list = Float32List(10);
/// myFunction(list.address);
/// }
/// ```
///
/// The expression before `.address` is evaluated like the left-hand-side of
/// an assignment, to something that gives access to the storage behind the
/// expression, which can be used both for reading and writing. The `.address`
/// then gives a native pointer to that storage.
///
/// The `.address` is evaluated just before calling into native code when
/// invoking a leaf [Native] external function. This ensures the Dart garbage
/// collector will not move the object that the address points in to.
external Pointer<Float> get address;
}
@Since('3.5')
extension Float64ListAddress on Float64List {
/// The memory address of the underlying data.
///
/// An expression of the form `expression.address` denoting this `address`
/// can only occurr as an entire argument expression in the invocation of
/// a leaf [Native] external function.
///
/// Example:
///
/// ```dart import:typed_data
/// @Native<Void Function(Pointer<Double>)>(isLeaf: true)
/// external void myFunction(Pointer<Double> pointer);
///
/// void main() {
/// final list = Float64List(10);
/// myFunction(list.address);
/// }
/// ```
///
/// The expression before `.address` is evaluated like the left-hand-side of
/// an assignment, to something that gives access to the storage behind the
/// expression, which can be used both for reading and writing. The `.address`
/// then gives a native pointer to that storage.
///
/// The `.address` is evaluated just before calling into native code when
/// invoking a leaf [Native] external function. This ensures the Dart garbage
/// collector will not move the object that the address points in to.
external Pointer<Double> get address;
}
//
// End of generated code.
//
@ -1471,6 +1781,264 @@ extension AbiSpecificIntegerArray<T extends AbiSpecificInteger> on Array<T> {
external void operator []=(int index, int value);
}
@Since('3.5')
extension ArrayAddress<T extends NativeType> on Array<T> {
/// The memory address of the underlying data.
///
/// An expression of the form `expression.address` denoting this `address` can
/// only occurr as an entire argument expression in the invocation of a leaf
/// [Native] external function.
///
/// Example:
///
/// ```dart
/// @Native<Void Function(Pointer<Int8>)>(isLeaf: true)
/// external void myFunction(Pointer<Int8> pointer);
///
/// final class MyStruct extends Struct {
/// @Array(10)
/// external Array<Int8> array;
/// }
///
/// void main() {
/// final array = Struct.create<MyStruct>().array;
/// myFunction(array.address);
/// }
/// ```
///
/// The expression before `.address` is evaluated like the left-hand-side of
/// an assignment, to something that gives access to the storage behind the
/// expression, which can be used both for reading and writing. The `.address`
/// then gives a native pointer to that storage.
///
/// The `.address` is evaluated just before calling into native code when
/// invoking a leaf [Native] external function. This ensures the Dart garbage
/// collector will not move the object that the address points in to.
external Pointer<T> get address;
}
@Since('3.5')
extension StructAddress<T extends Struct> on T {
/// The memory address of the underlying data.
///
/// An expression of the form `expression.address` denoting this `address` can
/// only occurr as an entire argument expression in the invocation of a leaf
/// [Native] external function.
///
/// Example:
///
/// ```dart
/// @Native<Void Function(Pointer<MyStruct>)>(isLeaf: true)
/// external void myFunction(Pointer<MyStruct> pointer);
///
/// final class MyStruct extends Struct {
/// @Int8()
/// external int x;
/// }
///
/// void main() {
/// final myStruct = Struct.create<MyStruct>();
/// myFunction(myStruct.address);
/// }
/// ```
///
/// The expression before `.address` is evaluated like the left-hand-side of
/// an assignment, to something that gives access to the storage behind the
/// expression, which can be used both for reading and writing. The `.address`
/// then gives a native pointer to that storage.
///
/// The `.address` is evaluated just before calling into native code when
/// invoking a leaf [Native] external function. This ensures the Dart garbage
/// collector will not move the object that the address points in to.
external Pointer<T> get address;
}
@Since('3.5')
extension UnionAddress<T extends Union> on T {
/// The memory address of the underlying data.
///
/// An expression of the form `expression.address` denoting this `address` can
/// only occurr as an entire argument expression in the invocation of a leaf
/// [Native] external function.
///
/// Example:
///
/// ```dart
/// @Native<Void Function(Pointer<MyUnion>)>(isLeaf: true)
/// external void myFunction(Pointer<MyUnion> pointer);
///
/// final class MyUnion extends Union {
/// @Int8()
/// external int x;
/// }
///
/// void main() {
/// final myUnion = Union.create<MyUnion>();
/// myFunction(myUnion.address);
/// }
/// ```
///
/// The expression before `.address` is evaluated like the left-hand-side of
/// an assignment, to something that gives access to the storage behind the
/// expression, which can be used both for reading and writing. The `.address`
/// then gives a native pointer to that storage.
///
/// The `.address` is evaluated just before calling into native code when
/// invoking a leaf [Native] external function. This ensures the Dart garbage
/// collector will not move the object that the address points in to.
external Pointer<T> get address;
}
@Since('3.5')
extension IntAddress on int {
/// The memory address of the underlying data.
///
/// An expression of the form `expression.address` denoting this `address` can
/// only occurr as an entire argument expression in the invocation of a leaf
/// [Native] external function.
///
/// Can only be used on fields of [Struct] subtypes, fields of [Union]
/// subtypes, [Array] elements, or [TypedData] elements. In other words, the
/// number whose address is being accessed must itself be acccessed through a
/// [Struct], [Union], [Array], or [TypedData].
///
/// Example:
///
/// ```dart import:typed_data
/// @Native<Void Function(Pointer<Int8>)>(isLeaf: true)
/// external void myFunction(Pointer<Int8> pointer);
///
/// final class MyStruct extends Struct {
/// @Int8()
/// external int x;
///
/// @Int8()
/// external int y;
///
/// @Array(10)
/// external Array<Int8> array;
/// }
///
/// void main() {
/// final myStruct = Struct.create<MyStruct>();
/// myFunction(myStruct.y.address);
/// myFunction(myStruct.array[5].address);
///
/// final list = Int8List(10);
/// myFunction(list[5].address);
/// }
/// ```
///
/// The expression before `.address` is evaluated like the left-hand-side of
/// an assignment, to something that gives access to the storage behind the
/// expression, which can be used both for reading and writing. The `.address`
/// then gives a native pointer to that storage.
///
/// The `.address` is evaluated just before calling into native code when
/// invoking a leaf [Native] external function. This ensures the Dart garbage
/// collector will not move the object that the address points in to.
external Pointer<Never> address;
}
@Since('3.5')
extension DoubleAddress on double {
/// The memory address of the underlying data.
///
/// An expression of the form `expression.address` denoting this `address` can
/// only occurr as an entire argument expression in the invocation of a leaf
/// [Native] external function.
///
/// Can only be used on fields of [Struct] subtypes, fields of [Union]
/// subtypes, [Array] elements, or [TypedData] elements. In other words, the
/// number whose address is being accessed must itself be acccessed through a
/// [Struct], [Union], [Array], or [TypedData].
///
/// Example:
///
/// ```dart import:typed_data
/// @Native<Void Function(Pointer<Float>)>(isLeaf: true)
/// external void myFunction(Pointer<Float> pointer);
///
/// final class MyStruct extends Struct {
/// @Float()
/// external double x;
///
/// @Float()
/// external double y;
///
/// @Array(10)
/// external Array<Float> array;
/// }
///
/// void main() {
/// final myStruct = Struct.create<MyStruct>();
/// myFunction(myStruct.y.address);
/// myFunction(myStruct.array[5].address);
///
/// final list = Float32List(10);
/// myFunction(list[5].address);
/// }
/// ```
///
/// The expression before `.address` is evaluated like the left-hand-side of
/// an assignment, to something that gives access to the storage behind the
/// expression, which can be used both for reading and writing. The `.address`
/// then gives a native pointer to that storage.
///
/// The `.address` is evaluated just before calling into native code when
/// invoking a leaf [Native] external function. This ensures the Dart garbage
/// collector will not move the object that the address points in to.
external Pointer<Never> address;
}
@Since('3.5')
extension BoolAddress on bool {
/// The memory address of the underlying data.
///
/// An expression of the form `expression.address` denoting this `address` can
/// only occurr as an entire argument expression in the invocation of a leaf
/// [Native] external function.
///
/// Can only be used on fields of [Struct] subtypes, fields of [Union]
/// subtypes, or [Array] elements. In other words, the boolean whose address
/// is being accessed must itself be acccessed through a [Struct], [Union] or
/// [Array].
///
/// Example:
///
/// ```dart
/// @Native<Void Function(Pointer<Int8>)>(isLeaf: true)
/// external void myFunction(Pointer<Int8> pointer);
///
/// final class MyStruct extends Struct {
/// @Bool()
/// external bool x;
///
/// @Bool()
/// external bool y;
///
/// @Array(10)
/// external Array<Bool> array;
/// }
///
/// void main() {
/// final myStruct = Struct.create<MyStruct>();
/// myFunction(myStruct.y.address);
/// myFunction(myStruct.array[5].address);
/// }
/// ```
///
/// The expression before `.address` is evaluated like the left-hand-side of
/// an assignment, to something that gives access to the storage behind the
/// expression, which can be used both for reading and writing. The `.address`
/// then gives a native pointer to that storage.
///
/// The `.address` is evaluated just before calling into native code when
/// invoking a leaf [Native] external function. This ensures the Dart garbage
/// collector will not move the object that the address points in to.
external Pointer<Never> address;
}
/// Extension to retrieve the native `Dart_Port` from a [SendPort].
@Since('2.7')
extension NativePort on SendPort {

View file

@ -4,12 +4,15 @@
part of dart.ffi;
/// A memory range, represented by its starting address.
///
/// Shared supertype of the FFI compound [Struct], [Union], and [Array] types.
///
/// FFI struct and union types should extend [Struct] and [Union]. For more
/// information see the documentation on those classes.
/// This class is not abstract because instances can be created as an anonymous
/// representation of a memory area, with no structure on top. (In particular,
/// during the transformation of `.address` in FFI leaf calls.)
@pragma("wasm:entry-point")
abstract final class _Compound implements NativeType {
final class _Compound implements NativeType {
/// The underlying [TypedData] or [Pointer] that a subtype uses.
@pragma("vm:entry-point")
final Object _typedDataBase;

View file

@ -0,0 +1,594 @@
// 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.
//
// This file has been automatically generated. Please do not edit it manually.
// Generated by tests/ffi/generator/address_of_test_generator.dart.
//
// SharedObjects=ffi_test_functions
// VMOptions=
// VMOptions=--deterministic --optimization-counter-threshold=90
// VMOptions=--use-slow-path
// VMOptions=--use-slow-path --stacktrace-every=100
// ignore_for_file: unused_import
import 'dart:ffi';
import 'dart:typed_data';
import 'package:expect/expect.dart';
import 'address_of_generated_shared.dart';
import 'address_of_shared.dart';
import 'dylib_utils.dart';
final ffiTestFunctions = dlopenPlatformSpecific('ffi_test_functions');
void main() {
// Force dlopen so @Native lookups in DynamicLibrary.process() succeed.
dlopenGlobalPlatformSpecific('ffi_test_functions');
for (int i = 0; i < 100; ++i) {
testAddressOfInt8Array();
testAddressOfInt8ArrayElementAt();
testAddressOfInt16Array();
testAddressOfInt16ArrayElementAt();
testAddressOfInt32Array();
testAddressOfInt32ArrayElementAt();
testAddressOfInt64Array();
testAddressOfInt64ArrayElementAt();
testAddressOfUint8Array();
testAddressOfUint8ArrayElementAt();
testAddressOfUint16Array();
testAddressOfUint16ArrayElementAt();
testAddressOfUint32Array();
testAddressOfUint32ArrayElementAt();
testAddressOfUint64Array();
testAddressOfUint64ArrayElementAt();
testAddressOfFloatArray();
testAddressOfFloatArrayElementAt();
testAddressOfDoubleArray();
testAddressOfDoubleArrayElementAt();
testAddressOfBoolArray();
testAddressOfBoolArrayElementAt();
}
}
final class Int8ArrayStruct extends Struct {
@Array(20)
external Array<Int8> array;
}
Array<Int8> makeInt8Array(int length) {
assert(length == 20);
final typedData = makeInt8List(length);
final struct = Struct.create<Int8ArrayStruct>(typedData);
return struct.array;
}
void testAddressOfInt8Array() {
const length = 20;
final array = makeInt8Array(length);
final expectedResult = makeExpectedResultInt8(0, length);
final result = takeInt8Pointer(array.address, length);
Expect.equals(expectedResult, result);
}
void testAddressOfInt8ArrayElementAt() {
const length = 20;
final array = makeInt8Array(length);
final expectedResult = makeExpectedResultInt8(0, length);
final result = takeInt8PointerMany(
array[0].address,
array[1].address,
array[2].address,
array[3].address,
array[4].address,
array[5].address,
array[6].address,
array[7].address,
array[8].address,
array[9].address,
array[10].address,
array[11].address,
array[12].address,
array[13].address,
array[14].address,
array[15].address,
array[16].address,
array[17].address,
array[18].address,
array[19].address,
);
Expect.equals(expectedResult, result);
}
final class Int16ArrayStruct extends Struct {
@Array(20)
external Array<Int16> array;
}
Array<Int16> makeInt16Array(int length) {
assert(length == 20);
final typedData = makeInt16List(length);
final struct = Struct.create<Int16ArrayStruct>(typedData);
return struct.array;
}
void testAddressOfInt16Array() {
const length = 20;
final array = makeInt16Array(length);
final expectedResult = makeExpectedResultInt16(0, length);
final result = takeInt16Pointer(array.address, length);
Expect.equals(expectedResult, result);
}
void testAddressOfInt16ArrayElementAt() {
const length = 20;
final array = makeInt16Array(length);
final expectedResult = makeExpectedResultInt16(0, length);
final result = takeInt16PointerMany(
array[0].address,
array[1].address,
array[2].address,
array[3].address,
array[4].address,
array[5].address,
array[6].address,
array[7].address,
array[8].address,
array[9].address,
array[10].address,
array[11].address,
array[12].address,
array[13].address,
array[14].address,
array[15].address,
array[16].address,
array[17].address,
array[18].address,
array[19].address,
);
Expect.equals(expectedResult, result);
}
final class Int32ArrayStruct extends Struct {
@Array(20)
external Array<Int32> array;
}
Array<Int32> makeInt32Array(int length) {
assert(length == 20);
final typedData = makeInt32List(length);
final struct = Struct.create<Int32ArrayStruct>(typedData);
return struct.array;
}
void testAddressOfInt32Array() {
const length = 20;
final array = makeInt32Array(length);
final expectedResult = makeExpectedResultInt32(0, length);
final result = takeInt32Pointer(array.address, length);
Expect.equals(expectedResult, result);
}
void testAddressOfInt32ArrayElementAt() {
const length = 20;
final array = makeInt32Array(length);
final expectedResult = makeExpectedResultInt32(0, length);
final result = takeInt32PointerMany(
array[0].address,
array[1].address,
array[2].address,
array[3].address,
array[4].address,
array[5].address,
array[6].address,
array[7].address,
array[8].address,
array[9].address,
array[10].address,
array[11].address,
array[12].address,
array[13].address,
array[14].address,
array[15].address,
array[16].address,
array[17].address,
array[18].address,
array[19].address,
);
Expect.equals(expectedResult, result);
}
final class Int64ArrayStruct extends Struct {
@Array(20)
external Array<Int64> array;
}
Array<Int64> makeInt64Array(int length) {
assert(length == 20);
final typedData = makeInt64List(length);
final struct = Struct.create<Int64ArrayStruct>(typedData);
return struct.array;
}
void testAddressOfInt64Array() {
const length = 20;
final array = makeInt64Array(length);
final expectedResult = makeExpectedResultInt64(0, length);
final result = takeInt64Pointer(array.address, length);
Expect.equals(expectedResult, result);
}
void testAddressOfInt64ArrayElementAt() {
const length = 20;
final array = makeInt64Array(length);
final expectedResult = makeExpectedResultInt64(0, length);
final result = takeInt64PointerMany(
array[0].address,
array[1].address,
array[2].address,
array[3].address,
array[4].address,
array[5].address,
array[6].address,
array[7].address,
array[8].address,
array[9].address,
array[10].address,
array[11].address,
array[12].address,
array[13].address,
array[14].address,
array[15].address,
array[16].address,
array[17].address,
array[18].address,
array[19].address,
);
Expect.equals(expectedResult, result);
}
final class Uint8ArrayStruct extends Struct {
@Array(20)
external Array<Uint8> array;
}
Array<Uint8> makeUint8Array(int length) {
assert(length == 20);
final typedData = makeUint8List(length);
final struct = Struct.create<Uint8ArrayStruct>(typedData);
return struct.array;
}
void testAddressOfUint8Array() {
const length = 20;
final array = makeUint8Array(length);
final expectedResult = makeExpectedResultUint8(0, length);
final result = takeUint8Pointer(array.address, length);
Expect.equals(expectedResult, result);
}
void testAddressOfUint8ArrayElementAt() {
const length = 20;
final array = makeUint8Array(length);
final expectedResult = makeExpectedResultUint8(0, length);
final result = takeUint8PointerMany(
array[0].address,
array[1].address,
array[2].address,
array[3].address,
array[4].address,
array[5].address,
array[6].address,
array[7].address,
array[8].address,
array[9].address,
array[10].address,
array[11].address,
array[12].address,
array[13].address,
array[14].address,
array[15].address,
array[16].address,
array[17].address,
array[18].address,
array[19].address,
);
Expect.equals(expectedResult, result);
}
final class Uint16ArrayStruct extends Struct {
@Array(20)
external Array<Uint16> array;
}
Array<Uint16> makeUint16Array(int length) {
assert(length == 20);
final typedData = makeUint16List(length);
final struct = Struct.create<Uint16ArrayStruct>(typedData);
return struct.array;
}
void testAddressOfUint16Array() {
const length = 20;
final array = makeUint16Array(length);
final expectedResult = makeExpectedResultUint16(0, length);
final result = takeUint16Pointer(array.address, length);
Expect.equals(expectedResult, result);
}
void testAddressOfUint16ArrayElementAt() {
const length = 20;
final array = makeUint16Array(length);
final expectedResult = makeExpectedResultUint16(0, length);
final result = takeUint16PointerMany(
array[0].address,
array[1].address,
array[2].address,
array[3].address,
array[4].address,
array[5].address,
array[6].address,
array[7].address,
array[8].address,
array[9].address,
array[10].address,
array[11].address,
array[12].address,
array[13].address,
array[14].address,
array[15].address,
array[16].address,
array[17].address,
array[18].address,
array[19].address,
);
Expect.equals(expectedResult, result);
}
final class Uint32ArrayStruct extends Struct {
@Array(20)
external Array<Uint32> array;
}
Array<Uint32> makeUint32Array(int length) {
assert(length == 20);
final typedData = makeUint32List(length);
final struct = Struct.create<Uint32ArrayStruct>(typedData);
return struct.array;
}
void testAddressOfUint32Array() {
const length = 20;
final array = makeUint32Array(length);
final expectedResult = makeExpectedResultUint32(0, length);
final result = takeUint32Pointer(array.address, length);
Expect.equals(expectedResult, result);
}
void testAddressOfUint32ArrayElementAt() {
const length = 20;
final array = makeUint32Array(length);
final expectedResult = makeExpectedResultUint32(0, length);
final result = takeUint32PointerMany(
array[0].address,
array[1].address,
array[2].address,
array[3].address,
array[4].address,
array[5].address,
array[6].address,
array[7].address,
array[8].address,
array[9].address,
array[10].address,
array[11].address,
array[12].address,
array[13].address,
array[14].address,
array[15].address,
array[16].address,
array[17].address,
array[18].address,
array[19].address,
);
Expect.equals(expectedResult, result);
}
final class Uint64ArrayStruct extends Struct {
@Array(20)
external Array<Uint64> array;
}
Array<Uint64> makeUint64Array(int length) {
assert(length == 20);
final typedData = makeUint64List(length);
final struct = Struct.create<Uint64ArrayStruct>(typedData);
return struct.array;
}
void testAddressOfUint64Array() {
const length = 20;
final array = makeUint64Array(length);
final expectedResult = makeExpectedResultUint64(0, length);
final result = takeUint64Pointer(array.address, length);
Expect.equals(expectedResult, result);
}
void testAddressOfUint64ArrayElementAt() {
const length = 20;
final array = makeUint64Array(length);
final expectedResult = makeExpectedResultUint64(0, length);
final result = takeUint64PointerMany(
array[0].address,
array[1].address,
array[2].address,
array[3].address,
array[4].address,
array[5].address,
array[6].address,
array[7].address,
array[8].address,
array[9].address,
array[10].address,
array[11].address,
array[12].address,
array[13].address,
array[14].address,
array[15].address,
array[16].address,
array[17].address,
array[18].address,
array[19].address,
);
Expect.equals(expectedResult, result);
}
final class FloatArrayStruct extends Struct {
@Array(20)
external Array<Float> array;
}
Array<Float> makeFloatArray(int length) {
assert(length == 20);
final typedData = makeFloat32List(length);
final struct = Struct.create<FloatArrayStruct>(typedData);
return struct.array;
}
void testAddressOfFloatArray() {
const length = 20;
final array = makeFloatArray(length);
final expectedResult = makeExpectedResultFloat(0, length);
final result = takeFloatPointer(array.address, length);
Expect.approxEquals(expectedResult, result);
}
void testAddressOfFloatArrayElementAt() {
const length = 20;
final array = makeFloatArray(length);
final expectedResult = makeExpectedResultFloat(0, length);
final result = takeFloatPointerMany(
array[0].address,
array[1].address,
array[2].address,
array[3].address,
array[4].address,
array[5].address,
array[6].address,
array[7].address,
array[8].address,
array[9].address,
array[10].address,
array[11].address,
array[12].address,
array[13].address,
array[14].address,
array[15].address,
array[16].address,
array[17].address,
array[18].address,
array[19].address,
);
Expect.approxEquals(expectedResult, result);
}
final class DoubleArrayStruct extends Struct {
@Array(20)
external Array<Double> array;
}
Array<Double> makeDoubleArray(int length) {
assert(length == 20);
final typedData = makeFloat64List(length);
final struct = Struct.create<DoubleArrayStruct>(typedData);
return struct.array;
}
void testAddressOfDoubleArray() {
const length = 20;
final array = makeDoubleArray(length);
final expectedResult = makeExpectedResultDouble(0, length);
final result = takeDoublePointer(array.address, length);
Expect.approxEquals(expectedResult, result);
}
void testAddressOfDoubleArrayElementAt() {
const length = 20;
final array = makeDoubleArray(length);
final expectedResult = makeExpectedResultDouble(0, length);
final result = takeDoublePointerMany(
array[0].address,
array[1].address,
array[2].address,
array[3].address,
array[4].address,
array[5].address,
array[6].address,
array[7].address,
array[8].address,
array[9].address,
array[10].address,
array[11].address,
array[12].address,
array[13].address,
array[14].address,
array[15].address,
array[16].address,
array[17].address,
array[18].address,
array[19].address,
);
Expect.approxEquals(expectedResult, result);
}
final class BoolArrayStruct extends Struct {
@Array(20)
external Array<Bool> array;
}
Array<Bool> makeBoolArray(int length) {
assert(length == 20);
final typedData = makeBoolList(length);
final struct = Struct.create<BoolArrayStruct>(typedData);
return struct.array;
}
void testAddressOfBoolArray() {
const length = 20;
final array = makeBoolArray(length);
final expectedResult = makeExpectedResultBool(0, length);
final result = takeBoolPointer(array.address, length);
Expect.equals(expectedResult, result);
}
void testAddressOfBoolArrayElementAt() {
const length = 20;
final array = makeBoolArray(length);
final expectedResult = makeExpectedResultBool(0, length);
final result = takeBoolPointerMany(
array[0].address,
array[1].address,
array[2].address,
array[3].address,
array[4].address,
array[5].address,
array[6].address,
array[7].address,
array[8].address,
array[9].address,
array[10].address,
array[11].address,
array[12].address,
array[13].address,
array[14].address,
array[15].address,
array[16].address,
array[17].address,
array[18].address,
array[19].address,
);
Expect.equals(expectedResult, result);
}

View file

@ -0,0 +1,755 @@
// 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.
//
// This file has been automatically generated. Please do not edit it manually.
// Generated by tests/ffi/generator/address_of_test_generator.dart.
//
// SharedObjects=ffi_test_functions
// VMOptions=
// VMOptions=--deterministic --optimization-counter-threshold=90
// VMOptions=--use-slow-path
// VMOptions=--use-slow-path --stacktrace-every=100
// ignore_for_file: unused_import
import 'dart:ffi';
import 'dart:typed_data';
import 'package:expect/expect.dart';
import 'address_of_generated_shared.dart';
import 'address_of_shared.dart';
import 'dylib_utils.dart';
final ffiTestFunctions = dlopenPlatformSpecific('ffi_test_functions');
@Native<Int8 Function(Pointer<Int8>, Size)>(
symbol: 'TakeInt8Pointer', isLeaf: true)
external int takeInt8Pointer(Pointer<Int8> pointer, int length);
@Native<
Int8 Function(
Pointer<Int8>,
Pointer<Int8>,
Pointer<Int8>,
Pointer<Int8>,
Pointer<Int8>,
Pointer<Int8>,
Pointer<Int8>,
Pointer<Int8>,
Pointer<Int8>,
Pointer<Int8>,
Pointer<Int8>,
Pointer<Int8>,
Pointer<Int8>,
Pointer<Int8>,
Pointer<Int8>,
Pointer<Int8>,
Pointer<Int8>,
Pointer<Int8>,
Pointer<Int8>,
Pointer<Int8>,
)>(symbol: 'TakeInt8PointerMany', isLeaf: true)
external int takeInt8PointerMany(
Pointer<Int8> pointer0,
Pointer<Int8> pointer1,
Pointer<Int8> pointer2,
Pointer<Int8> pointer3,
Pointer<Int8> pointer4,
Pointer<Int8> pointer5,
Pointer<Int8> pointer6,
Pointer<Int8> pointer7,
Pointer<Int8> pointer8,
Pointer<Int8> pointer9,
Pointer<Int8> pointer10,
Pointer<Int8> pointer11,
Pointer<Int8> pointer12,
Pointer<Int8> pointer13,
Pointer<Int8> pointer14,
Pointer<Int8> pointer15,
Pointer<Int8> pointer16,
Pointer<Int8> pointer17,
Pointer<Int8> pointer18,
Pointer<Int8> pointer19,
);
Int8List makeInt8List(int length) {
final typedData = Int8List(length);
for (int i = 0; i < length; i++) {
final value = i % 2 == 0 ? i : -i;
typedData[i] = value;
}
return typedData;
}
int makeExpectedResultInt8(int start, int end) {
int expectedResult = 0;
for (int i = start; i < end; i++) {
final value = i % 2 == 0 ? i : -i;
expectedResult += value;
}
return expectedResult;
}
@Native<Int16 Function(Pointer<Int16>, Size)>(
symbol: 'TakeInt16Pointer', isLeaf: true)
external int takeInt16Pointer(Pointer<Int16> pointer, int length);
@Native<
Int16 Function(
Pointer<Int16>,
Pointer<Int16>,
Pointer<Int16>,
Pointer<Int16>,
Pointer<Int16>,
Pointer<Int16>,
Pointer<Int16>,
Pointer<Int16>,
Pointer<Int16>,
Pointer<Int16>,
Pointer<Int16>,
Pointer<Int16>,
Pointer<Int16>,
Pointer<Int16>,
Pointer<Int16>,
Pointer<Int16>,
Pointer<Int16>,
Pointer<Int16>,
Pointer<Int16>,
Pointer<Int16>,
)>(symbol: 'TakeInt16PointerMany', isLeaf: true)
external int takeInt16PointerMany(
Pointer<Int16> pointer0,
Pointer<Int16> pointer1,
Pointer<Int16> pointer2,
Pointer<Int16> pointer3,
Pointer<Int16> pointer4,
Pointer<Int16> pointer5,
Pointer<Int16> pointer6,
Pointer<Int16> pointer7,
Pointer<Int16> pointer8,
Pointer<Int16> pointer9,
Pointer<Int16> pointer10,
Pointer<Int16> pointer11,
Pointer<Int16> pointer12,
Pointer<Int16> pointer13,
Pointer<Int16> pointer14,
Pointer<Int16> pointer15,
Pointer<Int16> pointer16,
Pointer<Int16> pointer17,
Pointer<Int16> pointer18,
Pointer<Int16> pointer19,
);
Int16List makeInt16List(int length) {
final typedData = Int16List(length);
for (int i = 0; i < length; i++) {
final value = i % 2 == 0 ? i : -i;
typedData[i] = value;
}
return typedData;
}
int makeExpectedResultInt16(int start, int end) {
int expectedResult = 0;
for (int i = start; i < end; i++) {
final value = i % 2 == 0 ? i : -i;
expectedResult += value;
}
return expectedResult;
}
@Native<Int32 Function(Pointer<Int32>, Size)>(
symbol: 'TakeInt32Pointer', isLeaf: true)
external int takeInt32Pointer(Pointer<Int32> pointer, int length);
@Native<
Int32 Function(
Pointer<Int32>,
Pointer<Int32>,
Pointer<Int32>,
Pointer<Int32>,
Pointer<Int32>,
Pointer<Int32>,
Pointer<Int32>,
Pointer<Int32>,
Pointer<Int32>,
Pointer<Int32>,
Pointer<Int32>,
Pointer<Int32>,
Pointer<Int32>,
Pointer<Int32>,
Pointer<Int32>,
Pointer<Int32>,
Pointer<Int32>,
Pointer<Int32>,
Pointer<Int32>,
Pointer<Int32>,
)>(symbol: 'TakeInt32PointerMany', isLeaf: true)
external int takeInt32PointerMany(
Pointer<Int32> pointer0,
Pointer<Int32> pointer1,
Pointer<Int32> pointer2,
Pointer<Int32> pointer3,
Pointer<Int32> pointer4,
Pointer<Int32> pointer5,
Pointer<Int32> pointer6,
Pointer<Int32> pointer7,
Pointer<Int32> pointer8,
Pointer<Int32> pointer9,
Pointer<Int32> pointer10,
Pointer<Int32> pointer11,
Pointer<Int32> pointer12,
Pointer<Int32> pointer13,
Pointer<Int32> pointer14,
Pointer<Int32> pointer15,
Pointer<Int32> pointer16,
Pointer<Int32> pointer17,
Pointer<Int32> pointer18,
Pointer<Int32> pointer19,
);
Int32List makeInt32List(int length) {
final typedData = Int32List(length);
for (int i = 0; i < length; i++) {
final value = i % 2 == 0 ? i : -i;
typedData[i] = value;
}
return typedData;
}
int makeExpectedResultInt32(int start, int end) {
int expectedResult = 0;
for (int i = start; i < end; i++) {
final value = i % 2 == 0 ? i : -i;
expectedResult += value;
}
return expectedResult;
}
@Native<Int64 Function(Pointer<Int64>, Size)>(
symbol: 'TakeInt64Pointer', isLeaf: true)
external int takeInt64Pointer(Pointer<Int64> pointer, int length);
@Native<
Int64 Function(
Pointer<Int64>,
Pointer<Int64>,
Pointer<Int64>,
Pointer<Int64>,
Pointer<Int64>,
Pointer<Int64>,
Pointer<Int64>,
Pointer<Int64>,
Pointer<Int64>,
Pointer<Int64>,
Pointer<Int64>,
Pointer<Int64>,
Pointer<Int64>,
Pointer<Int64>,
Pointer<Int64>,
Pointer<Int64>,
Pointer<Int64>,
Pointer<Int64>,
Pointer<Int64>,
Pointer<Int64>,
)>(symbol: 'TakeInt64PointerMany', isLeaf: true)
external int takeInt64PointerMany(
Pointer<Int64> pointer0,
Pointer<Int64> pointer1,
Pointer<Int64> pointer2,
Pointer<Int64> pointer3,
Pointer<Int64> pointer4,
Pointer<Int64> pointer5,
Pointer<Int64> pointer6,
Pointer<Int64> pointer7,
Pointer<Int64> pointer8,
Pointer<Int64> pointer9,
Pointer<Int64> pointer10,
Pointer<Int64> pointer11,
Pointer<Int64> pointer12,
Pointer<Int64> pointer13,
Pointer<Int64> pointer14,
Pointer<Int64> pointer15,
Pointer<Int64> pointer16,
Pointer<Int64> pointer17,
Pointer<Int64> pointer18,
Pointer<Int64> pointer19,
);
Int64List makeInt64List(int length) {
final typedData = Int64List(length);
for (int i = 0; i < length; i++) {
final value = i % 2 == 0 ? i : -i;
typedData[i] = value;
}
return typedData;
}
int makeExpectedResultInt64(int start, int end) {
int expectedResult = 0;
for (int i = start; i < end; i++) {
final value = i % 2 == 0 ? i : -i;
expectedResult += value;
}
return expectedResult;
}
@Native<Uint8 Function(Pointer<Uint8>, Size)>(
symbol: 'TakeUint8Pointer', isLeaf: true)
external int takeUint8Pointer(Pointer<Uint8> pointer, int length);
@Native<
Uint8 Function(
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
Pointer<Uint8>,
)>(symbol: 'TakeUint8PointerMany', isLeaf: true)
external int takeUint8PointerMany(
Pointer<Uint8> pointer0,
Pointer<Uint8> pointer1,
Pointer<Uint8> pointer2,
Pointer<Uint8> pointer3,
Pointer<Uint8> pointer4,
Pointer<Uint8> pointer5,
Pointer<Uint8> pointer6,
Pointer<Uint8> pointer7,
Pointer<Uint8> pointer8,
Pointer<Uint8> pointer9,
Pointer<Uint8> pointer10,
Pointer<Uint8> pointer11,
Pointer<Uint8> pointer12,
Pointer<Uint8> pointer13,
Pointer<Uint8> pointer14,
Pointer<Uint8> pointer15,
Pointer<Uint8> pointer16,
Pointer<Uint8> pointer17,
Pointer<Uint8> pointer18,
Pointer<Uint8> pointer19,
);
Uint8List makeUint8List(int length) {
final typedData = Uint8List(length);
for (int i = 0; i < length; i++) {
final value = i;
typedData[i] = value;
}
return typedData;
}
int makeExpectedResultUint8(int start, int end) {
int expectedResult = 0;
for (int i = start; i < end; i++) {
final value = i;
expectedResult += value;
}
return expectedResult;
}
@Native<Uint16 Function(Pointer<Uint16>, Size)>(
symbol: 'TakeUint16Pointer', isLeaf: true)
external int takeUint16Pointer(Pointer<Uint16> pointer, int length);
@Native<
Uint16 Function(
Pointer<Uint16>,
Pointer<Uint16>,
Pointer<Uint16>,
Pointer<Uint16>,
Pointer<Uint16>,
Pointer<Uint16>,
Pointer<Uint16>,
Pointer<Uint16>,
Pointer<Uint16>,
Pointer<Uint16>,
Pointer<Uint16>,
Pointer<Uint16>,
Pointer<Uint16>,
Pointer<Uint16>,
Pointer<Uint16>,
Pointer<Uint16>,
Pointer<Uint16>,
Pointer<Uint16>,
Pointer<Uint16>,
Pointer<Uint16>,
)>(symbol: 'TakeUint16PointerMany', isLeaf: true)
external int takeUint16PointerMany(
Pointer<Uint16> pointer0,
Pointer<Uint16> pointer1,
Pointer<Uint16> pointer2,
Pointer<Uint16> pointer3,
Pointer<Uint16> pointer4,
Pointer<Uint16> pointer5,
Pointer<Uint16> pointer6,
Pointer<Uint16> pointer7,
Pointer<Uint16> pointer8,
Pointer<Uint16> pointer9,
Pointer<Uint16> pointer10,
Pointer<Uint16> pointer11,
Pointer<Uint16> pointer12,
Pointer<Uint16> pointer13,
Pointer<Uint16> pointer14,
Pointer<Uint16> pointer15,
Pointer<Uint16> pointer16,
Pointer<Uint16> pointer17,
Pointer<Uint16> pointer18,
Pointer<Uint16> pointer19,
);
Uint16List makeUint16List(int length) {
final typedData = Uint16List(length);
for (int i = 0; i < length; i++) {
final value = i;
typedData[i] = value;
}
return typedData;
}
int makeExpectedResultUint16(int start, int end) {
int expectedResult = 0;
for (int i = start; i < end; i++) {
final value = i;
expectedResult += value;
}
return expectedResult;
}
@Native<Uint32 Function(Pointer<Uint32>, Size)>(
symbol: 'TakeUint32Pointer', isLeaf: true)
external int takeUint32Pointer(Pointer<Uint32> pointer, int length);
@Native<
Uint32 Function(
Pointer<Uint32>,
Pointer<Uint32>,
Pointer<Uint32>,
Pointer<Uint32>,
Pointer<Uint32>,
Pointer<Uint32>,
Pointer<Uint32>,
Pointer<Uint32>,
Pointer<Uint32>,
Pointer<Uint32>,
Pointer<Uint32>,
Pointer<Uint32>,
Pointer<Uint32>,
Pointer<Uint32>,
Pointer<Uint32>,
Pointer<Uint32>,
Pointer<Uint32>,
Pointer<Uint32>,
Pointer<Uint32>,
Pointer<Uint32>,
)>(symbol: 'TakeUint32PointerMany', isLeaf: true)
external int takeUint32PointerMany(
Pointer<Uint32> pointer0,
Pointer<Uint32> pointer1,
Pointer<Uint32> pointer2,
Pointer<Uint32> pointer3,
Pointer<Uint32> pointer4,
Pointer<Uint32> pointer5,
Pointer<Uint32> pointer6,
Pointer<Uint32> pointer7,
Pointer<Uint32> pointer8,
Pointer<Uint32> pointer9,
Pointer<Uint32> pointer10,
Pointer<Uint32> pointer11,
Pointer<Uint32> pointer12,
Pointer<Uint32> pointer13,
Pointer<Uint32> pointer14,
Pointer<Uint32> pointer15,
Pointer<Uint32> pointer16,
Pointer<Uint32> pointer17,
Pointer<Uint32> pointer18,
Pointer<Uint32> pointer19,
);
Uint32List makeUint32List(int length) {
final typedData = Uint32List(length);
for (int i = 0; i < length; i++) {
final value = i;
typedData[i] = value;
}
return typedData;
}
int makeExpectedResultUint32(int start, int end) {
int expectedResult = 0;
for (int i = start; i < end; i++) {
final value = i;
expectedResult += value;
}
return expectedResult;
}
@Native<Uint64 Function(Pointer<Uint64>, Size)>(
symbol: 'TakeUint64Pointer', isLeaf: true)
external int takeUint64Pointer(Pointer<Uint64> pointer, int length);
@Native<
Uint64 Function(
Pointer<Uint64>,
Pointer<Uint64>,
Pointer<Uint64>,
Pointer<Uint64>,
Pointer<Uint64>,
Pointer<Uint64>,
Pointer<Uint64>,
Pointer<Uint64>,
Pointer<Uint64>,
Pointer<Uint64>,
Pointer<Uint64>,
Pointer<Uint64>,
Pointer<Uint64>,
Pointer<Uint64>,
Pointer<Uint64>,
Pointer<Uint64>,
Pointer<Uint64>,
Pointer<Uint64>,
Pointer<Uint64>,
Pointer<Uint64>,
)>(symbol: 'TakeUint64PointerMany', isLeaf: true)
external int takeUint64PointerMany(
Pointer<Uint64> pointer0,
Pointer<Uint64> pointer1,
Pointer<Uint64> pointer2,
Pointer<Uint64> pointer3,
Pointer<Uint64> pointer4,
Pointer<Uint64> pointer5,
Pointer<Uint64> pointer6,
Pointer<Uint64> pointer7,
Pointer<Uint64> pointer8,
Pointer<Uint64> pointer9,
Pointer<Uint64> pointer10,
Pointer<Uint64> pointer11,
Pointer<Uint64> pointer12,
Pointer<Uint64> pointer13,
Pointer<Uint64> pointer14,
Pointer<Uint64> pointer15,
Pointer<Uint64> pointer16,
Pointer<Uint64> pointer17,
Pointer<Uint64> pointer18,
Pointer<Uint64> pointer19,
);
Uint64List makeUint64List(int length) {
final typedData = Uint64List(length);
for (int i = 0; i < length; i++) {
final value = i;
typedData[i] = value;
}
return typedData;
}
int makeExpectedResultUint64(int start, int end) {
int expectedResult = 0;
for (int i = start; i < end; i++) {
final value = i;
expectedResult += value;
}
return expectedResult;
}
@Native<Float Function(Pointer<Float>, Size)>(
symbol: 'TakeFloatPointer', isLeaf: true)
external double takeFloatPointer(Pointer<Float> pointer, int length);
@Native<
Float Function(
Pointer<Float>,
Pointer<Float>,
Pointer<Float>,
Pointer<Float>,
Pointer<Float>,
Pointer<Float>,
Pointer<Float>,
Pointer<Float>,
Pointer<Float>,
Pointer<Float>,
Pointer<Float>,
Pointer<Float>,
Pointer<Float>,
Pointer<Float>,
Pointer<Float>,
Pointer<Float>,
Pointer<Float>,
Pointer<Float>,
Pointer<Float>,
Pointer<Float>,
)>(symbol: 'TakeFloatPointerMany', isLeaf: true)
external double takeFloatPointerMany(
Pointer<Float> pointer0,
Pointer<Float> pointer1,
Pointer<Float> pointer2,
Pointer<Float> pointer3,
Pointer<Float> pointer4,
Pointer<Float> pointer5,
Pointer<Float> pointer6,
Pointer<Float> pointer7,
Pointer<Float> pointer8,
Pointer<Float> pointer9,
Pointer<Float> pointer10,
Pointer<Float> pointer11,
Pointer<Float> pointer12,
Pointer<Float> pointer13,
Pointer<Float> pointer14,
Pointer<Float> pointer15,
Pointer<Float> pointer16,
Pointer<Float> pointer17,
Pointer<Float> pointer18,
Pointer<Float> pointer19,
);
Float32List makeFloat32List(int length) {
final typedData = Float32List(length);
for (int i = 0; i < length; i++) {
final value = (i % 2 == 0 ? i : -i).toDouble();
typedData[i] = value;
}
return typedData;
}
double makeExpectedResultFloat(int start, int end) {
double expectedResult = 0;
for (int i = start; i < end; i++) {
final value = (i % 2 == 0 ? i : -i).toDouble();
expectedResult += value;
}
return expectedResult;
}
@Native<Double Function(Pointer<Double>, Size)>(
symbol: 'TakeDoublePointer', isLeaf: true)
external double takeDoublePointer(Pointer<Double> pointer, int length);
@Native<
Double Function(
Pointer<Double>,
Pointer<Double>,
Pointer<Double>,
Pointer<Double>,
Pointer<Double>,
Pointer<Double>,
Pointer<Double>,
Pointer<Double>,
Pointer<Double>,
Pointer<Double>,
Pointer<Double>,
Pointer<Double>,
Pointer<Double>,
Pointer<Double>,
Pointer<Double>,
Pointer<Double>,
Pointer<Double>,
Pointer<Double>,
Pointer<Double>,
Pointer<Double>,
)>(symbol: 'TakeDoublePointerMany', isLeaf: true)
external double takeDoublePointerMany(
Pointer<Double> pointer0,
Pointer<Double> pointer1,
Pointer<Double> pointer2,
Pointer<Double> pointer3,
Pointer<Double> pointer4,
Pointer<Double> pointer5,
Pointer<Double> pointer6,
Pointer<Double> pointer7,
Pointer<Double> pointer8,
Pointer<Double> pointer9,
Pointer<Double> pointer10,
Pointer<Double> pointer11,
Pointer<Double> pointer12,
Pointer<Double> pointer13,
Pointer<Double> pointer14,
Pointer<Double> pointer15,
Pointer<Double> pointer16,
Pointer<Double> pointer17,
Pointer<Double> pointer18,
Pointer<Double> pointer19,
);
Float64List makeFloat64List(int length) {
final typedData = Float64List(length);
for (int i = 0; i < length; i++) {
final value = (i % 2 == 0 ? i : -i).toDouble();
typedData[i] = value;
}
return typedData;
}
double makeExpectedResultDouble(int start, int end) {
double expectedResult = 0;
for (int i = start; i < end; i++) {
final value = (i % 2 == 0 ? i : -i).toDouble();
expectedResult += value;
}
return expectedResult;
}
@Native<Bool Function(Pointer<Bool>, Size)>(
symbol: 'TakeBoolPointer', isLeaf: true)
external bool takeBoolPointer(Pointer<Bool> pointer, int length);
@Native<
Bool Function(
Pointer<Bool>,
Pointer<Bool>,
Pointer<Bool>,
Pointer<Bool>,
Pointer<Bool>,
Pointer<Bool>,
Pointer<Bool>,
Pointer<Bool>,
Pointer<Bool>,
Pointer<Bool>,
Pointer<Bool>,
Pointer<Bool>,
Pointer<Bool>,
Pointer<Bool>,
Pointer<Bool>,
Pointer<Bool>,
Pointer<Bool>,
Pointer<Bool>,
Pointer<Bool>,
Pointer<Bool>,
)>(symbol: 'TakeBoolPointerMany', isLeaf: true)
external bool takeBoolPointerMany(
Pointer<Bool> pointer0,
Pointer<Bool> pointer1,
Pointer<Bool> pointer2,
Pointer<Bool> pointer3,
Pointer<Bool> pointer4,
Pointer<Bool> pointer5,
Pointer<Bool> pointer6,
Pointer<Bool> pointer7,
Pointer<Bool> pointer8,
Pointer<Bool> pointer9,
Pointer<Bool> pointer10,
Pointer<Bool> pointer11,
Pointer<Bool> pointer12,
Pointer<Bool> pointer13,
Pointer<Bool> pointer14,
Pointer<Bool> pointer15,
Pointer<Bool> pointer16,
Pointer<Bool> pointer17,
Pointer<Bool> pointer18,
Pointer<Bool> pointer19,
);

View file

@ -0,0 +1,34 @@
// 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.
//
// This file has been automatically generated. Please do not edit it manually.
// Generated by tests/ffi/generator/address_of_test_generator.dart.
//
// SharedObjects=ffi_test_functions
// VMOptions=
// VMOptions=--deterministic --optimization-counter-threshold=90
// VMOptions=--use-slow-path
// VMOptions=--use-slow-path --stacktrace-every=100
import 'dart:typed_data';
bool makeExpectedResultBool(int start, int end) {
bool result = false;
for (int i = start; i < end; i++) {
final value = _value(i);
result ^= value;
}
return result;
}
Int8List makeBoolList(int length) {
final typedData = Int8List(length);
for (int i = 0; i < length; i++) {
final value = _value(i) ? 1 : 0;
typedData[i] = value;
}
return typedData;
}
bool _value(int index) => index % 2 == 1;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,62 @@
// 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.
//
// This file has been automatically generated. Please do not edit it manually.
// Generated by tests/ffi/generator/address_of_test_generator.dart.
//
// SharedObjects=ffi_test_functions
// VMOptions=
// VMOptions=--deterministic --optimization-counter-threshold=90
// VMOptions=--use-slow-path
// VMOptions=--use-slow-path --stacktrace-every=100
// ignore_for_file: unused_import
import 'dart:ffi';
import 'dart:typed_data';
import 'package:expect/expect.dart';
import 'address_of_generated_shared.dart';
import 'address_of_shared.dart';
import 'dylib_utils.dart';
final ffiTestFunctions = dlopenPlatformSpecific('ffi_test_functions');
void main() {
// Force dlopen so @Native lookups in DynamicLibrary.process() succeed.
dlopenGlobalPlatformSpecific('ffi_test_functions');
for (int i = 0; i < 100; ++i) {
testAddressOfInt8Array();
}
}
final class Int8ArrayStruct extends Struct {
@Array(20)
external Array<Int8> array;
}
Array<Int8> makeInt8Array(int length) {
assert(length == 20);
final typedData = makeInt8List(length);
final struct = Struct.create<Int8ArrayStruct>(typedData);
return struct.array;
}
void testAddressOfInt8Array() {
const length = 20;
final array = makeInt8Array(length);
final expectedResult = makeExpectedResultInt8(0, length);
final result = Foo.takeInt8Pointer(array.address, length);
Expect.equals(expectedResult, result);
}
class Foo {
@Native<Int8 Function(Pointer<Int8>, Size)>(
symbol: 'TakeInt8Pointer',
isLeaf: true,
)
external static int takeInt8Pointer(Pointer<Int8> pointer, int length);
}

View file

@ -0,0 +1,853 @@
// 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.
//
// This file has been automatically generated. Please do not edit it manually.
// Generated by tests/ffi/generator/address_of_test_generator.dart.
//
// SharedObjects=ffi_test_functions
// VMOptions=
// VMOptions=--deterministic --optimization-counter-threshold=90
// VMOptions=--use-slow-path
// VMOptions=--use-slow-path --stacktrace-every=100
// ignore_for_file: unused_import
import 'dart:ffi';
import 'dart:typed_data';
import 'package:expect/expect.dart';
import 'address_of_generated_shared.dart';
import 'address_of_shared.dart';
import 'dylib_utils.dart';
final ffiTestFunctions = dlopenPlatformSpecific('ffi_test_functions');
void main() {
// Force dlopen so @Native lookups in DynamicLibrary.process() succeed.
dlopenGlobalPlatformSpecific('ffi_test_functions');
for (int i = 0; i < 100; ++i) {
testAddressOfInt8List();
testAddressOfInt8ListElementAt();
testAddressOfInt8ListView();
testAddressOfInt8ListViewMany();
testAddressOfInt16List();
testAddressOfInt16ListElementAt();
testAddressOfInt16ListView();
testAddressOfInt16ListViewMany();
testAddressOfInt32List();
testAddressOfInt32ListElementAt();
testAddressOfInt32ListView();
testAddressOfInt32ListViewMany();
testAddressOfInt64List();
testAddressOfInt64ListElementAt();
testAddressOfInt64ListView();
testAddressOfInt64ListViewMany();
testAddressOfUint8List();
testAddressOfUint8ListElementAt();
testAddressOfUint8ListView();
testAddressOfUint8ListViewMany();
testAddressOfUint16List();
testAddressOfUint16ListElementAt();
testAddressOfUint16ListView();
testAddressOfUint16ListViewMany();
testAddressOfUint32List();
testAddressOfUint32ListElementAt();
testAddressOfUint32ListView();
testAddressOfUint32ListViewMany();
testAddressOfUint64List();
testAddressOfUint64ListElementAt();
testAddressOfUint64ListView();
testAddressOfUint64ListViewMany();
testAddressOfFloat32List();
testAddressOfFloat32ListElementAt();
testAddressOfFloat32ListView();
testAddressOfFloat32ListViewMany();
testAddressOfFloat64List();
testAddressOfFloat64ListElementAt();
testAddressOfFloat64ListView();
testAddressOfFloat64ListViewMany();
}
}
void testAddressOfInt8List() {
const length = 20;
final typedData = makeInt8List(length);
final expectedResult = makeExpectedResultInt8(0, length);
final result = takeInt8Pointer(typedData.address, length);
Expect.equals(expectedResult, result);
}
void testAddressOfInt8ListElementAt() {
const length = 20;
final typedData = makeInt8List(length);
final expectedResult = makeExpectedResultInt8(0, length);
final result = takeInt8PointerMany(
typedData[0].address,
typedData[1].address,
typedData[2].address,
typedData[3].address,
typedData[4].address,
typedData[5].address,
typedData[6].address,
typedData[7].address,
typedData[8].address,
typedData[9].address,
typedData[10].address,
typedData[11].address,
typedData[12].address,
typedData[13].address,
typedData[14].address,
typedData[15].address,
typedData[16].address,
typedData[17].address,
typedData[18].address,
typedData[19].address,
);
Expect.equals(expectedResult, result);
}
void testAddressOfInt8ListView() {
const sourceLength = 30;
const viewStart = 10;
const viewLength = 10;
final viewEnd = viewStart + viewLength;
final source = makeInt8List(sourceLength);
final view = Int8List.sublistView(source, viewStart, viewEnd);
final expectedResult = makeExpectedResultInt8(viewStart, viewEnd);
final result = takeInt8Pointer(view.address, view.length);
Expect.equals(expectedResult, result);
}
void testAddressOfInt8ListViewMany() {
const length = 20;
final typedData = makeInt8List(length);
final expectedResult = makeExpectedResultInt8(0, length);
final result = takeInt8PointerMany(
Int8List.sublistView(typedData, 0, 0 + 1).address,
Int8List.sublistView(typedData, 1, 1 + 1).address,
Int8List.sublistView(typedData, 2, 2 + 1).address,
Int8List.sublistView(typedData, 3, 3 + 1).address,
Int8List.sublistView(typedData, 4, 4 + 1).address,
Int8List.sublistView(typedData, 5, 5 + 1).address,
Int8List.sublistView(typedData, 6, 6 + 1).address,
Int8List.sublistView(typedData, 7, 7 + 1).address,
Int8List.sublistView(typedData, 8, 8 + 1).address,
Int8List.sublistView(typedData, 9, 9 + 1).address,
Int8List.sublistView(typedData, 10, 10 + 1).address,
Int8List.sublistView(typedData, 11, 11 + 1).address,
Int8List.sublistView(typedData, 12, 12 + 1).address,
Int8List.sublistView(typedData, 13, 13 + 1).address,
Int8List.sublistView(typedData, 14, 14 + 1).address,
Int8List.sublistView(typedData, 15, 15 + 1).address,
Int8List.sublistView(typedData, 16, 16 + 1).address,
Int8List.sublistView(typedData, 17, 17 + 1).address,
Int8List.sublistView(typedData, 18, 18 + 1).address,
Int8List.sublistView(typedData, 19, 19 + 1).address,
);
Expect.equals(expectedResult, result);
}
void testAddressOfInt16List() {
const length = 20;
final typedData = makeInt16List(length);
final expectedResult = makeExpectedResultInt16(0, length);
final result = takeInt16Pointer(typedData.address, length);
Expect.equals(expectedResult, result);
}
void testAddressOfInt16ListElementAt() {
const length = 20;
final typedData = makeInt16List(length);
final expectedResult = makeExpectedResultInt16(0, length);
final result = takeInt16PointerMany(
typedData[0].address,
typedData[1].address,
typedData[2].address,
typedData[3].address,
typedData[4].address,
typedData[5].address,
typedData[6].address,
typedData[7].address,
typedData[8].address,
typedData[9].address,
typedData[10].address,
typedData[11].address,
typedData[12].address,
typedData[13].address,
typedData[14].address,
typedData[15].address,
typedData[16].address,
typedData[17].address,
typedData[18].address,
typedData[19].address,
);
Expect.equals(expectedResult, result);
}
void testAddressOfInt16ListView() {
const sourceLength = 30;
const viewStart = 10;
const viewLength = 10;
final viewEnd = viewStart + viewLength;
final source = makeInt16List(sourceLength);
final view = Int16List.sublistView(source, viewStart, viewEnd);
final expectedResult = makeExpectedResultInt16(viewStart, viewEnd);
final result = takeInt16Pointer(view.address, view.length);
Expect.equals(expectedResult, result);
}
void testAddressOfInt16ListViewMany() {
const length = 20;
final typedData = makeInt16List(length);
final expectedResult = makeExpectedResultInt16(0, length);
final result = takeInt16PointerMany(
Int16List.sublistView(typedData, 0, 0 + 1).address,
Int16List.sublistView(typedData, 1, 1 + 1).address,
Int16List.sublistView(typedData, 2, 2 + 1).address,
Int16List.sublistView(typedData, 3, 3 + 1).address,
Int16List.sublistView(typedData, 4, 4 + 1).address,
Int16List.sublistView(typedData, 5, 5 + 1).address,
Int16List.sublistView(typedData, 6, 6 + 1).address,
Int16List.sublistView(typedData, 7, 7 + 1).address,
Int16List.sublistView(typedData, 8, 8 + 1).address,
Int16List.sublistView(typedData, 9, 9 + 1).address,
Int16List.sublistView(typedData, 10, 10 + 1).address,
Int16List.sublistView(typedData, 11, 11 + 1).address,
Int16List.sublistView(typedData, 12, 12 + 1).address,
Int16List.sublistView(typedData, 13, 13 + 1).address,
Int16List.sublistView(typedData, 14, 14 + 1).address,
Int16List.sublistView(typedData, 15, 15 + 1).address,
Int16List.sublistView(typedData, 16, 16 + 1).address,
Int16List.sublistView(typedData, 17, 17 + 1).address,
Int16List.sublistView(typedData, 18, 18 + 1).address,
Int16List.sublistView(typedData, 19, 19 + 1).address,
);
Expect.equals(expectedResult, result);
}
void testAddressOfInt32List() {
const length = 20;
final typedData = makeInt32List(length);
final expectedResult = makeExpectedResultInt32(0, length);
final result = takeInt32Pointer(typedData.address, length);
Expect.equals(expectedResult, result);
}
void testAddressOfInt32ListElementAt() {
const length = 20;
final typedData = makeInt32List(length);
final expectedResult = makeExpectedResultInt32(0, length);
final result = takeInt32PointerMany(
typedData[0].address,
typedData[1].address,
typedData[2].address,
typedData[3].address,
typedData[4].address,
typedData[5].address,
typedData[6].address,
typedData[7].address,
typedData[8].address,
typedData[9].address,
typedData[10].address,
typedData[11].address,
typedData[12].address,
typedData[13].address,
typedData[14].address,
typedData[15].address,
typedData[16].address,
typedData[17].address,
typedData[18].address,
typedData[19].address,
);
Expect.equals(expectedResult, result);
}
void testAddressOfInt32ListView() {
const sourceLength = 30;
const viewStart = 10;
const viewLength = 10;
final viewEnd = viewStart + viewLength;
final source = makeInt32List(sourceLength);
final view = Int32List.sublistView(source, viewStart, viewEnd);
final expectedResult = makeExpectedResultInt32(viewStart, viewEnd);
final result = takeInt32Pointer(view.address, view.length);
Expect.equals(expectedResult, result);
}
void testAddressOfInt32ListViewMany() {
const length = 20;
final typedData = makeInt32List(length);
final expectedResult = makeExpectedResultInt32(0, length);
final result = takeInt32PointerMany(
Int32List.sublistView(typedData, 0, 0 + 1).address,
Int32List.sublistView(typedData, 1, 1 + 1).address,
Int32List.sublistView(typedData, 2, 2 + 1).address,
Int32List.sublistView(typedData, 3, 3 + 1).address,
Int32List.sublistView(typedData, 4, 4 + 1).address,
Int32List.sublistView(typedData, 5, 5 + 1).address,
Int32List.sublistView(typedData, 6, 6 + 1).address,
Int32List.sublistView(typedData, 7, 7 + 1).address,
Int32List.sublistView(typedData, 8, 8 + 1).address,
Int32List.sublistView(typedData, 9, 9 + 1).address,
Int32List.sublistView(typedData, 10, 10 + 1).address,
Int32List.sublistView(typedData, 11, 11 + 1).address,
Int32List.sublistView(typedData, 12, 12 + 1).address,
Int32List.sublistView(typedData, 13, 13 + 1).address,
Int32List.sublistView(typedData, 14, 14 + 1).address,
Int32List.sublistView(typedData, 15, 15 + 1).address,
Int32List.sublistView(typedData, 16, 16 + 1).address,
Int32List.sublistView(typedData, 17, 17 + 1).address,
Int32List.sublistView(typedData, 18, 18 + 1).address,
Int32List.sublistView(typedData, 19, 19 + 1).address,
);
Expect.equals(expectedResult, result);
}
void testAddressOfInt64List() {
const length = 20;
final typedData = makeInt64List(length);
final expectedResult = makeExpectedResultInt64(0, length);
final result = takeInt64Pointer(typedData.address, length);
Expect.equals(expectedResult, result);
}
void testAddressOfInt64ListElementAt() {
const length = 20;
final typedData = makeInt64List(length);
final expectedResult = makeExpectedResultInt64(0, length);
final result = takeInt64PointerMany(
typedData[0].address,
typedData[1].address,
typedData[2].address,
typedData[3].address,
typedData[4].address,
typedData[5].address,
typedData[6].address,
typedData[7].address,
typedData[8].address,
typedData[9].address,
typedData[10].address,
typedData[11].address,
typedData[12].address,
typedData[13].address,
typedData[14].address,
typedData[15].address,
typedData[16].address,
typedData[17].address,
typedData[18].address,
typedData[19].address,
);
Expect.equals(expectedResult, result);
}
void testAddressOfInt64ListView() {
const sourceLength = 30;
const viewStart = 10;
const viewLength = 10;
final viewEnd = viewStart + viewLength;
final source = makeInt64List(sourceLength);
final view = Int64List.sublistView(source, viewStart, viewEnd);
final expectedResult = makeExpectedResultInt64(viewStart, viewEnd);
final result = takeInt64Pointer(view.address, view.length);
Expect.equals(expectedResult, result);
}
void testAddressOfInt64ListViewMany() {
const length = 20;
final typedData = makeInt64List(length);
final expectedResult = makeExpectedResultInt64(0, length);
final result = takeInt64PointerMany(
Int64List.sublistView(typedData, 0, 0 + 1).address,
Int64List.sublistView(typedData, 1, 1 + 1).address,
Int64List.sublistView(typedData, 2, 2 + 1).address,
Int64List.sublistView(typedData, 3, 3 + 1).address,
Int64List.sublistView(typedData, 4, 4 + 1).address,
Int64List.sublistView(typedData, 5, 5 + 1).address,
Int64List.sublistView(typedData, 6, 6 + 1).address,
Int64List.sublistView(typedData, 7, 7 + 1).address,
Int64List.sublistView(typedData, 8, 8 + 1).address,
Int64List.sublistView(typedData, 9, 9 + 1).address,
Int64List.sublistView(typedData, 10, 10 + 1).address,
Int64List.sublistView(typedData, 11, 11 + 1).address,
Int64List.sublistView(typedData, 12, 12 + 1).address,
Int64List.sublistView(typedData, 13, 13 + 1).address,
Int64List.sublistView(typedData, 14, 14 + 1).address,
Int64List.sublistView(typedData, 15, 15 + 1).address,
Int64List.sublistView(typedData, 16, 16 + 1).address,
Int64List.sublistView(typedData, 17, 17 + 1).address,
Int64List.sublistView(typedData, 18, 18 + 1).address,
Int64List.sublistView(typedData, 19, 19 + 1).address,
);
Expect.equals(expectedResult, result);
}
void testAddressOfUint8List() {
const length = 20;
final typedData = makeUint8List(length);
final expectedResult = makeExpectedResultUint8(0, length);
final result = takeUint8Pointer(typedData.address, length);
Expect.equals(expectedResult, result);
}
void testAddressOfUint8ListElementAt() {
const length = 20;
final typedData = makeUint8List(length);
final expectedResult = makeExpectedResultUint8(0, length);
final result = takeUint8PointerMany(
typedData[0].address,
typedData[1].address,
typedData[2].address,
typedData[3].address,
typedData[4].address,
typedData[5].address,
typedData[6].address,
typedData[7].address,
typedData[8].address,
typedData[9].address,
typedData[10].address,
typedData[11].address,
typedData[12].address,
typedData[13].address,
typedData[14].address,
typedData[15].address,
typedData[16].address,
typedData[17].address,
typedData[18].address,
typedData[19].address,
);
Expect.equals(expectedResult, result);
}
void testAddressOfUint8ListView() {
const sourceLength = 30;
const viewStart = 10;
const viewLength = 10;
final viewEnd = viewStart + viewLength;
final source = makeUint8List(sourceLength);
final view = Uint8List.sublistView(source, viewStart, viewEnd);
final expectedResult = makeExpectedResultUint8(viewStart, viewEnd);
final result = takeUint8Pointer(view.address, view.length);
Expect.equals(expectedResult, result);
}
void testAddressOfUint8ListViewMany() {
const length = 20;
final typedData = makeUint8List(length);
final expectedResult = makeExpectedResultUint8(0, length);
final result = takeUint8PointerMany(
Uint8List.sublistView(typedData, 0, 0 + 1).address,
Uint8List.sublistView(typedData, 1, 1 + 1).address,
Uint8List.sublistView(typedData, 2, 2 + 1).address,
Uint8List.sublistView(typedData, 3, 3 + 1).address,
Uint8List.sublistView(typedData, 4, 4 + 1).address,
Uint8List.sublistView(typedData, 5, 5 + 1).address,
Uint8List.sublistView(typedData, 6, 6 + 1).address,
Uint8List.sublistView(typedData, 7, 7 + 1).address,
Uint8List.sublistView(typedData, 8, 8 + 1).address,
Uint8List.sublistView(typedData, 9, 9 + 1).address,
Uint8List.sublistView(typedData, 10, 10 + 1).address,
Uint8List.sublistView(typedData, 11, 11 + 1).address,
Uint8List.sublistView(typedData, 12, 12 + 1).address,
Uint8List.sublistView(typedData, 13, 13 + 1).address,
Uint8List.sublistView(typedData, 14, 14 + 1).address,
Uint8List.sublistView(typedData, 15, 15 + 1).address,
Uint8List.sublistView(typedData, 16, 16 + 1).address,
Uint8List.sublistView(typedData, 17, 17 + 1).address,
Uint8List.sublistView(typedData, 18, 18 + 1).address,
Uint8List.sublistView(typedData, 19, 19 + 1).address,
);
Expect.equals(expectedResult, result);
}
void testAddressOfUint16List() {
const length = 20;
final typedData = makeUint16List(length);
final expectedResult = makeExpectedResultUint16(0, length);
final result = takeUint16Pointer(typedData.address, length);
Expect.equals(expectedResult, result);
}
void testAddressOfUint16ListElementAt() {
const length = 20;
final typedData = makeUint16List(length);
final expectedResult = makeExpectedResultUint16(0, length);
final result = takeUint16PointerMany(
typedData[0].address,
typedData[1].address,
typedData[2].address,
typedData[3].address,
typedData[4].address,
typedData[5].address,
typedData[6].address,
typedData[7].address,
typedData[8].address,
typedData[9].address,
typedData[10].address,
typedData[11].address,
typedData[12].address,
typedData[13].address,
typedData[14].address,
typedData[15].address,
typedData[16].address,
typedData[17].address,
typedData[18].address,
typedData[19].address,
);
Expect.equals(expectedResult, result);
}
void testAddressOfUint16ListView() {
const sourceLength = 30;
const viewStart = 10;
const viewLength = 10;
final viewEnd = viewStart + viewLength;
final source = makeUint16List(sourceLength);
final view = Uint16List.sublistView(source, viewStart, viewEnd);
final expectedResult = makeExpectedResultUint16(viewStart, viewEnd);
final result = takeUint16Pointer(view.address, view.length);
Expect.equals(expectedResult, result);
}
void testAddressOfUint16ListViewMany() {
const length = 20;
final typedData = makeUint16List(length);
final expectedResult = makeExpectedResultUint16(0, length);
final result = takeUint16PointerMany(
Uint16List.sublistView(typedData, 0, 0 + 1).address,
Uint16List.sublistView(typedData, 1, 1 + 1).address,
Uint16List.sublistView(typedData, 2, 2 + 1).address,
Uint16List.sublistView(typedData, 3, 3 + 1).address,
Uint16List.sublistView(typedData, 4, 4 + 1).address,
Uint16List.sublistView(typedData, 5, 5 + 1).address,
Uint16List.sublistView(typedData, 6, 6 + 1).address,
Uint16List.sublistView(typedData, 7, 7 + 1).address,
Uint16List.sublistView(typedData, 8, 8 + 1).address,
Uint16List.sublistView(typedData, 9, 9 + 1).address,
Uint16List.sublistView(typedData, 10, 10 + 1).address,
Uint16List.sublistView(typedData, 11, 11 + 1).address,
Uint16List.sublistView(typedData, 12, 12 + 1).address,
Uint16List.sublistView(typedData, 13, 13 + 1).address,
Uint16List.sublistView(typedData, 14, 14 + 1).address,
Uint16List.sublistView(typedData, 15, 15 + 1).address,
Uint16List.sublistView(typedData, 16, 16 + 1).address,
Uint16List.sublistView(typedData, 17, 17 + 1).address,
Uint16List.sublistView(typedData, 18, 18 + 1).address,
Uint16List.sublistView(typedData, 19, 19 + 1).address,
);
Expect.equals(expectedResult, result);
}
void testAddressOfUint32List() {
const length = 20;
final typedData = makeUint32List(length);
final expectedResult = makeExpectedResultUint32(0, length);
final result = takeUint32Pointer(typedData.address, length);
Expect.equals(expectedResult, result);
}
void testAddressOfUint32ListElementAt() {
const length = 20;
final typedData = makeUint32List(length);
final expectedResult = makeExpectedResultUint32(0, length);
final result = takeUint32PointerMany(
typedData[0].address,
typedData[1].address,
typedData[2].address,
typedData[3].address,
typedData[4].address,
typedData[5].address,
typedData[6].address,
typedData[7].address,
typedData[8].address,
typedData[9].address,
typedData[10].address,
typedData[11].address,
typedData[12].address,
typedData[13].address,
typedData[14].address,
typedData[15].address,
typedData[16].address,
typedData[17].address,
typedData[18].address,
typedData[19].address,
);
Expect.equals(expectedResult, result);
}
void testAddressOfUint32ListView() {
const sourceLength = 30;
const viewStart = 10;
const viewLength = 10;
final viewEnd = viewStart + viewLength;
final source = makeUint32List(sourceLength);
final view = Uint32List.sublistView(source, viewStart, viewEnd);
final expectedResult = makeExpectedResultUint32(viewStart, viewEnd);
final result = takeUint32Pointer(view.address, view.length);
Expect.equals(expectedResult, result);
}
void testAddressOfUint32ListViewMany() {
const length = 20;
final typedData = makeUint32List(length);
final expectedResult = makeExpectedResultUint32(0, length);
final result = takeUint32PointerMany(
Uint32List.sublistView(typedData, 0, 0 + 1).address,
Uint32List.sublistView(typedData, 1, 1 + 1).address,
Uint32List.sublistView(typedData, 2, 2 + 1).address,
Uint32List.sublistView(typedData, 3, 3 + 1).address,
Uint32List.sublistView(typedData, 4, 4 + 1).address,
Uint32List.sublistView(typedData, 5, 5 + 1).address,
Uint32List.sublistView(typedData, 6, 6 + 1).address,
Uint32List.sublistView(typedData, 7, 7 + 1).address,
Uint32List.sublistView(typedData, 8, 8 + 1).address,
Uint32List.sublistView(typedData, 9, 9 + 1).address,
Uint32List.sublistView(typedData, 10, 10 + 1).address,
Uint32List.sublistView(typedData, 11, 11 + 1).address,
Uint32List.sublistView(typedData, 12, 12 + 1).address,
Uint32List.sublistView(typedData, 13, 13 + 1).address,
Uint32List.sublistView(typedData, 14, 14 + 1).address,
Uint32List.sublistView(typedData, 15, 15 + 1).address,
Uint32List.sublistView(typedData, 16, 16 + 1).address,
Uint32List.sublistView(typedData, 17, 17 + 1).address,
Uint32List.sublistView(typedData, 18, 18 + 1).address,
Uint32List.sublistView(typedData, 19, 19 + 1).address,
);
Expect.equals(expectedResult, result);
}
void testAddressOfUint64List() {
const length = 20;
final typedData = makeUint64List(length);
final expectedResult = makeExpectedResultUint64(0, length);
final result = takeUint64Pointer(typedData.address, length);
Expect.equals(expectedResult, result);
}
void testAddressOfUint64ListElementAt() {
const length = 20;
final typedData = makeUint64List(length);
final expectedResult = makeExpectedResultUint64(0, length);
final result = takeUint64PointerMany(
typedData[0].address,
typedData[1].address,
typedData[2].address,
typedData[3].address,
typedData[4].address,
typedData[5].address,
typedData[6].address,
typedData[7].address,
typedData[8].address,
typedData[9].address,
typedData[10].address,
typedData[11].address,
typedData[12].address,
typedData[13].address,
typedData[14].address,
typedData[15].address,
typedData[16].address,
typedData[17].address,
typedData[18].address,
typedData[19].address,
);
Expect.equals(expectedResult, result);
}
void testAddressOfUint64ListView() {
const sourceLength = 30;
const viewStart = 10;
const viewLength = 10;
final viewEnd = viewStart + viewLength;
final source = makeUint64List(sourceLength);
final view = Uint64List.sublistView(source, viewStart, viewEnd);
final expectedResult = makeExpectedResultUint64(viewStart, viewEnd);
final result = takeUint64Pointer(view.address, view.length);
Expect.equals(expectedResult, result);
}
void testAddressOfUint64ListViewMany() {
const length = 20;
final typedData = makeUint64List(length);
final expectedResult = makeExpectedResultUint64(0, length);
final result = takeUint64PointerMany(
Uint64List.sublistView(typedData, 0, 0 + 1).address,
Uint64List.sublistView(typedData, 1, 1 + 1).address,
Uint64List.sublistView(typedData, 2, 2 + 1).address,
Uint64List.sublistView(typedData, 3, 3 + 1).address,
Uint64List.sublistView(typedData, 4, 4 + 1).address,
Uint64List.sublistView(typedData, 5, 5 + 1).address,
Uint64List.sublistView(typedData, 6, 6 + 1).address,
Uint64List.sublistView(typedData, 7, 7 + 1).address,
Uint64List.sublistView(typedData, 8, 8 + 1).address,
Uint64List.sublistView(typedData, 9, 9 + 1).address,
Uint64List.sublistView(typedData, 10, 10 + 1).address,
Uint64List.sublistView(typedData, 11, 11 + 1).address,
Uint64List.sublistView(typedData, 12, 12 + 1).address,
Uint64List.sublistView(typedData, 13, 13 + 1).address,
Uint64List.sublistView(typedData, 14, 14 + 1).address,
Uint64List.sublistView(typedData, 15, 15 + 1).address,
Uint64List.sublistView(typedData, 16, 16 + 1).address,
Uint64List.sublistView(typedData, 17, 17 + 1).address,
Uint64List.sublistView(typedData, 18, 18 + 1).address,
Uint64List.sublistView(typedData, 19, 19 + 1).address,
);
Expect.equals(expectedResult, result);
}
void testAddressOfFloat32List() {
const length = 20;
final typedData = makeFloat32List(length);
final expectedResult = makeExpectedResultFloat(0, length);
final result = takeFloatPointer(typedData.address, length);
Expect.approxEquals(expectedResult, result);
}
void testAddressOfFloat32ListElementAt() {
const length = 20;
final typedData = makeFloat32List(length);
final expectedResult = makeExpectedResultFloat(0, length);
final result = takeFloatPointerMany(
typedData[0].address,
typedData[1].address,
typedData[2].address,
typedData[3].address,
typedData[4].address,
typedData[5].address,
typedData[6].address,
typedData[7].address,
typedData[8].address,
typedData[9].address,
typedData[10].address,
typedData[11].address,
typedData[12].address,
typedData[13].address,
typedData[14].address,
typedData[15].address,
typedData[16].address,
typedData[17].address,
typedData[18].address,
typedData[19].address,
);
Expect.approxEquals(expectedResult, result);
}
void testAddressOfFloat32ListView() {
const sourceLength = 30;
const viewStart = 10;
const viewLength = 10;
final viewEnd = viewStart + viewLength;
final source = makeFloat32List(sourceLength);
final view = Float32List.sublistView(source, viewStart, viewEnd);
final expectedResult = makeExpectedResultFloat(viewStart, viewEnd);
final result = takeFloatPointer(view.address, view.length);
Expect.approxEquals(expectedResult, result);
}
void testAddressOfFloat32ListViewMany() {
const length = 20;
final typedData = makeFloat32List(length);
final expectedResult = makeExpectedResultFloat(0, length);
final result = takeFloatPointerMany(
Float32List.sublistView(typedData, 0, 0 + 1).address,
Float32List.sublistView(typedData, 1, 1 + 1).address,
Float32List.sublistView(typedData, 2, 2 + 1).address,
Float32List.sublistView(typedData, 3, 3 + 1).address,
Float32List.sublistView(typedData, 4, 4 + 1).address,
Float32List.sublistView(typedData, 5, 5 + 1).address,
Float32List.sublistView(typedData, 6, 6 + 1).address,
Float32List.sublistView(typedData, 7, 7 + 1).address,
Float32List.sublistView(typedData, 8, 8 + 1).address,
Float32List.sublistView(typedData, 9, 9 + 1).address,
Float32List.sublistView(typedData, 10, 10 + 1).address,
Float32List.sublistView(typedData, 11, 11 + 1).address,
Float32List.sublistView(typedData, 12, 12 + 1).address,
Float32List.sublistView(typedData, 13, 13 + 1).address,
Float32List.sublistView(typedData, 14, 14 + 1).address,
Float32List.sublistView(typedData, 15, 15 + 1).address,
Float32List.sublistView(typedData, 16, 16 + 1).address,
Float32List.sublistView(typedData, 17, 17 + 1).address,
Float32List.sublistView(typedData, 18, 18 + 1).address,
Float32List.sublistView(typedData, 19, 19 + 1).address,
);
Expect.approxEquals(expectedResult, result);
}
void testAddressOfFloat64List() {
const length = 20;
final typedData = makeFloat64List(length);
final expectedResult = makeExpectedResultDouble(0, length);
final result = takeDoublePointer(typedData.address, length);
Expect.approxEquals(expectedResult, result);
}
void testAddressOfFloat64ListElementAt() {
const length = 20;
final typedData = makeFloat64List(length);
final expectedResult = makeExpectedResultDouble(0, length);
final result = takeDoublePointerMany(
typedData[0].address,
typedData[1].address,
typedData[2].address,
typedData[3].address,
typedData[4].address,
typedData[5].address,
typedData[6].address,
typedData[7].address,
typedData[8].address,
typedData[9].address,
typedData[10].address,
typedData[11].address,
typedData[12].address,
typedData[13].address,
typedData[14].address,
typedData[15].address,
typedData[16].address,
typedData[17].address,
typedData[18].address,
typedData[19].address,
);
Expect.approxEquals(expectedResult, result);
}
void testAddressOfFloat64ListView() {
const sourceLength = 30;
const viewStart = 10;
const viewLength = 10;
final viewEnd = viewStart + viewLength;
final source = makeFloat64List(sourceLength);
final view = Float64List.sublistView(source, viewStart, viewEnd);
final expectedResult = makeExpectedResultDouble(viewStart, viewEnd);
final result = takeDoublePointer(view.address, view.length);
Expect.approxEquals(expectedResult, result);
}
void testAddressOfFloat64ListViewMany() {
const length = 20;
final typedData = makeFloat64List(length);
final expectedResult = makeExpectedResultDouble(0, length);
final result = takeDoublePointerMany(
Float64List.sublistView(typedData, 0, 0 + 1).address,
Float64List.sublistView(typedData, 1, 1 + 1).address,
Float64List.sublistView(typedData, 2, 2 + 1).address,
Float64List.sublistView(typedData, 3, 3 + 1).address,
Float64List.sublistView(typedData, 4, 4 + 1).address,
Float64List.sublistView(typedData, 5, 5 + 1).address,
Float64List.sublistView(typedData, 6, 6 + 1).address,
Float64List.sublistView(typedData, 7, 7 + 1).address,
Float64List.sublistView(typedData, 8, 8 + 1).address,
Float64List.sublistView(typedData, 9, 9 + 1).address,
Float64List.sublistView(typedData, 10, 10 + 1).address,
Float64List.sublistView(typedData, 11, 11 + 1).address,
Float64List.sublistView(typedData, 12, 12 + 1).address,
Float64List.sublistView(typedData, 13, 13 + 1).address,
Float64List.sublistView(typedData, 14, 14 + 1).address,
Float64List.sublistView(typedData, 15, 15 + 1).address,
Float64List.sublistView(typedData, 16, 16 + 1).address,
Float64List.sublistView(typedData, 17, 17 + 1).address,
Float64List.sublistView(typedData, 18, 18 + 1).address,
Float64List.sublistView(typedData, 19, 19 + 1).address,
);
Expect.approxEquals(expectedResult, result);
}

View file

@ -0,0 +1,517 @@
// 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.
import 'dart:io';
import 'c_types.dart';
import 'utils.dart';
import 'structs_by_value_tests_generator.dart';
void main() async {
await Future.wait([
writeC(),
for (final container in Container.values) writeDart(container),
writeDartShared()
]);
}
class Container {
/// The name of the container in Dart code.
final String name;
/// The copyright year for the generated test file for this container.
final int copyrightYear;
/// How this containers' name shows up in tests.
final String Function(PointerType) testName;
final Iterable<Test> tests;
final Iterable<FundamentalType> elementTypes;
const Container(
this.name,
this.copyrightYear,
this.testName,
this.tests,
this.elementTypes,
);
static final array = Container(
'Array',
2024,
(pointerType) => '${pointerType.pointerTo}Array',
[
Test.self,
Test.elementAt,
],
[
int8,
int16,
int32,
int64,
uint8,
uint16,
uint32,
uint64,
float,
double_,
bool_,
],
);
static final struct = Container(
'Struct',
2024,
(pointerType) => '${pointerType.pointerTo}Struct',
[
Test.field,
],
[
int8,
int16,
int32,
int64,
uint8,
uint16,
uint32,
uint64,
float,
double_,
bool_,
],
);
static final typedData = Container(
'TypedData',
2024,
(pointerType) => pointerType.dartTypedData,
[
Test.self,
Test.elementAt,
Test.view,
Test.viewMany,
],
[
int8,
int16,
int32,
int64,
uint8,
uint16,
uint32,
uint64,
float,
double_,
],
);
static final values = [
array,
struct,
typedData,
];
}
class Test {
final String name;
const Test(this.name);
String testName(Container container, CType elementType) {
final containerName = container.testName(PointerType(elementType));
return 'testAddressOf$containerName$name';
}
static const elementAt = Test('ElementAt');
static const field = Test('Field');
static const self = Test('');
static const view = Test('View');
static const viewMany = Test('ViewMany');
}
const generatorPath = 'tests/ffi/generator/address_of_test_generator.dart';
Future<void> writeDart(Container container) async {
final StringBuffer buffer = StringBuffer();
buffer.write(headerDart(
copyrightYear: container.copyrightYear,
));
buffer.write("""
void main() {${'''
// Force dlopen so @Native lookups in DynamicLibrary.process() succeed.
dlopenGlobalPlatformSpecific('ffi_test_functions');
'''}
for (int i = 0; i < 100; ++i) {
""");
for (final elementType in container.elementTypes) {
for (final test in container.tests) {
buffer.write("""
${test.testName(container, elementType)}();
""");
}
}
if (container == Container.struct) {
buffer.write('''
testAddressOfStructPointerMany();
''');
}
buffer.write("""
}
}
""");
for (final elementType in container.elementTypes) {
final pointerType = PointerType(elementType);
final testName = container.testName(pointerType);
final varName = container.name.lowerCaseFirst();
String value = elementType.isSigned ? 'i % 2 == 0 ? i : -i' : 'i';
if (elementType.isFloatingPoint) {
value = '($value).toDouble()';
}
final equals = elementType.isFloatingPoint ? 'approxEquals' : 'equals';
if (container == Container.array) {
buffer.write("""
final class ${elementType.dartCType}ArrayStruct extends Struct {
@Array(20)
external Array<${elementType.dartCType}> array;
}
Array<${elementType.dartCType}> make${elementType.dartCType}Array(int length){
assert(length == 20);
final typedData = make${pointerType.dartTypedData}(length);
final struct = Struct.create<${elementType.dartCType}ArrayStruct>(typedData);
return struct.array;
}
""");
}
if (container == Container.struct) {
buffer.write("""
final class ${elementType.dartCType}Struct extends Struct {
${[
for (int i = 0; i < manyCount; i++)
'''
@${elementType.dartCType}()
external ${elementType.dartType} a$i;
'''
].join()}
}
${elementType.dartCType}Struct make${elementType.dartCType}Struct(int length){
assert(length == 20);
final typedData = make${pointerType.dartTypedData}(length);
final struct = Struct.create<${elementType.dartCType}Struct>(typedData);
return struct;
}
""");
}
for (final test in container.tests) {
final methodName = test.testName(container, elementType);
switch (test) {
case Test.self:
buffer.write("""
void $methodName() {
const length = 20;
final $varName = make${testName}(length);
final expectedResult = makeExpectedResult${elementType.dartCType}(0, length);
final result = take${elementType.dartCType}Pointer($varName.address, length);
Expect.$equals(expectedResult, result);
}
""");
case Test.elementAt:
buffer.write("""
void $methodName() {
const length = $manyCount;
final $varName = make${testName}(length);
final expectedResult = makeExpectedResult${elementType.dartCType}(0, length);
final result = take${elementType.dartCType}PointerMany(${[
for (int i = 0; i < manyCount; i++) '$varName[$i].address,'
].join()});
Expect.$equals(expectedResult, result);
}
""");
case Test.view:
buffer.write("""
void $methodName() {
const sourceLength = 30;
const viewStart = 10;
const viewLength = 10;
final viewEnd = viewStart + viewLength;
final source = make${pointerType.dartTypedData}(sourceLength);
final view = ${pointerType.dartTypedData}.sublistView(source, viewStart, viewEnd);
final expectedResult = makeExpectedResult${elementType.dartCType}(viewStart, viewEnd);
final result = take${elementType.dartCType}Pointer(view.address, view.length);
Expect.$equals(expectedResult, result);
}
""");
case Test.viewMany:
buffer.write("""
void $methodName() {
const length = $manyCount;
final typedData = make${pointerType.dartTypedData}(length);
final expectedResult = makeExpectedResult${elementType.dartCType}(0, length);
final result = take${elementType.dartCType}PointerMany(${[
for (int i = 0; i < manyCount; i++)
'${pointerType.dartTypedData}.sublistView(typedData, $i, $i + 1).address,'
].join()});
Expect.$equals(expectedResult, result);
}
""");
case Test.field:
buffer.write("""
void $methodName() {
const length = $manyCount;
final $varName = make${testName}(length);
final expectedResult = makeExpectedResult${elementType.dartCType}(0, length);
final result = take${elementType.dartCType}PointerMany(${[
for (int i = 0; i < manyCount; i++) '$varName.a$i.address,'
].join()});
Expect.$equals(expectedResult, result);
}
""");
}
}
}
if (container == Container.struct) {
final elementType = int16;
for (final compoundType in [
StructType([elementType]),
UnionType([elementType])
]) {
final compoundKind = compoundType is StructType ? 'Struct' : 'Union';
final pointerType = PointerType(compoundType);
buffer.write(compoundType.dartClass());
buffer.write('''
@Native<${elementType.dartCType} Function(
${[for (int i = 0; i < manyCount; i++) '${pointerType.dartCType},'].join()}
)>(symbol: 'Take${compoundKind}2BytesIntPointerMany', isLeaf: true)
external ${elementType.dartType} take${compoundKind}2BytesIntPointerMany(
${[
for (int i = 0; i < manyCount; i++)
'${pointerType.dartCType} pointer$i,',
].join()}
);
void testAddressOf${compoundKind}PointerMany() {
const length = $manyCount;
final typedData = makeInt16List(length);
${[
for (int i = 0; i < manyCount; i++)
'final struct$i = ${compoundKind}.create<${compoundKind}2BytesInt>(typedData, $i);'
].join()}
final expectedResult = makeExpectedResult${elementType.dartCType}(0, length);
final result = take${compoundKind}2BytesIntPointerMany(${[
for (int i = 0; i < manyCount; i++) 'struct$i.address,'
].join()});
Expect.equals(expectedResult, result);
}
''');
}
}
final path = testPath(container);
await File(path).writeAsString(buffer.toString());
await runProcess(Platform.resolvedExecutable, ["format", path]);
}
Future<void> writeDartShared() async {
final StringBuffer buffer = StringBuffer();
buffer.write(headerDart(
copyrightYear: 2024,
));
for (final elementType in Container.struct.elementTypes) {
final pointerType = PointerType(elementType);
String value = elementType.isSigned ? 'i % 2 == 0 ? i : -i' : 'i';
if (elementType.isFloatingPoint) {
value = '($value).toDouble()';
}
buffer.write("""
@Native<${elementType.dartCType} Function(${pointerType.dartCType}, Size)>(symbol: 'Take${elementType.dartCType}Pointer', isLeaf: true)
external ${elementType.dartType} take${elementType.dartCType}Pointer(${pointerType.dartCType} pointer, int length);
@Native<${elementType.dartCType} Function(
${[for (int i = 0; i < manyCount; i++) '${pointerType.dartCType},'].join()}
)>(symbol: 'Take${elementType.dartCType}PointerMany', isLeaf: true)
external ${elementType.dartType} take${elementType.dartCType}PointerMany(
${[
for (int i = 0; i < manyCount; i++) '${pointerType.dartCType} pointer$i,',
].join()}
);
""");
if (elementType != bool_) {
buffer.write("""
${pointerType.dartTypedData} make${pointerType.dartTypedData}(int length) {
final typedData = ${pointerType.dartTypedData}(length);
for (int i = 0; i < length; i++) {
final value = $value;
typedData[i] = value;
}
return typedData;
}
${elementType.dartType} makeExpectedResult${elementType.dartCType}(int start, int end) {
${elementType.dartType} expectedResult = 0;
for (int i = start; i < end; i++) {
final value = $value;
expectedResult += value;
}
return expectedResult;
}
""");
}
}
final path = Platform.script
.resolve("../../ffi/address_of_generated_shared.dart")
.toFilePath();
;
await File(path).writeAsString(buffer.toString());
await runProcess(Platform.resolvedExecutable, ["format", path]);
}
String testPath(Container container) {
final lowerCase = container.name.toLowerCase();
return Platform.script
.resolve("../../ffi/address_of_${lowerCase}_generated_test.dart")
.toFilePath();
}
String headerDart({
required int copyrightYear,
}) {
return """
${headerCommon(copyrightYear: copyrightYear, generatorPath: generatorPath)}
//
// SharedObjects=ffi_test_functions
// VMOptions=
// VMOptions=--deterministic --optimization-counter-threshold=90
// VMOptions=--use-slow-path
// VMOptions=--use-slow-path --stacktrace-every=100
// ignore_for_file: unused_import
import 'dart:ffi';
import 'dart:typed_data';
import 'package:expect/expect.dart';
import 'address_of_generated_shared.dart';
import 'address_of_shared.dart';
import 'dylib_utils.dart';
final ffiTestFunctions = dlopenPlatformSpecific('ffi_test_functions');
""";
}
const manyCount = 20;
Future<void> writeC() async {
final StringBuffer buffer = StringBuffer();
buffer.write(headerC(copyrightYear: 2024, generatorPath: generatorPath));
for (final elementType in Container.struct.elementTypes) {
final pointerType = PointerType(elementType);
String coutCast(String input) {
if (elementType == uint8 || elementType == int8 || elementType == bool_) {
return "static_cast<int>($input)";
}
return input;
}
String opCast(String input) {
if (elementType == bool_) {
return "static_cast<int>($input)";
}
return input;
}
buffer.write('''
DART_EXPORT ${elementType.cType} Take${elementType.dartCType}Pointer(${pointerType.cType} data, size_t length) {
${elementType.cType} result = ${elementType.zero};
if (length > 100) {
std::cout << "Mangled arguments\\n";
return result;
}
for (size_t i = 0; i < length; i++) {
std::cout << "data[" << i << "] = " << ${coutCast('data[i]')} << "\\n";
result ${elementType.addAssignOp} ${opCast('data[i]')};
}
return result;
}
DART_EXPORT ${elementType.cType} Take${elementType.dartCType}PointerMany(
${[
for (int i = 0; i < manyCount; i++) '${pointerType.cType} data$i'
].join(',')}
) {
${elementType.cType} result = ${elementType.zero};
${[
for (int i = 0; i < manyCount; i++)
'''
std::cout << "data$i[0] = " << ${coutCast('data$i[0]')} << "\\n";
result ${elementType.addAssignOp} ${opCast('data$i[0]')};''',
].join('\n')}
return result;
}
''');
}
final elementType = int16;
for (final compoundType in [
StructType([elementType]),
UnionType([elementType])
]) {
final pointerType = PointerType(compoundType);
buffer.write(compoundType.cDefinition);
buffer.write('''
DART_EXPORT ${elementType.cType} Take${compoundType.dartCType}PointerMany(
${[
for (int i = 0; i < manyCount; i++) '${pointerType.cType} data$i'
].join(',')}
) {
${elementType.cType} result = ${elementType.zero};
${[
for (int i = 0; i < manyCount; i++)
'''
std::cout << "data$i->a0 = " << ${'data$i->a0'} << "\\n";
result += data$i->a0;''',
].join('\n')}
return result;
}
''');
}
buffer.write(footerC);
await File(ccPath).writeAsString(buffer.toString());
await runProcess("clang-format", ["-i", ccPath]);
}
final ccPath = Platform.script
.resolve("../../../runtime/bin/ffi_test/ffi_test_functions_generated_2.cc")
.toFilePath();

View file

@ -194,6 +194,19 @@ class FundamentalType extends CType {
}
return primitiveSizesInBytes[primitive]!;
}
String get zero {
if (isBool) return 'false';
if (isInteger) return '0';
if (isFloatingPoint) return '0.0';
throw 'Unknown type $primitive';
}
String get addAssignOp {
if (isBool) return '^=';
if (isInteger || isFloatingPoint) return '+=';
throw 'Unknown type $primitive';
}
}
class PointerType extends CType {

View file

@ -502,7 +502,7 @@ extension on List<Member> {
}
}
extension on CompositeType {
extension CompositeTypeGenerator on CompositeType {
String dartClass() {
final self = this;
final packingAnnotation = (self is StructType) && self.hasPacking

View file

@ -1,259 +0,0 @@
// Copyright (c) 2023, 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.
import 'dart:io';
import 'c_types.dart';
import 'utils.dart';
void main() async {
await Future.wait([
writeC(),
for (bool isNative in [true, false]) writeDart(isNative: isNative),
]);
}
final elementTypes = [
int8,
int16,
int32,
int64,
uint8,
uint16,
uint32,
uint64,
float,
double_,
];
const generatorPath =
'tests/ffi/generator/unwrap_typeddata_test_generator.dart';
Future<void> writeDart({
required bool isNative,
}) async {
final StringBuffer buffer = StringBuffer();
buffer.write(headerDart(
copyrightYear: 2023,
));
final forceDlOpen = !isNative
? ''
: '''
// Force dlopen so @Native lookups in DynamicLibrary.process() succeed.
dlopenGlobalPlatformSpecific('ffi_test_functions');
''';
buffer.write("""
void main() {$forceDlOpen
for (int i = 0; i < 100; ++i) {
""");
for (final elementType in elementTypes) {
final pointerType = PointerType(elementType);
buffer.write("""
testUnwrap${pointerType.dartTypedData}();
testUnwrap${pointerType.dartTypedData}View();
testUnwrap${pointerType.dartTypedData}Many();
""");
}
buffer.write("""
}
}
""");
for (final elementType in elementTypes) {
final pointerType = PointerType(elementType);
String value = elementType.isSigned ? 'i % 2 == 0 ? i : -i' : 'i';
if (elementType.isFloatingPoint) {
value = '($value).toDouble()';
}
final equals = elementType.isFloatingPoint ? 'approxEquals' : 'equals';
final manyDartCTypes = Iterable.generate(
many,
(i) => '${pointerType.dartCType}',
).join(',');
final manyArgs = Iterable.generate(
many,
(i) => '${pointerType.dartTypedData} typedData$i',
).join(',');
final manyBodies = Iterable.generate(
many,
(i) =>
'${pointerType.dartTypedData}.view(source.buffer, elementSize * $i, 1)',
).join(',');
if (isNative) {
buffer.write("""
@Native<${elementType.dartCType} Function(${pointerType.dartCType}, Size)>(symbol: 'Unwrap${pointerType.dartTypedData}', isLeaf: true)
external ${elementType.dartType} unwrap${pointerType.dartTypedData}(${pointerType.dartTypedData} typedData, int length);
@Native<${elementType.dartCType} Function($manyDartCTypes,)>(symbol: 'Unwrap${pointerType.dartTypedData}Many', isLeaf: true)
external ${elementType.dartType} unwrap${pointerType.dartTypedData}Many($manyArgs,);
""");
} else {
buffer.write("""
final unwrap${pointerType.dartTypedData} = ffiTestFunctions.lookupFunction<
${elementType.dartCType} Function(${pointerType.dartCType}, Size),
${elementType.dartType} Function(${pointerType.dartTypedData}, int)>('Unwrap${pointerType.dartTypedData}', isLeaf: true);
final unwrap${pointerType.dartTypedData}Many = ffiTestFunctions.lookupFunction<
${elementType.dartCType} Function($manyDartCTypes,),
${elementType.dartType} Function($manyArgs,)>('Unwrap${pointerType.dartTypedData}Many', isLeaf: true);
""");
}
buffer.write("""
void testUnwrap${pointerType.dartTypedData}() {
const length = 10;
final typedData = ${pointerType.dartTypedData}(length);
${elementType.dartType} expectedResult = 0;
for (int i = 0; i < length; i++) {
final value = $value;
typedData[i] = value;
expectedResult += value;
}
final result = unwrap${pointerType.dartTypedData}(typedData, typedData.length);
Expect.$equals(expectedResult, result);
}
void testUnwrap${pointerType.dartTypedData}View() {
const sourceLength = 30;
const elementSize = ${elementType.size};
const viewStart = 10;
const viewOffsetInBytes = viewStart * elementSize;
const viewLength = 10;
final viewEnd = viewStart + viewLength;
final source = ${pointerType.dartTypedData}(sourceLength);
final view = ${pointerType.dartTypedData}.view(source.buffer, viewOffsetInBytes, viewLength);
${elementType.dartType} expectedResult = 0;
for (int i = 0; i < sourceLength; i++) {
final value = $value;
source[i] = value;
if (viewStart <= i && i < viewEnd) {
expectedResult += value;
}
}
final result = unwrap${pointerType.dartTypedData}(view, view.length);
Expect.$equals(expectedResult, result);
}
void testUnwrap${pointerType.dartTypedData}Many() {
const length = 20;
const elementSize = ${elementType.size};
final source = ${pointerType.dartTypedData}(length);
${elementType.dartType} expectedResult = 0;
for (int i = 0; i < length; i++) {
final value = $value;
source[i] = value;
expectedResult += value;
}
final result = unwrap${pointerType.dartTypedData}Many(
$manyBodies,
);
Expect.$equals(expectedResult, result);
}
""");
}
final path = testPath(isNative: isNative);
await File(path).writeAsString(buffer.toString());
await runProcess(Platform.resolvedExecutable, ["format", path]);
}
String testPath({
required bool isNative,
}) {
final suffix = '${isNative ? '_native' : ''}';
return Platform.script
.resolve("../../ffi/unwrap_typeddata_generated${suffix}_test.dart")
.toFilePath();
}
String headerDart({
required int copyrightYear,
}) {
return """
${headerCommon(copyrightYear: copyrightYear, generatorPath: generatorPath)}
//
// SharedObjects=ffi_test_functions
// VMOptions=
// VMOptions=--deterministic --optimization-counter-threshold=90
// VMOptions=--use-slow-path
// VMOptions=--use-slow-path --stacktrace-every=100
import 'dart:ffi';
import 'dart:typed_data';
import 'package:expect/expect.dart';
import 'dylib_utils.dart';
final ffiTestFunctions = dlopenPlatformSpecific('ffi_test_functions');
""";
}
const many = 20;
Future<void> writeC() async {
final StringBuffer buffer = StringBuffer();
buffer.write(headerC(copyrightYear: 2023, generatorPath: generatorPath));
for (final elementType in elementTypes) {
final pointerType = PointerType(elementType);
String coutCast(String input) {
if (elementType == uint8 || elementType == int8) {
return "static_cast<int>($input)";
}
return input;
}
final manyArgs = Iterable.generate(
many,
(i) => '${pointerType.cType} data$i',
).join(',');
final manyBodies = Iterable.generate(
many,
(i) => '''
std::cout << "data$i[0] = " << ${coutCast('data$i[0]')} << "\\n";
result += data$i[0];''',
).join('\n');
buffer.write('''
DART_EXPORT ${elementType.cType} Unwrap${pointerType.dartTypedData}(${pointerType.cType} data, size_t length) {
${elementType.cType} result = 0;
for (size_t i = 0; i < length; i++) {
std::cout << "data[" << i << "] = " << ${coutCast('data[i]')} << "\\n";
result += data[i];
}
return result;
}
DART_EXPORT ${elementType.cType} Unwrap${pointerType.dartTypedData}Many($manyArgs) {
${elementType.cType} result = 0;
$manyBodies
return result;
}
''');
}
buffer.write(footerC);
await File(ccPath).writeAsString(buffer.toString());
await runProcess("clang-format", ["-i", ccPath]);
}
final ccPath = Platform.script
.resolve("../../../runtime/bin/ffi_test/ffi_test_functions_generated_2.cc")
.toFilePath();

View file

@ -0,0 +1,42 @@
// 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() {
testUnsupportedAddress();
}
void testUnsupportedAddress() {
myNative(4.address);
// ^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.ADDRESS_RECEIVER
// [cfe] The receiver of '.address' must be a concrete 'TypedData', a concrete 'TypedData' '[]', an 'Array', an 'Array' '[]', a Struct field, or a Union field.
final myStruct = Struct.create<MyStruct>();
myStruct.address;
// ^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.ADDRESS_POSITION
// [cfe] The '.address' expression can only be used as argument to a leaf native external call.
myNativeNoLeaf(myStruct.a.address);
// ^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.ADDRESS_POSITION
// [cfe] The '.address' expression can only be used as argument to a leaf native external call.
}
@Native<Int8 Function(Pointer<Int8>)>(isLeaf: true)
external int myNative(Pointer<Int8> pointer);
@Native<Int8 Function(Pointer<Int8>)>()
external int myNativeNoLeaf(Pointer<Int8> pointer);
final class MyStruct extends Struct {
@Int8()
external int a;
}