Format instructions

This commit is contained in:
ktkk 2026-09-07 13:35:46 +00:00
parent 76699b87e6
commit 6d8e9d05f9
2 changed files with 191 additions and 11 deletions

View file

@ -319,7 +319,9 @@ pub const AttributeInfo = union(enum) {
defer instr_indent -= 1;
try w.splatByteAll(' ', depth * instr_indent);
try w.print("{d}: {t}\n", .{ i, instr });
try w.print("{d}:\n", .{ i });
try instr.indentedFormat(w, depth, instr_indent + 1, constant_pool);
}
try w.splatByteAll(' ', depth * indent);

View file

@ -255,7 +255,7 @@ fn Branch(comptime T: type) type {
const bytes = bits / 8;
return struct {
branch: T,
branch_offset: T,
const Self = @This();
@ -263,7 +263,7 @@ fn Branch(comptime T: type) type {
defer byte_index.* += bytes;
return .{
.branch = try input.takeInt(u16, .big),
.branch_offset = try input.takeInt(u16, .big),
};
}
};
@ -456,7 +456,7 @@ const TableSwitch = struct {
}
};
const Wide = union {
const Wide = union(enum) {
form1: struct {
opcode: ModifiableOp,
index: u16,
@ -595,8 +595,8 @@ pub const Instruction = union(Op) {
fsub,
getfield: Index(u16),
getstatic: Index(u16),
goto: Index(u16),
goto_w: Index(u32),
goto: Branch(u16),
goto_w: Branch(u32),
i2b,
i2c,
i2d,
@ -1045,5 +1045,183 @@ pub const Instruction = union(Op) {
else => {},
}
}
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("type: {t}\n", .{ self.* });
switch (self.*) {
.aload, .astore, .dload, .dstore, .fload, .fstore, .iload, .istore, .lload, .lstore => |load_store| {
try w.splatByteAll(' ', depth * indent);
try w.print("index: {d}\n", .{ load_store.index });
},
.anewarray, .checkcast, .instanceof => |constant| {
const @"type" = constant_pool.get(constant.index) catch return error.WriteFailed;
try w.splatByteAll(' ', depth * indent);
_ = try w.write("type:\n");
try @"type".indentedFormat(w, depth, indent + 1, constant_pool);
},
.bipush => |bipush| {
try w.splatByteAll(' ', depth * indent);
try w.print("byte: 0x{X}\n", .{ bipush.byte });
},
.getfield, .getstatic, .putfield, .putstatic => |get_set_field| {
const field = constant_pool.get(get_set_field.index) catch return error.WriteFailed;
try w.splatByteAll(' ', depth * indent);
_ = try w.write("field:\n");
try field.indentedFormat(w, depth, indent + 1, constant_pool);
},
.goto, .if_acmpeq, .if_acmpne, .if_icmpeq, .if_icmpne, .if_icmplt, .if_icmpge, .if_icmpgt, .if_icmple, .ifeq, .ifne, .iflt, .ifge, .ifgt, .ifle, .ifnonnull, .ifnull, .jsr => |branch| {
try w.splatByteAll(' ', depth * indent);
try w.print("branch_offset: {d}\n", .{ branch.branch_offset });
},
.goto_w, .jsr_w => |branch_w| {
try w.splatByteAll(' ', depth * indent);
try w.print("branch_offset: {d}\n", .{ branch_w.branch_offset });
},
.iinc => |iinc| {
try w.splatByteAll(' ', depth * indent);
try w.print("index: {d}\n", .{ iinc.index });
try w.splatByteAll(' ', depth * indent);
try w.print("const: {d}\n", .{ iinc.@"const" });
},
.invokedynamic => |invokedynamic| {
const call_site_specifier = constant_pool.get(invokedynamic.index) catch return error.WriteFailed;
try w.splatByteAll(' ', depth * indent);
_ = try w.write("call_site_specifier:\n");
try call_site_specifier.indentedFormat(w, depth, indent + 1, constant_pool);
},
.invokeinterface => |invokeinterface| {
const interface_method = constant_pool.get(invokeinterface.index) catch return error.WriteFailed;
try w.splatByteAll(' ', depth * indent);
_ = try w.write("interface_method:\n");
try interface_method.indentedFormat(w, depth, indent + 1, constant_pool);
try w.splatByteAll(' ', depth * indent);
try w.print("count: {d}\n", .{ invokeinterface.count });
},
.invokespecial, .invokestatic, .invokevirtual => |invokespecial| {
const method = constant_pool.get(invokespecial.index) catch return error.WriteFailed;
try w.splatByteAll(' ', depth * indent);
_ = try w.write("method:\n");
try method.indentedFormat(w, depth, indent + 1, constant_pool);
},
.ldc => |ldc| {
const value = constant_pool.get(ldc.index) catch return error.WriteFailed;
try w.splatByteAll(' ', depth * indent);
_ = try w.write("value:\n");
try value.indentedFormat(w, depth, indent + 1, constant_pool);
},
.ldc_w, .ldc2_w => |ldc_w| {
const value = constant_pool.get(ldc_w.index) catch return error.WriteFailed;
try w.splatByteAll(' ', depth * indent);
_ = try w.write("value:\n");
try value.indentedFormat(w, depth, indent + 1, constant_pool);
},
.lookupswitch => |lookupswitch| {
try w.splatByteAll(' ', depth * indent);
try w.print("default: {d}\n", .{ lookupswitch.default });
try w.splatByteAll(' ', depth * indent);
_ = try w.write("pairs:\n");
var pairs_indent = indent;
for (lookupswitch.pairs, 0..) |pair, i| {
pairs_indent += 1;
defer pairs_indent -= 1;
try w.splatByteAll(' ', depth * pairs_indent);
try w.print("{d}:\n", .{ i });
{
pairs_indent += 1;
defer pairs_indent -= 1;
try w.splatByteAll(' ', depth * pairs_indent);
try w.print("match: {d}\n", .{ pair.match });
try w.splatByteAll(' ', depth * pairs_indent);
try w.print("offset: {d}\n", .{ pair.offset });
}
}
},
.multianewarray => |multianewarray| {
const @"type" = constant_pool.get(multianewarray.index) 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.print("dimensions: {d}\n", .{ multianewarray.dimensions });
},
.new => |new| {
const @"type" = constant_pool.get(new.index) catch return error.WriteFailed;
try w.splatByteAll(' ', depth * indent);
_ = try w.write("type:\n");
try @"type".indentedFormat(w, depth, indent + 1, constant_pool);
},
.newarray => |newarray| {
try w.splatByteAll(' ', depth * indent);
try w.print("type: {t}\n", .{ newarray.array_type });
},
.ret => |ret| {
try w.splatByteAll(' ', depth * indent);
try w.print("index: {d}\n", .{ ret.index });
},
.sipush => |sipush| {
try w.splatByteAll(' ', depth * indent);
try w.print("short: 0x{X}\n", .{ sipush.short });
},
.tableswitch => |tableswitch| {
try w.splatByteAll(' ', depth * indent);
try w.print("default: {d}\n", .{ tableswitch.default });
try w.splatByteAll(' ', depth * indent);
_ = try w.write("offsets:\n");
var offsets_indent = indent;
for (tableswitch.offsets, 0..) |offset, i| {
offsets_indent += 1;
defer offsets_indent -= 1;
try w.splatByteAll(' ', depth * offsets_indent);
try w.print("{d}: {d}\n", .{ i, offset });
}
},
.wide => |wide| switch (wide) {
.form1 => |form1| {
try w.splatByteAll(' ', depth * indent);
try w.print("opcode: {t}\n", .{ form1.opcode });
try w.splatByteAll(' ', depth * indent);
try w.print("index: {d}\n", .{ form1.index });
},
.form2 => |form2| {
try w.splatByteAll(' ', depth * indent);
try w.print("opcode: {t}\n", .{ form2.opcode });
try w.splatByteAll(' ', depth * indent);
try w.print("index: {d}\n", .{ form2.index });
try w.splatByteAll(' ', depth * indent);
try w.print("const: {d}\n", .{ form2.@"const" });
},
},
else => {},
}
}
};