[vm/ffi] Change Pointer.elementAt and sizeOf to use static type

This CL changes the semantics of
`Pointer<T extends NativeType>.elementAt` and
`sizeOf<T extends NativeType>` to use the compile-time `T` rather than
the runtime `T`.

Issue: https://github.com/dart-lang/sdk/issues/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: Ifb25a4bd66d50a385d3db6dec9213b96dff21722
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/+/178200
Reviewed-by: Aske Simon Christensen <askesc@google.com>
This commit is contained in:
Daco Harkes 2021-02-17 11:39:42 +00:00 committed by commit-bot@chromium.org
parent f169abcee0
commit 3593de9179
14 changed files with 29 additions and 113 deletions

View file

@ -474,7 +474,6 @@ const List<ErrorCode> errorCodeValues = [
FfiCode.MUST_BE_A_NATIVE_FUNCTION_TYPE, FfiCode.MUST_BE_A_NATIVE_FUNCTION_TYPE,
FfiCode.MUST_BE_A_SUBTYPE, FfiCode.MUST_BE_A_SUBTYPE,
FfiCode.NON_CONSTANT_TYPE_ARGUMENT, FfiCode.NON_CONSTANT_TYPE_ARGUMENT,
FfiCode.NON_CONSTANT_TYPE_ARGUMENT_WARNING,
FfiCode.NON_NATIVE_FUNCTION_TYPE_ARGUMENT_TO_POINTER, FfiCode.NON_NATIVE_FUNCTION_TYPE_ARGUMENT_TO_POINTER,
FfiCode.NON_SIZED_TYPE_ARGUMENT, FfiCode.NON_SIZED_TYPE_ARGUMENT,
FfiCode.SUBTYPE_OF_FFI_CLASS_IN_EXTENDS, FfiCode.SUBTYPE_OF_FFI_CLASS_IN_EXTENDS,

View file

@ -183,20 +183,6 @@ class FfiCode extends AnalyzerErrorCode {
"parameters are not constants.", "parameters are not constants.",
correction: "Try changing the type argument to be a constant type."); correction: "Try changing the type argument to be a constant type.");
/**
* Parameters:
* 0: the name of the function, method, or constructor having type arguments
*/
static const FfiCode NON_CONSTANT_TYPE_ARGUMENT_WARNING = FfiCode(
name: 'NON_CONSTANT_TYPE_ARGUMENT_WARNING',
message:
"Support for using non-constant type arguments '{0}' 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.",
correction: "Try changing the type argument to be a constant type.",
type: ErrorType.HINT);
/** /**
* Parameters: * Parameters:
* 0: the type that should be a valid dart:ffi native type. * 0: the type that should be a valid dart:ffi native type.

View file

@ -525,9 +525,7 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
if (!_isValidFfiNativeType(T, allowVoid: true, allowEmptyStruct: true)) { if (!_isValidFfiNativeType(T, allowVoid: true, allowEmptyStruct: true)) {
final AstNode errorNode = node; final AstNode errorNode = node;
_errorReporter.reportErrorForNode( _errorReporter.reportErrorForNode(
FfiCode.NON_CONSTANT_TYPE_ARGUMENT_WARNING, FfiCode.NON_CONSTANT_TYPE_ARGUMENT, errorNode, ['elementAt']);
errorNode,
['elementAt']);
} }
} }
} }
@ -703,7 +701,7 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
if (!_isValidFfiNativeType(T, allowVoid: true, allowEmptyStruct: true)) { if (!_isValidFfiNativeType(T, allowVoid: true, allowEmptyStruct: true)) {
final AstNode errorNode = node; final AstNode errorNode = node;
_errorReporter.reportErrorForNode( _errorReporter.reportErrorForNode(
FfiCode.NON_CONSTANT_TYPE_ARGUMENT_WARNING, errorNode, ['sizeOf']); FfiCode.NON_CONSTANT_TYPE_ARGUMENT, errorNode, ['sizeOf']);
} }
} }

View file

@ -62,7 +62,6 @@ export '../fasta/fasta_codes.dart'
templateFfiFieldCyclic, templateFfiFieldCyclic,
templateFfiFieldInitializer, templateFfiFieldInitializer,
templateFfiFieldNoAnnotation, templateFfiFieldNoAnnotation,
templateFfiNonConstantTypeArgumentWarning,
templateFfiNotStatic, templateFfiNotStatic,
templateFfiStructGeneric, templateFfiStructGeneric,
templateFfiTypeInvalid, templateFfiTypeInvalid,

View file

@ -915,35 +915,6 @@ Message _withArgumentsFfiExpectedNoExceptionalReturn(
arguments: {'type': _type}); arguments: {'type': _type});
} }
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Template<Message Function(DartType _type, bool isNonNullableByDefault)>
templateFfiNonConstantTypeArgumentWarning = const Template<
Message Function(DartType _type, bool isNonNullableByDefault)>(
messageTemplate:
r"""Support for using non-constant type arguments '#type' 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.""",
withArguments: _withArgumentsFfiNonConstantTypeArgumentWarning);
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Code<Message Function(DartType _type, bool isNonNullableByDefault)>
codeFfiNonConstantTypeArgumentWarning =
const Code<Message Function(DartType _type, bool isNonNullableByDefault)>(
"FfiNonConstantTypeArgumentWarning",
analyzerCodes: <String>["NON_CONSTANT_TYPE_ARGUMENT_WARNING"],
severity: Severity.info);
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
Message _withArgumentsFfiNonConstantTypeArgumentWarning(
DartType _type, bool isNonNullableByDefault) {
TypeLabeler labeler = new TypeLabeler(isNonNullableByDefault);
List<Object> typeParts = labeler.labelType(_type);
String type = typeParts.join();
return new Message(codeFfiNonConstantTypeArgumentWarning,
message:
"""Support for using non-constant type arguments '${type}' 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.""" +
labeler.originMessages,
arguments: {'type': _type});
}
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Template< const Template<
Message Function( Message Function(

View file

@ -4242,13 +4242,6 @@ FfiFieldCyclic:
#names #names
external: test/ffi_test.dart external: test/ffi_test.dart
FfiNonConstantTypeArgumentWarning:
# Used by dart:ffi
template: "Support for using non-constant type arguments '#type' 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."
analyzerCode: NON_CONSTANT_TYPE_ARGUMENT_WARNING
severity: INFO
external: test/ffi_test.dart
FfiNotStatic: FfiNotStatic:
# Used by dart:ffi # Used by dart:ffi
template: "#name expects a static function as parameter. dart:ffi only supports calling static Dart functions from native code." template: "#name expects a static function as parameter. dart:ffi only supports calling static Dart functions from native code."

View file

@ -13,7 +13,6 @@ import 'package:front_end/src/api_unstable/vm.dart'
templateFfiExpectedExceptionalReturn, templateFfiExpectedExceptionalReturn,
templateFfiExpectedNoExceptionalReturn, templateFfiExpectedNoExceptionalReturn,
templateFfiExtendsOrImplementsSealedClass, templateFfiExtendsOrImplementsSealedClass,
templateFfiNonConstantTypeArgumentWarning,
templateFfiNotStatic, templateFfiNotStatic,
templateFfiTypeInvalid, templateFfiTypeInvalid,
templateFfiTypeMismatch; templateFfiTypeMismatch;
@ -189,7 +188,7 @@ class _FfiUseSiteTransformer extends FfiTransformer {
// TODO(http://dartbug.com/38721): Change this to an error after // TODO(http://dartbug.com/38721): Change this to an error after
// package:ffi is no longer using sizeOf generically. // package:ffi is no longer using sizeOf generically.
if (!isFfiLibrary) { if (!isFfiLibrary) {
_warningNativeTypeValid(nativeType, node); _ensureNativeTypeValid(nativeType, node);
} }
if (nativeType is InterfaceType) { if (nativeType is InterfaceType) {
@ -467,13 +466,7 @@ class _FfiUseSiteTransformer extends FfiTransformer {
node.receiver.getStaticType(_staticTypeContext); node.receiver.getStaticType(_staticTypeContext);
final DartType nativeType = _pointerTypeGetTypeArg(pointerType); final DartType nativeType = _pointerTypeGetTypeArg(pointerType);
_warningNativeTypeValid(nativeType, node); _ensureNativeTypeValid(nativeType, node);
// TODO(http://dartbug.com/38721): Change this to an error.
if (nativeType is TypeParameterType) {
// Do not rewire generic invocations.
return node;
}
Expression inlineSizeOf = _inlineSizeOf(nativeType); Expression inlineSizeOf = _inlineSizeOf(nativeType);
if (inlineSizeOf != null) { if (inlineSizeOf != null) {
@ -541,22 +534,6 @@ class _FfiUseSiteTransformer extends FfiTransformer {
} }
} }
void _warningNativeTypeValid(DartType nativeType, Expression node,
{bool allowHandle: false, bool allowStructItself = true}) {
if (!_nativeTypeValid(nativeType,
allowStructs: true,
allowStructItself: allowStructItself,
allowHandle: allowHandle)) {
diagnosticReporter.report(
templateFfiNonConstantTypeArgumentWarning.withArguments(
nativeType, currentLibrary.isNonNullableByDefault),
node.fileOffset,
1,
node.location.file);
throw _FfiStaticTypeError();
}
}
void _ensureNoEmptyStructs(DartType nativeType, Expression node) { void _ensureNoEmptyStructs(DartType nativeType, Expression node) {
// Error on structs with no fields. // Error on structs with no fields.
if (nativeType is InterfaceType) { if (nativeType is InterfaceType) {

View file

@ -12,6 +12,7 @@ extension AllocatorAlloc on Allocator {
// TODO(http://dartbug.com/39964): Add `alignmentOf<T>()` call. // TODO(http://dartbug.com/39964): Add `alignmentOf<T>()` call.
@patch @patch
Pointer<T> call<T extends NativeType>([int count = 1]) { Pointer<T> call<T extends NativeType>([int count = 1]) {
return this.allocate(sizeOf<T>() * count); // This case should have been rewritten in pre-processing.
throw UnimplementedError("Pointer<$T>");
} }
} }

View file

@ -26,19 +26,10 @@ int get _intPtrSize => (const [8, 4, 4])[_abi()];
@patch @patch
int sizeOf<T extends NativeType>() { int sizeOf<T extends NativeType>() {
// This is not super fast, but it is faster than a runtime entry. // This case should have been rewritten in pre-processing.
// Hot loops with elementAt().load() do not use this sizeOf, elementAt is throw UnimplementedError("$T");
// optimized per NativeType statically to prevent use of sizeOf at runtime.
final int? knownSize = _knownSizes[T];
if (knownSize != null) return knownSize;
if (T == IntPtr) return _intPtrSize;
if (T == Pointer) return _intPtrSize;
// For structs we fall back to a runtime entry.
return _sizeOf<T>();
} }
int _sizeOf<T extends NativeType>() native "Ffi_sizeOf";
@pragma("vm:recognized", "other") @pragma("vm:recognized", "other")
Pointer<T> _fromAddress<T extends NativeType>(int ptr) native "Ffi_fromAddress"; Pointer<T> _fromAddress<T extends NativeType>(int ptr) native "Ffi_fromAddress";
@ -98,11 +89,13 @@ class Pointer<T extends NativeType> {
@pragma("vm:recognized", "other") @pragma("vm:recognized", "other")
int get address native "Ffi_address"; int get address native "Ffi_address";
// For statically known types, this is rewired. // For statically known types, this is rewritten.
// (Method sizeOf is slow, see notes above.)
@patch @patch
Pointer<T> elementAt(int index) => Pointer<T> elementAt(int index) {
Pointer.fromAddress(address + sizeOf<T>() * index); // This case should have been rewritten in pre-processing.
// Only dynamic invocations are not rewritten in pre-processing.
throw UnsupportedError("Pointer.elementAt cannot be called dynamically.");
}
@patch @patch
Pointer<T> _offsetBy(int offsetInBytes) => Pointer<T> _offsetBy(int offsetInBytes) =>

View file

@ -24,9 +24,7 @@ part "struct.dart";
/// ///
/// Includes padding and alignment of structs. /// Includes padding and alignment of structs.
/// ///
/// Support for invoking this function with non-constant [T] will be removed in /// This function must be invoked with a compile-time constant [T].
/// 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>(); external int sizeOf<T extends NativeType>();
/// Represents a pointer into the native C memory corresponding to "NULL", e.g. /// Represents a pointer into the native C memory corresponding to "NULL", e.g.
@ -65,9 +63,10 @@ class Pointer<T extends NativeType> extends NativeType {
/// Pointer arithmetic (takes element size into account). /// Pointer arithmetic (takes element size into account).
/// ///
/// Support for invoking this method with non-constant [T] will be removed in /// This method must be invoked with a compile-time constant [T].
/// the next stable version of Dart and it will become mandatory to invoke it ///
/// with a compile-time constant [T]. /// Does not accept dynamic invocations -- where the type of the receiver is
/// [dynamic].
external Pointer<T> elementAt(int index); external Pointer<T> elementAt(int index);
/// Cast Pointer<T> to a Pointer<V>. /// Cast Pointer<T> to a Pointer<V>.

View file

@ -449,7 +449,7 @@ void testDynamicInvocation() {
final int i = p.value; final int i = p.value;
}); });
Expect.throws(() => p.value = 1); Expect.throws(() => p.value = 1);
p.elementAt(5); // Works, but is slow. Expect.throws(() => p.elementAt(5));
final int addr = p.address; final int addr = p.address;
final Pointer<Int16> p2 = p.cast<Int16>(); final Pointer<Int16> p2 = p.cast<Int16>();
calloc.free(p); calloc.free(p);

View file

@ -625,7 +625,7 @@ T genericRef3<T extends Struct>(Pointer<T> p) => //# 1202: compile-time error
void testSizeOfGeneric() { void testSizeOfGeneric() {
int generic<T extends Pointer>() { int generic<T extends Pointer>() {
int size = sizeOf<IntPtr>(); int size = sizeOf<IntPtr>();
size = sizeOf<T>(); //# 1300: ok size = sizeOf<T>(); //# 1300: compile-time error
return size; return size;
} }
@ -634,7 +634,7 @@ void testSizeOfGeneric() {
void testSizeOfNativeType() { void testSizeOfNativeType() {
try { try {
sizeOf(); //# 1301: ok sizeOf(); //# 1301: compile-time error
} catch (e) { } catch (e) {
print(e); print(e);
} }
@ -643,7 +643,7 @@ void testSizeOfNativeType() {
void testElementAtGeneric() { void testElementAtGeneric() {
Pointer<T> generic<T extends NativeType>(Pointer<T> pointer) { Pointer<T> generic<T extends NativeType>(Pointer<T> pointer) {
Pointer<T> returnValue = pointer; Pointer<T> returnValue = pointer;
returnValue = returnValue.elementAt(1); //# 1310: ok returnValue = returnValue.elementAt(1); //# 1310: compile-time error
return returnValue; return returnValue;
} }
@ -657,6 +657,6 @@ void testElementAtNativeType() {
Pointer<Int8> p = calloc(); Pointer<Int8> p = calloc();
p.elementAt(1); p.elementAt(1);
Pointer<NativeType> p2 = p; Pointer<NativeType> p2 = p;
p2.elementAt(1); //# 1311: ok p2.elementAt(1); //# 1311: compile-time error
calloc.free(p); calloc.free(p);
} }

View file

@ -449,7 +449,7 @@ void testDynamicInvocation() {
final int i = p.value; final int i = p.value;
}); });
Expect.throws(() => p.value = 1); Expect.throws(() => p.value = 1);
p.elementAt(5); // Works, but is slow. Expect.throws(() => p.elementAt(5));
final int addr = p.address; final int addr = p.address;
final Pointer<Int16> p2 = p.cast<Int16>(); final Pointer<Int16> p2 = p.cast<Int16>();
calloc.free(p); calloc.free(p);

View file

@ -623,7 +623,7 @@ T genericRef3<T extends Struct>(Pointer<T> p) => //# 1202: compile-time error
void testSizeOfGeneric() { void testSizeOfGeneric() {
int generic<T extends Pointer>() { int generic<T extends Pointer>() {
int size = sizeOf<IntPtr>(); int size = sizeOf<IntPtr>();
size = sizeOf<T>(); //# 1300: ok size = sizeOf<T>(); //# 1300: compile-time error
return size; return size;
} }
@ -632,7 +632,7 @@ void testSizeOfGeneric() {
void testSizeOfNativeType() { void testSizeOfNativeType() {
try { try {
sizeOf(); //# 1301: ok sizeOf(); //# 1301: compile-time error
} catch (e) { } catch (e) {
print(e); print(e);
} }
@ -641,7 +641,7 @@ void testSizeOfNativeType() {
void testElementAtGeneric() { void testElementAtGeneric() {
Pointer<T> generic<T extends NativeType>(Pointer<T> pointer) { Pointer<T> generic<T extends NativeType>(Pointer<T> pointer) {
Pointer<T> returnValue = pointer; Pointer<T> returnValue = pointer;
returnValue = returnValue.elementAt(1); //# 1310: ok returnValue = returnValue.elementAt(1); //# 1310: compile-time error
return returnValue; return returnValue;
} }
@ -655,6 +655,6 @@ void testElementAtNativeType() {
Pointer<Int8> p = calloc(); Pointer<Int8> p = calloc();
p.elementAt(1); p.elementAt(1);
Pointer<NativeType> p2 = p; Pointer<NativeType> p2 = p;
p2.elementAt(1); //# 1311: ok p2.elementAt(1); //# 1311: compile-time error
calloc.free(p); calloc.free(p);
} }