Skip to content

Commit 466fa55

Browse files
halildurmusCommit Queue
authored andcommitted
[vm/ffi] Add refWithFinalizer methods to StructPointer and UnionPointer
TEST=tests/ffi/extension_methods_test.dart CoreLibraryReviewExempt: VM only Closes: #56796 Change-Id: I29af1e6b61b5a07887f00b3a924f1b354ffb0bbf Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/399600 Reviewed-by: Brian Wilkerson <[email protected]> Reviewed-by: Daco Harkes <[email protected]> Commit-Queue: Daco Harkes <[email protected]>
1 parent 9f82b85 commit 466fa55

File tree

13 files changed

+319
-8
lines changed

13 files changed

+319
-8
lines changed

pkg/analyzer/lib/src/generated/ffi_verifier.dart

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,11 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
355355
if (element.name3 == 'lookupFunction') {
356356
_validateLookupFunction(node);
357357
}
358+
} else if (enclosingElement.isNativeStructPointerExtension ||
359+
enclosingElement.isNativeUnionPointerExtension) {
360+
if (element.name3 == 'refWithFinalizer') {
361+
_validateRefWithFinalizer(node);
362+
}
358363
}
359364
} else if (element is TopLevelFunctionElement) {
360365
if (element.library2.name3 == 'dart.ffi') {
@@ -1929,6 +1934,20 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
19291934
}
19301935
}
19311936

1937+
/// Validate the invocation of the
1938+
/// `Pointer<T extends Struct>.refWithFinalizer` and
1939+
/// `Pointer<T extends Union>.refWithFinalizer` extension methods.
1940+
void _validateRefWithFinalizer(MethodInvocation node) {
1941+
var targetType = node.realTarget?.staticType;
1942+
if (!_isValidFfiNativeType(targetType, allowEmptyStruct: true)) {
1943+
_errorReporter.atNode(
1944+
node,
1945+
FfiCode.NON_CONSTANT_TYPE_ARGUMENT,
1946+
arguments: ['refWithFinalizer'],
1947+
);
1948+
}
1949+
}
1950+
19321951
void _validateSizeOf(MethodInvocation node) {
19331952
var typeArgumentTypes = node.typeArgumentTypes;
19341953
if (typeArgumentTypes == null || typeArgumentTypes.length != 1) {

pkg/analyzer/lib/src/test_utilities/mock_sdk.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -998,6 +998,12 @@ extension StructPointer<T extends Struct> on Pointer<T> {
998998
external T get ref;
999999
10001000
external T operator [](int index);
1001+
1002+
@Since('3.7')
1003+
external T refWithFinalizer(
1004+
Pointer<NativeFinalizerFunction> finalizer, {
1005+
Pointer<Void>? token,
1006+
});
10011007
}
10021008
10031009
@Since('2.19')
@@ -1084,6 +1090,9 @@ abstract interface class Finalizable {
10841090
factory Finalizable._() => throw UnsupportedError("");
10851091
}
10861092
1093+
typedef NativeFinalizerFunction =
1094+
NativeFunction<Void Function(Pointer<Void> token)>;
1095+
10871096
@Since('3.0')
10881097
abstract final class VarArgs<T extends Record> implements NativeType {}
10891098

pkg/analyzer/test/src/diagnostics/non_constant_type_argument_test.dart

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,46 @@ T genericRef<T extends Struct>(Pointer<T> p) =>
7474
p.ref;
7575
''', [error(FfiCode.NON_CONSTANT_TYPE_ARGUMENT, 72, 5)]);
7676
}
77+
78+
test_refWithFinalizer_class() async {
79+
await assertNoErrorsInCode(r'''
80+
import 'dart:ffi';
81+
82+
final class MyStruct extends Struct {
83+
@Uint8()
84+
external int myField;
85+
}
86+
87+
void main() {
88+
final pointer = Pointer<MyStruct>.fromAddress(0);
89+
pointer.refWithFinalizer(nullptr).myField = 1;
90+
}
91+
''');
92+
}
93+
94+
test_refWithFinalizer_class_cascade() async {
95+
await assertNoErrorsInCode(r'''
96+
import 'dart:ffi';
97+
98+
final class MyStruct extends Struct {
99+
@Uint8()
100+
external int myField;
101+
}
102+
103+
void main() {
104+
final pointer = Pointer<MyStruct>.fromAddress(0)
105+
..refWithFinalizer(nullptr).myField = 1;
106+
print(pointer);
107+
}
108+
''');
109+
}
110+
111+
test_refWithFinalizer_typeParameter() async {
112+
await assertErrorsInCode(r'''
113+
import 'dart:ffi';
114+
115+
T genericRefWithFinalizer<T extends Struct>(Pointer<T> p) =>
116+
p.refWithFinalizer(nullptr);
117+
''', [error(FfiCode.NON_CONSTANT_TYPE_ARGUMENT, 85, 27)]);
118+
}
77119
}

pkg/vm/lib/modular/transformations/ffi/common.dart

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,8 @@ class FfiTransformer extends Transformer {
252252
final Procedure addressGetter;
253253
final Procedure structPointerGetRef;
254254
final Procedure structPointerSetRef;
255+
final Procedure structPointerRefWithFinalizer;
256+
final Procedure structPointerRefWithFinalizerTearoff;
255257
final Procedure structPointerGetElemAt;
256258
final Procedure structPointerSetElemAt;
257259
final Procedure structPointerElementAt;
@@ -260,12 +262,15 @@ class FfiTransformer extends Transformer {
260262
final Procedure structPointerElementAtTearoff;
261263
final Procedure unionPointerGetRef;
262264
final Procedure unionPointerSetRef;
265+
final Procedure unionPointerRefWithFinalizer;
266+
final Procedure unionPointerRefWithFinalizerTearoff;
263267
final Procedure unionPointerGetElemAt;
264268
final Procedure unionPointerSetElemAt;
265269
final Procedure unionPointerElementAt;
266270
final Procedure unionPointerPlusOperator;
267271
final Procedure unionPointerMinusOperator;
268272
final Procedure unionPointerElementAtTearoff;
273+
final Procedure uint8PointerAsTypedList;
269274
final Procedure structArrayElemAt;
270275
final Procedure unionArrayElemAt;
271276
final Procedure arrayArrayElemAt;
@@ -355,6 +360,7 @@ class FfiTransformer extends Transformer {
355360
late final InterfaceType nativeTypeType;
356361
// The Pointer type when instantiated to bounds.
357362
late final InterfaceType pointerNativeTypeType;
363+
late final InterfaceType uint8Type;
358364
late final InterfaceType compoundType;
359365

360366
/// Classes corresponding to [NativeType], indexed by [NativeType].
@@ -494,6 +500,10 @@ class FfiTransformer extends Transformer {
494500
index.getProcedure('dart:ffi', 'StructPointer', 'get:ref'),
495501
structPointerSetRef =
496502
index.getProcedure('dart:ffi', 'StructPointer', 'set:ref'),
503+
structPointerRefWithFinalizer =
504+
index.getProcedure('dart:ffi', 'StructPointer', 'refWithFinalizer'),
505+
structPointerRefWithFinalizerTearoff = index.getProcedure('dart:ffi',
506+
'StructPointer', LibraryIndex.tearoffPrefix + 'refWithFinalizer'),
497507
structPointerGetElemAt =
498508
index.getProcedure('dart:ffi', 'StructPointer', '[]'),
499509
structPointerSetElemAt =
@@ -510,6 +520,10 @@ class FfiTransformer extends Transformer {
510520
index.getProcedure('dart:ffi', 'UnionPointer', 'get:ref'),
511521
unionPointerSetRef =
512522
index.getProcedure('dart:ffi', 'UnionPointer', 'set:ref'),
523+
unionPointerRefWithFinalizer =
524+
index.getProcedure('dart:ffi', 'UnionPointer', 'refWithFinalizer'),
525+
unionPointerRefWithFinalizerTearoff = index.getProcedure('dart:ffi',
526+
'UnionPointer', LibraryIndex.tearoffPrefix + 'refWithFinalizer'),
513527
unionPointerGetElemAt =
514528
index.getProcedure('dart:ffi', 'UnionPointer', '[]'),
515529
unionPointerSetElemAt =
@@ -522,6 +536,8 @@ class FfiTransformer extends Transformer {
522536
index.getProcedure('dart:ffi', 'UnionPointer', '-'),
523537
unionPointerElementAtTearoff = index.getProcedure('dart:ffi',
524538
'UnionPointer', LibraryIndex.tearoffPrefix + 'elementAt'),
539+
uint8PointerAsTypedList =
540+
index.getProcedure('dart:ffi', 'Uint8Pointer', 'asTypedList'),
525541
structArrayElemAt = index.getProcedure('dart:ffi', 'StructArray', '[]'),
526542
unionArrayElemAt = index.getProcedure('dart:ffi', 'UnionArray', '[]'),
527543
arrayArrayElemAt = index.getProcedure('dart:ffi', 'ArrayArray', '[]'),
@@ -676,6 +692,8 @@ class FfiTransformer extends Transformer {
676692
intptrNativeTypeCfe =
677693
NativeTypeCfe(this, InterfaceType(intptrClass, Nullability.nonNullable))
678694
as AbiSpecificNativeTypeCfe;
695+
uint8Type = InterfaceType(
696+
nativeTypesClasses[NativeType.kUint8]!, Nullability.nonNullable);
679697
compoundType = InterfaceType(
680698
compoundClass,
681699
Nullability.nonNullable,

pkg/vm/lib/modular/transformations/ffi/use_sites.dart

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,9 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
210210
node == lookupFunctionTearoff ||
211211
node == abiSpecificIntegerPointerElementAtTearoff ||
212212
node == structPointerElementAtTearoff ||
213-
node == unionPointerElementAtTearoff))) ||
213+
node == structPointerRefWithFinalizerTearoff ||
214+
node == unionPointerElementAtTearoff ||
215+
node == unionPointerRefWithFinalizerTearoff))) ||
214216
// Dart2wasm uses enabledConstructorTearOffLowerings but these are not
215217
// users trying to call constructors.
216218
isConstructorTearOffLowering(node);
@@ -337,6 +339,13 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
337339
ensureNativeTypeValid(nativeType, node, allowStructAndUnion: true);
338340

339341
return _replaceSetRef(node);
342+
} else if (target == structPointerRefWithFinalizer ||
343+
target == unionPointerRefWithFinalizer) {
344+
final DartType nativeType = node.arguments.types[0];
345+
346+
ensureNativeTypeValid(nativeType, node, allowStructAndUnion: true);
347+
348+
return _replaceRefWithFinalizer(node);
340349
} else if (target == abiSpecificIntegerPointerElementAt ||
341350
target == structPointerElementAt ||
342351
target == unionPointerElementAt ||
@@ -1050,6 +1059,52 @@ mixin _FfiUseSiteTransformer on FfiTransformer {
10501059
);
10511060
}
10521061

1062+
/// Replaces a `.refWithFinalizer` on a compound pointer extension with a
1063+
/// typed data view.
1064+
Expression _replaceRefWithFinalizer(StaticInvocation node) {
1065+
final dartType = node.arguments.types[0] as InterfaceType;
1066+
final constructors = dartType.classNode.constructors;
1067+
final fromTypedDataCtor =
1068+
constructors.firstWhere((c) => c.name == Name("#fromTypedData"));
1069+
// args: (receiver, finalizer, {token})
1070+
final pointer = node.arguments.positional[0];
1071+
final finalizer =
1072+
NamedExpression('finalizer', node.arguments.positional[1]);
1073+
NamedExpression? token;
1074+
if (node.arguments.named.isNotEmpty) {
1075+
token = node.arguments.named[0];
1076+
}
1077+
1078+
final cast = InstanceInvocation(
1079+
InstanceAccessKind.Instance,
1080+
pointer,
1081+
castMethod.name,
1082+
Arguments(const [], types: [uint8Type]),
1083+
interfaceTarget: castMethod,
1084+
functionType: FunctionTypeInstantiator.instantiate(
1085+
castMethod.getterType as FunctionType,
1086+
[uint8Type],
1087+
),
1088+
);
1089+
1090+
return ConstructorInvocation(
1091+
fromTypedDataCtor,
1092+
Arguments(
1093+
[
1094+
StaticInvocation(
1095+
uint8PointerAsTypedList,
1096+
Arguments(
1097+
[cast, inlineSizeOf(dartType)!],
1098+
named: [finalizer, if (token != null) token],
1099+
),
1100+
),
1101+
ConstantExpression(IntConstant(0)),
1102+
inlineSizeOf(dartType)!,
1103+
],
1104+
),
1105+
);
1106+
}
1107+
10531108
Expression _replaceRefArray(StaticInvocation node) {
10541109
final dartType = node.arguments.types[0];
10551110
final clazz = (dartType as InterfaceType).classNode;

pkg/vm/testcases/transformations/ffi/compound_copies.dart renamed to pkg/vm/testcases/transformations/ffi/compound_references.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@ final class Coordinate extends Struct {
1010
void copyInto(Pointer<Coordinate> ptr) {
1111
ptr.ref = this;
1212
}
13+
14+
void getRefWithFinalizer(
15+
Pointer<Coordinate> ptr,
16+
Pointer<NativeFinalizerFunction> finalizer, {
17+
Pointer<Void>? token,
18+
}) {
19+
ptr.refWithFinalizer(finalizer, token: token);
20+
}
1321
}
1422

1523
final class SomeUnion extends Union {
@@ -20,6 +28,14 @@ final class SomeUnion extends Union {
2028
void copyIntoAtIndex(Pointer<SomeUnion> ptr, int index) {
2129
ptr[index] = this;
2230
}
31+
32+
void getRefWithFinalizer(
33+
Pointer<SomeUnion> ptr,
34+
Pointer<NativeFinalizerFunction> finalizer, {
35+
Pointer<Void>? token,
36+
}) {
37+
ptr.refWithFinalizer(finalizer, token: token);
38+
}
2339
}
2440

2541
void main() {}

pkg/vm/testcases/transformations/ffi/compound_copies.dart.expect renamed to pkg/vm/testcases/transformations/ffi/compound_references.dart.expect

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ final class Coordinate extends ffi::Struct {
3939
synthesized self::Coordinate #source = this;
4040
} =>ffi::_memCopy(ptr, #C10, #source.{ffi::_Compound::_typedDataBase}{core::Object}, #source.{ffi::_Compound::_offsetInBytes}{core::int}, self::Coordinate::#sizeOf);
4141
}
42+
method getRefWithFinalizer(ffi::Pointer<self::Coordinate> ptr, ffi::Pointer<ffi::NativeFunction<(ffi::Pointer<ffi::Void>) → ffi::Void>> finalizer, {ffi::Pointer<ffi::Void>? token = #C4}) → void {
43+
new self::Coordinate::#fromTypedData(ffi::Uint8Pointer|asTypedList(ptr.{ffi::Pointer::cast}<ffi::Uint8>(){() → ffi::Pointer<ffi::Uint8>}, self::Coordinate::#sizeOf, finalizer: finalizer, token: token), #C10, self::Coordinate::#sizeOf);
44+
}
4245
@#C8
4346
static get x#offsetOf() → core::int
4447
return #C11.{core::List::[]}(ffi::_abi()){(core::int) → core::int};
@@ -80,6 +83,9 @@ final class SomeUnion extends ffi::Union {
8083
synthesized self::SomeUnion #source = this;
8184
} =>ffi::_memCopy(ptr, index.{core::num::*}(self::SomeUnion::#sizeOf){(core::num) → core::num}, #source.{ffi::_Compound::_typedDataBase}{core::Object}, #source.{ffi::_Compound::_offsetInBytes}{core::int}, self::SomeUnion::#sizeOf);
8285
}
86+
method getRefWithFinalizer(ffi::Pointer<self::SomeUnion> ptr, ffi::Pointer<ffi::NativeFunction<(ffi::Pointer<ffi::Void>) → ffi::Void>> finalizer, {ffi::Pointer<ffi::Void>? token = #C4}) → void {
87+
new self::SomeUnion::#fromTypedData(ffi::Uint8Pointer|asTypedList(ptr.{ffi::Pointer::cast}<ffi::Uint8>(){() → ffi::Pointer<ffi::Uint8>}, self::SomeUnion::#sizeOf, finalizer: finalizer, token: token), #C10, self::SomeUnion::#sizeOf);
88+
}
8389
@#C8
8490
static get coordinate#offsetOf() → core::int
8591
return #C11.{core::List::[]}(ffi::_abi()){(core::int) → core::int};

runtime/bin/ffi_test/ffi_test_functions.cc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1358,4 +1358,21 @@ DART_EXPORT char TakeString(char* my_string) {
13581358
return my_string[4];
13591359
}
13601360

1361+
struct Vec4 {
1362+
double x;
1363+
double y;
1364+
double z;
1365+
double w;
1366+
};
1367+
1368+
// Rearranges the components of the input Vec4 and stores them in the result
1369+
// Vec4.
1370+
// [x, y, z, w] -> [y, z, w, x]
1371+
DART_EXPORT void TwiddleVec4Components(Vec4 input, Vec4* result) {
1372+
result->x = input.y;
1373+
result->y = input.z;
1374+
result->z = input.w;
1375+
result->w = input.x;
1376+
}
1377+
13611378
} // namespace dart

sdk/lib/_internal/vm/lib/ffi_patch.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1279,6 +1279,12 @@ extension StructPointer<T extends Struct> on Pointer<T> {
12791279
set ref(T value) =>
12801280
throw "UNREACHABLE: This case should have been rewritten in the CFE";
12811281

1282+
@patch
1283+
T refWithFinalizer(
1284+
Pointer<NativeFinalizerFunction> finalizer, {
1285+
Pointer<Void>? token,
1286+
}) => throw "UNREACHABLE: This case should have been rewritten in the CFE.";
1287+
12821288
@patch
12831289
T operator [](int index) =>
12841290
throw "UNREACHABLE: This case should have been rewritten in the CFE.";
@@ -1310,6 +1316,12 @@ extension UnionPointer<T extends Union> on Pointer<T> {
13101316
set ref(T value) =>
13111317
throw "UNREACHABLE: This case should have been rewritten in the CFE";
13121318

1319+
@patch
1320+
T refWithFinalizer(
1321+
Pointer<NativeFinalizerFunction> finalizer, {
1322+
Pointer<Void>? token,
1323+
}) => throw "UNREACHABLE: This case should have been rewritten in the CFE.";
1324+
13131325
@patch
13141326
T operator [](int index) =>
13151327
throw "UNREACHABLE: This case should have been rewritten in the CFE.";

0 commit comments

Comments
 (0)