From a18ae9c26505a08367fd9c00aeaa8d99cdf5af0d Mon Sep 17 00:00:00 2001 From: ktkk Date: Fri, 3 Jul 2026 23:59:28 +0200 Subject: [PATCH] Refactor AttributeInfo --- src/Class.zig | 22 +- src/Class/AttributeInfo.zig | 1997 +++++++++++++++++++---------------- src/Class/FieldInfo.zig | 4 +- src/Class/MethodInfo.zig | 4 +- 4 files changed, 1122 insertions(+), 905 deletions(-) diff --git a/src/Class.zig b/src/Class.zig index 2f09ba7..1eafdd6 100644 --- a/src/Class.zig +++ b/src/Class.zig @@ -4,7 +4,7 @@ const EnumFlags = @import("EnumFlags.zig").EnumFlags; pub const FieldInfo = @import("Class/FieldInfo.zig"); pub const MethodInfo = @import("Class/MethodInfo.zig"); -pub const AttributeInfo = @import("Class/AttributeInfo.zig"); +pub const AttributeInfo = @import("Class/AttributeInfo.zig").AttributeInfo; allocator: std.mem.Allocator, @@ -45,7 +45,7 @@ pub const ResolveError = error { InvalidConstantType, }; -pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { +pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) (ParseError || ResolveError)!Self { const magic = try input.takeInt(u32, .big); if (magic != 0xCAFEBABE) { return ParseError.InvalidMagicNumber; @@ -70,7 +70,7 @@ pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Sel const interfaces = try parseInterfaces(input, allocator); errdefer allocator.free(interfaces); - const fields = try parseFields(input, allocator); + const fields = try parseFields(input, constant_pool, allocator); errdefer { for (fields) |*field| { field.deinit(allocator); @@ -78,7 +78,7 @@ pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Sel allocator.free(fields); } - const methods = try parseMethods(input, allocator); + const methods = try parseMethods(input, constant_pool, allocator); errdefer { for (methods) |*method| { method.deinit(allocator); @@ -86,7 +86,7 @@ pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Sel allocator.free(methods); } - const attributes = try parseAttributes(input, allocator); + const attributes = try parseAttributes(input, constant_pool, allocator); errdefer { for (attributes) |*attribute| { attribute.deinit(allocator); @@ -146,37 +146,37 @@ fn parseInterfaces(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseErr return interfaces; } -fn parseFields(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError![]FieldInfo { +fn parseFields(input: *std.Io.Reader, constant_pool: []const ?ConstantPoolInfo, allocator: std.mem.Allocator) (ParseError || ResolveError)![]FieldInfo { const fields_count = try input.takeInt(u16, .big); const fields = try allocator.alloc(FieldInfo, fields_count); errdefer allocator.free(fields); for (0..fields_count) |i| { - fields[i] = try FieldInfo.parse(input, allocator); + fields[i] = try FieldInfo.parse(input, constant_pool, allocator); } return fields; } -fn parseMethods(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError![]MethodInfo { +fn parseMethods(input: *std.Io.Reader, constant_pool: []const ?ConstantPoolInfo, allocator: std.mem.Allocator) (ParseError || ResolveError)![]MethodInfo { const methods_count = try input.takeInt(u16, .big); const methods = try allocator.alloc(MethodInfo, methods_count); errdefer allocator.free(methods); for (0..methods_count) |i| { - methods[i] = try MethodInfo.parse(input, allocator); + methods[i] = try MethodInfo.parse(input, constant_pool, allocator); } return methods; } -pub fn parseAttributes(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError![]AttributeInfo { +pub fn parseAttributes(input: *std.Io.Reader, constant_pool: []const ?ConstantPoolInfo, allocator: std.mem.Allocator) (ParseError || ResolveError)![]AttributeInfo { const attributes_count = try input.takeInt(u16, .big); const attributes = try allocator.alloc(AttributeInfo, attributes_count); errdefer allocator.free(attributes); for (0..attributes_count) |i| { - attributes[i] = try AttributeInfo.parse(input, allocator); + attributes[i] = try AttributeInfo.parse(input, constant_pool, allocator); } return attributes; diff --git a/src/Class/AttributeInfo.zig b/src/Class/AttributeInfo.zig index a3276da..44edf19 100644 --- a/src/Class/AttributeInfo.zig +++ b/src/Class/AttributeInfo.zig @@ -4,974 +4,1138 @@ const EnumFlags = @import("../EnumFlags.zig").EnumFlags; const Class = @import("../Class.zig"); const ConstantPoolInfo = Class.ConstantPoolInfo; -const AttributeInfo = Class.AttributeInfo; const ParseError = Class.ParseError; const ResolveError = Class.ResolveError; -attribute_name_index: u16, -info: []u8, +pub const AttributeInfo = union(enum) { + new: NewAttribute, + predefined: union(enum) { + constant_value: ConstantValue, + code: Code, + stack_map_table: StackMapTable, + exceptions: Exceptions, + inner_classes: InnerClasses, + enclosing_method: EnclosingMethod, + synthetic: Synthetic, + signature: Signature, + source_file: SourceFile, + source_debug_extension: SourceDebugExtension, + line_number_table: LineNumberTable, + local_variable_table: LocalVariableTable, + local_variable_type_table: LocalVariableTypeTable, + deprecated: Deprecated, + runtime_visible_annotations: RuntimeVisibleAnnotations, + runtime_invisible_annotations: RuntimeInvisibleAnnotations, + runtime_visible_parameter_annotations: RuntimeVisibleParameterAnnotations, + runtime_invisible_parameter_annotations: RuntimeInvisibleParameterAnnotations, + annotation_default: AnnotationDefault, + bootstrap_methods: BootstrapMethods, + }, -const Self = @This(); + const Self = @This(); -pub const Predefined = union(enum) { - constant_value: ConstantValue, - code: Code, - stack_map_table: StackMapTable, - exceptions: Exceptions, - inner_classes: InnerClasses, - enclosing_method: EnclosingMethod, - synthetic: Synthetic, - signature: Signature, - source_file: SourceFile, - source_debug_extension: SourceDebugExtension, - line_number_table: LineNumberTable, - local_variable_table: LocalVariableTable, - local_variable_type_table: LocalVariableTypeTable, - deprecated: Deprecated, - runtime_visible_annotations: RuntimeVisibleAnnotations, - runtime_invisible_annotations: RuntimeInvisibleAnnotations, - runtime_visible_parameter_annotations: RuntimeVisibleParameterAnnotations, - runtime_invisible_parameter_annotations: RuntimeInvisibleParameterAnnotations, - annotation_default: AnnotationDefault, - bootstrap_methods: BootstrapMethods, + pub fn parse(input: *std.Io.Reader, constant_pool: []const ?ConstantPoolInfo, allocator: std.mem.Allocator) (ParseError || ResolveError)!AttributeInfo { + const attribute_name_index = try input.takeInt(u16, .big) - 1; + if (attribute_name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[attribute_name_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + const attribute_name = cp_info.?.utf8; - pub const ConstantValue = struct { - constant_value_index: u16, + const attribute_length = try input.takeInt(u32, .big); - pub fn constantValue(self: *const ConstantValue, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { - const constant_value_index = self.constant_value_index - 1; - if (constant_value_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[constant_value_index]; - if (cp_info == null) return ResolveError.InvalidConstantType; + var limited_buf: [1024]u8 = undefined; + var limited_reader: std.Io.Reader.Limited = .init(input, .limited(attribute_length), &limited_buf); + const limited = &limited_reader.interface; - return cp_info.?; + if (std.mem.eql(u8, attribute_name.bytes, "ConstantValue")) { + return .{ + .predefined = .{ + .constant_value = try ConstantValue.parse(limited), + }, + }; + } else if (std.mem.eql(u8, attribute_name.bytes, "Code")) { + return .{ + .predefined = .{ + .code = try Code.parse(limited, constant_pool, allocator), + }, + }; + } else if (std.mem.eql(u8, attribute_name.bytes, "StackMapTable")) { + return .{ + .predefined = .{ + .stack_map_table = try StackMapTable.parse(limited, allocator), + }, + }; + } else if (std.mem.eql(u8, attribute_name.bytes, "Exceptions")) { + return .{ + .predefined = .{ + .exceptions = try Exceptions.parse(limited, allocator), + }, + }; + } else if (std.mem.eql(u8, attribute_name.bytes, "InnerClasses")) { + return .{ + .predefined = .{ + .inner_classes = try InnerClasses.parse(limited, allocator), + }, + }; + } else if (std.mem.eql(u8, attribute_name.bytes, "EnclosingMethod")) { + return .{ + .predefined = .{ + .enclosing_method = try EnclosingMethod.parse(limited), + }, + }; + } else if (std.mem.eql(u8, attribute_name.bytes, "Synthetic")) { + return .{ + .predefined = .{ + .synthetic = .{}, + }, + }; + } else if (std.mem.eql(u8, attribute_name.bytes, "Signature")) { + return .{ + .predefined = .{ + .signature = try Signature.parse(limited), + }, + }; + } else if (std.mem.eql(u8, attribute_name.bytes, "SourceFile")) { + return .{ + .predefined = .{ + .source_file = try SourceFile.parse(limited), + }, + }; + } else if (std.mem.eql(u8, attribute_name.bytes, "SourceDebugExtension")) { + return .{ + .predefined = .{ + .source_debug_extension = try SourceDebugExtension.parse(limited, attribute_length, allocator), + }, + }; + } else if (std.mem.eql(u8, attribute_name.bytes, "LineNumberTable")) { + return .{ + .predefined = .{ + .line_number_table = try LineNumberTable.parse(limited, allocator), + }, + }; + } else if (std.mem.eql(u8, attribute_name.bytes, "LocalVariableTable")) { + return .{ + .predefined = .{ + .local_variable_table = try LocalVariableTable.parse(limited, allocator), + }, + }; + } else if (std.mem.eql(u8, attribute_name.bytes, "LocalVariableTypeTable")) { + return .{ + .predefined = .{ + .local_variable_type_table = try LocalVariableTypeTable.parse(limited, allocator), + }, + }; + } else if (std.mem.eql(u8, attribute_name.bytes, "Deprecated")) { + return .{ + .predefined = .{ + .deprecated = .{}, + }, + }; + } else if (std.mem.eql(u8, attribute_name.bytes, "RuntimeVisibleAnnotations")) { + return .{ + .predefined = .{ + .runtime_visible_annotations = try RuntimeVisibleAnnotations.parse(limited, allocator), + }, + }; + } else if (std.mem.eql(u8, attribute_name.bytes, "RuntimeInvisibleAnnotations")) { + return .{ + .predefined = .{ + .runtime_invisible_annotations = try RuntimeInvisibleAnnotations.parse(limited, allocator), + }, + }; + } else if (std.mem.eql(u8, attribute_name.bytes, "RuntimeVisibleParameterAnnotations")) { + return .{ + .predefined = .{ + .runtime_visible_parameter_annotations = try RuntimeVisibleParameterAnnotations.parse(limited, allocator), + }, + }; + } else if (std.mem.eql(u8, attribute_name.bytes, "RuntimeInvisibleParameterAnnotations")) { + return .{ + .predefined = .{ + .runtime_invisible_parameter_annotations = try RuntimeInvisibleParameterAnnotations.parse(limited, allocator), + }, + }; + } else if (std.mem.eql(u8, attribute_name.bytes, "AnnotationDefault")) { + return .{ + .predefined = .{ + .annotation_default = try AnnotationDefault.parse(limited, allocator), + }, + }; + } else if (std.mem.eql(u8, attribute_name.bytes, "BootstrapMethods")) { + return .{ + .predefined = .{ + .bootstrap_methods = try BootstrapMethods.parse(limited, allocator), + }, + }; + } else { + return .{ + .new = try NewAttribute.parse(limited, attribute_name_index, attribute_length, allocator), + }; } - }; + } - pub const Code = struct { - max_stack: u16, - max_locals: u16, - code: []u8, - exception_table: []ExceptionHandler, - attributes: []AttributeInfo, + pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + switch (self.*) { + .new => |*new| new.deinit(allocator), + .predefined => |*predefined| switch (predefined.*) { + .code => |*code| code.deinit(allocator), + .stack_map_table => |*stack_map_table| stack_map_table.deinit(allocator), + .exceptions => |*exceptions| exceptions.deinit(allocator), + .inner_classes => |*inner_classes| inner_classes.deinit(allocator), + .source_debug_extension => |*source_debug_extension| source_debug_extension.deinit(allocator), + .line_number_table => |*line_number_table| line_number_table.deinit(allocator), + .local_variable_table => |*local_variable_table| local_variable_table.deinit(allocator), + .local_variable_type_table => |*local_variable_type_table| local_variable_type_table.deinit(allocator), + .runtime_visible_annotations => |*runtime_visible_annotations| runtime_visible_annotations.deinit(allocator), + .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), + .annotation_default => |*annotation_default| annotation_default.deinit(allocator), + .bootstrap_methods => |*bootstrap_methods| bootstrap_methods.deinit(allocator), + else => {}, + }, + } + } - pub const ExceptionHandler = struct { - start_pc: u16, - end_pc: u16, - handler_pc: u16, - catch_type: u16, - }; - }; + pub fn format( + self: *const Self, + w: *std.Io.Writer, + depth: usize, + indent: u8, + allocator: std.mem.Allocator, + constant_pool: []const ?ConstantPoolInfo, + ) std.Io.Writer.Error!void { + switch (self.*) { + .new => |new| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("predefined: no\n"); - pub const StackMapTable = struct { - entries: []StackMapFrame, + try w.splatByteAll(' ', depth * indent); + _ = try w.write("name:\n"); + const attribute_name = new.attributeName(constant_pool) catch return error.WriteFailed; + try attribute_name.format(w, depth, indent + 1, constant_pool); + }, + .predefined => |predefined| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("predefined: yes\n"); - pub const StackMapFrame = union(enum) { - // 0-63 - same_frame, - // 64-127 - same_locals_1_stack_item_frame: struct { - stack: [1]VerificationTypeInfo, - }, - // 247 - same_locals_1_stack_item_frame_extended: struct { - offset_delta: u16, - stack: [1]VerificationTypeInfo, - }, - // 248-250 - chop_frame: struct { - offset_delta: u16, - }, - // 251 - same_frame_extended: struct { - offset_delta: u16, - }, - // 252-254 - append_frame: struct { - offset_delta: u16, - locals: []const VerificationTypeInfo, - }, - // 255 - full_frame: struct { - offset_delta: u16, - locals: []const VerificationTypeInfo, - stack: []const VerificationTypeInfo, - }, + try w.splatByteAll(' ', depth * indent); + try w.print("type: {s}\n", .{ @tagName(predefined) }); - pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!StackMapFrame { - const frame_type = try input.takeByte(); - return switch (frame_type) { - 0...63 => .same_frame, - 64...127 => .{ - .same_locals_1_stack_item_frame = .{ - .stack = .{ try .parse(input) }, - }, - }, - 247 => .{ - .same_locals_1_stack_item_frame_extended = .{ - .offset_delta = try input.takeInt(u16, .big), - .stack = .{ try .parse(input) }, - }, - }, - 248...250 => .{ - .chop_frame = .{ - .offset_delta = try input.takeInt(u16, .big), - }, - }, - 252...254 => blk: { - const offset_delta = try input.takeInt(u16, .big); + switch (predefined) { + .constant_value => |attr| { + const constant_value = attr.constantValue(constant_pool) catch return error.WriteFailed; + try constant_value.format(w, depth, indent + 1, constant_pool); + }, + .code => |attr| { + try w.splatByteAll(' ', depth * indent); + try w.print("max_stack: {d}\n", .{ attr.max_stack }); - const length = frame_type - 251; - const locals = try allocator.alloc(Predefined.StackMapTable.VerificationTypeInfo, length); - errdefer allocator.free(locals); - for (locals) |*local| { - local.* = try .parse(input); + try w.splatByteAll(' ', depth * indent); + try w.print("max_locals: {d}\n", .{ attr.max_locals }); + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("code:\n"); + var instr_indent = indent; + for (attr.code, 0..) |instr, i| { + instr_indent += 1; + defer instr_indent -= 1; + + try w.splatByteAll(' ', depth * instr_indent); + try w.print("{d}: 0x{x}\n", .{ i, instr }); } - break :blk .{ - .append_frame = .{ - .offset_delta = offset_delta, - .locals = locals, - }, - }; - }, - 255 => blk: { - const offset_delta = try input.takeInt(u16, .big); + //try w.splatByteAll(' ', depth * indent); + //_ = try w.write("exception_table:\n"); + //var exception_indent = indent; + //for (attr.exception_table, 0..) |*exception, i| { + // exception_indent += 1; + // defer exception_indent -= 1; - const number_of_locals = try input.takeInt(u16, .big); - const locals = try allocator.alloc(Predefined.StackMapTable.VerificationTypeInfo, number_of_locals); - errdefer allocator.free(locals); - for (locals) |*local| { - local.* = try .parse(input); + // try w.splatByteAll(' ', depth * exception_indent); + // try w.print("{d}:\n", .{ i }); + + // try self.formatExceptionHandler(w, depth, exception_indent + 1, exception); + //} + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("attributes:\n"); + var attr_indent = indent; + for (attr.attributes, 0..) |*a, i| { + attr_indent += 1; + defer attr_indent -= 1; + + try w.splatByteAll(' ', depth * attr_indent); + try w.print("{d}:\n", .{ i }); + + try a.format(w, depth, attr_indent + 1, allocator, constant_pool); } + }, + //.stack_map_table, + .exceptions => |attr| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("exceptions:\n"); + var exception_indent = indent; + for (attr.exception_index_table, 0..) |exception, i| { + exception_indent += 1; + defer exception_indent -= 1; - const number_of_stack_items = try input.takeInt(u16, .big); - const stack = try allocator.alloc(Predefined.StackMapTable.VerificationTypeInfo, number_of_stack_items); - errdefer allocator.free(stack); - for (stack) |*stack_item| { - stack_item.* = try .parse(input); + if (exception >= constant_pool.len) return error.WriteFailed; + const cp_info = constant_pool[exception - 1]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return error.WriteFailed; + + try w.splatByteAll(' ', depth * exception_indent); + try w.print("{d}:\n", .{ i }); + try cp_info.?.format(w, depth, exception_indent + 1, constant_pool); } - - break :blk .{ - .full_frame = .{ - .offset_delta = offset_delta, - .locals = locals, - .stack = stack, - }, - }; }, - else => unreachable, - }; - } + .inner_classes => |attr| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("classes:\n"); + var class_indent = indent; + for (attr.classes, 0..) |*class, i| { + class_indent += 1; + defer class_indent -= 1; - pub fn deinit(self: *StackMapFrame, allocator: std.mem.Allocator) void { - switch (self.*) { - .append_frame => |frame| allocator.free(frame.locals), - .full_frame => |frame| { - allocator.free(frame.locals); - allocator.free(frame.stack); + try w.splatByteAll(' ', depth * class_indent); + try w.print("{d}:\n", .{ i }); + try class.format(w, depth, class_indent + 1, constant_pool); + } }, + .enclosing_method => |attr| { + const class = attr.class(constant_pool) catch return error.WriteFailed; + try w.splatByteAll(' ', depth * indent); + _ = try w.write("class:\n"); + try class.format(w, depth, indent + 1, constant_pool); + + const method = attr.method(constant_pool) catch return error.WriteFailed; + if (method) |m| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("method:\n"); + try m.format(w, depth, indent + 1, constant_pool); + } + }, + .synthetic => {}, + .signature => |attr| { + const signature = attr.signature(constant_pool) catch return error.WriteFailed; + try signature.format(w, depth, indent + 1, constant_pool); + }, + .source_file => |attr| { + const source_file = attr.sourceFile(constant_pool) catch return error.WriteFailed; + try source_file.format(w, depth, indent + 1, constant_pool); + }, + //.source_debug_extension, + //.line_number_table, + //.local_variable_table, + //.local_variable_type_table, + .deprecated => {}, + //.runtime_visible_annotations, + //.runtime_invisible_annotations, + //.runtime_visible_parameter_annotations, + //.runtime_invisible_parameter_annotations, + //.annotation_default, + //.bootstrap_methods, else => {}, } - } + }, + } + } +}; + +pub const NewAttribute = struct { + attribute_name_index: u16, + info: []u8, + + const Self = @This(); + + pub fn parse(input: *std.Io.Reader, attribute_name_index: u16, attribute_length: u32, allocator: std.mem.Allocator) ParseError!Self { + return .{ + .attribute_name_index = attribute_name_index, + .info = try input.readAlloc(allocator, attribute_length), }; + } - pub const VerificationTypeInfo = union(VerificationTypeInfo.Tag) { - top, - integer, - float, - double, - long, - null, - uninitialized_this, - object: struct { - cpool_index: u16, - }, - uninitialized: struct { - cpool_index: u16, - }, + pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + allocator.free(self.info); + } - pub const Tag = enum(u8) { - top = 0, - integer = 1, - float = 2, - double = 3, - long = 4, - null = 5, - uninitialized_this = 6, - object = 7, - uninitialized = 8, + pub fn attributeName(self: *const Self, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const attribute_name_index = self.attribute_name_index - 1; + if (attribute_name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[attribute_name_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + + return cp_info.?; + } +}; + +pub const ConstantValue = struct { + constant_value_index: u16, + + const Self = @This(); + + pub fn parse(input: *std.Io.Reader) ParseError!Self { + return .{ + .constant_value_index = try input.takeInt(u16, .big), + }; + } + + pub fn constantValue(self: *const Self, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const constant_value_index = self.constant_value_index - 1; + if (constant_value_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[constant_value_index]; + if (cp_info == null) return ResolveError.InvalidConstantType; + + return cp_info.?; + } +}; + +pub const Code = struct { + max_stack: u16, + max_locals: u16, + code: []u8, + exception_table: []ExceptionHandler, + attributes: []AttributeInfo, + + const Self = @This(); + + pub const ExceptionHandler = struct { + start_pc: u16, + end_pc: u16, + handler_pc: u16, + catch_type: u16, + }; + + pub fn parse(input: *std.Io.Reader, constant_pool: []const ?ConstantPoolInfo, allocator: std.mem.Allocator) (ParseError || ResolveError)!Self { + const max_stack = try input.takeInt(u16, .big); + const max_locals = try input.takeInt(u16, .big); + + const code_length = try input.takeInt(u32, .big); + const code = try input.readAlloc(allocator, code_length); + errdefer allocator.free(code); + + const exception_table_length = try input.takeInt(u16, .big); + const exception_table = try allocator.alloc(ExceptionHandler, exception_table_length); + errdefer allocator.free(exception_table); + for (exception_table) |*exception_handler| { + exception_handler.* = .{ + .start_pc = try input.takeInt(u16, .big), + .end_pc = try input.takeInt(u16, .big), + .handler_pc = try input.takeInt(u16, .big), + .catch_type = try input.takeInt(u16, .big), }; - - pub fn parse(input: *std.Io.Reader) ParseError!VerificationTypeInfo { - const tag_byte = try input.takeByte(); - const tag = std.enums.fromInt(Predefined.StackMapTable.VerificationTypeInfo.Tag, tag_byte) orelse return ParseError.InvalidTag; - - return switch (tag) { - .top => .top, - .integer => .integer, - .float => .float, - .double => .double, - .long => .long, - .null => .null, - .uninitialized_this => .uninitialized_this, - .object => .{ - .object = .{ - .cpool_index = try input.takeInt(u16, .big), - }, - }, - .uninitialized => .{ - .uninitialized = .{ - .cpool_index = try input.takeInt(u16, .big), - }, - }, - }; - } - }; - }; - - pub const Exceptions = struct { - exception_index_table: []u16, - }; - - pub const InnerClasses = struct { - classes: []InnerClass, - - pub const InnerClass = struct { - inner_class_info_index: u16, - outer_class_info_index: u16, - inner_name_index: u16, - inner_class_access_flags: InnerClassAccessFlags, - - pub fn format(self: *const InnerClass, w: *std.Io.Writer, depth: usize, indent: u8, constant_pool: []const ?ConstantPoolInfo) std.Io.Writer.Error!void { - try w.splatByteAll(' ', depth * indent); - try w.print("flags: {f}\n", .{ self.inner_class_access_flags }); - - const inner_class = self.innerClass(constant_pool) catch return error.WriteFailed; - try w.splatByteAll(' ', depth * indent); - _ = try w.write("inner_class:\n"); - try inner_class.format(w, depth, indent + 1, constant_pool); - - const outer_class = self.outerClass(constant_pool) catch return error.WriteFailed; - if (outer_class) |oc| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("outer_class:\n"); - try oc.format(w, depth, indent + 1, constant_pool); - } - - const inner_name = self.innerName(constant_pool) catch return error.WriteFailed; - if (inner_name) |in| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("inner_name:\n"); - try in.format(w, depth, indent + 1, constant_pool); - } - } - - pub fn innerClass(self: *const InnerClass, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { - const inner_class_info_index = self.inner_class_info_index - 1; - if (inner_class_info_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[inner_class_info_index]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; - return cp_info.?; - } - - pub fn outerClass(self: *const InnerClass, constant_pool: []const ?ConstantPoolInfo) ResolveError!?ConstantPoolInfo { - if (self.outer_class_info_index == 0) return null; - const outer_class_info_index = self.outer_class_info_index - 1; - if (outer_class_info_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - - const cp_info = constant_pool[outer_class_info_index]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; - return cp_info.?; - } - - pub fn innerName(self: *const InnerClass, constant_pool: []const ?ConstantPoolInfo) ResolveError!?ConstantPoolInfo { - if (self.inner_name_index == 0) return null; - const inner_name_index = self.inner_name_index - 1; - if (inner_name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - - const cp_info = constant_pool[inner_name_index]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; - } - }; - - pub const InnerClassFlags = enum(u16) { - public = 0x0001, - private = 0x0002, - protected = 0x0004, - static = 0x0008, - final = 0x0010, - interface = 0x0200, - abstract = 0x0400, - synthetic = 0x1000, - annotation = 0x2000, - @"enum" = 0x4000, - }; - - pub const InnerClassAccessFlags = EnumFlags(InnerClassFlags); - }; - - pub const EnclosingMethod = struct { - class_index: u16, - method_index: u16, - - pub fn class(self: *const EnclosingMethod, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { - const class_index = self.class_index - 1; - if (class_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[class_index]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; - return cp_info.?; } - pub fn method(self: *const EnclosingMethod, constant_pool: []const ?ConstantPoolInfo) ResolveError!?ConstantPoolInfo { - if (self.method_index == 0) return null; - const method_index = self.method_index - 1; - if (method_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[method_index]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType; - return cp_info.?; + const attribute_count = try input.takeInt(u16, .big); + const attributes = try allocator.alloc(AttributeInfo, attribute_count); + errdefer allocator.free(attributes); + for (attributes) |*attribute| { + attribute.* = try .parse(input, constant_pool, allocator); } - }; - pub const Synthetic = struct { }; + return .{ + .max_stack = max_stack, + .max_locals = max_locals, + .code = code, + .exception_table = exception_table, + .attributes = attributes, + }; + } - pub const Signature = struct { - signature_index: u16, - - pub fn signature(self: *const Signature, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { - const signature_index = self.signature_index - 1; - if (signature_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[signature_index]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + allocator.free(self.code); + allocator.free(self.exception_table); + for (self.attributes) |*attr| { + attr.deinit(allocator); } - }; + allocator.free(self.attributes); + } +}; - pub const SourceFile = struct { - source_file_index: u16, +pub const StackMapTable = struct { + entries: []StackMapFrame, - pub fn sourceFile(self: *const SourceFile, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { - const source_file_index = self.source_file_index - 1; - if (source_file_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[source_file_index]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; - } - }; + const Self = @This(); - pub const SourceDebugExtension = struct { - debug_extension: []u8, - }; - - pub const LineNumberTable = struct { - line_number_table: []const LineNumber, - - pub const LineNumber = struct { - start_pc: u16, - line_number: u16, - }; - }; - - pub const LocalVariableTable = struct { - local_variable_table: []LocalVariable, - - pub const LocalVariable = struct { - start_pc: u16, - length: u16, - name_index: u16, - descriptor_index: u16, - index: u16, - }; - }; - - pub const LocalVariableTypeTable = struct { - local_variable_type_table: []LocalVariableType, - - pub const LocalVariableType = struct { - start_pc: u16, - length: u16, - name_index: u16, - signature_index: u16, - index: u16, - }; - }; - - pub const Deprecated = struct { }; - - pub const ElementValue = union(enum) { - // B, C, D, F, I, J, S, Z, s - const_value_index: u16, - // e - enum_const_value: struct { - type_name_index: u16, - const_name_index: u16, + pub const StackMapFrame = union(enum) { + // 0-63 + same_frame, + // 64-127 + same_locals_1_stack_item_frame: struct { + stack: [1]VerificationTypeInfo, }, - // c - class_info_index: u16, - // @ - annotation_value: Annotation, - // [ - array_value: struct { - values: []ElementValue, + // 247 + same_locals_1_stack_item_frame_extended: struct { + offset_delta: u16, + stack: [1]VerificationTypeInfo, + }, + // 248-250 + chop_frame: struct { + offset_delta: u16, + }, + // 251 + same_frame_extended: struct { + offset_delta: u16, + }, + // 252-254 + append_frame: struct { + offset_delta: u16, + locals: []const VerificationTypeInfo, + }, + // 255 + full_frame: struct { + offset_delta: u16, + locals: []const VerificationTypeInfo, + stack: []const VerificationTypeInfo, }, - 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), - }, - 'e' => .{ - .enum_const_value = .{ - .type_name_index = try input.takeInt(u16, .big), - .const_name_index = try input.takeInt(u16, .big), + pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!StackMapFrame { + const frame_type = try input.takeByte(); + return switch (frame_type) { + 0...63 => .same_frame, + 64...127 => .{ + .same_locals_1_stack_item_frame = .{ + .stack = .{ try .parse(input) }, }, + }, + 247 => .{ + .same_locals_1_stack_item_frame_extended = .{ + .offset_delta = try input.takeInt(u16, .big), + .stack = .{ try .parse(input) }, }, - 'c' => .{ - .class_info_index = try input.takeInt(u16, .big), }, - '@' => .{ - .annotation_value = try .parse(input, allocator), + 248...250 => .{ + .chop_frame = .{ + .offset_delta = try input.takeInt(u16, .big), + }, }, - '[' => .{ - .array_value = blk: { - const num_values = try input.takeInt(u16, .big); - const values = try allocator.alloc(ElementValue, num_values); - errdefer allocator.free(values); - for (values) |*value| { - value.* = try .parse(input, allocator); - } + 252...254 => blk: { + const offset_delta = try input.takeInt(u16, .big); - break :blk .{ - .values = values, - }; - }, - }, + const length = frame_type - 251; + const locals = try allocator.alloc(StackMapTable.VerificationTypeInfo, length); + errdefer allocator.free(locals); + for (locals) |*local| { + local.* = try .parse(input); + } + + break :blk .{ + .append_frame = .{ + .offset_delta = offset_delta, + .locals = locals, + }, + }; + }, + 255 => blk: { + const offset_delta = try input.takeInt(u16, .big); + + const number_of_locals = try input.takeInt(u16, .big); + const locals = try allocator.alloc(StackMapTable.VerificationTypeInfo, number_of_locals); + errdefer allocator.free(locals); + for (locals) |*local| { + local.* = try .parse(input); + } + + const number_of_stack_items = try input.takeInt(u16, .big); + const stack = try allocator.alloc(StackMapTable.VerificationTypeInfo, number_of_stack_items); + errdefer allocator.free(stack); + for (stack) |*stack_item| { + stack_item.* = try .parse(input); + } + + break :blk .{ + .full_frame = .{ + .offset_delta = offset_delta, + .locals = locals, + .stack = stack, + }, + }; + }, else => unreachable, }; } - pub fn deinit(self: *ElementValue, allocator: std.mem.Allocator) void { + pub fn deinit(self: *StackMapFrame, allocator: std.mem.Allocator) void { switch (self.*) { - .annotation_value => |*element_value| element_value.deinit(allocator), - .array_value => |element_value| { - for (element_value.values) |*value| { - value.deinit(allocator); - } - allocator.free(element_value.values); + .append_frame => |frame| allocator.free(frame.locals), + .full_frame => |frame| { + allocator.free(frame.locals); + allocator.free(frame.stack); }, else => {}, } } }; - pub const Annotation = struct { - type_index: u16, - element_value_pairs: []ElementValuePair, + pub const VerificationTypeInfo = union(VerificationTypeInfo.Tag) { + top, + integer, + float, + double, + long, + null, + uninitialized_this, + object: struct { + cpool_index: u16, + }, + uninitialized: struct { + cpool_index: u16, + }, - pub const ElementValuePair = struct { - element_name_index: u16, - value: ElementValue, + pub const Tag = enum(u8) { + top = 0, + integer = 1, + float = 2, + double = 3, + long = 4, + null = 5, + uninitialized_this = 6, + object = 7, + uninitialized = 8, }; - pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Annotation { - const type_index = try input.takeInt(u16, .big); + pub fn parse(input: *std.Io.Reader) ParseError!VerificationTypeInfo { + const tag_byte = try input.takeByte(); + const tag = std.enums.fromInt(StackMapTable.VerificationTypeInfo.Tag, tag_byte) orelse return ParseError.InvalidTag; - 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 switch (tag) { + .top => .top, + .integer => .integer, + .float => .float, + .double => .double, + .long => .long, + .null => .null, + .uninitialized_this => .uninitialized_this, + .object => .{ + .object = .{ + .cpool_index = try input.takeInt(u16, .big), + }, + }, + .uninitialized => .{ + .uninitialized = .{ + .cpool_index = try input.takeInt(u16, .big), + }, + }, + }; + } + }; + + pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { + const number_of_entries = try input.takeInt(u16, .big); + const entries = try allocator.alloc(StackMapTable.StackMapFrame, number_of_entries); + errdefer allocator.free(entries); + for (entries) |*entry| { + entry.* = try .parse(input, allocator); + } + + return .{ + .entries = entries, + }; + } + + pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + for (self.entries) |*entry| { + entry.deinit(allocator); + } + allocator.free(self.entries); + } +}; + +pub const Exceptions = struct { + exception_index_table: []u16, + + const Self = @This(); + + pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { + const number_of_exceptions = try input.takeInt(u16, .big); + const exception_index_table = try allocator.alloc(u16, number_of_exceptions); + errdefer allocator.free(exception_index_table); + for (exception_index_table) |*exception_index| { + exception_index.* = try input.takeInt(u16, .big); + } + + return .{ + .exception_index_table = exception_index_table, + }; + } + + pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + allocator.free(self.exception_index_table); + } +}; + +pub const InnerClasses = struct { + classes: []InnerClass, + + const Self = @This(); + + pub const InnerClass = struct { + inner_class_info_index: u16, + outer_class_info_index: u16, + inner_name_index: u16, + inner_class_access_flags: InnerClassAccessFlags, + + pub fn format(self: *const InnerClass, w: *std.Io.Writer, depth: usize, indent: u8, constant_pool: []const ?ConstantPoolInfo) std.Io.Writer.Error!void { + try w.splatByteAll(' ', depth * indent); + try w.print("flags: {f}\n", .{ self.inner_class_access_flags }); + + const inner_class = self.innerClass(constant_pool) catch return error.WriteFailed; + try w.splatByteAll(' ', depth * indent); + _ = try w.write("inner_class:\n"); + try inner_class.format(w, depth, indent + 1, constant_pool); + + const outer_class = self.outerClass(constant_pool) catch return error.WriteFailed; + if (outer_class) |oc| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("outer_class:\n"); + try oc.format(w, depth, indent + 1, constant_pool); } - return .{ - .type_index = type_index, - .element_value_pairs = element_value_pairs, + const inner_name = self.innerName(constant_pool) catch return error.WriteFailed; + if (inner_name) |in| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("inner_name:\n"); + try in.format(w, depth, indent + 1, constant_pool); + } + } + + pub fn innerClass(self: *const InnerClass, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const inner_class_info_index = self.inner_class_info_index - 1; + if (inner_class_info_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[inner_class_info_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; + return cp_info.?; + } + + pub fn outerClass(self: *const InnerClass, constant_pool: []const ?ConstantPoolInfo) ResolveError!?ConstantPoolInfo { + if (self.outer_class_info_index == 0) return null; + const outer_class_info_index = self.outer_class_info_index - 1; + if (outer_class_info_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + + const cp_info = constant_pool[outer_class_info_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; + return cp_info.?; + } + + pub fn innerName(self: *const InnerClass, constant_pool: []const ?ConstantPoolInfo) ResolveError!?ConstantPoolInfo { + if (self.inner_name_index == 0) return null; + const inner_name_index = self.inner_name_index - 1; + if (inner_name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + + const cp_info = constant_pool[inner_name_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; + } + }; + + pub const InnerClassFlags = enum(u16) { + public = 0x0001, + private = 0x0002, + protected = 0x0004, + static = 0x0008, + final = 0x0010, + interface = 0x0200, + abstract = 0x0400, + synthetic = 0x1000, + annotation = 0x2000, + @"enum" = 0x4000, + }; + + pub const InnerClassAccessFlags = EnumFlags(InnerClassFlags); + + pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { + const number_of_classes = try input.takeInt(u16, .big); + const classes = try allocator.alloc(InnerClass, number_of_classes); + errdefer allocator.free(classes); + for (classes) |*class| { + class.* = .{ + .inner_class_info_index = try input.takeInt(u16, .big), + .outer_class_info_index = try input.takeInt(u16, .big), + .inner_name_index = try input.takeInt(u16, .big), + .inner_class_access_flags = .{ .mask = try input.takeInt(u16, .big) }, }; } - pub fn deinit(self: *Annotation, allocator: std.mem.Allocator) void { - for (self.element_value_pairs) |*element_value_pair| { - element_value_pair.value.deinit(allocator); - } - allocator.free(self.element_value_pairs); - } - }; - - pub const RuntimeVisibleAnnotations = struct { - annotations: []Annotation, - }; - - pub const RuntimeInvisibleAnnotations = struct { - annotations: []Annotation, - }; - - pub const ParameterAnnotation = struct { - annotations: []Annotation, - }; - - pub const RuntimeVisibleParameterAnnotations = struct { - parameter_annotations: []ParameterAnnotation, - }; - - pub const RuntimeInvisibleParameterAnnotations = struct { - parameter_annotations: []ParameterAnnotation, - }; - - pub const AnnotationDefault = struct { - default_value: ElementValue, - }; - - pub const BootstrapMethods = struct { - bootstrap_methods: []BootstrapMethod, - - pub const BootstrapMethod = struct { - bootstrap_method_ref: u16, - bootstrap_arguments: []u16, + return .{ + .classes = classes, }; + } + + pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + allocator.free(self.classes); + } +}; + +pub const EnclosingMethod = struct { + class_index: u16, + method_index: u16, + + const Self = @This(); + + pub fn parse(input: *std.Io.Reader) ParseError!Self { + return .{ + .class_index = try input.takeInt(u16, .big), + .method_index = try input.takeInt(u16, .big), + }; + } + + pub fn class(self: *const EnclosingMethod, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const class_index = self.class_index - 1; + if (class_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[class_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; + return cp_info.?; + } + + pub fn method(self: *const EnclosingMethod, constant_pool: []const ?ConstantPoolInfo) ResolveError!?ConstantPoolInfo { + if (self.method_index == 0) return null; + const method_index = self.method_index - 1; + if (method_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[method_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType; + return cp_info.?; + } +}; + +pub const Synthetic = struct { }; + +pub const Signature = struct { + signature_index: u16, + + const Self = @This(); + + pub fn parse(input: *std.Io.Reader) ParseError!Self { + return .{ + .signature_index = try input.takeInt(u16, .big), + }; + } + + pub fn signature(self: *const Signature, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const signature_index = self.signature_index - 1; + if (signature_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[signature_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; + } +}; + +pub const SourceFile = struct { + source_file_index: u16, + + const Self = @This(); + + pub fn parse(input: *std.Io.Reader) ParseError!Self { + return .{ + .source_file_index = try input.takeInt(u16, .big), + }; + } + + pub fn sourceFile(self: *const SourceFile, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const source_file_index = self.source_file_index - 1; + if (source_file_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[source_file_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; + } +}; + +pub const SourceDebugExtension = struct { + debug_extension: []u8, + + const Self = @This(); + + pub fn parse(input: *std.Io.Reader, length: usize, allocator: std.mem.Allocator) ParseError!Self { + return .{ + .debug_extension = try input.readAlloc(allocator, length), + }; + } + + pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + allocator.free(self.debug_extension); + } +}; + +pub const LineNumberTable = struct { + line_number_table: []const LineNumber, + + const Self = @This(); + + pub const LineNumber = struct { + start_pc: u16, + line_number: u16, }; - pub fn deinit(self: *Predefined, allocator: std.mem.Allocator) void { + pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { + const line_number_table_length = try input.takeInt(u16, .big); + const line_number_table = try allocator.alloc(LineNumber, line_number_table_length); + errdefer allocator.free(line_number_table); + for (line_number_table) |*line_number| { + line_number.* = .{ + .start_pc = try input.takeInt(u16, .big), + .line_number = try input.takeInt(u16, .big), + }; + } + + return .{ + .line_number_table = line_number_table, + }; + } + + pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + allocator.free(self.line_number_table); + } +}; + +pub const LocalVariableTable = struct { + local_variable_table: []LocalVariable, + + const Self = @This(); + + pub const LocalVariable = struct { + start_pc: u16, + length: u16, + name_index: u16, + descriptor_index: u16, + index: u16, + }; + + pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { + const local_variable_table_length = try input.takeInt(u16, .big); + const local_variable_table = try allocator.alloc(LocalVariable, local_variable_table_length); + errdefer allocator.free(local_variable_table); + for (local_variable_table) |*local_variable| { + local_variable.* = .{ + .start_pc = try input.takeInt(u16, .big), + .length = try input.takeInt(u16, .big), + .name_index = try input.takeInt(u16, .big), + .descriptor_index = try input.takeInt(u16, .big), + .index = try input.takeInt(u16, .big), + }; + } + + return .{ + .local_variable_table = local_variable_table, + }; + } + + pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + allocator.free(self.local_variable_table); + } +}; + +pub const LocalVariableTypeTable = struct { + local_variable_type_table: []LocalVariableType, + + const Self = @This(); + + pub const LocalVariableType = struct { + start_pc: u16, + length: u16, + name_index: u16, + signature_index: u16, + index: u16, + }; + + pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { + const local_variable_type_table_length = try input.takeInt(u16, .big); + const local_variable_type_table = try allocator.alloc(LocalVariableType, local_variable_type_table_length); + errdefer allocator.free(local_variable_type_table); + for (local_variable_type_table) |*local_variable_type| { + local_variable_type.* = .{ + .start_pc = try input.takeInt(u16, .big), + .length = try input.takeInt(u16, .big), + .name_index = try input.takeInt(u16, .big), + .signature_index = try input.takeInt(u16, .big), + .index = try input.takeInt(u16, .big), + }; + } + + return .{ + .local_variable_type_table = local_variable_type_table, + }; + } + + pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + allocator.free(self.local_variable_type_table); + } +}; + +pub const Deprecated = struct { }; + +pub const ElementValue = union(enum) { + // B, C, D, F, I, J, S, Z, s + const_value_index: u16, + // e + enum_const_value: struct { + type_name_index: u16, + const_name_index: u16, + }, + // c + class_info_index: u16, + // @ + annotation_value: Annotation, + // [ + array_value: struct { + values: []ElementValue, + }, + + 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), + }, + 'e' => .{ + .enum_const_value = .{ + .type_name_index = try input.takeInt(u16, .big), + .const_name_index = try input.takeInt(u16, .big), + }, + }, + 'c' => .{ + .class_info_index = try input.takeInt(u16, .big), + }, + '@' => .{ + .annotation_value = try .parse(input, allocator), + }, + '[' => .{ + .array_value = blk: { + const num_values = try input.takeInt(u16, .big); + const values = try allocator.alloc(ElementValue, num_values); + errdefer allocator.free(values); + for (values) |*value| { + value.* = try .parse(input, allocator); + } + + break :blk .{ + .values = values, + }; + }, + }, + else => unreachable, + }; + } + + pub fn deinit(self: *ElementValue, allocator: std.mem.Allocator) void { switch (self.*) { - .code => |attribute| { - allocator.free(attribute.code); - allocator.free(attribute.exception_table); - for (attribute.attributes) |*attr| { - attr.deinit(allocator); + .annotation_value => |*element_value| element_value.deinit(allocator), + .array_value => |element_value| { + for (element_value.values) |*value| { + value.deinit(allocator); } - allocator.free(attribute.attributes); - }, - .stack_map_table => |attribute| { - for (attribute.entries) |*entry| { - entry.deinit(allocator); - } - allocator.free(attribute.entries); - }, - .exceptions => |attribute| allocator.free(attribute.exception_index_table), - .inner_classes => |attribute| allocator.free(attribute.classes), - .source_debug_extension => |attribute| allocator.free(attribute.debug_extension), - .line_number_table => |attribute| allocator.free(attribute.line_number_table), - .local_variable_table => |attribute| allocator.free(attribute.local_variable_table), - .local_variable_type_table => |attribute| allocator.free(attribute.local_variable_type_table), - .runtime_visible_annotations => |attribute| { - for (attribute.annotations) |*annotation| { - annotation.deinit(allocator); - } - allocator.free(attribute.annotations); - }, - .runtime_invisible_annotations => |attribute| { - for (attribute.annotations) |*annotation| { - annotation.deinit(allocator); - } - allocator.free(attribute.annotations); - }, - .runtime_visible_parameter_annotations => |attribute| { - for (attribute.parameter_annotations) |parameter_annotation| { - for (parameter_annotation.annotations) |*annotation| { - annotation.deinit(allocator); - } - allocator.free(parameter_annotation.annotations); - } - allocator.free(attribute.parameter_annotations); - }, - .runtime_invisible_parameter_annotations => |attribute| { - for (attribute.parameter_annotations) |parameter_annotation| { - for (parameter_annotation.annotations) |*annotation| { - annotation.deinit(allocator); - } - allocator.free(parameter_annotation.annotations); - } - allocator.free(attribute.parameter_annotations); - }, - .bootstrap_methods => |attribute| { - for (attribute.bootstrap_methods) |bootstrap_method| { - allocator.free(bootstrap_method.bootstrap_arguments); - } - allocator.free(attribute.bootstrap_methods); + allocator.free(element_value.values); }, else => {}, } } }; -pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!AttributeInfo { - const attribute_name_index = try input.takeInt(u16, .big); - const attribute_length = try input.takeInt(u32, .big); - const info = try input.readAlloc(allocator, attribute_length); - return .{ - .attribute_name_index = attribute_name_index, - .info = info, +pub const Annotation = struct { + type_index: u16, + element_value_pairs: []ElementValuePair, + + pub const ElementValuePair = struct { + element_name_index: u16, + value: ElementValue, }; -} -pub fn deinit(self: *AttributeInfo, allocator: std.mem.Allocator) void { - allocator.free(self.info); -} + pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Annotation { + const type_index = try input.takeInt(u16, .big); -pub fn format(self: *const AttributeInfo, w: *std.Io.Writer, depth: usize, indent: u8, allocator: std.mem.Allocator, constant_pool: []const ?ConstantPoolInfo) std.Io.Writer.Error!void { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("name:\n"); - const attribute_name = self.attributeName(constant_pool) catch return error.WriteFailed; - try attribute_name.format(w, depth, indent + 1, constant_pool); - - var predefined = self.resolve(allocator, constant_pool) catch return error.WriteFailed; - if (predefined) |*p| { - defer p.deinit(allocator); - - try w.splatByteAll(' ', depth * indent); - _ = try w.write("predefined: yes\n"); - - try w.splatByteAll(' ', depth * indent); - try w.print("type: {s}\n", .{ @tagName(p.*) }); - - switch (p.*) { - .constant_value => |attr| { - const constant_value = attr.constantValue(constant_pool) catch return error.WriteFailed; - try constant_value.format(w, depth, indent + 1, constant_pool); - }, - .code => |attr| { - try w.splatByteAll(' ', depth * indent); - try w.print("max_stack: {d}\n", .{ attr.max_stack }); - - try w.splatByteAll(' ', depth * indent); - try w.print("max_locals: {d}\n", .{ attr.max_locals }); - - try w.splatByteAll(' ', depth * indent); - _ = try w.write("code:\n"); - var instr_indent = indent; - for (attr.code, 0..) |instr, i| { - instr_indent += 1; - defer instr_indent -= 1; - - try w.splatByteAll(' ', depth * instr_indent); - try w.print("{d}: 0x{x}\n", .{ i, instr }); - } - - //try w.splatByteAll(' ', depth * indent); - //_ = try w.write("exception_table:\n"); - //var exception_indent = indent; - //for (attr.exception_table, 0..) |*exception, i| { - // exception_indent += 1; - // defer exception_indent -= 1; - - // try w.splatByteAll(' ', depth * exception_indent); - // try w.print("{d}:\n", .{ i }); - - // try self.formatExceptionHandler(w, depth, exception_indent + 1, exception); - //} - - try w.splatByteAll(' ', depth * indent); - _ = try w.write("attributes:\n"); - var attr_indent = indent; - for (attr.attributes, 0..) |*a, i| { - attr_indent += 1; - defer attr_indent -= 1; - - try w.splatByteAll(' ', depth * attr_indent); - try w.print("{d}:\n", .{ i }); - - try a.format(w, depth, attr_indent + 1, allocator, constant_pool); - } - }, - //.stack_map_table, - .exceptions => |attr| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("exceptions:\n"); - var exception_indent = indent; - for (attr.exception_index_table, 0..) |exception, i| { - exception_indent += 1; - defer exception_indent -= 1; - - if (exception >= constant_pool.len) return error.WriteFailed; - const cp_info = constant_pool[exception - 1]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return error.WriteFailed; - - try w.splatByteAll(' ', depth * exception_indent); - try w.print("{d}:\n", .{ i }); - try cp_info.?.format(w, depth, exception_indent + 1, constant_pool); - } - }, - .inner_classes => |attr| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("classes:\n"); - var class_indent = indent; - for (attr.classes, 0..) |*class, i| { - class_indent += 1; - defer class_indent -= 1; - - try w.splatByteAll(' ', depth * class_indent); - try w.print("{d}:\n", .{ i }); - try class.format(w, depth, class_indent + 1, constant_pool); - } - }, - .enclosing_method => |attr| { - const class = attr.class(constant_pool) catch return error.WriteFailed; - try w.splatByteAll(' ', depth * indent); - _ = try w.write("class:\n"); - try class.format(w, depth, indent + 1, constant_pool); - - const method = attr.method(constant_pool) catch return error.WriteFailed; - if (method) |m| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("method:\n"); - try m.format(w, depth, indent + 1, constant_pool); - } - }, - .synthetic => {}, - .signature => |attr| { - const signature = attr.signature(constant_pool) catch return error.WriteFailed; - try signature.format(w, depth, indent + 1, constant_pool); - }, - .source_file => |attr| { - const source_file = attr.sourceFile(constant_pool) catch return error.WriteFailed; - try source_file.format(w, depth, indent + 1, constant_pool); - }, - //.source_debug_extension, - //.line_number_table, - //.local_variable_table, - //.local_variable_type_table, - .deprecated => {}, - //.runtime_visible_annotations, - //.runtime_invisible_annotations, - //.runtime_visible_parameter_annotations, - //.runtime_invisible_parameter_annotations, - //.annotation_default, - //.bootstrap_methods, - else => {}, + 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), + }; } - } else { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("predefined: no\n"); + + return .{ + .type_index = type_index, + .element_value_pairs = element_value_pairs, + }; } -} -pub fn attributeName(self: *const AttributeInfo, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { - const attribute_name_index = self.attribute_name_index - 1; - if (attribute_name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[attribute_name_index]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; -} - -pub fn resolve(self: *const AttributeInfo, allocator: std.mem.Allocator, constant_pool: []const ?ConstantPoolInfo) (ResolveError || ParseError)!?Predefined { - const attribute_name = (try self.attributeName(constant_pool)).utf8; - - var r: std.Io.Reader = .fixed(self.info); - - const eql = std.mem.eql; - if (eql(u8, attribute_name.bytes, "ConstantValue")) { - return .{ - .constant_value = .{ - .constant_value_index = try r.takeInt(u16, .big), - }, - }; - } else if (eql(u8, attribute_name.bytes, "Code")) { - const max_stack = try r.takeInt(u16, .big); - const max_locals = try r.takeInt(u16, .big); - - const code_length = try r.takeInt(u32, .big); - const code = try r.readAlloc(allocator, code_length); - errdefer allocator.free(code); - - const exception_table_length = try r.takeInt(u16, .big); - const exception_table = try allocator.alloc(Predefined.Code.ExceptionHandler, exception_table_length); - errdefer allocator.free(exception_table); - for (exception_table) |*exception_handler| { - exception_handler.* = .{ - .start_pc = try r.takeInt(u16, .big), - .end_pc = try r.takeInt(u16, .big), - .handler_pc = try r.takeInt(u16, .big), - .catch_type = try r.takeInt(u16, .big), - }; + pub fn deinit(self: *Annotation, allocator: std.mem.Allocator) void { + for (self.element_value_pairs) |*element_value_pair| { + element_value_pair.value.deinit(allocator); } + allocator.free(self.element_value_pairs); + } +}; - const attribute_count = try r.takeInt(u16, .big); - const attributes = try allocator.alloc(AttributeInfo, attribute_count); - errdefer allocator.free(attributes); - for (attributes) |*attribute| { - attribute.* = try .parse(&r, allocator); - } +pub const RuntimeVisibleAnnotations = struct { + annotations: []Annotation, - return .{ - .code = .{ - .max_stack = max_stack, - .max_locals = max_locals, - .code = code, - .exception_table = exception_table, - .attributes = attributes, - }, - }; - } else if (eql(u8, attribute_name.bytes, "StackMapTable")) { - const number_of_entries = try r.takeInt(u16, .big); - const entries = try allocator.alloc(Predefined.StackMapTable.StackMapFrame, number_of_entries); - errdefer allocator.free(entries); - for (entries) |*entry| { - entry.* = try .parse(&r, allocator); - } + const Self = @This(); - return .{ - .stack_map_table = .{ - .entries = entries, - }, - }; - } else if (eql(u8, attribute_name.bytes, "Exceptions")) { - const number_of_exceptions = try r.takeInt(u16, .big); - const exception_index_table = try allocator.alloc(u16, number_of_exceptions); - errdefer allocator.free(exception_index_table); - for (exception_index_table) |*exception_index| { - exception_index.* = try r.takeInt(u16, .big); - } - - return .{ - .exceptions = .{ - .exception_index_table = exception_index_table, - }, - }; - } else if (eql(u8, attribute_name.bytes, "InnerClasses")) { - const number_of_classes = try r.takeInt(u16, .big); - const classes = try allocator.alloc(Predefined.InnerClasses.InnerClass, number_of_classes); - errdefer allocator.free(classes); - for (classes) |*class| { - class.* = .{ - .inner_class_info_index = try r.takeInt(u16, .big), - .outer_class_info_index = try r.takeInt(u16, .big), - .inner_name_index = try r.takeInt(u16, .big), - .inner_class_access_flags = .{ .mask = try r.takeInt(u16, .big) }, - }; - } - - return .{ - .inner_classes = .{ - .classes = classes, - }, - }; - } else if (eql(u8, attribute_name.bytes, "EnclosingMethod")) { - return .{ - .enclosing_method = .{ - .class_index = try r.takeInt(u16, .big), - .method_index = try r.takeInt(u16, .big), - }, - }; - } else if (eql(u8, attribute_name.bytes, "Synthetic")) { - return .{ - .synthetic = .{}, - }; - } else if (eql(u8, attribute_name.bytes, "Signature")) { - return .{ - .signature = .{ - .signature_index = try r.takeInt(u16, .big), - }, - }; - } else if (eql(u8, attribute_name.bytes, "SourceFile")) { - return .{ - .source_file = .{ - .source_file_index = try r.takeInt(u16, .big), - }, - }; - } else if (eql(u8, attribute_name.bytes, "SourceDebugExtension")) { - return .{ - .source_debug_extension = .{ - .debug_extension = try allocator.dupe(u8, self.info), - }, - }; - } else if (eql(u8, attribute_name.bytes, "LineNumberTable")) { - const line_number_table_length = try r.takeInt(u16, .big); - const line_number_table = try allocator.alloc(Predefined.LineNumberTable.LineNumber, line_number_table_length); - errdefer allocator.free(line_number_table); - for (line_number_table) |*line_number| { - line_number.* = .{ - .start_pc = try r.takeInt(u16, .big), - .line_number = try r.takeInt(u16, .big), - }; - } - - return .{ - .line_number_table = .{ - .line_number_table = line_number_table, - }, - }; - } else if (eql(u8, attribute_name.bytes, "LocalVariableTable")) { - const local_variable_table_length = try r.takeInt(u16, .big); - const local_variable_table = try allocator.alloc(Predefined.LocalVariableTable.LocalVariable, local_variable_table_length); - errdefer allocator.free(local_variable_table); - for (local_variable_table) |*local_variable| { - local_variable.* = .{ - .start_pc = try r.takeInt(u16, .big), - .length = try r.takeInt(u16, .big), - .name_index = try r.takeInt(u16, .big), - .descriptor_index = try r.takeInt(u16, .big), - .index = try r.takeInt(u16, .big), - }; - } - - return .{ - .local_variable_table = .{ - .local_variable_table = local_variable_table, - }, - }; - } else if (eql(u8, attribute_name.bytes, "LocalVariableTypeTable")) { - const local_variable_type_table_length = try r.takeInt(u16, .big); - const local_variable_type_table = try allocator.alloc(Predefined.LocalVariableTypeTable.LocalVariableType, local_variable_type_table_length); - errdefer allocator.free(local_variable_type_table); - for (local_variable_type_table) |*local_variable_type| { - local_variable_type.* = .{ - .start_pc = try r.takeInt(u16, .big), - .length = try r.takeInt(u16, .big), - .name_index = try r.takeInt(u16, .big), - .signature_index = try r.takeInt(u16, .big), - .index = try r.takeInt(u16, .big), - }; - } - - return .{ - .local_variable_type_table = .{ - .local_variable_type_table = local_variable_type_table, - }, - }; - } else if (eql(u8, attribute_name.bytes, "Deprecated")) { - return .{ - .deprecated = .{}, - }; - } else if (eql(u8, attribute_name.bytes, "RuntimeVisibleAnnotations")) { - const num_annotations = try r.takeInt(u16, .big); - const annotations = try allocator.alloc(Predefined.Annotation, num_annotations); + pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { + const num_annotations = try input.takeInt(u16, .big); + const annotations = try allocator.alloc(Annotation, num_annotations); errdefer allocator.free(annotations); for (annotations) |*annotation| { - annotation.* = try .parse(&r, allocator); + annotation.* = try .parse(input, allocator); } return .{ - .runtime_visible_annotations = .{ - .annotations = annotations, - }, + .annotations = annotations, }; - } else if (eql(u8, attribute_name.bytes, "RuntimeInvisibleAnnotations")) { - const num_annotations = try r.takeInt(u16, .big); - const annotations = try allocator.alloc(Predefined.Annotation, num_annotations); + } + + pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + for (self.annotations) |*annotation| { + annotation.deinit(allocator); + } + allocator.free(self.annotations); + } +}; + +pub const RuntimeInvisibleAnnotations = struct { + annotations: []Annotation, + + 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 annotations = try allocator.alloc(Annotation, num_annotations); errdefer allocator.free(annotations); for (annotations) |*annotation| { - annotation.* = try .parse(&r, allocator); + annotation.* = try .parse(input, allocator); } return .{ - .runtime_invisible_annotations = .{ - .annotations = annotations, - }, + .annotations = annotations, }; - } else if (eql(u8, attribute_name.bytes, "RuntimeVisibleParameterAnnotations")) { - const num_parameters = try r.takeByte(); - const parameter_annotations = try allocator.alloc(Predefined.ParameterAnnotation, num_parameters); + } + + pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + for (self.annotations) |*annotation| { + annotation.deinit(allocator); + } + allocator.free(self.annotations); + } +}; + +pub const ParameterAnnotation = struct { + annotations: []Annotation, +}; + +pub const RuntimeVisibleParameterAnnotations = struct { + parameter_annotations: []ParameterAnnotation, + + const Self = @This(); + + pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { + const num_parameters = try input.takeByte(); + const parameter_annotations = try allocator.alloc(ParameterAnnotation, num_parameters); errdefer allocator.free(parameter_annotations); for (parameter_annotations) |*parameter_annotation| { - const num_annotations = try r.takeInt(u16, .big); - const annotations = try allocator.alloc(Predefined.Annotation, num_annotations); + const num_annotations = try input.takeInt(u16, .big); + const annotations = try allocator.alloc(Annotation, num_annotations); errdefer allocator.free(annotations); for (annotations) |*annotation| { - annotation.* = try .parse(&r, allocator); + annotation.* = try .parse(input, allocator); } parameter_annotation.* = .{ @@ -980,20 +1144,36 @@ pub fn resolve(self: *const AttributeInfo, allocator: std.mem.Allocator, constan } return .{ - .runtime_visible_parameter_annotations = .{ - .parameter_annotations = parameter_annotations, - }, + .parameter_annotations = parameter_annotations, }; - } else if (eql(u8, attribute_name.bytes, "RuntimeInvisibleParameterAnnotations")) { - const num_parameters = try r.takeByte(); - const parameter_annotations = try allocator.alloc(Predefined.ParameterAnnotation, num_parameters); + } + + pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + for (self.parameter_annotations) |parameter_annotation| { + for (parameter_annotation.annotations) |*annotation| { + annotation.deinit(allocator); + } + allocator.free(parameter_annotation.annotations); + } + allocator.free(self.parameter_annotations); + } +}; + +pub const RuntimeInvisibleParameterAnnotations = struct { + parameter_annotations: []ParameterAnnotation, + + const Self = @This(); + + pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { + const num_parameters = try input.takeByte(); + const parameter_annotations = try allocator.alloc(ParameterAnnotation, num_parameters); errdefer allocator.free(parameter_annotations); for (parameter_annotations) |*parameter_annotation| { - const num_annotations = try r.takeInt(u16, .big); - const annotations = try allocator.alloc(Predefined.Annotation, num_annotations); + const num_annotations = try input.takeInt(u16, .big); + const annotations = try allocator.alloc(Annotation, num_annotations); errdefer allocator.free(annotations); for (annotations) |*annotation| { - annotation.* = try .parse(&r, allocator); + annotation.* = try .parse(input, allocator); } parameter_annotation.* = .{ @@ -1002,24 +1182,59 @@ pub fn resolve(self: *const AttributeInfo, allocator: std.mem.Allocator, constan } return .{ - .runtime_invisible_parameter_annotations = .{ - .parameter_annotations = parameter_annotations, - }, + .parameter_annotations = parameter_annotations, }; - } else if (eql(u8, attribute_name.bytes, "AnnotationDefault")) { - unreachable; - } else if (eql(u8, attribute_name.bytes, "BootstrapMethods")) { - const num_bootstrap_methods = try r.takeInt(u16, .big); - const bootstrap_methods = try allocator.alloc(Predefined.BootstrapMethods.BootstrapMethod, num_bootstrap_methods); + } + + pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + for (self.parameter_annotations) |parameter_annotation| { + for (parameter_annotation.annotations) |*annotation| { + annotation.deinit(allocator); + } + allocator.free(parameter_annotation.annotations); + } + allocator.free(self.parameter_annotations); + } +}; + +pub const AnnotationDefault = struct { + default_value: ElementValue, + + const Self = @This(); + + pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { + return .{ + .default_value = try .parse(input, allocator), + }; + } + + pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + self.default_value.deinit(allocator); + } +}; + +pub const BootstrapMethods = struct { + bootstrap_methods: []BootstrapMethod, + + const Self = @This(); + + pub const BootstrapMethod = struct { + bootstrap_method_ref: u16, + bootstrap_arguments: []u16, + }; + + pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { + const num_bootstrap_methods = try input.takeInt(u16, .big); + const bootstrap_methods = try allocator.alloc(BootstrapMethod, num_bootstrap_methods); errdefer allocator.free(bootstrap_methods); for (bootstrap_methods) |*bootstrap_method| { - const bootstrap_method_ref = try r.takeInt(u16, .big); + const bootstrap_method_ref = try input.takeInt(u16, .big); - const num_bootstrap_arguments = try r.takeInt(u16, .big); + const num_bootstrap_arguments = try input.takeInt(u16, .big); const bootstrap_arguments = try allocator.alloc(u16, num_bootstrap_arguments); errdefer allocator.free(bootstrap_arguments); for (bootstrap_arguments) |*bootstrap_argument| { - bootstrap_argument.* = try r.takeInt(u16, .big); + bootstrap_argument.* = try input.takeInt(u16, .big); } bootstrap_method.* = .{ @@ -1029,12 +1244,14 @@ pub fn resolve(self: *const AttributeInfo, allocator: std.mem.Allocator, constan } return .{ - .bootstrap_methods = .{ - .bootstrap_methods = bootstrap_methods, - }, + .bootstrap_methods = bootstrap_methods, }; - } else { - return null; } -} + pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + for (self.bootstrap_methods) |bootstrap_method| { + allocator.free(bootstrap_method.bootstrap_arguments); + } + allocator.free(self.bootstrap_methods); + } +}; diff --git a/src/Class/FieldInfo.zig b/src/Class/FieldInfo.zig index 565efb0..2d80676 100644 --- a/src/Class/FieldInfo.zig +++ b/src/Class/FieldInfo.zig @@ -28,11 +28,11 @@ pub const FieldFlags = enum(u16) { pub const FieldAccessFlags = EnumFlags(FieldFlags); -pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) !Self { +pub fn parse(input: *std.Io.Reader, constant_pool: []const ?ConstantPoolInfo, allocator: std.mem.Allocator) !Self { const access_flags: FieldAccessFlags = .{ .mask = try input.takeInt(u16, .big) }; const name_index = try input.takeInt(u16, .big); const descriptor_index = try input.takeInt(u16, .big); - const attributes = try Class.parseAttributes(input, allocator); + const attributes = try Class.parseAttributes(input, constant_pool, allocator); return .{ .access_flags = access_flags, .name_index = name_index, diff --git a/src/Class/MethodInfo.zig b/src/Class/MethodInfo.zig index 0699c46..4380e21 100644 --- a/src/Class/MethodInfo.zig +++ b/src/Class/MethodInfo.zig @@ -31,11 +31,11 @@ pub const MethodFlags = enum(u16) { pub const MethodAccessFlags = EnumFlags(MethodFlags); -pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) !Self { +pub fn parse(input: *std.Io.Reader, constant_pool: []const ?ConstantPoolInfo, allocator: std.mem.Allocator) !Self { const access_flags: MethodAccessFlags = .{ .mask = try input.takeInt(u16, .big) }; const name_index = try input.takeInt(u16, .big); const descriptor_index = try input.takeInt(u16, .big); - const attributes = try Class.parseAttributes(input, allocator); + const attributes = try Class.parseAttributes(input, constant_pool, allocator); return .{ .access_flags = access_flags, .name_index = name_index,