@@ -396,14 +396,21 @@ void returnConstantAnnoAttributes(byte[] attributes) {
396396 private int [] constantPoolOffsets ;
397397 private byte [] constantPoolAnnoAttrributes ;
398398
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+
399404 private ClassInfo currentClass ;
400405 private HashMap <DotName , List <AnnotationInstance >> classAnnotations ;
401406 private ArrayList <AnnotationInstance > elementAnnotations ;
402407 private IdentityHashMap <AnnotationTarget , Object > signaturePresent ;
403408 private List <Object > signatures ;
404409 private int classSignatureIndex = -1 ;
405410 private Map <DotName , InnerClassInfo > innerClasses ;
411+ // iteration: `typeAnnotationsKeys` is used for predictable iteration order, we never iterate on `typeAnnotations`
406412 private IdentityHashMap <AnnotationTarget , List <TypeAnnotationState >> typeAnnotations ;
413+ private List <AnnotationTarget > typeAnnotationsKeys ;
407414 private List <MethodInfo > methods ;
408415 private List <FieldInfo > fields ;
409416 private List <RecordComponentInfo > recordComponents ;
@@ -416,9 +423,12 @@ void returnConstantAnnoAttributes(byte[] attributes) {
416423 private Map <DotName , List <ClassInfo >> subclasses ;
417424 private Map <DotName , List <ClassInfo >> subinterfaces ;
418425 private Map <DotName , List <ClassInfo >> implementors ;
426+ // iteration: `DotName` has predictable `equals`/`hashCode`, which implies predictable iteration order
419427 private Map <DotName , ClassInfo > classes ;
420428 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 ;
422432 private NameTable names ;
423433 private GenericSignatureParser signatureParser ;
424434 private final TmpObjects tmpObjects = new TmpObjects ();
@@ -459,6 +469,7 @@ private void initClassFields() {
459469 signaturePresent = new IdentityHashMap <AnnotationTarget , Object >();
460470 signatures = new ArrayList <Object >();
461471 typeAnnotations = new IdentityHashMap <AnnotationTarget , List <TypeAnnotationState >>();
472+ typeAnnotationsKeys = new ArrayList <>();
462473
463474 // in bytecode, record components are stored as class attributes,
464475 // and if the attribute is missing, processRecordComponents isn't called at all
@@ -903,6 +914,7 @@ private void processTypeAnnotations(DataInputStream data, AnnotationTarget targe
903914 typeAnnotations .get (target ).addAll (annotations );
904915 } else {
905916 typeAnnotations .put (target , annotations );
917+ typeAnnotationsKeys .add (target );
906918 }
907919 }
908920
@@ -1104,9 +1116,8 @@ private static boolean isEnumConstructor(MethodInfo method) {
11041116 }
11051117
11061118 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 );
11101121
11111122 for (TypeAnnotationState annotation : annotations ) {
11121123 resolveTypeAnnotation (key , annotation );
@@ -1202,9 +1213,8 @@ private void recordUsedClass(DotName usedClass) {
12021213 }
12031214
12041215 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 );
12081218
12091219 for (TypeAnnotationState annotation : annotations ) {
12101220 updateTypeTarget (key , annotation );
@@ -2686,6 +2696,7 @@ public ClassSummary indexWithSummary(InputStream stream) throws IOException {
26862696 classSignatureIndex = -1 ;
26872697 innerClasses = null ;
26882698 typeAnnotations = null ;
2699+ typeAnnotationsKeys = null ;
26892700 methods = null ;
26902701 fields = null ;
26912702 recordComponents = null ;
0 commit comments