Reland "[vm/ffi] Introduce Struct.create and Union.create"

Analyzer fix https://github.com/dart-lang/sdk/issues/54754 has
landed. A new version of package:analyzer and package:dartdoc have
been released. `pub global activate dartdoc` should now work.

Patchset 1 is identical to the original CL.
The only difference is an extra test testing with negative offsets.

=== Original CL description ===

Structs and unions can now be created from an existing typed data
with the new `create` methods.

The typed data argument to these `create` methods is optional. If
the typed data argument is omitted, a new typed data of the right
size will be allocated.

Compound field reads and writes are unchecked. (These are
TypedDataBase loads and stores, rather than TypedData loads and stores.
And Pointers have no byte length.) Therefore the `create` method taking
existing TypedData objects check whether the length in bytes it at
least the size of the compound.

TEST=pkg/analyzer/test/src/diagnostics/creation_of_struct_or_union_test.dart
TEST=pkg/vm/testcases/transformations/ffi/struct_typed_data.dart
TEST=tests/ffi/structs_typed_data_test.dart
TEST=tests/ffi/vmspecific_static_checks_test.dart

Closes: https://github.com/dart-lang/sdk/issues/45697
Closes: https://github.com/dart-lang/sdk/issues/53418

Change-Id: Id7f30bcd4a6ae55a8298b39c9eadf4e80bc699a9
CoreLibraryReviewExempt: FFI is a VM and WASM only feature.
Cq-Include-Trybots: luci.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-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-arm-try,vm-ffi-android-debug-arm64c-try,vm-ffi-qemu-linux-release-arm-try,vm-ffi-qemu-linux-release-riscv64-try,vm-fuchsia-release-x64-try,vm-kernel-linux-debug-x64-try,vm-kernel-precomp-linux-release-x64-try,vm-linux-debug-ia32-try,vm-linux-debug-x64-try,vm-linux-debug-x64c-try,vm-mac-debug-arm64-try,vm-mac-debug-x64-try,vm-msan-linux-release-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-release-ia32-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/349260
Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
Daco Harkes 2024-02-01 18:28:03 +00:00
parent 0151574bb0
commit bf683bacbb
50 changed files with 944 additions and 56 deletions

View file

@ -219,7 +219,9 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
if (element is MethodElement) {
var enclosingElement = element.enclosingElement;
if (enclosingElement.isNativeStructPointerExtension ||
enclosingElement.isNativeStructArrayExtension) {
enclosingElement.isNativeStructArrayExtension ||
enclosingElement.isNativeUnionPointerExtension ||
enclosingElement.isNativeUnionArrayExtension) {
if (element.name == '[]') {
_validateRefIndexed(node);
}
@ -232,10 +234,12 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
var constructor = node.constructorName.staticElement;
var class_ = constructor?.enclosingElement;
if (class_.isStructSubclass || class_.isUnionSubclass) {
_errorReporter.reportErrorForNode(
FfiCode.CREATION_OF_STRUCT_OR_UNION,
node.constructorName,
);
if (!constructor!.isFactory) {
_errorReporter.reportErrorForNode(
FfiCode.CREATION_OF_STRUCT_OR_UNION,
node.constructorName,
);
}
} else if (class_.isNativeCallable) {
_validateNativeCallable(node);
}
@ -289,6 +293,10 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
} else if (element.name == 'elementAt') {
_validateElementAt(node);
}
} else if (enclosingElement.isStruct || enclosingElement.isUnion) {
if (element.name == 'create') {
_validateCreate(node, enclosingElement.name!);
}
} else if (enclosingElement.isNative) {
if (element.name == 'addressOf') {
_validateNativeAddressOf(node);
@ -320,7 +328,8 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
var element = node.staticElement;
if (element != null) {
var enclosingElement = element.enclosingElement;
if (enclosingElement.isNativeStructPointerExtension) {
if (enclosingElement.isNativeStructPointerExtension ||
enclosingElement.isNativeUnionPointerExtension) {
if (element.name == 'ref') {
_validateRefPrefixedIdentifier(node);
}
@ -334,7 +343,8 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
var element = node.propertyName.staticElement;
if (element != null) {
var enclosingElement = element.enclosingElement;
if (enclosingElement.isNativeStructPointerExtension) {
if (enclosingElement.isNativeStructPointerExtension ||
enclosingElement.isNativeUnionPointerExtension) {
if (element.name == 'ref') {
_validateRefPropertyAccess(node);
}
@ -1148,6 +1158,22 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
}
}
void _validateCreate(MethodInvocation node, String errorClass) {
final typeArgumentTypes = node.typeArgumentTypes;
if (typeArgumentTypes == null || typeArgumentTypes.length != 1) {
return;
}
final DartType dartType = typeArgumentTypes[0];
if (!_isValidFfiNativeType(dartType)) {
final AstNode errorNode = node;
_errorReporter.reportErrorForNode(
FfiCode.NON_CONSTANT_TYPE_ARGUMENT,
errorNode,
['$errorClass.create'],
);
}
}
void _validateElementAt(MethodInvocation node) {
var targetType = node.realTarget?.staticType;
if (targetType is InterfaceType && targetType.isPointer) {
@ -1856,6 +1882,20 @@ extension on Element? {
element.isFfiExtension;
}
bool get isNativeUnionArrayExtension {
final element = this;
return element is ExtensionElement &&
element.name == 'UnionArray' &&
element.isFfiExtension;
}
bool get isNativeUnionPointerExtension {
final element = this;
return element is ExtensionElement &&
element.name == 'UnionPointer' &&
element.isFfiExtension;
}
/// Return `true` if this represents the class `Opaque`.
bool get isOpaque {
final element = this;

View file

@ -9,6 +9,7 @@ library /*isLegacy*/;
import self as self;
import "dart:core" as core;
import "dart:ffi" as ffi;
import "dart:typed_data" as typ;
import "dart:ffi";
import "package:ffi/ffi.dart";
@ -18,6 +19,9 @@ class Coordinate extends ffi::Struct {
constructor #fromTypedDataBase(synthesized core::Object #typedDataBase) → self::Coordinate
: super ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized typ::TypedData #typedData, synthesized core::int #offset, synthesized core::int #sizeInBytes) → self::Coordinate
: super ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
static factory allocate(ffi::Allocator* allocator, core::double* x, core::double* y, ffi::Pointer<self::Coordinate*>* next) → self::Coordinate* {
return let final self::Coordinate* #t1 = new self::Coordinate::#fromTypedDataBase(allocator.{ffi::Allocator::allocate}<self::Coordinate*>(self::Coordinate::#sizeOf){(core::int, {alignment: core::int?}) → ffi::Pointer<self::Coordinate*>}!) in block {
#t1.{self::Coordinate::x} = x;

View file

@ -2,6 +2,7 @@ library /*isLegacy*/;
import self as self;
import "dart:core" as core;
import "dart:ffi" as ffi;
import "dart:typed_data" as typ;
import "dart:ffi";
import "package:ffi/ffi.dart";
@ -11,6 +12,9 @@ class Coordinate extends ffi::Struct {
constructor #fromTypedDataBase(synthesized core::Object #typedDataBase) → self::Coordinate
: super ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized typ::TypedData #typedData, synthesized core::int #offset, synthesized core::int #sizeInBytes) → self::Coordinate
: super ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
static factory allocate(ffi::Allocator* allocator, core::double* x, core::double* y, ffi::Pointer<self::Coordinate*>* next) → self::Coordinate* {
return let final self::Coordinate* #t1 = new self::Coordinate::#fromTypedDataBase(allocator.{ffi::Allocator::allocate}<self::Coordinate*>(self::Coordinate::#sizeOf){(core::int, {alignment: core::int?}) → ffi::Pointer<self::Coordinate*>}!) in block {
#t1.{self::Coordinate::x} = x;

View file

@ -11,6 +11,9 @@ library from "org-dartlang-test:///lib.dart" as lib {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → lib::Y
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → lib::Y
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C7
get yy() → dart.core::int
return dart.ffi::_loadUint32(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C9.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});
@ -35,6 +38,9 @@ library from "org-dartlang-test:///main.dart" as main {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → main::X
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → main::X
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
get xx() → lib::Y
return new lib::Y::#fromTypedDataBase( block {
synthesized dart.core::Object #typedDataBase = this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object};

View file

@ -11,6 +11,9 @@ library from "org-dartlang-test:///lib.dart" as lib {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → lib::Y
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → lib::Y
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C7
get yy() → dart.core::int
return dart.ffi::_loadUint32(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C9.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});
@ -35,6 +38,9 @@ library from "org-dartlang-test:///main.dart" as main {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → main::X
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → main::X
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
get xx() → lib::Y
return new lib::Y::#fromTypedDataBase( block {
synthesized dart.core::Object #typedDataBase = this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object};

View file

@ -29,6 +29,9 @@ library from "org-dartlang-test:///structs.dart" as str {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → str::A
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → str::A
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
get yy() → str::Y
return new str::Y::#fromTypedDataBase( block {
synthesized dart.core::Object #typedDataBase = this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object};
@ -47,6 +50,9 @@ library from "org-dartlang-test:///structs.dart" as str {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → str::Y
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → str::Y
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
external get zz() → invalid-type;
external set zz(synthesized invalid-type #externalFieldValue) → void;
@#C10

View file

@ -29,6 +29,9 @@ library from "org-dartlang-test:///structs.dart" as str {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → str::A
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → str::A
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
get yy() → str::Y
return new str::Y::#fromTypedDataBase( block {
synthesized dart.core::Object #typedDataBase = this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object};
@ -47,6 +50,9 @@ library from "org-dartlang-test:///structs.dart" as str {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → str::Y
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → str::Y
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
external get zz() → invalid-type;
external set zz(synthesized invalid-type #externalFieldValue) → void;
@#C10

View file

@ -8,6 +8,9 @@ library from "org-dartlang-test:///lib.dart" as lib {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → lib::Coordinate
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → lib::Coordinate
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C8
get x() → dart.core::double
return dart.ffi::_loadDouble(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C10.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});

View file

@ -8,6 +8,9 @@ library from "org-dartlang-test:///lib.dart" as lib {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → lib::Coordinate
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → lib::Coordinate
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C8
get x() → dart.core::double
return dart.ffi::_loadDouble(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C10.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});

View file

@ -8,6 +8,9 @@ library from "org-dartlang-test:///lib.dart" as lib {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → lib::Coordinate
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → lib::Coordinate
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C8
get x() → dart.core::double
return dart.ffi::_loadDouble(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C10.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});

View file

@ -11,6 +11,9 @@ library from "org-dartlang-test:///a.dart" as a {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → a::StructA
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → a::StructA
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
get a1() → dart.ffi::Pointer<dart.ffi::Void>
return dart.ffi::_loadPointer<dart.ffi::Void>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C9.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});
set a1(synthesized dart.ffi::Pointer<dart.ffi::Void> #externalFieldValue) → void
@ -42,6 +45,9 @@ library from "org-dartlang-test:///a.dart" as a {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → a::NestedStruct
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → a::NestedStruct
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
get n1() → dart.ffi::Pointer<dart.ffi::Void>
return dart.ffi::_loadPointer<dart.ffi::Void>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C9.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});
set n1(synthesized dart.ffi::Pointer<dart.ffi::Void> #externalFieldValue) → void
@ -74,6 +80,9 @@ library from "org-dartlang-test:///b.dart" as b {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → b::StructB
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → b::StructB
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
get b1() → a::StructA
return new a::StructA::#fromTypedDataBase( block {
synthesized dart.core::Object #typedDataBase = this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object};

View file

@ -11,6 +11,9 @@ library from "org-dartlang-test:///a.dart" as a {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → a::StructA
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → a::StructA
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
get a1() → dart.ffi::Pointer<dart.ffi::Void>
return dart.ffi::_loadPointer<dart.ffi::Void>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C9.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});
set a1(synthesized dart.ffi::Pointer<dart.ffi::Void> #externalFieldValue) → void
@ -42,6 +45,9 @@ library from "org-dartlang-test:///a.dart" as a {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → a::NestedStruct
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → a::NestedStruct
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
get n1() → dart.ffi::Pointer<dart.ffi::Void>
return dart.ffi::_loadPointer<dart.ffi::Void>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C9.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});
set n1(synthesized dart.ffi::Pointer<dart.ffi::Void> #externalFieldValue) → void
@ -74,6 +80,9 @@ library from "org-dartlang-test:///b.dart" as b {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → b::StructB
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → b::StructB
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
get b1() → a::StructA
return new a::StructA::#fromTypedDataBase( block {
synthesized dart.core::Object #typedDataBase = this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object};

View file

@ -8,6 +8,9 @@ library from "org-dartlang-test:///lib.dart" as lib {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → lib::Coordinate
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → lib::Coordinate
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C8
get x() → dart.core::double
return dart.ffi::_loadDouble(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C10.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});

View file

@ -8,6 +8,9 @@ library from "org-dartlang-test:///lib.dart" as lib {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → lib::Coordinate
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → lib::Coordinate
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C8
get x() → dart.core::double
return dart.ffi::_loadDouble(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C10.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});

View file

@ -8,6 +8,9 @@ library from "org-dartlang-test:///lib.dart" as lib {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → lib::Coordinate
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → lib::Coordinate
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C8
get x() → dart.core::double
return dart.ffi::_loadDouble(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C10.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});

View file

@ -11,6 +11,9 @@ library from "org-dartlang-test:///lib.dart" as lib {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → lib::Y
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → lib::Y
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C8
get y1() → dart.core::int
return dart.ffi::_loadUint8(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C10.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});
@ -47,6 +50,9 @@ library from "org-dartlang-test:///main.dart" as main {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → main::X
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → main::X
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
get x1() → lib::Y
return new lib::Y::#fromTypedDataBase( block {
synthesized dart.core::Object #typedDataBase = this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object};

View file

@ -11,6 +11,9 @@ library from "org-dartlang-test:///lib.dart" as lib {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → lib::Y
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → lib::Y
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C8
get y1() → dart.core::int
return dart.ffi::_loadUint8(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C10.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});
@ -47,6 +50,9 @@ library from "org-dartlang-test:///main.dart" as main {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → main::X
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → main::X
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
get x1() → lib::Y
return new lib::Y::#fromTypedDataBase( block {
synthesized dart.core::Object #typedDataBase = this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object};

View file

@ -11,6 +11,9 @@ library from "org-dartlang-test:///lib.dart" as lib {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → lib::Y
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → lib::Y
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C8
get y1() → dart.core::int
return dart.ffi::_loadUint8(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C10.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});
@ -47,6 +50,9 @@ library from "org-dartlang-test:///main.dart" as main {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → main::X
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → main::X
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
get x1() → lib::Y
return new lib::Y::#fromTypedDataBase( block {
synthesized dart.core::Object #typedDataBase = this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object};

View file

@ -11,6 +11,9 @@ library from "org-dartlang-test:///lib.dart" as lib {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → lib::Y
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → lib::Y
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C8
get y1() → dart.core::int
return dart.ffi::_loadUint8(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C10.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});
@ -47,6 +50,9 @@ library from "org-dartlang-test:///main.dart" as main {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → main::X
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → main::X
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
get x1() → lib::Y
return new lib::Y::#fromTypedDataBase( block {
synthesized dart.core::Object #typedDataBase = this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object};

View file

@ -11,6 +11,9 @@ library from "org-dartlang-test:///lib.dart" as lib {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → lib::Y
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → lib::Y
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C8
get y1() → dart.core::int
return dart.ffi::_loadUint8(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C10.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});
@ -237,6 +240,9 @@ library from "org-dartlang-test:///main.dart" as main {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → main::X
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → main::X
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
get x1() → lib::Y
return new lib::Y::#fromTypedDataBase( block {
synthesized dart.core::Object #typedDataBase = this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object};

View file

@ -11,6 +11,9 @@ library from "org-dartlang-test:///lib.dart" as lib {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → lib::Y
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → lib::Y
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C8
get y1() → dart.core::int
return dart.ffi::_loadUint8(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C10.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});
@ -237,6 +240,9 @@ library from "org-dartlang-test:///main.dart" as main {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → main::X
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → main::X
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
get x1() → lib::Y
return new lib::Y::#fromTypedDataBase( block {
synthesized dart.core::Object #typedDataBase = this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object};

View file

@ -11,6 +11,9 @@ library from "org-dartlang-test:///lib.dart" as lib {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → lib::COMObject
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → lib::COMObject
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
get lpVtbl() → dart.ffi::Pointer<dart.ffi::IntPtr>
return dart.ffi::_loadPointer<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C8.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});
set lpVtbl(synthesized dart.ffi::Pointer<dart.ffi::IntPtr> #externalFieldValue) → void
@ -35,6 +38,9 @@ library from "org-dartlang-test:///main.dart" as main {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → main::X
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → main::X
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
get xx() → lib::COMObject
return new lib::COMObject::#fromTypedDataBase( block {
synthesized dart.core::Object #typedDataBase = this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object};

View file

@ -11,6 +11,9 @@ library from "org-dartlang-test:///lib.dart" as lib {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → lib::COMObject
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → lib::COMObject
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
get lpVtbl() → dart.ffi::Pointer<dart.ffi::IntPtr>
return dart.ffi::_loadPointer<dart.ffi::IntPtr>(this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object}, #C8.{dart.core::List::[]}(dart.ffi::_abi()){(dart.core::int) → dart.core::int*});
set lpVtbl(synthesized dart.ffi::Pointer<dart.ffi::IntPtr> #externalFieldValue) → void
@ -35,6 +38,9 @@ library from "org-dartlang-test:///main.dart" as main {
constructor #fromTypedDataBase(synthesized dart.core::Object #typedDataBase) → main::X
: super dart.ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized dart.typed_data::TypedData #typedData, synthesized dart.core::int #offset, synthesized dart.core::int #sizeInBytes) → main::X
: super dart.ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
get xx() → lib::COMObject
return new lib::COMObject::#fromTypedDataBase( block {
synthesized dart.core::Object #typedDataBase = this.{dart.ffi::_Compound::_typedDataBase}{dart.core::Object};

View file

@ -2,6 +2,7 @@ library;
import self as self;
import "dart:core" as core;
import "dart:ffi" as ffi;
import "dart:typed_data" as typ;
import "dart:ffi";
import "package:ffi/ffi.dart";
@ -11,6 +12,9 @@ final class Coordinate extends ffi::Struct {
constructor #fromTypedDataBase(synthesized core::Object #typedDataBase) → self::Coordinate
: super ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized typ::TypedData #typedData, synthesized core::int #offset, synthesized core::int #sizeInBytes) → self::Coordinate
: super ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C8
get x() → core::double
return ffi::_loadDouble(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C10.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});

View file

@ -2,6 +2,7 @@ library;
import self as self;
import "dart:core" as core;
import "dart:ffi" as ffi;
import "dart:typed_data" as typ;
import "dart:ffi";
import "package:ffi/ffi.dart";
@ -11,6 +12,9 @@ final class Coordinate extends ffi::Struct {
constructor #fromTypedDataBase(synthesized core::Object #typedDataBase) → self::Coordinate
: super ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized typ::TypedData #typedData, synthesized core::int #offset, synthesized core::int #sizeInBytes) → self::Coordinate
: super ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C8
get x() → core::double
return ffi::_loadDouble(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C10.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});

View file

@ -16,6 +16,9 @@ final class StructInlineArray extends ffi::Struct {
constructor #fromTypedDataBase(synthesized core::Object #typedDataBase) → self::StructInlineArray
: super ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized typ::TypedData #typedData, synthesized core::int #offset, synthesized core::int #sizeInBytes) → self::StructInlineArray
: super ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C9
get a0() → ffi::Array<ffi::Uint8>
return new ffi::Array::_<ffi::Uint8>( block {

View file

@ -16,6 +16,9 @@ final class StructInlineArray extends ffi::Struct {
constructor #fromTypedDataBase(synthesized core::Object #typedDataBase) → self::StructInlineArray
: super ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized typ::TypedData #typedData, synthesized core::int #offset, synthesized core::int #sizeInBytes) → self::StructInlineArray
: super ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C9
get a0() → ffi::Array<ffi::Uint8>
return new ffi::Array::_<ffi::Uint8>( block {

View file

@ -17,6 +17,9 @@ final class StructInlineArrayMultiDimensional extends ffi::Struct {
constructor #fromTypedDataBase(synthesized core::Object #typedDataBase) → self::StructInlineArrayMultiDimensional
: super ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized typ::TypedData #typedData, synthesized core::int #offset, synthesized core::int #sizeInBytes) → self::StructInlineArrayMultiDimensional
: super ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C10
get a0() → ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>>
return new ffi::Array::_<ffi::Array<ffi::Array<ffi::Uint8>>>( block {
@ -80,7 +83,7 @@ constants {
Extra constant evaluation status:
Evaluated: NullCheck @ org-dartlang-testcase:///ffi_struct_inline_array_multi_dimensional.dart:18:25 -> IntConstant(0)
Evaluated: NullCheck @ org-dartlang-testcase:///ffi_struct_inline_array_multi_dimensional.dart:19:8 -> IntConstant(1)
Extra constant evaluation: evaluated: 110, effectively constant: 2
Extra constant evaluation: evaluated: 113, effectively constant: 2
Constructor coverage from constants:

View file

@ -17,6 +17,9 @@ final class StructInlineArrayMultiDimensional extends ffi::Struct {
constructor #fromTypedDataBase(synthesized core::Object #typedDataBase) → self::StructInlineArrayMultiDimensional
: super ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized typ::TypedData #typedData, synthesized core::int #offset, synthesized core::int #sizeInBytes) → self::StructInlineArrayMultiDimensional
: super ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C10
get a0() → ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>>
return new ffi::Array::_<ffi::Array<ffi::Array<ffi::Uint8>>>( block {
@ -80,7 +83,7 @@ constants {
Extra constant evaluation status:
Evaluated: NullCheck @ org-dartlang-testcase:///ffi_struct_inline_array_multi_dimensional.dart:18:25 -> IntConstant(0)
Evaluated: NullCheck @ org-dartlang-testcase:///ffi_struct_inline_array_multi_dimensional.dart:19:8 -> IntConstant(1)
Extra constant evaluation: evaluated: 110, effectively constant: 2
Extra constant evaluation: evaluated: 113, effectively constant: 2
Constructor coverage from constants:

View file

@ -248,8 +248,12 @@ class FfiTransformer extends Transformer {
final Procedure arrayNestedDimensionsFlattened;
final Procedure arrayNestedDimensionsFirst;
final Procedure arrayNestedDimensionsRest;
final Procedure structCreate;
final Procedure unionCreate;
final Constructor structFromTypedDataBase;
final Constructor unionFromTypedDataBase;
final Constructor structFromTypedData;
final Constructor unionFromTypedData;
final Constructor arrayConstructor;
final Procedure fromAddressInternal;
final Procedure libraryLookupMethod;
@ -412,10 +416,16 @@ class FfiTransformer extends Transformer {
'dart:ffi', 'Array', 'get:_nestedDimensionsFirst'),
arrayNestedDimensionsRest = index.getProcedure(
'dart:ffi', 'Array', 'get:_nestedDimensionsRest'),
structCreate = index.getProcedure('dart:ffi', 'Struct', 'create'),
unionCreate = index.getProcedure('dart:ffi', 'Union', 'create'),
structFromTypedDataBase =
index.getConstructor('dart:ffi', 'Struct', '_fromTypedDataBase'),
unionFromTypedDataBase =
index.getConstructor('dart:ffi', 'Union', '_fromTypedDataBase'),
structFromTypedData =
index.getConstructor('dart:ffi', 'Struct', '_fromTypedData'),
unionFromTypedData =
index.getConstructor('dart:ffi', 'Union', '_fromTypedData'),
arrayConstructor = index.getConstructor('dart:ffi', 'Array', '_'),
fromAddressInternal =
index.getTopLevelProcedure('dart:ffi', '_fromAddress'),

View file

@ -86,7 +86,7 @@ void transformLibraries(
// If dart:ffi is not loaded (for real): do not do the transformation.
return;
}
final transformer = new _FfiDefinitionTransformer(index, coreTypes, hierarchy,
final transformer = _FfiDefinitionTransformer(index, coreTypes, hierarchy,
diagnosticReporter, referenceFromIndex, changedStructureNotifier);
libraries.forEach(transformer.visitLibrary);
transformer.manualVisitInTopologicalOrder();
@ -510,7 +510,7 @@ class _FfiDefinitionTransformer extends FfiTransformer {
/// #fromTypedDataBase(Object #typedDataBase) :
/// super._fromTypedDataBase(#typedDataBase);
/// ```
final VariableDeclaration typedDataBase = new VariableDeclaration(
final VariableDeclaration typedDataBase = VariableDeclaration(
"#typedDataBase",
type: coreTypes.objectNonNullableRawType,
isSynthesized: true);
@ -537,6 +537,66 @@ class _FfiDefinitionTransformer extends FfiTransformer {
// in return position in FFI calls, and by value in arguments in FFI
// callbacks.
node.addConstructor(ctor);
{
/// Add a constructor which `Struct.create` can use.
///
/// ```dart
/// MyStruct.#fromTypedData(
/// super.typedData,
/// super.offset,
/// super.sizeInBytes,
/// ) : super._fromTypedData();
/// ```
final VariableDeclaration typedData = VariableDeclaration(
"#typedData",
type: InterfaceType(
typedDataClass,
Nullability.nonNullable,
const <DartType>[],
),
isSynthesized: true,
);
final VariableDeclaration offset = VariableDeclaration(
"#offset",
type: coreTypes.intNonNullableRawType,
isSynthesized: true,
);
final VariableDeclaration sizeInBytes = VariableDeclaration(
"#sizeInBytes",
type: coreTypes.intNonNullableRawType,
isSynthesized: true,
);
final name = Name("#fromTypedData");
final reference = indexedClass?.lookupConstructorReference(name);
final Constructor ctor = Constructor(
FunctionNode(
EmptyStatement(),
positionalParameters: [typedData, offset, sizeInBytes],
returnType: InterfaceType(node, Nullability.nonNullable),
),
name: name,
initializers: [
SuperInitializer(
node.superclass == structClass
? structFromTypedData
: unionFromTypedData,
Arguments(
[
VariableGet(typedData),
VariableGet(offset),
VariableGet(sizeInBytes),
],
),
)
],
fileUri: node.fileUri,
reference: reference)
..fileOffset = node.fileOffset
..isNonNullableByDefault = node.enclosingLibrary.isNonNullableByDefault;
node.addConstructor(ctor);
}
}
// Works only for non-transformed classes.

View file

@ -137,13 +137,61 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
}
final target = node.target;
if (hierarchy.isSubclassOf(target.enclosingClass, compoundClass) &&
target.name != Name("#fromTypedDataBase")) {
target.name != Name("#fromTypedDataBase") &&
target.name != Name("#fromTypedData")) {
diagnosticReporter.report(messageFfiCreateOfStructOrUnion,
node.fileOffset, 1, node.location?.file);
}
return super.visitConstructorInvocation(node);
}
/// Transforms calls to Struct.create and Union.create.
///
/// Transforms `create<T>()` into
///
/// ```
/// Compound._fromTypedDataBase(Uint8List(sizeOf<T>()))
/// ```
///
/// and `create<T>(typedList)` into
///
/// ```
/// Compound._fromTypedData(typedList, sizeOf<T>())
/// ```
///
/// in subclasses of `Struct` and `Union`.
Expression _transformCompoundCreate(StaticInvocation node) {
final positionalArguments = node.arguments.positional;
final nativeType = (node.arguments.types.first as InterfaceType);
final constructors = nativeType.classNode.constructors;
final sizeOfExpression = inlineSizeOf(nativeType)!;
if (positionalArguments.isNotEmpty) {
// Check length of provided typed data, use checked constructor.
return ConstructorInvocation(
constructors.firstWhere((c) => c.name == Name("#fromTypedData")),
Arguments([
(defaultExpression(node.arguments.positional.first) as Expression),
(positionalArguments.length >= 2
? defaultExpression(positionalArguments[1]) as Expression
: ConstantExpression(IntConstant(0))),
// Length in bytes to check the typedData against.
sizeOfExpression,
]),
);
}
// Correct-size typed data is allocated, use unchecked constructor.
return ConstructorInvocation(
constructors.firstWhere((c) => c.name == Name("#fromTypedDataBase")),
Arguments([
StaticInvocation(
uint8ListFactory,
Arguments([sizeOfExpression]),
),
]),
);
}
@override
visitProcedure(Procedure node) {
assert(_inFfiTearoff == false);
@ -457,6 +505,10 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
functionType: FunctionTypeInstantiator.instantiate(
allocateFunctionType, node.arguments.types));
}
} else if (target == structCreate || target == unionCreate) {
final nativeType = node.arguments.types.first;
ensureNativeTypeValid(nativeType, node, allowCompounds: true);
return _transformCompoundCreate(node);
} else if (target == nativeAddressOf) {
return _replaceNativeAddressOf(node);
}

View file

@ -19,9 +19,9 @@ final class WCharStruct extends ffi::Struct {
: super ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:1,getterSelectorId:2] [@vm.unboxing-info.metadata=()->i] get a0() → core::int
return [@vm.inferred-type.metadata=int] ffi::_loadAbiSpecificInt<self::WChar>([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] [@vm.inferred-type.metadata=dart.ffi::Pointer] this.{ffi::_Compound::_typedDataBase}{core::Object}, #C20.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
return [@vm.inferred-type.metadata=int] ffi::_loadAbiSpecificInt<self::WChar>([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] [@vm.inferred-type.metadata=dart.ffi::Pointer?] this.{ffi::_Compound::_typedDataBase}{core::Object}, #C20.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:1,getterSelectorId:2] [@vm.unboxing-info.metadata=(i)->i] set a0([@vm.inferred-arg-type.metadata=dart.core::_Smi] synthesized core::int #externalFieldValue) → void
return [@vm.inferred-type.metadata=int] ffi::_storeAbiSpecificInt<self::WChar>([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] [@vm.inferred-type.metadata=dart.ffi::Pointer] this.{ffi::_Compound::_typedDataBase}{core::Object}, #C20.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue);
return [@vm.inferred-type.metadata=int] ffi::_storeAbiSpecificInt<self::WChar>([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] [@vm.inferred-type.metadata=dart.ffi::Pointer?] this.{ffi::_Compound::_typedDataBase}{core::Object}, #C20.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue);
[@vm.unboxing-info.metadata=()->i] @#C10
static get #sizeOf() → core::int*
return #C22.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
@ -33,7 +33,7 @@ final class WCharArrayStruct extends ffi::Struct {
;
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:3] get a0() → ffi::Array<self::WChar>
return new ffi::Array::_<self::WChar>( block {
synthesized core::Object #typedDataBase = [@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] [@vm.inferred-type.metadata=dart.ffi::Pointer] this.{ffi::_Compound::_typedDataBase}{core::Object};
synthesized core::Object #typedDataBase = [@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] [@vm.inferred-type.metadata=dart.ffi::Pointer?] this.{ffi::_Compound::_typedDataBase}{core::Object};
synthesized core::int #offset = #C20.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
} =>#typedDataBase is{ForLegacy} ffi::Pointer<ffi::NativeType> ?{core::Object} [@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<self::WChar>([@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] [@vm.direct-call.metadata=dart.ffi::Pointer.address] [@vm.inferred-type.metadata=int] #typedDataBase.{ffi::Pointer::address}{core::int}.{core::num::+}(#offset){(core::num) → core::num}) : let synthesized typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in throw "Attempt to execute code removed by Dart AOT compiler (TFA)", #C23, #C28);
[@vm.unboxing-info.metadata=()->i] @#C10

View file

@ -25,6 +25,9 @@ final class WCharStruct extends ffi::Struct {
constructor #fromTypedDataBase(synthesized core::Object #typedDataBase) → self::WCharStruct
: super ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized typ::TypedData #typedData, synthesized core::int #offset, synthesized core::int #sizeInBytes) → self::WCharStruct
: super ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C74
get a0() → core::int
return ffi::_loadAbiSpecificInt<self::WChar>(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C75.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
@ -49,6 +52,9 @@ final class WCharArrayStruct extends ffi::Struct {
constructor #fromTypedDataBase(synthesized core::Object #typedDataBase) → self::WCharArrayStruct
: super ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized typ::TypedData #typedData, synthesized core::int #offset, synthesized core::int #sizeInBytes) → self::WCharArrayStruct
: super ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C83
get a0() → ffi::Array<self::WChar>
return new ffi::Array::_<self::WChar>( block {

View file

@ -19,9 +19,9 @@ final class IncompleteStruct extends ffi::Struct {
: super ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:1,getterSelectorId:2] [@vm.unboxing-info.metadata=()->i] get a0() → core::int
return [@vm.inferred-type.metadata=int] ffi::_loadAbiSpecificInt<self::Incomplete>([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] [@vm.inferred-type.metadata=dart.ffi::Pointer] this.{ffi::_Compound::_typedDataBase}{core::Object}, #C17.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
return [@vm.inferred-type.metadata=int] ffi::_loadAbiSpecificInt<self::Incomplete>([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] [@vm.inferred-type.metadata=dart.ffi::Pointer?] this.{ffi::_Compound::_typedDataBase}{core::Object}, #C17.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:1,getterSelectorId:2] [@vm.unboxing-info.metadata=(i)->i] set a0([@vm.inferred-arg-type.metadata=dart.core::_Smi] synthesized core::int #externalFieldValue) → void
return [@vm.inferred-type.metadata=int] ffi::_storeAbiSpecificInt<self::Incomplete>([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] [@vm.inferred-type.metadata=dart.ffi::Pointer] this.{ffi::_Compound::_typedDataBase}{core::Object}, #C17.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue);
return [@vm.inferred-type.metadata=int] ffi::_storeAbiSpecificInt<self::Incomplete>([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] [@vm.inferred-type.metadata=dart.ffi::Pointer?] this.{ffi::_Compound::_typedDataBase}{core::Object}, #C17.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue);
[@vm.unboxing-info.metadata=()->i] @#C8
static get #sizeOf() → core::int*
return [@vm.inferred-type.metadata=dart.core::_Smi (value: 8)] ffi::_checkAbiSpecificIntegerMapping<core::int>(#C19.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
@ -33,7 +33,7 @@ final class IncompleteArrayStruct extends ffi::Struct {
;
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:3] get a0() → ffi::Array<self::Incomplete>
return new ffi::Array::_<self::Incomplete>( block {
synthesized core::Object #typedDataBase = [@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] [@vm.inferred-type.metadata=dart.ffi::Pointer] this.{ffi::_Compound::_typedDataBase}{core::Object};
synthesized core::Object #typedDataBase = [@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] [@vm.inferred-type.metadata=dart.ffi::Pointer?] this.{ffi::_Compound::_typedDataBase}{core::Object};
synthesized core::int #offset = #C17.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
} =>#typedDataBase is{ForLegacy} ffi::Pointer<ffi::NativeType> ?{core::Object} [@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<self::Incomplete>([@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] [@vm.direct-call.metadata=dart.ffi::Pointer.address] [@vm.inferred-type.metadata=int] #typedDataBase.{ffi::Pointer::address}{core::int}.{core::num::+}(#offset){(core::num) → core::num}) : let synthesized typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in throw "Attempt to execute code removed by Dart AOT compiler (TFA)", #C20, #C25);
[@vm.unboxing-info.metadata=()->i] @#C8

View file

@ -25,6 +25,9 @@ final class IncompleteStruct extends ffi::Struct {
constructor #fromTypedDataBase(synthesized core::Object #typedDataBase) → self::IncompleteStruct
: super ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized typ::TypedData #typedData, synthesized core::int #offset, synthesized core::int #sizeInBytes) → self::IncompleteStruct
: super ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C37
get a0() → core::int
return ffi::_loadAbiSpecificInt<self::Incomplete>(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C38.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
@ -49,6 +52,9 @@ final class IncompleteArrayStruct extends ffi::Struct {
constructor #fromTypedDataBase(synthesized core::Object #typedDataBase) → self::IncompleteArrayStruct
: super ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized typ::TypedData #typedData, synthesized core::int #offset, synthesized core::int #sizeInBytes) → self::IncompleteArrayStruct
: super ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C46
get a0() → ffi::Array<self::Incomplete>
return new ffi::Array::_<self::Incomplete>( block {

View file

@ -15,6 +15,9 @@ final class Coordinate extends ffi::Struct {
constructor #fromTypedDataBase(synthesized core::Object #typedDataBase) → self::Coordinate
: super ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized typ::TypedData #typedData, synthesized core::int #offset, synthesized core::int #sizeInBytes) → self::Coordinate
: super ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C7
get x() → core::int
return ffi::_loadInt64(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C9.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
@ -42,6 +45,9 @@ final class SomeUnion extends ffi::Union {
constructor #fromTypedDataBase(synthesized core::Object #typedDataBase) → self::SomeUnion
: super ffi::Union::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized typ::TypedData #typedData, synthesized core::int #offset, synthesized core::int #sizeInBytes) → self::SomeUnion
: super ffi::Union::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
get coordinate() → self::Coordinate
return new self::Coordinate::#fromTypedDataBase( block {
synthesized core::Object #typedDataBase = this.{ffi::_Compound::_typedDataBase}{core::Object};

View file

@ -13,7 +13,7 @@ final class Struct1ByteInt extends ffi::Struct {
: super ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:1] [@vm.unboxing-info.metadata=()->i] get a0() → core::int
return [@vm.inferred-type.metadata=int] ffi::_loadInt8([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] [@vm.inferred-type.metadata=dart.typed_data::_Uint8List] this.{ffi::_Compound::_typedDataBase}{core::Object}, #C8.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
return [@vm.inferred-type.metadata=int] ffi::_loadInt8([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] [@vm.inferred-type.metadata=dart.typed_data::_Uint8List?] this.{ffi::_Compound::_typedDataBase}{core::Object}, #C8.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:2,getterSelectorId:3] method toString() → core::String
return "(${[@vm.direct-call.metadata=#lib::Struct1ByteInt.a0] this.{self::Struct1ByteInt::a0}{core::int}})";
}

View file

@ -2,8 +2,8 @@ library #lib;
import self as self;
import "dart:core" as core;
import "dart:ffi" as ffi;
import "dart:_internal" as _in;
import "dart:typed_data" as typ;
import "dart:_internal" as _in;
import "dart:ffi";
@ -15,6 +15,9 @@ final class Struct1ByteInt extends ffi::Struct {
constructor #fromTypedDataBase(synthesized core::Object #typedDataBase) → self::Struct1ByteInt
: super ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized typ::TypedData #typedData, synthesized core::int #offset, synthesized core::int #sizeInBytes) → self::Struct1ByteInt
: super ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C7
get a0() → core::int
return ffi::_loadInt8(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C9.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});

View file

@ -13,9 +13,9 @@ final class Vec2d extends ffi::Struct {
: super ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:1] [@vm.unboxing-info.metadata=()->d] get x() → core::double
return [@vm.inferred-type.metadata=dart.core::_Double] ffi::_loadDouble([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] [@vm.inferred-type.metadata=dart.ffi::Pointer] this.{ffi::_Compound::_typedDataBase}{core::Object}, #C8.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
return [@vm.inferred-type.metadata=dart.core::_Double] ffi::_loadDouble([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] [@vm.inferred-type.metadata=dart.ffi::Pointer?] this.{ffi::_Compound::_typedDataBase}{core::Object}, #C8.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:2] [@vm.unboxing-info.metadata=()->d] get y() → core::double
return [@vm.inferred-type.metadata=dart.core::_Double] ffi::_loadDouble([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] [@vm.inferred-type.metadata=dart.ffi::Pointer] this.{ffi::_Compound::_typedDataBase}{core::Object}, #C10.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
return [@vm.inferred-type.metadata=dart.core::_Double] ffi::_loadDouble([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] [@vm.inferred-type.metadata=dart.ffi::Pointer?] this.{ffi::_Compound::_typedDataBase}{core::Object}, #C10.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
}
@#C15
final class MyUnion extends ffi::Union {
@ -23,7 +23,7 @@ final class MyUnion extends ffi::Union {
: super ffi::Union::_fromTypedDataBase(#typedDataBase)
;
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3] set indirectVector([@vm.inferred-arg-type.metadata=dart.ffi::Pointer] synthesized ffi::Pointer<self::Vec2d> #externalFieldValue) → void
return ffi::_storePointer<self::Vec2d>([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] [@vm.inferred-type.metadata=dart.ffi::Pointer] this.{ffi::_Compound::_typedDataBase}{core::Object}, #C8.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue);
return ffi::_storePointer<self::Vec2d>([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] [@vm.inferred-type.metadata=dart.ffi::Pointer?] this.{ffi::_Compound::_typedDataBase}{core::Object}, #C8.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue);
}
@#C21
static get aString() → ffi::Pointer<ffi::Char>

View file

@ -15,6 +15,9 @@ final class Vec2d extends ffi::Struct {
constructor #fromTypedDataBase(synthesized core::Object #typedDataBase) → self::Vec2d
: super ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized typ::TypedData #typedData, synthesized core::int #offset, synthesized core::int #sizeInBytes) → self::Vec2d
: super ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
@#C7
get x() → core::double
return ffi::_loadDouble(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C9.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
@ -39,6 +42,9 @@ final class MyUnion extends ffi::Union {
constructor #fromTypedDataBase(synthesized core::Object #typedDataBase) → self::MyUnion
: super ffi::Union::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized typ::TypedData #typedData, synthesized core::int #offset, synthesized core::int #sizeInBytes) → self::MyUnion
: super ffi::Union::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
get vector() → self::Vec2d
return new self::Vec2d::#fromTypedDataBase( block {
synthesized core::Object #typedDataBase = this.{ffi::_Compound::_typedDataBase}{core::Object};

View file

@ -0,0 +1,53 @@
// 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:ffi';
import 'dart:typed_data';
import 'package:expect/expect.dart';
void main() {
for (int i = 0; i < 100; i++) {
testStructAllocateDart();
}
print('done');
}
final class Coordinate extends Struct {
factory Coordinate({double? x, double? y}) {
final result = Struct.create<Coordinate>();
if (x != null) result.x = x;
if (y != null) result.y = y;
return result;
}
factory Coordinate.fromTypedList(TypedData typedList) {
return Struct.create<Coordinate>(typedList);
}
@Double()
external double x;
@Double()
external double y;
}
void testStructAllocateDart() {
final c1 = Coordinate()
..x = 10.0
..y = 20.0;
Expect.equals(10.0, c1.x);
Expect.equals(20.0, c1.y);
final typedList = Float64List(2);
typedList[0] = 30.0;
typedList[1] = 40.0;
final c2 = Coordinate.fromTypedList(typedList);
Expect.equals(30.0, c2.x);
Expect.equals(40.0, c2.y);
final c3 = Coordinate(x: 50.0, y: 60);
Expect.equals(50.0, c3.x);
Expect.equals(60.0, c3.y);
}

View file

@ -0,0 +1,81 @@
library #lib;
import self as self;
import "dart:core" as core;
import "dart:ffi" as ffi;
import "dart:typed_data" as typ;
import "package:expect/expect.dart" as exp;
import "dart:ffi";
import "dart:typed_data";
import "package:expect/expect.dart";
@#C6
final class Coordinate extends ffi::Struct {
constructor #fromTypedDataBase([@vm.inferred-arg-type.metadata=dart.typed_data::_Uint8List] synthesized core::Object #typedDataBase) → self::Coordinate
: super ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData([@vm.inferred-arg-type.metadata=dart.typed_data::_Float64List] synthesized typ::TypedData #typedData) → self::Coordinate
: super ffi::Struct::_fromTypedData(#typedData)
;
static factory •({[@vm.inferred-arg-type.metadata=dart.core::_Double?] core::double? x = #C4, [@vm.inferred-arg-type.metadata=dart.core::_Double?] core::double? y = #C4}) → self::Coordinate {
final self::Coordinate result = new self::Coordinate::#fromTypedDataBase([@vm.inferred-type.metadata=dart.typed_data::_Uint8List] typ::Uint8List::•([@vm.inferred-type.metadata=dart.core::_Smi (value: 16)] self::Coordinate::#sizeOf));
if(!(x == null))
[@vm.direct-call.metadata=#lib::Coordinate.x] [@vm.inferred-type.metadata=!? (skip check)] result.{self::Coordinate::x} = x{core::double};
if(!(y == null))
[@vm.direct-call.metadata=#lib::Coordinate.y] [@vm.inferred-type.metadata=!? (skip check)] result.{self::Coordinate::y} = y{core::double};
return result;
}
static factory fromTypedList([@vm.inferred-arg-type.metadata=dart.typed_data::_Float64List] typ::TypedData typedList) → self::Coordinate {
return let final typ::TypedData #t1 = typedList in let final core::int* #t2 = [@vm.inferred-type.metadata=dart.core::_Smi (value: 16)] self::Coordinate::#sizeOf in new self::Coordinate::#fromTypedData(#t1);
}
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:1,getterSelectorId:2] [@vm.unboxing-info.metadata=()->d] get x() → core::double
return [@vm.inferred-type.metadata=dart.core::_Double] ffi::_loadDouble([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] this.{ffi::_Compound::_typedDataBase}{core::Object}, #C8.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:1,getterSelectorId:2] [@vm.unboxing-info.metadata=(d)->b] set x([@vm.inferred-arg-type.metadata=dart.core::_Double] synthesized core::double #externalFieldValue) → void
return ffi::_storeDouble([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] this.{ffi::_Compound::_typedDataBase}{core::Object}, #C8.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue);
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3,getterSelectorId:4] [@vm.unboxing-info.metadata=()->d] get y() → core::double
return [@vm.inferred-type.metadata=dart.core::_Double] ffi::_loadDouble([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] this.{ffi::_Compound::_typedDataBase}{core::Object}, #C10.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3,getterSelectorId:4] [@vm.unboxing-info.metadata=(d)->b] set y([@vm.inferred-arg-type.metadata=dart.core::_Double] synthesized core::double #externalFieldValue) → void
return ffi::_storeDouble([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] this.{ffi::_Compound::_typedDataBase}{core::Object}, #C10.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue);
[@vm.unboxing-info.metadata=()->i] @#C12
static get #sizeOf() → core::int*
return #C14.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
}
static method main() → void {
for (core::int i = 0; [@vm.direct-call.metadata=dart.core::_IntegerImplementation.<] [@vm.inferred-type.metadata=dart.core::bool (skip check)] i.{core::num::<}(100){(core::num) → core::bool}; i = [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] i.{core::num::+}(1){(core::num) → core::int}) {
self::testStructAllocateDart();
}
core::print("done");
}
static method testStructAllocateDart() → void {
final self::Coordinate c1 = let final self::Coordinate #t3 = [@vm.inferred-type.metadata=#lib::Coordinate] self::Coordinate::•() in block {
[@vm.direct-call.metadata=#lib::Coordinate.x] [@vm.inferred-type.metadata=!? (skip check)] #t3.{self::Coordinate::x} = 10.0;
[@vm.direct-call.metadata=#lib::Coordinate.y] [@vm.inferred-type.metadata=!? (skip check)] #t3.{self::Coordinate::y} = 20.0;
} =>#t3;
exp::Expect::equals(10.0, [@vm.direct-call.metadata=#lib::Coordinate.x] [@vm.inferred-type.metadata=dart.core::_Double] c1.{self::Coordinate::x}{core::double});
exp::Expect::equals(20.0, [@vm.direct-call.metadata=#lib::Coordinate.y] [@vm.inferred-type.metadata=dart.core::_Double] c1.{self::Coordinate::y}{core::double});
final typ::Float64List typedList = [@vm.inferred-type.metadata=dart.typed_data::_Float64List] typ::Float64List::•(2);
[@vm.call-site-attributes.metadata=receiverType:dart.typed_data::Float64List] [@vm.direct-call.metadata=dart.typed_data::_Float64List.[]=] [@vm.inferred-type.metadata=!? (skip check)] typedList.{core::List::[]=}(0, 30.0){(core::int, core::double) → void};
[@vm.call-site-attributes.metadata=receiverType:dart.typed_data::Float64List] [@vm.direct-call.metadata=dart.typed_data::_Float64List.[]=] [@vm.inferred-type.metadata=!? (skip check)] typedList.{core::List::[]=}(1, 40.0){(core::int, core::double) → void};
final self::Coordinate c2 = [@vm.inferred-type.metadata=#lib::Coordinate] self::Coordinate::fromTypedList(typedList);
exp::Expect::equals(30.0, [@vm.direct-call.metadata=#lib::Coordinate.x] [@vm.inferred-type.metadata=dart.core::_Double] c2.{self::Coordinate::x}{core::double});
exp::Expect::equals(40.0, [@vm.direct-call.metadata=#lib::Coordinate.y] [@vm.inferred-type.metadata=dart.core::_Double] c2.{self::Coordinate::y}{core::double});
final self::Coordinate c3 = [@vm.inferred-type.metadata=#lib::Coordinate] self::Coordinate::•(x: 50.0, y: 60.0);
exp::Expect::equals(50.0, [@vm.direct-call.metadata=#lib::Coordinate.x] [@vm.inferred-type.metadata=dart.core::_Double] c3.{self::Coordinate::x}{core::double});
exp::Expect::equals(60.0, [@vm.direct-call.metadata=#lib::Coordinate.y] [@vm.inferred-type.metadata=dart.core::_Double] c3.{self::Coordinate::y}{core::double});
}
constants {
#C1 = "vm:ffi:struct-fields"
#C2 = TypeLiteralConstant(ffi::Double)
#C3 = <core::Type>[#C2, #C2]
#C4 = null
#C5 = ffi::_FfiStructLayout {fieldTypes:#C3, packing:#C4}
#C6 = core::pragma {name:#C1, options:#C5}
#C7 = 0
#C8 = <core::int*>[#C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7]
#C9 = 8
#C10 = <core::int*>[#C9, #C9, #C9, #C9, #C9, #C9, #C9, #C9, #C9, #C9, #C9, #C9, #C9, #C9, #C9, #C9, #C9, #C9, #C9, #C9, #C9, #C9]
#C11 = "vm:prefer-inline"
#C12 = core::pragma {name:#C11, options:#C4}
#C13 = 16
#C14 = <core::int*>[#C13, #C13, #C13, #C13, #C13, #C13, #C13, #C13, #C13, #C13, #C13, #C13, #C13, #C13, #C13, #C13, #C13, #C13, #C13, #C13, #C13, #C13]
}

View file

@ -0,0 +1,86 @@
library #lib;
import self as self;
import "dart:core" as core;
import "dart:ffi" as ffi;
import "dart:typed_data" as typ;
import "package:expect/expect.dart" as exp;
import "dart:ffi";
import "dart:typed_data";
import "package:expect/expect.dart";
@#C6
final class Coordinate extends ffi::Struct {
constructor #fromTypedDataBase(synthesized core::Object #typedDataBase) → self::Coordinate
: super ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
constructor #fromTypedData(synthesized typ::TypedData #typedData, synthesized core::int #offset, synthesized core::int #sizeInBytes) → self::Coordinate
: super ffi::Struct::_fromTypedData(#typedData, #offset, #sizeInBytes)
;
static factory •({core::double? x = #C4, core::double? y = #C4}) → self::Coordinate {
final self::Coordinate result = new self::Coordinate::#fromTypedDataBase(typ::Uint8List::•(self::Coordinate::#sizeOf));
if(!(x == null))
result.{self::Coordinate::x} = x{core::double};
if(!(y == null))
result.{self::Coordinate::y} = y{core::double};
return result;
}
static factory fromTypedList(typ::TypedData typedList) → self::Coordinate {
return new self::Coordinate::#fromTypedData(typedList, #C7, self::Coordinate::#sizeOf);
}
@#C8
get x() → core::double
return ffi::_loadDouble(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C9.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
@#C8
set x(synthesized core::double #externalFieldValue) → void
return ffi::_storeDouble(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C9.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue);
@#C8
get y() → core::double
return ffi::_loadDouble(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C11.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
@#C8
set y(synthesized core::double #externalFieldValue) → void
return ffi::_storeDouble(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C11.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue);
@#C13
static get #sizeOf() → core::int*
return #C15.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
}
static method main() → void {
for (core::int i = 0; i.{core::num::<}(100){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::num) → core::int}) {
self::testStructAllocateDart();
}
core::print("done");
}
static method testStructAllocateDart() → void {
final self::Coordinate c1 = let final self::Coordinate #t1 = self::Coordinate::•() in block {
#t1.{self::Coordinate::x} = 10.0;
#t1.{self::Coordinate::y} = 20.0;
} =>#t1;
exp::Expect::equals(10.0, c1.{self::Coordinate::x}{core::double});
exp::Expect::equals(20.0, c1.{self::Coordinate::y}{core::double});
final typ::Float64List typedList = typ::Float64List::•(2);
[@vm.call-site-attributes.metadata=receiverType:dart.typed_data::Float64List] typedList.{core::List::[]=}(0, 30.0){(core::int, core::double) → void};
[@vm.call-site-attributes.metadata=receiverType:dart.typed_data::Float64List] typedList.{core::List::[]=}(1, 40.0){(core::int, core::double) → void};
final self::Coordinate c2 = self::Coordinate::fromTypedList(typedList);
exp::Expect::equals(30.0, c2.{self::Coordinate::x}{core::double});
exp::Expect::equals(40.0, c2.{self::Coordinate::y}{core::double});
final self::Coordinate c3 = self::Coordinate::•(x: 50.0, y: 60.0);
exp::Expect::equals(50.0, c3.{self::Coordinate::x}{core::double});
exp::Expect::equals(60.0, c3.{self::Coordinate::y}{core::double});
}
constants {
#C1 = "vm:ffi:struct-fields"
#C2 = TypeLiteralConstant(ffi::Double)
#C3 = <core::Type>[#C2, #C2]
#C4 = null
#C5 = ffi::_FfiStructLayout {fieldTypes:#C3, packing:#C4}
#C6 = core::pragma {name:#C1, options:#C5}
#C7 = 0
#C8 = ffi::Double {}
#C9 = <core::int*>[#C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7, #C7]
#C10 = 8
#C11 = <core::int*>[#C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10]
#C12 = "vm:prefer-inline"
#C13 = core::pragma {name:#C12, options:#C4}
#C14 = 16
#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]
}

View file

@ -41,7 +41,7 @@ final class Struct11 extends ffi::Struct {
;
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:1] get nested() → self::Struct12
return new self::Struct12::#fromTypedDataBase( block {
synthesized core::Object #typedDataBase = [@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] [@vm.inferred-type.metadata=!] this.{ffi::_Compound::_typedDataBase}{core::Object};
synthesized core::Object #typedDataBase = [@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] this.{ffi::_Compound::_typedDataBase}{core::Object};
synthesized core::int #offset = #C12.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
} =>#typedDataBase is{ForLegacy} ffi::Pointer<ffi::NativeType> ?{core::Object} [@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress<self::Struct12>([@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] [@vm.direct-call.metadata=dart.ffi::Pointer.address] [@vm.inferred-type.metadata=int] #typedDataBase.{ffi::Pointer::address}{core::int}.{core::num::+}(#offset){(core::num) → core::num}) : let synthesized typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in [@vm.direct-call.metadata=dart.typed_data::_ByteBuffer.asUint8List] [@vm.inferred-type.metadata=dart.typed_data::_Uint8ArrayView (skip check)] [@vm.inferred-type.metadata=dart.typed_data::_ByteBuffer] #typedData.{typ::TypedData::buffer}{typ::ByteBuffer}.{typ::ByteBuffer::asUint8List}([@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] [@vm.inferred-type.metadata=dart.core::_Smi] #typedData.{typ::TypedData::offsetInBytes}{core::int}.{core::num::+}(#offset){(core::num) → core::num}, #C15.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}){([core::int, core::int?]) → typ::Uint8List});
}

View file

@ -4,18 +4,39 @@
part of dart.ffi;
/// The supertype of all FFI compound types.
/// Shared supertype of the FFI compound [Struct] and [Union] types.
///
/// FFI struct types should extend [Struct]. For more information see the
/// documentation on this class.
/// FFI struct and union types should extend [Struct] and [Union]. For more
/// information see the documentation on those classes.
@pragma("wasm:entry-point")
abstract final class _Compound implements SizedNativeType {
/// The underlying [TypedData] or [Pointer] that a subtype uses.
@pragma("vm:entry-point")
final Object _typedDataBase;
_Compound._() : _typedDataBase = nullptr;
external _Compound._();
_Compound._fromTypedDataBase(this._typedDataBase);
/// Constructs a view on [typedData].
///
/// The length in bytes of [typedData] must at least be [sizeInBytes].
_Compound._fromTypedData(
TypedData typedData,
int offset,
int sizeInBytes,
) : _typedDataBase = Uint8List.sublistView(typedData, offset) {
if (typedData.lengthInBytes <
typedData.elementSizeInBytes * offset + sizeInBytes) {
throw RangeError.range(
typedData.lengthInBytes,
sizeInBytes + typedData.elementSizeInBytes * offset,
null,
'typedData.lengthInBytes',
'The typed list is not large enough',
);
}
}
}
/// The supertype of all FFI struct types.
@ -29,7 +50,8 @@ abstract final class _Compound implements SizedNativeType {
///
/// All field declarations in a [Struct] subclass declaration must either have
/// type [int] or [double] and be annotated with a [NativeType] representing the
/// native type, or must be of type [Pointer]. For example:
/// native type, or must be of type [Pointer], [Array] or a subtype of [Struct]
/// or [Union]. For example:
///
/// ```c
/// typedef struct {
@ -51,16 +73,18 @@ abstract final class _Compound implements SizedNativeType {
/// }
/// ```
///
/// All field declarations in a [Struct] subclass declaration must be marked
/// `external`. You cannot create instances of the class, only have it point to
/// existing native memory, so there is no memory in which to store non-native
/// fields. External fields also cannot be initialized by constructors since no
/// Dart object is being created.
/// The field declarations of a [Struct] subclass *must* be marked `external`. A
/// struct subclass points directly into a location of native memory ([Pointer])
/// or Dart memory ([TypedData]), and the external field's getter and setter
/// implementations directly read and write bytes at appropriate offsets from
/// that location. This does not allow for non-native fields to also exist.
///
/// Instances of a subclass of [Struct] have reference semantics and are backed
/// by native memory or typed data. They may allocated via allocation or loaded
/// from a [Pointer] or created by ffi calls or callbacks. They cannot be
/// created by a generative constructor.
/// An instance of a struct subclass cannot be created with a generative
/// constructor. Instead, an instance can be created by [StructPointer.ref],
/// [Struct.create], FFI call return values, FFI callback arguments,
/// [StructArray], and accessing [Struct] fields. To create an instance backed
/// by native memory, use [StructPointer.ref]. To create an instance backed by
/// Dart memory, use [Struct.create].
@Since('2.12')
abstract base class Struct extends _Compound {
/// Construct a reference to the [nullptr].
@ -69,8 +93,72 @@ abstract base class Struct extends _Compound {
/// structs.
Struct() : super._();
Struct._fromTypedDataBase(Object typedDataBase)
: super._fromTypedDataBase(typedDataBase);
/// Creates a struct view of bytes in [typedData].
///
/// The created instance of the struct subclass will then be backed by the
/// bytes at [TypedData.offsetInBytes] plus [offset] times
/// [TypedData.elementSizeInBytes]. That is, the getters and setters of the
/// external instance variables declared by the subclass, will read an write
/// their values from the bytes of the [TypedData.buffer] of [typedData],
/// starting at [TypedData.offsetInBytes] plus [offset] times
/// [TypedData.elementSizeInBytes]. The [TypedData.lengthInBytes] of
/// [typedData] *must* be sufficient to contain the [sizeOf] of the struct
/// subclass. _It doesn't matter whether the [typedData] is, for example, a
/// [Uint8List], a [Float64List], or any other [TypedData], it's only treated
/// as a view into a [ByteBuffer], through its [TypedData.buffer],
/// [TypedData.offsetInBytes] and [TypedData.lengthInBytes]._
///
/// If [typedData] is omitted, a fresh [ByteBuffer], with precisely enough
/// bytes for the [sizeOf] of the created struct, is allocated on the Dart
/// heap, and used as memory to store the struct fields.
///
/// If [offset] is provded, the indexing into [typedData] is offset by
/// [offset] times [TypedData.elementSizeInBytes].
///
/// Example:
///
/// ```dart import:typed_data
/// final class Point extends Struct {
/// @Double()
/// external double x;
///
/// @Double()
/// external double y;
///
/// /// Creates Dart managed memory to hold a `Point` and returns the
/// /// `Point` view on it.
/// factory Point(double x, double y) {
/// return Struct.create()
/// ..x = x
/// ..y = y;
/// }
///
/// /// Creates a [Point] view on [typedData].
/// factory Point.fromTypedData(TypedData typedData) {
/// return Struct.create(typedData);
/// }
/// }
/// ```
///
/// To create a struct object from a [Pointer], use [StructPointer.ref].
@Since('3.3')
external static T create<T extends Struct>([TypedData typedData, int offset]);
/// Creates a view on a [TypedData] or [Pointer].
///
/// Used in [StructPointer.ref], FFI calls, and FFI callbacks.
Struct._fromTypedDataBase(super._typedDataBase) : super._fromTypedDataBase();
/// Creates a view on [typedData].
///
/// The length in bytes of [typedData] must at least be [sizeInBytes].
///
/// Used in the `external` public constructor of [Struct].
Struct._fromTypedData(
super.typedData,
super.offset,
super.sizeInBytes,
) : super._fromTypedData();
}
/// Annotation to specify on `Struct` subtypes to indicate that its members

View file

@ -6,16 +6,17 @@ part of dart.ffi;
/// The supertype of all FFI union types.
///
/// FFI union types should extend this class and declare fields corresponding
/// to the underlying native union.
/// FFI union types should extend this class and declare fields corresponding to
/// the underlying native union.
///
/// Field declarations in a [Union] subclass declaration are automatically
/// given a setter and getter implementation which accesses the native union's
/// field in memory.
/// Field declarations in a [Union] subclass declaration are automatically given
/// a setter and getter implementation which accesses the native union's field
/// in memory.
///
/// All field declarations in a [Union] subclass declaration must either have
/// type [int] or [double] and be annotated with a [NativeType] representing the
/// native type, or must be of type [Pointer]. For example:
/// native type, or must be of type [Pointer], [Array] or a subtype of [Struct]
/// or [Union]. For example:
///
/// ```c
/// typedef union {
@ -37,15 +38,18 @@ part of dart.ffi;
/// }
/// ```
///
/// All field declarations in a [Union] subclass declaration must be marked
/// `external`. You cannot create instances of the class, only have it point to
/// existing native memory, so there is no memory in which to store non-native
/// fields. External fields also cannot be initialized by constructors since no
/// Dart object is being created.
/// The field declarations of a [Union] subclass *must* be marked `external`. A
/// union subclass points directly into a location of native memory ([Pointer])
/// or Dart memory ([TypedData]), and the external field's getter and setter
/// implementations directly read and write bytes at appropriate offsets from
/// that location. This does not allow for non-native fields to also exist.
///
/// Instances of a subclass of [Union] have reference semantics and are backed
/// by native memory. The may allocated via allocation or loaded from a
/// [Pointer], but cannot be created by a generative constructor.
/// An instance of a union subclass cannot be created with a generative
/// constructor. Instead, an instance can be created by [UnionPointer.ref],
/// [Union.create], FFI call return values, FFI callback arguments,
/// [UnionArray], and accessing [Union] fields. To create an instance backed
/// by native memory, use [UnionPointer.ref]. To create an instance backed by
/// Dart memory, use [Union.create].
@Since('2.14')
abstract base class Union extends _Compound {
/// Construct a reference to the [nullptr].
@ -54,6 +58,74 @@ abstract base class Union extends _Compound {
/// unions.
Union() : super._();
Union._fromTypedDataBase(Object typedDataBase)
: super._fromTypedDataBase(typedDataBase);
/// Creates a union view of bytes in [typedData].
///
/// The created instance of the union subclass will then be backed by the
/// bytes at [TypedData.offsetInBytes] plus [offset] times
/// [TypedData.elementSizeInBytes]. That is, the getters and setters of the
/// external instance variables declared by the subclass, will read an write
/// their values from the bytes of the [TypedData.buffer] of [typedData],
/// starting at [TypedData.offsetInBytes] plus [offset] times
/// [TypedData.elementSizeInBytes]. The [TypedData.lengthInBytes] of
/// [typedData] *must* be sufficient to contain the [sizeOf] of the union
/// subclass. _It doesn't matter whether the [typedData] is, for example, a
/// [Uint8List], a [Float64List], or any other [TypedData], it's only treated
/// as a view into a [ByteBuffer], through its [TypedData.buffer],
/// [TypedData.offsetInBytes] and [TypedData.lengthInBytes]._
///
/// If [typedData] is omitted, a fresh [ByteBuffer], with precisely enough
/// bytes for the [sizeOf] of the created union, is allocated on the Dart
/// heap, and used as memory to store the union fields.
///
/// If [offset] is provded, the indexing into [typedData] is offset by
/// [offset] times [TypedData.elementSizeInBytes].
///
/// Example:
///
/// ```dart import:typed_data
/// final class MyUnion extends Union {
/// @Int32()
/// external int a;
///
/// @Float()
/// external double b;
///
/// /// Creates Dart managed memory to hold a `MyUnion` and returns the
/// /// `MyUnion` view on it.
/// factory MyUnion.a(int a) {
/// return Union.create()..a = a;
/// }
///
/// /// Creates Dart managed memory to hold a `MyUnion` and returns the
/// /// `MyUnion` view on it.
/// factory MyUnion.b(double b) {
/// return Union.create()..b = b;
/// }
///
/// /// Creates a [MyUnion] view on [typedData].
/// factory MyUnion.fromTypedData(TypedData typedData) {
/// return Union.create(typedData);
/// }
/// }
/// ```
///
/// To create a union object from a [Pointer], use [UnionPointer.ref].
@Since('3.3')
external static T create<T extends Union>([TypedData typedData, int offset]);
/// Creates a view on a [TypedData] or [Pointer].
///
/// Used in [UnionPointer.ref], FFI calls, and FFI callbacks.
Union._fromTypedDataBase(super._typedDataBase) : super._fromTypedDataBase();
/// Creates a view on [typedData].
///
/// The length in bytes of [typedData] must at least be [sizeInBytes].
///
/// Used in the `external` public constructor of [Union].
Union._fromTypedData(
super.typedData,
super.offset,
super.sizeInBytes,
) : super._fromTypedData();
}

View file

@ -7,11 +7,13 @@
// SharedObjects=ffi_test_functions
import 'dart:ffi';
import 'dart:typed_data';
import "package:expect/expect.dart";
import "package:ffi/ffi.dart";
import 'ffi_test_helpers.dart';
import 'regress_47673_test.dart';
void main() {
testPointerBasic();

View file

@ -0,0 +1,166 @@
// 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.
//
// VMOptions=--deterministic --optimization-counter-threshold=50
import 'dart:ffi';
import 'dart:typed_data';
import 'package:expect/expect.dart';
void main() {
for (int i = 0; i < 100; i++) {
testStructAllocateDart();
testUseCreateDirectly();
testOffsets();
testOutOfBounds();
testUnion();
}
print('done');
}
final class Coordinate extends Struct {
factory Coordinate({double? x, double? y}) {
final result = Struct.create<Coordinate>();
if (x != null) result.x = x;
if (y != null) result.y = y;
return result;
}
factory Coordinate.fromTypedList(TypedData typedList, [int offset = 0]) {
return Struct.create<Coordinate>(typedList, offset);
}
@Double()
external double x;
@Double()
external double y;
}
void testStructAllocateDart() {
final c1 = Coordinate()
..x = 10.0
..y = 20.0;
Expect.equals(10.0, c1.x);
Expect.equals(20.0, c1.y);
final typedList = Float64List(2);
typedList[0] = 30.0;
typedList[1] = 40.0;
final c2 = Coordinate.fromTypedList(typedList);
Expect.equals(30.0, c2.x);
Expect.equals(40.0, c2.y);
final c3 = Coordinate(x: 50.0, y: 60);
Expect.equals(50.0, c3.x);
Expect.equals(60.0, c3.y);
}
final class SomeStruct extends Struct {
@Double()
external double x;
@Double()
external double y;
}
void testUseCreateDirectly() {
final c1 = Struct.create<SomeStruct>()
..x = 10.0
..y = 20.0;
Expect.equals(10.0, c1.x);
Expect.equals(20.0, c1.y);
}
void testOffsets() {
const length = 100;
final typedList = Float64List(length * 2);
for (int i = 0; i < length * 2; i++) {
typedList[i] = i.toDouble();
}
final size = sizeOf<Coordinate>();
var structs = [
for (var i = 0; i < length; i++)
Coordinate.fromTypedList(
typedList,
i * size ~/ typedList.elementSizeInBytes,
)
];
for (int i = 0; i < length; i++) {
Expect.approxEquals(structs[i].x, 2 * i);
Expect.approxEquals(structs[i].y, 2 * i + 1);
}
}
void testOutOfBounds() {
final typedList = Uint8List(3 * sizeOf<Double>());
final c1 = Coordinate.fromTypedList(typedList)
..x = 4
..y = 6;
final view = Uint8List.view(typedList.buffer, 16);
Expect.equals(8, view.lengthInBytes);
Expect.throws<RangeError>(() {
Coordinate.fromTypedList(view)
..x = 6
..y = 8;
});
Expect.throws<RangeError>(() {
Coordinate.fromTypedList(typedList, 16)
..x = 6
..y = 8;
});
Expect.throws<RangeError>(() {
// Negative offsets are not allowed. One should access the ByteBuffer to
// apply a negative offset if this is wanted.
Coordinate.fromTypedList(view, -1)
..x = 6
..y = 8;
});
Expect.approxEquals(c1.x, 4);
Expect.approxEquals(c1.y, 6);
}
final class MyUnion extends Union {
@Int32()
external int a;
@Float()
external double b;
/// Allocates a new [TypedData] of size `sizeOf<MyUnion>()` and wraps it in
/// [MyUnion].
factory MyUnion.a(int a) {
return Union.create<MyUnion>()..a = a;
}
/// Allocates a new [TypedData] of size `sizeOf<MyUnion>()` and wraps it in
/// [MyUnion].
factory MyUnion.b(double b) {
return Union.create<MyUnion>()..b = b;
}
/// Constructs a [MyUnion] view on [typedList].
factory MyUnion.fromTypedData(TypedData typedList) {
return Union.create<MyUnion>(typedList);
}
}
final class MyUnion2 extends Union {
@Int32()
external int a;
@Float()
external double b;
}
void testUnion() {
final myUnion = MyUnion.a(123);
Expect.equals(123, myUnion.a);
Expect.approxEquals(1.723597111119525e-43, myUnion.b);
final myUnion2 = Union.create<MyUnion2>()..a = 123;
Expect.equals(123, myUnion2.a);
Expect.approxEquals(1.723597111119525e-43, myUnion2.b);
}

View file

@ -65,6 +65,7 @@ void main() {
testEmptyStructFromFunctionReturn();
testAllocateGeneric();
testAllocateInvalidType();
testCreateInvalidType();
testRefStruct();
testSizeOfGeneric();
testSizeOfInvalidType();
@ -1063,6 +1064,20 @@ void testAllocateInvalidType() {
// [cfe] Expected type 'AbiSpecificInteger' to be a valid and instantiated subtype of 'NativeType'.
}
// TODO(https://dartbug.com/36780): Improve error messages.
void testCreateInvalidType() {
/**/ Struct.create<Struct>();
// ^^^^^^^^^^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.NON_CONSTANT_TYPE_ARGUMENT
// ^
// [cfe] Expected type 'Struct' to be a valid and instantiated subtype of 'NativeType'.
/**/ Union.create<Union>();
// ^^^^^^^^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.NON_CONSTANT_TYPE_ARGUMENT
// ^
// [cfe] Expected type 'Union' to be a valid and instantiated subtype of 'NativeType'.
}
void testRefStruct() {
final myStructPointer = calloc<TestStruct13>();
Pointer<Struct> structPointer = myStructPointer;