Skip to content

Commit d74b2f4

Browse files
dcharkescommit-bot@chromium.org
authored andcommitted
[vm/ffi] Deprecate Pointer.elementAt and sizeOf generic calls
The analyzer and CFE now report a warning on calls to `Pointer<T extends NativeType>.elementAt()` and `sizeOf<T extends NativeType>()` where `T` is a generic. Adapted from https://dart-review.googlesource.com/c/sdk/+/178200 to only deprecate but not error out on generic calls. Does not roll forward `package:ffi` to a version with the `Allocator` but keeps the generic `sizeOf` invocation. This causes extra warnings in the pkg/front_end testcases. Issue: #38721 TEST=tests/ffi/data_test.dart TEST=tests/ffi/sizeof_test.dart TEST=tests/ffi/structs_test.dart TEST=tests/ffi/vmspecific_static_checks_test.dart Change-Id: I8f41c4dc04fc44e7e6c540ba87a3f41604130fe9 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/180560 Reviewed-by: Vyacheslav Egorov <[email protected]>
1 parent f74c9d4 commit d74b2f4

11 files changed

+161
-38
lines changed

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

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,8 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
174174
if (_isPointer(enclosingElement)) {
175175
if (element.name == 'fromFunction') {
176176
_validateFromFunction(node, element);
177+
} else if (element.name == 'elementAt') {
178+
_validateElementAt(node);
177179
}
178180
}
179181
}
@@ -187,6 +189,15 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
187189
_validateLookupFunction(node);
188190
}
189191
}
192+
} else if (element is FunctionElement) {
193+
Element enclosingElement = element.enclosingElement;
194+
if (enclosingElement is CompilationUnitElement) {
195+
if (element.library.name == 'dart.ffi') {
196+
if (element.name == 'sizeOf') {
197+
_validateSizeOf(node);
198+
}
199+
}
200+
}
190201
}
191202
super.visitMethodInvocation(node);
192203
}
@@ -618,6 +629,24 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
618629
}
619630
}
620631

632+
void _validateElementAt(MethodInvocation node) {
633+
Expression target = node.realTarget;
634+
DartType targetType = target.staticType;
635+
if (targetType is InterfaceType &&
636+
_isPointer(targetType.element) &&
637+
targetType.typeArguments.length == 1) {
638+
final DartType T = targetType.typeArguments[0];
639+
640+
if (!_isValidFfiNativeType(T, true, true)) {
641+
final AstNode errorNode = node;
642+
_errorReporter.reportErrorForNode(
643+
FfiCode.NON_CONSTANT_TYPE_ARGUMENT_WARNING,
644+
errorNode,
645+
['elementAt']);
646+
}
647+
}
648+
}
649+
621650
/// Validate that the fields declared by the given [node] meet the
622651
/// requirements for fields within a struct class.
623652
void _validateFieldsInStruct(FieldDeclaration node) {
@@ -771,6 +800,15 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
771800
}
772801
}
773802

803+
void _validateSizeOf(MethodInvocation node) {
804+
final DartType T = node.typeArgumentTypes[0];
805+
if (!_isValidFfiNativeType(T, true, true)) {
806+
final AstNode errorNode = node;
807+
_errorReporter.reportErrorForNode(
808+
FfiCode.NON_CONSTANT_TYPE_ARGUMENT_WARNING, errorNode, ['sizeOf']);
809+
}
810+
}
811+
774812
/// Validate that the given [typeArgument] has a constant value. Return `true`
775813
/// if a diagnostic was produced because it isn't constant.
776814
bool _validateTypeArgument(TypeAnnotation typeArgument, String functionName) {

pkg/front_end/testcases/general_nnbd_opt_out/ffi_sample.dart.weak.transformed.expect

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
//
2+
// Problems outside component:
3+
//
4+
// third_party/pkg/ffi/lib/src/allocation.dart: Info: Support for using non-constant type arguments 'T' in this FFI API is deprecated and will be removed in the next stable version of Dart. Rewrite the code to ensure that type arguments are compile time constants referring to a valid native type.
5+
//
16
library;
27
import self as self;
38
import "dart:core" as core;

pkg/front_end/testcases/nnbd/ffi_sample.dart.strong.transformed.expect

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
//
2+
// Problems outside component:
3+
//
4+
// third_party/pkg/ffi/lib/src/allocation.dart: Info: Support for using non-constant type arguments 'T' in this FFI API is deprecated and will be removed in the next stable version of Dart. Rewrite the code to ensure that type arguments are compile time constants referring to a valid native type.
5+
//
16
library /*isNonNullableByDefault*/;
27
import self as self;
38
import "dart:core" as core;

pkg/front_end/testcases/nnbd/ffi_sample.dart.weak.transformed.expect

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
//
2+
// Problems outside component:
3+
//
4+
// third_party/pkg/ffi/lib/src/allocation.dart: Info: Support for using non-constant type arguments 'T' in this FFI API is deprecated and will be removed in the next stable version of Dart. Rewrite the code to ensure that type arguments are compile time constants referring to a valid native type.
5+
//
16
library /*isNonNullableByDefault*/;
27
import self as self;
38
import "dart:core" as core;

pkg/vm/lib/transformations/ffi.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ class FfiTransformer extends Transformer {
223223
final Procedure structPointerElemAt;
224224
final Procedure asFunctionMethod;
225225
final Procedure asFunctionInternal;
226+
final Procedure sizeOfMethod;
226227
final Procedure lookupFunctionMethod;
227228
final Procedure fromFunctionMethod;
228229
final Field addressOfField;
@@ -292,6 +293,7 @@ class FfiTransformer extends Transformer {
292293
index.getMember('dart:ffi', 'NativeFunctionPointer', 'asFunction'),
293294
asFunctionInternal =
294295
index.getTopLevelMember('dart:ffi', '_asFunctionInternal'),
296+
sizeOfMethod = index.getTopLevelMember('dart:ffi', 'sizeOf'),
295297
lookupFunctionMethod = index.getMember(
296298
'dart:ffi', 'DynamicLibraryExtension', 'lookupFunction'),
297299
fromFunctionMethod =

pkg/vm/lib/transformations/ffi_use_sites.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,15 @@ class _FfiUseSiteTransformer extends FfiTransformer {
181181

182182
// TODO(http://dartbug.com/38721): Replace calls with direct
183183
// constructor invocations.
184+
} else if (target == sizeOfMethod) {
185+
final DartType nativeType = node.arguments.types[0];
186+
187+
if (!isFfiLibrary) {
188+
_warningNativeTypeValid(nativeType, node);
189+
}
190+
191+
// TODO(http://dartbug.com/38721): Replace calls with constant
192+
// expressions.
184193
} else if (target == lookupFunctionMethod) {
185194
final DartType nativeType = InterfaceType(
186195
nativeFunctionClass, Nullability.legacy, [node.arguments.types[0]]);
@@ -384,6 +393,9 @@ class _FfiUseSiteTransformer extends FfiTransformer {
384393
final DartType pointerType =
385394
node.receiver.getStaticType(_staticTypeContext);
386395
final DartType nativeType = _pointerTypeGetTypeArg(pointerType);
396+
397+
_warningNativeTypeValid(nativeType, node);
398+
387399
if (nativeType is TypeParameterType) {
388400
// Do not rewire generic invocations.
389401
return node;

sdk/lib/ffi/ffi.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ part "struct.dart";
2525
/// Number of bytes used by native type T.
2626
///
2727
/// Includes padding and alignment of structs.
28+
///
29+
/// Support for invoking this function with non-constant [T] will be removed in
30+
/// the next stable version of Dart and it will become mandatory to invoke it
31+
/// with a compile-time constant [T].
2832
external int sizeOf<T extends NativeType>();
2933

3034
/// Represents a pointer into the native C memory corresponding to "NULL", e.g.
@@ -62,6 +66,10 @@ class Pointer<T extends NativeType> extends NativeType {
6266
external int get address;
6367

6468
/// Pointer arithmetic (takes element size into account).
69+
///
70+
/// Support for invoking this method with non-constant [T] will be removed in
71+
/// the next stable version of Dart and it will become mandatory to invoke it
72+
/// with a compile-time constant [T].
6573
external Pointer<T> elementAt(int index);
6674

6775
/// Cast Pointer<T> to a Pointer<V>.

tests/ffi/data_test.dart

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,8 @@ void main() {
4747
testEquality();
4848
testAllocateVoid();
4949
testAllocateNativeFunction();
50-
testSizeOfGeneric();
5150
testSizeOfVoid();
5251
testSizeOfNativeFunction();
53-
testSizeOfNativeType();
5452
testDynamicInvocation();
5553
testMemoryAddressTruncation();
5654
testNullptrCast();
@@ -434,17 +432,6 @@ void testAllocateNativeFunction() {
434432
});
435433
}
436434

437-
void testSizeOfGeneric() {
438-
int generic<T extends Pointer>() {
439-
int size;
440-
size = sizeOf<T>();
441-
return size;
442-
}
443-
444-
int size = generic<Pointer<Int64>>();
445-
Expect.isTrue(size == 8 || size == 4);
446-
}
447-
448435
void testSizeOfVoid() {
449436
Expect.throws(() {
450437
sizeOf<Void>();
@@ -457,12 +444,6 @@ void testSizeOfNativeFunction() {
457444
});
458445
}
459446

460-
void testSizeOfNativeType() {
461-
Expect.throws(() {
462-
sizeOf();
463-
});
464-
}
465-
466447
void testDynamicInvocation() {
467448
dynamic p = calloc<Int8>();
468449
Expect.throws(() {

tests/ffi/vmspecific_static_checks_test.dart

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ void main() {
5757
testAllocateGeneric();
5858
testAllocateNativeType();
5959
testRefStruct();
60+
testSizeOfGeneric();
61+
testSizeOfNativeType();
62+
testElementAtGeneric();
63+
testElementAtNativeType();
6064
}
6165

6266
typedef Int8UnOp = Int8 Function(Int8);
@@ -618,3 +622,42 @@ T genericRef2<T extends Struct>(Pointer<T> p) => //# 1201: ok
618622

619623
T genericRef3<T extends Struct>(Pointer<T> p) => //# 1202: ok
620624
p[0]; //# 1202: ok
625+
626+
void testSizeOfGeneric() {
627+
int generic<T extends Pointer>() {
628+
int size = sizeOf<IntPtr>();
629+
size = sizeOf<T>(); //# 1300: ok
630+
return size;
631+
}
632+
633+
int size = generic<Pointer<Int64>>();
634+
}
635+
636+
void testSizeOfNativeType() {
637+
try {
638+
sizeOf(); //# 1301: ok
639+
} catch (e) {
640+
print(e);
641+
}
642+
}
643+
644+
void testElementAtGeneric() {
645+
Pointer<T> generic<T extends NativeType>(Pointer<T> pointer) {
646+
Pointer<T> returnValue = pointer;
647+
returnValue = returnValue.elementAt(1); //# 1310: ok
648+
return returnValue;
649+
}
650+
651+
Pointer<Int8> p = calloc();
652+
p.elementAt(1);
653+
generic(p);
654+
calloc.free(p);
655+
}
656+
657+
void testElementAtNativeType() {
658+
Pointer<Int8> p = calloc();
659+
p.elementAt(1);
660+
Pointer<NativeType> p2 = p;
661+
p2.elementAt(1); //# 1311: ok
662+
calloc.free(p);
663+
}

tests/ffi_2/data_test.dart

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,8 @@ void main() {
4747
testEquality();
4848
testAllocateVoid();
4949
testAllocateNativeFunction();
50-
testSizeOfGeneric();
5150
testSizeOfVoid();
5251
testSizeOfNativeFunction();
53-
testSizeOfNativeType();
5452
testDynamicInvocation();
5553
testMemoryAddressTruncation();
5654
testNullptrCast();
@@ -434,17 +432,6 @@ void testAllocateNativeFunction() {
434432
});
435433
}
436434

437-
void testSizeOfGeneric() {
438-
int generic<T extends Pointer>() {
439-
int size;
440-
size = sizeOf<T>();
441-
return size;
442-
}
443-
444-
int size = generic<Pointer<Int64>>();
445-
Expect.isTrue(size == 8 || size == 4);
446-
}
447-
448435
void testSizeOfVoid() {
449436
Expect.throws(() {
450437
sizeOf<Void>();
@@ -457,12 +444,6 @@ void testSizeOfNativeFunction() {
457444
});
458445
}
459446

460-
void testSizeOfNativeType() {
461-
Expect.throws(() {
462-
sizeOf();
463-
});
464-
}
465-
466447
void testDynamicInvocation() {
467448
dynamic p = calloc<Int8>();
468449
Expect.throws(() {

tests/ffi_2/vmspecific_static_checks_test.dart

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ void main() {
5757
testAllocateGeneric();
5858
testAllocateNativeType();
5959
testRefStruct();
60+
testSizeOfGeneric();
61+
testSizeOfNativeType();
62+
testElementAtGeneric();
63+
testElementAtNativeType();
6064
}
6165

6266
typedef Int8UnOp = Int8 Function(Int8);
@@ -616,3 +620,42 @@ T genericRef2<T extends Struct>(Pointer<T> p) => //# 1201: ok
616620

617621
T genericRef3<T extends Struct>(Pointer<T> p) => //# 1202: ok
618622
p[0]; //# 1202: ok
623+
624+
void testSizeOfGeneric() {
625+
int generic<T extends Pointer>() {
626+
int size = sizeOf<IntPtr>();
627+
size = sizeOf<T>(); //# 1300: ok
628+
return size;
629+
}
630+
631+
int size = generic<Pointer<Int64>>();
632+
}
633+
634+
void testSizeOfNativeType() {
635+
try {
636+
sizeOf(); //# 1301: ok
637+
} catch (e) {
638+
print(e);
639+
}
640+
}
641+
642+
void testElementAtGeneric() {
643+
Pointer<T> generic<T extends NativeType>(Pointer<T> pointer) {
644+
Pointer<T> returnValue = pointer;
645+
returnValue = returnValue.elementAt(1); //# 1310: ok
646+
return returnValue;
647+
}
648+
649+
Pointer<Int8> p = calloc();
650+
p.elementAt(1);
651+
generic(p);
652+
calloc.free(p);
653+
}
654+
655+
void testElementAtNativeType() {
656+
Pointer<Int8> p = calloc();
657+
p.elementAt(1);
658+
Pointer<NativeType> p2 = p;
659+
p2.elementAt(1); //# 1311: ok
660+
calloc.free(p);
661+
}

0 commit comments

Comments
 (0)