Skip to content

Commit

Permalink
[vm/ffi] Deprecate Pointer.elementAt and sizeOf generic calls
Browse files Browse the repository at this point in the history
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 <vegorov@google.com>
  • Loading branch information
dcharkes authored and commit-bot@chromium.org committed Jan 22, 2021
1 parent f74c9d4 commit d74b2f4
Show file tree
Hide file tree
Showing 11 changed files with 161 additions and 38 deletions.
38 changes: 38 additions & 0 deletions pkg/analyzer/lib/src/generated/ffi_verifier.dart
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
if (_isPointer(enclosingElement)) {
if (element.name == 'fromFunction') {
_validateFromFunction(node, element);
} else if (element.name == 'elementAt') {
_validateElementAt(node);
}
}
}
Expand All @@ -187,6 +189,15 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
_validateLookupFunction(node);
}
}
} else if (element is FunctionElement) {
Element enclosingElement = element.enclosingElement;
if (enclosingElement is CompilationUnitElement) {
if (element.library.name == 'dart.ffi') {
if (element.name == 'sizeOf') {
_validateSizeOf(node);
}
}
}
}
super.visitMethodInvocation(node);
}
Expand Down Expand Up @@ -618,6 +629,24 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
}
}

void _validateElementAt(MethodInvocation node) {
Expression target = node.realTarget;
DartType targetType = target.staticType;
if (targetType is InterfaceType &&
_isPointer(targetType.element) &&
targetType.typeArguments.length == 1) {
final DartType T = targetType.typeArguments[0];

if (!_isValidFfiNativeType(T, true, true)) {
final AstNode errorNode = node;
_errorReporter.reportErrorForNode(
FfiCode.NON_CONSTANT_TYPE_ARGUMENT_WARNING,
errorNode,
['elementAt']);
}
}
}

/// Validate that the fields declared by the given [node] meet the
/// requirements for fields within a struct class.
void _validateFieldsInStruct(FieldDeclaration node) {
Expand Down Expand Up @@ -771,6 +800,15 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
}
}

void _validateSizeOf(MethodInvocation node) {
final DartType T = node.typeArgumentTypes[0];
if (!_isValidFfiNativeType(T, true, true)) {
final AstNode errorNode = node;
_errorReporter.reportErrorForNode(
FfiCode.NON_CONSTANT_TYPE_ARGUMENT_WARNING, errorNode, ['sizeOf']);
}
}

/// Validate that the given [typeArgument] has a constant value. Return `true`
/// if a diagnostic was produced because it isn't constant.
bool _validateTypeArgument(TypeAnnotation typeArgument, String functionName) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
//
// Problems outside component:
//
// 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.
//
library;
import self as self;
import "dart:core" as core;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
//
// Problems outside component:
//
// 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.
//
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
//
// Problems outside component:
//
// 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.
//
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
Expand Down
2 changes: 2 additions & 0 deletions pkg/vm/lib/transformations/ffi.dart
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ class FfiTransformer extends Transformer {
final Procedure structPointerElemAt;
final Procedure asFunctionMethod;
final Procedure asFunctionInternal;
final Procedure sizeOfMethod;
final Procedure lookupFunctionMethod;
final Procedure fromFunctionMethod;
final Field addressOfField;
Expand Down Expand Up @@ -292,6 +293,7 @@ class FfiTransformer extends Transformer {
index.getMember('dart:ffi', 'NativeFunctionPointer', 'asFunction'),
asFunctionInternal =
index.getTopLevelMember('dart:ffi', '_asFunctionInternal'),
sizeOfMethod = index.getTopLevelMember('dart:ffi', 'sizeOf'),
lookupFunctionMethod = index.getMember(
'dart:ffi', 'DynamicLibraryExtension', 'lookupFunction'),
fromFunctionMethod =
Expand Down
12 changes: 12 additions & 0 deletions pkg/vm/lib/transformations/ffi_use_sites.dart
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,15 @@ class _FfiUseSiteTransformer extends FfiTransformer {

// TODO(http://dartbug.com/38721): Replace calls with direct
// constructor invocations.
} else if (target == sizeOfMethod) {
final DartType nativeType = node.arguments.types[0];

if (!isFfiLibrary) {
_warningNativeTypeValid(nativeType, node);
}

// TODO(http://dartbug.com/38721): Replace calls with constant
// expressions.
} else if (target == lookupFunctionMethod) {
final DartType nativeType = InterfaceType(
nativeFunctionClass, Nullability.legacy, [node.arguments.types[0]]);
Expand Down Expand Up @@ -384,6 +393,9 @@ class _FfiUseSiteTransformer extends FfiTransformer {
final DartType pointerType =
node.receiver.getStaticType(_staticTypeContext);
final DartType nativeType = _pointerTypeGetTypeArg(pointerType);

_warningNativeTypeValid(nativeType, node);

if (nativeType is TypeParameterType) {
// Do not rewire generic invocations.
return node;
Expand Down
8 changes: 8 additions & 0 deletions sdk/lib/ffi/ffi.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ part "struct.dart";
/// Number of bytes used by native type T.
///
/// Includes padding and alignment of structs.
///
/// Support for invoking this function with non-constant [T] will be removed in
/// the next stable version of Dart and it will become mandatory to invoke it
/// with a compile-time constant [T].
external int sizeOf<T extends NativeType>();

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

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

/// Cast Pointer<T> to a Pointer<V>.
Expand Down
19 changes: 0 additions & 19 deletions tests/ffi/data_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,8 @@ void main() {
testEquality();
testAllocateVoid();
testAllocateNativeFunction();
testSizeOfGeneric();
testSizeOfVoid();
testSizeOfNativeFunction();
testSizeOfNativeType();
testDynamicInvocation();
testMemoryAddressTruncation();
testNullptrCast();
Expand Down Expand Up @@ -434,17 +432,6 @@ void testAllocateNativeFunction() {
});
}

void testSizeOfGeneric() {
int generic<T extends Pointer>() {
int size;
size = sizeOf<T>();
return size;
}

int size = generic<Pointer<Int64>>();
Expect.isTrue(size == 8 || size == 4);
}

void testSizeOfVoid() {
Expect.throws(() {
sizeOf<Void>();
Expand All @@ -457,12 +444,6 @@ void testSizeOfNativeFunction() {
});
}

void testSizeOfNativeType() {
Expect.throws(() {
sizeOf();
});
}

void testDynamicInvocation() {
dynamic p = calloc<Int8>();
Expect.throws(() {
Expand Down
43 changes: 43 additions & 0 deletions tests/ffi/vmspecific_static_checks_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ void main() {
testAllocateGeneric();
testAllocateNativeType();
testRefStruct();
testSizeOfGeneric();
testSizeOfNativeType();
testElementAtGeneric();
testElementAtNativeType();
}

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

T genericRef3<T extends Struct>(Pointer<T> p) => //# 1202: ok
p[0]; //# 1202: ok

void testSizeOfGeneric() {
int generic<T extends Pointer>() {
int size = sizeOf<IntPtr>();
size = sizeOf<T>(); //# 1300: ok
return size;
}

int size = generic<Pointer<Int64>>();
}

void testSizeOfNativeType() {
try {
sizeOf(); //# 1301: ok
} catch (e) {
print(e);
}
}

void testElementAtGeneric() {
Pointer<T> generic<T extends NativeType>(Pointer<T> pointer) {
Pointer<T> returnValue = pointer;
returnValue = returnValue.elementAt(1); //# 1310: ok
return returnValue;
}

Pointer<Int8> p = calloc();
p.elementAt(1);
generic(p);
calloc.free(p);
}

void testElementAtNativeType() {
Pointer<Int8> p = calloc();
p.elementAt(1);
Pointer<NativeType> p2 = p;
p2.elementAt(1); //# 1311: ok
calloc.free(p);
}
19 changes: 0 additions & 19 deletions tests/ffi_2/data_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,8 @@ void main() {
testEquality();
testAllocateVoid();
testAllocateNativeFunction();
testSizeOfGeneric();
testSizeOfVoid();
testSizeOfNativeFunction();
testSizeOfNativeType();
testDynamicInvocation();
testMemoryAddressTruncation();
testNullptrCast();
Expand Down Expand Up @@ -434,17 +432,6 @@ void testAllocateNativeFunction() {
});
}

void testSizeOfGeneric() {
int generic<T extends Pointer>() {
int size;
size = sizeOf<T>();
return size;
}

int size = generic<Pointer<Int64>>();
Expect.isTrue(size == 8 || size == 4);
}

void testSizeOfVoid() {
Expect.throws(() {
sizeOf<Void>();
Expand All @@ -457,12 +444,6 @@ void testSizeOfNativeFunction() {
});
}

void testSizeOfNativeType() {
Expect.throws(() {
sizeOf();
});
}

void testDynamicInvocation() {
dynamic p = calloc<Int8>();
Expect.throws(() {
Expand Down
43 changes: 43 additions & 0 deletions tests/ffi_2/vmspecific_static_checks_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ void main() {
testAllocateGeneric();
testAllocateNativeType();
testRefStruct();
testSizeOfGeneric();
testSizeOfNativeType();
testElementAtGeneric();
testElementAtNativeType();
}

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

T genericRef3<T extends Struct>(Pointer<T> p) => //# 1202: ok
p[0]; //# 1202: ok

void testSizeOfGeneric() {
int generic<T extends Pointer>() {
int size = sizeOf<IntPtr>();
size = sizeOf<T>(); //# 1300: ok
return size;
}

int size = generic<Pointer<Int64>>();
}

void testSizeOfNativeType() {
try {
sizeOf(); //# 1301: ok
} catch (e) {
print(e);
}
}

void testElementAtGeneric() {
Pointer<T> generic<T extends NativeType>(Pointer<T> pointer) {
Pointer<T> returnValue = pointer;
returnValue = returnValue.elementAt(1); //# 1310: ok
return returnValue;
}

Pointer<Int8> p = calloc();
p.elementAt(1);
generic(p);
calloc.free(p);
}

void testElementAtNativeType() {
Pointer<Int8> p = calloc();
p.elementAt(1);
Pointer<NativeType> p2 = p;
p2.elementAt(1); //# 1311: ok
calloc.free(p);
}

0 comments on commit d74b2f4

Please sign in to comment.