const std = @import("std"); const EnumFlags = @import("../EnumFlags.zig").EnumFlags; const Class = @import("../Class.zig"); const ConstantPool = Class.ConstantPool; const ParseError = Class.ParseError; const ResolveError = Class.ResolveError; 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, 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(); 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); 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; if (std.mem.eql(u8, name, "ConstantValue")) { return .{ .predefined = .{ .constant_value = try ConstantValue.parse(limited), }, }; } else if (std.mem.eql(u8, name, "Code")) { return .{ .predefined = .{ .code = try Code.parse(limited, constant_pool, allocator), }, }; } else if (std.mem.eql(u8, name, "StackMapTable")) { return .{ .predefined = .{ .stack_map_table = try StackMapTable.parse(limited, allocator), }, }; } else if (std.mem.eql(u8, name, "Exceptions")) { return .{ .predefined = .{ .exceptions = try Exceptions.parse(limited, allocator), }, }; } else if (std.mem.eql(u8, name, "InnerClasses")) { return .{ .predefined = .{ .inner_classes = try InnerClasses.parse(limited, allocator), }, }; } else if (std.mem.eql(u8, name, "EnclosingMethod")) { return .{ .predefined = .{ .enclosing_method = try EnclosingMethod.parse(limited), }, }; } else if (std.mem.eql(u8, name, "Synthetic")) { return .{ .predefined = .{ .synthetic = .{}, }, }; } else if (std.mem.eql(u8, name, "Signature")) { return .{ .predefined = .{ .signature = try Signature.parse(limited), }, }; } else if (std.mem.eql(u8, name, "SourceFile")) { return .{ .predefined = .{ .source_file = try SourceFile.parse(limited), }, }; } 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, name, "LineNumberTable")) { return .{ .predefined = .{ .line_number_table = try LineNumberTable.parse(limited, allocator), }, }; } else if (std.mem.eql(u8, name, "LocalVariableTable")) { return .{ .predefined = .{ .local_variable_table = try LocalVariableTable.parse(limited, allocator), }, }; } 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, name, "Deprecated")) { return .{ .predefined = .{ .deprecated = .{}, }, }; } else if (std.mem.eql(u8, name, "RuntimeVisibleAnnotations")) { return .{ .predefined = .{ .runtime_visible_annotations = try RuntimeVisibleAnnotations.parse(limited, allocator), }, }; } else if (std.mem.eql(u8, name, "RuntimeInvisibleAnnotations")) { return .{ .predefined = .{ .runtime_invisible_annotations = try RuntimeInvisibleAnnotations.parse(limited, allocator), }, }; } 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, name, "RuntimeInvisibleParameterAnnotations")) { return .{ .predefined = .{ .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 = .{ .annotation_default = try AnnotationDefault.parse(limited, allocator), }, }; } else if (std.mem.eql(u8, name, "BootstrapMethods")) { return .{ .predefined = .{ .bootstrap_methods = try BootstrapMethods.parse(limited, allocator), }, }; } else if (std.mem.eql(u8, name, "MethodParameters")) { return .{ .predefined = .{ .method_parameters = try MethodParameters.parse(limited, allocator), }, }; } else if (std.mem.eql(u8, name, "Module")) { return .{ .predefined = .{ .module = try Module.parse(limited, allocator), }, }; } else if (std.mem.eql(u8, name, "ModulePackages")) { return .{ .predefined = .{ .module_packages = try ModulePackages.parse(limited, allocator), }, }; } else if (std.mem.eql(u8, name, "ModuleMainClass")) { return .{ .predefined = .{ .module_main_class = try ModuleMainClass.parse(limited), }, }; } else if (std.mem.eql(u8, name, "NestHost")) { return .{ .predefined = .{ .nest_host = try NestHost.parse(limited), }, }; } else if (std.mem.eql(u8, name, "NestMembers")) { return .{ .predefined = .{ .nest_members = try NestMembers.parse(limited, allocator), }, }; } else if (std.mem.eql(u8, name, "Record")) { return .{ .predefined = .{ .record = try Record.parse(limited, constant_pool, allocator), }, }; } else if (std.mem.eql(u8, name, "PermittedSubclasses")) { return .{ .predefined = .{ .permitted_subclasses = try PermittedSubclasses.parse(limited, allocator), }, }; } else { return .{ .new = try NewAttribute.parse(limited, attribute_name_index + 1, attribute_length, allocator), }; } } 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), .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), .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), .permitted_subclasses => |*permitted_subclasses| permitted_subclasses.deinit(allocator), else => {}, }, } } pub fn indentedFormat( self: *const Self, w: *std.Io.Writer, depth: usize, indent: u8, constant_pool: ConstantPool, ) std.Io.Writer.Error!void { switch (self.*) { .new => |new| { try w.splatByteAll(' ', depth * indent); _ = try w.write("predefined: no\n"); try w.splatByteAll(' ', depth * indent); _ = try w.write("name:\n"); const attribute_name = new.attributeName(constant_pool) catch return error.WriteFailed; try attribute_name.indentedFormat(w, depth, indent + 1, constant_pool); }, .predefined => |predefined| { try w.splatByteAll(' ', depth * indent); _ = try w.write("predefined: yes\n"); try w.splatByteAll(' ', depth * indent); 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| { 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 exception.indentedFormat(w, depth, exception_indent + 1, constant_pool); } 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.indentedFormat(w, depth, attr_indent + 1, constant_pool); } }, .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"); var exception_indent = indent; for (attr.exception_index_table, 0..) |exception_index, i| { exception_indent += 1; defer exception_indent -= 1; 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 info.indentedFormat(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.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.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.indentedFormat(w, depth, indent + 1, constant_pool); } }, .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 => |attr| { try w.splatByteAll(' ', depth * indent); try w.print("debug_extension: {s}\n", .{ attr.debug_extension }); }, .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 => |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 => |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 => |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 => |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"); 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"); 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 + 1, constant_pool); } }, .module => |attr| try attr.indentedFormat(w, depth, indent, constant_pool), .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 info = constant_pool.getTag(package_index, .package) catch return error.WriteFailed; try w.splatByteAll(' ', depth * package_indent); try w.print("{d}:\n", .{ i }); try 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 info = constant_pool.getTag(class_index, .class) catch return error.WriteFailed; try w.splatByteAll(' ', depth * class_indent); try w.print("{d}:\n", .{ i }); try 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 info = constant_pool.getTag(class_index, .class) catch return error.WriteFailed; try w.splatByteAll(' ', depth * class_indent); try w.print("{d}:\n", .{ i }); try info.indentedFormat(w, depth, class_indent + 1, constant_pool); } }, } }, } } }; 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 fn deinit(self: *Self, allocator: std.mem.Allocator) void { allocator.free(self.info); } pub fn attributeName(self: *const Self, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { return constant_pool.getTag(self.attribute_name_index, .utf8); } }; 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: ConstantPool) ResolveError!ConstantPool.Info { return constant_pool.get(self.constant_value_index); } }; 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 indentedFormat( self: *const ExceptionHandler, w: *std.Io.Writer, depth: usize, indent: u8, constant_pool: ConstantPool, ) 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: ConstantPool) ResolveError!?ConstantPool.Info { return constant_pool.getTagOptional(self.catch_type, .class); } }; 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); 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), }; } 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 .{ .max_stack = max_stack, .max_locals = max_locals, .code = code, .exception_table = exception_table, .attributes = attributes, }; } 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 StackMapTable = struct { entries: []StackMapFrame, const Self = @This(); 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, }, 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), }, }, 251 => .{ .same_frame_extended = .{ .offset_delta = try input.takeInt(u16, .big), }, }, 252...254 => blk: { const offset_delta = try input.takeInt(u16, .big); 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: *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); }, 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) { top, integer, float, double, long, null, uninitialized_this, object: Object, uninitialized: Uninitialized, 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 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; 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 = .{ .offset = try input.takeInt(u16, .big), }, }, }; } 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 { 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 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, depth: usize, indent: u8, constant_pool: ConstantPool, ) 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.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.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.indentedFormat(w, depth, indent + 1, constant_pool); } } 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: ConstantPool) ResolveError!?ConstantPool.Info { return constant_pool.getTagOptional(self.outer_class_info_index, .class); } pub fn innerName(self: *const InnerClass, constant_pool: ConstantPool) ResolveError!?ConstantPool.Info { return constant_pool.getTagOptional(self.inner_name_index, .utf8); } }; 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) }, }; } 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: ConstantPool) ResolveError!ConstantPool.Info { return constant_pool.getTag(self.class_index, .class); } pub fn method(self: *const EnclosingMethod, constant_pool: ConstantPool) ResolveError!?ConstantPool.Info { return constant_pool.getTagOptional(self.method_index, .name_and_type); } }; 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: ConstantPool) ResolveError!ConstantPool.Info { return constant_pool.getTag(self.signature_index, .utf8); } }; 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: ConstantPool) ResolveError!ConstantPool.Info { return constant_pool.getTag(self.source_file_index, .utf8); } }; 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 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 { 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 indentedFormat( self: *const LocalVariable, w: *std.Io.Writer, depth: usize, indent: u8, constant_pool: ConstantPool, ) 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: ConstantPool) ResolveError!ConstantPool.Info { return constant_pool.getTag(self.name_index, .utf8); } pub fn descriptor(self: *const LocalVariable, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { return constant_pool.getTag(self.descriptor_index, .utf8); } }; 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 indentedFormat( self: *const LocalVariableType, w: *std.Io.Writer, depth: usize, indent: u8, constant_pool: ConstantPool, ) 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: ConstantPool) ResolveError!ConstantPool.Info { return constant_pool.getTag(self.name_index, .utf8); } pub fn signature(self: *const LocalVariableType, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { return constant_pool.getTag(self.signature_index, .utf8); } }; 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: ConstValue, // e enum_const_value: EnumConstValue, // c class_const_value: ClassConstValue, // @ annotation_value: Annotation, // [ array_value: struct { 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 = .{ .@"type" = @enumFromInt(tag), .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_const_value = .{ .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.*) { .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); }, 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 { element_name_index: u16, value: ElementValue, 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); } }; 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); 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 .{ .type_index = type_index, .element_value_pairs = element_value_pairs, }; } 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 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); } }; pub const RuntimeVisibleAnnotations = 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(input, allocator); } return .{ .annotations = 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(input, allocator); } return .{ .annotations = annotations, }; } 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 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 { 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 input.takeInt(u16, .big); const annotations = try allocator.alloc(Annotation, num_annotations); errdefer allocator.free(annotations); for (annotations) |*annotation| { annotation.* = try .parse(input, allocator); } parameter_annotation.* = .{ .annotations = annotations, }; } return .{ .parameter_annotations = parameter_annotations, }; } 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 input.takeInt(u16, .big); const annotations = try allocator.alloc(Annotation, num_annotations); errdefer allocator.free(annotations); for (annotations) |*annotation| { annotation.* = try .parse(input, allocator); } parameter_annotation.* = .{ .annotations = annotations, }; } return .{ .parameter_annotations = parameter_annotations, }; } 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 TypeAnnotation = struct { target_type: TargetType, target_info: TargetInfo, target_path: TypePath, type_index: u16, element_value_pairs: []ElementValuePair, 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 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 }); } }; }; 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 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 { paths: []Path, 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 { 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 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 { 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 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 { 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, 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 indentedFormat( self: *const BootstrapMethod, w: *std.Io.Writer, depth: usize, indent: u8, constant_pool: ConstantPool, ) 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 info = constant_pool.getLoadable(bootstrap_argument) catch return error.WriteFailed; try w.splatByteAll(' ', depth * bootstrap_argument_indent); try w.print("{d}:\n", .{ i }); try info.indentedFormat(w, depth, bootstrap_argument_indent + 1, constant_pool); } } pub fn bootstrapMethod(self: *const BootstrapMethod, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { return constant_pool.getTag(self.bootstrap_method_ref, .method_handle); } }; 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 input.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 input.takeInt(u16, .big); } bootstrap_method.* = .{ .bootstrap_method_ref = bootstrap_method_ref, .bootstrap_arguments = bootstrap_arguments, }; } return .{ .bootstrap_methods = bootstrap_methods, }; } 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); } }; pub const MethodParameters = struct { parameters: []Parameter, const Self = @This(); pub const Parameter = struct { name_index: u16, access_flags: ParameterAccessFlags, pub const ParameterAccessFlags = EnumFlags(enum(u16) { final = 0x0010, synthetic = 0x1000, mandated = 0x8000, }); pub fn indentedFormat( self: *const Parameter, w: *std.Io.Writer, depth: usize, indent: u8, constant_pool: ConstantPool, ) 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; 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.getTagOptional(self.name_index, .utf8); } }; 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 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 fn indentedFormat( self: *const Requires, w: *std.Io.Writer, depth: usize, indent: u8, constant_pool: ConstantPool, ) 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: ConstantPool) ResolveError!ConstantPool.Info { return constant_pool.getTag(self.requires_index, .module); } pub fn requiresVersion(self: *const Requires, constant_pool: ConstantPool) ResolveError!?ConstantPool.Info { return constant_pool.getTagOptional(self.requires_version_index, .utf8); } }; 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 fn indentedFormat( self: *const Exports, w: *std.Io.Writer, depth: usize, indent: u8, constant_pool: ConstantPool, ) 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 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 info.indentedFormat(w, depth, exports_to_indent + 1, constant_pool); } } pub fn exports(self: *const Exports, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { return constant_pool.getTag(self.exports_index, .package); } }; 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 fn indentedFormat( self: *const Opens, w: *std.Io.Writer, depth: usize, indent: u8, constant_pool: ConstantPool, ) 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 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 info.indentedFormat(w, depth, opens_to_indent + 1, constant_pool); } } pub fn opens(self: *const Opens, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { return constant_pool.getTag(self.opens_index, .package); } }; 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: ConstantPool, ) 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 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 info.indentedFormat(w, depth, provides_with_indent + 1, constant_pool); } } pub fn provides(self: *const Provides, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { return constant_pool.getTag(self.provides_index, .class); } }; 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 fn indentedFormat( self: *const Module, w: *std.Io.Writer, depth: usize, indent: u8, constant_pool: ConstantPool, ) 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 + 1, 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 + 1, 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 + 1, 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 info = constant_pool.getTag(uses_index, .class) catch return error.WriteFailed; try w.splatByteAll(' ', depth * uses_indent); try w.print("{d}:\n", .{ i }); try 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 + 1, constant_pool); } } pub fn moduleName(self: *const Module, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { return constant_pool.getTag(self.module_name_index, .module); } pub fn moduleVersion(self: *const Module, constant_pool: ConstantPool) ResolveError!?ConstantPool.Info { return constant_pool.getTagOptional(self.module_version_index, .utf8); } }; 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 fn mainClass(self: *const ModuleMainClass, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { return constant_pool.getTag(self.main_class_index, .class); } }; 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 fn hostClass(self: *const NestHost, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { return constant_pool.getTag(self.host_class_index, .class); } }; 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: ConstantPool, 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 indentedFormat( self: *const RecordComponentInfo, w: *std.Io.Writer, depth: usize, indent: u8, constant_pool: ConstantPool, ) 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: ConstantPool) ResolveError!ConstantPool.Info { return constant_pool.getTag(self.name_index, .utf8); } 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: 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| { 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); } };