java-class-parser/src/Class.zig
2026-06-23 20:53:14 +02:00

1965 lines
78 KiB
Zig

const std = @import("std");
const EnumFlags = @import("EnumFlags.zig").EnumFlags;
allocator: std.mem.Allocator,
magic: u32,
minor_version: u16,
major_version: u16,
constant_pool: []?CpInfo,
access_flags: ClassAccessFlags,
this_class: u16,
super_class: u16,
interfaces: []u16,
fields: []FieldInfo,
methods: []MethodInfo,
attributes: []AttributeInfo,
const Self = @This();
const ParseError = std.Io.Reader.Error || std.mem.Allocator.Error || error {
InvalidMagicNumber,
InvalidTag,
};
const ResolveError = error {
InvalidConstantPoolIndex,
InvalidConstantType,
};
pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self {
const magic = try input.takeInt(u32, .big);
if (magic != 0xCAFEBABE) {
return ParseError.InvalidMagicNumber;
}
const minor_version = try input.takeInt(u16, .big);
const major_version = try input.takeInt(u16, .big);
const constant_pool = try parseConstantPool(input, allocator);
errdefer {
for (constant_pool) |*cp_info| {
if (cp_info.*) |*c| c.deinit(allocator);
}
allocator.free(constant_pool);
}
const access_flags: ClassAccessFlags = .{ .mask = try input.takeInt(u16, .big) };
const this_class = try input.takeInt(u16, .big);
const super_class = try input.takeInt(u16, .big);
const interfaces = try parseInterfaces(input, allocator);
errdefer allocator.free(interfaces);
const fields = try parseFields(input, allocator);
errdefer {
for (fields) |*field| {
field.deinit(allocator);
}
allocator.free(fields);
}
const methods = try parseMethods(input, allocator);
errdefer {
for (methods) |*method| {
method.deinit(allocator);
}
allocator.free(methods);
}
const attributes = try parseAttributes(input, allocator);
errdefer {
for (attributes) |*attribute| {
attribute.deinit(allocator);
}
allocator.free(attributes);
}
return .{
.allocator = allocator,
.magic = magic,
.minor_version = minor_version,
.major_version = major_version,
.constant_pool = constant_pool,
.access_flags = access_flags,
.this_class = this_class,
.super_class = super_class,
.interfaces = interfaces,
.fields = fields,
.methods = methods,
.attributes = attributes,
};
}
fn parseConstantPool(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError![]?CpInfo {
const constant_pool_size = try input.takeInt(u16, .big);
const constant_pool = try allocator.alloc(?CpInfo, constant_pool_size - 1);
errdefer allocator.free(constant_pool);
var i: usize = 0;
while (i < constant_pool_size - 1) {
defer i += 1;
const cp_info = try CpInfo.parse(input, allocator);
constant_pool[i] = cp_info;
switch (cp_info) {
.long, .double => {
i += 1;
constant_pool[i] = null;
},
else => {},
}
}
return constant_pool;
}
fn parseInterfaces(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError![]u16 {
const interfaces_count = try input.takeInt(u16, .big);
const interfaces = try allocator.alloc(u16, interfaces_count);
errdefer allocator.free(interfaces);
for (0..interfaces_count) |i| {
interfaces[i] = try input.takeInt(u16, .big);
}
return interfaces;
}
fn parseFields(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError![]FieldInfo {
const fields_count = try input.takeInt(u16, .big);
const fields = try allocator.alloc(FieldInfo, fields_count);
errdefer allocator.free(fields);
for (0..fields_count) |i| {
fields[i] = try FieldInfo.parse(input, allocator);
}
return fields;
}
fn parseMethods(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError![]MethodInfo {
const methods_count = try input.takeInt(u16, .big);
const methods = try allocator.alloc(MethodInfo, methods_count);
errdefer allocator.free(methods);
for (0..methods_count) |i| {
methods[i] = try MethodInfo.parse(input, allocator);
}
return methods;
}
fn parseAttributes(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError![]AttributeInfo {
const attributes_count = try input.takeInt(u16, .big);
const attributes = try allocator.alloc(AttributeInfo, attributes_count);
errdefer allocator.free(attributes);
for (0..attributes_count) |i| {
attributes[i] = try AttributeInfo.parse(input, allocator);
}
return attributes;
}
pub fn deinit(self: *Self) void {
self.freeConstantPool();
self.freeInterfaces();
self.freeFields();
self.freeMethods();
self.freeAttributes();
}
fn freeConstantPool(self: *Self) void {
for (self.constant_pool) |*cp_info| {
if (cp_info.*) |*c| c.deinit(self.allocator);
}
self.allocator.free(self.constant_pool);
}
fn freeInterfaces(self: *Self) void {
self.allocator.free(self.interfaces);
}
fn freeFields(self: *Self) void {
for (self.fields) |*field_info| {
field_info.deinit(self.allocator);
}
self.allocator.free(self.fields);
}
fn freeMethods(self: *Self) void {
for (self.methods) |*method_info| {
method_info.deinit(self.allocator);
}
self.allocator.free(self.methods);
}
fn freeAttributes(self: *Self) void {
for (self.attributes) |*attr| {
attr.deinit(self.allocator);
}
self.allocator.free(self.attributes);
}
pub fn jsonStringify(self: *const Self, jws: *std.json.Stringify) !void {
try jws.beginObject();
inline for (std.meta.fields(Self)) |field| {
if (comptime std.mem.eql(u8, field.name, "allocator")) continue;
try jws.objectField(field.name);
try jws.write(@field(self, field.name));
}
try jws.endObject();
}
pub fn format(self: *const Self, w: *std.Io.Writer) std.Io.Writer.Error!void {
const depth = 2;
var indent: u8 = 0;
try w.print("magic: 0x{X}\n", .{ self.magic });
try w.print("version: {d}.{d}\n", .{ self.major_version, self.minor_version });
try w.print("flags: {f}\n", .{ self.access_flags });
const this_class = self.thisClass() catch return error.WriteFailed;
_ = try w.write("this class:\n");
try this_class.format(w, depth, indent + 1, self.constant_pool);
if (self.superClass() catch return error.WriteFailed) |super_class| {
_ = try w.write("super class:\n");
try super_class.format(w, depth, indent + 1, self.constant_pool);
}
_ = try w.write("interfaces:\n");
for (self.interfaces, 0..) |interface, i| {
indent += 1;
defer indent -= 1;
if (interface >= self.constant_pool.len) return error.WriteFailed;
const cp_info = self.constant_pool[interface - 1];
if (cp_info == null or @as(CpInfo.Tag, cp_info.?) != .class) return error.WriteFailed;
try w.splatByteAll(' ', depth * indent);
try w.print("{d}:\n", .{ i });
try cp_info.?.format(w, depth, indent + 1, self.constant_pool);
}
_ = try w.write("fields:\n");
for (self.fields, 0..) |field, i| {
indent += 1;
defer indent -= 1;
try w.splatByteAll(' ', depth * indent);
try w.print("{d}:\n", .{ i });
try field.format(w, depth, indent + 1, self.allocator, self.constant_pool);
}
_ = try w.write("methods:\n");
for (self.methods, 0..) |method, i| {
indent += 1;
defer indent -= 1;
try w.splatByteAll(' ', depth * indent);
try w.print("{d}:\n", .{ i });
try method.format(w, depth, indent + 1, self.allocator, self.constant_pool);
}
_ = try w.write("attributes:\n");
for (self.attributes, 0..) |*attribute, i| {
indent += 1;
defer indent -= 1;
try w.splatByteAll(' ', depth * indent);
try w.print("{d}:\n", .{ i });
try attribute.format(w, depth, indent + 1, self.allocator, self.constant_pool);
}
}
pub fn thisClass(self: *const Self) ResolveError!CpInfo {
const this_class = self.this_class - 1;
if (this_class >= self.constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = self.constant_pool[this_class];
if (cp_info == null or @as(CpInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType;
return cp_info.?;
}
pub fn superClass(self: *const Self) ResolveError!?CpInfo {
// TODO: Determine whether this is an interface or a regular class.
// When this is an interface, assert that the super class must be Object.
if (self.super_class == 0) {
const this_class = (try self.thisClass()).class;
const this_class_name = (try this_class.name(self.constant_pool)).utf8;
if (!std.mem.eql(u8, this_class_name.bytes, "Object")) return ResolveError.InvalidConstantType;
return null;
} else {
const super_class = self.super_class - 1;
if (super_class >= self.constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = self.constant_pool[super_class];
if (cp_info == null or @as(CpInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType;
// TODO: Assert that superclass does not have ACC_FINAL flag set.
return cp_info.?;
}
}
pub const CpInfo = union(CpInfo.Tag) {
class: Class,
field_ref: FieldRef,
method_ref: MethodRef,
interface_method_ref: InterfaceMethodRef,
string: String,
integer: Integer,
float: Float,
long: Long,
double: Double,
name_and_type: NameAndType,
utf8: Utf8,
method_handle: MethodHandle,
method_type: MethodType,
invoke_dynamic: InvokeDynamic,
pub const Tag = enum(u8) {
class = 7,
field_ref = 9,
method_ref = 10,
interface_method_ref = 11,
string = 8,
integer = 3,
float = 4,
long = 5,
double = 6,
name_and_type = 12,
utf8 = 1,
method_handle = 15,
method_type = 16,
invoke_dynamic = 18,
};
pub const Class = struct {
name_index: u16,
pub fn name(self: *const Class, constant_pool: []const ?CpInfo) ResolveError!CpInfo {
const name_index = self.name_index - 1;
if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[name_index];
if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType;
return cp_info.?;
}
};
pub const FieldRef = struct {
class_index: u16,
name_and_type_index: u16,
pub fn class(self: *const FieldRef, constant_pool: []const ?CpInfo) ResolveError!CpInfo {
const class_index = self.class_index - 1;
if (class_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[class_index];
if (cp_info == null or @as(Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType;
return cp_info.?;
}
pub fn nameAndType(self: *const FieldRef, constant_pool: []const ?CpInfo) ResolveError!CpInfo {
const name_and_type_index = self.name_and_type_index - 1;
if (name_and_type_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[name_and_type_index];
if (cp_info == null or @as(Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType;
return cp_info.?;
}
};
pub const MethodRef = struct {
class_index: u16,
name_and_type_index: u16,
pub fn class(self: *const MethodRef, constant_pool: []const ?CpInfo) ResolveError!CpInfo {
const class_index = self.class_index - 1;
if (class_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[class_index];
if (cp_info == null or @as(Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType;
return cp_info.?;
}
pub fn nameAndType(self: *const MethodRef, constant_pool: []const ?CpInfo) ResolveError!CpInfo {
const name_and_type_index = self.name_and_type_index - 1;
if (name_and_type_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[name_and_type_index];
if (cp_info == null or @as(Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType;
return cp_info.?;
}
};
pub const InterfaceMethodRef = struct {
class_index: u16,
name_and_type_index: u16,
pub fn class(self: *const InterfaceMethodRef, constant_pool: []const ?CpInfo) ResolveError!CpInfo {
const class_index = self.class_index - 1;
if (class_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[class_index];
if (cp_info == null or @as(Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType;
return cp_info.?;
}
pub fn nameAndType(self: *const InterfaceMethodRef, constant_pool: []const ?CpInfo) ResolveError!CpInfo {
const name_and_type_index = self.name_and_type_index - 1;
if (name_and_type_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[name_and_type_index];
if (cp_info == null or @as(Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType;
return cp_info.?;
}
};
pub const String = struct {
string_index: u16,
pub fn string(self: *const String, constant_pool: []const ?CpInfo) ResolveError!CpInfo {
const string_index = self.string_index - 1;
if (string_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[string_index];
if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType;
return cp_info.?;
}
};
pub const Integer = struct {
bytes: u32,
};
pub const Float = struct {
bytes: u32,
};
pub const Long = struct {
high_bytes: u32,
low_bytes: u32,
};
pub const Double = struct {
high_bytes: u32,
low_bytes: u32,
};
pub const NameAndType = struct {
name_index: u16,
descriptor_index: u16,
pub fn name(self: *const NameAndType, constant_pool: []const ?CpInfo) ResolveError!CpInfo {
const name_index = self.name_index - 1;
if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[name_index];
if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType;
return cp_info.?;
}
pub fn descriptor(self: *const NameAndType, constant_pool: []const ?CpInfo) ResolveError!CpInfo {
const descriptor_index = self.descriptor_index - 1;
if (descriptor_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[descriptor_index];
if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType;
return cp_info.?;
}
};
pub const Utf8 = struct {
bytes: []const u8,
};
pub const MethodHandle = struct {
reference_kind: Kind,
reference_index: u16,
pub const Kind = enum(u8) {
get_field = 1,
get_static = 2,
put_field = 3,
put_static = 4,
invoke_virtual = 5,
invoke_static = 6,
invoke_special = 7,
new_invoke_special = 8,
invoke_interface = 9,
};
pub fn reference(self: *const MethodHandle, constant_pool: []const ?CpInfo) ResolveError!CpInfo {
const reference_index = self.reference_index - 1;
if (reference_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[reference_index];
return switch (self.reference_kind) {
.get_field, .get_static, .put_field, .put_static => blk: {
if (cp_info == null or @as(Tag, cp_info.?) != .field_ref) break :blk ResolveError.InvalidConstantType;
break :blk cp_info.?;
},
.invoke_virtual, .invoke_static, .invoke_special, .new_invoke_special => blk: {
if (cp_info == null or @as(Tag, cp_info.?) != .method_ref) break :blk ResolveError.InvalidConstantType;
break :blk cp_info.?;
},
.invoke_interface => blk: {
if (cp_info == null or @as(Tag, cp_info.?) != .interface_method_ref) break :blk ResolveError.InvalidConstantType;
break :blk cp_info.?;
},
};
}
};
pub const MethodType = struct {
descriptor_index: u16,
pub fn descriptor(self: *const MethodType, constant_pool: []const ?CpInfo) ResolveError!CpInfo {
const descriptor_index = self.descriptor_index - 1;
if (descriptor_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[descriptor_index];
if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType;
return cp_info.?;
}
};
pub const InvokeDynamic = struct {
bootstrap_method_attr_index: u16,
name_and_type_index: u16,
pub fn nameAndType(self: *const InvokeDynamic, constant_pool: []const ?CpInfo) ResolveError!CpInfo {
const name_and_type_index = self.name_and_type_index - 1;
if (name_and_type_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[name_and_type_index];
if (cp_info == null or @as(Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType;
return cp_info.?;
}
};
pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!CpInfo {
const tag_byte = try input.takeByte();
const tag = std.enums.fromInt(Tag, tag_byte) orelse return ParseError.InvalidTag;
return switch (tag) {
.class => .{
.class = .{
.name_index = try input.takeInt(u16, .big),
},
},
.field_ref => .{
.field_ref = .{
.class_index = try input.takeInt(u16, .big),
.name_and_type_index = try input.takeInt(u16, .big),
},
},
.method_ref => .{
.method_ref = .{
.class_index = try input.takeInt(u16, .big),
.name_and_type_index = try input.takeInt(u16, .big),
},
},
.interface_method_ref => .{
.interface_method_ref = .{
.class_index = try input.takeInt(u16, .big),
.name_and_type_index = try input.takeInt(u16, .big),
},
},
.string => .{
.string = .{
.string_index = try input.takeInt(u16, .big),
},
},
.integer => .{
.integer = .{
.bytes = try input.takeInt(u32, .big),
},
},
.float => .{
.integer = .{
.bytes = try input.takeInt(u32, .big),
},
},
.long => .{
.long = .{
.high_bytes = try input.takeInt(u32, .big),
.low_bytes = try input.takeInt(u32, .big),
},
},
.double => .{
.double = .{
.high_bytes = try input.takeInt(u32, .big),
.low_bytes = try input.takeInt(u32, .big),
},
},
.name_and_type => .{
.name_and_type = .{
.name_index = try input.takeInt(u16, .big),
.descriptor_index = try input.takeInt(u16, .big),
},
},
.utf8 => blk: {
const length = try input.takeInt(u16, .big);
const bytes = try input.readAlloc(allocator, length);
break :blk .{
.utf8 = .{
.bytes = bytes,
},
};
},
.method_handle => blk: {
const kind_byte = try input.takeByte();
const kind = std.enums.fromInt(MethodHandle.Kind, kind_byte) orelse return ParseError.InvalidTag;
break :blk .{
.method_handle = .{
.reference_kind = kind,
.reference_index = try input.takeInt(u16, .big),
},
};
},
.method_type => .{
.method_type = .{
.descriptor_index = try input.takeInt(u16, .big),
},
},
.invoke_dynamic => .{
.invoke_dynamic = .{
.bootstrap_method_attr_index = try input.takeInt(u16, .big),
.name_and_type_index = try input.takeInt(u16, .big),
},
},
};
}
pub fn deinit(self: *CpInfo, allocator: std.mem.Allocator) void {
switch (self.*) {
.utf8 => |info| allocator.free(info.bytes),
else => {},
}
}
pub fn format(self: *const CpInfo, w: *std.Io.Writer, depth: usize, indent: u8, constant_pool: []const ?CpInfo) std.Io.Writer.Error!void {
try w.splatByteAll(' ', depth * indent);
try w.print("constant_value_type: {s}\n", .{ @tagName(self.*) });
switch (self.*) {
.class => |class| {
try w.splatByteAll(' ', depth * indent);
_ = try w.write("name:\n");
const class_name = class.name(constant_pool) catch return error.WriteFailed;
try class_name.format(w, depth, indent + 1, constant_pool);
},
.field_ref => |field_ref| {
try w.splatByteAll(' ', depth * indent);
_ = try w.write("class:\n");
const class = field_ref.class(constant_pool) catch return error.WriteFailed;
try class.format(w, depth, indent + 1, constant_pool);
try w.splatByteAll(' ', depth * indent);
_ = try w.write("name_and_type:\n");
const name_and_type = field_ref.nameAndType(constant_pool) catch return error.WriteFailed;
try name_and_type.format(w, depth, indent + 1, constant_pool);
},
.method_ref => |method_ref| {
try w.splatByteAll(' ', depth * indent);
_ = try w.write("class:\n");
const class = method_ref.class(constant_pool) catch return error.WriteFailed;
try class.format(w, depth, indent + 1, constant_pool);
try w.splatByteAll(' ', depth * indent);
_ = try w.write("name_and_type:\n");
const name_and_type = method_ref.nameAndType(constant_pool) catch return error.WriteFailed;
try name_and_type.format(w, depth, indent + 1, constant_pool);
},
.interface_method_ref => |interface_method_ref| {
try w.splatByteAll(' ', depth * indent);
_ = try w.write("class:\n");
const class = interface_method_ref.class(constant_pool) catch return error.WriteFailed;
try class.format(w, depth, indent + 1, constant_pool);
try w.splatByteAll(' ', depth * indent);
_ = try w.write("name_and_type:\n");
const name_and_type = interface_method_ref.nameAndType(constant_pool) catch return error.WriteFailed;
try name_and_type.format(w, depth, indent + 1, constant_pool);
},
.string => |string| {
const s = string.string(constant_pool) catch return error.WriteFailed;
try s.format(w, depth, indent + 1, constant_pool);
},
.integer => |integer| {
try w.splatByteAll(' ', depth * indent);
try w.print("integer: {d}\n", .{ integer.bytes });
},
.float => |float| {
const f: f32 = @bitCast(float.bytes);
try w.splatByteAll(' ', depth * indent);
try w.print("float: {d}\n", .{ f });
},
.long => |long| {
const l: u64 = (@as(u64, long.high_bytes) << 32) | long.low_bytes;
try w.splatByteAll(' ', depth * indent);
try w.print("long: {d}\n", .{ l });
},
.double => |double| {
const l: u64 = (@as(u64, double.high_bytes) << 32) | double.low_bytes;
const d: f64 = @bitCast(l);
try w.splatByteAll(' ', depth * indent);
try w.print("double: {d}\n", .{ d });
},
.name_and_type => |name_and_type| {
try w.splatByteAll(' ', depth * indent);
_ = try w.write("name:\n");
const name = name_and_type.name(constant_pool) catch return error.WriteFailed;
try name.format(w, depth, indent + 1, constant_pool);
try w.splatByteAll(' ', depth * indent);
_ = try w.write("descriptor:\n");
const descriptor = name_and_type.descriptor(constant_pool) catch return error.WriteFailed;
try descriptor.format(w, depth, indent + 1, constant_pool);
},
.utf8 => |utf8| {
try w.splatByteAll(' ', depth * indent);
try w.print("bytes: \"{s}\"\n", .{ utf8.bytes });
},
.method_handle => |method_handle| {
try w.splatByteAll(' ', depth * indent);
try w.print("kind: {t}\n", .{ method_handle.reference_kind });
try w.splatByteAll(' ', depth * indent);
_ = try w.write("reference:\n");
const reference = method_handle.reference(constant_pool) catch return error.WriteFailed;
try reference.format(w, depth, indent + 1, constant_pool);
},
.method_type => |method_type| {
try w.splatByteAll(' ', depth * indent);
_ = try w.write("descriptor:\n");
const descriptor = method_type.descriptor(constant_pool) catch return error.WriteFailed;
try descriptor.format(w, depth, indent + 1, constant_pool);
},
.invoke_dynamic => |invoke_dynamic| {
try w.splatByteAll(' ', depth * indent);
_ = try w.write("name_and_type:\n");
const name_and_type = invoke_dynamic.nameAndType(constant_pool) catch return error.WriteFailed;
try name_and_type.format(w, depth, indent + 1, constant_pool);
},
}
}
};
pub const ClassFlags = enum(u16) {
public = 0x0001,
final = 0x0010,
super = 0x0020,
interface = 0x0200,
abstract = 0x0400,
synthetic = 0x1000,
annotation = 0x2000,
@"enum" = 0x4000,
};
pub const ClassAccessFlags = EnumFlags(ClassFlags);
pub const FieldInfo = struct {
access_flags: FieldAccessFlags,
name_index: u16,
descriptor_index: u16,
attributes: []AttributeInfo,
pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) !FieldInfo {
const access_flags: FieldAccessFlags = .{ .mask = try input.takeInt(u16, .big) };
const name_index = try input.takeInt(u16, .big);
const descriptor_index = try input.takeInt(u16, .big);
const attributes = try parseAttributes(input, allocator);
return .{
.access_flags = access_flags,
.name_index = name_index,
.descriptor_index = descriptor_index,
.attributes = attributes,
};
}
pub fn deinit(self: *FieldInfo, allocator: std.mem.Allocator) void {
for (self.attributes) |*attr_info| {
attr_info.deinit(allocator);
}
allocator.free(self.attributes);
}
pub fn format(self: *const FieldInfo, w: *std.Io.Writer, depth: usize, indent: u8, allocator: std.mem.Allocator, constant_pool: []const ?CpInfo) std.Io.Writer.Error!void {
try w.splatByteAll(' ', depth * indent);
try w.print("flags: {f}\n", .{ self.access_flags });
try w.splatByteAll(' ', depth * indent);
_ = try w.write("name:\n");
const field_name = self.name(constant_pool) catch return error.WriteFailed;
try field_name.format(w, depth, indent + 1, constant_pool);
try w.splatByteAll(' ', depth * indent);
_ = try w.write("descriptor:\n");
const field_descriptor = self.descriptor(constant_pool) catch return error.WriteFailed;
try field_descriptor.format(w, depth, indent + 1, constant_pool);
try w.splatByteAll(' ', depth * indent);
_ = try w.write("attributes:\n");
var attr_indent = indent;
for (self.attributes, 0..) |*attribute, j| {
attr_indent += 1;
defer attr_indent -= 1;
try w.splatByteAll(' ', depth * attr_indent);
try w.print("{d}:\n", .{ j });
try attribute.format(w, depth, attr_indent + 1, allocator, constant_pool);
}
}
pub fn name(self: *const FieldInfo, constant_pool: []const ?CpInfo) ResolveError!CpInfo {
const name_index = self.name_index - 1;
if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[name_index];
if (cp_info == null or @as(CpInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType;
return cp_info.?;
}
pub fn descriptor(self: *const FieldInfo, constant_pool: []const ?CpInfo) ResolveError!CpInfo {
const descriptor_index = self.descriptor_index - 1;
if (descriptor_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[descriptor_index];
if (cp_info == null or @as(CpInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType;
return cp_info.?;
}
};
pub const FieldFlags = enum(u16) {
public = 0x0001,
private = 0x0002,
protected = 0x0004,
static = 0x0008,
final = 0x0010,
@"volatile" = 0x0040,
transient = 0x0080,
synthetic = 0x1000,
@"enum" = 0x4000,
};
pub const FieldAccessFlags = EnumFlags(FieldFlags);
pub const MethodInfo = struct {
access_flags: MethodAccessFlags,
name_index: u16,
descriptor_index: u16,
attributes: []AttributeInfo,
pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) !MethodInfo {
const access_flags: MethodAccessFlags = .{ .mask = try input.takeInt(u16, .big) };
const name_index = try input.takeInt(u16, .big);
const descriptor_index = try input.takeInt(u16, .big);
const attributes = try parseAttributes(input, allocator);
return .{
.access_flags = access_flags,
.name_index = name_index,
.descriptor_index = descriptor_index,
.attributes = attributes,
};
}
pub fn deinit(self: *MethodInfo, allocator: std.mem.Allocator) void {
for (self.attributes) |*attr_info| {
attr_info.deinit(allocator);
}
allocator.free(self.attributes);
}
pub fn format(self: *const MethodInfo, w: *std.Io.Writer, depth: usize, indent: u8, allocator: std.mem.Allocator, constant_pool: []const ?CpInfo) std.Io.Writer.Error!void {
try w.splatByteAll(' ', depth * indent);
try w.print("flags: {f}\n", .{ self.access_flags });
try w.splatByteAll(' ', depth * indent);
_ = try w.write("name:\n");
const method_name = self.name(constant_pool) catch return error.WriteFailed;
try method_name.format(w, depth, indent + 1, constant_pool);
try w.splatByteAll(' ', depth * indent);
_ = try w.write("descriptor:\n");
const method_descriptor = self.descriptor(constant_pool) catch return error.WriteFailed;
try method_descriptor.format(w, depth, indent + 1, constant_pool);
try w.splatByteAll(' ', depth * indent);
_ = try w.write("attributes:\n");
var attr_indent = indent;
for (self.attributes, 0..) |*attribute, j| {
attr_indent += 1;
defer attr_indent -= 1;
try w.splatByteAll(' ', depth * attr_indent);
try w.print("{d}:\n", .{ j });
try attribute.format(w, depth, attr_indent + 1, allocator, constant_pool);
}
}
pub fn name(self: *const MethodInfo, constant_pool: []const ?CpInfo) ResolveError!CpInfo {
const name_index = self.name_index - 1;
if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[name_index];
if (cp_info == null or @as(CpInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType;
return cp_info.?;
}
pub fn descriptor(self: *const MethodInfo, constant_pool: []const ?CpInfo) ResolveError!CpInfo {
const descriptor_index = self.descriptor_index - 1;
if (descriptor_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[descriptor_index];
if (cp_info == null or @as(CpInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType;
return cp_info.?;
}
};
pub const MethodFlags = enum(u16) {
public = 0x0001,
private = 0x0002,
protected = 0x0004,
static = 0x0008,
final = 0x0010,
synchronized = 0x0020,
bridge = 0x0040,
varargs = 0x0080,
native = 0x0100,
abstract = 0x0400,
strict = 0x0800,
synthetic = 0x1000,
};
pub const MethodAccessFlags = EnumFlags(MethodFlags);
pub const AttributeInfo = struct {
attribute_name_index: u16,
info: []u8,
pub const Predefined = union(enum) {
constant_value: ConstantValue,
code: Code,
stack_map_table: StackMapTable,
exceptions: Exceptions,
inner_classes: InnerClasses,
enclosing_method: EnclosingMethod,
synthetic: Synthetic,
signature: Signature,
source_file: SourceFile,
source_debug_extension: SourceDebugExtension,
line_number_table: LineNumberTable,
local_variable_table: LocalVariableTable,
local_variable_type_table: LocalVariableTypeTable,
deprecated: Deprecated,
runtime_visible_annotations: RuntimeVisibleAnnotations,
runtime_invisible_annotations: RuntimeInvisibleAnnotations,
runtime_visible_parameter_annotations: RuntimeVisibleParameterAnnotations,
runtime_invisible_parameter_annotations: RuntimeInvisibleParameterAnnotations,
annotation_default: AnnotationDefault,
bootstrap_methods: BootstrapMethods,
pub const ConstantValue = struct {
constant_value_index: u16,
pub fn constantValue(self: *const ConstantValue, constant_pool: []const ?CpInfo) ResolveError!CpInfo {
const constant_value_index = self.constant_value_index - 1;
if (constant_value_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[constant_value_index];
if (cp_info == null) return ResolveError.InvalidConstantType;
return cp_info.?;
}
};
pub const Code = struct {
max_stack: u16,
max_locals: u16,
code: []u8,
exception_table: []ExceptionHandler,
attributes: []AttributeInfo,
pub const ExceptionHandler = struct {
start_pc: u16,
end_pc: u16,
handler_pc: u16,
catch_type: u16,
};
};
pub const StackMapTable = struct {
entries: []StackMapFrame,
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),
},
},
252...254 => blk: {
const offset_delta = try input.takeInt(u16, .big);
const length = frame_type - 251;
const locals = try allocator.alloc(Predefined.StackMapTable.VerificationTypeInfo, length);
errdefer allocator.free(locals);
for (locals) |*local| {
local.* = try .parse(input);
}
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(Predefined.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(Predefined.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 const VerificationTypeInfo = union(VerificationTypeInfo.Tag) {
top,
integer,
float,
double,
long,
null,
uninitialized_this,
object: struct {
cpool_index: u16,
},
uninitialized: struct {
cpool_index: u16,
},
pub const Tag = enum(u8) {
top = 0,
integer = 1,
float = 2,
double = 3,
long = 4,
null = 5,
uninitialized_this = 6,
object = 7,
uninitialized = 8,
};
pub fn parse(input: *std.Io.Reader) ParseError!VerificationTypeInfo {
const tag_byte = try input.takeByte();
const tag = std.enums.fromInt(Predefined.StackMapTable.VerificationTypeInfo.Tag, tag_byte) orelse return ParseError.InvalidTag;
return switch (tag) {
.top => .top,
.integer => .integer,
.float => .float,
.double => .double,
.long => .long,
.null => .null,
.uninitialized_this => .uninitialized_this,
.object => .{
.object = .{
.cpool_index = try input.takeInt(u16, .big),
},
},
.uninitialized => .{
.uninitialized = .{
.cpool_index = try input.takeInt(u16, .big),
},
},
};
}
};
};
pub const Exceptions = struct {
exception_index_table: []u16,
};
pub const InnerClasses = struct {
classes: []InnerClass,
pub const InnerClass = struct {
inner_class_info_index: u16,
outer_class_info_index: u16,
inner_name_index: u16,
inner_class_access_flags: InnerClassAccessFlags,
pub fn format(self: *const InnerClass, w: *std.Io.Writer, depth: usize, indent: u8, constant_pool: []const ?CpInfo) std.Io.Writer.Error!void {
try w.splatByteAll(' ', depth * indent);
try w.print("flags: {f}\n", .{ self.inner_class_access_flags });
const inner_class = self.innerClass(constant_pool) catch return error.WriteFailed;
try w.splatByteAll(' ', depth * indent);
_ = try w.write("inner_class:\n");
try inner_class.format(w, depth, indent + 1, constant_pool);
const outer_class = self.outerClass(constant_pool) catch return error.WriteFailed;
if (outer_class) |oc| {
try w.splatByteAll(' ', depth * indent);
_ = try w.write("outer_class:\n");
try oc.format(w, depth, indent + 1, constant_pool);
}
const inner_name = self.innerName(constant_pool) catch return error.WriteFailed;
if (inner_name) |in| {
try w.splatByteAll(' ', depth * indent);
_ = try w.write("inner_name:\n");
try in.format(w, depth, indent + 1, constant_pool);
}
}
pub fn innerClass(self: *const InnerClass, constant_pool: []const ?CpInfo) ResolveError!CpInfo {
const inner_class_info_index = self.inner_class_info_index - 1;
if (inner_class_info_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[inner_class_info_index];
if (cp_info == null or @as(CpInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType;
return cp_info.?;
}
pub fn outerClass(self: *const InnerClass, constant_pool: []const ?CpInfo) ResolveError!?CpInfo {
if (self.outer_class_info_index == 0) return null;
const outer_class_info_index = self.outer_class_info_index - 1;
if (outer_class_info_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[outer_class_info_index];
if (cp_info == null or @as(CpInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType;
return cp_info.?;
}
pub fn innerName(self: *const InnerClass, constant_pool: []const ?CpInfo) ResolveError!?CpInfo {
if (self.inner_name_index == 0) return null;
const inner_name_index = self.inner_name_index - 1;
if (inner_name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[inner_name_index];
if (cp_info == null or @as(CpInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType;
return cp_info.?;
}
};
pub const InnerClassFlags = enum(u16) {
public = 0x0001,
private = 0x0002,
protected = 0x0004,
static = 0x0008,
final = 0x0010,
interface = 0x0200,
abstract = 0x0400,
synthetic = 0x1000,
annotation = 0x2000,
@"enum" = 0x4000,
};
pub const InnerClassAccessFlags = EnumFlags(InnerClassFlags);
};
pub const EnclosingMethod = struct {
class_index: u16,
method_index: u16,
pub fn class(self: *const EnclosingMethod, constant_pool: []const ?CpInfo) ResolveError!CpInfo {
const class_index = self.class_index - 1;
if (class_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[class_index];
if (cp_info == null or @as(CpInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType;
return cp_info.?;
}
pub fn method(self: *const EnclosingMethod, constant_pool: []const ?CpInfo) ResolveError!?CpInfo {
if (self.method_index == 0) return null;
const method_index = self.method_index - 1;
if (method_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[method_index];
if (cp_info == null or @as(CpInfo.Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType;
return cp_info.?;
}
};
pub const Synthetic = struct { };
pub const Signature = struct {
signature_index: u16,
pub fn signature(self: *const Signature, constant_pool: []const ?CpInfo) ResolveError!CpInfo {
const signature_index = self.signature_index - 1;
if (signature_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[signature_index];
if (cp_info == null or @as(CpInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType;
return cp_info.?;
}
};
pub const SourceFile = struct {
source_file_index: u16,
pub fn sourceFile(self: *const SourceFile, constant_pool: []const ?CpInfo) ResolveError!CpInfo {
const source_file_index = self.source_file_index - 1;
if (source_file_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[source_file_index];
if (cp_info == null or @as(CpInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType;
return cp_info.?;
}
};
pub const SourceDebugExtension = struct {
debug_extension: []u8,
};
pub const LineNumberTable = struct {
line_number_table: []const LineNumber,
pub const LineNumber = struct {
start_pc: u16,
line_number: u16,
};
};
pub const LocalVariableTable = struct {
local_variable_table: []LocalVariable,
pub const LocalVariable = struct {
start_pc: u16,
length: u16,
name_index: u16,
descriptor_index: u16,
index: u16,
};
};
pub const LocalVariableTypeTable = struct {
local_variable_type_table: []LocalVariableType,
pub const LocalVariableType = struct {
start_pc: u16,
length: u16,
name_index: u16,
signature_index: u16,
index: u16,
};
};
pub const Deprecated = struct { };
pub const ElementValue = union(enum) {
// B, C, D, F, I, J, S, Z, s
const_value_index: u16,
// e
enum_const_value: struct {
type_name_index: u16,
const_name_index: u16,
},
// c
class_info_index: u16,
// @
annotation_value: Annotation,
// [
array_value: struct {
values: []ElementValue,
},
pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!ElementValue {
const tag = try input.takeByte();
return switch (tag) {
'B', 'C', 'D', 'F', 'I', 'J', 'S', 'Z', 's' => .{
.const_value_index = try input.takeInt(u16, .big),
},
'e' => .{
.enum_const_value = .{
.type_name_index = try input.takeInt(u16, .big),
.const_name_index = try input.takeInt(u16, .big),
},
},
'c' => .{
.class_info_index = try input.takeInt(u16, .big),
},
'@' => .{
.annotation_value = try .parse(input, allocator),
},
'[' => .{
.array_value = blk: {
const num_values = try input.takeInt(u16, .big);
const values = try allocator.alloc(ElementValue, num_values);
errdefer allocator.free(values);
for (values) |*value| {
value.* = try .parse(input, allocator);
}
break :blk .{
.values = values,
};
},
},
else => unreachable,
};
}
pub fn deinit(self: *ElementValue, allocator: std.mem.Allocator) void {
switch (self.*) {
.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 const Annotation = struct {
type_index: u16,
element_value_pairs: []ElementValuePair,
pub const ElementValuePair = struct {
element_name_index: u16,
value: ElementValue,
};
pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Annotation {
const type_index = try input.takeInt(u16, .big);
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 const RuntimeVisibleAnnotations = struct {
annotations: []Annotation,
};
pub const RuntimeInvisibleAnnotations = struct {
annotations: []Annotation,
};
pub const ParameterAnnotation = struct {
annotations: []Annotation,
};
pub const RuntimeVisibleParameterAnnotations = struct {
parameter_annotations: []ParameterAnnotation,
};
pub const RuntimeInvisibleParameterAnnotations = struct {
parameter_annotations: []ParameterAnnotation,
};
pub const AnnotationDefault = struct {
default_value: ElementValue,
};
pub const BootstrapMethods = struct {
bootstrap_methods: []BootstrapMethod,
pub const BootstrapMethod = struct {
bootstrap_method_ref: u16,
bootstrap_arguments: []u16,
};
};
pub fn deinit(self: *Predefined, allocator: std.mem.Allocator) void {
switch (self.*) {
.code => |attribute| {
allocator.free(attribute.code);
allocator.free(attribute.exception_table);
for (attribute.attributes) |*attr| {
attr.deinit(allocator);
}
allocator.free(attribute.attributes);
},
.stack_map_table => |attribute| {
for (attribute.entries) |*entry| {
entry.deinit(allocator);
}
allocator.free(attribute.entries);
},
.exceptions => |attribute| allocator.free(attribute.exception_index_table),
.inner_classes => |attribute| allocator.free(attribute.classes),
.source_debug_extension => |attribute| allocator.free(attribute.debug_extension),
.line_number_table => |attribute| allocator.free(attribute.line_number_table),
.local_variable_table => |attribute| allocator.free(attribute.local_variable_table),
.local_variable_type_table => |attribute| allocator.free(attribute.local_variable_type_table),
.runtime_visible_annotations => |attribute| {
for (attribute.annotations) |*annotation| {
annotation.deinit(allocator);
}
allocator.free(attribute.annotations);
},
.runtime_invisible_annotations => |attribute| {
for (attribute.annotations) |*annotation| {
annotation.deinit(allocator);
}
allocator.free(attribute.annotations);
},
.runtime_visible_parameter_annotations => |attribute| {
for (attribute.parameter_annotations) |parameter_annotation| {
for (parameter_annotation.annotations) |*annotation| {
annotation.deinit(allocator);
}
allocator.free(parameter_annotation.annotations);
}
allocator.free(attribute.parameter_annotations);
},
.runtime_invisible_parameter_annotations => |attribute| {
for (attribute.parameter_annotations) |parameter_annotation| {
for (parameter_annotation.annotations) |*annotation| {
annotation.deinit(allocator);
}
allocator.free(parameter_annotation.annotations);
}
allocator.free(attribute.parameter_annotations);
},
.bootstrap_methods => |attribute| {
for (attribute.bootstrap_methods) |bootstrap_method| {
allocator.free(bootstrap_method.bootstrap_arguments);
}
allocator.free(attribute.bootstrap_methods);
},
else => {},
}
}
};
pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!AttributeInfo {
const attribute_name_index = try input.takeInt(u16, .big);
const attribute_length = try input.takeInt(u32, .big);
const info = try input.readAlloc(allocator, attribute_length);
return .{
.attribute_name_index = attribute_name_index,
.info = info,
};
}
pub fn deinit(self: *AttributeInfo, allocator: std.mem.Allocator) void {
allocator.free(self.info);
}
pub fn format(self: *const AttributeInfo, w: *std.Io.Writer, depth: usize, indent: u8, allocator: std.mem.Allocator, constant_pool: []const ?CpInfo) std.Io.Writer.Error!void {
try w.splatByteAll(' ', depth * indent);
_ = try w.write("name:\n");
const attribute_name = self.attributeName(constant_pool) catch return error.WriteFailed;
try attribute_name.format(w, depth, indent + 1, constant_pool);
var predefined = self.resolve(allocator, constant_pool) catch return error.WriteFailed;
if (predefined) |*p| {
defer p.deinit(allocator);
try w.splatByteAll(' ', depth * indent);
_ = try w.write("predefined: yes\n");
try w.splatByteAll(' ', depth * indent);
try w.print("type: {s}\n", .{ @tagName(p.*) });
switch (p.*) {
.constant_value => |attr| {
const constant_value = attr.constantValue(constant_pool) catch return error.WriteFailed;
try constant_value.format(w, depth, indent + 1, constant_pool);
},
.code => |attr| {
try w.splatByteAll(' ', depth * indent);
try w.print("max_stack: {d}\n", .{ attr.max_stack });
try w.splatByteAll(' ', depth * indent);
try w.print("max_locals: {d}\n", .{ attr.max_locals });
try w.splatByteAll(' ', depth * indent);
_ = try w.write("code:\n");
var instr_indent = indent;
for (attr.code, 0..) |instr, i| {
instr_indent += 1;
defer instr_indent -= 1;
try w.splatByteAll(' ', depth * instr_indent);
try w.print("{d}: 0x{x}\n", .{ i, instr });
}
//try w.splatByteAll(' ', depth * indent);
//_ = try w.write("exception_table:\n");
//var exception_indent = indent;
//for (attr.exception_table, 0..) |*exception, i| {
// exception_indent += 1;
// defer exception_indent -= 1;
// try w.splatByteAll(' ', depth * exception_indent);
// try w.print("{d}:\n", .{ i });
// try self.formatExceptionHandler(w, depth, exception_indent + 1, exception);
//}
try w.splatByteAll(' ', depth * indent);
_ = try w.write("attributes:\n");
var attr_indent = indent;
for (attr.attributes, 0..) |*a, i| {
attr_indent += 1;
defer attr_indent -= 1;
try w.splatByteAll(' ', depth * attr_indent);
try w.print("{d}:\n", .{ i });
try a.format(w, depth, attr_indent + 1, allocator, constant_pool);
}
},
//.stack_map_table,
.exceptions => |attr| {
try w.splatByteAll(' ', depth * indent);
_ = try w.write("exceptions:\n");
var exception_indent = indent;
for (attr.exception_index_table, 0..) |exception, i| {
exception_indent += 1;
defer exception_indent -= 1;
if (exception >= constant_pool.len) return error.WriteFailed;
const cp_info = constant_pool[exception - 1];
if (cp_info == null or @as(CpInfo.Tag, cp_info.?) != .class) return error.WriteFailed;
try w.splatByteAll(' ', depth * exception_indent);
try w.print("{d}:\n", .{ i });
try cp_info.?.format(w, depth, exception_indent + 1, constant_pool);
}
},
.inner_classes => |attr| {
try w.splatByteAll(' ', depth * indent);
_ = try w.write("classes:\n");
var class_indent = indent;
for (attr.classes, 0..) |*class, i| {
class_indent += 1;
defer class_indent -= 1;
try w.splatByteAll(' ', depth * class_indent);
try w.print("{d}:\n", .{ i });
try class.format(w, depth, class_indent + 1, constant_pool);
}
},
.enclosing_method => |attr| {
const class = attr.class(constant_pool) catch return error.WriteFailed;
try w.splatByteAll(' ', depth * indent);
_ = try w.write("class:\n");
try class.format(w, depth, indent + 1, constant_pool);
const method = attr.method(constant_pool) catch return error.WriteFailed;
if (method) |m| {
try w.splatByteAll(' ', depth * indent);
_ = try w.write("method:\n");
try m.format(w, depth, indent + 1, constant_pool);
}
},
.synthetic => {},
.signature => |attr| {
const signature = attr.signature(constant_pool) catch return error.WriteFailed;
try signature.format(w, depth, indent + 1, constant_pool);
},
.source_file => |attr| {
const source_file = attr.sourceFile(constant_pool) catch return error.WriteFailed;
try source_file.format(w, depth, indent + 1, constant_pool);
},
//.source_debug_extension,
//.line_number_table,
//.local_variable_table,
//.local_variable_type_table,
.deprecated => {},
//.runtime_visible_annotations,
//.runtime_invisible_annotations,
//.runtime_visible_parameter_annotations,
//.runtime_invisible_parameter_annotations,
//.annotation_default,
//.bootstrap_methods,
else => {},
}
} else {
try w.splatByteAll(' ', depth * indent);
_ = try w.write("predefined: no\n");
}
}
pub fn attributeName(self: *const AttributeInfo, constant_pool: []const ?CpInfo) ResolveError!CpInfo {
const attribute_name_index = self.attribute_name_index - 1;
if (attribute_name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[attribute_name_index];
if (cp_info == null or @as(CpInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType;
return cp_info.?;
}
pub fn resolve(self: *const AttributeInfo, allocator: std.mem.Allocator, constant_pool: []const ?CpInfo) (ResolveError || ParseError)!?Predefined {
const attribute_name = (try self.attributeName(constant_pool)).utf8;
var r: std.Io.Reader = .fixed(self.info);
const eql = std.mem.eql;
if (eql(u8, attribute_name.bytes, "ConstantValue")) {
return .{
.constant_value = .{
.constant_value_index = try r.takeInt(u16, .big),
},
};
} else if (eql(u8, attribute_name.bytes, "Code")) {
const max_stack = try r.takeInt(u16, .big);
const max_locals = try r.takeInt(u16, .big);
const code_length = try r.takeInt(u32, .big);
const code = try r.readAlloc(allocator, code_length);
errdefer allocator.free(code);
const exception_table_length = try r.takeInt(u16, .big);
const exception_table = try allocator.alloc(Predefined.Code.ExceptionHandler, exception_table_length);
errdefer allocator.free(exception_table);
for (exception_table) |*exception_handler| {
exception_handler.* = .{
.start_pc = try r.takeInt(u16, .big),
.end_pc = try r.takeInt(u16, .big),
.handler_pc = try r.takeInt(u16, .big),
.catch_type = try r.takeInt(u16, .big),
};
}
const attribute_count = try r.takeInt(u16, .big);
const attributes = try allocator.alloc(AttributeInfo, attribute_count);
errdefer allocator.free(attributes);
for (attributes) |*attribute| {
attribute.* = try .parse(&r, allocator);
}
return .{
.code = .{
.max_stack = max_stack,
.max_locals = max_locals,
.code = code,
.exception_table = exception_table,
.attributes = attributes,
},
};
} else if (eql(u8, attribute_name.bytes, "StackMapTable")) {
const number_of_entries = try r.takeInt(u16, .big);
const entries = try allocator.alloc(Predefined.StackMapTable.StackMapFrame, number_of_entries);
errdefer allocator.free(entries);
for (entries) |*entry| {
entry.* = try .parse(&r, allocator);
}
return .{
.stack_map_table = .{
.entries = entries,
},
};
} else if (eql(u8, attribute_name.bytes, "Exceptions")) {
const number_of_exceptions = try r.takeInt(u16, .big);
const exception_index_table = try allocator.alloc(u16, number_of_exceptions);
errdefer allocator.free(exception_index_table);
for (exception_index_table) |*exception_index| {
exception_index.* = try r.takeInt(u16, .big);
}
return .{
.exceptions = .{
.exception_index_table = exception_index_table,
},
};
} else if (eql(u8, attribute_name.bytes, "InnerClasses")) {
const number_of_classes = try r.takeInt(u16, .big);
const classes = try allocator.alloc(Predefined.InnerClasses.InnerClass, number_of_classes);
errdefer allocator.free(classes);
for (classes) |*class| {
class.* = .{
.inner_class_info_index = try r.takeInt(u16, .big),
.outer_class_info_index = try r.takeInt(u16, .big),
.inner_name_index = try r.takeInt(u16, .big),
.inner_class_access_flags = .{ .mask = try r.takeInt(u16, .big) },
};
}
return .{
.inner_classes = .{
.classes = classes,
},
};
} else if (eql(u8, attribute_name.bytes, "EnclosingMethod")) {
return .{
.enclosing_method = .{
.class_index = try r.takeInt(u16, .big),
.method_index = try r.takeInt(u16, .big),
},
};
} else if (eql(u8, attribute_name.bytes, "Synthetic")) {
return .{
.synthetic = .{},
};
} else if (eql(u8, attribute_name.bytes, "Signature")) {
return .{
.signature = .{
.signature_index = try r.takeInt(u16, .big),
},
};
} else if (eql(u8, attribute_name.bytes, "SourceFile")) {
return .{
.source_file = .{
.source_file_index = try r.takeInt(u16, .big),
},
};
} else if (eql(u8, attribute_name.bytes, "SourceDebugExtension")) {
return .{
.source_debug_extension = .{
.debug_extension = try allocator.dupe(u8, self.info),
},
};
} else if (eql(u8, attribute_name.bytes, "LineNumberTable")) {
const line_number_table_length = try r.takeInt(u16, .big);
const line_number_table = try allocator.alloc(Predefined.LineNumberTable.LineNumber, line_number_table_length);
errdefer allocator.free(line_number_table);
for (line_number_table) |*line_number| {
line_number.* = .{
.start_pc = try r.takeInt(u16, .big),
.line_number = try r.takeInt(u16, .big),
};
}
return .{
.line_number_table = .{
.line_number_table = line_number_table,
},
};
} else if (eql(u8, attribute_name.bytes, "LocalVariableTable")) {
const local_variable_table_length = try r.takeInt(u16, .big);
const local_variable_table = try allocator.alloc(Predefined.LocalVariableTable.LocalVariable, local_variable_table_length);
errdefer allocator.free(local_variable_table);
for (local_variable_table) |*local_variable| {
local_variable.* = .{
.start_pc = try r.takeInt(u16, .big),
.length = try r.takeInt(u16, .big),
.name_index = try r.takeInt(u16, .big),
.descriptor_index = try r.takeInt(u16, .big),
.index = try r.takeInt(u16, .big),
};
}
return .{
.local_variable_table = .{
.local_variable_table = local_variable_table,
},
};
} else if (eql(u8, attribute_name.bytes, "LocalVariableTypeTable")) {
const local_variable_type_table_length = try r.takeInt(u16, .big);
const local_variable_type_table = try allocator.alloc(Predefined.LocalVariableTypeTable.LocalVariableType, local_variable_type_table_length);
errdefer allocator.free(local_variable_type_table);
for (local_variable_type_table) |*local_variable_type| {
local_variable_type.* = .{
.start_pc = try r.takeInt(u16, .big),
.length = try r.takeInt(u16, .big),
.name_index = try r.takeInt(u16, .big),
.signature_index = try r.takeInt(u16, .big),
.index = try r.takeInt(u16, .big),
};
}
return .{
.local_variable_type_table = .{
.local_variable_type_table = local_variable_type_table,
},
};
} else if (eql(u8, attribute_name.bytes, "Deprecated")) {
return .{
.deprecated = .{},
};
} else if (eql(u8, attribute_name.bytes, "RuntimeVisibleAnnotations")) {
const num_annotations = try r.takeInt(u16, .big);
const annotations = try allocator.alloc(Predefined.Annotation, num_annotations);
errdefer allocator.free(annotations);
for (annotations) |*annotation| {
annotation.* = try .parse(&r, allocator);
}
return .{
.runtime_visible_annotations = .{
.annotations = annotations,
},
};
} else if (eql(u8, attribute_name.bytes, "RuntimeInvisibleAnnotations")) {
const num_annotations = try r.takeInt(u16, .big);
const annotations = try allocator.alloc(Predefined.Annotation, num_annotations);
errdefer allocator.free(annotations);
for (annotations) |*annotation| {
annotation.* = try .parse(&r, allocator);
}
return .{
.runtime_invisible_annotations = .{
.annotations = annotations,
},
};
} else if (eql(u8, attribute_name.bytes, "RuntimeVisibleParameterAnnotations")) {
const num_parameters = try r.takeByte();
const parameter_annotations = try allocator.alloc(Predefined.ParameterAnnotation, num_parameters);
errdefer allocator.free(parameter_annotations);
for (parameter_annotations) |*parameter_annotation| {
const num_annotations = try r.takeInt(u16, .big);
const annotations = try allocator.alloc(Predefined.Annotation, num_annotations);
errdefer allocator.free(annotations);
for (annotations) |*annotation| {
annotation.* = try .parse(&r, allocator);
}
parameter_annotation.* = .{
.annotations = annotations,
};
}
return .{
.runtime_visible_parameter_annotations = .{
.parameter_annotations = parameter_annotations,
},
};
} else if (eql(u8, attribute_name.bytes, "RuntimeInvisibleParameterAnnotations")) {
const num_parameters = try r.takeByte();
const parameter_annotations = try allocator.alloc(Predefined.ParameterAnnotation, num_parameters);
errdefer allocator.free(parameter_annotations);
for (parameter_annotations) |*parameter_annotation| {
const num_annotations = try r.takeInt(u16, .big);
const annotations = try allocator.alloc(Predefined.Annotation, num_annotations);
errdefer allocator.free(annotations);
for (annotations) |*annotation| {
annotation.* = try .parse(&r, allocator);
}
parameter_annotation.* = .{
.annotations = annotations,
};
}
return .{
.runtime_invisible_parameter_annotations = .{
.parameter_annotations = parameter_annotations,
},
};
} else if (eql(u8, attribute_name.bytes, "AnnotationDefault")) {
unreachable;
} else if (eql(u8, attribute_name.bytes, "BootstrapMethods")) {
const num_bootstrap_methods = try r.takeInt(u16, .big);
const bootstrap_methods = try allocator.alloc(Predefined.BootstrapMethods.BootstrapMethod, num_bootstrap_methods);
errdefer allocator.free(bootstrap_methods);
for (bootstrap_methods) |*bootstrap_method| {
const bootstrap_method_ref = try r.takeInt(u16, .big);
const num_bootstrap_arguments = try r.takeInt(u16, .big);
const bootstrap_arguments = try allocator.alloc(u16, num_bootstrap_arguments);
errdefer allocator.free(bootstrap_arguments);
for (bootstrap_arguments) |*bootstrap_argument| {
bootstrap_argument.* = try r.takeInt(u16, .big);
}
bootstrap_method.* = .{
.bootstrap_method_ref = bootstrap_method_ref,
.bootstrap_arguments = bootstrap_arguments,
};
}
return .{
.bootstrap_methods = .{
.bootstrap_methods = bootstrap_methods,
},
};
} else {
return null;
}
}
};