blob: 8cc04cfa2f6be3a89012e2fae2d514582b642cce [file] [log] [blame]
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/dart/element/type_system.dart';
import 'package:analyzer/src/dart/error/ffi_code.dart';
/// A visitor used to find problems with the way the `dart:ffi` APIs are being
/// used. See 'pkg/vm/lib/transformations/ffi_checks.md' for the specification
/// of the desired hints.
class FfiVerifier extends RecursiveAstVisitor<void> {
static const _allocatorClassName = 'Allocator';
static const _allocateExtensionMethodName = 'call';
static const _allocatorExtensionName = 'AllocatorAlloc';
static const _dartFfiLibraryName = 'dart.ffi';
static const _opaqueClassName = 'Opaque';
static const List<String> _primitiveIntegerNativeTypes = [
'Int8',
'Int16',
'Int32',
'Int64',
'Uint8',
'Uint16',
'Uint32',
'Uint64',
'IntPtr'
];
static const List<String> _primitiveDoubleNativeTypes = [
'Float',
'Double',
];
static const _structClassName = 'Struct';
/// The type system used to check types.
final TypeSystemImpl typeSystem;
/// The error reporter used to report errors.
final ErrorReporter _errorReporter;
/// A flag indicating whether we are currently visiting inside a subclass of
/// `Struct`.
bool inStruct = false;
/// Initialize a newly created verifier.
FfiVerifier(this.typeSystem, this._errorReporter);
@override
void visitClassDeclaration(ClassDeclaration node) {
inStruct = false;
// Only the Allocator, Opaque and Struct class may be extended.
ExtendsClause extendsClause = node.extendsClause;
if (extendsClause != null) {
final TypeName superclass = extendsClause.superclass;
if (_isDartFfiClass(superclass)) {
final className = superclass.name.staticElement.name;
if (className == _structClassName) {
inStruct = true;
if (_isEmptyStruct(node.declaredElement)) {
_errorReporter.reportErrorForNode(
FfiCode.EMPTY_STRUCT_WARNING, node, [node.name]);
}
} else if (className != _allocatorClassName &&
className != _opaqueClassName) {
_errorReporter.reportErrorForNode(
FfiCode.SUBTYPE_OF_FFI_CLASS_IN_EXTENDS,
superclass.name,
[node.name.name, superclass.name.name]);
}
} else if (_isSubtypeOfStruct(superclass)) {
_errorReporter.reportErrorForNode(
FfiCode.SUBTYPE_OF_STRUCT_CLASS_IN_EXTENDS,
superclass,
[node.name.name, superclass.name.name]);
}
}
// No classes from the FFI may be explicitly implemented.
void checkSupertype(TypeName typename, FfiCode subtypeOfFfiCode,
FfiCode subtypeOfStructCode) {
final superName = typename.name.staticElement?.name;
if (superName == _allocatorClassName) {
return;
}
if (_isDartFfiClass(typename)) {
_errorReporter.reportErrorForNode(
subtypeOfFfiCode, typename, [node.name, typename.name]);
} else if (_isSubtypeOfStruct(typename)) {
_errorReporter.reportErrorForNode(
subtypeOfStructCode, typename, [node.name, typename.name]);
}
}
ImplementsClause implementsClause = node.implementsClause;
if (implementsClause != null) {
for (TypeName type in implementsClause.interfaces) {
checkSupertype(type, FfiCode.SUBTYPE_OF_FFI_CLASS_IN_IMPLEMENTS,
FfiCode.SUBTYPE_OF_STRUCT_CLASS_IN_IMPLEMENTS);
}
}
WithClause withClause = node.withClause;
if (withClause != null) {
for (TypeName type in withClause.mixinTypes) {
checkSupertype(type, FfiCode.SUBTYPE_OF_FFI_CLASS_IN_WITH,
FfiCode.SUBTYPE_OF_STRUCT_CLASS_IN_WITH);
}
}
if (inStruct && node.declaredElement.typeParameters.isNotEmpty) {
_errorReporter.reportErrorForNode(
FfiCode.GENERIC_STRUCT_SUBCLASS, node.name, [node.name]);
}
super.visitClassDeclaration(node);
}
@override
void visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
if (inStruct) {
_errorReporter.reportErrorForNode(
FfiCode.FIELD_INITIALIZER_IN_STRUCT, node);
}
super.visitConstructorFieldInitializer(node);
}
@override
void visitFieldDeclaration(FieldDeclaration node) {
if (inStruct) {
_validateFieldsInStruct(node);
}
super.visitFieldDeclaration(node);
}
@override
void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
Element element = node.staticElement;
if (element is MethodElement) {
Element enclosingElement = element.enclosingElement;
if (enclosingElement is ExtensionElement) {
if (_isAllocatorExtension(enclosingElement) &&
element.name == _allocateExtensionMethodName) {
_validateAllocate(node);
}
}
}
super.visitFunctionExpressionInvocation(node);
}
@override
void visitIndexExpression(IndexExpression node) {
Element element = node.staticElement;
Element enclosingElement = element?.enclosingElement;
if (enclosingElement is ExtensionElement) {
if (_isNativeStructPointerExtension(enclosingElement)) {
if (element.name == '[]') {
_validateRefIndexed(node);
}
}
}
}
@override
void visitMethodInvocation(MethodInvocation node) {
Element 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 is ExtensionElement) {
if (_isNativeFunctionPointerExtension(enclosingElement)) {
if (element.name == 'asFunction') {
_validateAsFunction(node, element);
}
} else if (_isDynamicLibraryExtension(enclosingElement) &&
element.name == 'lookupFunction') {
_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);
}
@override
void visitPrefixedIdentifier(PrefixedIdentifier node) {
Element element = node.staticElement;
Element enclosingElement = element?.enclosingElement;
if (enclosingElement is ExtensionElement) {
if (_isNativeStructPointerExtension(enclosingElement)) {
if (element.name == 'ref') {
_validateRefPrefixedIdentifier(node);
}
}
}
super.visitPrefixedIdentifier(node);
}
@override
void visitPropertyAccess(PropertyAccess node) {
Element element = node.propertyName.staticElement;
Element enclosingElement = element?.enclosingElement;
if (enclosingElement is ExtensionElement) {
if (_isNativeStructPointerExtension(enclosingElement)) {
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 [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++;
}
}
return structFieldCount == 0;
}
bool _isHandle(Element element) =>
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.name == 'NativeFunctionPointer' &&
element.library.name == _dartFfiLibraryName;
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.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` 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 == true;
}
}
return false;
}
/// Return `true` if the [typeName] represents a subtype of `Struct`.
bool _isSubtypeOfStruct(TypeName typeName) {
Element 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) {
if (nativeType is FunctionType && !nativeType.isDartCoreFunction) {
if (nativeType.namedParameterTypes.isNotEmpty ||
nativeType.optionalParameterTypes.isNotEmpty) {
return false;
}
if (!_isValidFfiNativeType(nativeType.returnType, true, false)) {
return false;
}
for (final DartType typeArg in nativeType.normalParameterTypes) {
if (!_isValidFfiNativeType(typeArg, false, false)) {
return false;
}
}
return true;
}
return false;
}
/// Validates that the given [nativeType] is a valid dart:ffi native type.
// TODO(https://dartbug.com/44747): Change to named arguments.
bool _isValidFfiNativeType(
DartType nativeType, bool allowVoid, bool allowEmptyStruct) {
if (nativeType is InterfaceType) {
// Is it a primitive integer/double type (or ffi.Void if we allow it).
final primitiveType = _primitiveNativeType(nativeType);
if (primitiveType != _PrimitiveDartType.none &&
(primitiveType != _PrimitiveDartType.void_ || allowVoid)) {
return true;
}
if (_isNativeFunctionInterfaceType(nativeType)) {
return _isValidFfiNativeFunctionType(nativeType.typeArguments.single);
}
if (_isPointerInterfaceType(nativeType)) {
final nativeArgumentType = nativeType.typeArguments.single;
return _isValidFfiNativeType(nativeArgumentType, true, true) ||
_isStructClass(nativeArgumentType) ||
_isNativeTypeInterfaceType(nativeArgumentType);
}
if (_isStructClass(nativeType)) {
if (!allowEmptyStruct) {
if (_isEmptyStruct(nativeType.element)) {
// TODO(dartbug.com/36780): This results in an error message not
// mentioning empty structs at all.
return false;
}
}
return true;
}
if (_isOpaqueClass(nativeType)) {
return true;
}
} else if (nativeType is FunctionType) {
return _isValidFfiNativeFunctionType(nativeType);
}
return false;
}
_PrimitiveDartType _primitiveNativeType(DartType nativeType) {
if (nativeType is InterfaceType) {
final element = nativeType.element;
if (element.library.name == _dartFfiLibraryName) {
final String name = element.name;
if (_primitiveIntegerNativeTypes.contains(name)) {
return _PrimitiveDartType.int;
}
if (_primitiveDoubleNativeTypes.contains(name)) {
return _PrimitiveDartType.double;
}
if (name == 'Void') {
return _PrimitiveDartType.void_;
}
if (name == 'Handle') {
return _PrimitiveDartType.handle;
}
}
}
return _PrimitiveDartType.none;
}
/// Return an indication of the Dart type associated with the [annotation].
_PrimitiveDartType _typeForAnnotation(Annotation annotation) {
Element element = annotation.element;
if (element is ConstructorElement) {
String name = element.enclosingElement.name;
if (_primitiveIntegerNativeTypes.contains(name)) {
return _PrimitiveDartType.int;
} else if (_primitiveDoubleNativeTypes.contains(name)) {
return _PrimitiveDartType.double;
}
}
return _PrimitiveDartType.none;
}
void _validateAllocate(FunctionExpressionInvocation node) {
final DartType dartType = node.typeArgumentTypes[0];
if (!_isValidFfiNativeType(dartType, true, true)) {
final AstNode errorNode = node;
_errorReporter.reportErrorForNode(
FfiCode.NON_CONSTANT_TYPE_ARGUMENT,
errorNode,
['$_allocatorExtensionName.$_allocateExtensionMethodName']);
}
}
/// Validate that the [annotations] include exactly one annotation that
/// satisfies the [requiredTypes]. If an error is produced that cannot be
/// associated with an annotation, associate it with the [errorNode].
void _validateAnnotations(AstNode errorNode, NodeList<Annotation> annotations,
_PrimitiveDartType requiredType) {
bool requiredFound = false;
List<Annotation> extraAnnotations = [];
for (Annotation annotation in annotations) {
if (_isDartFfiElement(annotation.element)) {
if (requiredFound) {
extraAnnotations.add(annotation);
} else {
_PrimitiveDartType foundType = _typeForAnnotation(annotation);
if (foundType == requiredType) {
requiredFound = true;
} else {
extraAnnotations.add(annotation);
}
}
}
}
if (extraAnnotations.isNotEmpty) {
if (!requiredFound) {
Annotation invalidAnnotation = extraAnnotations.removeAt(0);
_errorReporter.reportErrorForNode(
FfiCode.MISMATCHED_ANNOTATION_ON_STRUCT_FIELD, invalidAnnotation);
}
for (Annotation extraAnnotation in extraAnnotations) {
_errorReporter.reportErrorForNode(
FfiCode.EXTRA_ANNOTATION_ON_STRUCT_FIELD, extraAnnotation);
}
} else if (!requiredFound) {
_errorReporter.reportErrorForNode(
FfiCode.MISSING_ANNOTATION_ON_STRUCT_FIELD, errorNode);
}
}
/// Validate the invocation of the instance method
/// `Pointer<T>.asFunction<F>()`.
void _validateAsFunction(MethodInvocation node, MethodElement element) {
NodeList<TypeAnnotation> typeArguments = node.typeArguments?.arguments;
if (typeArguments != null && typeArguments.length == 1) {
if (_validateTypeArgument(typeArguments[0], 'asFunction')) {
return;
}
}
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 (!_isNativeFunctionInterfaceType(T) ||
!_isValidFfiNativeFunctionType(
(T as InterfaceType).typeArguments.single)) {
final AstNode errorNode =
typeArguments != null ? typeArguments[0] : node;
_errorReporter.reportErrorForNode(
FfiCode.NON_NATIVE_FUNCTION_TYPE_ARGUMENT_TO_POINTER,
errorNode,
[T]);
return;
}
final DartType TPrime = (T as InterfaceType).typeArguments[0];
final DartType F = node.typeArgumentTypes[0];
if (!_validateCompatibleFunctionTypes(F, TPrime)) {
_errorReporter.reportErrorForNode(
FfiCode.MUST_BE_A_SUBTYPE, node, [TPrime, F, 'asFunction']);
}
}
}
/// Validates that the given [nativeType] is, when native types are converted
/// to their Dart equivalent, a subtype of [dartType].
bool _validateCompatibleFunctionTypes(
DartType dartType, DartType nativeType) {
// We require both to be valid function types.
if (dartType is! FunctionType ||
dartType.isDartCoreFunction ||
nativeType is! FunctionType ||
nativeType.isDartCoreFunction) {
return false;
}
final dartFType = dartType as FunctionType;
final nativeFType = nativeType as FunctionType;
// We disallow any optional parameters.
final int parameterCount = dartFType.normalParameterTypes.length;
if (parameterCount != nativeFType.normalParameterTypes.length) {
return false;
}
// We disallow generic function types.
if (dartFType.typeFormals.isNotEmpty ||
nativeFType.typeFormals.isNotEmpty) {
return false;
}
if (dartFType.namedParameterTypes.isNotEmpty ||
dartFType.optionalParameterTypes.isNotEmpty ||
nativeFType.namedParameterTypes.isNotEmpty ||
nativeFType.optionalParameterTypes.isNotEmpty) {
return false;
}
// Validate that the return types are compatible.
if (!_validateCompatibleNativeType(
dartFType.returnType, nativeFType.returnType, false)) {
return false;
}
// Validate that the parameter types are compatible.
for (int i = 0; i < parameterCount; ++i) {
if (!_validateCompatibleNativeType(dartFType.normalParameterTypes[i],
nativeFType.normalParameterTypes[i], true)) {
return false;
}
}
// Signatures have same number of parameters and the types match.
return true;
}
/// Validates that, if we convert [nativeType] to it's corresponding
/// [dartType] the latter is a subtype of the former if
/// [checkCovariance].
bool _validateCompatibleNativeType(
DartType dartType, DartType nativeType, bool checkCovariance) {
final nativeReturnType = _primitiveNativeType(nativeType);
if (nativeReturnType == _PrimitiveDartType.int) {
return dartType.isDartCoreInt;
} else if (nativeReturnType == _PrimitiveDartType.double) {
return dartType.isDartCoreDouble;
} else if (nativeReturnType == _PrimitiveDartType.void_) {
return dartType.isVoid;
} else if (nativeReturnType == _PrimitiveDartType.handle) {
InterfaceType objectType = typeSystem.objectStar;
return checkCovariance
? /* everything is subtype of objectStar */ true
: typeSystem.isSubtypeOf2(objectType, dartType);
} else if (dartType is InterfaceType && nativeType is InterfaceType) {
return checkCovariance
? typeSystem.isSubtypeOf2(dartType, nativeType)
: typeSystem.isSubtypeOf2(nativeType, dartType);
} else {
// If the [nativeType] is not a primitive int/double type then it has to
// be a Pointer type atm.
return false;
}
}
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) {
if (node.isStatic) {
return;
}
VariableDeclarationList fields = node.fields;
NodeList<Annotation> annotations = node.metadata;
TypeAnnotation fieldType = fields.type;
if (fieldType == null) {
_errorReporter.reportErrorForNode(
FfiCode.MISSING_FIELD_TYPE_IN_STRUCT, fields.variables[0].name);
} else {
DartType declaredType = fieldType.type;
if (declaredType.isDartCoreInt) {
_validateAnnotations(fieldType, annotations, _PrimitiveDartType.int);
} else if (declaredType.isDartCoreDouble) {
_validateAnnotations(fieldType, annotations, _PrimitiveDartType.double);
} else if (_isPointer(declaredType.element)) {
_validateNoAnnotations(annotations);
} else if (_isStructClass(declaredType)) {
final clazz = (declaredType as InterfaceType).element;
if (_isEmptyStruct(clazz)) {
_errorReporter
.reportErrorForNode(FfiCode.EMPTY_STRUCT, node, [clazz.name]);
}
} else {
_errorReporter.reportErrorForNode(FfiCode.INVALID_FIELD_TYPE_IN_STRUCT,
fieldType, [fieldType.toSource()]);
}
}
for (VariableDeclaration field in fields.variables) {
if (field.initializer != null) {
_errorReporter.reportErrorForNode(
FfiCode.FIELD_IN_STRUCT_WITH_INITIALIZER, field.name);
}
}
}
/// Validate the invocation of the static method
/// `Pointer<T>.fromFunction(f, e)`.
void _validateFromFunction(MethodInvocation node, MethodElement element) {
final int argCount = node.argumentList.arguments.length;
if (argCount < 1 || argCount > 2) {
// There are other diagnostics reported against the invocation and the
// diagnostics generated below might be inaccurate, so don't report them.
return;
}
final DartType T = node.typeArgumentTypes[0];
if (!_isValidFfiNativeFunctionType(T)) {
_errorReporter.reportErrorForNode(
FfiCode.MUST_BE_A_NATIVE_FUNCTION_TYPE, node, [T, 'fromFunction']);
return;
}
Expression f = node.argumentList.arguments[0];
DartType FT = f.staticType;
if (!_validateCompatibleFunctionTypes(FT, T)) {
_errorReporter.reportErrorForNode(
FfiCode.MUST_BE_A_SUBTYPE, f, [f.staticType, T, 'fromFunction']);
return;
}
// 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)) {
if (argCount != 1) {
_errorReporter.reportErrorForNode(
FfiCode.INVALID_EXCEPTION_VALUE, node.argumentList.arguments[1]);
}
} else if (argCount != 2) {
_errorReporter.reportErrorForNode(
FfiCode.MISSING_EXCEPTION_VALUE, node.methodName);
} else {
Expression e = node.argumentList.arguments[1];
// TODO(brianwilkerson) Validate that `e` is a constant expression.
if (!_validateCompatibleNativeType(e.staticType, R, true)) {
_errorReporter.reportErrorForNode(
FfiCode.MUST_BE_A_SUBTYPE, e, [e.staticType, R, 'fromFunction']);
}
}
}
/// Validate the invocation of the instance method
/// `DynamicLibrary.lookupFunction<S, F>()`.
void _validateLookupFunction(MethodInvocation node) {
final NodeList<TypeAnnotation> typeArguments =
node.typeArguments?.arguments;
if (typeArguments?.length != 2) {
// There are other diagnostics reported against the invocation and the
// diagnostics generated below might be inaccurate, so don't report them.
return;
}
final List<DartType> argTypes = node.typeArgumentTypes;
final DartType S = argTypes[0];
final DartType F = argTypes[1];
if (!_isValidFfiNativeFunctionType(S)) {
final AstNode errorNode = typeArguments[0];
_errorReporter.reportErrorForNode(FfiCode.MUST_BE_A_NATIVE_FUNCTION_TYPE,
errorNode, [S, 'lookupFunction']);
return;
}
if (!_validateCompatibleFunctionTypes(F, S)) {
final AstNode errorNode = typeArguments[1];
_errorReporter.reportErrorForNode(
FfiCode.MUST_BE_A_SUBTYPE, errorNode, [S, F, 'lookupFunction']);
}
}
/// Validate that none of the [annotations] are from `dart:ffi`.
void _validateNoAnnotations(NodeList<Annotation> annotations) {
for (Annotation annotation in annotations) {
if (_isDartFfiElement(annotation.element)) {
_errorReporter.reportErrorForNode(
FfiCode.ANNOTATION_ON_POINTER_FIELD, annotation);
}
}
}
void _validateRefIndexed(IndexExpression node) {
DartType targetType = node.target.staticType;
if (!_isValidFfiNativeType(targetType, false, true)) {
final AstNode errorNode = node;
_errorReporter.reportErrorForNode(
FfiCode.NON_CONSTANT_TYPE_ARGUMENT_WARNING, errorNode, ['[]']);
}
}
/// Validate the invocation of the extension method
/// `Pointer<T extends Struct>.ref`.
void _validateRefPrefixedIdentifier(PrefixedIdentifier node) {
DartType targetType = node.prefix.staticType;
if (!_isValidFfiNativeType(targetType, false, true)) {
final AstNode errorNode = node;
_errorReporter.reportErrorForNode(
FfiCode.NON_CONSTANT_TYPE_ARGUMENT_WARNING, errorNode, ['ref']);
}
}
void _validateRefPropertyAccess(PropertyAccess node) {
DartType targetType = node.target.staticType;
if (!_isValidFfiNativeType(targetType, false, true)) {
final AstNode errorNode = node;
_errorReporter.reportErrorForNode(
FfiCode.NON_CONSTANT_TYPE_ARGUMENT_WARNING, errorNode, ['ref']);
}
}
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) {
if (typeArgument.type is TypeParameterType) {
_errorReporter.reportErrorForNode(
FfiCode.NON_CONSTANT_TYPE_ARGUMENT, typeArgument, [functionName]);
return true;
}
return false;
}
}
enum _PrimitiveDartType {
double,
int,
void_,
handle,
none,
}