From a18ae9c26505a08367fd9c00aeaa8d99cdf5af0d Mon Sep 17 00:00:00 2001 From: ktkk Date: Fri, 3 Jul 2026 23:59:28 +0200 Subject: [PATCH 01/31] 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, From 07d43f2fe7e88eb9fab2ac26bfdce0bb41449797 Mon Sep 17 00:00:00 2001 From: ktkk Date: Sat, 4 Jul 2026 01:43:11 +0200 Subject: [PATCH 02/31] Dump constant pool in debug mode --- src/Class.zig | 59 ++++++++++++++++++++++++------------- src/Class/AttributeInfo.zig | 34 ++++++++++++--------- src/Class/FieldInfo.zig | 8 ++--- src/Class/MethodInfo.zig | 8 ++--- src/main.zig | 10 +++++++ 5 files changed, 77 insertions(+), 42 deletions(-) diff --git a/src/Class.zig b/src/Class.zig index 1eafdd6..787f7c2 100644 --- a/src/Class.zig +++ b/src/Class.zig @@ -246,11 +246,11 @@ pub fn format(self: *const Self, w: *std.Io.Writer) std.Io.Writer.Error!void { const this_class = self.thisClass() catch return error.WriteFailed; _ = try w.write("this class:\n"); - try this_class.format(w, depth, indent + 1, self.constant_pool); + try this_class.indentedFormat(w, depth, indent + 1, self.constant_pool); _ = try w.write("super class:\n"); if (self.superClass() catch return error.WriteFailed) |super_class| { - try super_class.format(w, depth, indent + 1, self.constant_pool); + try super_class.indentedFormat(w, depth, indent + 1, self.constant_pool); } _ = try w.write("interfaces:\n"); @@ -264,7 +264,7 @@ pub fn format(self: *const Self, w: *std.Io.Writer) std.Io.Writer.Error!void { try w.splatByteAll(' ', depth * indent); try w.print("{d}:\n", .{ i }); - try cp_info.?.format(w, depth, indent + 1, self.constant_pool); + try cp_info.?.indentedFormat(w, depth, indent + 1, self.constant_pool); } _ = try w.write("fields:\n"); @@ -274,7 +274,7 @@ pub fn format(self: *const Self, w: *std.Io.Writer) std.Io.Writer.Error!void { try w.splatByteAll(' ', depth * indent); try w.print("{d}:\n", .{ i }); - try field.format(w, depth, indent + 1, self.allocator, self.constant_pool); + try field.indentedFormat(w, depth, indent + 1, self.allocator, self.constant_pool); } _ = try w.write("methods:\n"); @@ -284,7 +284,7 @@ pub fn format(self: *const Self, w: *std.Io.Writer) std.Io.Writer.Error!void { try w.splatByteAll(' ', depth * indent); try w.print("{d}:\n", .{ i }); - try method.format(w, depth, indent + 1, self.allocator, self.constant_pool); + try method.indentedFormat(w, depth, indent + 1, self.allocator, self.constant_pool); } _ = try w.write("attributes:\n"); @@ -294,7 +294,7 @@ pub fn format(self: *const Self, w: *std.Io.Writer) std.Io.Writer.Error!void { try w.splatByteAll(' ', depth * indent); try w.print("{d}:\n", .{ i }); - try attribute.format(w, depth, indent + 1, self.allocator, self.constant_pool); + try attribute.indentedFormat(w, depth, indent + 1, self.allocator, self.constant_pool); } } @@ -656,7 +656,26 @@ pub const ConstantPoolInfo = union(ConstantPoolInfo.Tag) { } } - pub fn format(self: *const ConstantPoolInfo, w: *std.Io.Writer, depth: usize, indent: u8, constant_pool: []const ?ConstantPoolInfo) std.Io.Writer.Error!void { + pub fn format(self: *const ConstantPoolInfo, w: *std.Io.Writer) std.Io.Writer.Error!void { + switch (self.*) { + .class => |class| try w.print("#{d}", .{ class.name_index }), + .field_ref => |field_ref| try w.print("#{d}.#{d}", .{ field_ref.class_index, field_ref.name_and_type_index }), + .method_ref => |method_ref| try w.print("#{d}.#{d}", .{ method_ref.class_index, method_ref.name_and_type_index }), + .interface_method_ref => |interface_method_ref| try w.print("#{d}.#{d}", .{ interface_method_ref.class_index, interface_method_ref.name_and_type_index }), + .string => |string| try w.print("#{d}", .{ string.string_index }), + .integer => |integer| try w.print("{d}", .{ integer.bytes }), + .float => |float| try w.print("{d}", .{ @as(f32, @bitCast(float.bytes)) }), + .long => |long| try w.print("{d}", .{ (@as(u64, long.high_bytes) << 32) | long.low_bytes }), + .double => |double| try w.print("{d}", .{ @as(f64, @bitCast((@as(u64, double.high_bytes) << 32) | double.low_bytes)) }), + .name_and_type => |name_and_type| try w.print("#{d}:#{d}", .{ name_and_type.name_index, name_and_type.descriptor_index }), + .utf8 => |utf8| try w.print("\"{s}\"", .{ utf8.bytes }), + .method_handle => |method_handle| try w.print("{s} #{d}", .{ @tagName(method_handle.reference_kind), method_handle.reference_index }), + .method_type => |method_type| try w.print("#{d}", .{ method_type.descriptor_index }), + .invoke_dynamic => |invoke_dynamic| try w.print("#{d} #{d}", .{ invoke_dynamic.bootstrap_method_attr_index, invoke_dynamic.name_and_type_index }), + } + } + + pub fn indentedFormat(self: *const ConstantPoolInfo, 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("constant_value_type: {s}\n", .{ @tagName(self.*) }); @@ -665,44 +684,44 @@ pub const ConstantPoolInfo = union(ConstantPoolInfo.Tag) { try w.splatByteAll(' ', depth * indent); _ = try w.write("name:\n"); const class_name = class.name(constant_pool) catch return error.WriteFailed; - try class_name.format(w, depth, indent + 1, constant_pool); + try class_name.indentedFormat(w, depth, indent + 1, constant_pool); }, .field_ref => |field_ref| { try w.splatByteAll(' ', depth * indent); _ = try w.write("class:\n"); const class = field_ref.class(constant_pool) catch return error.WriteFailed; - try class.format(w, depth, indent + 1, constant_pool); + try class.indentedFormat(w, depth, indent + 1, constant_pool); try w.splatByteAll(' ', depth * indent); _ = try w.write("name_and_type:\n"); const name_and_type = field_ref.nameAndType(constant_pool) catch return error.WriteFailed; - try name_and_type.format(w, depth, indent + 1, constant_pool); + try name_and_type.indentedFormat(w, depth, indent + 1, constant_pool); }, .method_ref => |method_ref| { try w.splatByteAll(' ', depth * indent); _ = try w.write("class:\n"); const class = method_ref.class(constant_pool) catch return error.WriteFailed; - try class.format(w, depth, indent + 1, constant_pool); + try class.indentedFormat(w, depth, indent + 1, constant_pool); try w.splatByteAll(' ', depth * indent); _ = try w.write("name_and_type:\n"); const name_and_type = method_ref.nameAndType(constant_pool) catch return error.WriteFailed; - try name_and_type.format(w, depth, indent + 1, constant_pool); + try name_and_type.indentedFormat(w, depth, indent + 1, constant_pool); }, .interface_method_ref => |interface_method_ref| { try w.splatByteAll(' ', depth * indent); _ = try w.write("class:\n"); const class = interface_method_ref.class(constant_pool) catch return error.WriteFailed; - try class.format(w, depth, indent + 1, constant_pool); + try class.indentedFormat(w, depth, indent + 1, constant_pool); try w.splatByteAll(' ', depth * indent); _ = try w.write("name_and_type:\n"); const name_and_type = interface_method_ref.nameAndType(constant_pool) catch return error.WriteFailed; - try name_and_type.format(w, depth, indent + 1, constant_pool); + try name_and_type.indentedFormat(w, depth, indent + 1, constant_pool); }, .string => |string| { const s = string.string(constant_pool) catch return error.WriteFailed; - try s.format(w, depth, indent + 1, constant_pool); + try s.indentedFormat(w, depth, indent + 1, constant_pool); }, .integer => |integer| { try w.splatByteAll(' ', depth * indent); @@ -731,12 +750,12 @@ pub const ConstantPoolInfo = union(ConstantPoolInfo.Tag) { try w.splatByteAll(' ', depth * indent); _ = try w.write("name:\n"); const name = name_and_type.name(constant_pool) catch return error.WriteFailed; - try name.format(w, depth, indent + 1, constant_pool); + try name.indentedFormat(w, depth, indent + 1, constant_pool); try w.splatByteAll(' ', depth * indent); _ = try w.write("descriptor:\n"); const descriptor = name_and_type.descriptor(constant_pool) catch return error.WriteFailed; - try descriptor.format(w, depth, indent + 1, constant_pool); + try descriptor.indentedFormat(w, depth, indent + 1, constant_pool); }, .utf8 => |utf8| { try w.splatByteAll(' ', depth * indent); @@ -749,19 +768,19 @@ pub const ConstantPoolInfo = union(ConstantPoolInfo.Tag) { try w.splatByteAll(' ', depth * indent); _ = try w.write("reference:\n"); const reference = method_handle.reference(constant_pool) catch return error.WriteFailed; - try reference.format(w, depth, indent + 1, constant_pool); + try reference.indentedFormat(w, depth, indent + 1, constant_pool); }, .method_type => |method_type| { try w.splatByteAll(' ', depth * indent); _ = try w.write("descriptor:\n"); const descriptor = method_type.descriptor(constant_pool) catch return error.WriteFailed; - try descriptor.format(w, depth, indent + 1, constant_pool); + try descriptor.indentedFormat(w, depth, indent + 1, constant_pool); }, .invoke_dynamic => |invoke_dynamic| { try w.splatByteAll(' ', depth * indent); _ = try w.write("name_and_type:\n"); const name_and_type = invoke_dynamic.nameAndType(constant_pool) catch return error.WriteFailed; - try name_and_type.format(w, depth, indent + 1, constant_pool); + try name_and_type.indentedFormat(w, depth, indent + 1, constant_pool); }, } } diff --git a/src/Class/AttributeInfo.zig b/src/Class/AttributeInfo.zig index 44edf19..aa6fce6 100644 --- a/src/Class/AttributeInfo.zig +++ b/src/Class/AttributeInfo.zig @@ -197,7 +197,7 @@ pub const AttributeInfo = union(enum) { } } - pub fn format( + pub fn indentedFormat( self: *const Self, w: *std.Io.Writer, depth: usize, @@ -213,7 +213,7 @@ pub const AttributeInfo = union(enum) { 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); + try attribute_name.indentedFormat(w, depth, indent + 1, constant_pool); }, .predefined => |predefined| { try w.splatByteAll(' ', depth * indent); @@ -225,7 +225,7 @@ pub const AttributeInfo = union(enum) { 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); + try constant_value.indentedFormat(w, depth, indent + 1, constant_pool); }, .code => |attr| { try w.splatByteAll(' ', depth * indent); @@ -268,7 +268,7 @@ pub const AttributeInfo = union(enum) { try w.splatByteAll(' ', depth * attr_indent); try w.print("{d}:\n", .{ i }); - try a.format(w, depth, attr_indent + 1, allocator, constant_pool); + try a.indentedFormat(w, depth, attr_indent + 1, allocator, constant_pool); } }, //.stack_map_table, @@ -286,7 +286,7 @@ pub const AttributeInfo = union(enum) { try w.splatByteAll(' ', depth * exception_indent); try w.print("{d}:\n", .{ i }); - try cp_info.?.format(w, depth, exception_indent + 1, constant_pool); + try cp_info.?.indentedFormat(w, depth, exception_indent + 1, constant_pool); } }, .inner_classes => |attr| { @@ -299,30 +299,30 @@ pub const AttributeInfo = union(enum) { try w.splatByteAll(' ', depth * class_indent); try w.print("{d}:\n", .{ i }); - try class.format(w, depth, class_indent + 1, constant_pool); + try class.indentedFormat(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); + try class.indentedFormat(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); + try m.indentedFormat(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); + try signature.indentedFormat(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); + try source_file.indentedFormat(w, depth, indent + 1, constant_pool); }, //.source_debug_extension, //.line_number_table, @@ -675,27 +675,33 @@ pub const InnerClasses = struct { 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 { + pub fn indentedFormat( + 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); + try inner_class.indentedFormat(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); + try oc.indentedFormat(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); + try in.indentedFormat(w, depth, indent + 1, constant_pool); } } diff --git a/src/Class/FieldInfo.zig b/src/Class/FieldInfo.zig index 2d80676..ed3cb61 100644 --- a/src/Class/FieldInfo.zig +++ b/src/Class/FieldInfo.zig @@ -48,7 +48,7 @@ pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { allocator.free(self.attributes); } -pub fn format( +pub fn indentedFormat( self: *const Self, w: *std.Io.Writer, depth: usize, @@ -62,12 +62,12 @@ pub fn format( try w.splatByteAll(' ', depth * indent); _ = try w.write("name:\n"); const field_name = self.name(constant_pool) catch return error.WriteFailed; - try field_name.format(w, depth, indent + 1, constant_pool); + try field_name.indentedFormat(w, depth, indent + 1, constant_pool); try w.splatByteAll(' ', depth * indent); _ = try w.write("descriptor:\n"); const field_descriptor = self.descriptor(constant_pool) catch return error.WriteFailed; - try field_descriptor.format(w, depth, indent + 1, constant_pool); + try field_descriptor.indentedFormat(w, depth, indent + 1, constant_pool); try w.splatByteAll(' ', depth * indent); _ = try w.write("attributes:\n"); @@ -79,7 +79,7 @@ pub fn format( try w.splatByteAll(' ', depth * attr_indent); try w.print("{d}:\n", .{ j }); - try attribute.format(w, depth, attr_indent + 1, allocator, constant_pool); + try attribute.indentedFormat(w, depth, attr_indent + 1, allocator, constant_pool); } } diff --git a/src/Class/MethodInfo.zig b/src/Class/MethodInfo.zig index 4380e21..7a457ee 100644 --- a/src/Class/MethodInfo.zig +++ b/src/Class/MethodInfo.zig @@ -51,7 +51,7 @@ pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { allocator.free(self.attributes); } -pub fn format( +pub fn indentedFormat( self: *const Self, w: *std.Io.Writer, depth: usize, @@ -65,12 +65,12 @@ pub fn format( try w.splatByteAll(' ', depth * indent); _ = try w.write("name:\n"); const method_name = self.name(constant_pool) catch return error.WriteFailed; - try method_name.format(w, depth, indent + 1, constant_pool); + try method_name.indentedFormat(w, depth, indent + 1, constant_pool); try w.splatByteAll(' ', depth * indent); _ = try w.write("descriptor:\n"); const method_descriptor = self.descriptor(constant_pool) catch return error.WriteFailed; - try method_descriptor.format(w, depth, indent + 1, constant_pool); + try method_descriptor.indentedFormat(w, depth, indent + 1, constant_pool); try w.splatByteAll(' ', depth * indent); _ = try w.write("attributes:\n"); @@ -82,7 +82,7 @@ pub fn format( try w.splatByteAll(' ', depth * attr_indent); try w.print("{d}:\n", .{ j }); - try attribute.format(w, depth, attr_indent + 1, allocator, constant_pool); + try attribute.indentedFormat(w, depth, attr_indent + 1, allocator, constant_pool); } } diff --git a/src/main.zig b/src/main.zig index 2dde461..4ea41ec 100644 --- a/src/main.zig +++ b/src/main.zig @@ -52,6 +52,16 @@ pub fn main(init: std.process.Init) !void { }; defer class.deinit(); + if (@import("builtin").mode == .Debug) { + for (class.constant_pool, 1..) |cp_info, i| { + if (cp_info) |info| { + std.debug.print("#{d} = {s}\t{f}\n", .{ i, @tagName(info), info }); + } else { + std.debug.print("#{d} = null\n", .{ i }); + } + } + } + try stdout.print("{f}", .{ class }); try stdout.flush(); From 75d023cfe12e3fb705516d7bf9510d51a5938e9e Mon Sep 17 00:00:00 2001 From: ktkk Date: Sat, 4 Jul 2026 01:43:29 +0200 Subject: [PATCH 03/31] Fix new attribute name index --- src/Class/AttributeInfo.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Class/AttributeInfo.zig b/src/Class/AttributeInfo.zig index aa6fce6..c5eb83c 100644 --- a/src/Class/AttributeInfo.zig +++ b/src/Class/AttributeInfo.zig @@ -169,7 +169,7 @@ pub const AttributeInfo = union(enum) { }; } else { return .{ - .new = try NewAttribute.parse(limited, attribute_name_index, attribute_length, allocator), + .new = try NewAttribute.parse(limited, attribute_name_index + 1, attribute_length, allocator), }; } } From 9af766e495de9021a5972e5c6d45bb40d292088e Mon Sep 17 00:00:00 2001 From: ktkk Date: Sat, 4 Jul 2026 18:10:33 +0200 Subject: [PATCH 04/31] Add additional predefined attribute infos --- src/Class.zig | 8 +- src/Class/AttributeInfo.zig | 320 ++++++++++++++++++++++++++++++++++-- src/main.zig | 1 - 3 files changed, 317 insertions(+), 12 deletions(-) diff --git a/src/Class.zig b/src/Class.zig index 787f7c2..1d96a19 100644 --- a/src/Class.zig +++ b/src/Class.zig @@ -675,7 +675,13 @@ pub const ConstantPoolInfo = union(ConstantPoolInfo.Tag) { } } - pub fn indentedFormat(self: *const ConstantPoolInfo, w: *std.Io.Writer, depth: usize, indent: u8, constant_pool: []const ?ConstantPoolInfo) std.Io.Writer.Error!void { + pub fn indentedFormat( + self: *const ConstantPoolInfo, + 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("constant_value_type: {s}\n", .{ @tagName(self.*) }); diff --git a/src/Class/AttributeInfo.zig b/src/Class/AttributeInfo.zig index c5eb83c..4b17b67 100644 --- a/src/Class/AttributeInfo.zig +++ b/src/Class/AttributeInfo.zig @@ -28,8 +28,18 @@ 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, annotation_default: AnnotationDefault, bootstrap_methods: BootstrapMethods, + method_parameters: MethodParameters, + // module: Module, + module_packages: ModulePackages, + module_main_class: ModuleMainClass, + nest_host: NestHost, + nest_members: NestMembers, + record: Record, + permitted_subclasses: PermittedSubclasses, }, const Self = @This(); @@ -167,6 +177,48 @@ pub const AttributeInfo = union(enum) { .bootstrap_methods = try BootstrapMethods.parse(limited, allocator), }, }; + } else if (std.mem.eql(u8, attribute_name.bytes, "MethodParameters")) { + return .{ + .predefined = .{ + .method_parameters = try MethodParameters.parse(limited, allocator), + }, + }; + } else if (std.mem.eql(u8, attribute_name.bytes, "ModulePackages")) { + return .{ + .predefined = .{ + .module_packages = try ModulePackages.parse(limited, allocator), + }, + }; + } else if (std.mem.eql(u8, attribute_name.bytes, "ModuleMainClass")) { + return .{ + .predefined = .{ + .module_main_class = try ModuleMainClass.parse(limited), + }, + }; + } else if (std.mem.eql(u8, attribute_name.bytes, "NestHost")) { + return .{ + .predefined = .{ + .nest_host = try NestHost.parse(limited), + }, + }; + } else if (std.mem.eql(u8, attribute_name.bytes, "NestMembers")) { + return .{ + .predefined = .{ + .nest_members = try NestMembers.parse(limited, allocator), + }, + }; + } else if (std.mem.eql(u8, attribute_name.bytes, "Record")) { + return .{ + .predefined = .{ + .record = try Record.parse(limited, constant_pool, allocator), + }, + }; + } else if (std.mem.eql(u8, attribute_name.bytes, "PermittedSubclasses")) { + return .{ + .predefined = .{ + .permitted_subclasses = try PermittedSubclasses.parse(limited, allocator), + }, + }; } else { return .{ .new = try NewAttribute.parse(limited, attribute_name_index + 1, attribute_length, allocator), @@ -192,6 +244,11 @@ pub const AttributeInfo = union(enum) { .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), + .method_parameters => |*method_parameters| method_parameters.deinit(allocator), + .module_packages => |*module_packages| module_packages.deinit(allocator), + .nest_members => |*nest_members| nest_members.deinit(allocator), + .record => |*record| record.deinit(allocator), + .permitted_subclasses => |*permitted_subclasses| permitted_subclasses.deinit(allocator), else => {}, }, } @@ -318,10 +375,14 @@ pub const AttributeInfo = union(enum) { .synthetic => {}, .signature => |attr| { const signature = attr.signature(constant_pool) catch return error.WriteFailed; + try w.splatByteAll(' ', depth * indent); + _ = try w.write("signature:\n"); try signature.indentedFormat(w, depth, indent + 1, constant_pool); }, .source_file => |attr| { const source_file = attr.sourceFile(constant_pool) catch return error.WriteFailed; + try w.splatByteAll(' ', depth * indent); + _ = try w.write("source_file:\n"); try source_file.indentedFormat(w, depth, indent + 1, constant_pool); }, //.source_debug_extension, @@ -335,6 +396,14 @@ pub const AttributeInfo = union(enum) { //.runtime_invisible_parameter_annotations, //.annotation_default, //.bootstrap_methods, + //.method_parameters, + //.module, + //.module_packages, + //.module_main_class, + //.nest_host, + //.nest_members, + //.record, + //.permitted_subclasses, else => {}, } }, @@ -970,18 +1039,18 @@ pub const Deprecated = struct { }; pub const ElementValue = union(enum) { // B, C, D, F, I, J, S, Z, s - const_value_index: u16, + const_value_index: u16, // e - enum_const_value: struct { + enum_const_value: struct { type_name_index: u16, const_name_index: u16, }, // c - class_info_index: u16, + class_info_index: u16, // @ - annotation_value: Annotation, + annotation_value: Annotation, // [ - array_value: struct { + array_value: struct { values: []ElementValue, }, @@ -1035,15 +1104,15 @@ pub const ElementValue = union(enum) { } }; +pub const ElementValuePair = struct { + element_name_index: u16, + value: ElementValue, +}; + pub const Annotation = struct { type_index: u16, element_value_pairs: []ElementValuePair, - pub const ElementValuePair = struct { - element_name_index: u16, - value: ElementValue, - }; - pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Annotation { const type_index = try input.takeInt(u16, .big); @@ -1203,6 +1272,51 @@ 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 RuntimeVisibleTypeAnnotations = struct { +// type_annotations: []TypeAnnotation, +// +// const Self = @This(); +// }; +// +// pub const RuntimeInvisibleTypeAnnotations = struct { +// type_annotations: []TypeAnnotation, +// +// const Self = @This(); +// }; + pub const AnnotationDefault = struct { default_value: ElementValue, @@ -1261,3 +1375,189 @@ pub const BootstrapMethods = struct { allocator.free(self.bootstrap_methods); } }; + +pub const MethodParameters = struct { + parameters: []Parameter, + + const Self = @This(); + + pub const Parameter = struct { + name_index: u16, + access_flags: ParameterAccessFlags, + + pub const ParameterFlags = enum(u16) { + final = 0x0010, + synthetic = 0x1000, + mandated = 0x8000, + }; + + pub const ParameterAccessFlags = EnumFlags(ParameterFlags); + }; + + pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { + const parameter_count = try input.takeByte(); + const parameters = try allocator.alloc(Parameter, parameter_count); + errdefer allocator.free(parameters); + for (parameters) |*parameter| { + parameter.* = .{ + .name_index = try input.takeInt(u16, .big), + .access_flags = .{ .mask = try input.takeInt(u16, .big) }, + }; + } + + return .{ + .parameters = parameters, + }; + } + + pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + allocator.free(self.parameters); + } +}; + +pub const ModulePackages = struct { + package_indeces: []u16, + + const Self = @This(); + + pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { + const package_count = try input.takeInt(u16, .big); + const package_indeces = try allocator.alloc(u16, package_count); + errdefer allocator.free(package_indeces); + for (package_indeces) |*package_index| { + package_index.* = try input.takeInt(u16, .big); + } + + return .{ + .package_indeces = package_indeces, + }; + } + + pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + allocator.free(self.package_indeces); + } +}; + +pub const ModuleMainClass = struct { + main_class_index: u16, + + const Self = @This(); + + pub fn parse(input: *std.Io.Reader) ParseError!Self { + return .{ + .main_class_index = try input.takeInt(u16, .big), + }; + } +}; + +pub const NestHost = struct { + host_class_index: u16, + + const Self = @This(); + + pub fn parse(input: *std.Io.Reader) ParseError!Self { + return .{ + .host_class_index = try input.takeInt(u16, .big), + }; + } +}; + +pub const NestMembers = struct { + classes: []u16, + + const Self = @This(); + + 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(u16, number_of_classes); + for (classes) |*class| { + class.* = try input.takeInt(u16, .big); + } + + return .{ + .classes = classes, + }; + } + + pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + allocator.free(self.classes); + } +}; + +pub const Record = struct { + components: []RecordComponentInfo, + + const Self = @This(); + + pub const RecordComponentInfo = struct { + name_index: u16, + descriptor_index: u16, + attributes: []AttributeInfo, + + pub fn parse(input: *std.Io.Reader, constant_pool: []const ?ConstantPoolInfo, allocator: std.mem.Allocator) (ParseError || ResolveError)!RecordComponentInfo { + const name_index = try input.takeInt(u16, .big); + const descriptor_index = try input.takeInt(u16, .big); + + 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); + } + + return .{ + .name_index = name_index, + .descriptor_index = descriptor_index, + .attributes = attributes, + }; + } + + pub fn deinit(self: *RecordComponentInfo, allocator: std.mem.Allocator) void { + for (self.attributes) |*attribute| { + attribute.deinit(allocator); + } + allocator.free(self.attributes); + } + }; + + pub fn parse(input: *std.Io.Reader, constant_pool: []const ?ConstantPoolInfo, allocator: std.mem.Allocator) (ParseError || ResolveError)!Self { + const components_count = try input.takeInt(u16, .big); + const components = try allocator.alloc(RecordComponentInfo, components_count); + for (components) |*component| { + component.* = try .parse(input, constant_pool, allocator); + } + + return .{ + .components = components, + }; + } + + pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + for (self.components) |*component| { + component.deinit(allocator); + } + allocator.free(self.components); + } +}; + +pub const PermittedSubclasses = struct { + classes: []u16, + + const Self = @This(); + + 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(u16, number_of_classes); + for (classes) |*class| { + class.* = try input.takeInt(u16, .big); + } + + return .{ + .classes = classes, + }; + } + + pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + allocator.free(self.classes); + } +}; diff --git a/src/main.zig b/src/main.zig index 4ea41ec..922c825 100644 --- a/src/main.zig +++ b/src/main.zig @@ -66,4 +66,3 @@ pub fn main(init: std.process.Init) !void { try stdout.flush(); } - From f8b1f968e09960f40823eadccd8759c6d886a732 Mon Sep 17 00:00:00 2001 From: ktkk Date: Sun, 5 Jul 2026 00:25:25 +0200 Subject: [PATCH 05/31] Format additional predefined attribute infos --- src/Class.zig | 86 +++++++++++++++- src/Class/AttributeInfo.zig | 200 +++++++++++++++++++++++++++++++++--- src/Class/FieldInfo.zig | 3 +- src/Class/MethodInfo.zig | 3 +- 4 files changed, 273 insertions(+), 19 deletions(-) diff --git a/src/Class.zig b/src/Class.zig index 1d96a19..df4f523 100644 --- a/src/Class.zig +++ b/src/Class.zig @@ -274,7 +274,7 @@ pub fn format(self: *const Self, w: *std.Io.Writer) std.Io.Writer.Error!void { try w.splatByteAll(' ', depth * indent); try w.print("{d}:\n", .{ i }); - try field.indentedFormat(w, depth, indent + 1, self.allocator, self.constant_pool); + try field.indentedFormat(w, depth, indent + 1, self.constant_pool); } _ = try w.write("methods:\n"); @@ -284,7 +284,7 @@ pub fn format(self: *const Self, w: *std.Io.Writer) std.Io.Writer.Error!void { try w.splatByteAll(' ', depth * indent); try w.print("{d}:\n", .{ i }); - try method.indentedFormat(w, depth, indent + 1, self.allocator, self.constant_pool); + try method.indentedFormat(w, depth, indent + 1, self.constant_pool); } _ = try w.write("attributes:\n"); @@ -294,7 +294,7 @@ pub fn format(self: *const Self, w: *std.Io.Writer) std.Io.Writer.Error!void { try w.splatByteAll(' ', depth * indent); try w.print("{d}:\n", .{ i }); - try attribute.indentedFormat(w, depth, indent + 1, self.allocator, self.constant_pool); + try attribute.indentedFormat(w, depth, indent + 1, self.constant_pool); } } @@ -342,7 +342,10 @@ pub const ConstantPoolInfo = union(ConstantPoolInfo.Tag) { utf8: Utf8, method_handle: MethodHandle, method_type: MethodType, + dynamic: Dynamic, invoke_dynamic: InvokeDynamic, + module: Module, + package: Package, pub const Tag = enum(u8) { class = 7, @@ -358,7 +361,10 @@ pub const ConstantPoolInfo = union(ConstantPoolInfo.Tag) { utf8 = 1, method_handle = 15, method_type = 16, + dynamic = 17, invoke_dynamic = 18, + module = 19, + package = 20, }; pub const Class = struct { @@ -540,6 +546,19 @@ pub const ConstantPoolInfo = union(ConstantPoolInfo.Tag) { } }; + pub const Dynamic = struct { + bootstrap_method_attr_index: u16, + name_and_type_index: u16, + + pub fn nameAndType(self: *const Dynamic, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const name_and_type_index = self.name_and_type_index - 1; + if (name_and_type_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[name_and_type_index]; + if (cp_info == null or @as(Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType; + return cp_info.?; + } + }; + pub const InvokeDynamic = struct { bootstrap_method_attr_index: u16, name_and_type_index: u16, @@ -553,6 +572,30 @@ pub const ConstantPoolInfo = union(ConstantPoolInfo.Tag) { } }; + pub const Module = struct { + name_index: u16, + + pub fn name(self: *const Module, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const name_index = self.name_index - 1; + if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[name_index]; + if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; + } + }; + + pub const Package = struct { + name_index: u16, + + pub fn name(self: *const Package, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const name_index = self.name_index - 1; + if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[name_index]; + if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; + } + }; + pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!ConstantPoolInfo { const tag_byte = try input.takeByte(); const tag = std.enums.fromInt(Tag, tag_byte) orelse return ParseError.InvalidTag; @@ -640,12 +683,28 @@ pub const ConstantPoolInfo = union(ConstantPoolInfo.Tag) { .descriptor_index = try input.takeInt(u16, .big), }, }, + .dynamic => .{ + .dynamic = .{ + .bootstrap_method_attr_index = try input.takeInt(u16, .big), + .name_and_type_index = try input.takeInt(u16, .big), + }, + }, .invoke_dynamic => .{ .invoke_dynamic = .{ .bootstrap_method_attr_index = try input.takeInt(u16, .big), .name_and_type_index = try input.takeInt(u16, .big), }, }, + .module => .{ + .module = .{ + .name_index = try input.takeInt(u16, .big), + }, + }, + .package => .{ + .package = .{ + .name_index = try input.takeInt(u16, .big), + }, + }, }; } @@ -671,7 +730,10 @@ pub const ConstantPoolInfo = union(ConstantPoolInfo.Tag) { .utf8 => |utf8| try w.print("\"{s}\"", .{ utf8.bytes }), .method_handle => |method_handle| try w.print("{s} #{d}", .{ @tagName(method_handle.reference_kind), method_handle.reference_index }), .method_type => |method_type| try w.print("#{d}", .{ method_type.descriptor_index }), + .dynamic => |dynamic| try w.print("#{d} #{d}", .{ dynamic.bootstrap_method_attr_index, dynamic.name_and_type_index }), .invoke_dynamic => |invoke_dynamic| try w.print("#{d} #{d}", .{ invoke_dynamic.bootstrap_method_attr_index, invoke_dynamic.name_and_type_index }), + .module => |module| try w.print("#{d}", .{ module.name_index }), + .package => |package| try w.print("#{d}", .{ package.name_index }), } } @@ -782,12 +844,30 @@ pub const ConstantPoolInfo = union(ConstantPoolInfo.Tag) { const descriptor = method_type.descriptor(constant_pool) catch return error.WriteFailed; try descriptor.indentedFormat(w, depth, indent + 1, constant_pool); }, + .dynamic => |dynamic| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("name_and_type:\n"); + const name_and_type = dynamic.nameAndType(constant_pool) catch return error.WriteFailed; + try name_and_type.indentedFormat(w, depth, indent + 1, constant_pool); + }, .invoke_dynamic => |invoke_dynamic| { try w.splatByteAll(' ', depth * indent); _ = try w.write("name_and_type:\n"); const name_and_type = invoke_dynamic.nameAndType(constant_pool) catch return error.WriteFailed; try name_and_type.indentedFormat(w, depth, indent + 1, constant_pool); }, + .module => |module| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("name:\n"); + const name = module.name(constant_pool) catch return error.WriteFailed; + try name.indentedFormat(w, depth, indent + 1, constant_pool); + }, + .package => |package| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("name:\n"); + const name = package.name(constant_pool) catch return error.WriteFailed; + try name.indentedFormat(w, depth, indent + 1, constant_pool); + }, } } }; diff --git a/src/Class/AttributeInfo.zig b/src/Class/AttributeInfo.zig index 4b17b67..e41738d 100644 --- a/src/Class/AttributeInfo.zig +++ b/src/Class/AttributeInfo.zig @@ -259,7 +259,6 @@ pub const AttributeInfo = union(enum) { w: *std.Io.Writer, depth: usize, indent: u8, - allocator: std.mem.Allocator, constant_pool: []const ?ConstantPoolInfo, ) std.Io.Writer.Error!void { switch (self.*) { @@ -325,7 +324,7 @@ pub const AttributeInfo = union(enum) { try w.splatByteAll(' ', depth * attr_indent); try w.print("{d}:\n", .{ i }); - try a.indentedFormat(w, depth, attr_indent + 1, allocator, constant_pool); + try a.indentedFormat(w, depth, attr_indent + 1, constant_pool); } }, //.stack_map_table, @@ -333,12 +332,14 @@ pub const AttributeInfo = union(enum) { try w.splatByteAll(' ', depth * indent); _ = try w.write("exceptions:\n"); var exception_indent = indent; - for (attr.exception_index_table, 0..) |exception, i| { + for (attr.exception_index_table, 0..) |exception_index, i| { exception_indent += 1; defer exception_indent -= 1; - if (exception >= constant_pool.len) return error.WriteFailed; - const cp_info = constant_pool[exception - 1]; + const index = exception_index - 1; + + if (index >= constant_pool.len) return error.WriteFailed; + const cp_info = constant_pool[index]; if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return error.WriteFailed; try w.splatByteAll(' ', depth * exception_indent); @@ -396,14 +397,102 @@ pub const AttributeInfo = union(enum) { //.runtime_invisible_parameter_annotations, //.annotation_default, //.bootstrap_methods, - //.method_parameters, + .method_parameters => |attr| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("parameters:\n"); + var parameter_indent = indent; + for (attr.parameters, 0..) |parameter, i| { + parameter_indent += 1; + defer parameter_indent -= 1; + + try w.splatByteAll(' ', depth * parameter_indent); + try w.print("{d}:\n", .{ i }); + try parameter.indentedFormat(w, depth, parameter_indent, constant_pool); + } + }, //.module, - //.module_packages, - //.module_main_class, - //.nest_host, - //.nest_members, - //.record, - //.permitted_subclasses, + .module_packages => |attr| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("packages:\n"); + var package_indent = indent; + for (attr.package_indeces, 0..) |package_index, i| { + package_indent += 1; + defer package_indent -= 1; + + const index = package_index - 1; + + if (index >= constant_pool.len) return error.WriteFailed; + const cp_info = constant_pool[index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .package) return error.WriteFailed; + + try w.splatByteAll(' ', depth * package_indent); + try w.print("{d}:\n", .{ i }); + try cp_info.?.indentedFormat(w, depth, package_indent + 1, constant_pool); + } + }, + .module_main_class => |attr| { + const main_class = attr.mainClass(constant_pool) catch return error.WriteFailed; + try w.splatByteAll(' ', depth * indent); + _ = try w.write("main_class:\n"); + try main_class.indentedFormat(w, depth, indent + 1, constant_pool); + }, + .nest_host => |attr| { + const host_class = attr.hostClass(constant_pool) catch return error.WriteFailed; + try w.splatByteAll(' ', depth * indent); + _ = try w.write("host_class:\n"); + try host_class.indentedFormat(w, depth, indent + 1, constant_pool); + }, + .nest_members => |attr| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("classes:\n"); + var class_indent = indent; + for (attr.classes, 0..) |class_index, i| { + class_indent += 1; + defer class_indent -= 1; + + const index = class_index - 1; + + if (index >= constant_pool.len) return error.WriteFailed; + const cp_info = constant_pool[index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return error.WriteFailed; + + try w.splatByteAll(' ', depth * class_indent); + try w.print("{d}:\n", .{ i }); + try cp_info.?.indentedFormat(w, depth, class_indent + 1, constant_pool); + } + }, + .record => |attr| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("components:\n"); + var component_indent = indent; + for (attr.components, 0..) |component, i| { + component_indent += 1; + defer component_indent -= 1; + + try w.splatByteAll(' ', depth * component_indent); + try w.print("{d}:\n", .{ i }); + try component.indentedFormat(w, depth, component_indent, constant_pool); + } + }, + .permitted_subclasses => |attr| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("classes:\n"); + var class_indent = indent; + for (attr.classes, 0..) |class_index, i| { + class_indent += 1; + defer class_indent -= 1; + + const index = class_index - 1; + + if (index >= constant_pool.len) return error.WriteFailed; + const cp_info = constant_pool[index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return error.WriteFailed; + + try w.splatByteAll(' ', depth * class_indent); + try w.print("{d}:\n", .{ i }); + try cp_info.?.indentedFormat(w, depth, class_indent + 1, constant_pool); + } + }, else => {}, } }, @@ -1392,6 +1481,30 @@ pub const MethodParameters = struct { }; pub const ParameterAccessFlags = EnumFlags(ParameterFlags); + + pub fn indentedFormat( + self: *const Parameter, + 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.access_flags }); + + const name_ = self.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); + } + + pub fn name(self: *const Parameter, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const name_index = self.name_index - 1; + if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[name_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; + } }; pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { @@ -1448,6 +1561,14 @@ pub const ModuleMainClass = struct { .main_class_index = try input.takeInt(u16, .big), }; } + + pub fn mainClass(self: *const ModuleMainClass, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const main_class_index = self.main_class_index - 1; + if (main_class_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[main_class_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; + return cp_info.?; + } }; pub const NestHost = struct { @@ -1460,6 +1581,14 @@ pub const NestHost = struct { .host_class_index = try input.takeInt(u16, .big), }; } + + pub fn hostClass(self: *const NestHost, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const host_class_index = self.host_class_index - 1; + if (host_class_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[host_class_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; + return cp_info.?; + } }; pub const NestMembers = struct { @@ -1518,6 +1647,53 @@ pub const Record = struct { } allocator.free(self.attributes); } + + pub fn indentedFormat( + self: *const RecordComponentInfo, + w: *std.Io.Writer, + depth: usize, + indent: u8, + constant_pool: []const ?ConstantPoolInfo, + ) std.Io.Writer.Error!void { + const name_ = self.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); + + const descriptor_ = self.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); + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("attributes:\n"); + var attr_indent = indent; + for (self.attributes, 0..) |*attr, i| { + attr_indent += 1; + defer attr_indent -= 1; + + try w.splatByteAll(' ', depth * attr_indent); + try w.print("{d}:\n", .{ i }); + + try attr.indentedFormat(w, depth, attr_indent + 1, constant_pool); + } + } + + pub fn name(self: *const RecordComponentInfo, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const name_index = self.name_index - 1; + if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[name_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; + } + + pub fn descriptor(self: *const RecordComponentInfo, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const descriptor_index = self.descriptor_index - 1; + if (descriptor_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[descriptor_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; + } }; pub fn parse(input: *std.Io.Reader, constant_pool: []const ?ConstantPoolInfo, allocator: std.mem.Allocator) (ParseError || ResolveError)!Self { diff --git a/src/Class/FieldInfo.zig b/src/Class/FieldInfo.zig index ed3cb61..62325d9 100644 --- a/src/Class/FieldInfo.zig +++ b/src/Class/FieldInfo.zig @@ -53,7 +53,6 @@ pub fn indentedFormat( 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); @@ -79,7 +78,7 @@ pub fn indentedFormat( try w.splatByteAll(' ', depth * attr_indent); try w.print("{d}:\n", .{ j }); - try attribute.indentedFormat(w, depth, attr_indent + 1, allocator, constant_pool); + try attribute.indentedFormat(w, depth, attr_indent + 1, constant_pool); } } diff --git a/src/Class/MethodInfo.zig b/src/Class/MethodInfo.zig index 7a457ee..87ad266 100644 --- a/src/Class/MethodInfo.zig +++ b/src/Class/MethodInfo.zig @@ -56,7 +56,6 @@ pub fn indentedFormat( 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); @@ -82,7 +81,7 @@ pub fn indentedFormat( try w.splatByteAll(' ', depth * attr_indent); try w.print("{d}:\n", .{ j }); - try attribute.indentedFormat(w, depth, attr_indent + 1, allocator, constant_pool); + try attribute.indentedFormat(w, depth, attr_indent + 1, constant_pool); } } From 7cc907dc1964da17fa0f5a4e4fc48983bf6611c1 Mon Sep 17 00:00:00 2001 From: ktkk Date: Sun, 5 Jul 2026 01:08:13 +0200 Subject: [PATCH 06/31] Inline flag definitions --- src/Class.zig | 6 ++---- src/Class/AttributeInfo.zig | 12 ++++-------- src/Class/FieldInfo.zig | 6 ++---- src/Class/MethodInfo.zig | 6 ++---- 4 files changed, 10 insertions(+), 20 deletions(-) diff --git a/src/Class.zig b/src/Class.zig index df4f523..39a1cdb 100644 --- a/src/Class.zig +++ b/src/Class.zig @@ -22,7 +22,7 @@ attributes: []AttributeInfo, const Self = @This(); -pub const ClassFlags = enum(u16) { +pub const ClassAccessFlags = EnumFlags(enum(u16) { public = 0x0001, final = 0x0010, super = 0x0020, @@ -31,9 +31,7 @@ pub const ClassFlags = enum(u16) { synthetic = 0x1000, annotation = 0x2000, @"enum" = 0x4000, -}; - -pub const ClassAccessFlags = EnumFlags(ClassFlags); +}); pub const ParseError = std.Io.Reader.Error || std.mem.Allocator.Error || error { InvalidMagicNumber, diff --git a/src/Class/AttributeInfo.zig b/src/Class/AttributeInfo.zig index e41738d..df0dc1a 100644 --- a/src/Class/AttributeInfo.zig +++ b/src/Class/AttributeInfo.zig @@ -892,7 +892,7 @@ pub const InnerClasses = struct { } }; - pub const InnerClassFlags = enum(u16) { + pub const InnerClassAccessFlags = EnumFlags(enum(u16) { public = 0x0001, private = 0x0002, protected = 0x0004, @@ -903,9 +903,7 @@ pub const InnerClasses = struct { 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); @@ -1474,13 +1472,11 @@ pub const MethodParameters = struct { name_index: u16, access_flags: ParameterAccessFlags, - pub const ParameterFlags = enum(u16) { + pub const ParameterAccessFlags = EnumFlags(enum(u16) { final = 0x0010, synthetic = 0x1000, mandated = 0x8000, - }; - - pub const ParameterAccessFlags = EnumFlags(ParameterFlags); + }); pub fn indentedFormat( self: *const Parameter, diff --git a/src/Class/FieldInfo.zig b/src/Class/FieldInfo.zig index 62325d9..952cf38 100644 --- a/src/Class/FieldInfo.zig +++ b/src/Class/FieldInfo.zig @@ -14,7 +14,7 @@ attributes: []AttributeInfo, const Self = @This(); -pub const FieldFlags = enum(u16) { +pub const FieldAccessFlags = EnumFlags(enum(u16) { public = 0x0001, private = 0x0002, protected = 0x0004, @@ -24,9 +24,7 @@ pub const FieldFlags = enum(u16) { transient = 0x0080, synthetic = 0x1000, @"enum" = 0x4000, -}; - -pub const FieldAccessFlags = EnumFlags(FieldFlags); +}); 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) }; diff --git a/src/Class/MethodInfo.zig b/src/Class/MethodInfo.zig index 87ad266..a9c3e61 100644 --- a/src/Class/MethodInfo.zig +++ b/src/Class/MethodInfo.zig @@ -14,7 +14,7 @@ attributes: []AttributeInfo, const Self = @This(); -pub const MethodFlags = enum(u16) { +pub const MethodAccessFlags = EnumFlags(enum(u16) { public = 0x0001, private = 0x0002, protected = 0x0004, @@ -27,9 +27,7 @@ pub const MethodFlags = enum(u16) { abstract = 0x0400, strict = 0x0800, synthetic = 0x1000, -}; - -pub const MethodAccessFlags = EnumFlags(MethodFlags); +}); 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) }; From 21644ef7f03af96be842b5f152b7217c2abe31cc Mon Sep 17 00:00:00 2001 From: ktkk Date: Sun, 5 Jul 2026 13:58:11 +0200 Subject: [PATCH 07/31] Add module attribute --- src/Class/AttributeInfo.zig | 178 +++++++++++++++++++++++++++++++++++- 1 file changed, 177 insertions(+), 1 deletion(-) diff --git a/src/Class/AttributeInfo.zig b/src/Class/AttributeInfo.zig index df0dc1a..1b4c6af 100644 --- a/src/Class/AttributeInfo.zig +++ b/src/Class/AttributeInfo.zig @@ -33,7 +33,7 @@ pub const AttributeInfo = union(enum) { annotation_default: AnnotationDefault, bootstrap_methods: BootstrapMethods, method_parameters: MethodParameters, - // module: Module, + module: Module, module_packages: ModulePackages, module_main_class: ModuleMainClass, nest_host: NestHost, @@ -183,6 +183,12 @@ pub const AttributeInfo = union(enum) { .method_parameters = try MethodParameters.parse(limited, allocator), }, }; + } else if (std.mem.eql(u8, attribute_name.bytes, "Module")) { + return .{ + .predefined = .{ + .module = try Module.parse(limited, allocator), + }, + }; } else if (std.mem.eql(u8, attribute_name.bytes, "ModulePackages")) { return .{ .predefined = .{ @@ -245,6 +251,7 @@ pub const AttributeInfo = union(enum) { .annotation_default => |*annotation_default| annotation_default.deinit(allocator), .bootstrap_methods => |*bootstrap_methods| bootstrap_methods.deinit(allocator), .method_parameters => |*method_parameters| method_parameters.deinit(allocator), + .module => |*module| module.deinit(allocator), .module_packages => |*module_packages| module_packages.deinit(allocator), .nest_members => |*nest_members| nest_members.deinit(allocator), .record => |*record| record.deinit(allocator), @@ -1524,6 +1531,175 @@ pub const MethodParameters = struct { } }; +pub const Module = struct { + module_name_index: u16, + module_flags: ModuleFlags, + module_version_index: u16, + requires: []Requires, + exports: []Exports, + opens: []Opens, + uses_indeces: []u16, + provides: []Provides, + + const Self = @This(); + + pub const ModuleFlags = EnumFlags(enum(u16) { + open = 0x0020, + sythetic = 0x1000, + mandated = 0x8000, + }); + + pub const Requires = struct { + requires_index: u16, + requires_flags: RequiresFlags, + requires_version_index: u16, + + pub const RequiresFlags = EnumFlags(enum(u16) { + transitive = 0x0020, + static_phase = 0x0040, + synthetic = 0x1000, + mandated = 0x8000, + }); + }; + + pub const Exports = struct { + exports_index: u16, + exports_flags: ExportsFlags, + exports_to_indeces: []u16, + + pub const ExportsFlags = EnumFlags(enum(u16) { + synthetic = 0x1000, + mandated = 0x8000, + }); + }; + + pub const Opens = struct { + opens_index: u16, + opens_flags: OpensFlags, + opens_to_indeces: []u16, + + pub const OpensFlags = EnumFlags(enum(u16) { + synthetic = 0x1000, + mandated = 0x8000, + }); + }; + + pub const Provides = struct { + provides_index: u16, + provides_with_indeces: []u16, + }; + + pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { + const module_name_index = try input.takeInt(u16, .big); + const module_flags: ModuleFlags = .{ .mask = try input.takeInt(u16, .big) }; + const module_version_index = try input.takeInt(u16, .big); + + const requires_count = try input.takeInt(u16, .big); + const requires = try allocator.alloc(Requires, requires_count); + errdefer allocator.free(requires); + for (requires) |*r| { + const requires_index = try input.takeInt(u16, .big); + const requires_flags: Requires.RequiresFlags = .{ .mask = try input.takeInt(u16, .big) }; + const requires_version_index = try input.takeInt(u16, .big); + r.* = .{ + .requires_index = requires_index, + .requires_flags = requires_flags, + .requires_version_index = requires_version_index, + }; + } + + const exports_count = try input.takeInt(u16, .big); + const exports = try allocator.alloc(Exports, exports_count); + errdefer allocator.free(exports); + for (exports) |*e| { + const exports_index = try input.takeInt(u16, .big); + const exports_flags: Exports.ExportsFlags = .{ .mask = try input.takeInt(u16, .big) }; + const exports_to_count = try input.takeInt(u16, .big); + const exports_to_indeces = try allocator.alloc(u16, exports_to_count); + errdefer allocator.free(exports_to_indeces); + for (exports_to_indeces) |*i| { + i.* = try input.takeInt(u16, .big); + } + e.* = .{ + .exports_index = exports_index, + .exports_flags = exports_flags, + .exports_to_indeces = exports_to_indeces, + }; + } + + const opens_count = try input.takeInt(u16, .big); + const opens = try allocator.alloc(Opens, opens_count); + errdefer allocator.free(opens); + for (opens) |*o| { + const opens_index = try input.takeInt(u16, .big); + const opens_flags: Opens.OpensFlags = .{ .mask = try input.takeInt(u16, .big) }; + const opens_to_count = try input.takeInt(u16, .big); + const opens_to_indeces = try allocator.alloc(u16, opens_to_count); + errdefer allocator.free(opens_to_indeces); + for (opens_to_indeces) |*i| { + i.* = try input.takeInt(u16, .big); + } + o.* = .{ + .opens_index = opens_index, + .opens_flags = opens_flags, + .opens_to_indeces = opens_to_indeces, + }; + } + + const uses_count = try input.takeInt(u16, .big); + const uses_indeces = try allocator.alloc(u16, uses_count); + errdefer allocator.free(uses_indeces); + for (uses_indeces) |*i| { + i.* = try input.takeInt(u16, .big); + } + + const provides_count = try input.takeInt(u16, .big); + const provides = try allocator.alloc(Provides, provides_count); + errdefer allocator.free(provides); + for (provides) |*p| { + const provides_index = try input.takeInt(u16, .big); + const provides_with_count = try input.takeInt(u16, .big); + const provides_with_indeces = try allocator.alloc(u16, provides_with_count); + errdefer allocator.free(provides_with_indeces); + for (provides_with_indeces) |*i| { + i.* = try input.takeInt(u16, .big); + } + p.* = .{ + .provides_index = provides_index, + .provides_with_indeces = provides_with_indeces, + }; + } + + return .{ + .module_name_index = module_name_index, + .module_flags = module_flags, + .module_version_index = module_version_index, + .requires = requires, + .exports = exports, + .opens = opens, + .uses_indeces = uses_indeces, + .provides = provides, + }; + } + + pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + allocator.free(self.requires); + for (self.exports) |*e| { + allocator.free(e.exports_to_indeces); + } + allocator.free(self.exports); + for (self.opens) |*o| { + allocator.free(o.opens_to_indeces); + } + allocator.free(self.opens); + allocator.free(self.uses_indeces); + for (self.provides) |*p| { + allocator.free(p.provides_with_indeces); + } + allocator.free(self.provides); + } +}; + pub const ModulePackages = struct { package_indeces: []u16, From eefe96292e24803649c5e3def8cf4bfa1473af46 Mon Sep 17 00:00:00 2001 From: ktkk Date: Sun, 5 Jul 2026 15:31:00 +0200 Subject: [PATCH 08/31] Format module attribute info --- src/Class/AttributeInfo.zig | 297 ++++++++++++++++++++++++++++++++++-- 1 file changed, 283 insertions(+), 14 deletions(-) diff --git a/src/Class/AttributeInfo.zig b/src/Class/AttributeInfo.zig index 1b4c6af..50fd96d 100644 --- a/src/Class/AttributeInfo.zig +++ b/src/Class/AttributeInfo.zig @@ -417,7 +417,7 @@ pub const AttributeInfo = union(enum) { try parameter.indentedFormat(w, depth, parameter_indent, constant_pool); } }, - //.module, + .module => |attr| try attr.indentedFormat(w, depth, indent, constant_pool), .module_packages => |attr| { try w.splatByteAll(' ', depth * indent); _ = try w.write("packages:\n"); @@ -840,6 +840,19 @@ pub const InnerClasses = struct { inner_name_index: u16, inner_class_access_flags: InnerClassAccessFlags, + pub const InnerClassAccessFlags = EnumFlags(enum(u16) { + public = 0x0001, + private = 0x0002, + protected = 0x0004, + static = 0x0008, + final = 0x0010, + interface = 0x0200, + abstract = 0x0400, + synthetic = 0x1000, + annotation = 0x2000, + @"enum" = 0x4000, + }); + pub fn indentedFormat( self: *const InnerClass, w: *std.Io.Writer, @@ -899,19 +912,6 @@ pub const InnerClasses = struct { } }; - pub const InnerClassAccessFlags = EnumFlags(enum(u16) { - public = 0x0001, - private = 0x0002, - protected = 0x0004, - static = 0x0008, - final = 0x0010, - interface = 0x0200, - abstract = 0x0400, - synthetic = 0x1000, - annotation = 0x2000, - @"enum" = 0x4000, - }); - 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); @@ -1560,6 +1560,46 @@ pub const Module = struct { synthetic = 0x1000, mandated = 0x8000, }); + + pub fn indentedFormat( + self: *const Requires, + w: *std.Io.Writer, + depth: usize, + indent: u8, + constant_pool: []const ?ConstantPoolInfo, + ) std.Io.Writer.Error!void { + const requires_ = self.requires(constant_pool) catch return error.WriteFailed; + try w.splatByteAll(' ', depth * indent); + _ = try w.write("requires:\n"); + try requires_.indentedFormat(w, depth, indent + 1, constant_pool); + + try w.splatByteAll(' ', depth * indent); + try w.print("flags: {f}\n", .{ self.requires_flags }); + + const version = self.requiresVersion(constant_pool) catch return error.WriteFailed; + if (version) |v| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("version:\n"); + try v.indentedFormat(w, depth, indent + 1, constant_pool); + } + } + + pub fn requires(self: *const Requires, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const requires_index = self.requires_index - 1; + if (requires_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[requires_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .module) return ResolveError.InvalidConstantType; + return cp_info.?; + } + + pub fn requiresVersion(self: *const Requires, constant_pool: []const ?ConstantPoolInfo) ResolveError!?ConstantPoolInfo { + if (self.requires_version_index == 0) return null; + const requires_version_index = self.requires_version_index - 1; + if (requires_version_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[requires_version_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; + } }; pub const Exports = struct { @@ -1571,6 +1611,48 @@ pub const Module = struct { synthetic = 0x1000, mandated = 0x8000, }); + + pub fn indentedFormat( + self: *const Exports, + w: *std.Io.Writer, + depth: usize, + indent: u8, + constant_pool: []const ?ConstantPoolInfo, + ) std.Io.Writer.Error!void { + const exports_ = self.exports(constant_pool) catch return error.WriteFailed; + try w.splatByteAll(' ', depth * indent); + _ = try w.write("exports:\n"); + try exports_.indentedFormat(w, depth, indent + 1, constant_pool); + + try w.splatByteAll(' ', depth * indent); + try w.print("flags: {f}\n", .{ self.exports_flags }); + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("exports_to:\n"); + var exports_to_indent = indent; + for (self.exports_to_indeces, 0..) |exports_to_index, i| { + exports_to_indent += 1; + defer exports_to_indent -= 1; + + const index = exports_to_index - 1; + + if (index >= constant_pool.len) return error.WriteFailed; + const cp_info = constant_pool[index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .module) return error.WriteFailed; + + try w.splatByteAll(' ', depth * exports_to_indent); + try w.print("{d}:\n", .{ i }); + try cp_info.?.indentedFormat(w, depth, exports_to_indent + 1, constant_pool); + } + } + + pub fn exports(self: *const Exports, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const exports_index = self.exports_index - 1; + if (exports_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[exports_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .package) return ResolveError.InvalidConstantType; + return cp_info.?; + } }; pub const Opens = struct { @@ -1582,11 +1664,92 @@ pub const Module = struct { synthetic = 0x1000, mandated = 0x8000, }); + + pub fn indentedFormat( + self: *const Opens, + w: *std.Io.Writer, + depth: usize, + indent: u8, + constant_pool: []const ?ConstantPoolInfo, + ) std.Io.Writer.Error!void { + const opens_ = self.opens(constant_pool) catch return error.WriteFailed; + try w.splatByteAll(' ', depth * indent); + _ = try w.write("opens:\n"); + try opens_.indentedFormat(w, depth, indent + 1, constant_pool); + + try w.splatByteAll(' ', depth * indent); + try w.print("flags: {f}\n", .{ self.opens_flags }); + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("opens_to:\n"); + var opens_to_indent = indent; + for (self.opens_to_indeces, 0..) |opens_to_index, i| { + opens_to_indent += 1; + defer opens_to_indent -= 1; + + const index = opens_to_index - 1; + + if (index >= constant_pool.len) return error.WriteFailed; + const cp_info = constant_pool[index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .module) return error.WriteFailed; + + try w.splatByteAll(' ', depth * opens_to_indent); + try w.print("{d}:\n", .{ i }); + try cp_info.?.indentedFormat(w, depth, opens_to_indent + 1, constant_pool); + } + } + + pub fn opens(self: *const Opens, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const opens_index = self.opens_index - 1; + if (opens_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[opens_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .package) return ResolveError.InvalidConstantType; + return cp_info.?; + } }; pub const Provides = struct { provides_index: u16, provides_with_indeces: []u16, + + pub fn indentedFormat( + self: *const Provides, + w: *std.Io.Writer, + depth: usize, + indent: u8, + constant_pool: []const ?ConstantPoolInfo, + ) std.Io.Writer.Error!void { + const provides_ = self.provides(constant_pool) catch return error.WriteFailed; + try w.splatByteAll(' ', depth * indent); + _ = try w.write("provides:\n"); + try provides_.indentedFormat(w, depth, indent + 1, constant_pool); + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("provides_with:\n"); + var provides_with_indent = indent; + for (self.provides_with_indeces, 0..) |provides_with_index, i| { + provides_with_indent += 1; + defer provides_with_indent -= 1; + + const index = provides_with_index - 1; + + if (index >= constant_pool.len) return error.WriteFailed; + const cp_info = constant_pool[index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return error.WriteFailed; + + try w.splatByteAll(' ', depth * provides_with_indent); + try w.print("{d}:\n", .{ i }); + try cp_info.?.indentedFormat(w, depth, provides_with_indent + 1, constant_pool); + } + } + + pub fn provides(self: *const Provides, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const provides_index = self.provides_index - 1; + if (provides_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[provides_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; + return cp_info.?; + } }; pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { @@ -1698,6 +1861,112 @@ pub const Module = struct { } allocator.free(self.provides); } + + pub fn indentedFormat( + self: *const Module, + w: *std.Io.Writer, + depth: usize, + indent: u8, + constant_pool: []const ?ConstantPoolInfo, + ) std.Io.Writer.Error!void { + const module_name = self.moduleName(constant_pool) catch return error.WriteFailed; + try w.splatByteAll(' ', depth * indent); + _ = try w.write("name:\n"); + try module_name.indentedFormat(w, depth, indent + 1, constant_pool); + + try w.splatByteAll(' ', depth * indent); + try w.print("flags: {f}\n", .{ self.module_flags }); + + const module_version = self.moduleVersion(constant_pool) catch return error.WriteFailed; + if (module_version) |m| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("version:\n"); + try m.indentedFormat(w, depth, indent + 1, constant_pool); + } + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("requires:\n"); + var requires_indent = indent; + for (self.requires, 0..) |requires, i| { + requires_indent += 1; + defer requires_indent -= 1; + + try w.splatByteAll(' ', depth * requires_indent); + try w.print("{d}:\n", .{ i }); + try requires.indentedFormat(w, depth, requires_indent, constant_pool); + } + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("exports:\n"); + var exports_indent = indent; + for (self.exports, 0..) |exports, i| { + exports_indent += 1; + defer exports_indent -= 1; + + try w.splatByteAll(' ', depth * exports_indent); + try w.print("{d}:\n", .{ i }); + try exports.indentedFormat(w, depth, exports_indent, constant_pool); + } + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("opens:\n"); + var opens_indent = indent; + for (self.opens, 0..) |opens, i| { + opens_indent += 1; + defer opens_indent -= 1; + + try w.splatByteAll(' ', depth * opens_indent); + try w.print("{d}:\n", .{ i }); + try opens.indentedFormat(w, depth, opens_indent, constant_pool); + } + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("uses:\n"); + var uses_indent = indent; + for (self.uses_indeces, 0..) |uses_index, i| { + uses_indent += 1; + defer uses_indent -= 1; + + const index = uses_index - 1; + + if (index >= constant_pool.len) return error.WriteFailed; + const cp_info = constant_pool[index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return error.WriteFailed; + + try w.splatByteAll(' ', depth * uses_indent); + try w.print("{d}:\n", .{ i }); + try cp_info.?.indentedFormat(w, depth, uses_indent + 1, constant_pool); + } + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("provides:\n"); + var provides_indent = indent; + for (self.provides, 0..) |provides, i| { + provides_indent += 1; + defer provides_indent -= 1; + + try w.splatByteAll(' ', depth * provides_indent); + try w.print("{d}:\n", .{ i }); + try provides.indentedFormat(w, depth, provides_indent, constant_pool); + } + } + + pub fn moduleName(self: *const Module, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const module_name_index = self.module_name_index - 1; + if (module_name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[module_name_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; + } + + pub fn moduleVersion(self: *const Module, constant_pool: []const ?ConstantPoolInfo) ResolveError!?ConstantPoolInfo { + if (self.module_version_index == 0) return null; + const module_version_index = self.module_version_index - 1; + if (module_version_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[module_version_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; + } }; pub const ModulePackages = struct { From 0fbdea320cd8fed599f7b3ea328af9202dc01f66 Mon Sep 17 00:00:00 2001 From: ktkk Date: Sun, 5 Jul 2026 15:31:24 +0200 Subject: [PATCH 09/31] Add module class access flag --- src/Class.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Class.zig b/src/Class.zig index 39a1cdb..458627f 100644 --- a/src/Class.zig +++ b/src/Class.zig @@ -31,6 +31,7 @@ pub const ClassAccessFlags = EnumFlags(enum(u16) { synthetic = 0x1000, annotation = 0x2000, @"enum" = 0x4000, + module = 0x8000, }); pub const ParseError = std.Io.Reader.Error || std.mem.Allocator.Error || error { From 6971dce705ca2c8e82ac607b957cc132536b66e0 Mon Sep 17 00:00:00 2001 From: ktkk Date: Sun, 5 Jul 2026 16:05:46 +0200 Subject: [PATCH 10/31] Format code attribute exception table --- src/Class/AttributeInfo.zig | 53 ++++++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/src/Class/AttributeInfo.zig b/src/Class/AttributeInfo.zig index 50fd96d..9e13aa0 100644 --- a/src/Class/AttributeInfo.zig +++ b/src/Class/AttributeInfo.zig @@ -308,18 +308,18 @@ pub const AttributeInfo = union(enum) { 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 * 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 w.splatByteAll(' ', depth * exception_indent); + try w.print("{d}:\n", .{ i }); - // try self.formatExceptionHandler(w, depth, exception_indent + 1, exception); - //} + try exception.indentedFormat(w, depth, exception_indent + 1, constant_pool); + } try w.splatByteAll(' ', depth * indent); _ = try w.write("attributes:\n"); @@ -569,6 +569,39 @@ pub const Code = struct { end_pc: u16, handler_pc: u16, catch_type: u16, + + pub fn indentedFormat( + self: *const ExceptionHandler, + 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("start_pc: {d}\n", .{ self.start_pc }); + + try w.splatByteAll(' ', depth * indent); + try w.print("end_pc: {d}\n", .{ self.end_pc }); + + try w.splatByteAll(' ', depth * indent); + try w.print("handler_pc: {d}\n", .{ self.handler_pc }); + + const catch_type = self.catchType(constant_pool) catch return error.WriteFailed; + if (catch_type) |ct| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("catch_type:\n"); + try ct.indentedFormat(w, depth, indent + 1, constant_pool); + } + } + + pub fn catchType(self: *const ExceptionHandler, constant_pool: []const ?ConstantPoolInfo) ResolveError!?ConstantPoolInfo { + if (self.catch_type == 0) return null; + const catch_type = self.catch_type - 1; + if (catch_type >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[catch_type]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; + return cp_info.?; + } }; pub fn parse(input: *std.Io.Reader, constant_pool: []const ?ConstantPoolInfo, allocator: std.mem.Allocator) (ParseError || ResolveError)!Self { From 41aad5e7b81f632248a51b40b9ccfd3661a22965 Mon Sep 17 00:00:00 2001 From: ktkk Date: Sun, 5 Jul 2026 16:08:32 +0200 Subject: [PATCH 11/31] Format source debug extension attribute --- src/Class/AttributeInfo.zig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Class/AttributeInfo.zig b/src/Class/AttributeInfo.zig index 9e13aa0..3fd70ba 100644 --- a/src/Class/AttributeInfo.zig +++ b/src/Class/AttributeInfo.zig @@ -393,7 +393,10 @@ pub const AttributeInfo = union(enum) { _ = try w.write("source_file:\n"); try source_file.indentedFormat(w, depth, indent + 1, constant_pool); }, - //.source_debug_extension, + .source_debug_extension => |attr| { + try w.splatByteAll(' ', depth * indent); + try w.print("debug_extension: {s}\n", .{ attr.debug_extension }); + }, //.line_number_table, //.local_variable_table, //.local_variable_type_table, From 5c816759bb66364072bcb1ca00f60d2cc58a9c94 Mon Sep 17 00:00:00 2001 From: ktkk Date: Sun, 5 Jul 2026 19:54:34 +0200 Subject: [PATCH 12/31] Format line number table attribute --- src/Class/AttributeInfo.zig | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/Class/AttributeInfo.zig b/src/Class/AttributeInfo.zig index 3fd70ba..f7a8e59 100644 --- a/src/Class/AttributeInfo.zig +++ b/src/Class/AttributeInfo.zig @@ -397,7 +397,20 @@ pub const AttributeInfo = union(enum) { try w.splatByteAll(' ', depth * indent); try w.print("debug_extension: {s}\n", .{ attr.debug_extension }); }, - //.line_number_table, + .line_number_table => |attr| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("line_number_table:\n"); + var line_number_indent = indent; + for (attr.line_number_table, 0..) |*line_number, i| { + line_number_indent += 1; + defer line_number_indent -= 1; + + try w.splatByteAll(' ', depth * line_number_indent); + try w.print("{d}:\n", .{ i }); + + try line_number.indentedFormat(w, depth, line_number_indent + 1); + } + }, //.local_variable_table, //.local_variable_type_table, .deprecated => {}, @@ -1068,6 +1081,19 @@ pub const LineNumberTable = struct { pub const LineNumber = struct { start_pc: u16, line_number: u16, + + pub fn indentedFormat( + self: *const LineNumber, + w: *std.Io.Writer, + depth: usize, + indent: u8, + ) std.Io.Writer.Error!void { + try w.splatByteAll(' ', depth * indent); + try w.print("start_pc: {d}\n", .{ self.start_pc }); + + try w.splatByteAll(' ', depth * indent); + try w.print("line_number: {d}\n", .{ self.line_number }); + } }; pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { From b8c63f726eda7b9cfe740a2a2249e1e7813d7b1f Mon Sep 17 00:00:00 2001 From: ktkk Date: Sun, 5 Jul 2026 20:17:10 +0200 Subject: [PATCH 13/31] Format local variable table attribute --- src/Class/AttributeInfo.zig | 55 ++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/src/Class/AttributeInfo.zig b/src/Class/AttributeInfo.zig index f7a8e59..3403136 100644 --- a/src/Class/AttributeInfo.zig +++ b/src/Class/AttributeInfo.zig @@ -411,7 +411,20 @@ pub const AttributeInfo = union(enum) { try line_number.indentedFormat(w, depth, line_number_indent + 1); } }, - //.local_variable_table, + .local_variable_table => |attr| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("local_variable_table:\n"); + var local_variable_indent = indent; + for (attr.local_variable_table, 0..) |*local_variable, i| { + local_variable_indent += 1; + defer local_variable_indent -= 1; + + try w.splatByteAll(' ', depth * local_variable_indent); + try w.print("{d}:\n", .{ i }); + + try local_variable.indentedFormat(w, depth, local_variable_indent + 1, constant_pool); + } + }, //.local_variable_type_table, .deprecated => {}, //.runtime_visible_annotations, @@ -1128,6 +1141,46 @@ pub const LocalVariableTable = struct { name_index: u16, descriptor_index: u16, index: u16, + + pub fn indentedFormat( + self: *const LocalVariable, + 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("start_pc: {d}\n", .{ self.start_pc }); + + try w.splatByteAll(' ', depth * indent); + try w.print("length: {d}\n", .{ self.length }); + + const name_ = self.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); + + const descriptor_ = self.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); + } + + pub fn name(self: *const LocalVariable, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const name_index = self.name_index - 1; + if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[name_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; + } + + pub fn descriptor(self: *const LocalVariable, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const descriptor_index = self.descriptor_index - 1; + if (descriptor_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[descriptor_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; + } }; pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { From a8c87dc4210f2256425900b4aad58255474ebe5a Mon Sep 17 00:00:00 2001 From: ktkk Date: Sun, 5 Jul 2026 20:29:06 +0200 Subject: [PATCH 14/31] Format local variable type table attribute --- src/Class/AttributeInfo.zig | 55 ++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/src/Class/AttributeInfo.zig b/src/Class/AttributeInfo.zig index 3403136..300d02d 100644 --- a/src/Class/AttributeInfo.zig +++ b/src/Class/AttributeInfo.zig @@ -425,7 +425,20 @@ pub const AttributeInfo = union(enum) { try local_variable.indentedFormat(w, depth, local_variable_indent + 1, constant_pool); } }, - //.local_variable_type_table, + .local_variable_type_table => |attr| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("local_variable_type_table:\n"); + var local_variable_type_indent = indent; + for (attr.local_variable_type_table, 0..) |*local_variable_type, i| { + local_variable_type_indent += 1; + defer local_variable_type_indent -= 1; + + try w.splatByteAll(' ', depth * local_variable_type_indent); + try w.print("{d}:\n", .{ i }); + + try local_variable_type.indentedFormat(w, depth, local_variable_type_indent + 1, constant_pool); + } + }, .deprecated => {}, //.runtime_visible_annotations, //.runtime_invisible_annotations, @@ -1218,6 +1231,46 @@ pub const LocalVariableTypeTable = struct { name_index: u16, signature_index: u16, index: u16, + + pub fn indentedFormat( + self: *const LocalVariableType, + 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("start_pc: {d}\n", .{ self.start_pc }); + + try w.splatByteAll(' ', depth * indent); + try w.print("length: {d}\n", .{ self.length }); + + const name_ = self.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); + + const signature_ = self.signature(constant_pool) catch return error.WriteFailed; + try w.splatByteAll(' ', depth * indent); + _ = try w.write("signature:\n"); + try signature_.indentedFormat(w, depth, indent + 1, constant_pool); + } + + pub fn name(self: *const LocalVariableType, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const name_index = self.name_index - 1; + if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[name_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; + } + + pub fn signature(self: *const LocalVariableType, 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 parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { From 04cf7663af67e272084e5c4164f5e596bf6da97b Mon Sep 17 00:00:00 2001 From: ktkk Date: Sun, 5 Jul 2026 20:54:23 +0200 Subject: [PATCH 15/31] Format bootstrap methods attribute --- src/Class.zig | 7 +++++ src/Class/AttributeInfo.zig | 57 +++++++++++++++++++++++++++++++++++-- 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/src/Class.zig b/src/Class.zig index 458627f..f31ed15 100644 --- a/src/Class.zig +++ b/src/Class.zig @@ -364,6 +364,13 @@ pub const ConstantPoolInfo = union(ConstantPoolInfo.Tag) { invoke_dynamic = 18, module = 19, package = 20, + + pub fn loadable(self: Tag) bool { + return switch (self) { + .integer, .float, .long, .double, .class, .string, .method_handle, .method_type, .dynamic => true, + else => false, + }; + } }; pub const Class = struct { diff --git a/src/Class/AttributeInfo.zig b/src/Class/AttributeInfo.zig index 300d02d..b5b913c 100644 --- a/src/Class/AttributeInfo.zig +++ b/src/Class/AttributeInfo.zig @@ -444,8 +444,22 @@ pub const AttributeInfo = union(enum) { //.runtime_invisible_annotations, //.runtime_visible_parameter_annotations, //.runtime_invisible_parameter_annotations, + //.runtime_visible_type_annotations, + //.runtime_invisible_type_annotations, //.annotation_default, - //.bootstrap_methods, + .bootstrap_methods => |attr| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("bootstrap_methods:\n"); + var bootstrap_method_indent = indent; + for (attr.bootstrap_methods, 0..) |bootstrap_method, i| { + bootstrap_method_indent += 1; + defer bootstrap_method_indent -= 1; + + try w.splatByteAll(' ', depth * bootstrap_method_indent); + try w.print("{d}:\n", .{ i }); + try bootstrap_method.indentedFormat(w, depth, bootstrap_method_indent + 1, constant_pool); + } + }, .method_parameters => |attr| { try w.splatByteAll(' ', depth * indent); _ = try w.write("parameters:\n"); @@ -456,7 +470,7 @@ pub const AttributeInfo = union(enum) { try w.splatByteAll(' ', depth * parameter_indent); try w.print("{d}:\n", .{ i }); - try parameter.indentedFormat(w, depth, parameter_indent, constant_pool); + try parameter.indentedFormat(w, depth, parameter_indent + 1, constant_pool); } }, .module => |attr| try attr.indentedFormat(w, depth, indent, constant_pool), @@ -1603,6 +1617,45 @@ pub const BootstrapMethods = struct { pub const BootstrapMethod = struct { bootstrap_method_ref: u16, bootstrap_arguments: []u16, + + pub fn indentedFormat( + self: *const BootstrapMethod, + w: *std.Io.Writer, + depth: usize, + indent: u8, + constant_pool: []const ?ConstantPoolInfo, + ) std.Io.Writer.Error!void { + const bootstrap_method = self.bootstrapMethod(constant_pool) catch return error.WriteFailed; + try w.splatByteAll(' ', depth * indent); + _ = try w.write("bootstrap_method:\n"); + try bootstrap_method.indentedFormat(w, depth, indent + 1, constant_pool); + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("bootstrap_arguments:\n"); + var bootstrap_argument_indent = indent; + for (self.bootstrap_arguments, 0..) |bootstrap_argument, i| { + bootstrap_argument_indent += 1; + defer bootstrap_argument_indent -= 1; + + const index = bootstrap_argument - 1; + + if (index >= constant_pool.len) return error.WriteFailed; + const cp_info = constant_pool[index]; + if (cp_info == null or !@as(ConstantPoolInfo.Tag, cp_info.?).loadable()) return error.WriteFailed; + + try w.splatByteAll(' ', depth * bootstrap_argument_indent); + try w.print("{d}:\n", .{ i }); + try cp_info.?.indentedFormat(w, depth, bootstrap_argument_indent + 1, constant_pool); + } + } + + pub fn bootstrapMethod(self: *const BootstrapMethod, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const bootstrap_method_ref = self.bootstrap_method_ref - 1; + if (bootstrap_method_ref >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[bootstrap_method_ref]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .method_handle) return ResolveError.InvalidConstantType; + return cp_info.?; + } }; pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { From 9135e95dbd179e63d861359b34d7a074bbd52cb6 Mon Sep 17 00:00:00 2001 From: ktkk Date: Sun, 5 Jul 2026 21:28:53 +0200 Subject: [PATCH 16/31] Refactor constant pool --- src/Class.zig | 577 +---------------------------------- src/Class/AttributeInfo.zig | 162 +++++----- src/Class/ConstantPool.zig | 581 ++++++++++++++++++++++++++++++++++++ src/Class/FieldInfo.zig | 14 +- src/Class/MethodInfo.zig | 14 +- 5 files changed, 689 insertions(+), 659 deletions(-) create mode 100644 src/Class/ConstantPool.zig diff --git a/src/Class.zig b/src/Class.zig index f31ed15..a8719b9 100644 --- a/src/Class.zig +++ b/src/Class.zig @@ -5,13 +5,14 @@ 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").AttributeInfo; +pub const ConstantPool = @import("Class/ConstantPool.zig"); allocator: std.mem.Allocator, magic: u32, minor_version: u16, major_version: u16, -constant_pool: []?ConstantPoolInfo, +constant_pool: []?ConstantPool.Info, access_flags: ClassAccessFlags, this_class: u16, super_class: u16, @@ -109,16 +110,16 @@ pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) (ParseError || }; } -fn parseConstantPool(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError![]?ConstantPoolInfo { +fn parseConstantPool(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError![]?ConstantPool.Info { const constant_pool_size = try input.takeInt(u16, .big); - const constant_pool = try allocator.alloc(?ConstantPoolInfo, constant_pool_size - 1); + const constant_pool = try allocator.alloc(?ConstantPool.Info, constant_pool_size - 1); errdefer allocator.free(constant_pool); var i: usize = 0; while (i < constant_pool_size - 1) { defer i += 1; - const cp_info = try ConstantPoolInfo.parse(input, allocator); + const cp_info = try ConstantPool.Info.parse(input, allocator); constant_pool[i] = cp_info; switch (cp_info) { @@ -145,7 +146,7 @@ fn parseInterfaces(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseErr return interfaces; } -fn parseFields(input: *std.Io.Reader, constant_pool: []const ?ConstantPoolInfo, allocator: std.mem.Allocator) (ParseError || ResolveError)![]FieldInfo { +fn parseFields(input: *std.Io.Reader, constant_pool: []const ?ConstantPool.Info, 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); @@ -157,7 +158,7 @@ fn parseFields(input: *std.Io.Reader, constant_pool: []const ?ConstantPoolInfo, return fields; } -fn parseMethods(input: *std.Io.Reader, constant_pool: []const ?ConstantPoolInfo, allocator: std.mem.Allocator) (ParseError || ResolveError)![]MethodInfo { +fn parseMethods(input: *std.Io.Reader, constant_pool: []const ?ConstantPool.Info, 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); @@ -169,7 +170,7 @@ fn parseMethods(input: *std.Io.Reader, constant_pool: []const ?ConstantPoolInfo, return methods; } -pub fn parseAttributes(input: *std.Io.Reader, constant_pool: []const ?ConstantPoolInfo, allocator: std.mem.Allocator) (ParseError || ResolveError)![]AttributeInfo { +pub fn parseAttributes(input: *std.Io.Reader, constant_pool: []const ?ConstantPool.Info, 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); @@ -259,7 +260,7 @@ pub fn format(self: *const Self, w: *std.Io.Writer) std.Io.Writer.Error!void { if (interface >= self.constant_pool.len) return error.WriteFailed; const cp_info = self.constant_pool[interface - 1]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return error.WriteFailed; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .class) return error.WriteFailed; try w.splatByteAll(' ', depth * indent); try w.print("{d}:\n", .{ i }); @@ -297,15 +298,15 @@ pub fn format(self: *const Self, w: *std.Io.Writer) std.Io.Writer.Error!void { } } -pub fn thisClass(self: *const Self) ResolveError!ConstantPoolInfo { +pub fn thisClass(self: *const Self) ResolveError!ConstantPool.Info { const this_class = self.this_class - 1; if (this_class >= self.constant_pool.len) return ResolveError.InvalidConstantPoolIndex; const cp_info = self.constant_pool[this_class]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; return cp_info.?; } -pub fn superClass(self: *const Self) ResolveError!?ConstantPoolInfo { +pub fn superClass(self: *const Self) ResolveError!?ConstantPool.Info { if (self.super_class == 0) { const this_class = (try self.thisClass()).class; const this_class_name = (try this_class.name(self.constant_pool)).utf8; @@ -315,7 +316,7 @@ pub fn superClass(self: *const Self) ResolveError!?ConstantPoolInfo { const super_class = self.super_class - 1; if (super_class >= self.constant_pool.len) return ResolveError.InvalidConstantPoolIndex; const cp_info = self.constant_pool[super_class]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; if (self.access_flags.contains(.interface)) { const super_class_name = (try cp_info.?.class.name(self.constant_pool)).utf8; @@ -326,555 +327,3 @@ pub fn superClass(self: *const Self) ResolveError!?ConstantPoolInfo { return cp_info.?; } } - -pub const ConstantPoolInfo = union(ConstantPoolInfo.Tag) { - class: Class, - field_ref: FieldRef, - method_ref: MethodRef, - interface_method_ref: InterfaceMethodRef, - string: String, - integer: Integer, - float: Float, - long: Long, - double: Double, - name_and_type: NameAndType, - utf8: Utf8, - method_handle: MethodHandle, - method_type: MethodType, - dynamic: Dynamic, - invoke_dynamic: InvokeDynamic, - module: Module, - package: Package, - - pub const Tag = enum(u8) { - class = 7, - field_ref = 9, - method_ref = 10, - interface_method_ref = 11, - string = 8, - integer = 3, - float = 4, - long = 5, - double = 6, - name_and_type = 12, - utf8 = 1, - method_handle = 15, - method_type = 16, - dynamic = 17, - invoke_dynamic = 18, - module = 19, - package = 20, - - pub fn loadable(self: Tag) bool { - return switch (self) { - .integer, .float, .long, .double, .class, .string, .method_handle, .method_type, .dynamic => true, - else => false, - }; - } - }; - - pub const Class = struct { - name_index: u16, - - pub fn name(self: *const Class, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { - const name_index = self.name_index - 1; - if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[name_index]; - if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; - } - }; - - pub const FieldRef = struct { - class_index: u16, - name_and_type_index: u16, - - pub fn class(self: *const FieldRef, 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(Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; - return cp_info.?; - } - - pub fn nameAndType(self: *const FieldRef, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { - const name_and_type_index = self.name_and_type_index - 1; - if (name_and_type_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[name_and_type_index]; - if (cp_info == null or @as(Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType; - return cp_info.?; - } - }; - - pub const MethodRef = struct { - class_index: u16, - name_and_type_index: u16, - - pub fn class(self: *const MethodRef, 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(Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; - return cp_info.?; - } - - pub fn nameAndType(self: *const MethodRef, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { - const name_and_type_index = self.name_and_type_index - 1; - if (name_and_type_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[name_and_type_index]; - if (cp_info == null or @as(Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType; - return cp_info.?; - } - }; - - pub const InterfaceMethodRef = struct { - class_index: u16, - name_and_type_index: u16, - - pub fn class(self: *const InterfaceMethodRef, 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(Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; - return cp_info.?; - } - - pub fn nameAndType(self: *const InterfaceMethodRef, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { - const name_and_type_index = self.name_and_type_index - 1; - if (name_and_type_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[name_and_type_index]; - if (cp_info == null or @as(Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType; - return cp_info.?; - } - }; - - pub const String = struct { - string_index: u16, - - pub fn string(self: *const String, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { - const string_index = self.string_index - 1; - if (string_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[string_index]; - if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; - } - }; - - pub const Integer = struct { - bytes: u32, - }; - - pub const Float = struct { - bytes: u32, - }; - - pub const Long = struct { - high_bytes: u32, - low_bytes: u32, - }; - - pub const Double = struct { - high_bytes: u32, - low_bytes: u32, - }; - - pub const NameAndType = struct { - name_index: u16, - descriptor_index: u16, - - pub fn name(self: *const NameAndType, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { - const name_index = self.name_index - 1; - if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[name_index]; - if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; - } - - pub fn descriptor(self: *const NameAndType, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { - const descriptor_index = self.descriptor_index - 1; - if (descriptor_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[descriptor_index]; - if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; - } - }; - - pub const Utf8 = struct { - bytes: []const u8, - }; - - pub const MethodHandle = struct { - reference_kind: Kind, - reference_index: u16, - - pub const Kind = enum(u8) { - get_field = 1, - get_static = 2, - put_field = 3, - put_static = 4, - invoke_virtual = 5, - invoke_static = 6, - invoke_special = 7, - new_invoke_special = 8, - invoke_interface = 9, - }; - - pub fn reference(self: *const MethodHandle, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { - const reference_index = self.reference_index - 1; - if (reference_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[reference_index]; - return switch (self.reference_kind) { - .get_field, .get_static, .put_field, .put_static => blk: { - if (cp_info == null or @as(Tag, cp_info.?) != .field_ref) break :blk ResolveError.InvalidConstantType; - break :blk cp_info.?; - }, - .invoke_virtual, .invoke_static, .invoke_special, .new_invoke_special => blk: { - if (cp_info == null or @as(Tag, cp_info.?) != .method_ref) break :blk ResolveError.InvalidConstantType; - break :blk cp_info.?; - }, - .invoke_interface => blk: { - if (cp_info == null or @as(Tag, cp_info.?) != .interface_method_ref) break :blk ResolveError.InvalidConstantType; - break :blk cp_info.?; - }, - }; - } - }; - - pub const MethodType = struct { - descriptor_index: u16, - - pub fn descriptor(self: *const MethodType, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { - const descriptor_index = self.descriptor_index - 1; - if (descriptor_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[descriptor_index]; - if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; - } - }; - - pub const Dynamic = struct { - bootstrap_method_attr_index: u16, - name_and_type_index: u16, - - pub fn nameAndType(self: *const Dynamic, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { - const name_and_type_index = self.name_and_type_index - 1; - if (name_and_type_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[name_and_type_index]; - if (cp_info == null or @as(Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType; - return cp_info.?; - } - }; - - pub const InvokeDynamic = struct { - bootstrap_method_attr_index: u16, - name_and_type_index: u16, - - pub fn nameAndType(self: *const InvokeDynamic, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { - const name_and_type_index = self.name_and_type_index - 1; - if (name_and_type_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[name_and_type_index]; - if (cp_info == null or @as(Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType; - return cp_info.?; - } - }; - - pub const Module = struct { - name_index: u16, - - pub fn name(self: *const Module, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { - const name_index = self.name_index - 1; - if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[name_index]; - if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; - } - }; - - pub const Package = struct { - name_index: u16, - - pub fn name(self: *const Package, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { - const name_index = self.name_index - 1; - if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[name_index]; - if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; - } - }; - - pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!ConstantPoolInfo { - const tag_byte = try input.takeByte(); - const tag = std.enums.fromInt(Tag, tag_byte) orelse return ParseError.InvalidTag; - - return switch (tag) { - .class => .{ - .class = .{ - .name_index = try input.takeInt(u16, .big), - }, - }, - .field_ref => .{ - .field_ref = .{ - .class_index = try input.takeInt(u16, .big), - .name_and_type_index = try input.takeInt(u16, .big), - }, - }, - .method_ref => .{ - .method_ref = .{ - .class_index = try input.takeInt(u16, .big), - .name_and_type_index = try input.takeInt(u16, .big), - }, - }, - .interface_method_ref => .{ - .interface_method_ref = .{ - .class_index = try input.takeInt(u16, .big), - .name_and_type_index = try input.takeInt(u16, .big), - }, - }, - .string => .{ - .string = .{ - .string_index = try input.takeInt(u16, .big), - }, - }, - .integer => .{ - .integer = .{ - .bytes = try input.takeInt(u32, .big), - }, - }, - .float => .{ - .integer = .{ - .bytes = try input.takeInt(u32, .big), - }, - }, - .long => .{ - .long = .{ - .high_bytes = try input.takeInt(u32, .big), - .low_bytes = try input.takeInt(u32, .big), - }, - }, - .double => .{ - .double = .{ - .high_bytes = try input.takeInt(u32, .big), - .low_bytes = try input.takeInt(u32, .big), - }, - }, - .name_and_type => .{ - .name_and_type = .{ - .name_index = try input.takeInt(u16, .big), - .descriptor_index = try input.takeInt(u16, .big), - }, - }, - .utf8 => blk: { - const length = try input.takeInt(u16, .big); - const bytes = try input.readAlloc(allocator, length); - - break :blk .{ - .utf8 = .{ - .bytes = bytes, - }, - }; - }, - .method_handle => blk: { - const kind_byte = try input.takeByte(); - const kind = std.enums.fromInt(MethodHandle.Kind, kind_byte) orelse return ParseError.InvalidTag; - - break :blk .{ - .method_handle = .{ - .reference_kind = kind, - .reference_index = try input.takeInt(u16, .big), - }, - }; - }, - .method_type => .{ - .method_type = .{ - .descriptor_index = try input.takeInt(u16, .big), - }, - }, - .dynamic => .{ - .dynamic = .{ - .bootstrap_method_attr_index = try input.takeInt(u16, .big), - .name_and_type_index = try input.takeInt(u16, .big), - }, - }, - .invoke_dynamic => .{ - .invoke_dynamic = .{ - .bootstrap_method_attr_index = try input.takeInt(u16, .big), - .name_and_type_index = try input.takeInt(u16, .big), - }, - }, - .module => .{ - .module = .{ - .name_index = try input.takeInt(u16, .big), - }, - }, - .package => .{ - .package = .{ - .name_index = try input.takeInt(u16, .big), - }, - }, - }; - } - - pub fn deinit(self: *ConstantPoolInfo, allocator: std.mem.Allocator) void { - switch (self.*) { - .utf8 => |info| allocator.free(info.bytes), - else => {}, - } - } - - pub fn format(self: *const ConstantPoolInfo, w: *std.Io.Writer) std.Io.Writer.Error!void { - switch (self.*) { - .class => |class| try w.print("#{d}", .{ class.name_index }), - .field_ref => |field_ref| try w.print("#{d}.#{d}", .{ field_ref.class_index, field_ref.name_and_type_index }), - .method_ref => |method_ref| try w.print("#{d}.#{d}", .{ method_ref.class_index, method_ref.name_and_type_index }), - .interface_method_ref => |interface_method_ref| try w.print("#{d}.#{d}", .{ interface_method_ref.class_index, interface_method_ref.name_and_type_index }), - .string => |string| try w.print("#{d}", .{ string.string_index }), - .integer => |integer| try w.print("{d}", .{ integer.bytes }), - .float => |float| try w.print("{d}", .{ @as(f32, @bitCast(float.bytes)) }), - .long => |long| try w.print("{d}", .{ (@as(u64, long.high_bytes) << 32) | long.low_bytes }), - .double => |double| try w.print("{d}", .{ @as(f64, @bitCast((@as(u64, double.high_bytes) << 32) | double.low_bytes)) }), - .name_and_type => |name_and_type| try w.print("#{d}:#{d}", .{ name_and_type.name_index, name_and_type.descriptor_index }), - .utf8 => |utf8| try w.print("\"{s}\"", .{ utf8.bytes }), - .method_handle => |method_handle| try w.print("{s} #{d}", .{ @tagName(method_handle.reference_kind), method_handle.reference_index }), - .method_type => |method_type| try w.print("#{d}", .{ method_type.descriptor_index }), - .dynamic => |dynamic| try w.print("#{d} #{d}", .{ dynamic.bootstrap_method_attr_index, dynamic.name_and_type_index }), - .invoke_dynamic => |invoke_dynamic| try w.print("#{d} #{d}", .{ invoke_dynamic.bootstrap_method_attr_index, invoke_dynamic.name_and_type_index }), - .module => |module| try w.print("#{d}", .{ module.name_index }), - .package => |package| try w.print("#{d}", .{ package.name_index }), - } - } - - pub fn indentedFormat( - self: *const ConstantPoolInfo, - 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("constant_value_type: {s}\n", .{ @tagName(self.*) }); - - switch (self.*) { - .class => |class| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("name:\n"); - const class_name = class.name(constant_pool) catch return error.WriteFailed; - try class_name.indentedFormat(w, depth, indent + 1, constant_pool); - }, - .field_ref => |field_ref| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("class:\n"); - const class = field_ref.class(constant_pool) catch return error.WriteFailed; - try class.indentedFormat(w, depth, indent + 1, constant_pool); - - try w.splatByteAll(' ', depth * indent); - _ = try w.write("name_and_type:\n"); - const name_and_type = field_ref.nameAndType(constant_pool) catch return error.WriteFailed; - try name_and_type.indentedFormat(w, depth, indent + 1, constant_pool); - }, - .method_ref => |method_ref| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("class:\n"); - const class = method_ref.class(constant_pool) catch return error.WriteFailed; - try class.indentedFormat(w, depth, indent + 1, constant_pool); - - try w.splatByteAll(' ', depth * indent); - _ = try w.write("name_and_type:\n"); - const name_and_type = method_ref.nameAndType(constant_pool) catch return error.WriteFailed; - try name_and_type.indentedFormat(w, depth, indent + 1, constant_pool); - }, - .interface_method_ref => |interface_method_ref| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("class:\n"); - const class = interface_method_ref.class(constant_pool) catch return error.WriteFailed; - try class.indentedFormat(w, depth, indent + 1, constant_pool); - - try w.splatByteAll(' ', depth * indent); - _ = try w.write("name_and_type:\n"); - const name_and_type = interface_method_ref.nameAndType(constant_pool) catch return error.WriteFailed; - try name_and_type.indentedFormat(w, depth, indent + 1, constant_pool); - }, - .string => |string| { - const s = string.string(constant_pool) catch return error.WriteFailed; - try s.indentedFormat(w, depth, indent + 1, constant_pool); - }, - .integer => |integer| { - try w.splatByteAll(' ', depth * indent); - try w.print("integer: {d}\n", .{ integer.bytes }); - }, - .float => |float| { - const f: f32 = @bitCast(float.bytes); - - try w.splatByteAll(' ', depth * indent); - try w.print("float: {d}\n", .{ f }); - }, - .long => |long| { - const l: u64 = (@as(u64, long.high_bytes) << 32) | long.low_bytes; - - try w.splatByteAll(' ', depth * indent); - try w.print("long: {d}\n", .{ l }); - }, - .double => |double| { - const l: u64 = (@as(u64, double.high_bytes) << 32) | double.low_bytes; - const d: f64 = @bitCast(l); - - try w.splatByteAll(' ', depth * indent); - try w.print("double: {d}\n", .{ d }); - }, - .name_and_type => |name_and_type| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("name:\n"); - const name = name_and_type.name(constant_pool) catch return error.WriteFailed; - try name.indentedFormat(w, depth, indent + 1, constant_pool); - - try w.splatByteAll(' ', depth * indent); - _ = try w.write("descriptor:\n"); - const descriptor = name_and_type.descriptor(constant_pool) catch return error.WriteFailed; - try descriptor.indentedFormat(w, depth, indent + 1, constant_pool); - }, - .utf8 => |utf8| { - try w.splatByteAll(' ', depth * indent); - try w.print("bytes: \"{s}\"\n", .{ utf8.bytes }); - }, - .method_handle => |method_handle| { - try w.splatByteAll(' ', depth * indent); - try w.print("kind: {t}\n", .{ method_handle.reference_kind }); - - try w.splatByteAll(' ', depth * indent); - _ = try w.write("reference:\n"); - const reference = method_handle.reference(constant_pool) catch return error.WriteFailed; - try reference.indentedFormat(w, depth, indent + 1, constant_pool); - }, - .method_type => |method_type| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("descriptor:\n"); - const descriptor = method_type.descriptor(constant_pool) catch return error.WriteFailed; - try descriptor.indentedFormat(w, depth, indent + 1, constant_pool); - }, - .dynamic => |dynamic| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("name_and_type:\n"); - const name_and_type = dynamic.nameAndType(constant_pool) catch return error.WriteFailed; - try name_and_type.indentedFormat(w, depth, indent + 1, constant_pool); - }, - .invoke_dynamic => |invoke_dynamic| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("name_and_type:\n"); - const name_and_type = invoke_dynamic.nameAndType(constant_pool) catch return error.WriteFailed; - try name_and_type.indentedFormat(w, depth, indent + 1, constant_pool); - }, - .module => |module| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("name:\n"); - const name = module.name(constant_pool) catch return error.WriteFailed; - try name.indentedFormat(w, depth, indent + 1, constant_pool); - }, - .package => |package| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("name:\n"); - const name = package.name(constant_pool) catch return error.WriteFailed; - try name.indentedFormat(w, depth, indent + 1, constant_pool); - }, - } - } -}; - diff --git a/src/Class/AttributeInfo.zig b/src/Class/AttributeInfo.zig index b5b913c..ec44f51 100644 --- a/src/Class/AttributeInfo.zig +++ b/src/Class/AttributeInfo.zig @@ -3,7 +3,7 @@ const std = @import("std"); const EnumFlags = @import("../EnumFlags.zig").EnumFlags; const Class = @import("../Class.zig"); -const ConstantPoolInfo = Class.ConstantPoolInfo; +const ConstantPool = Class.ConstantPool; const ParseError = Class.ParseError; const ResolveError = Class.ResolveError; @@ -44,11 +44,11 @@ pub const AttributeInfo = union(enum) { const Self = @This(); - pub fn parse(input: *std.Io.Reader, constant_pool: []const ?ConstantPoolInfo, allocator: std.mem.Allocator) (ParseError || ResolveError)!AttributeInfo { + pub fn parse(input: *std.Io.Reader, constant_pool: []const ?ConstantPool.Info, 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; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; const attribute_name = cp_info.?.utf8; const attribute_length = try input.takeInt(u32, .big); @@ -266,7 +266,7 @@ pub const AttributeInfo = union(enum) { w: *std.Io.Writer, depth: usize, indent: u8, - constant_pool: []const ?ConstantPoolInfo, + constant_pool: []const ?ConstantPool.Info, ) std.Io.Writer.Error!void { switch (self.*) { .new => |new| { @@ -347,7 +347,7 @@ pub const AttributeInfo = union(enum) { if (index >= constant_pool.len) return error.WriteFailed; const cp_info = constant_pool[index]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return error.WriteFailed; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .class) return error.WriteFailed; try w.splatByteAll(' ', depth * exception_indent); try w.print("{d}:\n", .{ i }); @@ -486,7 +486,7 @@ pub const AttributeInfo = union(enum) { if (index >= constant_pool.len) return error.WriteFailed; const cp_info = constant_pool[index]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .package) return error.WriteFailed; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .package) return error.WriteFailed; try w.splatByteAll(' ', depth * package_indent); try w.print("{d}:\n", .{ i }); @@ -517,7 +517,7 @@ pub const AttributeInfo = union(enum) { if (index >= constant_pool.len) return error.WriteFailed; const cp_info = constant_pool[index]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return error.WriteFailed; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .class) return error.WriteFailed; try w.splatByteAll(' ', depth * class_indent); try w.print("{d}:\n", .{ i }); @@ -549,7 +549,7 @@ pub const AttributeInfo = union(enum) { if (index >= constant_pool.len) return error.WriteFailed; const cp_info = constant_pool[index]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return error.WriteFailed; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .class) return error.WriteFailed; try w.splatByteAll(' ', depth * class_indent); try w.print("{d}:\n", .{ i }); @@ -580,11 +580,11 @@ pub const NewAttribute = struct { allocator.free(self.info); } - pub fn attributeName(self: *const Self, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + pub fn attributeName(self: *const Self, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { 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; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; return cp_info.?; } @@ -601,7 +601,7 @@ pub const ConstantValue = struct { }; } - pub fn constantValue(self: *const Self, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + pub fn constantValue(self: *const Self, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { 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]; @@ -631,7 +631,7 @@ pub const Code = struct { w: *std.Io.Writer, depth: usize, indent: u8, - constant_pool: []const ?ConstantPoolInfo, + constant_pool: []const ?ConstantPool.Info, ) std.Io.Writer.Error!void { try w.splatByteAll(' ', depth * indent); try w.print("start_pc: {d}\n", .{ self.start_pc }); @@ -650,17 +650,17 @@ pub const Code = struct { } } - pub fn catchType(self: *const ExceptionHandler, constant_pool: []const ?ConstantPoolInfo) ResolveError!?ConstantPoolInfo { + pub fn catchType(self: *const ExceptionHandler, constant_pool: []const ?ConstantPool.Info) ResolveError!?ConstantPool.Info { if (self.catch_type == 0) return null; const catch_type = self.catch_type - 1; if (catch_type >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; const cp_info = constant_pool[catch_type]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; return cp_info.?; } }; - pub fn parse(input: *std.Io.Reader, constant_pool: []const ?ConstantPoolInfo, allocator: std.mem.Allocator) (ParseError || ResolveError)!Self { + pub fn parse(input: *std.Io.Reader, constant_pool: []const ?ConstantPool.Info, allocator: std.mem.Allocator) (ParseError || ResolveError)!Self { const max_stack = try input.takeInt(u16, .big); const max_locals = try input.takeInt(u16, .big); @@ -947,7 +947,7 @@ pub const InnerClasses = struct { w: *std.Io.Writer, depth: usize, indent: u8, - constant_pool: []const ?ConstantPoolInfo, + constant_pool: []const ?ConstantPool.Info, ) std.Io.Writer.Error!void { try w.splatByteAll(' ', depth * indent); try w.print("flags: {f}\n", .{ self.inner_class_access_flags }); @@ -972,31 +972,31 @@ pub const InnerClasses = struct { } } - pub fn innerClass(self: *const InnerClass, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + pub fn innerClass(self: *const InnerClass, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { 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; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; return cp_info.?; } - pub fn outerClass(self: *const InnerClass, constant_pool: []const ?ConstantPoolInfo) ResolveError!?ConstantPoolInfo { + pub fn outerClass(self: *const InnerClass, constant_pool: []const ?ConstantPool.Info) ResolveError!?ConstantPool.Info { 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; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; return cp_info.?; } - pub fn innerName(self: *const InnerClass, constant_pool: []const ?ConstantPoolInfo) ResolveError!?ConstantPoolInfo { + pub fn innerName(self: *const InnerClass, constant_pool: []const ?ConstantPool.Info) ResolveError!?ConstantPool.Info { 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; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; return cp_info.?; } }; @@ -1037,20 +1037,20 @@ pub const EnclosingMethod = struct { }; } - pub fn class(self: *const EnclosingMethod, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + pub fn class(self: *const EnclosingMethod, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { 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; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; return cp_info.?; } - pub fn method(self: *const EnclosingMethod, constant_pool: []const ?ConstantPoolInfo) ResolveError!?ConstantPoolInfo { + pub fn method(self: *const EnclosingMethod, constant_pool: []const ?ConstantPool.Info) ResolveError!?ConstantPool.Info { 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; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType; return cp_info.?; } }; @@ -1068,11 +1068,11 @@ pub const Signature = struct { }; } - pub fn signature(self: *const Signature, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + pub fn signature(self: *const Signature, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { 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; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; return cp_info.?; } }; @@ -1088,11 +1088,11 @@ pub const SourceFile = struct { }; } - pub fn sourceFile(self: *const SourceFile, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + pub fn sourceFile(self: *const SourceFile, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { 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; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; return cp_info.?; } }; @@ -1174,7 +1174,7 @@ pub const LocalVariableTable = struct { w: *std.Io.Writer, depth: usize, indent: u8, - constant_pool: []const ?ConstantPoolInfo, + constant_pool: []const ?ConstantPool.Info, ) std.Io.Writer.Error!void { try w.splatByteAll(' ', depth * indent); try w.print("start_pc: {d}\n", .{ self.start_pc }); @@ -1193,19 +1193,19 @@ pub const LocalVariableTable = struct { try descriptor_.indentedFormat(w, depth, indent + 1, constant_pool); } - pub fn name(self: *const LocalVariable, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + pub fn name(self: *const LocalVariable, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { const name_index = self.name_index - 1; if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; const cp_info = constant_pool[name_index]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; return cp_info.?; } - pub fn descriptor(self: *const LocalVariable, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + pub fn descriptor(self: *const LocalVariable, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { const descriptor_index = self.descriptor_index - 1; if (descriptor_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; const cp_info = constant_pool[descriptor_index]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; return cp_info.?; } }; @@ -1251,7 +1251,7 @@ pub const LocalVariableTypeTable = struct { w: *std.Io.Writer, depth: usize, indent: u8, - constant_pool: []const ?ConstantPoolInfo, + constant_pool: []const ?ConstantPool.Info, ) std.Io.Writer.Error!void { try w.splatByteAll(' ', depth * indent); try w.print("start_pc: {d}\n", .{ self.start_pc }); @@ -1270,19 +1270,19 @@ pub const LocalVariableTypeTable = struct { try signature_.indentedFormat(w, depth, indent + 1, constant_pool); } - pub fn name(self: *const LocalVariableType, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + pub fn name(self: *const LocalVariableType, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { const name_index = self.name_index - 1; if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; const cp_info = constant_pool[name_index]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; return cp_info.?; } - pub fn signature(self: *const LocalVariableType, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + pub fn signature(self: *const LocalVariableType, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { 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; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; return cp_info.?; } }; @@ -1623,7 +1623,7 @@ pub const BootstrapMethods = struct { w: *std.Io.Writer, depth: usize, indent: u8, - constant_pool: []const ?ConstantPoolInfo, + constant_pool: []const ?ConstantPool.Info, ) std.Io.Writer.Error!void { const bootstrap_method = self.bootstrapMethod(constant_pool) catch return error.WriteFailed; try w.splatByteAll(' ', depth * indent); @@ -1641,7 +1641,7 @@ pub const BootstrapMethods = struct { if (index >= constant_pool.len) return error.WriteFailed; const cp_info = constant_pool[index]; - if (cp_info == null or !@as(ConstantPoolInfo.Tag, cp_info.?).loadable()) return error.WriteFailed; + if (cp_info == null or !@as(ConstantPool.Info.Tag, cp_info.?).loadable()) return error.WriteFailed; try w.splatByteAll(' ', depth * bootstrap_argument_indent); try w.print("{d}:\n", .{ i }); @@ -1649,11 +1649,11 @@ pub const BootstrapMethods = struct { } } - pub fn bootstrapMethod(self: *const BootstrapMethod, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + pub fn bootstrapMethod(self: *const BootstrapMethod, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { const bootstrap_method_ref = self.bootstrap_method_ref - 1; if (bootstrap_method_ref >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; const cp_info = constant_pool[bootstrap_method_ref]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .method_handle) return ResolveError.InvalidConstantType; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .method_handle) return ResolveError.InvalidConstantType; return cp_info.?; } }; @@ -1711,7 +1711,7 @@ pub const MethodParameters = struct { w: *std.Io.Writer, depth: usize, indent: u8, - constant_pool: []const ?ConstantPoolInfo, + constant_pool: []const ?ConstantPool.Info, ) std.Io.Writer.Error!void { try w.splatByteAll(' ', depth * indent); try w.print("flags: {f}\n", .{ self.access_flags }); @@ -1722,11 +1722,11 @@ pub const MethodParameters = struct { try name_.indentedFormat(w, depth, indent + 1, constant_pool); } - pub fn name(self: *const Parameter, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + pub fn name(self: *const Parameter, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { const name_index = self.name_index - 1; if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; const cp_info = constant_pool[name_index]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; return cp_info.?; } }; @@ -1787,7 +1787,7 @@ pub const Module = struct { w: *std.Io.Writer, depth: usize, indent: u8, - constant_pool: []const ?ConstantPoolInfo, + constant_pool: []const ?ConstantPool.Info, ) std.Io.Writer.Error!void { const requires_ = self.requires(constant_pool) catch return error.WriteFailed; try w.splatByteAll(' ', depth * indent); @@ -1805,20 +1805,20 @@ pub const Module = struct { } } - pub fn requires(self: *const Requires, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + pub fn requires(self: *const Requires, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { const requires_index = self.requires_index - 1; if (requires_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; const cp_info = constant_pool[requires_index]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .module) return ResolveError.InvalidConstantType; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .module) return ResolveError.InvalidConstantType; return cp_info.?; } - pub fn requiresVersion(self: *const Requires, constant_pool: []const ?ConstantPoolInfo) ResolveError!?ConstantPoolInfo { + pub fn requiresVersion(self: *const Requires, constant_pool: []const ?ConstantPool.Info) ResolveError!?ConstantPool.Info { if (self.requires_version_index == 0) return null; const requires_version_index = self.requires_version_index - 1; if (requires_version_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; const cp_info = constant_pool[requires_version_index]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; return cp_info.?; } }; @@ -1838,7 +1838,7 @@ pub const Module = struct { w: *std.Io.Writer, depth: usize, indent: u8, - constant_pool: []const ?ConstantPoolInfo, + constant_pool: []const ?ConstantPool.Info, ) std.Io.Writer.Error!void { const exports_ = self.exports(constant_pool) catch return error.WriteFailed; try w.splatByteAll(' ', depth * indent); @@ -1859,7 +1859,7 @@ pub const Module = struct { if (index >= constant_pool.len) return error.WriteFailed; const cp_info = constant_pool[index]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .module) return error.WriteFailed; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .module) return error.WriteFailed; try w.splatByteAll(' ', depth * exports_to_indent); try w.print("{d}:\n", .{ i }); @@ -1867,11 +1867,11 @@ pub const Module = struct { } } - pub fn exports(self: *const Exports, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + pub fn exports(self: *const Exports, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { const exports_index = self.exports_index - 1; if (exports_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; const cp_info = constant_pool[exports_index]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .package) return ResolveError.InvalidConstantType; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .package) return ResolveError.InvalidConstantType; return cp_info.?; } }; @@ -1891,7 +1891,7 @@ pub const Module = struct { w: *std.Io.Writer, depth: usize, indent: u8, - constant_pool: []const ?ConstantPoolInfo, + constant_pool: []const ?ConstantPool.Info, ) std.Io.Writer.Error!void { const opens_ = self.opens(constant_pool) catch return error.WriteFailed; try w.splatByteAll(' ', depth * indent); @@ -1912,7 +1912,7 @@ pub const Module = struct { if (index >= constant_pool.len) return error.WriteFailed; const cp_info = constant_pool[index]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .module) return error.WriteFailed; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .module) return error.WriteFailed; try w.splatByteAll(' ', depth * opens_to_indent); try w.print("{d}:\n", .{ i }); @@ -1920,11 +1920,11 @@ pub const Module = struct { } } - pub fn opens(self: *const Opens, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + pub fn opens(self: *const Opens, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { const opens_index = self.opens_index - 1; if (opens_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; const cp_info = constant_pool[opens_index]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .package) return ResolveError.InvalidConstantType; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .package) return ResolveError.InvalidConstantType; return cp_info.?; } }; @@ -1938,7 +1938,7 @@ pub const Module = struct { w: *std.Io.Writer, depth: usize, indent: u8, - constant_pool: []const ?ConstantPoolInfo, + constant_pool: []const ?ConstantPool.Info, ) std.Io.Writer.Error!void { const provides_ = self.provides(constant_pool) catch return error.WriteFailed; try w.splatByteAll(' ', depth * indent); @@ -1956,7 +1956,7 @@ pub const Module = struct { if (index >= constant_pool.len) return error.WriteFailed; const cp_info = constant_pool[index]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return error.WriteFailed; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .class) return error.WriteFailed; try w.splatByteAll(' ', depth * provides_with_indent); try w.print("{d}:\n", .{ i }); @@ -1964,11 +1964,11 @@ pub const Module = struct { } } - pub fn provides(self: *const Provides, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + pub fn provides(self: *const Provides, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { const provides_index = self.provides_index - 1; if (provides_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; const cp_info = constant_pool[provides_index]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; return cp_info.?; } }; @@ -2088,7 +2088,7 @@ pub const Module = struct { w: *std.Io.Writer, depth: usize, indent: u8, - constant_pool: []const ?ConstantPoolInfo, + constant_pool: []const ?ConstantPool.Info, ) std.Io.Writer.Error!void { const module_name = self.moduleName(constant_pool) catch return error.WriteFailed; try w.splatByteAll(' ', depth * indent); @@ -2152,7 +2152,7 @@ pub const Module = struct { if (index >= constant_pool.len) return error.WriteFailed; const cp_info = constant_pool[index]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return error.WriteFailed; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .class) return error.WriteFailed; try w.splatByteAll(' ', depth * uses_indent); try w.print("{d}:\n", .{ i }); @@ -2172,20 +2172,20 @@ pub const Module = struct { } } - pub fn moduleName(self: *const Module, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + pub fn moduleName(self: *const Module, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { const module_name_index = self.module_name_index - 1; if (module_name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; const cp_info = constant_pool[module_name_index]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; return cp_info.?; } - pub fn moduleVersion(self: *const Module, constant_pool: []const ?ConstantPoolInfo) ResolveError!?ConstantPoolInfo { + pub fn moduleVersion(self: *const Module, constant_pool: []const ?ConstantPool.Info) ResolveError!?ConstantPool.Info { if (self.module_version_index == 0) return null; const module_version_index = self.module_version_index - 1; if (module_version_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; const cp_info = constant_pool[module_version_index]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; return cp_info.?; } }; @@ -2224,11 +2224,11 @@ pub const ModuleMainClass = struct { }; } - pub fn mainClass(self: *const ModuleMainClass, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + pub fn mainClass(self: *const ModuleMainClass, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { const main_class_index = self.main_class_index - 1; if (main_class_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; const cp_info = constant_pool[main_class_index]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; return cp_info.?; } }; @@ -2244,11 +2244,11 @@ pub const NestHost = struct { }; } - pub fn hostClass(self: *const NestHost, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + pub fn hostClass(self: *const NestHost, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { const host_class_index = self.host_class_index - 1; if (host_class_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; const cp_info = constant_pool[host_class_index]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; return cp_info.?; } }; @@ -2285,7 +2285,7 @@ pub const Record = struct { descriptor_index: u16, attributes: []AttributeInfo, - pub fn parse(input: *std.Io.Reader, constant_pool: []const ?ConstantPoolInfo, allocator: std.mem.Allocator) (ParseError || ResolveError)!RecordComponentInfo { + pub fn parse(input: *std.Io.Reader, constant_pool: []const ?ConstantPool.Info, allocator: std.mem.Allocator) (ParseError || ResolveError)!RecordComponentInfo { const name_index = try input.takeInt(u16, .big); const descriptor_index = try input.takeInt(u16, .big); @@ -2315,7 +2315,7 @@ pub const Record = struct { w: *std.Io.Writer, depth: usize, indent: u8, - constant_pool: []const ?ConstantPoolInfo, + constant_pool: []const ?ConstantPool.Info, ) std.Io.Writer.Error!void { const name_ = self.name(constant_pool) catch return error.WriteFailed; try w.splatByteAll(' ', depth * indent); @@ -2341,24 +2341,24 @@ pub const Record = struct { } } - pub fn name(self: *const RecordComponentInfo, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + pub fn name(self: *const RecordComponentInfo, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { const name_index = self.name_index - 1; if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; const cp_info = constant_pool[name_index]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; return cp_info.?; } - pub fn descriptor(self: *const RecordComponentInfo, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + pub fn descriptor(self: *const RecordComponentInfo, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { const descriptor_index = self.descriptor_index - 1; if (descriptor_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; const cp_info = constant_pool[descriptor_index]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; return cp_info.?; } }; - pub fn parse(input: *std.Io.Reader, constant_pool: []const ?ConstantPoolInfo, allocator: std.mem.Allocator) (ParseError || ResolveError)!Self { + pub fn parse(input: *std.Io.Reader, constant_pool: []const ?ConstantPool.Info, allocator: std.mem.Allocator) (ParseError || ResolveError)!Self { const components_count = try input.takeInt(u16, .big); const components = try allocator.alloc(RecordComponentInfo, components_count); for (components) |*component| { diff --git a/src/Class/ConstantPool.zig b/src/Class/ConstantPool.zig new file mode 100644 index 0000000..cdf4063 --- /dev/null +++ b/src/Class/ConstantPool.zig @@ -0,0 +1,581 @@ +const std = @import("std"); + +const Class = @import("../Class.zig"); +const AttributeInfo = Class.AttributeInfo; +const ParseError = Class.ParseError; +const ResolveError = Class.ResolveError; + +pub const Info = union(Info.Tag) { + class: Info.Class, + field_ref: FieldRef, + method_ref: MethodRef, + interface_method_ref: InterfaceMethodRef, + string: String, + integer: Integer, + float: Float, + long: Long, + double: Double, + name_and_type: NameAndType, + utf8: Utf8, + method_handle: MethodHandle, + method_type: MethodType, + dynamic: Dynamic, + invoke_dynamic: InvokeDynamic, + module: Module, + package: Package, + + pub const Tag = enum(u8) { + class = 7, + field_ref = 9, + method_ref = 10, + interface_method_ref = 11, + string = 8, + integer = 3, + float = 4, + long = 5, + double = 6, + name_and_type = 12, + utf8 = 1, + method_handle = 15, + method_type = 16, + dynamic = 17, + invoke_dynamic = 18, + module = 19, + package = 20, + + pub fn loadable(self: Tag) bool { + return switch (self) { + .integer, .float, .long, .double, .class, .string, .method_handle, .method_type, .dynamic => true, + else => false, + }; + } + }; + + pub const Class = struct { + name_index: u16, + + const Self = @This(); + + pub fn name(self: *const Self, constant_pool: []const ?Info) ResolveError!Info { + const name_index = self.name_index - 1; + if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[name_index]; + if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; + } + }; + + pub const FieldRef = struct { + class_index: u16, + name_and_type_index: u16, + + const Self = @This(); + + pub fn class(self: *const Self, constant_pool: []const ?Info) ResolveError!Info { + 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(Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; + return cp_info.?; + } + + pub fn nameAndType(self: *const Self, constant_pool: []const ?Info) ResolveError!Info { + const name_and_type_index = self.name_and_type_index - 1; + if (name_and_type_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[name_and_type_index]; + if (cp_info == null or @as(Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType; + return cp_info.?; + } + }; + + pub const MethodRef = struct { + class_index: u16, + name_and_type_index: u16, + + const Self = @This(); + + pub fn class(self: *const Self, constant_pool: []const ?Info) ResolveError!Info { + 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(Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; + return cp_info.?; + } + + pub fn nameAndType(self: *const Self, constant_pool: []const ?Info) ResolveError!Info { + const name_and_type_index = self.name_and_type_index - 1; + if (name_and_type_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[name_and_type_index]; + if (cp_info == null or @as(Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType; + return cp_info.?; + } + }; + + pub const InterfaceMethodRef = struct { + class_index: u16, + name_and_type_index: u16, + + const Self = @This(); + + pub fn class(self: *const Self, constant_pool: []const ?Info) ResolveError!Info { + 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(Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; + return cp_info.?; + } + + pub fn nameAndType(self: *const Self, constant_pool: []const ?Info) ResolveError!Info { + const name_and_type_index = self.name_and_type_index - 1; + if (name_and_type_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[name_and_type_index]; + if (cp_info == null or @as(Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType; + return cp_info.?; + } + }; + + pub const String = struct { + string_index: u16, + + const Self = @This(); + + pub fn string(self: *const Self, constant_pool: []const ?Info) ResolveError!Info { + const string_index = self.string_index - 1; + if (string_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[string_index]; + if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; + } + }; + + pub const Integer = struct { + bytes: u32, + }; + + pub const Float = struct { + bytes: u32, + }; + + pub const Long = struct { + high_bytes: u32, + low_bytes: u32, + }; + + pub const Double = struct { + high_bytes: u32, + low_bytes: u32, + }; + + pub const NameAndType = struct { + name_index: u16, + descriptor_index: u16, + + const Self = @This(); + + pub fn name(self: *const Self, constant_pool: []const ?Info) ResolveError!Info { + const name_index = self.name_index - 1; + if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[name_index]; + if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; + } + + pub fn descriptor(self: *const Self, constant_pool: []const ?Info) ResolveError!Info { + const descriptor_index = self.descriptor_index - 1; + if (descriptor_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[descriptor_index]; + if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; + } + }; + + pub const Utf8 = struct { + bytes: []const u8, + }; + + pub const MethodHandle = struct { + reference_kind: Kind, + reference_index: u16, + + const Self = @This(); + + pub const Kind = enum(u8) { + get_field = 1, + get_static = 2, + put_field = 3, + put_static = 4, + invoke_virtual = 5, + invoke_static = 6, + invoke_special = 7, + new_invoke_special = 8, + invoke_interface = 9, + }; + + pub fn reference(self: *const Self, constant_pool: []const ?Info) ResolveError!Info { + const reference_index = self.reference_index - 1; + if (reference_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[reference_index]; + return switch (self.reference_kind) { + .get_field, .get_static, .put_field, .put_static => blk: { + if (cp_info == null or @as(Tag, cp_info.?) != .field_ref) break :blk ResolveError.InvalidConstantType; + break :blk cp_info.?; + }, + .invoke_virtual, .invoke_static, .invoke_special, .new_invoke_special => blk: { + if (cp_info == null or @as(Tag, cp_info.?) != .method_ref) break :blk ResolveError.InvalidConstantType; + break :blk cp_info.?; + }, + .invoke_interface => blk: { + if (cp_info == null or @as(Tag, cp_info.?) != .interface_method_ref) break :blk ResolveError.InvalidConstantType; + break :blk cp_info.?; + }, + }; + } + }; + + pub const MethodType = struct { + descriptor_index: u16, + + const Self = @This(); + + pub fn descriptor(self: *const Self, constant_pool: []const ?Info) ResolveError!Info { + const descriptor_index = self.descriptor_index - 1; + if (descriptor_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[descriptor_index]; + if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; + } + }; + + pub const Dynamic = struct { + bootstrap_method_attr_index: u16, + name_and_type_index: u16, + + const Self = @This(); + + pub fn nameAndType(self: *const Self, constant_pool: []const ?Info) ResolveError!Info { + const name_and_type_index = self.name_and_type_index - 1; + if (name_and_type_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[name_and_type_index]; + if (cp_info == null or @as(Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType; + return cp_info.?; + } + }; + + pub const InvokeDynamic = struct { + bootstrap_method_attr_index: u16, + name_and_type_index: u16, + + const Self = @This(); + + pub fn nameAndType(self: *const Self, constant_pool: []const ?Info) ResolveError!Info { + const name_and_type_index = self.name_and_type_index - 1; + if (name_and_type_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[name_and_type_index]; + if (cp_info == null or @as(Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType; + return cp_info.?; + } + }; + + pub const Module = struct { + name_index: u16, + + const Self = @This(); + + pub fn name(self: *const Self, constant_pool: []const ?Info) ResolveError!Info { + const name_index = self.name_index - 1; + if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[name_index]; + if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; + } + }; + + pub const Package = struct { + name_index: u16, + + const Self = @This(); + + pub fn name(self: *const Self, constant_pool: []const ?Info) ResolveError!Info { + const name_index = self.name_index - 1; + if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[name_index]; + if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; + } + }; + + pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Info { + const tag_byte = try input.takeByte(); + const tag = std.enums.fromInt(Tag, tag_byte) orelse return ParseError.InvalidTag; + + return switch (tag) { + .class => .{ + .class = .{ + .name_index = try input.takeInt(u16, .big), + }, + }, + .field_ref => .{ + .field_ref = .{ + .class_index = try input.takeInt(u16, .big), + .name_and_type_index = try input.takeInt(u16, .big), + }, + }, + .method_ref => .{ + .method_ref = .{ + .class_index = try input.takeInt(u16, .big), + .name_and_type_index = try input.takeInt(u16, .big), + }, + }, + .interface_method_ref => .{ + .interface_method_ref = .{ + .class_index = try input.takeInt(u16, .big), + .name_and_type_index = try input.takeInt(u16, .big), + }, + }, + .string => .{ + .string = .{ + .string_index = try input.takeInt(u16, .big), + }, + }, + .integer => .{ + .integer = .{ + .bytes = try input.takeInt(u32, .big), + }, + }, + .float => .{ + .integer = .{ + .bytes = try input.takeInt(u32, .big), + }, + }, + .long => .{ + .long = .{ + .high_bytes = try input.takeInt(u32, .big), + .low_bytes = try input.takeInt(u32, .big), + }, + }, + .double => .{ + .double = .{ + .high_bytes = try input.takeInt(u32, .big), + .low_bytes = try input.takeInt(u32, .big), + }, + }, + .name_and_type => .{ + .name_and_type = .{ + .name_index = try input.takeInt(u16, .big), + .descriptor_index = try input.takeInt(u16, .big), + }, + }, + .utf8 => blk: { + const length = try input.takeInt(u16, .big); + const bytes = try input.readAlloc(allocator, length); + + break :blk .{ + .utf8 = .{ + .bytes = bytes, + }, + }; + }, + .method_handle => blk: { + const kind_byte = try input.takeByte(); + const kind = std.enums.fromInt(MethodHandle.Kind, kind_byte) orelse return ParseError.InvalidTag; + + break :blk .{ + .method_handle = .{ + .reference_kind = kind, + .reference_index = try input.takeInt(u16, .big), + }, + }; + }, + .method_type => .{ + .method_type = .{ + .descriptor_index = try input.takeInt(u16, .big), + }, + }, + .dynamic => .{ + .dynamic = .{ + .bootstrap_method_attr_index = try input.takeInt(u16, .big), + .name_and_type_index = try input.takeInt(u16, .big), + }, + }, + .invoke_dynamic => .{ + .invoke_dynamic = .{ + .bootstrap_method_attr_index = try input.takeInt(u16, .big), + .name_and_type_index = try input.takeInt(u16, .big), + }, + }, + .module => .{ + .module = .{ + .name_index = try input.takeInt(u16, .big), + }, + }, + .package => .{ + .package = .{ + .name_index = try input.takeInt(u16, .big), + }, + }, + }; + } + + pub fn deinit(self: *Info, allocator: std.mem.Allocator) void { + switch (self.*) { + .utf8 => |info| allocator.free(info.bytes), + else => {}, + } + } + + pub fn format(self: *const Info, w: *std.Io.Writer) std.Io.Writer.Error!void { + switch (self.*) { + .class => |class| try w.print("#{d}", .{ class.name_index }), + .field_ref => |field_ref| try w.print("#{d}.#{d}", .{ field_ref.class_index, field_ref.name_and_type_index }), + .method_ref => |method_ref| try w.print("#{d}.#{d}", .{ method_ref.class_index, method_ref.name_and_type_index }), + .interface_method_ref => |interface_method_ref| try w.print("#{d}.#{d}", .{ interface_method_ref.class_index, interface_method_ref.name_and_type_index }), + .string => |string| try w.print("#{d}", .{ string.string_index }), + .integer => |integer| try w.print("{d}", .{ integer.bytes }), + .float => |float| try w.print("{d}", .{ @as(f32, @bitCast(float.bytes)) }), + .long => |long| try w.print("{d}", .{ (@as(u64, long.high_bytes) << 32) | long.low_bytes }), + .double => |double| try w.print("{d}", .{ @as(f64, @bitCast((@as(u64, double.high_bytes) << 32) | double.low_bytes)) }), + .name_and_type => |name_and_type| try w.print("#{d}:#{d}", .{ name_and_type.name_index, name_and_type.descriptor_index }), + .utf8 => |utf8| try w.print("\"{s}\"", .{ utf8.bytes }), + .method_handle => |method_handle| try w.print("{s} #{d}", .{ @tagName(method_handle.reference_kind), method_handle.reference_index }), + .method_type => |method_type| try w.print("#{d}", .{ method_type.descriptor_index }), + .dynamic => |dynamic| try w.print("#{d} #{d}", .{ dynamic.bootstrap_method_attr_index, dynamic.name_and_type_index }), + .invoke_dynamic => |invoke_dynamic| try w.print("#{d} #{d}", .{ invoke_dynamic.bootstrap_method_attr_index, invoke_dynamic.name_and_type_index }), + .module => |module| try w.print("#{d}", .{ module.name_index }), + .package => |package| try w.print("#{d}", .{ package.name_index }), + } + } + + pub fn indentedFormat( + self: *const Info, + w: *std.Io.Writer, + depth: usize, + indent: u8, + constant_pool: []const ?Info, + ) std.Io.Writer.Error!void { + try w.splatByteAll(' ', depth * indent); + try w.print("constant_value_type: {s}\n", .{ @tagName(self.*) }); + + switch (self.*) { + .class => |class| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("name:\n"); + const class_name = class.name(constant_pool) catch return error.WriteFailed; + try class_name.indentedFormat(w, depth, indent + 1, constant_pool); + }, + .field_ref => |field_ref| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("class:\n"); + const class = field_ref.class(constant_pool) catch return error.WriteFailed; + try class.indentedFormat(w, depth, indent + 1, constant_pool); + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("name_and_type:\n"); + const name_and_type = field_ref.nameAndType(constant_pool) catch return error.WriteFailed; + try name_and_type.indentedFormat(w, depth, indent + 1, constant_pool); + }, + .method_ref => |method_ref| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("class:\n"); + const class = method_ref.class(constant_pool) catch return error.WriteFailed; + try class.indentedFormat(w, depth, indent + 1, constant_pool); + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("name_and_type:\n"); + const name_and_type = method_ref.nameAndType(constant_pool) catch return error.WriteFailed; + try name_and_type.indentedFormat(w, depth, indent + 1, constant_pool); + }, + .interface_method_ref => |interface_method_ref| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("class:\n"); + const class = interface_method_ref.class(constant_pool) catch return error.WriteFailed; + try class.indentedFormat(w, depth, indent + 1, constant_pool); + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("name_and_type:\n"); + const name_and_type = interface_method_ref.nameAndType(constant_pool) catch return error.WriteFailed; + try name_and_type.indentedFormat(w, depth, indent + 1, constant_pool); + }, + .string => |string| { + const s = string.string(constant_pool) catch return error.WriteFailed; + try s.indentedFormat(w, depth, indent + 1, constant_pool); + }, + .integer => |integer| { + try w.splatByteAll(' ', depth * indent); + try w.print("integer: {d}\n", .{ integer.bytes }); + }, + .float => |float| { + const f: f32 = @bitCast(float.bytes); + + try w.splatByteAll(' ', depth * indent); + try w.print("float: {d}\n", .{ f }); + }, + .long => |long| { + const l: u64 = (@as(u64, long.high_bytes) << 32) | long.low_bytes; + + try w.splatByteAll(' ', depth * indent); + try w.print("long: {d}\n", .{ l }); + }, + .double => |double| { + const l: u64 = (@as(u64, double.high_bytes) << 32) | double.low_bytes; + const d: f64 = @bitCast(l); + + try w.splatByteAll(' ', depth * indent); + try w.print("double: {d}\n", .{ d }); + }, + .name_and_type => |name_and_type| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("name:\n"); + const name = name_and_type.name(constant_pool) catch return error.WriteFailed; + try name.indentedFormat(w, depth, indent + 1, constant_pool); + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("descriptor:\n"); + const descriptor = name_and_type.descriptor(constant_pool) catch return error.WriteFailed; + try descriptor.indentedFormat(w, depth, indent + 1, constant_pool); + }, + .utf8 => |utf8| { + try w.splatByteAll(' ', depth * indent); + try w.print("bytes: \"{s}\"\n", .{ utf8.bytes }); + }, + .method_handle => |method_handle| { + try w.splatByteAll(' ', depth * indent); + try w.print("kind: {t}\n", .{ method_handle.reference_kind }); + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("reference:\n"); + const reference = method_handle.reference(constant_pool) catch return error.WriteFailed; + try reference.indentedFormat(w, depth, indent + 1, constant_pool); + }, + .method_type => |method_type| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("descriptor:\n"); + const descriptor = method_type.descriptor(constant_pool) catch return error.WriteFailed; + try descriptor.indentedFormat(w, depth, indent + 1, constant_pool); + }, + .dynamic => |dynamic| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("name_and_type:\n"); + const name_and_type = dynamic.nameAndType(constant_pool) catch return error.WriteFailed; + try name_and_type.indentedFormat(w, depth, indent + 1, constant_pool); + }, + .invoke_dynamic => |invoke_dynamic| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("name_and_type:\n"); + const name_and_type = invoke_dynamic.nameAndType(constant_pool) catch return error.WriteFailed; + try name_and_type.indentedFormat(w, depth, indent + 1, constant_pool); + }, + .module => |module| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("name:\n"); + const name = module.name(constant_pool) catch return error.WriteFailed; + try name.indentedFormat(w, depth, indent + 1, constant_pool); + }, + .package => |package| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("name:\n"); + const name = package.name(constant_pool) catch return error.WriteFailed; + try name.indentedFormat(w, depth, indent + 1, constant_pool); + }, + } + } +}; diff --git a/src/Class/FieldInfo.zig b/src/Class/FieldInfo.zig index 952cf38..12a75a3 100644 --- a/src/Class/FieldInfo.zig +++ b/src/Class/FieldInfo.zig @@ -3,7 +3,7 @@ const std = @import("std"); const EnumFlags = @import("../EnumFlags.zig").EnumFlags; const Class = @import("../Class.zig"); -const ConstantPoolInfo = Class.ConstantPoolInfo; +const ConstantPool = Class.ConstantPool; const AttributeInfo = Class.AttributeInfo; const ResolveError = Class.ResolveError; @@ -26,7 +26,7 @@ pub const FieldAccessFlags = EnumFlags(enum(u16) { @"enum" = 0x4000, }); -pub fn parse(input: *std.Io.Reader, constant_pool: []const ?ConstantPoolInfo, allocator: std.mem.Allocator) !Self { +pub fn parse(input: *std.Io.Reader, constant_pool: []const ?ConstantPool.Info, 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); @@ -51,7 +51,7 @@ pub fn indentedFormat( w: *std.Io.Writer, depth: usize, indent: u8, - constant_pool: []const ?ConstantPoolInfo, + constant_pool: []const ?ConstantPool.Info, ) std.Io.Writer.Error!void { try w.splatByteAll(' ', depth * indent); try w.print("flags: {f}\n", .{ self.access_flags }); @@ -80,19 +80,19 @@ pub fn indentedFormat( } } -pub fn name(self: *const Self, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { +pub fn name(self: *const Self, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { const name_index = self.name_index - 1; if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; const cp_info = constant_pool[name_index]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; return cp_info.?; } -pub fn descriptor(self: *const Self, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { +pub fn descriptor(self: *const Self, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { const descriptor_index = self.descriptor_index - 1; if (descriptor_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; const cp_info = constant_pool[descriptor_index]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; return cp_info.?; } diff --git a/src/Class/MethodInfo.zig b/src/Class/MethodInfo.zig index a9c3e61..f58a7bf 100644 --- a/src/Class/MethodInfo.zig +++ b/src/Class/MethodInfo.zig @@ -3,7 +3,7 @@ const std = @import("std"); const EnumFlags = @import("../EnumFlags.zig").EnumFlags; const Class = @import("../Class.zig"); -const ConstantPoolInfo = Class.ConstantPoolInfo; +const ConstantPool = Class.ConstantPool; const AttributeInfo = Class.AttributeInfo; const ResolveError = Class.ResolveError; @@ -29,7 +29,7 @@ pub const MethodAccessFlags = EnumFlags(enum(u16) { synthetic = 0x1000, }); -pub fn parse(input: *std.Io.Reader, constant_pool: []const ?ConstantPoolInfo, allocator: std.mem.Allocator) !Self { +pub fn parse(input: *std.Io.Reader, constant_pool: []const ?ConstantPool.Info, 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); @@ -54,7 +54,7 @@ pub fn indentedFormat( w: *std.Io.Writer, depth: usize, indent: u8, - constant_pool: []const ?ConstantPoolInfo, + constant_pool: []const ?ConstantPool.Info, ) std.Io.Writer.Error!void { try w.splatByteAll(' ', depth * indent); try w.print("flags: {f}\n", .{ self.access_flags }); @@ -83,19 +83,19 @@ pub fn indentedFormat( } } -pub fn name(self: *const Self, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { +pub fn name(self: *const Self, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { const name_index = self.name_index - 1; if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; const cp_info = constant_pool[name_index]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; return cp_info.?; } -pub fn descriptor(self: *const Self, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { +pub fn descriptor(self: *const Self, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { const descriptor_index = self.descriptor_index - 1; if (descriptor_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; const cp_info = constant_pool[descriptor_index]; - if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; return cp_info.?; } From 146d90ccec756f1ef93e8b719065b02f8cfb4605 Mon Sep 17 00:00:00 2001 From: ktkk Date: Sun, 5 Jul 2026 23:29:37 +0200 Subject: [PATCH 17/31] Refactor constant pool --- src/Class.zig | 73 ++----- src/Class/AttributeInfo.zig | 397 +++++++++++------------------------- src/Class/ConstantPool.zig | 236 ++++++++++----------- src/Class/FieldInfo.zig | 20 +- src/Class/MethodInfo.zig | 21 +- src/main.zig | 10 - 6 files changed, 257 insertions(+), 500 deletions(-) diff --git a/src/Class.zig b/src/Class.zig index a8719b9..58c02d1 100644 --- a/src/Class.zig +++ b/src/Class.zig @@ -12,7 +12,7 @@ allocator: std.mem.Allocator, magic: u32, minor_version: u16, major_version: u16, -constant_pool: []?ConstantPool.Info, +constant_pool: ConstantPool, access_flags: ClassAccessFlags, this_class: u16, super_class: u16, @@ -54,13 +54,8 @@ pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) (ParseError || const minor_version = try input.takeInt(u16, .big); const major_version = try input.takeInt(u16, .big); - const constant_pool = try parseConstantPool(input, allocator); - errdefer { - for (constant_pool) |*cp_info| { - if (cp_info.*) |*c| c.deinit(allocator); - } - allocator.free(constant_pool); - } + var constant_pool: ConstantPool = try .parse(input, allocator); + errdefer constant_pool.deinit(allocator); const access_flags: ClassAccessFlags = .{ .mask = try input.takeInt(u16, .big) }; @@ -110,30 +105,6 @@ pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) (ParseError || }; } -fn parseConstantPool(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError![]?ConstantPool.Info { - const constant_pool_size = try input.takeInt(u16, .big); - const constant_pool = try allocator.alloc(?ConstantPool.Info, constant_pool_size - 1); - errdefer allocator.free(constant_pool); - - var i: usize = 0; - while (i < constant_pool_size - 1) { - defer i += 1; - - const cp_info = try ConstantPool.Info.parse(input, allocator); - constant_pool[i] = cp_info; - - switch (cp_info) { - .long, .double => { - i += 1; - constant_pool[i] = null; - }, - else => {}, - } - } - - return constant_pool; -} - fn parseInterfaces(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError![]u16 { const interfaces_count = try input.takeInt(u16, .big); const interfaces = try allocator.alloc(u16, interfaces_count); @@ -146,7 +117,7 @@ fn parseInterfaces(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseErr return interfaces; } -fn parseFields(input: *std.Io.Reader, constant_pool: []const ?ConstantPool.Info, allocator: std.mem.Allocator) (ParseError || ResolveError)![]FieldInfo { +fn parseFields(input: *std.Io.Reader, constant_pool: ConstantPool, 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); @@ -158,19 +129,19 @@ fn parseFields(input: *std.Io.Reader, constant_pool: []const ?ConstantPool.Info, return fields; } -fn parseMethods(input: *std.Io.Reader, constant_pool: []const ?ConstantPool.Info, allocator: std.mem.Allocator) (ParseError || ResolveError)![]MethodInfo { +fn parseMethods(input: *std.Io.Reader, constant_pool: ConstantPool, 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, constant_pool, allocator); + methods[i] = try .parse(input, constant_pool, allocator); } return methods; } -pub fn parseAttributes(input: *std.Io.Reader, constant_pool: []const ?ConstantPool.Info, allocator: std.mem.Allocator) (ParseError || ResolveError)![]AttributeInfo { +pub fn parseAttributes(input: *std.Io.Reader, constant_pool: ConstantPool, 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); @@ -183,20 +154,13 @@ pub fn parseAttributes(input: *std.Io.Reader, constant_pool: []const ?ConstantPo } pub fn deinit(self: *Self) void { - self.freeConstantPool(); + self.constant_pool.deinit(self.allocator); self.freeInterfaces(); self.freeFields(); self.freeMethods(); self.freeAttributes(); } -fn freeConstantPool(self: *Self) void { - for (self.constant_pool) |*cp_info| { - if (cp_info.*) |*c| c.deinit(self.allocator); - } - self.allocator.free(self.constant_pool); -} - fn freeInterfaces(self: *Self) void { self.allocator.free(self.interfaces); } @@ -258,13 +222,11 @@ pub fn format(self: *const Self, w: *std.Io.Writer) std.Io.Writer.Error!void { indent += 1; defer indent -= 1; - if (interface >= self.constant_pool.len) return error.WriteFailed; - const cp_info = self.constant_pool[interface - 1]; - if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .class) return error.WriteFailed; + const info = self.constant_pool.getTag(interface, .class) catch return error.WriteFailed; try w.splatByteAll(' ', depth * indent); try w.print("{d}:\n", .{ i }); - try cp_info.?.indentedFormat(w, depth, indent + 1, self.constant_pool); + try info.indentedFormat(w, depth, indent + 1, self.constant_pool); } _ = try w.write("fields:\n"); @@ -299,11 +261,7 @@ pub fn format(self: *const Self, w: *std.Io.Writer) std.Io.Writer.Error!void { } pub fn thisClass(self: *const Self) ResolveError!ConstantPool.Info { - const this_class = self.this_class - 1; - if (this_class >= self.constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = self.constant_pool[this_class]; - if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; - return cp_info.?; + return self.constant_pool.getTag(self.this_class, .class); } pub fn superClass(self: *const Self) ResolveError!?ConstantPool.Info { @@ -313,17 +271,14 @@ pub fn superClass(self: *const Self) ResolveError!?ConstantPool.Info { if (!std.mem.eql(u8, this_class_name.bytes, "java/lang/Object")) return ResolveError.InvalidConstantType; return null; } else { - const super_class = self.super_class - 1; - if (super_class >= self.constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = self.constant_pool[super_class]; - if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; + const super_class = try self.constant_pool.getTag(self.super_class, .class); if (self.access_flags.contains(.interface)) { - const super_class_name = (try cp_info.?.class.name(self.constant_pool)).utf8; + const super_class_name = (try super_class.class.name(self.constant_pool)).utf8; if (!std.mem.eql(u8, super_class_name.bytes, "java/lang/Object")) return ResolveError.InvalidConstantType; } else { // TODO: Assert that superclass does not have ACC_FINAL flag set. } - return cp_info.?; + return super_class; } } diff --git a/src/Class/AttributeInfo.zig b/src/Class/AttributeInfo.zig index ec44f51..0e9540d 100644 --- a/src/Class/AttributeInfo.zig +++ b/src/Class/AttributeInfo.zig @@ -44,12 +44,10 @@ pub const AttributeInfo = union(enum) { const Self = @This(); - pub fn parse(input: *std.Io.Reader, constant_pool: []const ?ConstantPool.Info, 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(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - const attribute_name = cp_info.?.utf8; + pub fn parse(input: *std.Io.Reader, constant_pool: ConstantPool, allocator: std.mem.Allocator) (ParseError || ResolveError)!AttributeInfo { + const attribute_name_index = try input.takeInt(u16, .big); + const attribute_name = try constant_pool.getTag(attribute_name_index, .utf8); + const name = attribute_name.utf8.bytes; const attribute_length = try input.takeInt(u32, .big); @@ -57,169 +55,169 @@ pub const AttributeInfo = union(enum) { var limited_reader: std.Io.Reader.Limited = .init(input, .limited(attribute_length), &limited_buf); const limited = &limited_reader.interface; - if (std.mem.eql(u8, attribute_name.bytes, "ConstantValue")) { + if (std.mem.eql(u8, name, "ConstantValue")) { return .{ .predefined = .{ .constant_value = try ConstantValue.parse(limited), }, }; - } else if (std.mem.eql(u8, attribute_name.bytes, "Code")) { + } else if (std.mem.eql(u8, name, "Code")) { return .{ .predefined = .{ .code = try Code.parse(limited, constant_pool, allocator), }, }; - } else if (std.mem.eql(u8, attribute_name.bytes, "StackMapTable")) { + } else if (std.mem.eql(u8, name, "StackMapTable")) { return .{ .predefined = .{ .stack_map_table = try StackMapTable.parse(limited, allocator), }, }; - } else if (std.mem.eql(u8, attribute_name.bytes, "Exceptions")) { + } else if (std.mem.eql(u8, name, "Exceptions")) { return .{ .predefined = .{ .exceptions = try Exceptions.parse(limited, allocator), }, }; - } else if (std.mem.eql(u8, attribute_name.bytes, "InnerClasses")) { + } else if (std.mem.eql(u8, name, "InnerClasses")) { return .{ .predefined = .{ .inner_classes = try InnerClasses.parse(limited, allocator), }, }; - } else if (std.mem.eql(u8, attribute_name.bytes, "EnclosingMethod")) { + } else if (std.mem.eql(u8, name, "EnclosingMethod")) { return .{ .predefined = .{ .enclosing_method = try EnclosingMethod.parse(limited), }, }; - } else if (std.mem.eql(u8, attribute_name.bytes, "Synthetic")) { + } else if (std.mem.eql(u8, name, "Synthetic")) { return .{ .predefined = .{ .synthetic = .{}, }, }; - } else if (std.mem.eql(u8, attribute_name.bytes, "Signature")) { + } else if (std.mem.eql(u8, name, "Signature")) { return .{ .predefined = .{ .signature = try Signature.parse(limited), }, }; - } else if (std.mem.eql(u8, attribute_name.bytes, "SourceFile")) { + } else if (std.mem.eql(u8, name, "SourceFile")) { return .{ .predefined = .{ .source_file = try SourceFile.parse(limited), }, }; - } else if (std.mem.eql(u8, attribute_name.bytes, "SourceDebugExtension")) { + } else if (std.mem.eql(u8, name, "SourceDebugExtension")) { return .{ .predefined = .{ .source_debug_extension = try SourceDebugExtension.parse(limited, attribute_length, allocator), }, }; - } else if (std.mem.eql(u8, attribute_name.bytes, "LineNumberTable")) { + } else if (std.mem.eql(u8, name, "LineNumberTable")) { return .{ .predefined = .{ .line_number_table = try LineNumberTable.parse(limited, allocator), }, }; - } else if (std.mem.eql(u8, attribute_name.bytes, "LocalVariableTable")) { + } else if (std.mem.eql(u8, name, "LocalVariableTable")) { return .{ .predefined = .{ .local_variable_table = try LocalVariableTable.parse(limited, allocator), }, }; - } else if (std.mem.eql(u8, attribute_name.bytes, "LocalVariableTypeTable")) { + } else if (std.mem.eql(u8, name, "LocalVariableTypeTable")) { return .{ .predefined = .{ .local_variable_type_table = try LocalVariableTypeTable.parse(limited, allocator), }, }; - } else if (std.mem.eql(u8, attribute_name.bytes, "Deprecated")) { + } else if (std.mem.eql(u8, name, "Deprecated")) { return .{ .predefined = .{ .deprecated = .{}, }, }; - } else if (std.mem.eql(u8, attribute_name.bytes, "RuntimeVisibleAnnotations")) { + } else if (std.mem.eql(u8, name, "RuntimeVisibleAnnotations")) { return .{ .predefined = .{ .runtime_visible_annotations = try RuntimeVisibleAnnotations.parse(limited, allocator), }, }; - } else if (std.mem.eql(u8, attribute_name.bytes, "RuntimeInvisibleAnnotations")) { + } else if (std.mem.eql(u8, name, "RuntimeInvisibleAnnotations")) { return .{ .predefined = .{ .runtime_invisible_annotations = try RuntimeInvisibleAnnotations.parse(limited, allocator), }, }; - } else if (std.mem.eql(u8, attribute_name.bytes, "RuntimeVisibleParameterAnnotations")) { + } else if (std.mem.eql(u8, name, "RuntimeVisibleParameterAnnotations")) { return .{ .predefined = .{ .runtime_visible_parameter_annotations = try RuntimeVisibleParameterAnnotations.parse(limited, allocator), }, }; - } else if (std.mem.eql(u8, attribute_name.bytes, "RuntimeInvisibleParameterAnnotations")) { + } else if (std.mem.eql(u8, name, "RuntimeInvisibleParameterAnnotations")) { return .{ .predefined = .{ .runtime_invisible_parameter_annotations = try RuntimeInvisibleParameterAnnotations.parse(limited, allocator), }, }; - } else if (std.mem.eql(u8, attribute_name.bytes, "AnnotationDefault")) { + } else if (std.mem.eql(u8, name, "AnnotationDefault")) { return .{ .predefined = .{ .annotation_default = try AnnotationDefault.parse(limited, allocator), }, }; - } else if (std.mem.eql(u8, attribute_name.bytes, "BootstrapMethods")) { + } else if (std.mem.eql(u8, name, "BootstrapMethods")) { return .{ .predefined = .{ .bootstrap_methods = try BootstrapMethods.parse(limited, allocator), }, }; - } else if (std.mem.eql(u8, attribute_name.bytes, "MethodParameters")) { + } else if (std.mem.eql(u8, name, "MethodParameters")) { return .{ .predefined = .{ .method_parameters = try MethodParameters.parse(limited, allocator), }, }; - } else if (std.mem.eql(u8, attribute_name.bytes, "Module")) { + } else if (std.mem.eql(u8, name, "Module")) { return .{ .predefined = .{ .module = try Module.parse(limited, allocator), }, }; - } else if (std.mem.eql(u8, attribute_name.bytes, "ModulePackages")) { + } else if (std.mem.eql(u8, name, "ModulePackages")) { return .{ .predefined = .{ .module_packages = try ModulePackages.parse(limited, allocator), }, }; - } else if (std.mem.eql(u8, attribute_name.bytes, "ModuleMainClass")) { + } else if (std.mem.eql(u8, name, "ModuleMainClass")) { return .{ .predefined = .{ .module_main_class = try ModuleMainClass.parse(limited), }, }; - } else if (std.mem.eql(u8, attribute_name.bytes, "NestHost")) { + } else if (std.mem.eql(u8, name, "NestHost")) { return .{ .predefined = .{ .nest_host = try NestHost.parse(limited), }, }; - } else if (std.mem.eql(u8, attribute_name.bytes, "NestMembers")) { + } else if (std.mem.eql(u8, name, "NestMembers")) { return .{ .predefined = .{ .nest_members = try NestMembers.parse(limited, allocator), }, }; - } else if (std.mem.eql(u8, attribute_name.bytes, "Record")) { + } else if (std.mem.eql(u8, name, "Record")) { return .{ .predefined = .{ .record = try Record.parse(limited, constant_pool, allocator), }, }; - } else if (std.mem.eql(u8, attribute_name.bytes, "PermittedSubclasses")) { + } else if (std.mem.eql(u8, name, "PermittedSubclasses")) { return .{ .predefined = .{ .permitted_subclasses = try PermittedSubclasses.parse(limited, allocator), @@ -266,7 +264,7 @@ pub const AttributeInfo = union(enum) { w: *std.Io.Writer, depth: usize, indent: u8, - constant_pool: []const ?ConstantPool.Info, + constant_pool: ConstantPool, ) std.Io.Writer.Error!void { switch (self.*) { .new => |new| { @@ -343,15 +341,11 @@ pub const AttributeInfo = union(enum) { exception_indent += 1; defer exception_indent -= 1; - const index = exception_index - 1; - - if (index >= constant_pool.len) return error.WriteFailed; - const cp_info = constant_pool[index]; - if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .class) return error.WriteFailed; + const info = constant_pool.getTag(exception_index, .class) catch return error.WriteFailed; try w.splatByteAll(' ', depth * exception_indent); try w.print("{d}:\n", .{ i }); - try cp_info.?.indentedFormat(w, depth, exception_indent + 1, constant_pool); + try info.indentedFormat(w, depth, exception_indent + 1, constant_pool); } }, .inner_classes => |attr| { @@ -482,15 +476,11 @@ pub const AttributeInfo = union(enum) { package_indent += 1; defer package_indent -= 1; - const index = package_index - 1; - - if (index >= constant_pool.len) return error.WriteFailed; - const cp_info = constant_pool[index]; - if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .package) return error.WriteFailed; + const info = constant_pool.getTag(package_index, .package) catch return error.WriteFailed; try w.splatByteAll(' ', depth * package_indent); try w.print("{d}:\n", .{ i }); - try cp_info.?.indentedFormat(w, depth, package_indent + 1, constant_pool); + try info.indentedFormat(w, depth, package_indent + 1, constant_pool); } }, .module_main_class => |attr| { @@ -513,15 +503,11 @@ pub const AttributeInfo = union(enum) { class_indent += 1; defer class_indent -= 1; - const index = class_index - 1; - - if (index >= constant_pool.len) return error.WriteFailed; - const cp_info = constant_pool[index]; - if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .class) return error.WriteFailed; + const info = constant_pool.getTag(class_index, .class) catch return error.WriteFailed; try w.splatByteAll(' ', depth * class_indent); try w.print("{d}:\n", .{ i }); - try cp_info.?.indentedFormat(w, depth, class_indent + 1, constant_pool); + try info.indentedFormat(w, depth, class_indent + 1, constant_pool); } }, .record => |attr| { @@ -545,15 +531,11 @@ pub const AttributeInfo = union(enum) { class_indent += 1; defer class_indent -= 1; - const index = class_index - 1; - - if (index >= constant_pool.len) return error.WriteFailed; - const cp_info = constant_pool[index]; - if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .class) return error.WriteFailed; + const info = constant_pool.getTag(class_index, .class) catch return error.WriteFailed; try w.splatByteAll(' ', depth * class_indent); try w.print("{d}:\n", .{ i }); - try cp_info.?.indentedFormat(w, depth, class_indent + 1, constant_pool); + try info.indentedFormat(w, depth, class_indent + 1, constant_pool); } }, else => {}, @@ -580,13 +562,8 @@ pub const NewAttribute = struct { allocator.free(self.info); } - pub fn attributeName(self: *const Self, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { - 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(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - - return cp_info.?; + pub fn attributeName(self: *const Self, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.attribute_name_index, .utf8); } }; @@ -601,13 +578,8 @@ pub const ConstantValue = struct { }; } - pub fn constantValue(self: *const Self, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { - 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 fn constantValue(self: *const Self, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.get(self.constant_value_index); } }; @@ -631,7 +603,7 @@ pub const Code = struct { w: *std.Io.Writer, depth: usize, indent: u8, - constant_pool: []const ?ConstantPool.Info, + constant_pool: ConstantPool, ) std.Io.Writer.Error!void { try w.splatByteAll(' ', depth * indent); try w.print("start_pc: {d}\n", .{ self.start_pc }); @@ -650,17 +622,12 @@ pub const Code = struct { } } - pub fn catchType(self: *const ExceptionHandler, constant_pool: []const ?ConstantPool.Info) ResolveError!?ConstantPool.Info { - if (self.catch_type == 0) return null; - const catch_type = self.catch_type - 1; - if (catch_type >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[catch_type]; - if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn catchType(self: *const ExceptionHandler, constant_pool: ConstantPool) ResolveError!?ConstantPool.Info { + return constant_pool.getTagOptional(self.catch_type, .class); } }; - pub fn parse(input: *std.Io.Reader, constant_pool: []const ?ConstantPool.Info, allocator: std.mem.Allocator) (ParseError || ResolveError)!Self { + pub fn parse(input: *std.Io.Reader, constant_pool: ConstantPool, allocator: std.mem.Allocator) (ParseError || ResolveError)!Self { const max_stack = try input.takeInt(u16, .big); const max_locals = try input.takeInt(u16, .big); @@ -947,7 +914,7 @@ pub const InnerClasses = struct { w: *std.Io.Writer, depth: usize, indent: u8, - constant_pool: []const ?ConstantPool.Info, + constant_pool: ConstantPool, ) std.Io.Writer.Error!void { try w.splatByteAll(' ', depth * indent); try w.print("flags: {f}\n", .{ self.inner_class_access_flags }); @@ -972,32 +939,16 @@ pub const InnerClasses = struct { } } - pub fn innerClass(self: *const InnerClass, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { - 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(ConstantPool.Info.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn innerClass(self: *const InnerClass, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.inner_class_info_index, .class); } - pub fn outerClass(self: *const InnerClass, constant_pool: []const ?ConstantPool.Info) ResolveError!?ConstantPool.Info { - 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(ConstantPool.Info.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn outerClass(self: *const InnerClass, constant_pool: ConstantPool) ResolveError!?ConstantPool.Info { + return constant_pool.getTagOptional(self.outer_class_info_index, .class); } - pub fn innerName(self: *const InnerClass, constant_pool: []const ?ConstantPool.Info) ResolveError!?ConstantPool.Info { - 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(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn innerName(self: *const InnerClass, constant_pool: ConstantPool) ResolveError!?ConstantPool.Info { + return constant_pool.getTagOptional(self.inner_name_index, .utf8); } }; @@ -1037,21 +988,12 @@ pub const EnclosingMethod = struct { }; } - pub fn class(self: *const EnclosingMethod, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { - 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(ConstantPool.Info.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn class(self: *const EnclosingMethod, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.class_index, .class); } - pub fn method(self: *const EnclosingMethod, constant_pool: []const ?ConstantPool.Info) ResolveError!?ConstantPool.Info { - 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(ConstantPool.Info.Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn method(self: *const EnclosingMethod, constant_pool: ConstantPool) ResolveError!?ConstantPool.Info { + return constant_pool.getTagOptional(self.method_index, .name_and_type); } }; @@ -1068,12 +1010,8 @@ pub const Signature = struct { }; } - pub fn signature(self: *const Signature, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { - 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(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn signature(self: *const Signature, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.signature_index, .utf8); } }; @@ -1088,12 +1026,8 @@ pub const SourceFile = struct { }; } - pub fn sourceFile(self: *const SourceFile, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { - 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(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn sourceFile(self: *const SourceFile, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.source_file_index, .utf8); } }; @@ -1174,7 +1108,7 @@ pub const LocalVariableTable = struct { w: *std.Io.Writer, depth: usize, indent: u8, - constant_pool: []const ?ConstantPool.Info, + constant_pool: ConstantPool, ) std.Io.Writer.Error!void { try w.splatByteAll(' ', depth * indent); try w.print("start_pc: {d}\n", .{ self.start_pc }); @@ -1193,20 +1127,12 @@ pub const LocalVariableTable = struct { try descriptor_.indentedFormat(w, depth, indent + 1, constant_pool); } - pub fn name(self: *const LocalVariable, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { - const name_index = self.name_index - 1; - if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[name_index]; - if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn name(self: *const LocalVariable, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.name_index, .utf8); } - pub fn descriptor(self: *const LocalVariable, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { - const descriptor_index = self.descriptor_index - 1; - if (descriptor_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[descriptor_index]; - if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn descriptor(self: *const LocalVariable, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.descriptor_index, .utf8); } }; @@ -1251,7 +1177,7 @@ pub const LocalVariableTypeTable = struct { w: *std.Io.Writer, depth: usize, indent: u8, - constant_pool: []const ?ConstantPool.Info, + constant_pool: ConstantPool, ) std.Io.Writer.Error!void { try w.splatByteAll(' ', depth * indent); try w.print("start_pc: {d}\n", .{ self.start_pc }); @@ -1270,20 +1196,12 @@ pub const LocalVariableTypeTable = struct { try signature_.indentedFormat(w, depth, indent + 1, constant_pool); } - pub fn name(self: *const LocalVariableType, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { - const name_index = self.name_index - 1; - if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[name_index]; - if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn name(self: *const LocalVariableType, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.name_index, .utf8); } - pub fn signature(self: *const LocalVariableType, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { - 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(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn signature(self: *const LocalVariableType, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.signature_index, .utf8); } }; @@ -1623,7 +1541,7 @@ pub const BootstrapMethods = struct { w: *std.Io.Writer, depth: usize, indent: u8, - constant_pool: []const ?ConstantPool.Info, + constant_pool: ConstantPool, ) std.Io.Writer.Error!void { const bootstrap_method = self.bootstrapMethod(constant_pool) catch return error.WriteFailed; try w.splatByteAll(' ', depth * indent); @@ -1637,24 +1555,16 @@ pub const BootstrapMethods = struct { bootstrap_argument_indent += 1; defer bootstrap_argument_indent -= 1; - const index = bootstrap_argument - 1; - - if (index >= constant_pool.len) return error.WriteFailed; - const cp_info = constant_pool[index]; - if (cp_info == null or !@as(ConstantPool.Info.Tag, cp_info.?).loadable()) return error.WriteFailed; + const info = constant_pool.getLoadable(bootstrap_argument) catch return error.WriteFailed; try w.splatByteAll(' ', depth * bootstrap_argument_indent); try w.print("{d}:\n", .{ i }); - try cp_info.?.indentedFormat(w, depth, bootstrap_argument_indent + 1, constant_pool); + try info.indentedFormat(w, depth, bootstrap_argument_indent + 1, constant_pool); } } - pub fn bootstrapMethod(self: *const BootstrapMethod, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { - const bootstrap_method_ref = self.bootstrap_method_ref - 1; - if (bootstrap_method_ref >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[bootstrap_method_ref]; - if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .method_handle) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn bootstrapMethod(self: *const BootstrapMethod, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.bootstrap_method_ref, .method_handle); } }; @@ -1711,7 +1621,7 @@ pub const MethodParameters = struct { w: *std.Io.Writer, depth: usize, indent: u8, - constant_pool: []const ?ConstantPool.Info, + constant_pool: ConstantPool, ) std.Io.Writer.Error!void { try w.splatByteAll(' ', depth * indent); try w.print("flags: {f}\n", .{ self.access_flags }); @@ -1722,12 +1632,8 @@ pub const MethodParameters = struct { try name_.indentedFormat(w, depth, indent + 1, constant_pool); } - pub fn name(self: *const Parameter, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { - const name_index = self.name_index - 1; - if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[name_index]; - if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn name(self: *const Parameter, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.name_index, .utf8); } }; @@ -1787,7 +1693,7 @@ pub const Module = struct { w: *std.Io.Writer, depth: usize, indent: u8, - constant_pool: []const ?ConstantPool.Info, + constant_pool: ConstantPool, ) std.Io.Writer.Error!void { const requires_ = self.requires(constant_pool) catch return error.WriteFailed; try w.splatByteAll(' ', depth * indent); @@ -1805,21 +1711,12 @@ pub const Module = struct { } } - pub fn requires(self: *const Requires, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { - const requires_index = self.requires_index - 1; - if (requires_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[requires_index]; - if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .module) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn requires(self: *const Requires, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.requires_index, .module); } - pub fn requiresVersion(self: *const Requires, constant_pool: []const ?ConstantPool.Info) ResolveError!?ConstantPool.Info { - if (self.requires_version_index == 0) return null; - const requires_version_index = self.requires_version_index - 1; - if (requires_version_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[requires_version_index]; - if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn requiresVersion(self: *const Requires, constant_pool: ConstantPool) ResolveError!?ConstantPool.Info { + return constant_pool.getTagOptional(self.requires_version_index, .utf8); } }; @@ -1838,7 +1735,7 @@ pub const Module = struct { w: *std.Io.Writer, depth: usize, indent: u8, - constant_pool: []const ?ConstantPool.Info, + constant_pool: ConstantPool, ) std.Io.Writer.Error!void { const exports_ = self.exports(constant_pool) catch return error.WriteFailed; try w.splatByteAll(' ', depth * indent); @@ -1855,24 +1752,16 @@ pub const Module = struct { exports_to_indent += 1; defer exports_to_indent -= 1; - const index = exports_to_index - 1; - - if (index >= constant_pool.len) return error.WriteFailed; - const cp_info = constant_pool[index]; - if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .module) return error.WriteFailed; + const info = constant_pool.getTag(exports_to_index, .module) catch return error.WriteFailed; try w.splatByteAll(' ', depth * exports_to_indent); try w.print("{d}:\n", .{ i }); - try cp_info.?.indentedFormat(w, depth, exports_to_indent + 1, constant_pool); + try info.indentedFormat(w, depth, exports_to_indent + 1, constant_pool); } } - pub fn exports(self: *const Exports, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { - const exports_index = self.exports_index - 1; - if (exports_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[exports_index]; - if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .package) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn exports(self: *const Exports, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.exports_index, .package); } }; @@ -1891,7 +1780,7 @@ pub const Module = struct { w: *std.Io.Writer, depth: usize, indent: u8, - constant_pool: []const ?ConstantPool.Info, + constant_pool: ConstantPool, ) std.Io.Writer.Error!void { const opens_ = self.opens(constant_pool) catch return error.WriteFailed; try w.splatByteAll(' ', depth * indent); @@ -1908,24 +1797,16 @@ pub const Module = struct { opens_to_indent += 1; defer opens_to_indent -= 1; - const index = opens_to_index - 1; - - if (index >= constant_pool.len) return error.WriteFailed; - const cp_info = constant_pool[index]; - if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .module) return error.WriteFailed; + const info = constant_pool.getTag(opens_to_index, .module) catch return error.WriteFailed; try w.splatByteAll(' ', depth * opens_to_indent); try w.print("{d}:\n", .{ i }); - try cp_info.?.indentedFormat(w, depth, opens_to_indent + 1, constant_pool); + try info.indentedFormat(w, depth, opens_to_indent + 1, constant_pool); } } - pub fn opens(self: *const Opens, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { - const opens_index = self.opens_index - 1; - if (opens_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[opens_index]; - if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .package) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn opens(self: *const Opens, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.opens_index, .package); } }; @@ -1938,7 +1819,7 @@ pub const Module = struct { w: *std.Io.Writer, depth: usize, indent: u8, - constant_pool: []const ?ConstantPool.Info, + constant_pool: ConstantPool, ) std.Io.Writer.Error!void { const provides_ = self.provides(constant_pool) catch return error.WriteFailed; try w.splatByteAll(' ', depth * indent); @@ -1952,24 +1833,16 @@ pub const Module = struct { provides_with_indent += 1; defer provides_with_indent -= 1; - const index = provides_with_index - 1; - - if (index >= constant_pool.len) return error.WriteFailed; - const cp_info = constant_pool[index]; - if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .class) return error.WriteFailed; + const info = constant_pool.getTag(provides_with_index, .class) catch return error.WriteFailed; try w.splatByteAll(' ', depth * provides_with_indent); try w.print("{d}:\n", .{ i }); - try cp_info.?.indentedFormat(w, depth, provides_with_indent + 1, constant_pool); + try info.indentedFormat(w, depth, provides_with_indent + 1, constant_pool); } } - pub fn provides(self: *const Provides, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { - const provides_index = self.provides_index - 1; - if (provides_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[provides_index]; - if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn provides(self: *const Provides, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.provides_index, .class); } }; @@ -2088,7 +1961,7 @@ pub const Module = struct { w: *std.Io.Writer, depth: usize, indent: u8, - constant_pool: []const ?ConstantPool.Info, + constant_pool: ConstantPool, ) std.Io.Writer.Error!void { const module_name = self.moduleName(constant_pool) catch return error.WriteFailed; try w.splatByteAll(' ', depth * indent); @@ -2148,15 +2021,11 @@ pub const Module = struct { uses_indent += 1; defer uses_indent -= 1; - const index = uses_index - 1; - - if (index >= constant_pool.len) return error.WriteFailed; - const cp_info = constant_pool[index]; - if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .class) return error.WriteFailed; + const info = constant_pool.getTag(uses_index, .class) catch return error.WriteFailed; try w.splatByteAll(' ', depth * uses_indent); try w.print("{d}:\n", .{ i }); - try cp_info.?.indentedFormat(w, depth, uses_indent + 1, constant_pool); + try info.indentedFormat(w, depth, uses_indent + 1, constant_pool); } try w.splatByteAll(' ', depth * indent); @@ -2172,21 +2041,12 @@ pub const Module = struct { } } - pub fn moduleName(self: *const Module, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { - const module_name_index = self.module_name_index - 1; - if (module_name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[module_name_index]; - if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn moduleName(self: *const Module, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.module_name_index, .utf8); } - pub fn moduleVersion(self: *const Module, constant_pool: []const ?ConstantPool.Info) ResolveError!?ConstantPool.Info { - if (self.module_version_index == 0) return null; - const module_version_index = self.module_version_index - 1; - if (module_version_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[module_version_index]; - if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn moduleVersion(self: *const Module, constant_pool: ConstantPool) ResolveError!?ConstantPool.Info { + return constant_pool.getTagOptional(self.module_version_index, .utf8); } }; @@ -2224,12 +2084,8 @@ pub const ModuleMainClass = struct { }; } - pub fn mainClass(self: *const ModuleMainClass, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { - const main_class_index = self.main_class_index - 1; - if (main_class_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[main_class_index]; - if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn mainClass(self: *const ModuleMainClass, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.main_class_index, .class); } }; @@ -2244,12 +2100,8 @@ pub const NestHost = struct { }; } - pub fn hostClass(self: *const NestHost, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { - const host_class_index = self.host_class_index - 1; - if (host_class_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[host_class_index]; - if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn hostClass(self: *const NestHost, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.host_class_index, .class); } }; @@ -2285,7 +2137,7 @@ pub const Record = struct { descriptor_index: u16, attributes: []AttributeInfo, - pub fn parse(input: *std.Io.Reader, constant_pool: []const ?ConstantPool.Info, allocator: std.mem.Allocator) (ParseError || ResolveError)!RecordComponentInfo { + pub fn parse(input: *std.Io.Reader, constant_pool: ConstantPool, allocator: std.mem.Allocator) (ParseError || ResolveError)!RecordComponentInfo { const name_index = try input.takeInt(u16, .big); const descriptor_index = try input.takeInt(u16, .big); @@ -2315,7 +2167,7 @@ pub const Record = struct { w: *std.Io.Writer, depth: usize, indent: u8, - constant_pool: []const ?ConstantPool.Info, + constant_pool: ConstantPool, ) std.Io.Writer.Error!void { const name_ = self.name(constant_pool) catch return error.WriteFailed; try w.splatByteAll(' ', depth * indent); @@ -2336,29 +2188,20 @@ pub const Record = struct { try w.splatByteAll(' ', depth * attr_indent); try w.print("{d}:\n", .{ i }); - try attr.indentedFormat(w, depth, attr_indent + 1, constant_pool); } } - pub fn name(self: *const RecordComponentInfo, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { - const name_index = self.name_index - 1; - if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[name_index]; - if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn name(self: *const RecordComponentInfo, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.name_index, .utf8); } - pub fn descriptor(self: *const RecordComponentInfo, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { - const descriptor_index = self.descriptor_index - 1; - if (descriptor_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[descriptor_index]; - if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn descriptor(self: *const RecordComponentInfo, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.descriptor_index, .utf8); } }; - pub fn parse(input: *std.Io.Reader, constant_pool: []const ?ConstantPool.Info, allocator: std.mem.Allocator) (ParseError || ResolveError)!Self { + pub fn parse(input: *std.Io.Reader, constant_pool: ConstantPool, allocator: std.mem.Allocator) (ParseError || ResolveError)!Self { const components_count = try input.takeInt(u16, .big); const components = try allocator.alloc(RecordComponentInfo, components_count); for (components) |*component| { diff --git a/src/Class/ConstantPool.zig b/src/Class/ConstantPool.zig index cdf4063..17e71c2 100644 --- a/src/Class/ConstantPool.zig +++ b/src/Class/ConstantPool.zig @@ -5,6 +5,10 @@ const AttributeInfo = Class.AttributeInfo; const ParseError = Class.ParseError; const ResolveError = Class.ResolveError; +constant_pool: []?Info, + +const Self = @This(); + pub const Info = union(Info.Tag) { class: Info.Class, field_ref: FieldRef, @@ -54,14 +58,8 @@ pub const Info = union(Info.Tag) { pub const Class = struct { name_index: u16, - const Self = @This(); - - pub fn name(self: *const Self, constant_pool: []const ?Info) ResolveError!Info { - const name_index = self.name_index - 1; - if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[name_index]; - if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn name(self: *const Info.Class, constant_pool: Self) ResolveError!Info { + return constant_pool.getTag(self.name_index, .utf8); } }; @@ -69,22 +67,12 @@ pub const Info = union(Info.Tag) { class_index: u16, name_and_type_index: u16, - const Self = @This(); - - pub fn class(self: *const Self, constant_pool: []const ?Info) ResolveError!Info { - 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(Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn class(self: *const FieldRef, constant_pool: Self) ResolveError!Info { + return constant_pool.getTag(self.class_index, .class); } - pub fn nameAndType(self: *const Self, constant_pool: []const ?Info) ResolveError!Info { - const name_and_type_index = self.name_and_type_index - 1; - if (name_and_type_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[name_and_type_index]; - if (cp_info == null or @as(Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn nameAndType(self: *const FieldRef, constant_pool: Self) ResolveError!Info { + return constant_pool.getTag(self.name_and_type_index, .name_and_type); } }; @@ -92,22 +80,12 @@ pub const Info = union(Info.Tag) { class_index: u16, name_and_type_index: u16, - const Self = @This(); - - pub fn class(self: *const Self, constant_pool: []const ?Info) ResolveError!Info { - 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(Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn class(self: *const MethodRef, constant_pool: Self) ResolveError!Info { + return constant_pool.getTag(self.class_index, .class); } - pub fn nameAndType(self: *const Self, constant_pool: []const ?Info) ResolveError!Info { - const name_and_type_index = self.name_and_type_index - 1; - if (name_and_type_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[name_and_type_index]; - if (cp_info == null or @as(Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn nameAndType(self: *const MethodRef, constant_pool: Self) ResolveError!Info { + return constant_pool.getTag(self.name_and_type_index, .name_and_type); } }; @@ -115,36 +93,20 @@ pub const Info = union(Info.Tag) { class_index: u16, name_and_type_index: u16, - const Self = @This(); - - pub fn class(self: *const Self, constant_pool: []const ?Info) ResolveError!Info { - 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(Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn class(self: *const InterfaceMethodRef, constant_pool: Self) ResolveError!Info { + return constant_pool.getTag(self.class_index, .class); } - pub fn nameAndType(self: *const Self, constant_pool: []const ?Info) ResolveError!Info { - const name_and_type_index = self.name_and_type_index - 1; - if (name_and_type_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[name_and_type_index]; - if (cp_info == null or @as(Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn nameAndType(self: *const InterfaceMethodRef, constant_pool: Self) ResolveError!Info { + return constant_pool.getTag(self.name_and_type_index, .name_and_type); } }; pub const String = struct { string_index: u16, - const Self = @This(); - - pub fn string(self: *const Self, constant_pool: []const ?Info) ResolveError!Info { - const string_index = self.string_index - 1; - if (string_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[string_index]; - if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn string(self: *const String, constant_pool: Self) ResolveError!Info { + return constant_pool.getTag(self.string_index, .utf8); } }; @@ -170,22 +132,12 @@ pub const Info = union(Info.Tag) { name_index: u16, descriptor_index: u16, - const Self = @This(); - - pub fn name(self: *const Self, constant_pool: []const ?Info) ResolveError!Info { - const name_index = self.name_index - 1; - if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[name_index]; - if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn name(self: *const NameAndType, constant_pool: Self) ResolveError!Info { + return constant_pool.getTag(self.name_index, .utf8); } - pub fn descriptor(self: *const Self, constant_pool: []const ?Info) ResolveError!Info { - const descriptor_index = self.descriptor_index - 1; - if (descriptor_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[descriptor_index]; - if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn descriptor(self: *const NameAndType, constant_pool: Self) ResolveError!Info { + return constant_pool.getTag(self.descriptor_index, .utf8); } }; @@ -197,8 +149,6 @@ pub const Info = union(Info.Tag) { reference_kind: Kind, reference_index: u16, - const Self = @This(); - pub const Kind = enum(u8) { get_field = 1, get_static = 2, @@ -211,22 +161,20 @@ pub const Info = union(Info.Tag) { invoke_interface = 9, }; - pub fn reference(self: *const Self, constant_pool: []const ?Info) ResolveError!Info { - const reference_index = self.reference_index - 1; - if (reference_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[reference_index]; + pub fn reference(self: *const MethodHandle, constant_pool: Self) ResolveError!Info { + const info = try constant_pool.get(self.reference_index); return switch (self.reference_kind) { .get_field, .get_static, .put_field, .put_static => blk: { - if (cp_info == null or @as(Tag, cp_info.?) != .field_ref) break :blk ResolveError.InvalidConstantType; - break :blk cp_info.?; + if (@as(Tag, info) != .field_ref) break :blk ResolveError.InvalidConstantType; + break :blk info; }, .invoke_virtual, .invoke_static, .invoke_special, .new_invoke_special => blk: { - if (cp_info == null or @as(Tag, cp_info.?) != .method_ref) break :blk ResolveError.InvalidConstantType; - break :blk cp_info.?; + if (@as(Tag, info) != .method_ref) break :blk ResolveError.InvalidConstantType; + break :blk info; }, .invoke_interface => blk: { - if (cp_info == null or @as(Tag, cp_info.?) != .interface_method_ref) break :blk ResolveError.InvalidConstantType; - break :blk cp_info.?; + if (@as(Tag, info) != .interface_method_ref) break :blk ResolveError.InvalidConstantType; + break :blk info; }, }; } @@ -235,14 +183,8 @@ pub const Info = union(Info.Tag) { pub const MethodType = struct { descriptor_index: u16, - const Self = @This(); - - pub fn descriptor(self: *const Self, constant_pool: []const ?Info) ResolveError!Info { - const descriptor_index = self.descriptor_index - 1; - if (descriptor_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[descriptor_index]; - if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn descriptor(self: *const MethodType, constant_pool: Self) ResolveError!Info { + return constant_pool.getTag(self.descriptor_index, .utf8); } }; @@ -250,14 +192,8 @@ pub const Info = union(Info.Tag) { bootstrap_method_attr_index: u16, name_and_type_index: u16, - const Self = @This(); - - pub fn nameAndType(self: *const Self, constant_pool: []const ?Info) ResolveError!Info { - const name_and_type_index = self.name_and_type_index - 1; - if (name_and_type_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[name_and_type_index]; - if (cp_info == null or @as(Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn nameAndType(self: *const Dynamic, constant_pool: Self) ResolveError!Info { + return constant_pool.getTag(self.name_and_type_index, .name_and_type); } }; @@ -265,42 +201,24 @@ pub const Info = union(Info.Tag) { bootstrap_method_attr_index: u16, name_and_type_index: u16, - const Self = @This(); - - pub fn nameAndType(self: *const Self, constant_pool: []const ?Info) ResolveError!Info { - const name_and_type_index = self.name_and_type_index - 1; - if (name_and_type_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[name_and_type_index]; - if (cp_info == null or @as(Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn nameAndType(self: *const InvokeDynamic, constant_pool: Self) ResolveError!Info { + return constant_pool.getTag(self.name_and_type_index, .name_and_type); } }; pub const Module = struct { name_index: u16, - const Self = @This(); - - pub fn name(self: *const Self, constant_pool: []const ?Info) ResolveError!Info { - const name_index = self.name_index - 1; - if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[name_index]; - if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn name(self: *const Module, constant_pool: Self) ResolveError!Info { + return constant_pool.getTag(self.name_index, .utf8); } }; pub const Package = struct { name_index: u16, - const Self = @This(); - - pub fn name(self: *const Self, constant_pool: []const ?Info) ResolveError!Info { - const name_index = self.name_index - 1; - if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[name_index]; - if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; + pub fn name(self: *const Package, constant_pool: Self) ResolveError!Info { + return constant_pool.getTag(self.name_index, .utf8); } }; @@ -450,7 +368,7 @@ pub const Info = union(Info.Tag) { w: *std.Io.Writer, depth: usize, indent: u8, - constant_pool: []const ?Info, + constant_pool: Self, ) std.Io.Writer.Error!void { try w.splatByteAll(' ', depth * indent); try w.print("constant_value_type: {s}\n", .{ @tagName(self.*) }); @@ -579,3 +497,71 @@ pub const Info = union(Info.Tag) { } } }; + +pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { + const constant_pool_size = try input.takeInt(u16, .big); + const constant_pool = try allocator.alloc(?Info, constant_pool_size - 1); + errdefer allocator.free(constant_pool); + + var i: usize = 0; + while (i < constant_pool_size - 1) { + defer i += 1; + + const info: Info = try .parse(input, allocator); + constant_pool[i] = info; + + switch (info) { + .long, .double => { + i += 1; + constant_pool[i] = null; + }, + else => {}, + } + } + + return .{ + .constant_pool = constant_pool, + }; +} + +pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + for (self.constant_pool) |*info| { + if (info.*) |*c| c.deinit(allocator); + } + allocator.free(self.constant_pool); +} + +pub fn get(self: *const Self, index: u16) ResolveError!Info { + const actual_index = index - 1; + if (actual_index >= self.constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const info = self.constant_pool[actual_index]; + if (info == null) return ResolveError.InvalidConstantType; + return info.?; +} + +pub fn getOptional(self: *const Self, index: u16) ResolveError!?Info { + if (index == 0) return null; + return self.get(index); +} + +pub fn getTag(self: *const Self, index: u16, expected_tag: Info.Tag) ResolveError!Info { + const info = try self.get(index); + if (@as(Info.Tag, info) != expected_tag) return ResolveError.InvalidConstantType; + return info; +} + +pub fn getTagOptional(self: *const Self, index: u16, expected_tag: Info.Tag) ResolveError!?Info { + if (index == 0) return null; + return try self.getTag(index, expected_tag); +} + +pub fn getLoadable(self: *const Self, index: u16) ResolveError!Info { + const info = try self.get(index); + if (!@as(Info.Tag, info).loadable()) return ResolveError.InvalidConstantType; + return info; +} + +pub fn getLoadableOptional(self: *const Self, index: u16) ResolveError!?Info { + if (index == 0) return null; + return self.getLoadable(index); +} diff --git a/src/Class/FieldInfo.zig b/src/Class/FieldInfo.zig index 12a75a3..46746e6 100644 --- a/src/Class/FieldInfo.zig +++ b/src/Class/FieldInfo.zig @@ -26,7 +26,7 @@ pub const FieldAccessFlags = EnumFlags(enum(u16) { @"enum" = 0x4000, }); -pub fn parse(input: *std.Io.Reader, constant_pool: []const ?ConstantPool.Info, allocator: std.mem.Allocator) !Self { +pub fn parse(input: *std.Io.Reader, constant_pool: ConstantPool, 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); @@ -51,7 +51,7 @@ pub fn indentedFormat( w: *std.Io.Writer, depth: usize, indent: u8, - constant_pool: []const ?ConstantPool.Info, + constant_pool: ConstantPool, ) std.Io.Writer.Error!void { try w.splatByteAll(' ', depth * indent); try w.print("flags: {f}\n", .{ self.access_flags }); @@ -80,19 +80,11 @@ pub fn indentedFormat( } } -pub fn name(self: *const Self, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { - const name_index = self.name_index - 1; - if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[name_index]; - if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; +pub fn name(self: *const Self, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.name_index, .utf8); } -pub fn descriptor(self: *const Self, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { - const descriptor_index = self.descriptor_index - 1; - if (descriptor_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[descriptor_index]; - if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; +pub fn descriptor(self: *const Self, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.descriptor_index, .utf8); } diff --git a/src/Class/MethodInfo.zig b/src/Class/MethodInfo.zig index f58a7bf..2650061 100644 --- a/src/Class/MethodInfo.zig +++ b/src/Class/MethodInfo.zig @@ -29,7 +29,7 @@ pub const MethodAccessFlags = EnumFlags(enum(u16) { synthetic = 0x1000, }); -pub fn parse(input: *std.Io.Reader, constant_pool: []const ?ConstantPool.Info, allocator: std.mem.Allocator) !Self { +pub fn parse(input: *std.Io.Reader, constant_pool: ConstantPool, 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); @@ -54,7 +54,7 @@ pub fn indentedFormat( w: *std.Io.Writer, depth: usize, indent: u8, - constant_pool: []const ?ConstantPool.Info, + constant_pool: ConstantPool, ) std.Io.Writer.Error!void { try w.splatByteAll(' ', depth * indent); try w.print("flags: {f}\n", .{ self.access_flags }); @@ -83,19 +83,10 @@ pub fn indentedFormat( } } -pub fn name(self: *const Self, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { - const name_index = self.name_index - 1; - if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[name_index]; - if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; +pub fn name(self: *const Self, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.name_index, .utf8); } -pub fn descriptor(self: *const Self, constant_pool: []const ?ConstantPool.Info) ResolveError!ConstantPool.Info { - const descriptor_index = self.descriptor_index - 1; - if (descriptor_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const cp_info = constant_pool[descriptor_index]; - if (cp_info == null or @as(ConstantPool.Info.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; +pub fn descriptor(self: *const Self, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.descriptor_index, .utf8); } - diff --git a/src/main.zig b/src/main.zig index 922c825..8c4831e 100644 --- a/src/main.zig +++ b/src/main.zig @@ -52,16 +52,6 @@ pub fn main(init: std.process.Init) !void { }; defer class.deinit(); - if (@import("builtin").mode == .Debug) { - for (class.constant_pool, 1..) |cp_info, i| { - if (cp_info) |info| { - std.debug.print("#{d} = {s}\t{f}\n", .{ i, @tagName(info), info }); - } else { - std.debug.print("#{d} = null\n", .{ i }); - } - } - } - try stdout.print("{f}", .{ class }); try stdout.flush(); From cb43efcbb14a0b703745390c6399016c707acff3 Mon Sep 17 00:00:00 2001 From: ktkk Date: Mon, 6 Jul 2026 08:52:12 +0000 Subject: [PATCH 18/31] Add `is` method to ConstantPool.Info --- src/Class/ConstantPool.zig | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Class/ConstantPool.zig b/src/Class/ConstantPool.zig index 17e71c2..6c5afe3 100644 --- a/src/Class/ConstantPool.zig +++ b/src/Class/ConstantPool.zig @@ -165,15 +165,15 @@ pub const Info = union(Info.Tag) { const info = try constant_pool.get(self.reference_index); return switch (self.reference_kind) { .get_field, .get_static, .put_field, .put_static => blk: { - if (@as(Tag, info) != .field_ref) break :blk ResolveError.InvalidConstantType; + if (!info.is(.field_ref)) break :blk ResolveError.InvalidConstantType; break :blk info; }, .invoke_virtual, .invoke_static, .invoke_special, .new_invoke_special => blk: { - if (@as(Tag, info) != .method_ref) break :blk ResolveError.InvalidConstantType; + if (!info.is(.method_ref)) break :blk ResolveError.InvalidConstantType; break :blk info; }, .invoke_interface => blk: { - if (@as(Tag, info) != .interface_method_ref) break :blk ResolveError.InvalidConstantType; + if (!info.is(.interface_method_ref)) break :blk ResolveError.InvalidConstantType; break :blk info; }, }; @@ -496,6 +496,10 @@ pub const Info = union(Info.Tag) { }, } } + + pub fn is(self: *const Info, tag: Tag) bool { + return @as(Tag, self.*) == tag; + } }; pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { @@ -546,7 +550,7 @@ pub fn getOptional(self: *const Self, index: u16) ResolveError!?Info { pub fn getTag(self: *const Self, index: u16, expected_tag: Info.Tag) ResolveError!Info { const info = try self.get(index); - if (@as(Info.Tag, info) != expected_tag) return ResolveError.InvalidConstantType; + if (!info.is(expected_tag)) return ResolveError.InvalidConstantType; return info; } From 0cc72ac2d9d2b29d0e167c836105d8dd44af0b9f Mon Sep 17 00:00:00 2001 From: ktkk Date: Mon, 6 Jul 2026 09:52:42 +0000 Subject: [PATCH 19/31] Remove this_class and super_class verification The plan is to implement a separate "verify" function that is separate from parsing or formatting. --- src/Class.zig | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/src/Class.zig b/src/Class.zig index 58c02d1..d244335 100644 --- a/src/Class.zig +++ b/src/Class.zig @@ -265,20 +265,6 @@ pub fn thisClass(self: *const Self) ResolveError!ConstantPool.Info { } pub fn superClass(self: *const Self) ResolveError!?ConstantPool.Info { - if (self.super_class == 0) { - const this_class = (try self.thisClass()).class; - const this_class_name = (try this_class.name(self.constant_pool)).utf8; - if (!std.mem.eql(u8, this_class_name.bytes, "java/lang/Object")) return ResolveError.InvalidConstantType; - return null; - } else { - const super_class = try self.constant_pool.getTag(self.super_class, .class); - - if (self.access_flags.contains(.interface)) { - const super_class_name = (try super_class.class.name(self.constant_pool)).utf8; - if (!std.mem.eql(u8, super_class_name.bytes, "java/lang/Object")) return ResolveError.InvalidConstantType; - } else { - // TODO: Assert that superclass does not have ACC_FINAL flag set. - } - return super_class; - } + return self.constant_pool.getTagOptional(self.super_class, .class); } + From 0cf25ebaf4f8c50313676f1f4fa66225602e5548 Mon Sep 17 00:00:00 2001 From: ktkk Date: Mon, 6 Jul 2026 10:13:15 +0000 Subject: [PATCH 20/31] Fix module name tag check --- src/Class/AttributeInfo.zig | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Class/AttributeInfo.zig b/src/Class/AttributeInfo.zig index 0e9540d..5814295 100644 --- a/src/Class/AttributeInfo.zig +++ b/src/Class/AttributeInfo.zig @@ -1987,7 +1987,7 @@ pub const Module = struct { try w.splatByteAll(' ', depth * requires_indent); try w.print("{d}:\n", .{ i }); - try requires.indentedFormat(w, depth, requires_indent, constant_pool); + try requires.indentedFormat(w, depth, requires_indent + 1, constant_pool); } try w.splatByteAll(' ', depth * indent); @@ -1999,7 +1999,7 @@ pub const Module = struct { try w.splatByteAll(' ', depth * exports_indent); try w.print("{d}:\n", .{ i }); - try exports.indentedFormat(w, depth, exports_indent, constant_pool); + try exports.indentedFormat(w, depth, exports_indent + 1, constant_pool); } try w.splatByteAll(' ', depth * indent); @@ -2011,7 +2011,7 @@ pub const Module = struct { try w.splatByteAll(' ', depth * opens_indent); try w.print("{d}:\n", .{ i }); - try opens.indentedFormat(w, depth, opens_indent, constant_pool); + try opens.indentedFormat(w, depth, opens_indent + 1, constant_pool); } try w.splatByteAll(' ', depth * indent); @@ -2037,12 +2037,12 @@ pub const Module = struct { try w.splatByteAll(' ', depth * provides_indent); try w.print("{d}:\n", .{ i }); - try provides.indentedFormat(w, depth, provides_indent, constant_pool); + try provides.indentedFormat(w, depth, provides_indent + 1, constant_pool); } } pub fn moduleName(self: *const Module, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { - return constant_pool.getTag(self.module_name_index, .utf8); + return constant_pool.getTag(self.module_name_index, .module); } pub fn moduleVersion(self: *const Module, constant_pool: ConstantPool) ResolveError!?ConstantPool.Info { From 114b9b099c3b0528e9402f5a11045badfcbaa0a6 Mon Sep 17 00:00:00 2001 From: ktkk Date: Mon, 6 Jul 2026 11:28:58 +0000 Subject: [PATCH 21/31] Put testsuite in its own directory --- flake.nix | 2 ++ testsuite/.gitignore | 3 +++ testsuite/classes/java/CustomAttribute.java | 10 ++++++++ testsuite/classes/java/Hello.java | 6 +++++ .../classes/java/Interface.java | 0 Main.java => testsuite/classes/java/Main.java | 1 + testsuite/classes/java/MultiThreading.java | 22 ++++++++++++++++++ testsuite/classes/java/Object.class | Bin 0 -> 2412 bytes testsuite/classes/kotlin/Main.kt | 12 ++++++++++ testsuite/modules/compile-simple-modules.sh | 2 ++ testsuite/modules/run-simple-module-app.sh | 2 ++ .../katkak/modules/hello/HelloInterface.java | 6 +++++ .../katkak/modules/hello/HelloModules.java | 12 ++++++++++ .../hello.modules/module-info.java | 6 +++++ .../dev/katkak/modules/main/MainApp.java | 17 ++++++++++++++ .../simple-modules/main.app/module-info.java | 6 +++++ 16 files changed, 107 insertions(+) create mode 100644 testsuite/.gitignore create mode 100644 testsuite/classes/java/CustomAttribute.java create mode 100644 testsuite/classes/java/Hello.java rename Interface.java => testsuite/classes/java/Interface.java (100%) rename Main.java => testsuite/classes/java/Main.java (98%) create mode 100644 testsuite/classes/java/MultiThreading.java create mode 100644 testsuite/classes/java/Object.class create mode 100644 testsuite/classes/kotlin/Main.kt create mode 100755 testsuite/modules/compile-simple-modules.sh create mode 100755 testsuite/modules/run-simple-module-app.sh create mode 100644 testsuite/modules/simple-modules/hello.modules/dev/katkak/modules/hello/HelloInterface.java create mode 100644 testsuite/modules/simple-modules/hello.modules/dev/katkak/modules/hello/HelloModules.java create mode 100644 testsuite/modules/simple-modules/hello.modules/module-info.java create mode 100644 testsuite/modules/simple-modules/main.app/dev/katkak/modules/main/MainApp.java create mode 100644 testsuite/modules/simple-modules/main.app/module-info.java diff --git a/flake.nix b/flake.nix index c48263a..b1ef715 100644 --- a/flake.nix +++ b/flake.nix @@ -19,6 +19,8 @@ zls javaPackages.compiler.openjdk25 jdt-language-server + kotlin + kotlin-language-server ]; buildInputs = with pkgs; []; diff --git a/testsuite/.gitignore b/testsuite/.gitignore new file mode 100644 index 0000000..415ec0f --- /dev/null +++ b/testsuite/.gitignore @@ -0,0 +1,3 @@ +*.class +!Object.class +META-INF diff --git a/testsuite/classes/java/CustomAttribute.java b/testsuite/classes/java/CustomAttribute.java new file mode 100644 index 0000000..e3fe264 --- /dev/null +++ b/testsuite/classes/java/CustomAttribute.java @@ -0,0 +1,10 @@ +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) +public @interface CustomAttribute { +} + diff --git a/testsuite/classes/java/Hello.java b/testsuite/classes/java/Hello.java new file mode 100644 index 0000000..a5a4fbf --- /dev/null +++ b/testsuite/classes/java/Hello.java @@ -0,0 +1,6 @@ +class Hello { + public static void main(String[] args) { + System.out.println("Hello, world!"); + } +} + diff --git a/Interface.java b/testsuite/classes/java/Interface.java similarity index 100% rename from Interface.java rename to testsuite/classes/java/Interface.java diff --git a/Main.java b/testsuite/classes/java/Main.java similarity index 98% rename from Main.java rename to testsuite/classes/java/Main.java index a3c6d9b..ec77f66 100644 --- a/Main.java +++ b/testsuite/classes/java/Main.java @@ -1,5 +1,6 @@ import java.util.function.Supplier; +@CustomAttribute public class Main implements Runnable, Supplier, Interface { private static final String name = "John"; private static final int number = 42; diff --git a/testsuite/classes/java/MultiThreading.java b/testsuite/classes/java/MultiThreading.java new file mode 100644 index 0000000..62d84f5 --- /dev/null +++ b/testsuite/classes/java/MultiThreading.java @@ -0,0 +1,22 @@ +import java.util.concurrent.Executors; +import java.util.stream.IntStream; +import java.util.concurrent.TimeUnit; + +public class MultiThreading { + private int number = 0; + + public static void main(String[] args) throws InterruptedException { + final var multiThreading = new MultiThreading(); + + final var service = Executors.newFixedThreadPool(3); + IntStream.range(0, 1000) + .forEach(count -> service.submit(multiThreading::increment)); + service.awaitTermination(1000, TimeUnit.MILLISECONDS); + + System.out.println(multiThreading.number); + } + + public synchronized void increment() { + number += 1; + } +} diff --git a/testsuite/classes/java/Object.class b/testsuite/classes/java/Object.class new file mode 100644 index 0000000000000000000000000000000000000000..d33531f70deffc2ee62a65b89a7a44e7773be646 GIT binary patch literal 2412 zcmX^0Z`VEs1_oD#Vs-{5Mh5Y$#Ii*FoW#6z{os101ty8gAgMFKbkxv1ABUEiC@9#URHZ&(5I0!=T8Z#K<6saHMBmNosm(5hDY4NxnyF1;k)R22l-9O#8rYRpDV! z1$mUCBp;#}WUV?6g9d{pBLho$VrB^=1B-^2W*9qzHY0-)l1(`|sp*M1jz#IExv6<2 zt`*6t1tppJc^nM7j0}<`nYpR?r6mewi8-aI3Yo>LGnnu&m@=55L}5rqQEFleBLi=8X;D#XUI~nk8W9jtYjy?;l=KspSyWP* zm;;dodD4o9!5ZXAR*)wR*cogY8RU?>msgTnR8(3}l9~ed8W)2(gFO#}1A`+YgGhR6 ziDO=hb53ev5h&2XYQeD+#lhgr$RH050pHA=oXld(aA0QOce3YTaARap%}dP7FHTL) z&r4B&YJw^U1z>)fLQ!H~dMZ1E2O|R?!hJsZdFib6{~>^pfd!H{_k4| z*5sW0JdAt@)yBxc1&_C4c7_;61{H*7ok1G?@=JnC3kvd!kfWH9fh{k;Br~m&k%1G! zaLmbJWME8XWKcj#tne5|N*&gW4D4kk&?G8}q!=aTSu-;5f`T`{v;>kg7#SG77#UbK zygW6-7#Uc>iGh)U(UXyZBP}y8F(I{BlrqGBR+vq!tvVCMTAp zrZ6&yAz6wLw`OEuEzZnKPGw|Zv}9!9O3N<_O3lqLOUz+rU|?WmWZ(+UFD*(=b;|^$ z7j8)S>49uxP-Ng>fB;?wMo?+Xz{KFqz`&pl(g?yE85kIt7*OT8GF z7?>Cs7`U~xmoc!jEN5V0UcH-5DzNop5M@y785p=3!WjG+7#YGD7#LU?m^d96 z8Ce)27$O;%7@`;$7+4q>8KTi_Rb>!@+Um!^1on-}ZU)B4?F`IX+ZfolG4O6<5Ec^O z#vrVzy=L& z69#6m8`!isw=t+8!ki1_QO0-%R>lM*o7f;W!Tn|fHcJy~)>#G?237_J25WiFDRUTj zcQa^33h8WT(A&R*!O)6DlI0+S`Ai0BNtOc)=6+i8+ZZgDGYDC+=x$@M3zuZs4;IAUB*TRBgQ5MQ^r;X zE5>%PL$w%K7<3sp7~&Zc7+4sT8H5-T8Il-4$cm@V*hGd2mXsk&vun7ETkYZqF zXW(FAXE0}AWM{DX#lXpe@Fb%Us5EC_M2LXBoeK5#0tOZa76t|eDPx# literal 0 HcmV?d00001 diff --git a/testsuite/classes/kotlin/Main.kt b/testsuite/classes/kotlin/Main.kt new file mode 100644 index 0000000..ca75765 --- /dev/null +++ b/testsuite/classes/kotlin/Main.kt @@ -0,0 +1,12 @@ +fun main() { + println("Hello, world!") + + Test().apply { + test = true + } +} + +class Test { + var test: Boolean = false +} + diff --git a/testsuite/modules/compile-simple-modules.sh b/testsuite/modules/compile-simple-modules.sh new file mode 100755 index 0000000..ca384d8 --- /dev/null +++ b/testsuite/modules/compile-simple-modules.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +javac -g -d outDir --module-source-path simple-modules $(find simple-modules -name "*.java") diff --git a/testsuite/modules/run-simple-module-app.sh b/testsuite/modules/run-simple-module-app.sh new file mode 100755 index 0000000..533d45a --- /dev/null +++ b/testsuite/modules/run-simple-module-app.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +java --module-path outDir -m main.app/dev.katkak.modules.main.MainApp diff --git a/testsuite/modules/simple-modules/hello.modules/dev/katkak/modules/hello/HelloInterface.java b/testsuite/modules/simple-modules/hello.modules/dev/katkak/modules/hello/HelloInterface.java new file mode 100644 index 0000000..b98b718 --- /dev/null +++ b/testsuite/modules/simple-modules/hello.modules/dev/katkak/modules/hello/HelloInterface.java @@ -0,0 +1,6 @@ +package dev.katkak.modules.hello; + +public interface HelloInterface { + void sayHello(); +} + diff --git a/testsuite/modules/simple-modules/hello.modules/dev/katkak/modules/hello/HelloModules.java b/testsuite/modules/simple-modules/hello.modules/dev/katkak/modules/hello/HelloModules.java new file mode 100644 index 0000000..cdbc53a --- /dev/null +++ b/testsuite/modules/simple-modules/hello.modules/dev/katkak/modules/hello/HelloModules.java @@ -0,0 +1,12 @@ +package dev.katkak.modules.hello; + +public class HelloModules implements HelloInterface { + public static void doSomething() { + System.out.println("Hello, Modules!"); + } + + public void sayHello() { + System.out.println("Hello!"); + } +} + diff --git a/testsuite/modules/simple-modules/hello.modules/module-info.java b/testsuite/modules/simple-modules/hello.modules/module-info.java new file mode 100644 index 0000000..4dfc3e0 --- /dev/null +++ b/testsuite/modules/simple-modules/hello.modules/module-info.java @@ -0,0 +1,6 @@ +module hello.modules { + exports dev.katkak.modules.hello; + + provides dev.katkak.modules.hello.HelloInterface with dev.katkak.modules.hello.HelloModules; +} + diff --git a/testsuite/modules/simple-modules/main.app/dev/katkak/modules/main/MainApp.java b/testsuite/modules/simple-modules/main.app/dev/katkak/modules/main/MainApp.java new file mode 100644 index 0000000..ac4eda3 --- /dev/null +++ b/testsuite/modules/simple-modules/main.app/dev/katkak/modules/main/MainApp.java @@ -0,0 +1,17 @@ +package dev.katkak.modules.main; + +import dev.katkak.modules.hello.HelloModules; +import dev.katkak.modules.hello.HelloInterface; + +import java.util.ServiceLoader; + +public class MainApp { + public static void main(String[] args) { + HelloModules.doSomething(); + + final var services = ServiceLoader.load(HelloInterface.class); + final var service = services.iterator().next(); + service.sayHello(); + } +} + diff --git a/testsuite/modules/simple-modules/main.app/module-info.java b/testsuite/modules/simple-modules/main.app/module-info.java new file mode 100644 index 0000000..d7e9447 --- /dev/null +++ b/testsuite/modules/simple-modules/main.app/module-info.java @@ -0,0 +1,6 @@ +module main.app { + requires hello.modules; + + uses dev.katkak.modules.hello.HelloInterface; +} + From a451327a6a6cfe4ec3a608018f63fb363c7d9e05 Mon Sep 17 00:00:00 2001 From: ktkk Date: Mon, 6 Jul 2026 15:19:00 +0000 Subject: [PATCH 22/31] Add annotation attribute constant pool info getters --- src/Class/AttributeInfo.zig | 39 ++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/src/Class/AttributeInfo.zig b/src/Class/AttributeInfo.zig index 5814295..6bbf7b5 100644 --- a/src/Class/AttributeInfo.zig +++ b/src/Class/AttributeInfo.zig @@ -730,6 +730,11 @@ pub const StackMapTable = struct { .offset_delta = try input.takeInt(u16, .big), }, }, + 251 => .{ + .same_frame_extended = .{ + .offset_delta = try input.takeInt(u16, .big), + }, + }, 252...254 => blk: { const offset_delta = try input.takeInt(u16, .big); @@ -796,12 +801,8 @@ pub const StackMapTable = struct { long, null, uninitialized_this, - object: struct { - cpool_index: u16, - }, - uninitialized: struct { - cpool_index: u16, - }, + object: Object, + uninitialized: Uninitialized, pub const Tag = enum(u8) { top = 0, @@ -815,6 +816,18 @@ pub const StackMapTable = struct { uninitialized = 8, }; + pub const Object = struct { + cpool_index: u16, + + pub fn class(self: *const Object, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.cpool_index, .class); + } + }; + + pub const Uninitialized = struct { + offset: u16, + }; + 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; @@ -834,7 +847,7 @@ pub const StackMapTable = struct { }, .uninitialized => .{ .uninitialized = .{ - .cpool_index = try input.takeInt(u16, .big), + .offset = try input.takeInt(u16, .big), }, }, }; @@ -1301,12 +1314,20 @@ pub const ElementValue = union(enum) { pub const ElementValuePair = struct { element_name_index: u16, value: ElementValue, + + const Self = @This(); + + pub fn elementName(self: *const Self, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.element_name_index, .utf8); + } }; pub const Annotation = struct { type_index: u16, element_value_pairs: []ElementValuePair, + const Self = @This(); + pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Annotation { const type_index = try input.takeInt(u16, .big); @@ -1332,6 +1353,10 @@ pub const Annotation = struct { } allocator.free(self.element_value_pairs); } + + pub fn @"type"(self: *const Self, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.type_index, .utf8); + } }; pub const RuntimeVisibleAnnotations = struct { From 3a5fd90d14d54f308b20df60d58b76bdb5cb8d3e Mon Sep 17 00:00:00 2001 From: ktkk Date: Mon, 6 Jul 2026 21:55:57 +0200 Subject: [PATCH 23/31] Format stack map table attribute --- src/Class/AttributeInfo.zig | 137 ++++++++++++++++++++++++++++++- testsuite/classes/java/Main.java | 19 +++-- 2 files changed, 149 insertions(+), 7 deletions(-) diff --git a/src/Class/AttributeInfo.zig b/src/Class/AttributeInfo.zig index 6bbf7b5..8747ed2 100644 --- a/src/Class/AttributeInfo.zig +++ b/src/Class/AttributeInfo.zig @@ -332,7 +332,20 @@ pub const AttributeInfo = union(enum) { try a.indentedFormat(w, depth, attr_indent + 1, constant_pool); } }, - //.stack_map_table, + .stack_map_table => |attr| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("entries:\n"); + var stack_map_frame_indent = indent; + for (attr.entries, 0..) |*stack_map_frame, i| { + stack_map_frame_indent += 1; + defer stack_map_frame_indent -= 1; + + try w.splatByteAll(' ', depth * stack_map_frame_indent); + try w.print("{d}:\n", .{ i }); + + try stack_map_frame.indentedFormat(w, depth, stack_map_frame_indent + 1, constant_pool); + } + }, .exceptions => |attr| { try w.splatByteAll(' ', depth * indent); _ = try w.write("exceptions:\n"); @@ -791,6 +804,103 @@ pub const StackMapTable = struct { else => {}, } } + + pub fn indentedFormat( + self: *const StackMapFrame, + w: *std.Io.Writer, + depth: usize, + indent: u8, + constant_pool: ConstantPool, + ) std.Io.Writer.Error!void { + try w.splatByteAll(' ', depth * indent); + try w.print("frame_type: {s}\n", .{ @tagName(self.*) }); + + switch (self.*) { + .same_locals_1_stack_item_frame => |same_locals_1_stack_item_frame| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("stack:\n"); + var stack_indent = indent; + for (&same_locals_1_stack_item_frame.stack, 0..) |*info, i| { + stack_indent += 1; + defer stack_indent -= 1; + + try w.splatByteAll(' ', depth * stack_indent); + try w.print("{d}:\n", .{ i }); + + try info.indentedFormat(w, depth, stack_indent + 1, constant_pool); + } + }, + .same_locals_1_stack_item_frame_extended => |same_locals_1_stack_item_frame_extended| { + try w.splatByteAll(' ', depth * indent); + try w.print("offset_delta: {d}\n", .{ same_locals_1_stack_item_frame_extended.offset_delta }); + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("stack:\n"); + var stack_indent = indent; + for (&same_locals_1_stack_item_frame_extended.stack, 0..) |*info, i| { + stack_indent += 1; + defer stack_indent -= 1; + + try w.splatByteAll(' ', depth * stack_indent); + try w.print("{d}:\n", .{ i }); + + try info.indentedFormat(w, depth, stack_indent + 1, constant_pool); + } + }, + .chop_frame => |chop_frame| { + try w.splatByteAll(' ', depth * indent); + try w.print("offset_delta: {d}\n", .{ chop_frame.offset_delta }); + }, + .append_frame => |append_frame| { + try w.splatByteAll(' ', depth * indent); + try w.print("offset_delta: {d}\n", .{ append_frame.offset_delta }); + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("locals:\n"); + var locals_indent = indent; + for (append_frame.locals, 0..) |*info, i| { + locals_indent += 1; + defer locals_indent -= 1; + + try w.splatByteAll(' ', depth * locals_indent); + try w.print("{d}:\n", .{ i }); + + try info.indentedFormat(w, depth, locals_indent + 1, constant_pool); + } + }, + .full_frame => |full_frame| { + try w.splatByteAll(' ', depth * indent); + try w.print("offset_delta: {d}\n", .{ full_frame.offset_delta }); + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("locals:\n"); + var locals_indent = indent; + for (full_frame.locals, 0..) |*info, i| { + locals_indent += 1; + defer locals_indent -= 1; + + try w.splatByteAll(' ', depth * locals_indent); + try w.print("{d}:\n", .{ i }); + + try info.indentedFormat(w, depth, locals_indent + 1, constant_pool); + } + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("stack:\n"); + var stack_indent = indent; + for (full_frame.stack, 0..) |*info, i| { + stack_indent += 1; + defer stack_indent -= 1; + + try w.splatByteAll(' ', depth * stack_indent); + try w.print("{d}:\n", .{ i }); + + try info.indentedFormat(w, depth, stack_indent + 1, constant_pool); + } + }, + else => {}, + } + } }; pub const VerificationTypeInfo = union(VerificationTypeInfo.Tag) { @@ -852,6 +962,31 @@ pub const StackMapTable = struct { }, }; } + + pub fn indentedFormat( + self: *const VerificationTypeInfo, + w: *std.Io.Writer, + depth: usize, + indent: u8, + constant_pool: ConstantPool, + ) std.Io.Writer.Error!void { + try w.splatByteAll(' ', depth * indent); + try w.print("tag: {s}\n", .{ @tagName(self.*) }); + + switch (self.*) { + .object => |object| { + const class = object.class(constant_pool) catch return error.WriteFailed; + try w.splatByteAll(' ', depth * indent); + _ = try w.write("class:\n"); + try class.indentedFormat(w, depth, indent + 1, constant_pool); + }, + .uninitialized => |uninitialized| { + try w.splatByteAll(' ', depth * indent); + try w.print("offset: {d}\n", .{ uninitialized.offset }); + }, + else => {}, + } + } }; pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { diff --git a/testsuite/classes/java/Main.java b/testsuite/classes/java/Main.java index ec77f66..01f523a 100644 --- a/testsuite/classes/java/Main.java +++ b/testsuite/classes/java/Main.java @@ -1,18 +1,26 @@ import java.util.function.Supplier; -@CustomAttribute 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() { - new Main().sayHello(); + public static void main(String[] args) { + final var main = new Main(); + main.sayHello(); + + try { + main.throwSomething(); + } catch (Throwable e) { + System.err.println("Caught " + e.getMessage()); + } finally { + System.err.println("Finally block"); + } } - + @Override public void run() { - Main.main(); + Main.main(new String[] { "Hello" }); } @Override @@ -38,4 +46,3 @@ public class Main implements Runnable, Supplier, Interface { private static final String name = "Inner John"; } } - From f12bcd2911900cf37c0962ac84841a9dab2747da Mon Sep 17 00:00:00 2001 From: ktkk Date: Tue, 7 Jul 2026 13:38:46 +0000 Subject: [PATCH 24/31] 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(); From 89e548c8ccc701dc35927714c90f50d8bbef87ce Mon Sep 17 00:00:00 2001 From: ktkk Date: Tue, 7 Jul 2026 23:23:38 +0200 Subject: [PATCH 25/31] Introduce constant pool info formatting helper --- src/Class/ConstantPool.zig | 164 ++++++++++++++++--------------------- 1 file changed, 70 insertions(+), 94 deletions(-) diff --git a/src/Class/ConstantPool.zig b/src/Class/ConstantPool.zig index 6c5afe3..54c233f 100644 --- a/src/Class/ConstantPool.zig +++ b/src/Class/ConstantPool.zig @@ -116,16 +116,28 @@ pub const Info = union(Info.Tag) { pub const Float = struct { bytes: u32, + + pub fn float(self: *const Float) f32 { + return @bitCast(self.bytes); + } }; pub const Long = struct { high_bytes: u32, low_bytes: u32, + + pub fn long(self: *const Long) u64 { + return (@as(u64, self.high_bytes) << 32) | self.low_bytes; + } }; pub const Double = struct { high_bytes: u32, low_bytes: u32, + + pub fn double(self: *const Double) f64 { + return @bitCast((@as(u64, self.high_bytes) << 32) | self.low_bytes); + } }; pub const NameAndType = struct { @@ -349,12 +361,12 @@ pub const Info = union(Info.Tag) { .interface_method_ref => |interface_method_ref| try w.print("#{d}.#{d}", .{ interface_method_ref.class_index, interface_method_ref.name_and_type_index }), .string => |string| try w.print("#{d}", .{ string.string_index }), .integer => |integer| try w.print("{d}", .{ integer.bytes }), - .float => |float| try w.print("{d}", .{ @as(f32, @bitCast(float.bytes)) }), - .long => |long| try w.print("{d}", .{ (@as(u64, long.high_bytes) << 32) | long.low_bytes }), - .double => |double| try w.print("{d}", .{ @as(f64, @bitCast((@as(u64, double.high_bytes) << 32) | double.low_bytes)) }), + .float => |float| try w.print("{d}", .{ float.float() }), + .long => |long| try w.print("{d}", .{ long.long() }), + .double => |double| try w.print("{d}", .{ double.double() }), .name_and_type => |name_and_type| try w.print("#{d}:#{d}", .{ name_and_type.name_index, name_and_type.descriptor_index }), .utf8 => |utf8| try w.print("\"{s}\"", .{ utf8.bytes }), - .method_handle => |method_handle| try w.print("{s} #{d}", .{ @tagName(method_handle.reference_kind), method_handle.reference_index }), + .method_handle => |method_handle| try w.print("{t} #{d}", .{ method_handle.reference_kind, method_handle.reference_index }), .method_type => |method_type| try w.print("#{d}", .{ method_type.descriptor_index }), .dynamic => |dynamic| try w.print("#{d} #{d}", .{ dynamic.bootstrap_method_attr_index, dynamic.name_and_type_index }), .invoke_dynamic => |invoke_dynamic| try w.print("#{d} #{d}", .{ invoke_dynamic.bootstrap_method_attr_index, invoke_dynamic.name_and_type_index }), @@ -371,128 +383,75 @@ pub const Info = union(Info.Tag) { constant_pool: Self, ) std.Io.Writer.Error!void { try w.splatByteAll(' ', depth * indent); - try w.print("constant_value_type: {s}\n", .{ @tagName(self.*) }); + try w.print("constant_value_type: {t}\n", .{ self.* }); switch (self.*) { - .class => |class| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("name:\n"); - const class_name = class.name(constant_pool) catch return error.WriteFailed; - try class_name.indentedFormat(w, depth, indent + 1, constant_pool); + .class => |*class| { + try formatInfo(w, depth, indent, "name", class, .name, constant_pool); }, - .field_ref => |field_ref| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("class:\n"); - const class = field_ref.class(constant_pool) catch return error.WriteFailed; - try class.indentedFormat(w, depth, indent + 1, constant_pool); + .field_ref => |*field_ref| { + try formatInfo(w, depth, indent, "class", field_ref, .class, constant_pool); - try w.splatByteAll(' ', depth * indent); - _ = try w.write("name_and_type:\n"); - const name_and_type = field_ref.nameAndType(constant_pool) catch return error.WriteFailed; - try name_and_type.indentedFormat(w, depth, indent + 1, constant_pool); + try formatInfo(w, depth, indent, "name_and_type", field_ref, .nameAndType, constant_pool); }, - .method_ref => |method_ref| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("class:\n"); - const class = method_ref.class(constant_pool) catch return error.WriteFailed; - try class.indentedFormat(w, depth, indent + 1, constant_pool); + .method_ref => |*method_ref| { + try formatInfo(w, depth, indent, "class", method_ref, .class, constant_pool); - try w.splatByteAll(' ', depth * indent); - _ = try w.write("name_and_type:\n"); - const name_and_type = method_ref.nameAndType(constant_pool) catch return error.WriteFailed; - try name_and_type.indentedFormat(w, depth, indent + 1, constant_pool); + try formatInfo(w, depth, indent, "name_and_type", method_ref, .nameAndType, constant_pool); }, - .interface_method_ref => |interface_method_ref| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("class:\n"); - const class = interface_method_ref.class(constant_pool) catch return error.WriteFailed; - try class.indentedFormat(w, depth, indent + 1, constant_pool); + .interface_method_ref => |*interface_method_ref| { + try formatInfo(w, depth, indent, "class", interface_method_ref, .class, constant_pool); - try w.splatByteAll(' ', depth * indent); - _ = try w.write("name_and_type:\n"); - const name_and_type = interface_method_ref.nameAndType(constant_pool) catch return error.WriteFailed; - try name_and_type.indentedFormat(w, depth, indent + 1, constant_pool); + try formatInfo(w, depth, indent, "name_and_type", interface_method_ref, .nameAndType, constant_pool); }, - .string => |string| { - const s = string.string(constant_pool) catch return error.WriteFailed; - try s.indentedFormat(w, depth, indent + 1, constant_pool); + .string => |*string| { + try formatInfo(w, depth, indent, "string", string, .string, constant_pool); }, - .integer => |integer| { + .integer => |*integer| { try w.splatByteAll(' ', depth * indent); try w.print("integer: {d}\n", .{ integer.bytes }); }, - .float => |float| { - const f: f32 = @bitCast(float.bytes); - + .float => |*float| { try w.splatByteAll(' ', depth * indent); - try w.print("float: {d}\n", .{ f }); + try w.print("float: {d}\n", .{ float.float() }); }, - .long => |long| { - const l: u64 = (@as(u64, long.high_bytes) << 32) | long.low_bytes; - + .long => |*long| { try w.splatByteAll(' ', depth * indent); - try w.print("long: {d}\n", .{ l }); + try w.print("long: {d}\n", .{ long.long() }); }, - .double => |double| { - const l: u64 = (@as(u64, double.high_bytes) << 32) | double.low_bytes; - const d: f64 = @bitCast(l); - + .double => |*double| { try w.splatByteAll(' ', depth * indent); - try w.print("double: {d}\n", .{ d }); + try w.print("double: {d}\n", .{ double.double() }); }, - .name_and_type => |name_and_type| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("name:\n"); - const name = name_and_type.name(constant_pool) catch return error.WriteFailed; - try name.indentedFormat(w, depth, indent + 1, constant_pool); + .name_and_type => |*name_and_type| { + try formatInfo(w, depth, indent, "name", name_and_type, .name, constant_pool); - try w.splatByteAll(' ', depth * indent); - _ = try w.write("descriptor:\n"); - const descriptor = name_and_type.descriptor(constant_pool) catch return error.WriteFailed; - try descriptor.indentedFormat(w, depth, indent + 1, constant_pool); + try formatInfo(w, depth, indent, "descriptor", name_and_type, .descriptor, constant_pool); }, - .utf8 => |utf8| { + .utf8 => |*utf8| { try w.splatByteAll(' ', depth * indent); try w.print("bytes: \"{s}\"\n", .{ utf8.bytes }); }, - .method_handle => |method_handle| { + .method_handle => |*method_handle| { try w.splatByteAll(' ', depth * indent); try w.print("kind: {t}\n", .{ method_handle.reference_kind }); - try w.splatByteAll(' ', depth * indent); - _ = try w.write("reference:\n"); - const reference = method_handle.reference(constant_pool) catch return error.WriteFailed; - try reference.indentedFormat(w, depth, indent + 1, constant_pool); + try formatInfo(w, depth, indent, "descriptor", method_handle, .reference, constant_pool); }, - .method_type => |method_type| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("descriptor:\n"); - const descriptor = method_type.descriptor(constant_pool) catch return error.WriteFailed; - try descriptor.indentedFormat(w, depth, indent + 1, constant_pool); + .method_type => |*method_type| { + try formatInfo(w, depth, indent, "descriptor", method_type, .descriptor, constant_pool); }, - .dynamic => |dynamic| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("name_and_type:\n"); - const name_and_type = dynamic.nameAndType(constant_pool) catch return error.WriteFailed; - try name_and_type.indentedFormat(w, depth, indent + 1, constant_pool); + .dynamic => |*dynamic| { + try formatInfo(w, depth, indent, "name_and_type", dynamic, .nameAndType, constant_pool); }, - .invoke_dynamic => |invoke_dynamic| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("name_and_type:\n"); - const name_and_type = invoke_dynamic.nameAndType(constant_pool) catch return error.WriteFailed; - try name_and_type.indentedFormat(w, depth, indent + 1, constant_pool); + .invoke_dynamic => |*invoke_dynamic| { + try formatInfo(w, depth, indent, "name_and_type", invoke_dynamic, .nameAndType, constant_pool); }, - .module => |module| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("name:\n"); - const name = module.name(constant_pool) catch return error.WriteFailed; - try name.indentedFormat(w, depth, indent + 1, constant_pool); + .module => |*module| { + try formatInfo(w, depth, indent, "name", module, .name, constant_pool); }, - .package => |package| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("name:\n"); - const name = package.name(constant_pool) catch return error.WriteFailed; - try name.indentedFormat(w, depth, indent + 1, constant_pool); + .package => |*package| { + try formatInfo(w, depth, indent, "name", package, .name, constant_pool); }, } } @@ -569,3 +528,20 @@ pub fn getLoadableOptional(self: *const Self, index: u16) ResolveError!?Info { if (index == 0) return null; return self.getLoadable(index); } + +pub fn formatInfo( + w: *std.Io.Writer, + depth: usize, + indent: u8, + comptime name: []const u8, + context: anytype, + comptime getter: std.meta.DeclEnum(@TypeOf(context.*)), + constant_pool: Self, +) std.Io.Writer.Error!void { + const func = @field(@TypeOf(context.*), @tagName(getter)); + + try w.splatByteAll(' ', depth * indent); + _ = try w.write(name ++ ":\n"); + const info = func(context, constant_pool) catch return error.WriteFailed; + try info.indentedFormat(w, depth, indent + 1, constant_pool); +} From a47405e90365795aee07aa60a15126914c481d84 Mon Sep 17 00:00:00 2001 From: ktkk Date: Wed, 8 Jul 2026 14:24:18 +0000 Subject: [PATCH 26/31] 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, From 76c436714f457db4889db02060190e6e4f8d17cc Mon Sep 17 00:00:00 2001 From: ktkk Date: Wed, 8 Jul 2026 19:24:29 +0200 Subject: [PATCH 27/31] Fix method parameter attribute name --- src/Class/AttributeInfo.zig | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Class/AttributeInfo.zig b/src/Class/AttributeInfo.zig index f54c640..05ab95c 100644 --- a/src/Class/AttributeInfo.zig +++ b/src/Class/AttributeInfo.zig @@ -2289,13 +2289,15 @@ pub const MethodParameters = struct { try w.print("flags: {f}\n", .{ self.access_flags }); const name_ = self.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); + if (name_) |n| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("name:\n"); + try n.indentedFormat(w, depth, indent + 1, constant_pool); + } } - pub fn name(self: *const Parameter, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { - return constant_pool.getTag(self.name_index, .utf8); + pub fn name(self: *const Parameter, constant_pool: ConstantPool) ResolveError!?ConstantPool.Info { + return constant_pool.getTagOptional(self.name_index, .utf8); } }; From 5ff7a358f955ef0aad9bd47e9b784ac96f3b8c48 Mon Sep 17 00:00:00 2001 From: ktkk Date: Wed, 8 Jul 2026 22:22:17 +0200 Subject: [PATCH 28/31] Format runtime visible/invisible type annotations attributes --- src/Class/AttributeInfo.zig | 193 +++++++++++++++++- .../classes/java/CustomTypeAnnotation.java | 9 + testsuite/classes/java/GenericClass.java | 14 ++ 3 files changed, 211 insertions(+), 5 deletions(-) create mode 100644 testsuite/classes/java/CustomTypeAnnotation.java create mode 100644 testsuite/classes/java/GenericClass.java diff --git a/src/Class/AttributeInfo.zig b/src/Class/AttributeInfo.zig index 05ab95c..ceadc65 100644 --- a/src/Class/AttributeInfo.zig +++ b/src/Class/AttributeInfo.zig @@ -295,11 +295,13 @@ pub const AttributeInfo = union(enum) { _ = try w.write("predefined: yes\n"); try w.splatByteAll(' ', depth * indent); - try w.print("type: {s}\n", .{ @tagName(predefined) }); + try w.print("type: {t}\n", .{ predefined }); switch (predefined) { .constant_value => |attr| { const constant_value = attr.constantValue(constant_pool) catch return error.WriteFailed; + try w.splatByteAll(' ', depth * indent); + _ = try w.write("constant_value:\n"); try constant_value.indentedFormat(w, depth, indent + 1, constant_pool); }, .code => |attr| { @@ -513,9 +515,37 @@ pub const AttributeInfo = union(enum) { try parameter_annotation.indentedFormat(w, depth, parameter_annotation_indent + 1, constant_pool); } }, - //.runtime_visible_type_annotations, - //.runtime_invisible_type_annotations, - //.annotation_default, + .runtime_visible_type_annotations => |attr| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("type_annotations:\n"); + var type_annotation_indent = indent; + for (attr.type_annotations, 0..) |type_annotation, i| { + type_annotation_indent += 1; + defer type_annotation_indent -= 1; + + try w.splatByteAll(' ', depth * type_annotation_indent); + try w.print("{d}:\n", .{ i }); + try type_annotation.indentedFormat(w, depth, type_annotation_indent + 1, constant_pool); + } + }, + .runtime_invisible_type_annotations => |attr| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("type_annotations:\n"); + var type_annotation_indent = indent; + for (attr.type_annotations, 0..) |type_annotation, i| { + type_annotation_indent += 1; + defer type_annotation_indent -= 1; + + try w.splatByteAll(' ', depth * type_annotation_indent); + try w.print("{d}:\n", .{ i }); + try type_annotation.indentedFormat(w, depth, type_annotation_indent + 1, constant_pool); + } + }, + .annotation_default => |attr| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("default_value:\n"); + try attr.default_value.indentedFormat(w, depth, indent + 1, constant_pool); + }, .bootstrap_methods => |attr| { try w.splatByteAll(' ', depth * indent); _ = try w.write("bootstrap_methods:\n"); @@ -613,7 +643,6 @@ pub const AttributeInfo = union(enum) { try info.indentedFormat(w, depth, class_indent + 1, constant_pool); } }, - else => {}, } }, } @@ -1971,6 +2000,22 @@ pub const TypeAnnotation = struct { start_pc: u16, length: u16, index: u16, + + pub fn indentedFormat( + self: *const Entry, + w: *std.Io.Writer, + depth: usize, + indent: u8, + ) std.Io.Writer.Error!void { + try w.splatByteAll(' ', depth * indent); + try w.print("start_pc: {d}\n", .{ self.start_pc }); + + try w.splatByteAll(' ', depth * indent); + try w.print("length: {d}\n", .{ self.length }); + + try w.splatByteAll(' ', depth * indent); + try w.print("start_pc: {d}\n", .{ self.index }); + } }; }; @@ -1986,6 +2031,72 @@ pub const TypeAnnotation = struct { offset: u16, type_argument_index: u8, }; + + pub fn indentedFormat( + self: *const TargetInfo, + w: *std.Io.Writer, + depth: usize, + indent: u8, + ) std.Io.Writer.Error!void { + try w.splatByteAll(' ', depth * indent); + try w.print("target_info_type: {t}\n", .{ self.* }); + + switch (self.*) { + .type_parameter => |*type_parameter| { + try w.splatByteAll(' ', depth * indent); + try w.print("type_parameter_index: {d}\n", .{ type_parameter.type_parameter_index }); + }, + .supertype => |*supertype| { + try w.splatByteAll(' ', depth * indent); + try w.print("supertype_index: {d}\n", .{ supertype.supertype_index }); + }, + .type_parameter_bound => |*type_parameter_bound| { + try w.splatByteAll(' ', depth * indent); + try w.print("type_parameter_index: {d}\n", .{ type_parameter_bound.type_parameter_index }); + + try w.splatByteAll(' ', depth * indent); + try w.print("bound_index: {d}\n", .{ type_parameter_bound.bound_index }); + }, + .formal_parameter => |*formal_parameter| { + try w.splatByteAll(' ', depth * indent); + try w.print("formal_parameter_index: {d}\n", .{ formal_parameter.formal_parameter_index }); + }, + .throws => |*throws| { + try w.splatByteAll(' ', depth * indent); + try w.print("throws_type_index: {d}\n", .{ throws.throws_type_index }); + }, + .localvar => |*localvar| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("table:\n"); + var entry_indent = indent; + for (localvar.table, 0..) |*entry, i| { + entry_indent += 1; + defer entry_indent -= 1; + + try w.splatByteAll(' ', depth * entry_indent); + try w.print("{d}:\n", .{ i }); + + try entry.indentedFormat(w, depth, indent); + } + }, + .@"catch" => |*@"catch"| { + try w.splatByteAll(' ', depth * indent); + try w.print("exception_table_index: {d}\n", .{ @"catch".exception_table_index }); + }, + .offset => |*offset| { + try w.splatByteAll(' ', depth * indent); + try w.print("offset: {d}\n", .{ offset.offset }); + }, + .type_argument => |*type_argument| { + try w.splatByteAll(' ', depth * indent); + try w.print("offset: {d}\n", .{ type_argument.offset }); + + try w.splatByteAll(' ', depth * indent); + try w.print("type_argument_index: {d}\n", .{ type_argument.type_argument_index }); + }, + else => {}, + } + } }; pub const TypePath = struct { @@ -1994,6 +2105,19 @@ pub const TypeAnnotation = struct { pub const Path = struct { type_path_kind: u8, type_argument_index: u8, + + pub fn indentedFormat( + self: *const Path, + w: *std.Io.Writer, + depth: usize, + indent: u8, + ) std.Io.Writer.Error!void { + try w.splatByteAll(' ', depth * indent); + try w.print("type_path_kind: {d}\n", .{ self.type_path_kind }); + + try w.splatByteAll(' ', depth * indent); + try w.print("type_argument_index: {d}\n", .{ self.type_argument_index }); + } }; pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!TypePath { @@ -2015,6 +2139,25 @@ pub const TypeAnnotation = struct { pub fn deinit(self: *TypePath, allocator: std.mem.Allocator) void { allocator.free(self.paths); } + + pub fn indentedFormat( + self: *const TypePath, + w: *std.Io.Writer, + depth: usize, + indent: u8, + ) std.Io.Writer.Error!void { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("paths:\n"); + var path_indent = indent; + for (self.paths, 0..) |path, i| { + path_indent += 1; + defer path_indent -= 1; + + try w.splatByteAll(' ', depth * path_indent); + try w.print("{d}:\n", .{ i }); + try path.indentedFormat(w, depth, path_indent + 1); + } + } }; pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { @@ -2119,6 +2262,46 @@ pub const TypeAnnotation = struct { } allocator.free(self.element_value_pairs); } + + pub fn indentedFormat( + self: *const Self, + w: *std.Io.Writer, + depth: usize, + indent: u8, + constant_pool: ConstantPool, + ) std.Io.Writer.Error!void { + try w.splatByteAll(' ', depth * indent); + try w.print("target_type: {t}\n", .{ self.target_type }); + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("target_info:\n"); + try self.target_info.indentedFormat(w, depth + 1, indent); + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("target_path:\n"); + try self.target_path.indentedFormat(w, depth + 1, indent); + + 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); + } }; pub const RuntimeVisibleTypeAnnotations = struct { diff --git a/testsuite/classes/java/CustomTypeAnnotation.java b/testsuite/classes/java/CustomTypeAnnotation.java new file mode 100644 index 0000000..b6572f7 --- /dev/null +++ b/testsuite/classes/java/CustomTypeAnnotation.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.TYPE_PARAMETER) +public @interface CustomTypeAnnotation { +} diff --git a/testsuite/classes/java/GenericClass.java b/testsuite/classes/java/GenericClass.java new file mode 100644 index 0000000..151a15a --- /dev/null +++ b/testsuite/classes/java/GenericClass.java @@ -0,0 +1,14 @@ +import java.util.function.Supplier; + +public class GenericClass<@CustomTypeAnnotation T> { + + public Supplier getSupplier(T value) { + return new Supplier() { + @Override + public T get() { + return value; + } + }; + } + +} From 39fcd51a7c3a05e90a5cc881b078501793e8e1bc Mon Sep 17 00:00:00 2001 From: ktkk Date: Tue, 14 Jul 2026 10:13:26 +0000 Subject: [PATCH 29/31] Add EnumFlags.from --- src/Class.zig | 2 +- src/Class/AttributeInfo.zig | 12 ++++++------ src/Class/FieldInfo.zig | 2 +- src/Class/MethodInfo.zig | 2 +- src/EnumFlags.zig | 6 ++++++ 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/Class.zig b/src/Class.zig index d244335..e0d0244 100644 --- a/src/Class.zig +++ b/src/Class.zig @@ -57,7 +57,7 @@ pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) (ParseError || var constant_pool: ConstantPool = try .parse(input, allocator); errdefer constant_pool.deinit(allocator); - const access_flags: ClassAccessFlags = .{ .mask = try input.takeInt(u16, .big) }; + const access_flags: ClassAccessFlags = .from(try input.takeInt(u16, .big)); const this_class = try input.takeInt(u16, .big); const super_class = try input.takeInt(u16, .big); diff --git a/src/Class/AttributeInfo.zig b/src/Class/AttributeInfo.zig index ceadc65..45eec02 100644 --- a/src/Class/AttributeInfo.zig +++ b/src/Class/AttributeInfo.zig @@ -1200,7 +1200,7 @@ pub const InnerClasses = struct { .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) }, + .inner_class_access_flags = .from(try input.takeInt(u16, .big)), }; } @@ -2491,7 +2491,7 @@ pub const MethodParameters = struct { for (parameters) |*parameter| { parameter.* = .{ .name_index = try input.takeInt(u16, .big), - .access_flags = .{ .mask = try input.takeInt(u16, .big) }, + .access_flags = .from(try input.takeInt(u16, .big)), }; } @@ -2695,7 +2695,7 @@ pub const Module = struct { pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { const module_name_index = try input.takeInt(u16, .big); - const module_flags: ModuleFlags = .{ .mask = try input.takeInt(u16, .big) }; + const module_flags: ModuleFlags = .from(try input.takeInt(u16, .big)); const module_version_index = try input.takeInt(u16, .big); const requires_count = try input.takeInt(u16, .big); @@ -2703,7 +2703,7 @@ pub const Module = struct { errdefer allocator.free(requires); for (requires) |*r| { const requires_index = try input.takeInt(u16, .big); - const requires_flags: Requires.RequiresFlags = .{ .mask = try input.takeInt(u16, .big) }; + const requires_flags: Requires.RequiresFlags = .from(try input.takeInt(u16, .big)); const requires_version_index = try input.takeInt(u16, .big); r.* = .{ .requires_index = requires_index, @@ -2717,7 +2717,7 @@ pub const Module = struct { errdefer allocator.free(exports); for (exports) |*e| { const exports_index = try input.takeInt(u16, .big); - const exports_flags: Exports.ExportsFlags = .{ .mask = try input.takeInt(u16, .big) }; + const exports_flags: Exports.ExportsFlags = .from(try input.takeInt(u16, .big)); const exports_to_count = try input.takeInt(u16, .big); const exports_to_indeces = try allocator.alloc(u16, exports_to_count); errdefer allocator.free(exports_to_indeces); @@ -2736,7 +2736,7 @@ pub const Module = struct { errdefer allocator.free(opens); for (opens) |*o| { const opens_index = try input.takeInt(u16, .big); - const opens_flags: Opens.OpensFlags = .{ .mask = try input.takeInt(u16, .big) }; + const opens_flags: Opens.OpensFlags = .from(try input.takeInt(u16, .big)); const opens_to_count = try input.takeInt(u16, .big); const opens_to_indeces = try allocator.alloc(u16, opens_to_count); errdefer allocator.free(opens_to_indeces); diff --git a/src/Class/FieldInfo.zig b/src/Class/FieldInfo.zig index 46746e6..fb16e55 100644 --- a/src/Class/FieldInfo.zig +++ b/src/Class/FieldInfo.zig @@ -27,7 +27,7 @@ pub const FieldAccessFlags = EnumFlags(enum(u16) { }); pub fn parse(input: *std.Io.Reader, constant_pool: ConstantPool, allocator: std.mem.Allocator) !Self { - const access_flags: FieldAccessFlags = .{ .mask = try input.takeInt(u16, .big) }; + const access_flags: FieldAccessFlags = .from(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, constant_pool, allocator); diff --git a/src/Class/MethodInfo.zig b/src/Class/MethodInfo.zig index 2650061..7d97300 100644 --- a/src/Class/MethodInfo.zig +++ b/src/Class/MethodInfo.zig @@ -30,7 +30,7 @@ pub const MethodAccessFlags = EnumFlags(enum(u16) { }); pub fn parse(input: *std.Io.Reader, constant_pool: ConstantPool, allocator: std.mem.Allocator) !Self { - const access_flags: MethodAccessFlags = .{ .mask = try input.takeInt(u16, .big) }; + const access_flags: MethodAccessFlags = .from(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, constant_pool, allocator); diff --git a/src/EnumFlags.zig b/src/EnumFlags.zig index 9b5da99..80c7e40 100644 --- a/src/EnumFlags.zig +++ b/src/EnumFlags.zig @@ -10,6 +10,12 @@ pub fn EnumFlags(comptime E: type) type { pub const empty: Self = .{ .data = 0 }; + pub fn from(mask: BackingInt) Self { + return .{ + .mask = mask, + }; + } + pub fn contains(self: Self, flag: E) bool { return (self.mask & @intFromEnum(flag)) != 0; } From 10c8c9b98f936b655f6fce82b771f3c10dc16093 Mon Sep 17 00:00:00 2001 From: ktkk Date: Thu, 16 Jul 2026 08:19:34 +0000 Subject: [PATCH 30/31] Update record attribute formatting --- src/Class/AttributeInfo.zig | 2 +- testsuite/classes/java/Record.java | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 testsuite/classes/java/Record.java diff --git a/src/Class/AttributeInfo.zig b/src/Class/AttributeInfo.zig index 45eec02..5fbdc9f 100644 --- a/src/Class/AttributeInfo.zig +++ b/src/Class/AttributeInfo.zig @@ -625,7 +625,7 @@ pub const AttributeInfo = union(enum) { try w.splatByteAll(' ', depth * component_indent); try w.print("{d}:\n", .{ i }); - try component.indentedFormat(w, depth, component_indent, constant_pool); + try component.indentedFormat(w, depth, component_indent + 1, constant_pool); } }, .permitted_subclasses => |attr| { diff --git a/testsuite/classes/java/Record.java b/testsuite/classes/java/Record.java new file mode 100644 index 0000000..4eb2159 --- /dev/null +++ b/testsuite/classes/java/Record.java @@ -0,0 +1,2 @@ +public record Record(String name, int age) {} + From bc8d525a3e419fed3943d9dfa7c523b7881dd89e Mon Sep 17 00:00:00 2001 From: ktkk Date: Thu, 16 Jul 2026 08:34:33 +0000 Subject: [PATCH 31/31] Don't store allocator --- src/Class.zig | 37 +++++++++++++++++-------------------- src/main.zig | 2 +- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/Class.zig b/src/Class.zig index e0d0244..09a3846 100644 --- a/src/Class.zig +++ b/src/Class.zig @@ -7,8 +7,6 @@ pub const MethodInfo = @import("Class/MethodInfo.zig"); pub const AttributeInfo = @import("Class/AttributeInfo.zig").AttributeInfo; pub const ConstantPool = @import("Class/ConstantPool.zig"); -allocator: std.mem.Allocator, - magic: u32, minor_version: u16, major_version: u16, @@ -90,7 +88,6 @@ pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) (ParseError || } return .{ - .allocator = allocator, .magic = magic, .minor_version = minor_version, .major_version = major_version, @@ -153,37 +150,37 @@ pub fn parseAttributes(input: *std.Io.Reader, constant_pool: ConstantPool, alloc return attributes; } -pub fn deinit(self: *Self) void { - self.constant_pool.deinit(self.allocator); - self.freeInterfaces(); - self.freeFields(); - self.freeMethods(); - self.freeAttributes(); +pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + self.constant_pool.deinit(allocator); + self.freeInterfaces(allocator); + self.freeFields(allocator); + self.freeMethods(allocator); + self.freeAttributes(allocator); } -fn freeInterfaces(self: *Self) void { - self.allocator.free(self.interfaces); +fn freeInterfaces(self: *Self, allocator: std.mem.Allocator) void { + allocator.free(self.interfaces); } -fn freeFields(self: *Self) void { +fn freeFields(self: *Self, allocator: std.mem.Allocator) void { for (self.fields) |*field_info| { - field_info.deinit(self.allocator); + field_info.deinit(allocator); } - self.allocator.free(self.fields); + allocator.free(self.fields); } -fn freeMethods(self: *Self) void { +fn freeMethods(self: *Self, allocator: std.mem.Allocator) void { for (self.methods) |*method_info| { - method_info.deinit(self.allocator); + method_info.deinit(allocator); } - self.allocator.free(self.methods); + allocator.free(self.methods); } -fn freeAttributes(self: *Self) void { +fn freeAttributes(self: *Self, allocator: std.mem.Allocator) void { for (self.attributes) |*attr| { - attr.deinit(self.allocator); + attr.deinit(allocator); } - self.allocator.free(self.attributes); + allocator.free(self.attributes); } pub fn jsonStringify(self: *const Self, jws: *std.json.Stringify) !void { diff --git a/src/main.zig b/src/main.zig index 8c4831e..bf3f1f1 100644 --- a/src/main.zig +++ b/src/main.zig @@ -50,7 +50,7 @@ pub fn main(init: std.process.Init) !void { }, else => return err, }; - defer class.deinit(); + defer class.deinit(allocator); try stdout.print("{f}", .{ class });