Skip to content

Commit

Permalink
[analyzer/ffi] ABI-specific type support in analyzer
Browse files Browse the repository at this point in the history
TEST=pkg/analyzer/test/src/diagnostics/abi_specific_integer_mapping_test.dart

Bug: #42816

Change-Id: I7ae6e05073e6f28a42b5f8f7d06c9b5b91c510a7
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/221821
Reviewed-by: Brian Wilkerson <[email protected]>
  • Loading branch information
dcharkes authored and Commit Bot committed Dec 2, 2021
1 parent 734eb8e commit c3b5082
Show file tree
Hide file tree
Showing 7 changed files with 289 additions and 9 deletions.
3 changes: 3 additions & 0 deletions pkg/analyzer/lib/error/error.dart
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,9 @@ const List<ErrorCode> errorCodeValues = [
CompileTimeErrorCode.YIELD_IN_NON_GENERATOR,
CompileTimeErrorCode.YIELD_EACH_OF_INVALID_TYPE,
CompileTimeErrorCode.YIELD_OF_INVALID_TYPE,
FfiCode.ABI_SPECIFIC_INTEGER_MAPPING_EXTRA,
FfiCode.ABI_SPECIFIC_INTEGER_MAPPING_MISSING,
FfiCode.ABI_SPECIFIC_INTEGER_MAPPING_UNSUPPORTED,
FfiCode.ANNOTATION_ON_POINTER_FIELD,
FfiCode.ARGUMENT_MUST_BE_A_CONSTANT,
FfiCode.CREATION_OF_STRUCT_OR_UNION,
Expand Down
32 changes: 30 additions & 2 deletions pkg/analyzer/lib/src/dart/error/ffi_code.g.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,34 @@ import "package:analyzer/src/error/analyzer_error_code.dart";
// ignore_for_file: slash_for_doc_comments

class FfiCode extends AnalyzerErrorCode {
/**
* No parameters.
*/
static const FfiCode ABI_SPECIFIC_INTEGER_MAPPING_EXTRA = FfiCode(
'ABI_SPECIFIC_INTEGER_MAPPING_EXTRA',
"Classes extending 'AbiSpecificInteger' must have exactly one 'AbiSpecificIntegerMapping' annotation specifying the mapping from ABI to a 'NativeType' integer with a fixed size.",
correctionMessage: "Try removing the extra annotation.",
);

/**
* No parameters.
*/
static const FfiCode ABI_SPECIFIC_INTEGER_MAPPING_MISSING = FfiCode(
'ABI_SPECIFIC_INTEGER_MAPPING_MISSING',
"Classes extending 'AbiSpecificInteger' must have exactly one 'AbiSpecificIntegerMapping' annotation specifying the mapping from ABI to a 'NativeType' integer with a fixed size.",
correctionMessage: "Try adding an annotation.",
);

/**
* No parameters.
*/
static const FfiCode ABI_SPECIFIC_INTEGER_MAPPING_UNSUPPORTED = FfiCode(
'ABI_SPECIFIC_INTEGER_MAPPING_UNSUPPORTED',
"Only mappings to 'Int8', 'Int16', 'Int32', 'Int64', 'Uint8', 'Uint16', 'UInt32', and 'Uint64' are supported.",
correctionMessage:
"Try changing the value to 'Int8', 'Int16', 'Int32', 'Int64', 'Uint8', 'Uint16', 'UInt32', or 'Uint64'.",
);

/**
* No parameters.
*/
Expand Down Expand Up @@ -302,9 +330,9 @@ class FfiCode extends AnalyzerErrorCode {
*/
static const FfiCode NON_SIZED_TYPE_ARGUMENT = FfiCode(
'NON_SIZED_TYPE_ARGUMENT',
"Type arguments to '{0}' can't have the type '{1}'. They can only be declared as native integer, 'Float', 'Double', 'Pointer', or subtype of 'Struct' or 'Union'.",
"Type arguments to '{0}' can't have the type '{1}'. They can only be declared as native integer, 'Float', 'Double', 'Pointer', or subtype of 'Struct', 'Union', or 'AbiSpecificInteger'.",
correctionMessage:
"Try using a native integer, 'Float', 'Double', 'Pointer', or subtype of 'Struct' or 'Union'.",
"Try using a native integer, 'Float', 'Double', 'Pointer', or subtype of 'Struct', 'Union', or 'AbiSpecificInteger'.",
);

/**
Expand Down
114 changes: 110 additions & 4 deletions pkg/analyzer/lib/src/generated/ffi_verifier.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ import 'package:analyzer/src/dart/error/ffi_code.dart';
/// used. See 'pkg/vm/lib/transformations/ffi_checks.md' for the specification
/// of the desired hints.
class FfiVerifier extends RecursiveAstVisitor<void> {
static const _abiSpecificIntegerClassName = 'AbiSpecificInteger';
static const _abiSpecificIntegerMappingClassName =
'AbiSpecificIntegerMapping';
static const _allocatorClassName = 'Allocator';
static const _allocateExtensionMethodName = 'call';
static const _allocatorExtensionName = 'AllocatorAlloc';
Expand All @@ -25,7 +28,7 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
static const _opaqueClassName = 'Opaque';
static const _ffiNativeName = 'FfiNative';

static const Set<String> _primitiveIntegerNativeTypes = {
static const Set<String> _primitiveIntegerNativeTypesFixedSize = {
'Int8',
'Int16',
'Int32',
Expand All @@ -34,6 +37,9 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
'Uint16',
'Uint32',
'Uint64',
};
static const Set<String> _primitiveIntegerNativeTypes = {
..._primitiveIntegerNativeTypesFixedSize,
'IntPtr'
};

Expand Down Expand Up @@ -85,8 +91,12 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
if (className == _structClassName) {
_validatePackedAnnotation(node.metadata);
}
} else if (className == _abiSpecificIntegerClassName) {
_validateAbiSpecificIntegerMappingAnnotation(
node.name, node.metadata);
} else if (className != _allocatorClassName &&
className != _opaqueClassName) {
className != _opaqueClassName &&
className != _abiSpecificIntegerClassName) {
_errorReporter.reportErrorForNode(
FfiCode.SUBTYPE_OF_FFI_CLASS_IN_EXTENDS,
superclass.name,
Expand Down Expand Up @@ -452,6 +462,9 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
if (nativeType.isArray) {
return true;
}
if (nativeType.isAbiSpecificIntegerSubtype) {
return true;
}
return false;
}

Expand Down Expand Up @@ -523,6 +536,9 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
if (nativeType.isOpaqueSubtype) {
return true;
}
if (nativeType.isAbiSpecificIntegerSubtype) {
return true;
}
if (allowArray && nativeType.isArray) {
return _isValidFfiNativeType(nativeType.typeArguments.single,
allowVoid: false, allowEmptyStruct: false);
Expand Down Expand Up @@ -591,10 +607,51 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
} else if (_primitiveBoolNativeType == name) {
return _PrimitiveDartType.bool;
}
if (element.type.returnType.isAbiSpecificIntegerSubtype) {
return _PrimitiveDartType.int;
}
}
return _PrimitiveDartType.none;
}

/// Validate that the [annotations] include at most one mapping annotation.
void _validateAbiSpecificIntegerMappingAnnotation(
AstNode errorNode, NodeList<Annotation> annotations) {
final ffiPackedAnnotations = annotations
.where((annotation) => annotation.isAbiSpecificIntegerMapping)
.toList();

if (ffiPackedAnnotations.isEmpty) {
_errorReporter.reportErrorForNode(
FfiCode.ABI_SPECIFIC_INTEGER_MAPPING_MISSING, errorNode);
return;
}

if (ffiPackedAnnotations.length > 1) {
final extraAnnotations = ffiPackedAnnotations.skip(1);
for (final annotation in extraAnnotations) {
_errorReporter.reportErrorForNode(
FfiCode.ABI_SPECIFIC_INTEGER_MAPPING_EXTRA, annotation.name);
}
}

final annotationConstant =
ffiPackedAnnotations.first.elementAnnotation?.computeConstantValue();
final mappingValues = annotationConstant?.getField('mapping')?.toMapValue();
if (mappingValues == null) {
return;
}
for (final nativeType in mappingValues.values) {
final nativeTypeName = nativeType?.type?.element?.name;
if (nativeTypeName != null &&
!_primitiveIntegerNativeTypesFixedSize.contains(nativeTypeName)) {
_errorReporter.reportErrorForNode(
FfiCode.ABI_SPECIFIC_INTEGER_MAPPING_UNSUPPORTED,
ffiPackedAnnotations.first.name);
}
}
}

void _validateAllocate(FunctionExpressionInvocation node) {
final typeArgumentTypes = node.typeArgumentTypes;
if (typeArgumentTypes == null || typeArgumentTypes.length != 1) {
Expand All @@ -619,7 +676,9 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
bool requiredFound = false;
List<Annotation> extraAnnotations = [];
for (Annotation annotation in annotations) {
if (annotation.element.ffiClass != null) {
if (annotation.element.ffiClass != null ||
annotation.element?.enclosingElement.isAbiSpecificIntegerSubclass ==
true) {
if (requiredFound) {
extraAnnotations.add(annotation);
} else {
Expand Down Expand Up @@ -744,7 +803,10 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
bool _validateCompatibleNativeType(
DartType dartType, DartType nativeType, bool checkCovariance) {
final nativeReturnType = _primitiveNativeType(nativeType);
if (nativeReturnType == _PrimitiveDartType.int) {
if (nativeReturnType == _PrimitiveDartType.int ||
(nativeType is InterfaceType &&
nativeType.superclass?.element.name ==
_abiSpecificIntegerClassName)) {
return dartType.isDartCoreInt;
} else if (nativeReturnType == _PrimitiveDartType.double) {
return dartType.isDartCoreDouble;
Expand Down Expand Up @@ -1197,6 +1259,14 @@ extension on Annotation {
element.ffiClass != null &&
element.enclosingElement.name == 'Packed';
}

bool get isAbiSpecificIntegerMapping {
final element = this.element;
return element is ConstructorElement &&
element.ffiClass != null &&
element.enclosingElement.name ==
FfiVerifier._abiSpecificIntegerMappingClassName;
}
}

extension on ElementAnnotation {
Expand Down Expand Up @@ -1332,6 +1402,21 @@ extension on Element? {
return element is ClassElement && element.supertype.isUnion;
}

/// Return `true` if this represents the class `AbiSpecificInteger`.
bool get isAbiSpecificInteger {
final element = this;
return element is ClassElement &&
element.name == FfiVerifier._abiSpecificIntegerClassName &&
element.isFfiClass;
}

/// Return `true` if this represents a subclass of the class
/// `AbiSpecificInteger`.
bool get isAbiSpecificIntegerSubclass {
final element = this;
return element is ClassElement && element.supertype.isAbiSpecificInteger;
}

/// If this is a class element from `dart:ffi`, return it.
ClassElement? get ffiClass {
var element = this;
Expand Down Expand Up @@ -1398,6 +1483,11 @@ extension on DartType? {
final self = this;
return self is InterfaceType && self.element.isUnion;
}

bool get isAbiSpecificInteger {
final self = this;
return self is InterfaceType && self.element.isAbiSpecificInteger;
}
}

extension on DartType {
Expand Down Expand Up @@ -1467,6 +1557,22 @@ extension on DartType {
return false;
}

/// Returns `true` iff this is an Abi-specific integer type,
/// i.e. a subtype of `AbiSpecificInteger`.
bool get isAbiSpecificIntegerSubtype {
final self = this;
if (self is InterfaceType) {
final superType = self.element.supertype;
if (superType != null) {
final superClassElement = superType.element;
return superClassElement.name ==
FfiVerifier._abiSpecificIntegerClassName &&
superClassElement.isFfiClass;
}
}
return false;
}

/// Returns `true` iff this is a opaque type, i.e. a subtype of `Opaque`.
bool get isOpaqueSubtype {
final self = this;
Expand Down
50 changes: 49 additions & 1 deletion pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ abstract class Future<T> {
Future<T> whenComplete(action());
static Future<List<T>> wait<T>(Iterable<Future<T>> futures,
static Future<List<T>> wait<T>(Iterable<Future<T>> futures,
{void cleanUp(T successValue)?}) => throw 0;
}
Expand Down Expand Up @@ -705,6 +705,10 @@ class Double extends NativeType {
const Double();
}
class IntPtr extends NativeType {
const IntPtr();
}
class Pointer<T extends NativeType> extends NativeType {
external factory Pointer.fromAddress(int ptr);
Expand Down Expand Up @@ -789,6 +793,50 @@ class FfiNative<T> {
final bool isLeaf;
const FfiNative(this.nativeName, {this.isLeaf: false});
}
class Abi {
static const androidArm = _androidArm;
static const androidArm64 = _androidArm64;
static const androidIA32 = _androidIA32;
static const _androidArm = Abi._(_Architecture.arm, _OS.android);
static const _androidArm64 = Abi._(_Architecture.arm64, _OS.android);
static const _androidIA32 = Abi._(_Architecture.ia32, _OS.android);
final _OS _os;
final _Architecture _architecture;
const Abi._(this._architecture, this._os);
}
enum _Architecture {
arm,
arm64,
ia32,
x64,
}
enum _OS {
android,
fuchsia,
ios,
linux,
macos,
windows,
}
class AbiSpecificInteger extends NativeType {
const AbiSpecificInteger();
}
class AbiSpecificIntegerMapping {
final Map<Abi, NativeType> mapping;
const AbiSpecificIntegerMapping(this.mapping);
}
''',
)
]);
Expand Down
16 changes: 14 additions & 2 deletions pkg/analyzer/messages.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13775,6 +13775,18 @@ CompileTimeErrorCode:
}
```
FfiCode:
ABI_SPECIFIC_INTEGER_MAPPING_EXTRA:
problemMessage: "Classes extending 'AbiSpecificInteger' must have exactly one 'AbiSpecificIntegerMapping' annotation specifying the mapping from ABI to a 'NativeType' integer with a fixed size."
correctionMessage: Try removing the extra annotation.
comment: No parameters.
ABI_SPECIFIC_INTEGER_MAPPING_MISSING:
problemMessage: "Classes extending 'AbiSpecificInteger' must have exactly one 'AbiSpecificIntegerMapping' annotation specifying the mapping from ABI to a 'NativeType' integer with a fixed size."
correctionMessage: Try adding an annotation.
comment: No parameters.
ABI_SPECIFIC_INTEGER_MAPPING_UNSUPPORTED:
problemMessage: "Only mappings to 'Int8', 'Int16', 'Int32', 'Int64', 'Uint8', 'Uint16', 'UInt32', and 'Uint64' are supported."
correctionMessage: Try changing the value to 'Int8', 'Int16', 'Int32', 'Int64', 'Uint8', 'Uint16', 'UInt32', or 'Uint64'.
comment: No parameters.
ANNOTATION_ON_POINTER_FIELD:
problemMessage: "Fields in a struct class whose type is 'Pointer' should not have any annotations."
correctionMessage: Try removing the annotation.
Expand Down Expand Up @@ -13916,8 +13928,8 @@ FfiCode:
correctionMessage: Try changing the input to a positive number.
comment: No parameters.
NON_SIZED_TYPE_ARGUMENT:
problemMessage: "Type arguments to '{0}' can't have the type '{1}'. They can only be declared as native integer, 'Float', 'Double', 'Pointer', or subtype of 'Struct' or 'Union'."
correctionMessage: "Try using a native integer, 'Float', 'Double', 'Pointer', or subtype of 'Struct' or 'Union'."
problemMessage: "Type arguments to '{0}' can't have the type '{1}'. They can only be declared as native integer, 'Float', 'Double', 'Pointer', or subtype of 'Struct', 'Union', or 'AbiSpecificInteger'."
correctionMessage: "Try using a native integer, 'Float', 'Double', 'Pointer', or subtype of 'Struct', 'Union', or 'AbiSpecificInteger'."
comment: |-
Parameters:
0: the type of the field
Expand Down
Loading

0 comments on commit c3b5082

Please sign in to comment.