diff --git a/src/Class/AttributeInfo.zig b/src/Class/AttributeInfo.zig index bf1feb5..e138b98 100644 --- a/src/Class/AttributeInfo.zig +++ b/src/Class/AttributeInfo.zig @@ -319,20 +319,22 @@ 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); _ = try w.write("exception_table:\n"); var exception_indent = indent; for (attr.exception_table, 0..) |*exception, i| { - exception_indent += 1; - defer exception_indent -= 1; + exception_indent += 1; + defer exception_indent -= 1; - try w.splatByteAll(' ', depth * exception_indent); - try w.print("{d}:\n", .{ i }); + try w.splatByteAll(' ', depth * exception_indent); + try w.print("{d}:\n", .{ i }); - try exception.indentedFormat(w, depth, exception_indent + 1, constant_pool); + try exception.indentedFormat(w, depth, exception_indent + 1, constant_pool); } try w.splatByteAll(' ', depth * indent); diff --git a/src/Class/Code/Instruction.zig b/src/Class/Code/Instruction.zig index d1af3d7..494dc8b 100644 --- a/src/Class/Code/Instruction.zig +++ b/src/Class/Code/Instruction.zig @@ -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 => {}, + } + } };