java-class-parser/src/Class.zig

874 lines
34 KiB
Zig

const std = @import("std");
const EnumFlags = @import("EnumFlags.zig").EnumFlags;
pub const FieldInfo = @import("Class/FieldInfo.zig");
pub const MethodInfo = @import("Class/MethodInfo.zig");
pub const AttributeInfo = @import("Class/AttributeInfo.zig").AttributeInfo;
allocator: std.mem.Allocator,
magic: u32,
minor_version: u16,
major_version: u16,
constant_pool: []?ConstantPoolInfo,
access_flags: ClassAccessFlags,
this_class: u16,
super_class: u16,
interfaces: []u16,
fields: []FieldInfo,
methods: []MethodInfo,
attributes: []AttributeInfo,
const Self = @This();
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 ParseError = std.Io.Reader.Error || std.mem.Allocator.Error || error {
InvalidMagicNumber,
InvalidTag,
};
pub const ResolveError = error {
InvalidConstantPoolIndex,
InvalidConstantType,
};
pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) (ParseError || ResolveError)!Self {
const magic = try input.takeInt(u32, .big);
if (magic != 0xCAFEBABE) {
return ParseError.InvalidMagicNumber;
}
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, constant_pool, allocator);
errdefer {
for (fields) |*field| {
field.deinit(allocator);
}
allocator.free(fields);
}
const methods = try parseMethods(input, constant_pool, allocator);
errdefer {
for (methods) |*method| {
method.deinit(allocator);
}
allocator.free(methods);
}
const attributes = try parseAttributes(input, constant_pool, 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![]?ConstantPoolInfo {
const constant_pool_size = try input.takeInt(u16, .big);
const constant_pool = try allocator.alloc(?ConstantPoolInfo, constant_pool_size - 1);
errdefer allocator.free(constant_pool);
var i: usize = 0;
while (i < constant_pool_size - 1) {
defer i += 1;
const cp_info = try ConstantPoolInfo.parse(input, allocator);
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, constant_pool: []const ?ConstantPoolInfo, allocator: std.mem.Allocator) (ParseError || ResolveError)![]FieldInfo {
const fields_count = try input.takeInt(u16, .big);
const fields = try allocator.alloc(FieldInfo, fields_count);
errdefer allocator.free(fields);
for (0..fields_count) |i| {
fields[i] = try FieldInfo.parse(input, constant_pool, allocator);
}
return fields;
}
fn parseMethods(input: *std.Io.Reader, constant_pool: []const ?ConstantPoolInfo, allocator: std.mem.Allocator) (ParseError || ResolveError)![]MethodInfo {
const methods_count = try input.takeInt(u16, .big);
const methods = try allocator.alloc(MethodInfo, methods_count);
errdefer allocator.free(methods);
for (0..methods_count) |i| {
methods[i] = try MethodInfo.parse(input, constant_pool, allocator);
}
return methods;
}
pub fn parseAttributes(input: *std.Io.Reader, constant_pool: []const ?ConstantPoolInfo, allocator: std.mem.Allocator) (ParseError || ResolveError)![]AttributeInfo {
const attributes_count = try input.takeInt(u16, .big);
const attributes = try allocator.alloc(AttributeInfo, attributes_count);
errdefer allocator.free(attributes);
for (0..attributes_count) |i| {
attributes[i] = try AttributeInfo.parse(input, constant_pool, 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.indentedFormat(w, depth, indent + 1, self.constant_pool);
_ = try w.write("super class:\n");
if (self.superClass() catch return error.WriteFailed) |super_class| {
try super_class.indentedFormat(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(ConstantPoolInfo.Tag, cp_info.?) != .class) return error.WriteFailed;
try w.splatByteAll(' ', depth * indent);
try w.print("{d}:\n", .{ i });
try cp_info.?.indentedFormat(w, depth, indent + 1, self.constant_pool);
}
_ = try 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.indentedFormat(w, depth, indent + 1, 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.indentedFormat(w, depth, indent + 1, 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.indentedFormat(w, depth, indent + 1, self.constant_pool);
}
}
pub fn thisClass(self: *const Self) ResolveError!ConstantPoolInfo {
const this_class = self.this_class - 1;
if (this_class >= self.constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = self.constant_pool[this_class];
if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType;
return cp_info.?;
}
pub fn superClass(self: *const Self) ResolveError!?ConstantPoolInfo {
if (self.super_class == 0) {
const this_class = (try self.thisClass()).class;
const this_class_name = (try this_class.name(self.constant_pool)).utf8;
if (!std.mem.eql(u8, this_class_name.bytes, "java/lang/Object")) return ResolveError.InvalidConstantType;
return null;
} else {
const super_class = self.super_class - 1;
if (super_class >= self.constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = self.constant_pool[super_class];
if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType;
if (self.access_flags.contains(.interface)) {
const super_class_name = (try cp_info.?.class.name(self.constant_pool)).utf8;
if (!std.mem.eql(u8, super_class_name.bytes, "java/lang/Object")) return ResolveError.InvalidConstantType;
} else {
// TODO: Assert that superclass does not have ACC_FINAL flag set.
}
return cp_info.?;
}
}
pub const ConstantPoolInfo = union(ConstantPoolInfo.Tag) {
class: Class,
field_ref: FieldRef,
method_ref: MethodRef,
interface_method_ref: InterfaceMethodRef,
string: String,
integer: Integer,
float: Float,
long: Long,
double: Double,
name_and_type: NameAndType,
utf8: Utf8,
method_handle: MethodHandle,
method_type: MethodType,
dynamic: Dynamic,
invoke_dynamic: InvokeDynamic,
module: Module,
package: Package,
pub const Tag = enum(u8) {
class = 7,
field_ref = 9,
method_ref = 10,
interface_method_ref = 11,
string = 8,
integer = 3,
float = 4,
long = 5,
double = 6,
name_and_type = 12,
utf8 = 1,
method_handle = 15,
method_type = 16,
dynamic = 17,
invoke_dynamic = 18,
module = 19,
package = 20,
};
pub const Class = struct {
name_index: u16,
pub fn name(self: *const Class, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo {
const name_index = self.name_index - 1;
if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[name_index];
if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType;
return cp_info.?;
}
};
pub const FieldRef = struct {
class_index: u16,
name_and_type_index: u16,
pub fn class(self: *const FieldRef, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo {
const class_index = self.class_index - 1;
if (class_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[class_index];
if (cp_info == null or @as(Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType;
return cp_info.?;
}
pub fn nameAndType(self: *const FieldRef, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo {
const name_and_type_index = self.name_and_type_index - 1;
if (name_and_type_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[name_and_type_index];
if (cp_info == null or @as(Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType;
return cp_info.?;
}
};
pub const MethodRef = struct {
class_index: u16,
name_and_type_index: u16,
pub fn class(self: *const MethodRef, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo {
const class_index = self.class_index - 1;
if (class_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[class_index];
if (cp_info == null or @as(Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType;
return cp_info.?;
}
pub fn nameAndType(self: *const MethodRef, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo {
const name_and_type_index = self.name_and_type_index - 1;
if (name_and_type_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[name_and_type_index];
if (cp_info == null or @as(Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType;
return cp_info.?;
}
};
pub const InterfaceMethodRef = struct {
class_index: u16,
name_and_type_index: u16,
pub fn class(self: *const InterfaceMethodRef, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo {
const class_index = self.class_index - 1;
if (class_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[class_index];
if (cp_info == null or @as(Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType;
return cp_info.?;
}
pub fn nameAndType(self: *const InterfaceMethodRef, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo {
const name_and_type_index = self.name_and_type_index - 1;
if (name_and_type_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[name_and_type_index];
if (cp_info == null or @as(Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType;
return cp_info.?;
}
};
pub const String = struct {
string_index: u16,
pub fn string(self: *const String, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo {
const string_index = self.string_index - 1;
if (string_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[string_index];
if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType;
return cp_info.?;
}
};
pub const Integer = struct {
bytes: u32,
};
pub const Float = struct {
bytes: u32,
};
pub const Long = struct {
high_bytes: u32,
low_bytes: u32,
};
pub const Double = struct {
high_bytes: u32,
low_bytes: u32,
};
pub const NameAndType = struct {
name_index: u16,
descriptor_index: u16,
pub fn name(self: *const NameAndType, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo {
const name_index = self.name_index - 1;
if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[name_index];
if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType;
return cp_info.?;
}
pub fn descriptor(self: *const NameAndType, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo {
const descriptor_index = self.descriptor_index - 1;
if (descriptor_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[descriptor_index];
if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType;
return cp_info.?;
}
};
pub const Utf8 = struct {
bytes: []const u8,
};
pub const MethodHandle = struct {
reference_kind: Kind,
reference_index: u16,
pub const Kind = enum(u8) {
get_field = 1,
get_static = 2,
put_field = 3,
put_static = 4,
invoke_virtual = 5,
invoke_static = 6,
invoke_special = 7,
new_invoke_special = 8,
invoke_interface = 9,
};
pub fn reference(self: *const MethodHandle, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo {
const reference_index = self.reference_index - 1;
if (reference_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[reference_index];
return switch (self.reference_kind) {
.get_field, .get_static, .put_field, .put_static => blk: {
if (cp_info == null or @as(Tag, cp_info.?) != .field_ref) break :blk ResolveError.InvalidConstantType;
break :blk cp_info.?;
},
.invoke_virtual, .invoke_static, .invoke_special, .new_invoke_special => blk: {
if (cp_info == null or @as(Tag, cp_info.?) != .method_ref) break :blk ResolveError.InvalidConstantType;
break :blk cp_info.?;
},
.invoke_interface => blk: {
if (cp_info == null or @as(Tag, cp_info.?) != .interface_method_ref) break :blk ResolveError.InvalidConstantType;
break :blk cp_info.?;
},
};
}
};
pub const MethodType = struct {
descriptor_index: u16,
pub fn descriptor(self: *const MethodType, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo {
const descriptor_index = self.descriptor_index - 1;
if (descriptor_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[descriptor_index];
if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType;
return cp_info.?;
}
};
pub const Dynamic = struct {
bootstrap_method_attr_index: u16,
name_and_type_index: u16,
pub fn nameAndType(self: *const Dynamic, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo {
const name_and_type_index = self.name_and_type_index - 1;
if (name_and_type_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[name_and_type_index];
if (cp_info == null or @as(Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType;
return cp_info.?;
}
};
pub const InvokeDynamic = struct {
bootstrap_method_attr_index: u16,
name_and_type_index: u16,
pub fn nameAndType(self: *const InvokeDynamic, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo {
const name_and_type_index = self.name_and_type_index - 1;
if (name_and_type_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[name_and_type_index];
if (cp_info == null or @as(Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType;
return cp_info.?;
}
};
pub const Module = struct {
name_index: u16,
pub fn name(self: *const Module, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo {
const name_index = self.name_index - 1;
if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[name_index];
if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType;
return cp_info.?;
}
};
pub const Package = struct {
name_index: u16,
pub fn name(self: *const Package, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo {
const name_index = self.name_index - 1;
if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[name_index];
if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType;
return cp_info.?;
}
};
pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!ConstantPoolInfo {
const tag_byte = try input.takeByte();
const tag = std.enums.fromInt(Tag, tag_byte) orelse return ParseError.InvalidTag;
return switch (tag) {
.class => .{
.class = .{
.name_index = try input.takeInt(u16, .big),
},
},
.field_ref => .{
.field_ref = .{
.class_index = try input.takeInt(u16, .big),
.name_and_type_index = try input.takeInt(u16, .big),
},
},
.method_ref => .{
.method_ref = .{
.class_index = try input.takeInt(u16, .big),
.name_and_type_index = try input.takeInt(u16, .big),
},
},
.interface_method_ref => .{
.interface_method_ref = .{
.class_index = try input.takeInt(u16, .big),
.name_and_type_index = try input.takeInt(u16, .big),
},
},
.string => .{
.string = .{
.string_index = try input.takeInt(u16, .big),
},
},
.integer => .{
.integer = .{
.bytes = try input.takeInt(u32, .big),
},
},
.float => .{
.integer = .{
.bytes = try input.takeInt(u32, .big),
},
},
.long => .{
.long = .{
.high_bytes = try input.takeInt(u32, .big),
.low_bytes = try input.takeInt(u32, .big),
},
},
.double => .{
.double = .{
.high_bytes = try input.takeInt(u32, .big),
.low_bytes = try input.takeInt(u32, .big),
},
},
.name_and_type => .{
.name_and_type = .{
.name_index = try input.takeInt(u16, .big),
.descriptor_index = try input.takeInt(u16, .big),
},
},
.utf8 => blk: {
const length = try input.takeInt(u16, .big);
const bytes = try input.readAlloc(allocator, length);
break :blk .{
.utf8 = .{
.bytes = bytes,
},
};
},
.method_handle => blk: {
const kind_byte = try input.takeByte();
const kind = std.enums.fromInt(MethodHandle.Kind, kind_byte) orelse return ParseError.InvalidTag;
break :blk .{
.method_handle = .{
.reference_kind = kind,
.reference_index = try input.takeInt(u16, .big),
},
};
},
.method_type => .{
.method_type = .{
.descriptor_index = try input.takeInt(u16, .big),
},
},
.dynamic => .{
.dynamic = .{
.bootstrap_method_attr_index = try input.takeInt(u16, .big),
.name_and_type_index = try input.takeInt(u16, .big),
},
},
.invoke_dynamic => .{
.invoke_dynamic = .{
.bootstrap_method_attr_index = try input.takeInt(u16, .big),
.name_and_type_index = try input.takeInt(u16, .big),
},
},
.module => .{
.module = .{
.name_index = try input.takeInt(u16, .big),
},
},
.package => .{
.package = .{
.name_index = try input.takeInt(u16, .big),
},
},
};
}
pub fn deinit(self: *ConstantPoolInfo, allocator: std.mem.Allocator) void {
switch (self.*) {
.utf8 => |info| allocator.free(info.bytes),
else => {},
}
}
pub fn format(self: *const ConstantPoolInfo, w: *std.Io.Writer) std.Io.Writer.Error!void {
switch (self.*) {
.class => |class| try w.print("#{d}", .{ class.name_index }),
.field_ref => |field_ref| try w.print("#{d}.#{d}", .{ field_ref.class_index, field_ref.name_and_type_index }),
.method_ref => |method_ref| try w.print("#{d}.#{d}", .{ method_ref.class_index, method_ref.name_and_type_index }),
.interface_method_ref => |interface_method_ref| try w.print("#{d}.#{d}", .{ interface_method_ref.class_index, interface_method_ref.name_and_type_index }),
.string => |string| try w.print("#{d}", .{ string.string_index }),
.integer => |integer| try w.print("{d}", .{ integer.bytes }),
.float => |float| try w.print("{d}", .{ @as(f32, @bitCast(float.bytes)) }),
.long => |long| try w.print("{d}", .{ (@as(u64, long.high_bytes) << 32) | long.low_bytes }),
.double => |double| try w.print("{d}", .{ @as(f64, @bitCast((@as(u64, double.high_bytes) << 32) | double.low_bytes)) }),
.name_and_type => |name_and_type| try w.print("#{d}:#{d}", .{ name_and_type.name_index, name_and_type.descriptor_index }),
.utf8 => |utf8| try w.print("\"{s}\"", .{ utf8.bytes }),
.method_handle => |method_handle| try w.print("{s} #{d}", .{ @tagName(method_handle.reference_kind), method_handle.reference_index }),
.method_type => |method_type| try w.print("#{d}", .{ method_type.descriptor_index }),
.dynamic => |dynamic| try w.print("#{d} #{d}", .{ dynamic.bootstrap_method_attr_index, dynamic.name_and_type_index }),
.invoke_dynamic => |invoke_dynamic| try w.print("#{d} #{d}", .{ invoke_dynamic.bootstrap_method_attr_index, invoke_dynamic.name_and_type_index }),
.module => |module| try w.print("#{d}", .{ module.name_index }),
.package => |package| try w.print("#{d}", .{ package.name_index }),
}
}
pub fn indentedFormat(
self: *const ConstantPoolInfo,
w: *std.Io.Writer,
depth: usize,
indent: u8,
constant_pool: []const ?ConstantPoolInfo,
) std.Io.Writer.Error!void {
try w.splatByteAll(' ', depth * indent);
try w.print("constant_value_type: {s}\n", .{ @tagName(self.*) });
switch (self.*) {
.class => |class| {
try w.splatByteAll(' ', depth * indent);
_ = try w.write("name:\n");
const class_name = class.name(constant_pool) catch return error.WriteFailed;
try class_name.indentedFormat(w, depth, indent + 1, constant_pool);
},
.field_ref => |field_ref| {
try w.splatByteAll(' ', depth * indent);
_ = try w.write("class:\n");
const class = field_ref.class(constant_pool) catch return error.WriteFailed;
try class.indentedFormat(w, depth, indent + 1, constant_pool);
try w.splatByteAll(' ', depth * indent);
_ = try w.write("name_and_type:\n");
const name_and_type = field_ref.nameAndType(constant_pool) catch return error.WriteFailed;
try name_and_type.indentedFormat(w, depth, indent + 1, constant_pool);
},
.method_ref => |method_ref| {
try w.splatByteAll(' ', depth * indent);
_ = try w.write("class:\n");
const class = method_ref.class(constant_pool) catch return error.WriteFailed;
try class.indentedFormat(w, depth, indent + 1, constant_pool);
try w.splatByteAll(' ', depth * indent);
_ = try w.write("name_and_type:\n");
const name_and_type = method_ref.nameAndType(constant_pool) catch return error.WriteFailed;
try name_and_type.indentedFormat(w, depth, indent + 1, constant_pool);
},
.interface_method_ref => |interface_method_ref| {
try w.splatByteAll(' ', depth * indent);
_ = try w.write("class:\n");
const class = interface_method_ref.class(constant_pool) catch return error.WriteFailed;
try class.indentedFormat(w, depth, indent + 1, constant_pool);
try w.splatByteAll(' ', depth * indent);
_ = try w.write("name_and_type:\n");
const name_and_type = interface_method_ref.nameAndType(constant_pool) catch return error.WriteFailed;
try name_and_type.indentedFormat(w, depth, indent + 1, constant_pool);
},
.string => |string| {
const s = string.string(constant_pool) catch return error.WriteFailed;
try s.indentedFormat(w, depth, indent + 1, constant_pool);
},
.integer => |integer| {
try w.splatByteAll(' ', depth * indent);
try w.print("integer: {d}\n", .{ integer.bytes });
},
.float => |float| {
const f: f32 = @bitCast(float.bytes);
try w.splatByteAll(' ', depth * indent);
try w.print("float: {d}\n", .{ f });
},
.long => |long| {
const l: u64 = (@as(u64, long.high_bytes) << 32) | long.low_bytes;
try w.splatByteAll(' ', depth * indent);
try w.print("long: {d}\n", .{ l });
},
.double => |double| {
const l: u64 = (@as(u64, double.high_bytes) << 32) | double.low_bytes;
const d: f64 = @bitCast(l);
try w.splatByteAll(' ', depth * indent);
try w.print("double: {d}\n", .{ d });
},
.name_and_type => |name_and_type| {
try w.splatByteAll(' ', depth * indent);
_ = try w.write("name:\n");
const name = name_and_type.name(constant_pool) catch return error.WriteFailed;
try name.indentedFormat(w, depth, indent + 1, constant_pool);
try w.splatByteAll(' ', depth * indent);
_ = try w.write("descriptor:\n");
const descriptor = name_and_type.descriptor(constant_pool) catch return error.WriteFailed;
try descriptor.indentedFormat(w, depth, indent + 1, constant_pool);
},
.utf8 => |utf8| {
try w.splatByteAll(' ', depth * indent);
try w.print("bytes: \"{s}\"\n", .{ utf8.bytes });
},
.method_handle => |method_handle| {
try w.splatByteAll(' ', depth * indent);
try w.print("kind: {t}\n", .{ method_handle.reference_kind });
try w.splatByteAll(' ', depth * indent);
_ = try w.write("reference:\n");
const reference = method_handle.reference(constant_pool) catch return error.WriteFailed;
try reference.indentedFormat(w, depth, indent + 1, constant_pool);
},
.method_type => |method_type| {
try w.splatByteAll(' ', depth * indent);
_ = try w.write("descriptor:\n");
const descriptor = method_type.descriptor(constant_pool) catch return error.WriteFailed;
try descriptor.indentedFormat(w, depth, indent + 1, constant_pool);
},
.dynamic => |dynamic| {
try w.splatByteAll(' ', depth * indent);
_ = try w.write("name_and_type:\n");
const name_and_type = dynamic.nameAndType(constant_pool) catch return error.WriteFailed;
try name_and_type.indentedFormat(w, depth, indent + 1, constant_pool);
},
.invoke_dynamic => |invoke_dynamic| {
try w.splatByteAll(' ', depth * indent);
_ = try w.write("name_and_type:\n");
const name_and_type = invoke_dynamic.nameAndType(constant_pool) catch return error.WriteFailed;
try name_and_type.indentedFormat(w, depth, indent + 1, constant_pool);
},
.module => |module| {
try w.splatByteAll(' ', depth * indent);
_ = try w.write("name:\n");
const name = module.name(constant_pool) catch return error.WriteFailed;
try name.indentedFormat(w, depth, indent + 1, constant_pool);
},
.package => |package| {
try w.splatByteAll(' ', depth * indent);
_ = try w.write("name:\n");
const name = package.name(constant_pool) catch return error.WriteFailed;
try name.indentedFormat(w, depth, indent + 1, constant_pool);
},
}
}
};