@@ -51,12 +51,16 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
51
51
/// `Struct` .
52
52
bool inStruct = false ;
53
53
54
+ /// Subclass of `Struct` we are currently visiting, or `null` .
55
+ ClassDeclaration ? struct;
56
+
54
57
/// Initialize a newly created verifier.
55
58
FfiVerifier (this .typeSystem, this ._errorReporter);
56
59
57
60
@override
58
61
void visitClassDeclaration (ClassDeclaration node) {
59
62
inStruct = false ;
63
+ struct = null ;
60
64
// Only the Allocator, Opaque and Struct class may be extended.
61
65
var extendsClause = node.extendsClause;
62
66
if (extendsClause != null ) {
@@ -66,10 +70,12 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
66
70
final className = ffiClass.name;
67
71
if (className == _structClassName) {
68
72
inStruct = true ;
73
+ struct = node;
69
74
if (node.declaredElement! .isEmptyStruct) {
70
75
_errorReporter
71
76
.reportErrorForNode (FfiCode .EMPTY_STRUCT , node, [node.name]);
72
77
}
78
+ _validatePackedAnnotation (node.metadata);
73
79
} else if (className != _allocatorClassName &&
74
80
className != _opaqueClassName) {
75
81
_errorReporter.reportErrorForNode (
@@ -561,12 +567,20 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
561
567
}
562
568
final arrayDimensions = declaredType.arrayDimensions;
563
569
_validateSizeOfAnnotation (fieldType, annotations, arrayDimensions);
570
+ final arrayElement = declaredType.arrayElementType;
571
+ if (arrayElement.isStructSubtype) {
572
+ final elementClass = (arrayElement as InterfaceType ).element;
573
+ _validatePackingNesting (struct! .declaredElement! , elementClass,
574
+ errorNode: fieldType);
575
+ }
564
576
} else if (declaredType.isStructSubtype) {
565
577
final clazz = (declaredType as InterfaceType ).element;
566
578
if (clazz.isEmptyStruct) {
567
579
_errorReporter
568
580
.reportErrorForNode (FfiCode .EMPTY_STRUCT , node, [clazz.name]);
569
581
}
582
+ _validatePackingNesting (struct! .declaredElement! , clazz,
583
+ errorNode: fieldType);
570
584
} else {
571
585
_errorReporter.reportErrorForNode (FfiCode .INVALID_FIELD_TYPE_IN_STRUCT ,
572
586
fieldType, [fieldType.toSource ()]);
@@ -664,6 +678,54 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
664
678
}
665
679
}
666
680
681
+ /// Validate that the [annotations] include at most one packed annotation.
682
+ void _validatePackedAnnotation (NodeList <Annotation > annotations) {
683
+ final ffiPackedAnnotations =
684
+ annotations.where ((annotation) => annotation.isPacked).toList ();
685
+
686
+ if (ffiPackedAnnotations.isEmpty) {
687
+ return ;
688
+ }
689
+
690
+ if (ffiPackedAnnotations.length > 1 ) {
691
+ final extraAnnotations = ffiPackedAnnotations.skip (1 );
692
+ for (final annotation in extraAnnotations) {
693
+ _errorReporter.reportErrorForNode (
694
+ FfiCode .PACKED_ANNOTATION , annotation);
695
+ }
696
+ }
697
+
698
+ // Check number of dimensions.
699
+ final annotation = ffiPackedAnnotations.first;
700
+ final value = annotation.elementAnnotation? .packedMemberAlignment;
701
+ if (! [1 , 2 , 4 , 8 , 16 ].contains (value)) {
702
+ _errorReporter.reportErrorForNode (
703
+ FfiCode .PACKED_ANNOTATION_ALIGNMENT , annotation);
704
+ }
705
+ }
706
+
707
+ void _validatePackingNesting (ClassElement outer, ClassElement nested,
708
+ {required TypeAnnotation errorNode}) {
709
+ final outerPacking = outer.structPacking;
710
+ if (outerPacking == null ) {
711
+ // No packing for outer class, so we're done.
712
+ return ;
713
+ }
714
+ bool error = false ;
715
+ final nestedPacking = nested.structPacking;
716
+ if (nestedPacking == null ) {
717
+ // The outer struct packs, but the nested struct does not.
718
+ error = true ;
719
+ } else if (outerPacking < nestedPacking) {
720
+ // The outer struct packs tighter than the nested struct.
721
+ error = true ;
722
+ }
723
+ if (error) {
724
+ _errorReporter.reportErrorForNode (FfiCode .PACKED_NESTING_NON_PACKED ,
725
+ errorNode, [nested.name, outer.name]);
726
+ }
727
+ }
728
+
667
729
void _validateRefIndexed (IndexExpression node) {
668
730
var targetType = node.realTarget.staticType;
669
731
if (! _isValidFfiNativeType (targetType,
@@ -774,6 +836,30 @@ enum _PrimitiveDartType {
774
836
none,
775
837
}
776
838
839
+ extension on Annotation {
840
+ bool get isPacked {
841
+ final element = this .element;
842
+ return element is ConstructorElement &&
843
+ element.ffiClass != null &&
844
+ element.enclosingElement.name == 'Packed' ;
845
+ }
846
+ }
847
+
848
+ extension on ElementAnnotation {
849
+ bool get isPacked {
850
+ final element = this .element;
851
+ return element is ConstructorElement &&
852
+ element.ffiClass != null &&
853
+ element.enclosingElement.name == 'Packed' ;
854
+ }
855
+
856
+ int get packedMemberAlignment {
857
+ assert (isPacked);
858
+ final value = computeConstantValue ();
859
+ return value! .getField ('memberAlignment' )! .toIntValue ()! ;
860
+ }
861
+ }
862
+
777
863
extension on Element ? {
778
864
/// Return `true` if this represents the extension `AllocatorAlloc` .
779
865
bool get isAllocatorExtension {
@@ -855,6 +941,17 @@ extension on ClassElement {
855
941
bool get isFfiClass {
856
942
return library.name == FfiVerifier ._dartFfiLibraryName;
857
943
}
944
+
945
+ int ? get structPacking {
946
+ final packedAnnotations =
947
+ metadata.where ((annotation) => annotation.isPacked);
948
+
949
+ if (packedAnnotations.isEmpty) {
950
+ return null ;
951
+ }
952
+
953
+ return packedAnnotations.first.packedMemberAlignment;
954
+ }
858
955
}
859
956
860
957
extension on ExtensionElement {
@@ -886,6 +983,16 @@ extension on DartType {
886
983
return dimensions;
887
984
}
888
985
986
+ DartType get arrayElementType {
987
+ DartType iterator = this ;
988
+ while (iterator is InterfaceType &&
989
+ iterator.element.name == FfiVerifier ._arrayClassName &&
990
+ iterator.element.isFfiClass) {
991
+ iterator = iterator.typeArguments.single;
992
+ }
993
+ return iterator;
994
+ }
995
+
889
996
bool get isPointer {
890
997
final self = this ;
891
998
return self is InterfaceType && self.element.isPointer;
0 commit comments