java-class-parser/src/Class/AttributeInfo.zig

3091 lines
119 KiB
Zig

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 + 1, 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 = .from(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 = .from(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 = .from(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 = .from(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 = .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);
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 = .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);
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);
}
};