@@ -396,14 +396,21 @@ void returnConstantAnnoAttributes(byte[] attributes) {
396
396
private int [] constantPoolOffsets ;
397
397
private byte [] constantPoolAnnoAttrributes ;
398
398
399
+ // note that for reproducibility, we have to establish a predictable iteration order for all `Map`s
400
+ // below that we iterate upon (fortunately, that's not too many)
401
+ // this is either by using keys with predictable `equals`/`hashCode` (such as `DotName`),
402
+ // or by storing the keys on the side in a list and iterate on that (needed for `IdentityHashMap`s)
403
+
399
404
private ClassInfo currentClass ;
400
405
private HashMap <DotName , List <AnnotationInstance >> classAnnotations ;
401
406
private ArrayList <AnnotationInstance > elementAnnotations ;
402
407
private IdentityHashMap <AnnotationTarget , Object > signaturePresent ;
403
408
private List <Object > signatures ;
404
409
private int classSignatureIndex = -1 ;
405
410
private Map <DotName , InnerClassInfo > innerClasses ;
411
+ // iteration: `typeAnnotationsKeys` is used for predictable iteration order, we never iterate on `typeAnnotations`
406
412
private IdentityHashMap <AnnotationTarget , List <TypeAnnotationState >> typeAnnotations ;
413
+ private List <AnnotationTarget > typeAnnotationsKeys ;
407
414
private List <MethodInfo > methods ;
408
415
private List <FieldInfo > fields ;
409
416
private List <RecordComponentInfo > recordComponents ;
@@ -416,9 +423,12 @@ void returnConstantAnnoAttributes(byte[] attributes) {
416
423
private Map <DotName , List <ClassInfo >> subclasses ;
417
424
private Map <DotName , List <ClassInfo >> subinterfaces ;
418
425
private Map <DotName , List <ClassInfo >> implementors ;
426
+ // iteration: `DotName` has predictable `equals`/`hashCode`, which implies predictable iteration order
419
427
private Map <DotName , ClassInfo > classes ;
420
428
private Map <DotName , ModuleInfo > modules ;
421
- private Map <DotName , Set <ClassInfo >> users ; // must be a linked set for reproducibility
429
+ // iteration: `DotName` has predictable `equals`/`hashCode`, which implies predictable iteration order
430
+ // iteration: the `Set`s in map values must be linked sets for predictable iteration order
431
+ private Map <DotName , Set <ClassInfo >> users ;
422
432
private NameTable names ;
423
433
private GenericSignatureParser signatureParser ;
424
434
private final TmpObjects tmpObjects = new TmpObjects ();
@@ -459,6 +469,7 @@ private void initClassFields() {
459
469
signaturePresent = new IdentityHashMap <AnnotationTarget , Object >();
460
470
signatures = new ArrayList <Object >();
461
471
typeAnnotations = new IdentityHashMap <AnnotationTarget , List <TypeAnnotationState >>();
472
+ typeAnnotationsKeys = new ArrayList <>();
462
473
463
474
// in bytecode, record components are stored as class attributes,
464
475
// and if the attribute is missing, processRecordComponents isn't called at all
@@ -903,6 +914,7 @@ private void processTypeAnnotations(DataInputStream data, AnnotationTarget targe
903
914
typeAnnotations .get (target ).addAll (annotations );
904
915
} else {
905
916
typeAnnotations .put (target , annotations );
917
+ typeAnnotationsKeys .add (target );
906
918
}
907
919
}
908
920
@@ -1104,9 +1116,8 @@ private static boolean isEnumConstructor(MethodInfo method) {
1104
1116
}
1105
1117
1106
1118
private void resolveTypeAnnotations () {
1107
- for (Map .Entry <AnnotationTarget , List <TypeAnnotationState >> entry : typeAnnotations .entrySet ()) {
1108
- AnnotationTarget key = entry .getKey ();
1109
- List <TypeAnnotationState > annotations = entry .getValue ();
1119
+ for (AnnotationTarget key : typeAnnotationsKeys ) {
1120
+ List <TypeAnnotationState > annotations = typeAnnotations .get (key );
1110
1121
1111
1122
for (TypeAnnotationState annotation : annotations ) {
1112
1123
resolveTypeAnnotation (key , annotation );
@@ -1202,9 +1213,8 @@ private void recordUsedClass(DotName usedClass) {
1202
1213
}
1203
1214
1204
1215
private void updateTypeTargets () {
1205
- for (Map .Entry <AnnotationTarget , List <TypeAnnotationState >> entry : typeAnnotations .entrySet ()) {
1206
- AnnotationTarget key = entry .getKey ();
1207
- List <TypeAnnotationState > annotations = entry .getValue ();
1216
+ for (AnnotationTarget key : typeAnnotationsKeys ) {
1217
+ List <TypeAnnotationState > annotations = typeAnnotations .get (key );
1208
1218
1209
1219
for (TypeAnnotationState annotation : annotations ) {
1210
1220
updateTypeTarget (key , annotation );
@@ -2686,6 +2696,7 @@ public ClassSummary indexWithSummary(InputStream stream) throws IOException {
2686
2696
classSignatureIndex = -1 ;
2687
2697
innerClasses = null ;
2688
2698
typeAnnotations = null ;
2699
+ typeAnnotationsKeys = null ;
2689
2700
methods = null ;
2690
2701
fields = null ;
2691
2702
recordComponents = null ;
0 commit comments