From a47405e90365795aee07aa60a15126914c481d84 Mon Sep 17 00:00:00 2001 From: ktkk Date: Wed, 8 Jul 2026 14:24:18 +0000 Subject: [PATCH] Add runtime visible/invisible type annotations attributes --- src/Class/AttributeInfo.zig | 361 +++++++++++++++++++++++++++++++----- 1 file changed, 316 insertions(+), 45 deletions(-) diff --git a/src/Class/AttributeInfo.zig b/src/Class/AttributeInfo.zig index adf6b8e..f54c640 100644 --- a/src/Class/AttributeInfo.zig +++ b/src/Class/AttributeInfo.zig @@ -28,8 +28,8 @@ pub const AttributeInfo = union(enum) { runtime_invisible_annotations: RuntimeInvisibleAnnotations, runtime_visible_parameter_annotations: RuntimeVisibleParameterAnnotations, runtime_invisible_parameter_annotations: RuntimeInvisibleParameterAnnotations, - // runtime_visible_type_annotations: RuntimeVisibleTypeAnnotations, - // runtime_invisible_type_annotations: RuntimeInvisibleTypeAnnotations, + runtime_visible_type_annotations: RuntimeVisibleTypeAnnotations, + runtime_invisible_type_annotations: RuntimeInvisibleTypeAnnotations, annotation_default: AnnotationDefault, bootstrap_methods: BootstrapMethods, method_parameters: MethodParameters, @@ -163,6 +163,18 @@ pub const AttributeInfo = union(enum) { .runtime_invisible_parameter_annotations = try RuntimeInvisibleParameterAnnotations.parse(limited, allocator), }, }; + } else if (std.mem.eql(u8, name, "RuntimeVisibleTypeAnnotations")) { + return .{ + .predefined = .{ + .runtime_invisible_type_annotations = try RuntimeInvisibleTypeAnnotations.parse(limited, allocator), + }, + }; + } else if (std.mem.eql(u8, name, "RuntimeInvisibleTypeAnnotations")) { + return .{ + .predefined = .{ + .runtime_invisible_type_annotations = try RuntimeInvisibleTypeAnnotations.parse(limited, allocator), + }, + }; } else if (std.mem.eql(u8, name, "AnnotationDefault")) { return .{ .predefined = .{ @@ -246,6 +258,8 @@ pub const AttributeInfo = union(enum) { .runtime_invisible_annotations => |*runtime_invisible_annotations| runtime_invisible_annotations.deinit(allocator), .runtime_visible_parameter_annotations => |*runtime_visible_parameter_annotations| runtime_visible_parameter_annotations.deinit(allocator), .runtime_invisible_parameter_annotations => |*runtime_invisible_parameter_annotations| runtime_invisible_parameter_annotations.deinit(allocator), + .runtime_visible_type_annotations => |*runtime_visible_type_annotations| runtime_visible_type_annotations.deinit(allocator), + .runtime_invisible_type_annotations => |*runtime_invisible_type_annotations| runtime_invisible_type_annotations.deinit(allocator), .annotation_default => |*annotation_default| annotation_default.deinit(allocator), .bootstrap_methods => |*bootstrap_methods| bootstrap_methods.deinit(allocator), .method_parameters => |*method_parameters| method_parameters.deinit(allocator), @@ -1857,50 +1871,307 @@ pub const RuntimeInvisibleParameterAnnotations = struct { } }; -// TODO: TypeAnnotation structure -// pub const TypeAnnotation = struct { -// target_info: union(TargetType) { -// type_parameter_target: TypeParameterTarget, -// supertype_target: SupertypeTarget, -// type_parameter_bound_target: TypeParameterBoundTarget, -// emtpy_target: EmptyTarget, -// formal_parameter_target: FormalParameterTarget, -// throws_target: ThrowsTarget, -// localvar_target: LocalvarTarget, -// catch_target: CatchTarget, -// offset_target: OffsetTarget, -// type_argument_target: TypeArgumentTarget, -// }, -// target_path: TypePath, -// type_index: u16, -// element_value_pairs: []ElementValuePair, -// -// pub const TargetType = enum(u8) { -// type_parameter_target = 0x00, -// type -// }; -// -// pub const TypePath = struct { -// path: []Path, -// -// pub const Path = struct { -// type_path_kind: u8, -// type_argument_index: u8, -// }; -// }; -// }; +pub const TypeAnnotation = struct { + target_type: TargetType, + target_info: TargetInfo, + target_path: TypePath, + type_index: u16, + element_value_pairs: []ElementValuePair, -// pub const RuntimeVisibleTypeAnnotations = struct { -// type_annotations: []TypeAnnotation, -// -// const Self = @This(); -// }; -// -// pub const RuntimeInvisibleTypeAnnotations = struct { -// type_annotations: []TypeAnnotation, -// -// const Self = @This(); -// }; + const Self = @This(); + + pub const TargetType = enum(u8) { + /// type parameter declaration of generic class or interface + type_parameter_generic_class_interface = 0x00, + /// type parameter declaration of generic method or constructor + type_parameter_generic_method_constructor = 0x01, + /// type in extends or implements clause of class declaration + /// (including the direct superclass or direct superinterface of an anonymous class declaration), + /// or in extends clause of interface declaration + type_in_extends_implements = 0x10, + /// type in bound of type parameter declaration of generic class or interface + type_in_bound_generic_class_interface = 0x11, + /// type in bound of type parameter declaration of generic method or constructor + type_in_bound_generic_method_constructor = 0x12, + /// type in field or record component declaration + type_in_field_record_component = 0x13, + /// return type of method, or type of newly constructed object + return_type_method_newly_constructed_object = 0x14, + /// receiver type of method or constructor + receiver_type_method_constructor = 0x15, + /// type in formal parameter declaration of method, constructor, or lambda expression + type_in_formal_parameter_declaration_method_constructor_lambda = 0x16, + /// type in throws clause of method or constructor + type_in_throws_method_constructor = 0x17, + /// type in local variable declaration + type_in_local_variable = 0x40, + /// type in resource variable declaration + type_in_resource_variable = 0x41, + /// type in exception parameter declaration + type_in_exception_parameter = 0x42, + /// type in instanceof expression + type_in_instanceof = 0x43, + /// type in new expression + type_in_new = 0x44, + /// type in method reference expression using ::new + type_in_method_reference_new = 0x45, + /// type in method reference expression using ::Identifier + type_in_method_reference_identifier = 0x46, + /// type in cast expression + type_in_cast = 0x47, + /// type argument for generic constructor in new expression or explicit constructor invocation statement + type_generic_constructor_new_explicit = 0x48, + /// type argument for generic method in method invocation expression + type_generic_method_invocation = 0x49, + /// type argument for generic constructor in method reference expression using ::new + type_generic_constructor_method_reference_new = 0x4a, + /// type argument for generic method in method reference expression using ::Identifier + type_generic_method_method_reference_identifier = 0x4b, + }; + + pub const TargetInfo = union(enum) { + type_parameter: TypeParameter, + supertype: SuperType, + type_parameter_bound: TypeParameterBound, + empty: Empty, + formal_parameter: FormalParameter, + throws: Throws, + localvar: Localvar, + @"catch": Catch, + offset: Offset, + type_argument: TypeArgument, + + pub const TypeParameter = struct { + type_parameter_index: u8, + }; + + pub const SuperType = struct { + supertype_index: u16, + }; + + pub const TypeParameterBound = struct { + type_parameter_index: u8, + bound_index: u8, + }; + + pub const Empty = struct { }; + + pub const FormalParameter = struct { + formal_parameter_index: u8, + }; + + pub const Throws = struct { + throws_type_index: u16, + }; + + pub const Localvar = struct { + table: []Entry, + + pub const Entry = struct { + start_pc: u16, + length: u16, + index: u16, + }; + }; + + pub const Catch = struct { + exception_table_index: u16, + }; + + pub const Offset = struct { + offset: u16, + }; + + pub const TypeArgument = struct { + offset: u16, + type_argument_index: u8, + }; + }; + + pub const TypePath = struct { + paths: []Path, + + pub const Path = struct { + type_path_kind: u8, + type_argument_index: u8, + }; + + pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!TypePath { + const length = try input.takeByte(); + const paths = try allocator.alloc(Path, length); + errdefer allocator.free(paths); + for (paths) |*path| { + path.* = .{ + .type_path_kind = try input.takeByte(), + .type_argument_index = try input.takeByte(), + }; + } + + return .{ + .paths = paths, + }; + } + + pub fn deinit(self: *TypePath, allocator: std.mem.Allocator) void { + allocator.free(self.paths); + } + }; + + pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { + const target_type: TargetType = @enumFromInt(try input.takeInt(u8, .big)); + const target_info: TargetInfo = switch (target_type) { + .type_parameter_generic_class_interface, .type_parameter_generic_method_constructor => .{ + .type_parameter = .{ + .type_parameter_index = try input.takeByte(), + }, + }, + .type_in_extends_implements => .{ + .supertype = .{ + .supertype_index = try input.takeInt(u16, .big), + }, + }, + .type_in_bound_generic_class_interface, .type_in_bound_generic_method_constructor => .{ + .type_parameter_bound = .{ + .type_parameter_index = try input.takeByte(), + .bound_index = try input.takeByte(), + }, + }, + .type_in_field_record_component, .return_type_method_newly_constructed_object, .receiver_type_method_constructor => .{ + .empty = .{}, + }, + .type_in_formal_parameter_declaration_method_constructor_lambda => .{ + .formal_parameter = .{ + .formal_parameter_index = try input.takeByte(), + }, + }, + .type_in_throws_method_constructor => .{ + .throws = .{ + .throws_type_index = try input.takeInt(u16, .big), + }, + }, + .type_in_local_variable, .type_in_resource_variable => blk: { + const table_length = try input.takeInt(u16, .big); + const table = try allocator.alloc(TargetInfo.Localvar.Entry, table_length); + errdefer allocator.free(table); + for (table) |*entry| { + entry.* = .{ + .start_pc = try input.takeInt(u16, .big), + .length = try input.takeInt(u16, .big), + .index = try input.takeInt(u16, .big), + }; + } + + break :blk .{ + .localvar = .{ + .table = table, + }, + }; + }, + .type_in_exception_parameter => .{ + .@"catch" = . { + .exception_table_index = try input.takeInt(u16, .big), + }, + }, + .type_in_instanceof, .type_in_new, .type_in_method_reference_new, .type_in_method_reference_identifier => .{ + .offset = .{ + .offset = try input.takeInt(u16, .big), + }, + }, + .type_in_cast, .type_generic_constructor_new_explicit, .type_generic_method_invocation, .type_generic_constructor_method_reference_new, .type_generic_method_method_reference_identifier => .{ + .type_argument = .{ + .offset = try input.takeInt(u16, .big), + .type_argument_index = try input.takeByte(), + }, + }, + }; + + var target_path: TypePath = try .parse(input, allocator); + errdefer target_path.deinit(allocator); + + const type_index = try input.takeInt(u16, .big); + + const num_element_value_pairs = try input.takeInt(u16, .big); + const element_value_pairs = try allocator.alloc(ElementValuePair, num_element_value_pairs); + errdefer allocator.free(element_value_pairs); + for (element_value_pairs) |*element_value_pair| { + element_value_pair.* = .{ + .element_name_index = try input.takeInt(u16, .big), + .value = try .parse(input, allocator), + }; + } + + return .{ + .target_type = target_type, + .target_info = target_info, + .target_path = target_path, + .type_index = type_index, + .element_value_pairs = element_value_pairs, + }; + } + + pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + switch (self.target_info) { + .localvar => |localvar| allocator.free(localvar.table), + else => {}, + } + for (self.element_value_pairs) |*element_value_pair| { + element_value_pair.value.deinit(allocator); + } + allocator.free(self.element_value_pairs); + } +}; + +pub const RuntimeVisibleTypeAnnotations = struct { + type_annotations: []TypeAnnotation, + + const Self = @This(); + + pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { + const num_annotations = try input.takeInt(u16, .big); + const type_annotations = try allocator.alloc(TypeAnnotation, num_annotations); + errdefer allocator.free(type_annotations); + for (type_annotations) |*type_annotation| { + type_annotation.* = try .parse(input, allocator); + } + + return .{ + .type_annotations = type_annotations, + }; + } + + pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + for (self.type_annotations) |*type_annotation| { + type_annotation.deinit(allocator); + } + allocator.free(self.type_annotations); + } +}; + +pub const RuntimeInvisibleTypeAnnotations = struct { + type_annotations: []TypeAnnotation, + + const Self = @This(); + + pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { + const num_annotations = try input.takeInt(u16, .big); + const type_annotations = try allocator.alloc(TypeAnnotation, num_annotations); + errdefer allocator.free(type_annotations); + for (type_annotations) |*type_annotation| { + type_annotation.* = try .parse(input, allocator); + } + + return .{ + .type_annotations = type_annotations, + }; + } + + pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + for (self.type_annotations) |*type_annotation| { + type_annotation.deinit(allocator); + } + allocator.free(self.type_annotations); + } +}; pub const AnnotationDefault = struct { default_value: ElementValue,