From f12bcd2911900cf37c0962ac84841a9dab2747da Mon Sep 17 00:00:00 2001 From: ktkk Date: Tue, 7 Jul 2026 13:38:46 +0000 Subject: [PATCH] Format annotation attributes --- src/Class/AttributeInfo.zig | 255 +++++++++++++++++- testsuite/classes/java/CustomAnnotation.java | 14 + .../java/CustomAnnotationAnnotation.java | 9 + .../classes/java/CustomAnnotationType.java | 5 + ...te.java => CustomParameterAnnotation.java} | 5 +- testsuite/classes/java/Main.java | 3 +- 6 files changed, 275 insertions(+), 16 deletions(-) create mode 100644 testsuite/classes/java/CustomAnnotation.java create mode 100644 testsuite/classes/java/CustomAnnotationAnnotation.java create mode 100644 testsuite/classes/java/CustomAnnotationType.java rename testsuite/classes/java/{CustomAttribute.java => CustomParameterAnnotation.java} (71%) diff --git a/src/Class/AttributeInfo.zig b/src/Class/AttributeInfo.zig index 8747ed2..adf6b8e 100644 --- a/src/Class/AttributeInfo.zig +++ b/src/Class/AttributeInfo.zig @@ -447,10 +447,58 @@ pub const AttributeInfo = union(enum) { } }, .deprecated => {}, - //.runtime_visible_annotations, - //.runtime_invisible_annotations, - //.runtime_visible_parameter_annotations, - //.runtime_invisible_parameter_annotations, + .runtime_visible_annotations => |attr| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("annotations:\n"); + var annotation_indent = indent; + for (attr.annotations, 0..) |annotation, i| { + annotation_indent += 1; + defer annotation_indent -= 1; + + try w.splatByteAll(' ', depth * annotation_indent); + try w.print("{d}:\n", .{ i }); + try annotation.indentedFormat(w, depth, annotation_indent + 1, constant_pool); + } + }, + .runtime_invisible_annotations => |attr| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("annotations:\n"); + var annotation_indent = indent; + for (attr.annotations, 0..) |annotation, i| { + annotation_indent += 1; + defer annotation_indent -= 1; + + try w.splatByteAll(' ', depth * annotation_indent); + try w.print("{d}:\n", .{ i }); + try annotation.indentedFormat(w, depth, annotation_indent + 1, constant_pool); + } + }, + .runtime_visible_parameter_annotations => |attr| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("parameter_annotations:\n"); + var parameter_annotation_indent = indent; + for (attr.parameter_annotations, 0..) |parameter_annotation, i| { + parameter_annotation_indent += 1; + defer parameter_annotation_indent -= 1; + + try w.splatByteAll(' ', depth * parameter_annotation_indent); + try w.print("{d}:\n", .{ i }); + try parameter_annotation.indentedFormat(w, depth, parameter_annotation_indent + 1, constant_pool); + } + }, + .runtime_invisible_parameter_annotations => |attr| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("parameter_annotations:\n"); + var parameter_annotation_indent = indent; + for (attr.parameter_annotations, 0..) |parameter_annotation, i| { + parameter_annotation_indent += 1; + defer parameter_annotation_indent -= 1; + + try w.splatByteAll(' ', depth * parameter_annotation_indent); + try w.print("{d}:\n", .{ i }); + try parameter_annotation.indentedFormat(w, depth, parameter_annotation_indent + 1, constant_pool); + } + }, //.runtime_visible_type_annotations, //.runtime_invisible_type_annotations, //.annotation_default, @@ -1381,14 +1429,11 @@ pub const Deprecated = struct { }; pub const ElementValue = union(enum) { // B, C, D, F, I, J, S, Z, s - const_value_index: u16, + const_value: ConstValue, // e - enum_const_value: struct { - type_name_index: u16, - const_name_index: u16, - }, + enum_const_value: EnumConstValue, // c - class_info_index: u16, + class_const_value: ClassConstValue, // @ annotation_value: Annotation, // [ @@ -1396,11 +1441,78 @@ pub const ElementValue = union(enum) { values: []ElementValue, }, + pub const ConstValue = struct { + @"type": Type, + index: u16, + + pub const Type = enum(u8) { + byte = 'B', + char = 'C', + double = 'D', + float = 'F', + int = 'I', + long = 'J', + short = 'S', + boolean = 'Z', + string = 's', + }; + + pub fn value(self: *const ConstValue, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + const value_ = try constant_pool.get(self.index); + return switch (self.@"type") { + .byte, .char, .int, .short, .boolean => blk: { + if (!value_.is(.integer)) break :blk ResolveError.InvalidConstantType; + break :blk value_; + }, + .double => blk: { + if (!value_.is(.double)) break :blk ResolveError.InvalidConstantType; + break :blk value_; + }, + .float => blk: { + if (!value_.is(.float)) break :blk ResolveError.InvalidConstantType; + break :blk value_; + }, + .long => blk: { + if (!value_.is(.long)) break :blk ResolveError.InvalidConstantType; + break :blk value_; + }, + .string => blk: { + if (!value_.is(.utf8)) break :blk ResolveError.InvalidConstantType; + break :blk value_; + }, + }; + } + }; + + pub const EnumConstValue = struct { + type_name_index: u16, + const_name_index: u16, + + pub fn descriptor(self: *const EnumConstValue, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.type_name_index, .utf8); + } + + pub fn name(self: *const EnumConstValue, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.const_name_index, .utf8); + } + }; + + pub const ClassConstValue = struct { + index: u16, + + pub fn descriptor(self: *const ClassConstValue, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.index, .utf8); + } + }; + pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!ElementValue { const tag = try input.takeByte(); return switch (tag) { 'B', 'C', 'D', 'F', 'I', 'J', 'S', 'Z', 's' => .{ - .const_value_index = try input.takeInt(u16, .big), + .const_value = .{ + .@"type" = @enumFromInt(tag), + .index = try input.takeInt(u16, .big), + }, }, 'e' => .{ .enum_const_value = .{ @@ -1409,7 +1521,9 @@ pub const ElementValue = union(enum) { }, }, 'c' => .{ - .class_info_index = try input.takeInt(u16, .big), + .class_const_value = .{ + .index = try input.takeInt(u16, .big), + }, }, '@' => .{ .annotation_value = try .parse(input, allocator), @@ -1444,6 +1558,61 @@ pub const ElementValue = union(enum) { else => {}, } } + + pub fn indentedFormat( + self: *const ElementValue, + w: *std.Io.Writer, + depth: usize, + indent: u8, + constant_pool: ConstantPool, + ) std.Io.Writer.Error!void { + try w.splatByteAll(' ', depth * indent); + try w.print("type: {s}\n", .{ @tagName(self.*) }); + + switch (self.*) { + .const_value => |const_value| { + const value = const_value.value(constant_pool) catch return error.WriteFailed; + try w.splatByteAll(' ', depth * indent); + _ = try w.write("value:\n"); + try value.indentedFormat(w, depth, indent + 1, constant_pool); + }, + .enum_const_value => |enum_const_value| { + const descriptor = enum_const_value.descriptor(constant_pool) catch return error.WriteFailed; + try w.splatByteAll(' ', depth * indent); + _ = try w.write("descriptor:\n"); + try descriptor.indentedFormat(w, depth, indent + 1, constant_pool); + + const name = enum_const_value.name(constant_pool) catch return error.WriteFailed; + try w.splatByteAll(' ', depth * indent); + _ = try w.write("name:\n"); + try name.indentedFormat(w, depth, indent + 1, constant_pool); + }, + .class_const_value => |class_const_value| { + const descriptor = class_const_value.descriptor(constant_pool) catch return error.WriteFailed; + try w.splatByteAll(' ', depth * indent); + _ = try w.write("descriptor:\n"); + try descriptor.indentedFormat(w, depth, indent + 1, constant_pool); + }, + .annotation_value => |annotation_value| { + _ = try w.write("annotation:\n"); + try annotation_value.indentedFormat(w, depth, indent + 1, constant_pool); + }, + .array_value => |array_value| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("values:\n"); + var value_indent = indent; + for (array_value.values, 0..) |*value, i| { + value_indent += 1; + defer value_indent -= 1; + + try w.splatByteAll(' ', depth * value_indent); + try w.print("{d}:\n", .{ i }); + + try value.indentedFormat(w, depth, value_indent + 1, constant_pool); + } + }, + } + } }; pub const ElementValuePair = struct { @@ -1452,6 +1621,23 @@ pub const ElementValuePair = struct { const Self = @This(); + pub fn indentedFormat( + self: *const ElementValuePair, + w: *std.Io.Writer, + depth: usize, + indent: u8, + constant_pool: ConstantPool, + ) std.Io.Writer.Error!void { + const element_name = self.elementName(constant_pool) catch return error.WriteFailed; + try w.splatByteAll(' ', depth * indent); + _ = try w.write("name:\n"); + try element_name.indentedFormat(w, depth, indent + 1, constant_pool); + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("value:\n"); + try self.value.indentedFormat(w, depth, indent + 1, constant_pool); + } + pub fn elementName(self: *const Self, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { return constant_pool.getTag(self.element_name_index, .utf8); } @@ -1489,6 +1675,31 @@ pub const Annotation = struct { allocator.free(self.element_value_pairs); } + pub fn indentedFormat( + self: *const Annotation, + w: *std.Io.Writer, + depth: usize, + indent: u8, + constant_pool: ConstantPool, + ) std.Io.Writer.Error!void { + const type_ = self.@"type"(constant_pool) catch return error.WriteFailed; + try w.splatByteAll(' ', depth * indent); + _ = try w.write("type:\n"); + try type_.indentedFormat(w, depth, indent + 1, constant_pool); + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("element_value_pairs:\n"); + var element_value_pair_indent = indent; + for (self.element_value_pairs, 0..) |element_value_pair, i| { + element_value_pair_indent += 1; + defer element_value_pair_indent -= 1; + + try w.splatByteAll(' ', depth * element_value_pair_indent); + try w.print("{d}:\n", .{ i }); + try element_value_pair.indentedFormat(w, depth, element_value_pair_indent + 1, constant_pool); + } + } + pub fn @"type"(self: *const Self, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { return constant_pool.getTag(self.type_index, .utf8); } @@ -1548,6 +1759,26 @@ pub const RuntimeInvisibleAnnotations = struct { pub const ParameterAnnotation = struct { annotations: []Annotation, + + pub fn indentedFormat( + self: *const ParameterAnnotation, + w: *std.Io.Writer, + depth: usize, + indent: u8, + constant_pool: ConstantPool, + ) std.Io.Writer.Error!void { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("annotations:\n"); + var annotation_indent = indent; + for (self.annotations, 0..) |annotation, i| { + annotation_indent += 1; + defer annotation_indent -= 1; + + try w.splatByteAll(' ', depth * annotation_indent); + try w.print("{d}:\n", .{ i }); + try annotation.indentedFormat(w, depth, annotation_indent + 1, constant_pool); + } + } }; pub const RuntimeVisibleParameterAnnotations = struct { diff --git a/testsuite/classes/java/CustomAnnotation.java b/testsuite/classes/java/CustomAnnotation.java new file mode 100644 index 0000000..394a3f8 --- /dev/null +++ b/testsuite/classes/java/CustomAnnotation.java @@ -0,0 +1,14 @@ +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.annotation.ElementType; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@CustomAnnotationAnnotation +public @interface CustomAnnotation { + public CustomAnnotationType type(); + + public String name() default "Bob"; +} + diff --git a/testsuite/classes/java/CustomAnnotationAnnotation.java b/testsuite/classes/java/CustomAnnotationAnnotation.java new file mode 100644 index 0000000..67da5e7 --- /dev/null +++ b/testsuite/classes/java/CustomAnnotationAnnotation.java @@ -0,0 +1,9 @@ +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.annotation.ElementType; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.ANNOTATION_TYPE) +public @interface CustomAnnotationAnnotation { } + diff --git a/testsuite/classes/java/CustomAnnotationType.java b/testsuite/classes/java/CustomAnnotationType.java new file mode 100644 index 0000000..77d0b61 --- /dev/null +++ b/testsuite/classes/java/CustomAnnotationType.java @@ -0,0 +1,5 @@ +public enum CustomAnnotationType { + ONE, + TWO, +} + diff --git a/testsuite/classes/java/CustomAttribute.java b/testsuite/classes/java/CustomParameterAnnotation.java similarity index 71% rename from testsuite/classes/java/CustomAttribute.java rename to testsuite/classes/java/CustomParameterAnnotation.java index e3fe264..b031772 100644 --- a/testsuite/classes/java/CustomAttribute.java +++ b/testsuite/classes/java/CustomParameterAnnotation.java @@ -4,7 +4,6 @@ import java.lang.annotation.Target; import java.lang.annotation.ElementType; @Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -public @interface CustomAttribute { -} +@Target(ElementType.PARAMETER) +public @interface CustomParameterAnnotation { } diff --git a/testsuite/classes/java/Main.java b/testsuite/classes/java/Main.java index 01f523a..0f33998 100644 --- a/testsuite/classes/java/Main.java +++ b/testsuite/classes/java/Main.java @@ -1,11 +1,12 @@ import java.util.function.Supplier; +@CustomAnnotation(type = CustomAnnotationType.ONE, name = "Alice") public class Main implements Runnable, Supplier, Interface { private static final String name = "John"; private static final int number = 42; private static final double funnyNumber = 6.9; - public static void main(String[] args) { + public static void main(@CustomParameterAnnotation String[] args) { final var main = new Main(); main.sayHello();