4646import java .io .StringWriter ;
4747import java .io .Writer ;
4848import java .lang .classfile .*;
49+ import java .lang .classfile .TypeAnnotation .TargetInfo ;
50+ import java .lang .classfile .TypeAnnotation .TypePathComponent ;
4951import java .lang .classfile .attribute .*;
5052import java .lang .classfile .constantpool .ClassEntry ;
5153import java .lang .classfile .constantpool .ConstantPoolBuilder ;
@@ -991,6 +993,12 @@ private void addGenericAttributes(FeatureDescription desc, Consumer<? super Attr
991993 if (desc .runtimeAnnotations != null && !desc .runtimeAnnotations .isEmpty ()) {
992994 builder .accept (RuntimeVisibleAnnotationsAttribute .of (createAnnotations (desc .runtimeAnnotations )));
993995 }
996+ if (desc .classTypeAnnotations != null && !desc .classTypeAnnotations .isEmpty ()) {
997+ builder .accept (RuntimeInvisibleTypeAnnotationsAttribute .of (createTypeAnnotations (desc .classTypeAnnotations )));
998+ }
999+ if (desc .runtimeTypeAnnotations != null && !desc .runtimeTypeAnnotations .isEmpty ()) {
1000+ builder .accept (RuntimeVisibleTypeAnnotationsAttribute .of (createTypeAnnotations (desc .runtimeTypeAnnotations )));
1001+ }
9941002 }
9951003
9961004 private List <Annotation > createAnnotations (List <AnnotationDescription > desc ) {
@@ -1066,6 +1074,44 @@ private AnnotationValue createAttributeValue(Object value) {
10661074 default -> throw new IllegalArgumentException (value .getClass ().getName ());
10671075 };
10681076 }
1077+
1078+ private List <TypeAnnotation > createTypeAnnotations (List <TypeAnnotationDescription > desc ) {
1079+ return desc .stream ().map (this ::createTypeAnnotation ).collect (Collectors .toList ());
1080+ }
1081+
1082+ private TypeAnnotation createTypeAnnotation (TypeAnnotationDescription desc ) {
1083+ Annotation baseAnn = createAnnotation (desc .annotation );
1084+ TargetInfo targetInfo = switch ((String ) desc .targetInfo .get ("targetType" )) {
1085+ case "CLASS_TYPE_PARAMETER" -> //TODO: test!
1086+ TargetInfo .ofClassTypeParameter ((int ) desc .targetInfo .get ("typeParameterIndex" ));
1087+ case "METHOD_TYPE_PARAMETER" ->
1088+ TargetInfo .ofMethodTypeParameter ((int ) desc .targetInfo .get ("typeParameterIndex" ));
1089+ case "CLASS_EXTENDS" ->
1090+ TargetInfo .ofClassExtends ((int ) desc .targetInfo .get ("supertypeIndex" ));
1091+ case "CLASS_TYPE_PARAMETER_BOUND" ->
1092+ TargetInfo .ofClassTypeParameterBound ((int ) desc .targetInfo .get ("typeParameterIndex" ),
1093+ (int ) desc .targetInfo .get ("boundIndex" ));
1094+ case "METHOD_TYPE_PARAMETER_BOUND" ->
1095+ TargetInfo .ofMethodTypeParameterBound ((int ) desc .targetInfo .get ("typeParameterIndex" ),
1096+ (int ) desc .targetInfo .get ("boundIndex" ));
1097+ case "METHOD_RETURN" ->
1098+ TargetInfo .ofMethodReturn ();
1099+ case "METHOD_RECEIVER" ->
1100+ TargetInfo .ofMethodReceiver ();
1101+ case "METHOD_FORMAL_PARAMETER" ->
1102+ TargetInfo .ofMethodFormalParameter ((int ) desc .targetInfo .get ("formalParameterIndex" ));
1103+ case "THROWS" ->
1104+ TargetInfo .ofThrows ((int ) desc .targetInfo .get ("throwsTargetIndex" ));
1105+ case "FIELD" ->
1106+ TargetInfo .ofField ();
1107+ case String targetType ->
1108+ throw new IllegalStateException ("Unsupported targetType: " + targetType );
1109+ };
1110+
1111+ List <TypePathComponent > typePath = desc .typePath .stream ().map (d -> TypePathComponent .of (TypePathComponent .Kind .valueOf (d .tag ()), d .index ())).toList ();
1112+
1113+ return TypeAnnotation .of (targetInfo , typePath , baseAnn );
1114+ }
10691115 //</editor-fold>
10701116 //</editor-fold>
10711117
@@ -2213,7 +2259,10 @@ private boolean readAttribute(FeatureDescription feature, Attribute<?> attr) {
22132259 chd .permittedSubclasses = a .permittedSubclasses ().stream ().map (ClassEntry ::asInternalName ).collect (Collectors .toList ());
22142260 }
22152261 case ModuleMainClassAttribute a -> ((ModuleHeaderDescription ) feature ).moduleMainClass = a .mainClass ().asInternalName ();
2216- case RuntimeVisibleTypeAnnotationsAttribute a -> {/* do nothing for now */ }
2262+ case RuntimeInvisibleTypeAnnotationsAttribute a ->
2263+ feature .classTypeAnnotations = typeAnnotations2Descriptions (a .annotations ());
2264+ case RuntimeVisibleTypeAnnotationsAttribute a ->
2265+ feature .runtimeTypeAnnotations = typeAnnotations2Descriptions (a .annotations ());
22172266 default -> throw new IllegalArgumentException ("Unhandled attribute: " + attr .attributeName ()); // Do nothing
22182267 }
22192268
@@ -2270,6 +2319,31 @@ private AnnotationDescription annotation2Description(java.lang.classfile.Annotat
22702319
22712320 return new AnnotationDescription (annotationType , values );
22722321 }
2322+
2323+ private List <TypeAnnotationDescription > typeAnnotations2Descriptions (List <TypeAnnotation > annos ) {
2324+ return annos .stream ().map (ta -> {
2325+ TypeAnnotationDescription desc = new TypeAnnotationDescription ();
2326+ desc .annotation = annotation2Description (ta .annotation ());
2327+ desc .targetInfo = new HashMap <>();
2328+ desc .targetInfo .put ("targetType" , ta .targetInfo ().targetType ().name ());
2329+ switch (ta .targetInfo ()) {
2330+ case TypeAnnotation .TypeParameterTarget tpt -> desc .targetInfo .put ("typeParameterIndex" , tpt .typeParameterIndex ());
2331+ case TypeAnnotation .SupertypeTarget st -> desc .targetInfo .put ("supertypeIndex" , st .supertypeIndex ());
2332+ case TypeAnnotation .TypeParameterBoundTarget tpbt -> {
2333+ desc .targetInfo .put ("typeParameterIndex" , tpbt .typeParameterIndex ());
2334+ desc .targetInfo .put ("boundIndex" , tpbt .boundIndex ());
2335+ }
2336+ case TypeAnnotation .EmptyTarget _ -> {
2337+ // nothing to write
2338+ }
2339+ case TypeAnnotation .FormalParameterTarget fpt -> desc .targetInfo .put ("formalParameterIndex" , fpt .formalParameterIndex ());
2340+ case TypeAnnotation .ThrowsTarget tt -> desc .targetInfo .put ("throwsTargetIndex" , tt .throwsTargetIndex ());
2341+ default -> throw new IllegalStateException (ta .targetInfo ().targetType ().name ());
2342+ }
2343+ desc .typePath = ta .targetPath ().stream ().map (tpc -> new TypeAnnotationDescription .TypePathComponentDesc (tpc .typePathKind ().name (), tpc .typeArgumentIndex ())).toList ();
2344+ return desc ;
2345+ }).toList ();
2346+ }
22732347 //</editor-fold>
22742348
22752349 protected boolean includeEffectiveAccess (ClassList classes , ClassDescription clazz ) {
@@ -2391,6 +2465,8 @@ static abstract class FeatureDescription {
23912465 String versions = "" ;
23922466 List <AnnotationDescription > classAnnotations ;
23932467 List <AnnotationDescription > runtimeAnnotations ;
2468+ List <TypeAnnotationDescription > classTypeAnnotations ;
2469+ List <TypeAnnotationDescription > runtimeTypeAnnotations ;
23942470
23952471 protected void writeAttributes (Appendable output ) throws IOException {
23962472 if (flags != 0 )
@@ -2413,6 +2489,18 @@ protected void writeAttributes(Appendable output) throws IOException {
24132489 output .append (quote (a .toString (), false ));
24142490 }
24152491 }
2492+ if (classTypeAnnotations != null && !classTypeAnnotations .isEmpty ()) {
2493+ output .append (" classTypeAnnotations " );
2494+ for (TypeAnnotationDescription a : classTypeAnnotations ) {
2495+ output .append (quote (a .toString (), false ));
2496+ }
2497+ }
2498+ if (runtimeTypeAnnotations != null && !runtimeTypeAnnotations .isEmpty ()) {
2499+ output .append (" runtimeTypeAnnotations " );
2500+ for (TypeAnnotationDescription a : runtimeTypeAnnotations ) {
2501+ output .append (quote (a .toString (), false ));
2502+ }
2503+ }
24162504 }
24172505
24182506 protected boolean shouldIgnore (String baselineVersion , String version ) {
@@ -2442,6 +2530,14 @@ protected void readAttributes(LineBasedReader reader) {
24422530 if (inRuntimeAnnotations != null ) {
24432531 runtimeAnnotations = parseAnnotations (inRuntimeAnnotations , new int [1 ]);
24442532 }
2533+ String inClassTypeAnnotations = reader .attributes .get ("classTypeAnnotations" );
2534+ if (inClassTypeAnnotations != null ) {
2535+ classTypeAnnotations = parseTypeAnnotations (inClassTypeAnnotations , new int [1 ]);
2536+ }
2537+ String inRuntimeTypeAnnotations = reader .attributes .get ("runtimeTypeAnnotations" );
2538+ if (inRuntimeTypeAnnotations != null ) {
2539+ runtimeTypeAnnotations = parseTypeAnnotations (inRuntimeTypeAnnotations , new int [1 ]);
2540+ }
24452541 }
24462542
24472543 public abstract boolean read (LineBasedReader reader ) throws IOException ;
@@ -2454,6 +2550,8 @@ public int hashCode() {
24542550 hash = 89 * hash + Objects .hashCode (this .signature );
24552551 hash = 89 * hash + listHashCode (this .classAnnotations );
24562552 hash = 89 * hash + listHashCode (this .runtimeAnnotations );
2553+ hash = 89 * hash + listHashCode (this .classTypeAnnotations );
2554+ hash = 89 * hash + listHashCode (this .runtimeTypeAnnotations );
24572555 return hash ;
24582556 }
24592557
@@ -2481,6 +2579,12 @@ public boolean equals(Object obj) {
24812579 if (!listEquals (this .runtimeAnnotations , other .runtimeAnnotations )) {
24822580 return false ;
24832581 }
2582+ if (!listEquals (this .classTypeAnnotations , other .classTypeAnnotations )) {
2583+ return false ;
2584+ }
2585+ if (!listEquals (this .runtimeTypeAnnotations , other .runtimeTypeAnnotations )) {
2586+ return false ;
2587+ }
24842588 return true ;
24852589 }
24862590
@@ -3285,6 +3389,8 @@ public int hashCode() {
32853389 hash = 59 * hash + Objects .hashCode (this .descriptor );
32863390 hash = 59 * hash + Objects .hashCode (this .thrownTypes );
32873391 hash = 59 * hash + Objects .hashCode (this .annotationDefaultValue );
3392+ hash = 59 * hash + Objects .hashCode (this .classParameterAnnotations );
3393+ hash = 59 * hash + Objects .hashCode (this .runtimeParameterAnnotations );
32883394 return hash ;
32893395 }
32903396
@@ -3309,6 +3415,12 @@ public boolean equals(Object obj) {
33093415 if (!Objects .equals (this .annotationDefaultValue , other .annotationDefaultValue )) {
33103416 return false ;
33113417 }
3418+ if (!Objects .equals (this .classParameterAnnotations , other .classParameterAnnotations )) {
3419+ return false ;
3420+ }
3421+ if (!Objects .equals (this .runtimeParameterAnnotations , other .runtimeParameterAnnotations )) {
3422+ return false ;
3423+ }
33123424 return true ;
33133425 }
33143426
@@ -3636,6 +3748,40 @@ private static String dumpAnnotationValue(Object value) {
36363748 }
36373749 }
36383750
3751+ static final class TypeAnnotationDescription {
3752+ AnnotationDescription annotation ;
3753+ Map <String , Object > targetInfo ;
3754+ List <TypePathComponentDesc > typePath ;
3755+
3756+ public TypeAnnotationDescription () {
3757+ }
3758+
3759+ public TypeAnnotationDescription (AnnotationDescription annotation , Map <String , Object > targetInfo , List <TypePathComponentDesc > typePath ) {
3760+ this .annotation = annotation ;
3761+ this .targetInfo = targetInfo ;
3762+ this .typePath = typePath ;
3763+ }
3764+
3765+ @ Override
3766+ public String toString () {
3767+ return annotation .toString () + "{" + targetInfo .entrySet ().stream ().map (e -> e .getKey () + "=" + quote (printValue (e .getValue ()), false )).collect (Collectors .joining ("," )) + "}" +
3768+ (!typePath .isEmpty () ? "[" + typePath .stream ().map (desc -> desc .tag + ":" + desc .index ).collect (Collectors .joining ("," )) + "]" : "" );
3769+ }
3770+
3771+ private String printValue (Object obj ) {
3772+ if (obj instanceof String s ) {
3773+ return "\" " + s + "\" " ;
3774+ } else if (obj instanceof Integer i ) {
3775+ return "I" + String .valueOf (i );
3776+ } else {
3777+ throw new IllegalStateException ("Unsupported value: " + obj .getClass ());
3778+ }
3779+ }
3780+
3781+ //TODO: path
3782+ record TypePathComponentDesc (String tag , int index ) {}
3783+ }
3784+
36393785 static final class EnumConstant {
36403786 String type ;
36413787 String constant ;
@@ -3975,23 +4121,69 @@ public static List<AnnotationDescription> parseAnnotations(String encoded, int[]
39754121
39764122 private static AnnotationDescription parseAnnotation (String value , int [] valuePointer ) {
39774123 String className = className (value , valuePointer );
3978- Map <String , Object > attribute2Value = new HashMap <> ();
4124+ Map <String , Object > attribute2Value = Map . of ();
39794125
39804126 if (valuePointer [0 ] < value .length () && value .charAt (valuePointer [0 ]) == '(' ) {
3981- while (value .charAt (valuePointer [0 ]) != ')' ) {
4127+ attribute2Value = parseMap (value , valuePointer , ')' );
4128+ }
4129+
4130+ return new AnnotationDescription (className , attribute2Value );
4131+ }
4132+
4133+ private static Map <String , Object > parseMap (String value , int [] valuePointer , char endBracket ) {
4134+ Map <String , Object > attribute2Value = new HashMap <>();
4135+
4136+ while (value .charAt (valuePointer [0 ]) != endBracket ) {
4137+ int nameStart = ++valuePointer [0 ];
4138+
4139+ while (value .charAt (valuePointer [0 ]++) != '=' );
4140+
4141+ String name = value .substring (nameStart , valuePointer [0 ] - 1 );
4142+
4143+ attribute2Value .put (name , parseAnnotationValue (value , valuePointer ));
4144+ }
4145+
4146+ valuePointer [0 ]++;
4147+
4148+ return attribute2Value ;
4149+ }
4150+
4151+ public static List <TypeAnnotationDescription > parseTypeAnnotations (String encoded , int [] pointer ) {
4152+ List <TypeAnnotationDescription > result = new ArrayList <>();
4153+
4154+ while (pointer [0 ] < encoded .length () && encoded .charAt (pointer [0 ]) == '@' ) {
4155+ pointer [0 ]++;
4156+ result .add (parseTypeAnnotation (encoded , pointer ));
4157+ }
4158+
4159+ return result ;
4160+ }
4161+
4162+ private static TypeAnnotationDescription parseTypeAnnotation (String value , int [] valuePointer ) {
4163+ AnnotationDescription ann = parseAnnotation (value , valuePointer );
4164+ Map <String , Object > targetInfo = Map .of ();
4165+
4166+ if (valuePointer [0 ] < value .length () && value .charAt (valuePointer [0 ]) == '{' ) {
4167+ targetInfo = parseMap (value , valuePointer , '}' );
4168+ }
4169+
4170+ List <TypeAnnotationDescription .TypePathComponentDesc > typePath = new ArrayList <>();
4171+
4172+ if (valuePointer [0 ] < value .length () && value .charAt (valuePointer [0 ]) == '[' ) {
4173+ while (value .charAt (valuePointer [0 ]) != ']' ) {
39824174 int nameStart = ++valuePointer [0 ];
39834175
3984- while (value .charAt (valuePointer [0 ]++) != '= ' );
4176+ while (value .charAt (valuePointer [0 ]++) != ': ' );
39854177
39864178 String name = value .substring (nameStart , valuePointer [0 ] - 1 );
39874179
3988- attribute2Value . put ( name , parseAnnotationValue ( value , valuePointer ));
4180+ typePath . add ( new TypeAnnotationDescription . TypePathComponentDesc ( name , Integer . parseInt ( readDigits ( value , valuePointer )) ));
39894181 }
39904182
39914183 valuePointer [0 ]++;
39924184 }
39934185
3994- return new AnnotationDescription ( className , attribute2Value );
4186+ return new TypeAnnotationDescription ( ann , targetInfo , typePath );
39954187 }
39964188 //</editor-fold>
39974189
0 commit comments