Use extensions in FfiVerifier.
Change-Id: Ib9fe8c4c327508e29aaa330054359b75157bb805
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/184521
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/generated/ffi_verifier.dart b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
index 16e000f..9b71259 100644
--- a/pkg/analyzer/lib/src/generated/ffi_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
@@ -18,7 +18,7 @@
static const _allocatorClassName = 'Allocator';
static const _allocateExtensionMethodName = 'call';
static const _allocatorExtensionName = 'AllocatorAlloc';
- static const _carrayClassName = 'CArray';
+ static const _cArrayClassName = 'CArray';
static const _dartFfiLibraryName = 'dart.ffi';
static const _opaqueClassName = 'Opaque';
@@ -61,11 +61,12 @@
var extendsClause = node.extendsClause;
if (extendsClause != null) {
final TypeName superclass = extendsClause.superclass;
- if (_isDartFfiClass(superclass)) {
- final className = superclass.name.staticElement!.name;
+ final ffiClass = superclass.ffiClass;
+ if (ffiClass != null) {
+ final className = ffiClass.name;
if (className == _structClassName) {
inStruct = true;
- if (_isEmptyStruct(node.declaredElement!)) {
+ if (node.declaredElement!.isEmptyStruct) {
_errorReporter.reportErrorForNode(
FfiCode.EMPTY_STRUCT_WARNING, node, [node.name]);
}
@@ -76,7 +77,7 @@
superclass.name,
[node.name.name, superclass.name.name]);
}
- } else if (_isSubtypeOfStruct(superclass)) {
+ } else if (superclass.isStructSubtype) {
_errorReporter.reportErrorForNode(
FfiCode.SUBTYPE_OF_STRUCT_CLASS_IN_EXTENDS,
superclass,
@@ -91,10 +92,10 @@
if (superName == _allocatorClassName) {
return;
}
- if (_isDartFfiClass(typename)) {
+ if (typename.ffiClass != null) {
_errorReporter.reportErrorForNode(
subtypeOfFfiCode, typename, [node.name, typename.name]);
- } else if (_isSubtypeOfStruct(typename)) {
+ } else if (typename.isStructSubtype) {
_errorReporter.reportErrorForNode(
subtypeOfStructCode, typename, [node.name, typename.name]);
}
@@ -144,11 +145,9 @@
var element = node.staticElement;
if (element is MethodElement) {
var enclosingElement = element.enclosingElement;
- if (enclosingElement is ExtensionElement) {
- if (_isAllocatorExtension(enclosingElement) &&
- element.name == _allocateExtensionMethodName) {
- _validateAllocate(node);
- }
+ if (enclosingElement.isAllocatorExtension &&
+ element.name == _allocateExtensionMethodName) {
+ _validateAllocate(node);
}
}
super.visitFunctionExpressionInvocation(node);
@@ -159,12 +158,10 @@
var element = node.staticElement;
if (element is MethodElement) {
var enclosingElement = element.enclosingElement;
- if (enclosingElement is ExtensionElement) {
- if (_isNativeStructPointerExtension(enclosingElement) ||
- _isNativeStructCArrayExtension(enclosingElement)) {
- if (element.name == '[]') {
- _validateRefIndexed(node);
- }
+ if (enclosingElement.isNativeStructPointerExtension ||
+ enclosingElement.isNativeStructCArrayExtension) {
+ if (element.name == '[]') {
+ _validateRefIndexed(node);
}
}
}
@@ -175,22 +172,18 @@
var element = node.methodName.staticElement;
if (element is MethodElement) {
Element enclosingElement = element.enclosingElement;
- if (enclosingElement is ClassElement) {
- if (_isPointer(enclosingElement)) {
- if (element.name == 'fromFunction') {
- _validateFromFunction(node, element);
- } else if (element.name == 'elementAt') {
- _validateElementAt(node);
- }
+ if (enclosingElement.isPointer) {
+ if (element.name == 'fromFunction') {
+ _validateFromFunction(node, element);
+ } else if (element.name == 'elementAt') {
+ _validateElementAt(node);
}
- }
- if (enclosingElement is ExtensionElement) {
- if (_isNativeFunctionPointerExtension(enclosingElement)) {
- if (element.name == 'asFunction') {
- _validateAsFunction(node, element);
- }
- } else if (_isDynamicLibraryExtension(enclosingElement) &&
- element.name == 'lookupFunction') {
+ } else if (enclosingElement.isNativeFunctionPointerExtension) {
+ if (element.name == 'asFunction') {
+ _validateAsFunction(node, element);
+ }
+ } else if (enclosingElement.isDynamicLibraryExtension) {
+ if (element.name == 'lookupFunction') {
_validateLookupFunction(node);
}
}
@@ -212,11 +205,9 @@
var element = node.staticElement;
if (element != null) {
var enclosingElement = element.enclosingElement;
- if (enclosingElement is ExtensionElement) {
- if (_isNativeStructPointerExtension(enclosingElement)) {
- if (element.name == 'ref') {
- _validateRefPrefixedIdentifier(node);
- }
+ if (enclosingElement.isNativeStructPointerExtension) {
+ if (element.name == 'ref') {
+ _validateRefPrefixedIdentifier(node);
}
}
}
@@ -228,140 +219,15 @@
var element = node.propertyName.staticElement;
if (element != null) {
var enclosingElement = element.enclosingElement;
- if (enclosingElement is ExtensionElement) {
- if (_isNativeStructPointerExtension(enclosingElement)) {
- if (element.name == 'ref') {
- _validateRefPropertyAccess(node);
- }
+ if (enclosingElement.isNativeStructPointerExtension) {
+ if (element.name == 'ref') {
+ _validateRefPropertyAccess(node);
}
}
}
super.visitPropertyAccess(node);
}
- /// Return `true` if the given [element] represents the extension
- /// `AllocatorAlloc`.
- bool _isAllocatorExtension(Element element) =>
- element.name == _allocatorExtensionName &&
- element.library?.name == _dartFfiLibraryName;
-
- /// Return `true` if the given [element] represents the class `CArray`.
- bool _isCArray(Element? element) =>
- element != null &&
- element.name == _carrayClassName &&
- element.library?.name == _dartFfiLibraryName;
-
- /// Return `true` if the [typeName] is the name of a type from `dart:ffi`.
- bool _isDartFfiClass(TypeName typeName) =>
- _isDartFfiElement(typeName.name.staticElement);
-
- /// Return `true` if the [element] is a class element from `dart:ffi`.
- bool _isDartFfiElement(Element? element) {
- if (element is ConstructorElement) {
- element = element.enclosingElement;
- }
- return element is ClassElement &&
- element.library.name == _dartFfiLibraryName;
- }
-
- /// Return `true` if the given [element] represents the extension
- /// `DynamicLibraryExtension`.
- bool _isDynamicLibraryExtension(Element element) =>
- element.name == 'DynamicLibraryExtension' &&
- element.library?.name == _dartFfiLibraryName;
-
- bool _isEmptyStruct(ClassElement classElement) {
- final fields = classElement.fields;
- var structFieldCount = 0;
- for (final field in fields) {
- final declaredType = field.type;
- if (declaredType.isDartCoreInt) {
- structFieldCount++;
- } else if (declaredType.isDartCoreDouble) {
- structFieldCount++;
- } else if (_isPointer(declaredType.element)) {
- structFieldCount++;
- } else if (_isStructClass(declaredType)) {
- structFieldCount++;
- } else if (_isCArray(declaredType.element)) {
- structFieldCount++;
- }
- }
- return structFieldCount == 0;
- }
-
- bool _isHandle(Element? element) =>
- element != null &&
- element.name == 'Handle' &&
- element.library?.name == _dartFfiLibraryName;
-
- /// Returns `true` iff [nativeType] is a `ffi.NativeFunction<???>` type.
- bool _isNativeFunctionInterfaceType(DartType nativeType) {
- if (nativeType is InterfaceType) {
- final element = nativeType.element;
- if (element.library.name == _dartFfiLibraryName) {
- return element.name == 'NativeFunction' &&
- nativeType.typeArguments.length == 1;
- }
- }
- return false;
- }
-
- bool _isNativeFunctionPointerExtension(Element? element) =>
- element != null &&
- element.name == 'NativeFunctionPointer' &&
- element.library?.name == _dartFfiLibraryName;
-
- bool _isNativeStructCArrayExtension(Element element) =>
- element.name == 'StructCArray' && element.library?.name == 'dart.ffi';
-
- bool _isNativeStructPointerExtension(Element element) =>
- element.name == 'StructPointer' && element.library?.name == 'dart.ffi';
-
- /// Returns `true` iff [nativeType] is a `ffi.NativeType` type.
- bool _isNativeTypeInterfaceType(DartType nativeType) {
- if (nativeType is InterfaceType) {
- final element = nativeType.element;
- if (element.library.name == _dartFfiLibraryName) {
- return element.name == 'NativeType';
- }
- }
- return false;
- }
-
- /// Returns `true` iff [nativeType] is a opaque type, i.e. a subtype of `Opaque`.
- bool _isOpaqueClass(DartType nativeType) {
- if (nativeType is InterfaceType) {
- final superType = nativeType.element.supertype;
- if (superType == null) {
- return false;
- }
- final superClassElement = superType.element;
- if (superClassElement.library.name == _dartFfiLibraryName) {
- return superClassElement.name == _opaqueClassName;
- }
- }
- return false;
- }
-
- /// Return `true` if the given [element] represents the class `Pointer`.
- bool _isPointer(Element? element) =>
- element != null &&
- element.name == 'Pointer' &&
- element.library?.name == _dartFfiLibraryName;
-
- /// Returns `true` iff [nativeType] is a `ffi.Pointer<???>` type.
- bool _isPointerInterfaceType(DartType nativeType) {
- if (nativeType is InterfaceType) {
- final element = nativeType.element;
- if (element.library.name == _dartFfiLibraryName) {
- return element.name == 'Pointer' &&
- nativeType.typeArguments.length == 1;
- }
- }
- return false;
- }
-
/// Returns `true` if [nativeType] is a C type that has a size.
bool _isSized(DartType nativeType) {
switch (_primitiveNativeType(nativeType)) {
@@ -376,48 +242,15 @@
case _PrimitiveDartType.none:
break;
}
- if (_isStructClass(nativeType)) {
+ if (nativeType.isStructSubtype) {
return true;
}
- if (_isPointer(nativeType.element)) {
+ if (nativeType.isPointer) {
return true;
}
return false;
}
- /// Returns `true` iff [nativeType] is a struct type.
- bool _isStructClass(DartType nativeType) {
- if (nativeType is InterfaceType) {
- final superType = nativeType.element.supertype;
- if (superType == null) {
- return false;
- }
- final superClassElement = superType.element;
- if (superClassElement.library.name == _dartFfiLibraryName) {
- return superClassElement.name == _structClassName &&
- nativeType.typeArguments.isEmpty;
- }
- }
- return false;
- }
-
- /// Return `true` if the [typeName] represents a subtype of `Struct`.
- bool _isSubtypeOfStruct(TypeName typeName) {
- var superType = typeName.name.staticElement;
- if (superType is ClassElement) {
- bool isStruct(InterfaceType? type) {
- return type != null &&
- type.element.name == _structClassName &&
- type.element.library.name == _dartFfiLibraryName;
- }
-
- return isStruct(superType.supertype) ||
- superType.interfaces.any(isStruct) ||
- superType.mixins.any(isStruct);
- }
- return false;
- }
-
/// Validates that the given type is a valid dart:ffi native function
/// signature.
bool _isValidFfiNativeFunctionType(DartType nativeType) {
@@ -454,19 +287,19 @@
(primitiveType != _PrimitiveDartType.void_ || allowVoid)) {
return true;
}
- if (_isNativeFunctionInterfaceType(nativeType)) {
+ if (nativeType.isNativeFunction) {
return _isValidFfiNativeFunctionType(nativeType.typeArguments.single);
}
- if (_isPointerInterfaceType(nativeType)) {
+ if (nativeType.isPointer) {
final nativeArgumentType = nativeType.typeArguments.single;
return _isValidFfiNativeType(nativeArgumentType,
allowVoid: true, allowEmptyStruct: true) ||
- _isStructClass(nativeArgumentType) ||
- _isNativeTypeInterfaceType(nativeArgumentType);
+ nativeArgumentType.isStructSubtype ||
+ nativeArgumentType.isNativeType;
}
- if (_isStructClass(nativeType)) {
+ if (nativeType.isStructSubtype) {
if (!allowEmptyStruct) {
- if (_isEmptyStruct(nativeType.element)) {
+ if (nativeType.element.isEmptyStruct) {
// TODO(dartbug.com/36780): This results in an error message not
// mentioning empty structs at all.
return false;
@@ -474,10 +307,10 @@
}
return true;
}
- if (_isOpaqueClass(nativeType)) {
+ if (nativeType.isOpaqueSubtype) {
return true;
}
- if (allowCArray && _isCArray(nativeType.element)) {
+ if (allowCArray && nativeType.isCArray) {
return _isValidFfiNativeType(nativeType.typeArguments.single,
allowVoid: false, allowEmptyStruct: false);
}
@@ -490,7 +323,7 @@
_PrimitiveDartType _primitiveNativeType(DartType nativeType) {
if (nativeType is InterfaceType) {
final element = nativeType.element;
- if (element.library.name == _dartFfiLibraryName) {
+ if (element.isFfiClass) {
final String name = element.name;
if (_primitiveIntegerNativeTypes.contains(name)) {
return _PrimitiveDartType.int;
@@ -547,7 +380,7 @@
bool requiredFound = false;
List<Annotation> extraAnnotations = [];
for (Annotation annotation in annotations) {
- if (_isDartFfiElement(annotation.element)) {
+ if (annotation.element.ffiClass != null) {
if (requiredFound) {
extraAnnotations.add(annotation);
} else {
@@ -587,11 +420,9 @@
}
var target = node.realTarget!;
var targetType = target.staticType;
- if (targetType is InterfaceType &&
- _isPointer(targetType.element) &&
- targetType.typeArguments.length == 1) {
+ if (targetType is InterfaceType && targetType.isPointer) {
final DartType T = targetType.typeArguments[0];
- if (!_isNativeFunctionInterfaceType(T) ||
+ if (!T.isNativeFunction ||
!_isValidFfiNativeFunctionType(
(T as InterfaceType).typeArguments.single)) {
final AstNode errorNode =
@@ -688,9 +519,7 @@
void _validateElementAt(MethodInvocation node) {
var targetType = node.realTarget?.staticType;
- if (targetType is InterfaceType &&
- _isPointer(targetType.element) &&
- targetType.typeArguments.length == 1) {
+ if (targetType is InterfaceType && targetType.isPointer) {
final DartType T = targetType.typeArguments[0];
if (!_isValidFfiNativeType(T, allowVoid: true, allowEmptyStruct: true)) {
@@ -721,18 +550,18 @@
_validateAnnotations(fieldType, annotations, _PrimitiveDartType.int);
} else if (declaredType.isDartCoreDouble) {
_validateAnnotations(fieldType, annotations, _PrimitiveDartType.double);
- } else if (_isPointer(declaredType.element)) {
+ } else if (declaredType.isPointer) {
_validateNoAnnotations(annotations);
- } else if (_isCArray(declaredType.element)) {
+ } else if (declaredType.isCArray) {
final typeArg = (declaredType as InterfaceType).typeArguments.single;
if (!_isSized(typeArg)) {
_errorReporter.reportErrorForNode(FfiCode.NON_SIZED_TYPE_ARGUMENT,
- fieldType, [_carrayClassName, typeArg.toString()]);
+ fieldType, [_cArrayClassName, typeArg.toString()]);
}
_validateSizeOfAnnotation(fieldType, annotations);
- } else if (_isStructClass(declaredType)) {
+ } else if (declaredType.isStructSubtype) {
final clazz = (declaredType as InterfaceType).element;
- if (_isEmptyStruct(clazz)) {
+ if (clazz.isEmptyStruct) {
_errorReporter
.reportErrorForNode(FfiCode.EMPTY_STRUCT, node, [clazz.name]);
}
@@ -777,9 +606,9 @@
// TODO(brianwilkerson) Validate that `f` is a top-level function.
final DartType R = (T as FunctionType).returnType;
if ((FT as FunctionType).returnType.isVoid ||
- _isPointer(R.element) ||
- _isHandle(R.element) ||
- _isStructClass(R)) {
+ R.isPointer ||
+ R.isHandle ||
+ R.isStructSubtype) {
if (argCount != 1) {
_errorReporter.reportErrorForNode(
FfiCode.INVALID_EXCEPTION_VALUE, node.argumentList.arguments[1]);
@@ -826,7 +655,7 @@
/// Validate that none of the [annotations] are from `dart:ffi`.
void _validateNoAnnotations(NodeList<Annotation> annotations) {
for (Annotation annotation in annotations) {
- if (_isDartFfiElement(annotation.element)) {
+ if (annotation.element.ffiClass != null) {
_errorReporter.reportErrorForNode(
FfiCode.ANNOTATION_ON_POINTER_FIELD, annotation);
}
@@ -886,7 +715,7 @@
final ffiSizeAnnotations = annotations.where((annotation) {
final element = annotation.element;
return element is ConstructorElement &&
- _isDartFfiElement(element) &&
+ element.ffiClass != null &&
element.enclosingElement.name == 'CArraySize';
}).toList();
@@ -922,3 +751,195 @@
handle,
none,
}
+
+extension on Element? {
+ /// Return `true` if this represents the extension `AllocatorAlloc`.
+ bool get isAllocatorExtension {
+ final element = this;
+ return element is ExtensionElement &&
+ element.name == FfiVerifier._allocatorExtensionName &&
+ element.isFfiExtension;
+ }
+
+ bool get isNativeFunctionPointerExtension {
+ final element = this;
+ return element is ExtensionElement &&
+ element.name == 'NativeFunctionPointer' &&
+ element.isFfiExtension;
+ }
+
+ bool get isNativeStructCArrayExtension {
+ final element = this;
+ return element is ExtensionElement &&
+ element.name == 'StructCArray' &&
+ element.isFfiExtension;
+ }
+
+ bool get isNativeStructPointerExtension {
+ final element = this;
+ return element is ExtensionElement &&
+ element.name == 'StructPointer' &&
+ element.isFfiExtension;
+ }
+
+ /// Return `true` if this represents the extension `DynamicLibraryExtension`.
+ bool get isDynamicLibraryExtension {
+ final element = this;
+ return element is ExtensionElement &&
+ element.name == 'DynamicLibraryExtension' &&
+ element.isFfiExtension;
+ }
+
+ /// Return `true` if this represents the class `Pointer`.
+ bool get isPointer {
+ final element = this;
+ return element is ClassElement &&
+ element.name == 'Pointer' &&
+ element.isFfiClass;
+ }
+
+ /// If this is a class element from `dart:ffi`, return it.
+ ClassElement? get ffiClass {
+ var element = this;
+ if (element is ConstructorElement) {
+ element = element.enclosingElement;
+ }
+ if (element is ClassElement && element.isFfiClass) {
+ return element;
+ }
+ return null;
+ }
+}
+
+extension on ClassElement {
+ bool get isEmptyStruct {
+ for (final field in fields) {
+ final declaredType = field.type;
+ if (declaredType.isDartCoreInt) {
+ return false;
+ } else if (declaredType.isDartCoreDouble) {
+ return false;
+ } else if (declaredType.isPointer) {
+ return false;
+ } else if (declaredType.isStructSubtype) {
+ return false;
+ } else if (declaredType.isCArray) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool get isFfiClass {
+ return library.name == FfiVerifier._dartFfiLibraryName;
+ }
+}
+
+extension on ExtensionElement {
+ bool get isFfiExtension {
+ return library.name == FfiVerifier._dartFfiLibraryName;
+ }
+}
+
+extension on DartType {
+ /// Return `true` if this represents the class `CArray`.
+ bool get isCArray {
+ final self = this;
+ if (self is InterfaceType) {
+ final element = self.element;
+ return element.name == FfiVerifier._cArrayClassName && element.isFfiClass;
+ }
+ return false;
+ }
+
+ bool get isPointer {
+ final self = this;
+ return self is InterfaceType && self.element.isPointer;
+ }
+
+ bool get isHandle {
+ final self = this;
+ if (self is InterfaceType) {
+ final element = self.element;
+ return element.name == 'Handle' && element.isFfiClass;
+ }
+ return false;
+ }
+
+ /// Returns `true` iff this is a `ffi.NativeFunction<???>` type.
+ bool get isNativeFunction {
+ final self = this;
+ if (self is InterfaceType) {
+ final element = self.element;
+ return element.name == 'NativeFunction' && element.isFfiClass;
+ }
+ return false;
+ }
+
+ /// Returns `true` iff this is a `ffi.NativeType` type.
+ bool get isNativeType {
+ final self = this;
+ if (self is InterfaceType) {
+ final element = self.element;
+ return element.name == 'NativeType' && element.isFfiClass;
+ }
+ return false;
+ }
+
+ /// Returns `true` iff this is a opaque type, i.e. a subtype of `Opaque`.
+ bool get isOpaqueSubtype {
+ final self = this;
+ if (self is InterfaceType) {
+ final superType = self.element.supertype;
+ if (superType != null) {
+ final superClassElement = superType.element;
+ return superClassElement.name == FfiVerifier._opaqueClassName &&
+ superClassElement.isFfiClass;
+ }
+ }
+ return false;
+ }
+
+ bool get isStruct {
+ final self = this;
+ if (self is InterfaceType) {
+ final element = self.element;
+ return element.name == FfiVerifier._structClassName && element.isFfiClass;
+ }
+ return false;
+ }
+
+ /// Returns `true` if this is a struct type, i.e. a subtype of `Struct`.
+ bool get isStructSubtype {
+ final self = this;
+ if (self is InterfaceType) {
+ final superType = self.element.supertype;
+ if (superType != null) {
+ return superType.isStruct;
+ }
+ }
+ return false;
+ }
+}
+
+extension on TypeName {
+ /// If this is a name of class from `dart:ffi`, return it.
+ ClassElement? get ffiClass {
+ return name.staticElement.ffiClass;
+ }
+
+ /// Return `true` if this represents a subtype of `Struct`.
+ bool get isStructSubtype {
+ var element = name.staticElement;
+ if (element is ClassElement) {
+ bool isStruct(InterfaceType? type) {
+ return type != null && type.isStruct;
+ }
+
+ return isStruct(element.supertype) ||
+ element.interfaces.any(isStruct) ||
+ element.mixins.any(isStruct);
+ }
+ return false;
+ }
+}