Skip to content

Commit 2e8f9b8

Browse files
dcharkescommit-bot@chromium.org
authored andcommitted
[vm/ffi] Change Pointer<T extends Struct>.ref to use static type
This CL changes the semantics of `Pointer<T extends Struct>.ref` to use the compile-time `T` rather than the runtime `T`. This enables tree shaking of subtypes of Struct and optimizing `.ref`. Issue: #38721 TEST=tests/ffi/vmspecific_static_checks_test.dart TEST=tests/ffi/*struct*test_.dart Change-Id: I3f5b08c08ec0799ef8aab3c4177e2ac70d25501c Cq-Include-Trybots: luci.dart.try:vm-precomp-ffi-qemu-linux-release-arm-try,analyzer-analysis-server-linux-try,analyzer-linux-release-try,analyzer-nnbd-linux-release-try,front-end-linux-release-x64-try,front-end-nnbd-linux-release-x64-try,benchmark-linux-try,dart-sdk-linux-try,pkg-linux-release-try,vm-ffi-android-release-arm-try,vm-ffi-android-release-arm64-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/177862 Commit-Queue: Daco Harkes <[email protected]> Reviewed-by: Aske Simon Christensen <[email protected]>
1 parent 1a71f94 commit 2e8f9b8

File tree

8 files changed

+149
-9
lines changed

8 files changed

+149
-9
lines changed

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

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,19 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
152152
super.visitFunctionExpressionInvocation(node);
153153
}
154154

155+
@override
156+
void visitIndexExpression(IndexExpression node) {
157+
Element element = node.staticElement;
158+
Element enclosingElement = element?.enclosingElement;
159+
if (enclosingElement is ExtensionElement) {
160+
if (_isNativeStructPointerExtension(enclosingElement)) {
161+
if (element.name == '[]') {
162+
_validateRefIndexed(node);
163+
}
164+
}
165+
}
166+
}
167+
155168
@override
156169
void visitMethodInvocation(MethodInvocation node) {
157170
Element element = node.methodName.staticElement;
@@ -178,6 +191,32 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
178191
super.visitMethodInvocation(node);
179192
}
180193

194+
@override
195+
void visitPrefixedIdentifier(PrefixedIdentifier node) {
196+
Element element = node.staticElement;
197+
Element enclosingElement = element?.enclosingElement;
198+
if (enclosingElement is ExtensionElement) {
199+
if (_isNativeStructPointerExtension(enclosingElement)) {
200+
if (element.name == 'ref') {
201+
_validateRefPrefixedIdentifier(node);
202+
}
203+
}
204+
}
205+
}
206+
207+
@override
208+
void visitPropertyAccess(PropertyAccess node) {
209+
Element element = node.propertyName.staticElement;
210+
Element enclosingElement = element?.enclosingElement;
211+
if (enclosingElement is ExtensionElement) {
212+
if (_isNativeStructPointerExtension(enclosingElement)) {
213+
if (element.name == 'ref') {
214+
_validateRefPropertyAccess(node);
215+
}
216+
}
217+
}
218+
}
219+
181220
/// Return `true` if the given [element] represents the extension
182221
/// `AllocatorAlloc`.
183222
bool _isAllocatorExtension(Element element) =>
@@ -240,6 +279,9 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
240279
element.name == 'NativeFunctionPointer' &&
241280
element.library.name == _dartFfiLibraryName;
242281

282+
bool _isNativeStructPointerExtension(Element element) =>
283+
element.name == 'StructPointer' && element.library.name == 'dart.ffi';
284+
243285
/// Returns `true` iff [nativeType] is a `ffi.NativeType` type.
244286
bool _isNativeTypeInterfaceType(DartType nativeType) {
245287
if (nativeType is InterfaceType) {
@@ -697,6 +739,35 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
697739
}
698740
}
699741

742+
void _validateRefIndexed(IndexExpression node) {
743+
DartType targetType = node.target.staticType;
744+
if (!_isValidFfiNativeType(targetType, false, true)) {
745+
final AstNode errorNode = node;
746+
_errorReporter.reportErrorForNode(
747+
FfiCode.NON_CONSTANT_TYPE_ARGUMENT, errorNode, ['[]']);
748+
}
749+
}
750+
751+
/// Validate the invocation of the extension method
752+
/// `Pointer<T extends Struct>.ref`.
753+
void _validateRefPrefixedIdentifier(PrefixedIdentifier node) {
754+
DartType targetType = node.prefix.staticType;
755+
if (!_isValidFfiNativeType(targetType, false, true)) {
756+
final AstNode errorNode = node;
757+
_errorReporter.reportErrorForNode(
758+
FfiCode.NON_CONSTANT_TYPE_ARGUMENT, errorNode, ['ref']);
759+
}
760+
}
761+
762+
void _validateRefPropertyAccess(PropertyAccess node) {
763+
DartType targetType = node.target.staticType;
764+
if (!_isValidFfiNativeType(targetType, false, true)) {
765+
final AstNode errorNode = node;
766+
_errorReporter.reportErrorForNode(
767+
FfiCode.NON_CONSTANT_TYPE_ARGUMENT, errorNode, ['ref']);
768+
}
769+
}
770+
700771
/// Validate that the given [typeArgument] has a constant value. Return `true`
701772
/// if a diagnostic was produced because it isn't constant.
702773
bool _validateTypeArgument(TypeAnnotation typeArgument, String functionName) {

pkg/vm/lib/transformations/ffi.dart

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,8 @@ class FfiTransformer extends Transformer {
219219
final Procedure offsetByMethod;
220220
final Procedure elementAtMethod;
221221
final Procedure addressGetter;
222+
final Procedure structPointerRef;
223+
final Procedure structPointerElemAt;
222224
final Procedure asFunctionMethod;
223225
final Procedure asFunctionInternal;
224226
final Procedure lookupFunctionMethod;
@@ -282,6 +284,10 @@ class FfiTransformer extends Transformer {
282284
index.getMember('dart:ffi', 'Struct', '_fromPointer'),
283285
fromAddressInternal =
284286
index.getTopLevelMember('dart:ffi', '_fromAddress'),
287+
structPointerRef =
288+
index.getMember('dart:ffi', 'StructPointer', 'get:ref'),
289+
structPointerElemAt =
290+
index.getMember('dart:ffi', 'StructPointer', '[]'),
285291
asFunctionMethod =
286292
index.getMember('dart:ffi', 'NativeFunctionPointer', 'asFunction'),
287293
asFunctionInternal =
@@ -344,7 +350,9 @@ class FfiTransformer extends Transformer {
344350
/// [NativeFunction]<T1 Function(T2, T3) -> S1 Function(S2, S3)
345351
/// where DartRepresentationOf(Tn) -> Sn
346352
DartType convertNativeTypeToDartType(DartType nativeType,
347-
{bool allowStructs = false, bool allowHandle = false}) {
353+
{bool allowStructs = false,
354+
bool allowStructItself = false,
355+
bool allowHandle = false}) {
348356
if (nativeType is! InterfaceType) {
349357
return null;
350358
}
@@ -353,6 +361,9 @@ class FfiTransformer extends Transformer {
353361
final NativeType nativeType_ = getType(nativeClass);
354362

355363
if (hierarchy.isSubclassOf(nativeClass, structClass)) {
364+
if (structClass == nativeClass) {
365+
return allowStructItself ? nativeType : null;
366+
}
356367
return allowStructs ? nativeType : null;
357368
}
358369
if (nativeType_ == null) {

pkg/vm/lib/transformations/ffi_use_sites.dart

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,14 @@ class _FfiUseSiteTransformer extends FfiTransformer {
173173

174174
final Member target = node.target;
175175
try {
176-
if (target == lookupFunctionMethod) {
176+
if (target == structPointerRef || target == structPointerElemAt) {
177+
final DartType nativeType = node.arguments.types[0];
178+
179+
_ensureNativeTypeValid(nativeType, node, allowStructItself: false);
180+
181+
// TODO(http://dartbug.com/38721): Replace calls with direct
182+
// constructor invocations.
183+
} else if (target == lookupFunctionMethod) {
177184
final DartType nativeType = InterfaceType(
178185
nativeFunctionClass, Nullability.legacy, [node.arguments.types[0]]);
179186
final DartType dartType = node.arguments.types[1];
@@ -427,9 +434,11 @@ class _FfiUseSiteTransformer extends FfiTransformer {
427434
}
428435

429436
void _ensureNativeTypeValid(DartType nativeType, Expression node,
430-
{bool allowHandle: false}) {
437+
{bool allowHandle: false, bool allowStructItself = true}) {
431438
if (!_nativeTypeValid(nativeType,
432-
allowStructs: true, allowHandle: allowHandle)) {
439+
allowStructs: true,
440+
allowStructItself: allowStructItself,
441+
allowHandle: allowHandle)) {
433442
diagnosticReporter.report(
434443
templateFfiTypeInvalid.withArguments(
435444
nativeType, currentLibrary.isNonNullableByDefault),
@@ -466,9 +475,13 @@ class _FfiUseSiteTransformer extends FfiTransformer {
466475
/// The Dart type system does not enforce that NativeFunction return and
467476
/// parameter types are only NativeTypes, so we need to check this.
468477
bool _nativeTypeValid(DartType nativeType,
469-
{bool allowStructs: false, allowHandle: false}) {
478+
{bool allowStructs: false,
479+
bool allowStructItself = false,
480+
bool allowHandle = false}) {
470481
return convertNativeTypeToDartType(nativeType,
471-
allowStructs: allowStructs, allowHandle: allowHandle) !=
482+
allowStructs: allowStructs,
483+
allowStructItself: allowStructItself,
484+
allowHandle: allowHandle) !=
472485
null;
473486
}
474487

runtime/lib/ffi.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ static ObjectPtr LoadValueStruct(Zone* zone,
116116
DEFINE_NATIVE_ENTRY(Ffi_loadStruct, 0, 2) {
117117
GET_NON_NULL_NATIVE_ARGUMENT(Pointer, pointer, arguments->NativeArgAt(0));
118118
const AbstractType& pointer_type_arg =
119-
AbstractType::Handle(pointer.type_argument());
119+
AbstractType::Handle(arguments->NativeTypeArgAt(0));
120120
GET_NON_NULL_NATIVE_ARGUMENT(Integer, index, arguments->NativeArgAt(1));
121121

122122
// TODO(36370): Make representation consistent with kUnboxedFfiIntPtr.

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,13 @@ double _loadDouble(Pointer pointer, int offsetInBytes) native "Ffi_loadDouble";
204204
Pointer<S> _loadPointer<S extends NativeType>(
205205
Pointer pointer, int offsetInBytes) native "Ffi_loadPointer";
206206

207+
S _loadStructNoStruct<S extends Struct>(Pointer<S> pointer, int index) {
208+
if (S == Struct) {
209+
throw ArgumentError("S should be a subtype of Struct.");
210+
}
211+
return _loadStruct(pointer, index);
212+
}
213+
207214
S _loadStruct<S extends Struct>(Pointer<S> pointer, int index)
208215
native "Ffi_loadStruct";
209216

@@ -511,10 +518,10 @@ extension PointerPointer<T extends NativeType> on Pointer<Pointer<T>> {
511518

512519
extension StructPointer<T extends Struct> on Pointer<T> {
513520
@patch
514-
T get ref => _loadStruct(this, 0);
521+
T get ref => _loadStructNoStruct(this, 0);
515522

516523
@patch
517-
T operator [](int index) => _loadStruct(this, index);
524+
T operator [](int index) => _loadStructNoStruct(this, index);
518525
}
519526

520527
extension NativePort on SendPort {

sdk/lib/ffi/ffi.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,13 +538,17 @@ extension StructPointer<T extends Struct> on Pointer<T> {
538538
///
539539
/// The [address] must be aligned according to the struct alignment rules of
540540
/// the platform.
541+
///
542+
/// This extension method must be invoked with a compile-time constant [T].
541543
external T get ref;
542544

543545
/// Creates a reference to access the fields of this struct backed by native
544546
/// memory at `address + sizeOf<T>() * index`.
545547
///
546548
/// The [address] must be aligned according to the struct alignment rules of
547549
/// the platform.
550+
///
551+
/// This extension method must be invoked with a compile-time constant [T].
548552
external T operator [](int index);
549553
}
550554

tests/ffi/vmspecific_static_checks_test.dart

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ void main() {
4949
testHandleVariance();
5050
testAllocateGeneric();
5151
testAllocateNativeType();
52+
testRefStruct();
5253
}
5354

5455
typedef Int8UnOp = Int8 Function(Int8);
@@ -540,3 +541,19 @@ void testAllocateGeneric() {
540541
void testAllocateNativeType() {
541542
calloc(); //# 1321: compile-time error
542543
}
544+
545+
void testRefStruct() {
546+
final myStructPointer = malloc<TestStruct13>();
547+
Pointer<Struct> structPointer = myStructPointer;
548+
structPointer.ref; //# 1330: compile-time error
549+
malloc.free(myStructPointer);
550+
}
551+
552+
T genericRef<T extends Struct>(Pointer<T> p) => //# 1200: compile-time error
553+
p.ref; //# 1200: compile-time error
554+
555+
T genericRef2<T extends Struct>(Pointer<T> p) => //# 1201: compile-time error
556+
p.cast<T>().ref; //# 1201: compile-time error
557+
558+
T genericRef3<T extends Struct>(Pointer<T> p) => //# 1202: compile-time error
559+
p[0]; //# 1202: compile-time error

tests/ffi_2/vmspecific_static_checks_test.dart

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ void main() {
4949
testHandleVariance();
5050
testAllocateGeneric();
5151
testAllocateNativeType();
52+
testRefStruct();
5253
}
5354

5455
typedef Int8UnOp = Int8 Function(Int8);
@@ -538,3 +539,19 @@ void testAllocateGeneric() {
538539
void testAllocateNativeType() {
539540
calloc(); //# 1321: compile-time error
540541
}
542+
543+
void testRefStruct() {
544+
final myStructPointer = malloc<TestStruct13>();
545+
Pointer<Struct> structPointer = myStructPointer;
546+
structPointer.ref; //# 1330: compile-time error
547+
malloc.free(myStructPointer);
548+
}
549+
550+
T genericRef<T extends Struct>(Pointer<T> p) => //# 1200: compile-time error
551+
p.ref; //# 1200: compile-time error
552+
553+
T genericRef2<T extends Struct>(Pointer<T> p) => //# 1201: compile-time error
554+
p.cast<T>().ref; //# 1201: compile-time error
555+
556+
T genericRef3<T extends Struct>(Pointer<T> p) => //# 1202: compile-time error
557+
p[0]; //# 1202: compile-time error

0 commit comments

Comments
 (0)