Skip to content

Commit 0a3d896

Browse files
simolus3Commit Bot
authored and
Commit Bot
committed
[ffi] Add .ref= setter for pointers of structs or unions
Adds the `ref` setter to the `StructPointer` and `UnionPointer` extensions, copying a compound structure into native memory. The FFI use-site transformation transforms invocations of this setter into an appropriate memcopy call. Closes #44768 TEST=tests/ffi(_2)/extension_methods_test.dart Change-Id: I3ef06ad08b8e71e39b05d662e8082fc5d0ad876d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/227542 Reviewed-by: Daco Harkes <[email protected]> Commit-Queue: Daco Harkes <[email protected]>
1 parent 981c16e commit 0a3d896

File tree

9 files changed

+320
-24
lines changed

9 files changed

+320
-24
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
- Add `Finalizer` and `WeakReference` which can potentially detect when
88
objects are "garbage collected".
99

10+
#### `dart:ffi`
11+
12+
- Add `ref=` and `[]=` methods to the `StructPointer` and `UnionPointer`
13+
extensions. They copy a compound instance into a native memory region.
14+
1015
#### `dart:indexed_db`
1116

1217
- `IdbFactory.supportsDatabaseNames` has been deprecated. It will always return

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

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -194,10 +194,14 @@ class FfiTransformer extends Transformer {
194194
final Procedure offsetByMethod;
195195
final Procedure elementAtMethod;
196196
final Procedure addressGetter;
197-
final Procedure structPointerRef;
198-
final Procedure structPointerElemAt;
199-
final Procedure unionPointerRef;
200-
final Procedure unionPointerElemAt;
197+
final Procedure structPointerGetRef;
198+
final Procedure structPointerSetRef;
199+
final Procedure structPointerGetElemAt;
200+
final Procedure structPointerSetElemAt;
201+
final Procedure unionPointerGetRef;
202+
final Procedure unionPointerSetRef;
203+
final Procedure unionPointerGetElemAt;
204+
final Procedure unionPointerSetElemAt;
201205
final Procedure structArrayElemAt;
202206
final Procedure unionArrayElemAt;
203207
final Procedure arrayArrayElemAt;
@@ -371,14 +375,22 @@ class FfiTransformer extends Transformer {
371375
arrayConstructor = index.getConstructor('dart:ffi', 'Array', '_'),
372376
fromAddressInternal =
373377
index.getTopLevelProcedure('dart:ffi', '_fromAddress'),
374-
structPointerRef =
378+
structPointerGetRef =
375379
index.getProcedure('dart:ffi', 'StructPointer', 'get:ref'),
376-
structPointerElemAt =
380+
structPointerSetRef =
381+
index.getProcedure('dart:ffi', 'StructPointer', 'set:ref'),
382+
structPointerGetElemAt =
377383
index.getProcedure('dart:ffi', 'StructPointer', '[]'),
378-
unionPointerRef =
384+
structPointerSetElemAt =
385+
index.getProcedure('dart:ffi', 'StructPointer', '[]='),
386+
unionPointerGetRef =
379387
index.getProcedure('dart:ffi', 'UnionPointer', 'get:ref'),
380-
unionPointerElemAt =
388+
unionPointerSetRef =
389+
index.getProcedure('dart:ffi', 'UnionPointer', 'set:ref'),
390+
unionPointerGetElemAt =
381391
index.getProcedure('dart:ffi', 'UnionPointer', '[]'),
392+
unionPointerSetElemAt =
393+
index.getProcedure('dart:ffi', 'UnionPointer', '[]='),
382394
structArrayElemAt = index.getProcedure('dart:ffi', 'StructArray', '[]'),
383395
unionArrayElemAt = index.getProcedure('dart:ffi', 'UnionArray', '[]'),
384396
arrayArrayElemAt = index.getProcedure('dart:ffi', 'ArrayArray', '[]'),

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

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -169,15 +169,24 @@ class _FfiUseSiteTransformer extends FfiTransformer {
169169
fileOffset: node.fileOffset,
170170
);
171171
}
172-
if (target == structPointerRef ||
173-
target == structPointerElemAt ||
174-
target == unionPointerRef ||
175-
target == unionPointerElemAt) {
172+
if (target == structPointerGetRef ||
173+
target == structPointerGetElemAt ||
174+
target == unionPointerGetRef ||
175+
target == unionPointerGetElemAt) {
176176
final DartType nativeType = node.arguments.types[0];
177177

178178
_ensureNativeTypeValid(nativeType, node, allowCompounds: true);
179179

180-
return _replaceRef(node);
180+
return _replaceGetRef(node);
181+
} else if (target == structPointerSetRef ||
182+
target == structPointerSetElemAt ||
183+
target == unionPointerSetRef ||
184+
target == unionPointerSetElemAt) {
185+
final DartType nativeType = node.arguments.types[0];
186+
187+
_ensureNativeTypeValid(nativeType, node, allowCompounds: true);
188+
189+
return _replaceSetRef(node);
181190
} else if (target == structArrayElemAt || target == unionArrayElemAt) {
182191
final DartType nativeType = node.arguments.types[0];
183192

@@ -533,7 +542,7 @@ class _FfiUseSiteTransformer extends FfiTransformer {
533542
return StaticGet(field);
534543
}
535544

536-
Expression _replaceRef(StaticInvocation node) {
545+
Expression _replaceGetRef(StaticInvocation node) {
537546
final dartType = node.arguments.types[0];
538547
final clazz = (dartType as InterfaceType).classNode;
539548
final constructor = clazz.constructors
@@ -555,6 +564,38 @@ class _FfiUseSiteTransformer extends FfiTransformer {
555564
return ConstructorInvocation(constructor, Arguments([pointer]));
556565
}
557566

567+
/// Replaces a `.ref=` or `[]=` on a compound pointer extension with a memcopy
568+
/// call.
569+
Expression _replaceSetRef(StaticInvocation node) {
570+
final target = node.arguments.positional[0]; // Receiver of extension
571+
572+
final Expression source, targetOffset;
573+
574+
if (node.arguments.positional.length == 3) {
575+
// []= call, args are (receiver, index, source)
576+
source = getCompoundTypedDataBaseField(
577+
node.arguments.positional[2], node.fileOffset);
578+
targetOffset = multiply(node.arguments.positional[1],
579+
_inlineSizeOf(node.arguments.types[0] as InterfaceType)!);
580+
} else {
581+
// .ref= call, args are (receiver, source)
582+
source = getCompoundTypedDataBaseField(
583+
node.arguments.positional[1], node.fileOffset);
584+
targetOffset = ConstantExpression(IntConstant(0));
585+
}
586+
587+
return StaticInvocation(
588+
memCopy,
589+
Arguments([
590+
target,
591+
targetOffset,
592+
source,
593+
ConstantExpression(IntConstant(0)),
594+
_inlineSizeOf(node.arguments.types[0] as InterfaceType)!,
595+
]),
596+
);
597+
}
598+
558599
Expression _replaceRefArray(StaticInvocation node) {
559600
final dartType = node.arguments.types[0];
560601
final clazz = (dartType as InterfaceType).classNode;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import 'dart:ffi';
2+
3+
import 'package:ffi/ffi.dart';
4+
5+
class Coordinate extends Struct {
6+
@Int64()
7+
external int x;
8+
9+
@Int64()
10+
external int y;
11+
12+
void copyInto(Pointer<Coordinate> ptr) {
13+
ptr.ref = this;
14+
}
15+
}
16+
17+
class SomeUnion extends Union {
18+
external Coordinate coordinate;
19+
@Int64()
20+
external int id;
21+
22+
void copyIntoAtIndex(Pointer<SomeUnion> ptr, int index) {
23+
ptr[index] = this;
24+
}
25+
}
26+
27+
void main() {}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
library #lib /*isNonNullableByDefault*/;
2+
import self as self;
3+
import "dart:core" as core;
4+
import "dart:ffi" as ffi;
5+
import "dart:typed_data" as typ;
6+
import "dart:_internal" as _in;
7+
8+
import "dart:ffi";
9+
import "package:ffi/ffi.dart";
10+
11+
@#C6
12+
class Coordinate extends ffi::Struct {
13+
synthetic constructor •() → self::Coordinate
14+
: super ffi::Struct::•()
15+
;
16+
constructor #fromTypedDataBase(core::Object #typedDataBase) → self::Coordinate
17+
: super ffi::Struct::_fromTypedDataBase(#typedDataBase)
18+
;
19+
@#C7
20+
get x() → core::int
21+
return ffi::_loadInt64(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C9.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
22+
@#C7
23+
set x(core::int #externalFieldValue) → void
24+
return ffi::_storeInt64(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C9.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue);
25+
@#C7
26+
get y() → core::int
27+
return ffi::_loadInt64(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C11.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
28+
@#C7
29+
set y(core::int #externalFieldValue) → void
30+
return ffi::_storeInt64(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C11.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue);
31+
method copyInto(ffi::Pointer<self::Coordinate> ptr) → void {
32+
ffi::_memCopy(ptr, #C8, this.{ffi::_Compound::_typedDataBase}{core::Object}, #C8, self::Coordinate::#sizeOf);
33+
}
34+
@#C13
35+
static get #sizeOf() → core::int*
36+
return #C15.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
37+
}
38+
@#C19
39+
class SomeUnion extends ffi::Union {
40+
synthetic constructor •() → self::SomeUnion
41+
: super ffi::Union::•()
42+
;
43+
constructor #fromTypedDataBase(core::Object #typedDataBase) → self::SomeUnion
44+
: super ffi::Union::_fromTypedDataBase(#typedDataBase)
45+
;
46+
get coordinate() → self::Coordinate
47+
return new self::Coordinate::#fromTypedDataBase( block {
48+
core::Object #typedDataBase = this.{ffi::_Compound::_typedDataBase}{core::Object};
49+
core::int #offset = #C9.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
50+
} =>#typedDataBase is ffi::Pointer<dynamic> ?{core::Object} ffi::_fromAddress<self::Coordinate>(#typedDataBase.{ffi::Pointer::address}{core::int}.{core::num::+}(#offset){(core::num) → core::num}) : let typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in #typedData.{typ::TypedData::buffer}{typ::ByteBuffer}.{typ::ByteBuffer::asUint8List}(#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});
51+
set coordinate(self::Coordinate #externalFieldValue) → void
52+
return ffi::_memCopy(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C9.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue.{ffi::_Compound::_typedDataBase}{core::Object}, #C8, #C15.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
53+
@#C7
54+
get id() → core::int
55+
return ffi::_loadInt64(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C9.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
56+
@#C7
57+
set id(core::int #externalFieldValue) → void
58+
return ffi::_storeInt64(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C9.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue);
59+
method copyIntoAtIndex(ffi::Pointer<self::SomeUnion> ptr, core::int index) → void {
60+
ffi::_memCopy(ptr, index.{core::num::*}(self::SomeUnion::#sizeOf){(core::num) → core::num}, this.{ffi::_Compound::_typedDataBase}{core::Object}, #C8, self::SomeUnion::#sizeOf);
61+
}
62+
@#C13
63+
static get #sizeOf() → core::int*
64+
return #C15.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
65+
}
66+
static method main() → void {}
67+
constants {
68+
#C1 = "vm:ffi:struct-fields"
69+
#C2 = TypeLiteralConstant(ffi::Int64)
70+
#C3 = <core::Type>[#C2, #C2]
71+
#C4 = null
72+
#C5 = ffi::_FfiStructLayout {fieldTypes:#C3, packing:#C4}
73+
#C6 = core::pragma {name:#C1, options:#C5}
74+
#C7 = ffi::Int64 {}
75+
#C8 = 0
76+
#C9 = <core::int*>[#C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8, #C8]
77+
#C10 = 8
78+
#C11 = <core::int*>[#C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10, #C10]
79+
#C12 = "vm:prefer-inline"
80+
#C13 = core::pragma {name:#C12, options:#C4}
81+
#C14 = 16
82+
#C15 = <core::int*>[#C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14, #C14]
83+
#C16 = TypeLiteralConstant(self::Coordinate)
84+
#C17 = <core::Type>[#C16, #C2]
85+
#C18 = ffi::_FfiStructLayout {fieldTypes:#C17, packing:#C4}
86+
#C19 = core::pragma {name:#C1, options:#C18}
87+
}

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -923,19 +923,35 @@ extension StructPointer<T extends Struct> on Pointer<T> {
923923
T get ref =>
924924
throw "UNREACHABLE: This case should have been rewritten in the CFE.";
925925

926+
@patch
927+
set ref(T value) =>
928+
throw "UNREACHABLE: This case should have been rewritten in the CFE";
929+
926930
@patch
927931
T operator [](int index) =>
928932
throw "UNREACHABLE: This case should have been rewritten in the CFE.";
933+
934+
@patch
935+
void operator []=(int index, T value) =>
936+
throw "UNREACHABLE: This case should have been rewritten in the CFE.";
929937
}
930938

931939
extension UnionPointer<T extends Union> on Pointer<T> {
932940
@patch
933941
T get ref =>
934942
throw "UNREACHABLE: This case should have been rewritten in the CFE.";
935943

944+
@patch
945+
set ref(T value) =>
946+
throw "UNREACHABLE: This case should have been rewritten in the CFE";
947+
936948
@patch
937949
T operator [](int index) =>
938950
throw "UNREACHABLE: This case should have been rewritten in the CFE.";
951+
952+
@patch
953+
void operator []=(int index, T value) =>
954+
throw "UNREACHABLE: This case should have been rewritten in the CFE.";
939955
}
940956

941957
extension AbiSpecificIntegerPointer<T extends AbiSpecificInteger>

sdk/lib/ffi/ffi.dart

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -648,44 +648,72 @@ extension PointerPointer<T extends NativeType> on Pointer<Pointer<T>> {
648648

649649
/// Extension on [Pointer] specialized for the type argument [Struct].
650650
extension StructPointer<T extends Struct> on Pointer<T> {
651-
/// Creates a reference to access the fields of this struct backed by native
652-
/// memory at [address].
651+
/// A Dart view of the struct referenced by this pointer.
653652
///
653+
/// Reading [ref] creates a reference accessing the fields of this struct
654+
/// backed by native memory at [address].
654655
/// The [address] must be aligned according to the struct alignment rules of
655656
/// the platform.
656657
///
657-
/// This extension method must be invoked with a compile-time constant [T].
658+
/// Assigning to [ref] copies contents of the struct into the native memory
659+
/// starting at [address].
660+
///
661+
/// This extension method must be invoked on a receiver of type `Pointer<T>`
662+
/// where `T` is a compile-time constant type.
658663
external T get ref;
664+
external set ref(T value);
659665

660666
/// Creates a reference to access the fields of this struct backed by native
661667
/// memory at `address + sizeOf<T>() * index`.
662668
///
663669
/// The [address] must be aligned according to the struct alignment rules of
664670
/// the platform.
665671
///
666-
/// This extension method must be invoked with a compile-time constant [T].
672+
/// This extension method must be invoked on a receiver of type `Pointer<T>`
673+
/// where `T` is a compile-time constant type.
667674
external T operator [](int index);
675+
676+
/// Copies the [value] struct into native memory, starting at
677+
/// `address * sizeOf<T>() * index`.
678+
///
679+
/// This extension method must be invoked on a receiver of type `Pointer<T>`
680+
/// where `T` is a compile-time constant type.
681+
external void operator []=(int index, T value);
668682
}
669683

670684
/// Extension on [Pointer] specialized for the type argument [Union].
671685
extension UnionPointer<T extends Union> on Pointer<T> {
672-
/// Creates a reference to access the fields of this union backed by native
673-
/// memory at [address].
686+
/// A Dart view of the union referenced by this pointer.
674687
///
688+
/// Reading [ref] creates a reference accessing the fields of this union
689+
/// backed by native memory at [address].
675690
/// The [address] must be aligned according to the union alignment rules of
676691
/// the platform.
677692
///
678-
/// This extension method must be invoked with a compile-time constant [T].
693+
/// Assigning to [ref] copies contents of the union into the native memory
694+
/// starting at [address].
695+
///
696+
/// This extension method must be invoked on a receiver of type `Pointer<T>`
697+
/// where `T` is a compile-time constant type.
679698
external T get ref;
699+
external set ref(T value);
680700

681701
/// Creates a reference to access the fields of this union backed by native
682702
/// memory at `address + sizeOf<T>() * index`.
683703
///
684704
/// The [address] must be aligned according to the union alignment rules of
685705
/// the platform.
686706
///
687-
/// This extension method must be invoked with a compile-time constant [T].
707+
/// This extension method must be invoked on a receiver of type `Pointer<T>`
708+
/// where `T` is a compile-time constant type.
688709
external T operator [](int index);
710+
711+
/// Copies the [value] union into native memory, starting at
712+
/// `address * sizeOf<T>() * index`.
713+
///
714+
/// This extension method must be invoked on a receiver of type `Pointer<T>`
715+
/// where `T` is a compile-time constant type.
716+
external void operator []=(int index, T value);
689717
}
690718

691719
/// Extension on [Pointer] specialized for the type argument
@@ -713,13 +741,15 @@ extension PointerArray<T extends NativeType> on Array<Pointer<T>> {
713741

714742
/// Bounds checking indexing methods on [Array]s of [Struct].
715743
extension StructArray<T extends Struct> on Array<T> {
716-
/// This extension method must be invoked with a compile-time constant [T].
744+
/// This extension method must be invoked on a receiver of type `Pointer<T>`
745+
/// where `T` is a compile-time constant type.
717746
external T operator [](int index);
718747
}
719748

720749
/// Bounds checking indexing methods on [Array]s of [Union].
721750
extension UnionArray<T extends Union> on Array<T> {
722-
/// This extension method must be invoked with a compile-time constant [T].
751+
/// This extension method must be invoked on a receiver of type `Pointer<T>`
752+
/// where `T` is a compile-time constant type.
723753
external T operator [](int index);
724754
}
725755

0 commit comments

Comments
 (0)