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; + } +}