diff --git a/pkg/analyzer/lib/src/generated/ffi_verifier.dart b/pkg/analyzer/lib/src/generated/ffi_verifier.dart index 3b74070b3ca8..df54ec524d53 100644 --- a/pkg/analyzer/lib/src/generated/ffi_verifier.dart +++ b/pkg/analyzer/lib/src/generated/ffi_verifier.dart @@ -219,7 +219,9 @@ class FfiVerifier extends RecursiveAstVisitor { 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 { 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 { } 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 { 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 { 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 _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; diff --git a/pkg/front_end/testcases/general/ffi_sample.dart.strong.transformed.expect b/pkg/front_end/testcases/general/ffi_sample.dart.strong.transformed.expect index 4e277504c0e8..5975978a05e9 100644 --- a/pkg/front_end/testcases/general/ffi_sample.dart.strong.transformed.expect +++ b/pkg/front_end/testcases/general/ffi_sample.dart.strong.transformed.expect @@ -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* next) → self::Coordinate* { return let final self::Coordinate* #t1 = new self::Coordinate::#fromTypedDataBase(allocator.{ffi::Allocator::allocate}(self::Coordinate::#sizeOf){(core::int, {alignment: core::int?}) → ffi::Pointer}!) in block { #t1.{self::Coordinate::x} = x; diff --git a/pkg/front_end/testcases/general/ffi_sample.dart.weak.transformed.expect b/pkg/front_end/testcases/general/ffi_sample.dart.weak.transformed.expect index d1f118b68d46..423fa0ffbd14 100644 --- a/pkg/front_end/testcases/general/ffi_sample.dart.weak.transformed.expect +++ b/pkg/front_end/testcases/general/ffi_sample.dart.weak.transformed.expect @@ -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* next) → self::Coordinate* { return let final self::Coordinate* #t1 = new self::Coordinate::#fromTypedDataBase(allocator.{ffi::Allocator::allocate}(self::Coordinate::#sizeOf){(core::int, {alignment: core::int?}) → ffi::Pointer}!) in block { #t1.{self::Coordinate::x} = x; diff --git a/pkg/front_end/testcases/incremental/crash_05.yaml.world.1.expect b/pkg/front_end/testcases/incremental/crash_05.yaml.world.1.expect index 50f774f52fc8..38324981fc38 100644 --- a/pkg/front_end/testcases/incremental/crash_05.yaml.world.1.expect +++ b/pkg/front_end/testcases/incremental/crash_05.yaml.world.1.expect @@ -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}; diff --git a/pkg/front_end/testcases/incremental/crash_05.yaml.world.2.expect b/pkg/front_end/testcases/incremental/crash_05.yaml.world.2.expect index 50f774f52fc8..38324981fc38 100644 --- a/pkg/front_end/testcases/incremental/crash_05.yaml.world.2.expect +++ b/pkg/front_end/testcases/incremental/crash_05.yaml.world.2.expect @@ -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}; diff --git a/pkg/front_end/testcases/incremental/crash_06.yaml.world.1.expect b/pkg/front_end/testcases/incremental/crash_06.yaml.world.1.expect index 6586b81fbe54..60505481d21f 100644 --- a/pkg/front_end/testcases/incremental/crash_06.yaml.world.1.expect +++ b/pkg/front_end/testcases/incremental/crash_06.yaml.world.1.expect @@ -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 diff --git a/pkg/front_end/testcases/incremental/crash_06.yaml.world.2.expect b/pkg/front_end/testcases/incremental/crash_06.yaml.world.2.expect index 6586b81fbe54..60505481d21f 100644 --- a/pkg/front_end/testcases/incremental/crash_06.yaml.world.2.expect +++ b/pkg/front_end/testcases/incremental/crash_06.yaml.world.2.expect @@ -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 diff --git a/pkg/front_end/testcases/incremental/ffi_01.yaml.world.1.expect b/pkg/front_end/testcases/incremental/ffi_01.yaml.world.1.expect index 6de3c742d91e..bbfb0711cdea 100644 --- a/pkg/front_end/testcases/incremental/ffi_01.yaml.world.1.expect +++ b/pkg/front_end/testcases/incremental/ffi_01.yaml.world.1.expect @@ -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*}); diff --git a/pkg/front_end/testcases/incremental/ffi_01.yaml.world.2.expect b/pkg/front_end/testcases/incremental/ffi_01.yaml.world.2.expect index 4cc52a169513..c51e8d961682 100644 --- a/pkg/front_end/testcases/incremental/ffi_01.yaml.world.2.expect +++ b/pkg/front_end/testcases/incremental/ffi_01.yaml.world.2.expect @@ -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*}); diff --git a/pkg/front_end/testcases/incremental/ffi_02.yaml.world.1.expect b/pkg/front_end/testcases/incremental/ffi_02.yaml.world.1.expect index 16fbde51564c..a792b6fb0fd9 100644 --- a/pkg/front_end/testcases/incremental/ffi_02.yaml.world.1.expect +++ b/pkg/front_end/testcases/incremental/ffi_02.yaml.world.1.expect @@ -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*}); diff --git a/pkg/front_end/testcases/incremental/issue_46666.yaml.world.1.expect b/pkg/front_end/testcases/incremental/issue_46666.yaml.world.1.expect index c0e4e7de2dc3..1a0d7ec8829b 100644 --- a/pkg/front_end/testcases/incremental/issue_46666.yaml.world.1.expect +++ b/pkg/front_end/testcases/incremental/issue_46666.yaml.world.1.expect @@ -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 return dart.ffi::_loadPointer(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 #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 return dart.ffi::_loadPointer(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 #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}; diff --git a/pkg/front_end/testcases/incremental/issue_46666.yaml.world.2.expect b/pkg/front_end/testcases/incremental/issue_46666.yaml.world.2.expect index c0e4e7de2dc3..1a0d7ec8829b 100644 --- a/pkg/front_end/testcases/incremental/issue_46666.yaml.world.2.expect +++ b/pkg/front_end/testcases/incremental/issue_46666.yaml.world.2.expect @@ -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 return dart.ffi::_loadPointer(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 #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 return dart.ffi::_loadPointer(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 #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}; diff --git a/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.1.expect b/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.1.expect index 6de3c742d91e..bbfb0711cdea 100644 --- a/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.1.expect +++ b/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.1.expect @@ -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*}); diff --git a/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.2.expect b/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.2.expect index 7e8fd47b6a2d..1ba3ab8bb30b 100644 --- a/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.2.expect +++ b/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.2.expect @@ -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*}); diff --git a/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.3.expect b/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.3.expect index b65181adb06a..36574284dfbd 100644 --- a/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.3.expect +++ b/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.3.expect @@ -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*}); diff --git a/pkg/front_end/testcases/incremental/no_outline_change_48_ffi.yaml.world.1.expect b/pkg/front_end/testcases/incremental/no_outline_change_48_ffi.yaml.world.1.expect index 561d331cf9c4..f8146ab7227a 100644 --- a/pkg/front_end/testcases/incremental/no_outline_change_48_ffi.yaml.world.1.expect +++ b/pkg/front_end/testcases/incremental/no_outline_change_48_ffi.yaml.world.1.expect @@ -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}; diff --git a/pkg/front_end/testcases/incremental/no_outline_change_48_ffi.yaml.world.2.expect b/pkg/front_end/testcases/incremental/no_outline_change_48_ffi.yaml.world.2.expect index 435c65bd842e..aefff5ca5689 100644 --- a/pkg/front_end/testcases/incremental/no_outline_change_48_ffi.yaml.world.2.expect +++ b/pkg/front_end/testcases/incremental/no_outline_change_48_ffi.yaml.world.2.expect @@ -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}; diff --git a/pkg/front_end/testcases/incremental/no_outline_change_49_ffi.yaml.world.1.expect b/pkg/front_end/testcases/incremental/no_outline_change_49_ffi.yaml.world.1.expect index 561d331cf9c4..f8146ab7227a 100644 --- a/pkg/front_end/testcases/incremental/no_outline_change_49_ffi.yaml.world.1.expect +++ b/pkg/front_end/testcases/incremental/no_outline_change_49_ffi.yaml.world.1.expect @@ -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}; diff --git a/pkg/front_end/testcases/incremental/no_outline_change_49_ffi.yaml.world.2.expect b/pkg/front_end/testcases/incremental/no_outline_change_49_ffi.yaml.world.2.expect index 435c65bd842e..aefff5ca5689 100644 --- a/pkg/front_end/testcases/incremental/no_outline_change_49_ffi.yaml.world.2.expect +++ b/pkg/front_end/testcases/incremental/no_outline_change_49_ffi.yaml.world.2.expect @@ -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}; diff --git a/pkg/front_end/testcases/incremental/no_outline_change_50_ffi.yaml.world.1.expect b/pkg/front_end/testcases/incremental/no_outline_change_50_ffi.yaml.world.1.expect index 573686e60059..a5d081cc955b 100644 --- a/pkg/front_end/testcases/incremental/no_outline_change_50_ffi.yaml.world.1.expect +++ b/pkg/front_end/testcases/incremental/no_outline_change_50_ffi.yaml.world.1.expect @@ -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}; diff --git a/pkg/front_end/testcases/incremental/no_outline_change_50_ffi.yaml.world.2.expect b/pkg/front_end/testcases/incremental/no_outline_change_50_ffi.yaml.world.2.expect index d15e6ee26a11..058b2dcd4cb8 100644 --- a/pkg/front_end/testcases/incremental/no_outline_change_50_ffi.yaml.world.2.expect +++ b/pkg/front_end/testcases/incremental/no_outline_change_50_ffi.yaml.world.2.expect @@ -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}; diff --git a/pkg/front_end/testcases/incremental/regress_46004.yaml.world.1.expect b/pkg/front_end/testcases/incremental/regress_46004.yaml.world.1.expect index 34888c6105b2..9e3230bde088 100644 --- a/pkg/front_end/testcases/incremental/regress_46004.yaml.world.1.expect +++ b/pkg/front_end/testcases/incremental/regress_46004.yaml.world.1.expect @@ -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 return dart.ffi::_loadPointer(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 #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}; diff --git a/pkg/front_end/testcases/incremental/regress_46004.yaml.world.2.expect b/pkg/front_end/testcases/incremental/regress_46004.yaml.world.2.expect index 34888c6105b2..9e3230bde088 100644 --- a/pkg/front_end/testcases/incremental/regress_46004.yaml.world.2.expect +++ b/pkg/front_end/testcases/incremental/regress_46004.yaml.world.2.expect @@ -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 return dart.ffi::_loadPointer(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 #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}; diff --git a/pkg/front_end/testcases/nnbd/ffi_sample.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_sample.dart.strong.transformed.expect index c01e3e336013..678fb4cef8e6 100644 --- a/pkg/front_end/testcases/nnbd/ffi_sample.dart.strong.transformed.expect +++ b/pkg/front_end/testcases/nnbd/ffi_sample.dart.strong.transformed.expect @@ -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*}); diff --git a/pkg/front_end/testcases/nnbd/ffi_sample.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_sample.dart.weak.transformed.expect index c01e3e336013..678fb4cef8e6 100644 --- a/pkg/front_end/testcases/nnbd/ffi_sample.dart.weak.transformed.expect +++ b/pkg/front_end/testcases/nnbd/ffi_sample.dart.weak.transformed.expect @@ -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*}); diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.transformed.expect index aa300bb0642c..c138882b56bc 100644 --- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.transformed.expect +++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.transformed.expect @@ -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 return new ffi::Array::_( block { diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.transformed.expect index 5cc12272e722..356565777e40 100644 --- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.transformed.expect +++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.transformed.expect @@ -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 return new ffi::Array::_( block { diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.transformed.expect index c53d68e9a6ed..95ec1a3e5873 100644 --- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.transformed.expect +++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.transformed.expect @@ -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>> return new ffi::Array::_>>( 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: diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.transformed.expect index 2d297c9b0a0f..1a7dcbfe4436 100644 --- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.transformed.expect +++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.transformed.expect @@ -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>> return new ffi::Array::_>>( 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: diff --git a/pkg/vm/lib/transformations/ffi/common.dart b/pkg/vm/lib/transformations/ffi/common.dart index f94e5bbe2c71..5f93772919f2 100644 --- a/pkg/vm/lib/transformations/ffi/common.dart +++ b/pkg/vm/lib/transformations/ffi/common.dart @@ -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'), diff --git a/pkg/vm/lib/transformations/ffi/definitions.dart b/pkg/vm/lib/transformations/ffi/definitions.dart index 52c2a3286a99..1deff43b6492 100644 --- a/pkg/vm/lib/transformations/ffi/definitions.dart +++ b/pkg/vm/lib/transformations/ffi/definitions.dart @@ -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 [], + ), + 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. diff --git a/pkg/vm/lib/transformations/ffi/use_sites.dart b/pkg/vm/lib/transformations/ffi/use_sites.dart index 5372b0466ce7..08cb5035beca 100644 --- a/pkg/vm/lib/transformations/ffi/use_sites.dart +++ b/pkg/vm/lib/transformations/ffi/use_sites.dart @@ -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()` into + /// + /// ``` + /// Compound._fromTypedDataBase(Uint8List(sizeOf())) + /// ``` + /// + /// and `create(typedList)` into + /// + /// ``` + /// Compound._fromTypedData(typedList, sizeOf()) + /// ``` + /// + /// 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); } diff --git a/pkg/vm/testcases/transformations/ffi/abi_specific_int.dart.aot.expect b/pkg/vm/testcases/transformations/ffi/abi_specific_int.dart.aot.expect index 3e98b9dff3fd..e8aafab84da4 100644 --- a/pkg/vm/testcases/transformations/ffi/abi_specific_int.dart.aot.expect +++ b/pkg/vm/testcases/transformations/ffi/abi_specific_int.dart.aot.expect @@ -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([@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([@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([@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([@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 return new ffi::Array::_( 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 ?{core::Object} [@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress([@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(#typedDataBase) in throw "Attempt to execute code removed by Dart AOT compiler (TFA)", #C23, #C28); [@vm.unboxing-info.metadata=()->i] @#C10 diff --git a/pkg/vm/testcases/transformations/ffi/abi_specific_int.dart.expect b/pkg/vm/testcases/transformations/ffi/abi_specific_int.dart.expect index 46b1e31598fc..c91fbced3c5b 100644 --- a/pkg/vm/testcases/transformations/ffi/abi_specific_int.dart.expect +++ b/pkg/vm/testcases/transformations/ffi/abi_specific_int.dart.expect @@ -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(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 return new ffi::Array::_( block { diff --git a/pkg/vm/testcases/transformations/ffi/abi_specific_int_incomplete.dart.aot.expect b/pkg/vm/testcases/transformations/ffi/abi_specific_int_incomplete.dart.aot.expect index f1f9958f2396..6ace5e870fc1 100644 --- a/pkg/vm/testcases/transformations/ffi/abi_specific_int_incomplete.dart.aot.expect +++ b/pkg/vm/testcases/transformations/ffi/abi_specific_int_incomplete.dart.aot.expect @@ -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([@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([@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([@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([@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(#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 return new ffi::Array::_( 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 ?{core::Object} [@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress([@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(#typedDataBase) in throw "Attempt to execute code removed by Dart AOT compiler (TFA)", #C20, #C25); [@vm.unboxing-info.metadata=()->i] @#C8 diff --git a/pkg/vm/testcases/transformations/ffi/abi_specific_int_incomplete.dart.expect b/pkg/vm/testcases/transformations/ffi/abi_specific_int_incomplete.dart.expect index ad3e7c2fe66e..2fc930463614 100644 --- a/pkg/vm/testcases/transformations/ffi/abi_specific_int_incomplete.dart.expect +++ b/pkg/vm/testcases/transformations/ffi/abi_specific_int_incomplete.dart.expect @@ -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(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 return new ffi::Array::_( block { diff --git a/pkg/vm/testcases/transformations/ffi/compound_copies.dart.expect b/pkg/vm/testcases/transformations/ffi/compound_copies.dart.expect index d515a6b577e3..65a96ce49e42 100644 --- a/pkg/vm/testcases/transformations/ffi/compound_copies.dart.expect +++ b/pkg/vm/testcases/transformations/ffi/compound_copies.dart.expect @@ -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}; diff --git a/pkg/vm/testcases/transformations/ffi/ffinative_compound_return.dart.aot.expect b/pkg/vm/testcases/transformations/ffi/ffinative_compound_return.dart.aot.expect index c0e8a49aace1..90dc1c9d3f8c 100644 --- a/pkg/vm/testcases/transformations/ffi/ffinative_compound_return.dart.aot.expect +++ b/pkg/vm/testcases/transformations/ffi/ffinative_compound_return.dart.aot.expect @@ -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}})"; } diff --git a/pkg/vm/testcases/transformations/ffi/ffinative_compound_return.dart.expect b/pkg/vm/testcases/transformations/ffi/ffinative_compound_return.dart.expect index 8365d081fc05..e552ff1f00dc 100644 --- a/pkg/vm/testcases/transformations/ffi/ffinative_compound_return.dart.expect +++ b/pkg/vm/testcases/transformations/ffi/ffinative_compound_return.dart.expect @@ -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*}); diff --git a/pkg/vm/testcases/transformations/ffi/native_fields.dart.aot.expect b/pkg/vm/testcases/transformations/ffi/native_fields.dart.aot.expect index e731b849eca7..4b932c13a08a 100644 --- a/pkg/vm/testcases/transformations/ffi/native_fields.dart.aot.expect +++ b/pkg/vm/testcases/transformations/ffi/native_fields.dart.aot.expect @@ -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 #externalFieldValue) → void - return ffi::_storePointer([@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([@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 diff --git a/pkg/vm/testcases/transformations/ffi/native_fields.dart.expect b/pkg/vm/testcases/transformations/ffi/native_fields.dart.expect index ef88361ba397..b3b2a38ccfbc 100644 --- a/pkg/vm/testcases/transformations/ffi/native_fields.dart.expect +++ b/pkg/vm/testcases/transformations/ffi/native_fields.dart.expect @@ -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}; diff --git a/pkg/vm/testcases/transformations/ffi/struct_typed_data.dart b/pkg/vm/testcases/transformations/ffi/struct_typed_data.dart new file mode 100644 index 000000000000..d6aa42606ad6 --- /dev/null +++ b/pkg/vm/testcases/transformations/ffi/struct_typed_data.dart @@ -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(); + if (x != null) result.x = x; + if (y != null) result.y = y; + return result; + } + + factory Coordinate.fromTypedList(TypedData typedList) { + return Struct.create(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); +} diff --git a/pkg/vm/testcases/transformations/ffi/struct_typed_data.dart.aot.expect b/pkg/vm/testcases/transformations/ffi/struct_typed_data.dart.aot.expect new file mode 100644 index 000000000000..261bb1b0f194 --- /dev/null +++ b/pkg/vm/testcases/transformations/ffi/struct_typed_data.dart.aot.expect @@ -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 = [#C2, #C2] + #C4 = null + #C5 = ffi::_FfiStructLayout {fieldTypes:#C3, packing:#C4} + #C6 = core::pragma {name:#C1, options:#C5} + #C7 = 0 + #C8 = [#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 = [#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 = [#C13, #C13, #C13, #C13, #C13, #C13, #C13, #C13, #C13, #C13, #C13, #C13, #C13, #C13, #C13, #C13, #C13, #C13, #C13, #C13, #C13, #C13] +} diff --git a/pkg/vm/testcases/transformations/ffi/struct_typed_data.dart.expect b/pkg/vm/testcases/transformations/ffi/struct_typed_data.dart.expect new file mode 100644 index 000000000000..51788d982bb6 --- /dev/null +++ b/pkg/vm/testcases/transformations/ffi/struct_typed_data.dart.expect @@ -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 = [#C2, #C2] + #C4 = null + #C5 = ffi::_FfiStructLayout {fieldTypes:#C3, packing:#C4} + #C6 = core::pragma {name:#C1, options:#C5} + #C7 = 0 + #C8 = ffi::Double {} + #C9 = [#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 = [#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 = [#C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14] +} diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/ffi_struct_constructors.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/ffi_struct_constructors.dart.expect index 14b69615e516..9ec8e53b8126 100644 --- a/pkg/vm/testcases/transformations/type_flow/transformer/ffi_struct_constructors.dart.expect +++ b/pkg/vm/testcases/transformations/type_flow/transformer/ffi_struct_constructors.dart.expect @@ -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 ?{core::Object} [@vm.inferred-type.metadata=dart.ffi::Pointer] ffi::_fromAddress([@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(#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}); } diff --git a/sdk/lib/ffi/struct.dart b/sdk/lib/ffi/struct.dart index e627978b9daf..bed1554dac57 100644 --- a/sdk/lib/ffi/struct.dart +++ b/sdk/lib/ffi/struct.dart @@ -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([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 diff --git a/sdk/lib/ffi/union.dart b/sdk/lib/ffi/union.dart index c6a9167c3ebb..cf743ffdccea 100644 --- a/sdk/lib/ffi/union.dart +++ b/sdk/lib/ffi/union.dart @@ -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([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(); } diff --git a/tests/ffi/data_test.dart b/tests/ffi/data_test.dart index 306a34451417..1650236c8f62 100644 --- a/tests/ffi/data_test.dart +++ b/tests/ffi/data_test.dart @@ -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(); diff --git a/tests/ffi/structs_typed_data_test.dart b/tests/ffi/structs_typed_data_test.dart new file mode 100644 index 000000000000..bc01047be4ec --- /dev/null +++ b/tests/ffi/structs_typed_data_test.dart @@ -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(); + 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(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() + ..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(); + 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()); + final c1 = Coordinate.fromTypedList(typedList) + ..x = 4 + ..y = 6; + final view = Uint8List.view(typedList.buffer, 16); + Expect.equals(8, view.lengthInBytes); + Expect.throws(() { + Coordinate.fromTypedList(view) + ..x = 6 + ..y = 8; + }); + Expect.throws(() { + Coordinate.fromTypedList(typedList, 16) + ..x = 6 + ..y = 8; + }); + Expect.throws(() { + // 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()` and wraps it in + /// [MyUnion]. + factory MyUnion.a(int a) { + return Union.create()..a = a; + } + + /// Allocates a new [TypedData] of size `sizeOf()` and wraps it in + /// [MyUnion]. + factory MyUnion.b(double b) { + return Union.create()..b = b; + } + + /// Constructs a [MyUnion] view on [typedList]. + factory MyUnion.fromTypedData(TypedData typedList) { + return Union.create(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()..a = 123; + Expect.equals(123, myUnion2.a); + Expect.approxEquals(1.723597111119525e-43, myUnion2.b); +} diff --git a/tests/ffi/vmspecific_static_checks_test.dart b/tests/ffi/vmspecific_static_checks_test.dart index 52b70d9d8108..fbe79521d7ad 100644 --- a/tests/ffi/vmspecific_static_checks_test.dart +++ b/tests/ffi/vmspecific_static_checks_test.dart @@ -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(); + // ^^^^^^^^^^^^^^^^^^^^^^^ + // [analyzer] COMPILE_TIME_ERROR.NON_CONSTANT_TYPE_ARGUMENT + // ^ + // [cfe] Expected type 'Struct' to be a valid and instantiated subtype of 'NativeType'. + /**/ Union.create(); + // ^^^^^^^^^^^^^^^^^^^^^ + // [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(); Pointer structPointer = myStructPointer;