From 51e90fae800448a575b4a27bb35ffda6ea2825b4 Mon Sep 17 00:00:00 2001 From: ktkk Date: Fri, 26 Jun 2026 15:35:38 +0000 Subject: [PATCH 1/6] Add instructions --- src/InstructionSet.zig | 205 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 src/InstructionSet.zig diff --git a/src/InstructionSet.zig b/src/InstructionSet.zig new file mode 100644 index 0000000..a3572e1 --- /dev/null +++ b/src/InstructionSet.zig @@ -0,0 +1,205 @@ +const InstructionSet = enum(u8) { + aaload = 0x32, + aastore = 0x53, + aconst_null = 0x1, + aload = 0x19, + aload_0 = 0x2a, + aload_1 = 0x2b, + aload_2 = 0x2c, + aload_3 = 0x2d, + anewarray = 0xbd, + areturn = 0xb0, + arraylength = 0xbe, + astore = 0x3a, + astore_0 = 0x4b, + astore_1 = 0x4c, + astore_2 = 0x4d, + astore_3 = 0x4e, + athrow = 0xbf, + baload = 0x33, + bastore = 0x54, + bipush = 0x10, + caload = 0x34, + castore = 0x55, + checkcast = 0xc0, + d2f = 0x90, + d2i = 0x8e, + d2l = 0x8f, + dadd = 0x63, + daload = 0x31, + dastore = 0x52, + dcmpg = 0x98, + dcmpl = 0x97, + dconst_0 = 0xe, + dconst_1 = 0xf, + ddiv = 0x6f, + dload = 0x18, + dload_0 = 0x26, + dload_1 = 0x27, + dload_2 = 0x28, + dload_3 = 0x29, + dmul = 0x6b, + dneg = 0x77, + drem = 0x73, + dreturn = 0xaf, + dstore = 0x39, + dstore_0 = 0x47, + dstore_1 = 0x48, + dstore_2 = 0x49, + dstore_3 = 0x4a, + dsub = 0x67, + dup = 0x59, + dup_x1 = 0x5a, + dup_x2 = 0x5b, + dup2 = 0x5c, + dup2_x1 = 0x5d, + dup2_x2 = 0x5e, + f2d = 0x8d, + f2i = 0x8b, + f2l = 0x8c, + fadd = 0x62, + faload = 0x30, + fastore = 0x51, + fcmpg = 0x96, + fcmpl = 0x95, + fconst_0 = 0xb, + fconst_1 = 0xc, + fconst_2 = 0xd, + fdiv = 0x6e, + fload = 0x17, + fload_0 = 0x22, + fload_1 = 0x23, + fload_2 = 0x24, + fload_3 = 0x25, + fmul = 0x6a, + fneg = 0x76, + frem = 0x72, + freturn = 0xae, + fstore = 0x38, + fstore_0 = 0x43, + fstore_1 = 0x44, + fstore_2 = 0x45, + fstore_3 = 0x46, + fsub = 0x66, + getfield = 0xb4, + getstatic = 0xb2, + goto = 0xa7, + goto_w = 0xc8, + i2b = 0x91, + i2c = 0x92, + i2d = 0x87, + i2f = 0x86, + i2l = 0x85, + i2s = 0x93, + iadd = 0x60, + iaload = 0x2e, + iand = 0x7e, + iastore = 0x4f, + iconst_m1 = 0x2, + iconst_0 = 0x3, + iconst_1 = 0x4, + iconst_2 = 0x5, + iconst_3 = 0x6, + iconst_4 = 0x7, + iconst_5 = 0x8, + idiv = 0x6c, + if_acmpeq = 0xa5, + if_acmpne = 0xa6, + if_icmpeq = 0x9f, + if_icmpne = 0xa0, + if_icmplt = 0xa1, + if_icmpge = 0xa2, + if_icmpgt = 0xa3, + if_icmple = 0xa4, + ifeq = 0x99, + ifne = 0x9a, + iflt = 0x9b, + ifge = 0x9c, + ifgt = 0x9d, + ifle = 0x9e, + ifnonnull = 0xc7, + ifnull = 0xc6, + iinc = 0x84, + iload = 0x15, + iload_0 = 0x1a, + iload_1 = 0x1b, + iload_2 = 0x1c, + iload_3 = 0x1d, + imul = 0x68, + ineg = 0x74, + instanceof = 0xc1, + invokedynamic = 0xba, + invokeinterface = 0xb9, + invokespecial = 0xb7, + invokestatic = 0xb8, + invokevirtual = 0xb6, + ior = 0x80, + irem = 0x70, + ireturn = 0xac, + ishl = 0x78, + ishr = 0x7a, + istore = 0x36, + istore_0 = 0x3b, + istore_1 = 0x3c, + istore_2 = 0x3d, + istore_3 = 0x3e, + isub = 0x64, + iushr = 0x7c, + ixor = 0x82, + jsr = 0xa8, + jsr_2 = 0xc9, + l2d = 0x8a, + l2f = 0x89, + l2i = 0x88, + ladd = 0x61, + laload = 0x2f, + land = 0x7f, + lastore = 0x50, + lcmp = 0x94, + lconst_0 = 0x9, + lconst_1 = 0xa, + ldc = 0x12, + ldc_2 = 0x13, + ldc2_w = 0x14, + ldiv = 0x6d, + lload = 0x16, + lload_0 = 0x1e, + lload_1 = 0x1f, + lload_2 = 0x20, + lload_3 = 0x21, + lmul = 0x69, + lneg = 0x75, + lookupswitch = 0xab, + lor = 0x81, + lrem = 0x71, + lreturn = 0xad, + lshl = 0x79, + lshr = 0x7b, + lstore = 0x37, + lstore_0 = 0x3f, + lstore_1 = 0x40, + lstore_2 = 0x41, + lstore_3 = 0x42, + lsub = 0x65, + lushr = 0x7d, + lxor = 0x83, + monitorenter = 0xc2, + monitorexit = 0xc3, + multianewarray = 0xc5, + new = 0xbb, + newarray = 0xbc, + nop = 0x0, + pop = 0x57, + pop2 = 0x58, + putfield = 0xb5, + putstatic = 0xb3, + ret = 0xa9, + @"return" = 0xb1, + saload = 0x35, + sastore = 0x56, + sipush = 0x11, + swap = 0x5f, + tableswitch = 0xaa, + wide = 0xc4, +}; + From 28ac98682734235ba2508b1bf98dfe2c3d21baa3 Mon Sep 17 00:00:00 2001 From: ktkk Date: Fri, 3 Jul 2026 08:52:49 +0000 Subject: [PATCH 2/6] WIP: Instructions --- src/Class/AttributeInfo/Code.zig | 26 + src/Class/AttributeInfo/Instructions.zig | 582 +++++++++++++++++++++++ src/InstructionSet.zig | 205 -------- 3 files changed, 608 insertions(+), 205 deletions(-) create mode 100644 src/Class/AttributeInfo/Code.zig create mode 100644 src/Class/AttributeInfo/Instructions.zig delete mode 100644 src/InstructionSet.zig diff --git a/src/Class/AttributeInfo/Code.zig b/src/Class/AttributeInfo/Code.zig new file mode 100644 index 0000000..f91a8ce --- /dev/null +++ b/src/Class/AttributeInfo/Code.zig @@ -0,0 +1,26 @@ +const std = @import("std"); + +const Class = @import("../../Class.zig"); +const AttributeInfo = Class.AttributeInfo; +const ParseError = Class.ParseError; + +const Instructions = @import("Instructions.zig").Instructions; + +max_stack: u16, +max_locals: u16, +code: []Instructions, +exception_table: []ExceptionHandler, +attributes: []AttributeInfo, + +const Self = @This(); + +pub const ExceptionHandler = struct { + start_pc: u16, + end_pc: u16, + handler_pc: u16, + catch_type: u16, +}; + +pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { +} + diff --git a/src/Class/AttributeInfo/Instructions.zig b/src/Class/AttributeInfo/Instructions.zig new file mode 100644 index 0000000..b4ec6b6 --- /dev/null +++ b/src/Class/AttributeInfo/Instructions.zig @@ -0,0 +1,582 @@ +const std = @import("std"); + +const InstructionSet = enum(u8) { + aaload = 0x32, + aastore = 0x53, + aconst_null = 0x1, + aload = 0x19, + aload_0 = 0x2a, + aload_1 = 0x2b, + aload_2 = 0x2c, + aload_3 = 0x2d, + anewarray = 0xbd, + areturn = 0xb0, + arraylength = 0xbe, + astore = 0x3a, + astore_0 = 0x4b, + astore_1 = 0x4c, + astore_2 = 0x4d, + astore_3 = 0x4e, + athrow = 0xbf, + baload = 0x33, + bastore = 0x54, + bipush = 0x10, + caload = 0x34, + castore = 0x55, + checkcast = 0xc0, + d2f = 0x90, + d2i = 0x8e, + d2l = 0x8f, + dadd = 0x63, + daload = 0x31, + dastore = 0x52, + dcmpg = 0x98, + dcmpl = 0x97, + dconst_0 = 0xe, + dconst_1 = 0xf, + ddiv = 0x6f, + dload = 0x18, + dload_0 = 0x26, + dload_1 = 0x27, + dload_2 = 0x28, + dload_3 = 0x29, + dmul = 0x6b, + dneg = 0x77, + drem = 0x73, + dreturn = 0xaf, + dstore = 0x39, + dstore_0 = 0x47, + dstore_1 = 0x48, + dstore_2 = 0x49, + dstore_3 = 0x4a, + dsub = 0x67, + dup = 0x59, + dup_x1 = 0x5a, + dup_x2 = 0x5b, + dup2 = 0x5c, + dup2_x1 = 0x5d, + dup2_x2 = 0x5e, + f2d = 0x8d, + f2i = 0x8b, + f2l = 0x8c, + fadd = 0x62, + faload = 0x30, + fastore = 0x51, + fcmpg = 0x96, + fcmpl = 0x95, + fconst_0 = 0xb, + fconst_1 = 0xc, + fconst_2 = 0xd, + fdiv = 0x6e, + fload = 0x17, + fload_0 = 0x22, + fload_1 = 0x23, + fload_2 = 0x24, + fload_3 = 0x25, + fmul = 0x6a, + fneg = 0x76, + frem = 0x72, + freturn = 0xae, + fstore = 0x38, + fstore_0 = 0x43, + fstore_1 = 0x44, + fstore_2 = 0x45, + fstore_3 = 0x46, + fsub = 0x66, + getfield = 0xb4, + getstatic = 0xb2, + goto = 0xa7, + goto_w = 0xc8, + i2b = 0x91, + i2c = 0x92, + i2d = 0x87, + i2f = 0x86, + i2l = 0x85, + i2s = 0x93, + iadd = 0x60, + iaload = 0x2e, + iand = 0x7e, + iastore = 0x4f, + iconst_m1 = 0x2, + iconst_0 = 0x3, + iconst_1 = 0x4, + iconst_2 = 0x5, + iconst_3 = 0x6, + iconst_4 = 0x7, + iconst_5 = 0x8, + idiv = 0x6c, + if_acmpeq = 0xa5, + if_acmpne = 0xa6, + if_icmpeq = 0x9f, + if_icmpne = 0xa0, + if_icmplt = 0xa1, + if_icmpge = 0xa2, + if_icmpgt = 0xa3, + if_icmple = 0xa4, + ifeq = 0x99, + ifne = 0x9a, + iflt = 0x9b, + ifge = 0x9c, + ifgt = 0x9d, + ifle = 0x9e, + ifnonnull = 0xc7, + ifnull = 0xc6, + iinc = 0x84, + iload = 0x15, + iload_0 = 0x1a, + iload_1 = 0x1b, + iload_2 = 0x1c, + iload_3 = 0x1d, + imul = 0x68, + ineg = 0x74, + instanceof = 0xc1, + invokedynamic = 0xba, + invokeinterface = 0xb9, + invokespecial = 0xb7, + invokestatic = 0xb8, + invokevirtual = 0xb6, + ior = 0x80, + irem = 0x70, + ireturn = 0xac, + ishl = 0x78, + ishr = 0x7a, + istore = 0x36, + istore_0 = 0x3b, + istore_1 = 0x3c, + istore_2 = 0x3d, + istore_3 = 0x3e, + isub = 0x64, + iushr = 0x7c, + ixor = 0x82, + jsr = 0xa8, + jsr_2 = 0xc9, + l2d = 0x8a, + l2f = 0x89, + l2i = 0x88, + ladd = 0x61, + laload = 0x2f, + land = 0x7f, + lastore = 0x50, + lcmp = 0x94, + lconst_0 = 0x9, + lconst_1 = 0xa, + ldc = 0x12, + ldc_2 = 0x13, + ldc2_w = 0x14, + ldiv = 0x6d, + lload = 0x16, + lload_0 = 0x1e, + lload_1 = 0x1f, + lload_2 = 0x20, + lload_3 = 0x21, + lmul = 0x69, + lneg = 0x75, + lookupswitch = 0xab, + lor = 0x81, + lrem = 0x71, + lreturn = 0xad, + lshl = 0x79, + lshr = 0x7b, + lstore = 0x37, + lstore_0 = 0x3f, + lstore_1 = 0x40, + lstore_2 = 0x41, + lstore_3 = 0x42, + lsub = 0x65, + lushr = 0x7d, + lxor = 0x83, + monitorenter = 0xc2, + monitorexit = 0xc3, + multianewarray = 0xc5, + new = 0xbb, + newarray = 0xbc, + nop = 0x0, + pop = 0x57, + pop2 = 0x58, + putfield = 0xb5, + putstatic = 0xb3, + ret = 0xa9, + @"return" = 0xb1, + saload = 0x35, + sastore = 0x56, + sipush = 0x11, + swap = 0x5f, + tableswitch = 0xaa, + wide = 0xc4, +}; + +const IndexU8 = struct { + index: u8, +}; + +const IndexU16 = struct { + index: u16, + + const Self = @This(); + + pub fn from(b1: u8, b2: u8) Self { + return .{ .index = (b1 << 8) | b2 }; + } +}; + +const IndexU32 = struct { + index: u32, + + const Self = @This(); + + pub fn from(b1: u8, b2: u8, b3: u8, b4: u8) Self { + return .{ .index = (b1 << 24) | (b2 << 16) | (b3 << 8) | b4 }; + } +}; + +const Byte = struct { + byte: u8, +}; + +const BranchU16 = struct { + branch: u16, + + const Self = @This(); + + pub fn from(b1: u8, b2: u8) Self { + return .{ .index = (b1 << 8) | b2 }; + } +}; + +const BranchU32 = struct { + branch: u32, + + const Self = @This(); + + pub fn from(b1: u8, b2: u8, b3: u8, b4: u8) Self { + return .{ .branch = (b1 << 24) | (b2 << 16) | (b3 << 8) | b4 }; + } +}; + +const IInc = struct { + index: u8, + @"const": u8, +}; + +const InvokeDynamic = struct { + index: u16, + + const Self = @This(); + + pub fn from(b1: u8, b2: u8, pad1: u8, pad2: u8) Self { + _ = pad1; + _ = pad2; + return .{ .index = (b1 << 8) | b2 }; + } +}; + +const InvokeInterface = struct { + index: u16, + count: u8, + + const Self = @This(); + + pub fn from(b1: u8, b2: u8, count: u8, pad: u8) Self { + _ = pad; + return .{ .index = (b1 << 8) | b2, .count = count }; + } +}; + +const LookupSwitch = struct { + default: i32, + pairs: []Pair, + + const Pair = struct { + match: u32, + offset: i32, + }; +}; + +const MultiANewArray = struct { + index: u16, + dimensions: u8, + + const Self = @This(); + + pub fn from(b1: u8, b2: u8, dimensions: u8) Self { + return .{ .index = (b1 << 8) | b2, .dimensions = dimensions }; + } +}; + +const NewArray = struct { + array_type: ArrayType, + + const Self = @This(); + + const ArrayType = enum(u8) { + boolean = 4, + char = 5, + float = 6, + double = 7, + byte = 8, + short = 9, + int = 10, + long = 11, + }; + + pub fn from(atype: u8) Self { + const array_type = std.enums.fromInt(ArrayType, atype) orelse { + std.log.err("Invalid array type"); + std.process.exit(1); + }; + return .{ .array_type = array_type }; + } +}; + +const Short = struct { + short: u16, + + const Self = @This(); + + pub fn from(b1: u8, b2: u8) Self { + return .{ .short = (b1 << 8) | b2 }; + } +}; + +const TableSwitch = struct { + default: i32, + low: i32, + hight: i32, + offsets: []i32, +}; + +const Wide = union { + form1: struct { + opcode: ModifiableInstruction, + index: u16, + }, + form2: struct { + // Should always be iinc + opcode: ModifiableInstruction, + index: u16, + @"const": u16, + }, + + const Self = @This(); + + const ModifiableInstruction = enum(u8) { + iload = 0x15, + fload = 0x17, + aload = 0x19, + lload = 0x16, + dload = 0x18, + istore = 0x36, + fstore = 0x38, + astore = 0x3a, + lstore = 0x37, + dstore = 0x39, + ret = 0xa9, + iinc = 0x84, + }; +}; + +const Instructions = union(InstructionSet) { + aaload, + aastore, + aconst_null, + aload: IndexU8, + aload_0, + aload_1, + aload_2, + aload_3, + anewarray: IndexU16, + areturn, + arraylength, + astore: IndexU8, + astore_0, + astore_1, + astore_2, + astore_3, + athrow, + baload, + bastore, + bipush: Byte, + caload, + castore, + checkcast: IndexU16, + d2f, + d2i, + d2l, + dadd, + daload, + dastore, + dcmpg, + dcmpl, + dconst_0, + dconst_1, + ddiv, + dload: IndexU8, + dload_0, + dload_1, + dload_2, + dload_3, + dmul, + dneg, + drem, + dreturn, + dstore: IndexU8, + dstore_0, + dstore_1, + dstore_2, + dstore_3, + dsub, + dup, + dup_x1, + dup_x2, + dup2, + dup2_x1, + dup2_x2, + f2d, + f2i, + f2l, + fadd, + faload, + fastore, + fcmpg, + fcmpl, + fconst_0, + fconst_1, + fconst_2, + fdiv, + fload: IndexU8, + fload_0, + fload_1, + fload_2, + fload_3, + fmul, + fneg, + frem, + freturn, + fstore: IndexU8, + fstore_0, + fstore_1, + fstore_2, + fstore_3, + fsub, + getfield: IndexU16, + getstatic: IndexU16, + goto: IndexU16, + goto_w: IndexU32, + i2b, + i2c, + i2d, + i2f, + i2l, + i2s, + iadd, + iaload, + iand, + iastore, + iconst_m1, + iconst_0, + iconst_1, + iconst_2, + iconst_3, + iconst_4, + iconst_5, + idiv, + if_acmpeq: BranchU16, + if_acmpne: BranchU16, + if_icmpeq: BranchU16, + if_icmpne: BranchU16, + if_icmplt: BranchU16, + if_icmpge: BranchU16, + if_icmpgt: BranchU16, + if_icmple: BranchU16, + ifeq: BranchU16, + ifne: BranchU16, + iflt: BranchU16, + ifge: BranchU16, + ifgt: BranchU16, + ifle: BranchU16, + ifnonnull: BranchU16, + ifnull: BranchU16, + iinc: IInc, + iload: IndexU8, + iload_0, + iload_1, + iload_2, + iload_3, + imul, + ineg, + instanceof: IndexU16, + invokedynamic: InvokeDynamic, + invokeinterface: InvokeInterface, + invokespecial: IndexU16, + invokestatic: IndexU16, + invokevirtual: IndexU16, + ior, + irem, + ireturn, + ishl, + ishr, + istore: IndexU8, + istore_0, + istore_1, + istore_2, + istore_3, + isub, + iushr, + ixor, + jsr: BranchU16, + jsr_w: BranchU32, + l2d, + l2f, + l2i, + ladd, + laload, + land, + lastore, + lcmp, + lconst_0, + lconst_1, + ldc: IndexU8, + ldc_w: IndexU16, + ldc2_w: IndexU16, + ldiv, + lload: IndexU8, + lload_0, + lload_1, + lload_2, + lload_3, + lmul, + lneg, + lookupswitch: LookupSwitch, + lor, + lrem, + lreturn, + lshl, + lshr, + lstore: IndexU8, + lstore_0, + lstore_1, + lstore_2, + lstore_3, + lsub, + lushr, + lxor, + monitorenter, + monitorexit, + mulitanewarray: MultiANewArray, + new: IndexU16, + newarray: NewArray, + nop, + pop, + pop2, + putfield: IndexU16, + putstatic: IndexU16, + ret: IndexU8, + @"return", + saload, + sastore, + sipush: Short, + swap, + tableswitch: TableSwitch, + wide: Wide, +}; + diff --git a/src/InstructionSet.zig b/src/InstructionSet.zig deleted file mode 100644 index a3572e1..0000000 --- a/src/InstructionSet.zig +++ /dev/null @@ -1,205 +0,0 @@ -const InstructionSet = enum(u8) { - aaload = 0x32, - aastore = 0x53, - aconst_null = 0x1, - aload = 0x19, - aload_0 = 0x2a, - aload_1 = 0x2b, - aload_2 = 0x2c, - aload_3 = 0x2d, - anewarray = 0xbd, - areturn = 0xb0, - arraylength = 0xbe, - astore = 0x3a, - astore_0 = 0x4b, - astore_1 = 0x4c, - astore_2 = 0x4d, - astore_3 = 0x4e, - athrow = 0xbf, - baload = 0x33, - bastore = 0x54, - bipush = 0x10, - caload = 0x34, - castore = 0x55, - checkcast = 0xc0, - d2f = 0x90, - d2i = 0x8e, - d2l = 0x8f, - dadd = 0x63, - daload = 0x31, - dastore = 0x52, - dcmpg = 0x98, - dcmpl = 0x97, - dconst_0 = 0xe, - dconst_1 = 0xf, - ddiv = 0x6f, - dload = 0x18, - dload_0 = 0x26, - dload_1 = 0x27, - dload_2 = 0x28, - dload_3 = 0x29, - dmul = 0x6b, - dneg = 0x77, - drem = 0x73, - dreturn = 0xaf, - dstore = 0x39, - dstore_0 = 0x47, - dstore_1 = 0x48, - dstore_2 = 0x49, - dstore_3 = 0x4a, - dsub = 0x67, - dup = 0x59, - dup_x1 = 0x5a, - dup_x2 = 0x5b, - dup2 = 0x5c, - dup2_x1 = 0x5d, - dup2_x2 = 0x5e, - f2d = 0x8d, - f2i = 0x8b, - f2l = 0x8c, - fadd = 0x62, - faload = 0x30, - fastore = 0x51, - fcmpg = 0x96, - fcmpl = 0x95, - fconst_0 = 0xb, - fconst_1 = 0xc, - fconst_2 = 0xd, - fdiv = 0x6e, - fload = 0x17, - fload_0 = 0x22, - fload_1 = 0x23, - fload_2 = 0x24, - fload_3 = 0x25, - fmul = 0x6a, - fneg = 0x76, - frem = 0x72, - freturn = 0xae, - fstore = 0x38, - fstore_0 = 0x43, - fstore_1 = 0x44, - fstore_2 = 0x45, - fstore_3 = 0x46, - fsub = 0x66, - getfield = 0xb4, - getstatic = 0xb2, - goto = 0xa7, - goto_w = 0xc8, - i2b = 0x91, - i2c = 0x92, - i2d = 0x87, - i2f = 0x86, - i2l = 0x85, - i2s = 0x93, - iadd = 0x60, - iaload = 0x2e, - iand = 0x7e, - iastore = 0x4f, - iconst_m1 = 0x2, - iconst_0 = 0x3, - iconst_1 = 0x4, - iconst_2 = 0x5, - iconst_3 = 0x6, - iconst_4 = 0x7, - iconst_5 = 0x8, - idiv = 0x6c, - if_acmpeq = 0xa5, - if_acmpne = 0xa6, - if_icmpeq = 0x9f, - if_icmpne = 0xa0, - if_icmplt = 0xa1, - if_icmpge = 0xa2, - if_icmpgt = 0xa3, - if_icmple = 0xa4, - ifeq = 0x99, - ifne = 0x9a, - iflt = 0x9b, - ifge = 0x9c, - ifgt = 0x9d, - ifle = 0x9e, - ifnonnull = 0xc7, - ifnull = 0xc6, - iinc = 0x84, - iload = 0x15, - iload_0 = 0x1a, - iload_1 = 0x1b, - iload_2 = 0x1c, - iload_3 = 0x1d, - imul = 0x68, - ineg = 0x74, - instanceof = 0xc1, - invokedynamic = 0xba, - invokeinterface = 0xb9, - invokespecial = 0xb7, - invokestatic = 0xb8, - invokevirtual = 0xb6, - ior = 0x80, - irem = 0x70, - ireturn = 0xac, - ishl = 0x78, - ishr = 0x7a, - istore = 0x36, - istore_0 = 0x3b, - istore_1 = 0x3c, - istore_2 = 0x3d, - istore_3 = 0x3e, - isub = 0x64, - iushr = 0x7c, - ixor = 0x82, - jsr = 0xa8, - jsr_2 = 0xc9, - l2d = 0x8a, - l2f = 0x89, - l2i = 0x88, - ladd = 0x61, - laload = 0x2f, - land = 0x7f, - lastore = 0x50, - lcmp = 0x94, - lconst_0 = 0x9, - lconst_1 = 0xa, - ldc = 0x12, - ldc_2 = 0x13, - ldc2_w = 0x14, - ldiv = 0x6d, - lload = 0x16, - lload_0 = 0x1e, - lload_1 = 0x1f, - lload_2 = 0x20, - lload_3 = 0x21, - lmul = 0x69, - lneg = 0x75, - lookupswitch = 0xab, - lor = 0x81, - lrem = 0x71, - lreturn = 0xad, - lshl = 0x79, - lshr = 0x7b, - lstore = 0x37, - lstore_0 = 0x3f, - lstore_1 = 0x40, - lstore_2 = 0x41, - lstore_3 = 0x42, - lsub = 0x65, - lushr = 0x7d, - lxor = 0x83, - monitorenter = 0xc2, - monitorexit = 0xc3, - multianewarray = 0xc5, - new = 0xbb, - newarray = 0xbc, - nop = 0x0, - pop = 0x57, - pop2 = 0x58, - putfield = 0xb5, - putstatic = 0xb3, - ret = 0xa9, - @"return" = 0xb1, - saload = 0x35, - sastore = 0x56, - sipush = 0x11, - swap = 0x5f, - tableswitch = 0xaa, - wide = 0xc4, -}; - From 9e0e5ff8ea07ec9f6497a3566fbeb152301c0d73 Mon Sep 17 00:00:00 2001 From: ktkk Date: Tue, 25 Aug 2026 12:07:43 +0000 Subject: [PATCH 3/6] Add EnumFlags tests --- src/EnumFlags.zig | 75 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/src/EnumFlags.zig b/src/EnumFlags.zig index 80c7e40..d7ca723 100644 --- a/src/EnumFlags.zig +++ b/src/EnumFlags.zig @@ -8,7 +8,13 @@ pub fn EnumFlags(comptime E: type) type { mask: BackingInt, - pub const empty: Self = .{ .data = 0 }; + pub const empty: Self = .{ .mask = 0 }; + + test empty { + const flags: EnumFlags(Enum) = .empty; + + try std.testing.expectEqual(0b0000_0000, flags.mask); + } pub fn from(mask: BackingInt) Self { return .{ @@ -16,6 +22,12 @@ pub fn EnumFlags(comptime E: type) type { }; } + test from { + const flags: EnumFlags(Enum) = .from(0b0000_0001); + + try std.testing.expectEqual(0b0000_0001, flags.mask); + } + pub fn contains(self: Self, flag: E) bool { return (self.mask & @intFromEnum(flag)) != 0; } @@ -48,3 +60,64 @@ pub fn EnumFlags(comptime E: type) type { }; } +const Enum = enum(u8) { + a = 0b0000_0001, + b = 0b0000_0010, + c = 0b0000_0100, +}; + +test { + + std.testing.refAllDecls(EnumFlags(Enum)); +} + +test "EnumFlags operations" { + var flags: EnumFlags(Enum) = .from(0b0000_0001); + + try std.testing.expect(flags.contains(Enum.a)); + + flags.remove(Enum.a); + try std.testing.expect(!flags.contains(Enum.a)); + + flags.remove(Enum.a); // Removing twice should not enable the flag + try std.testing.expect(!flags.contains(Enum.a)); + + flags.insert(Enum.b); + try std.testing.expectEqual(0b0000_0010, flags.mask); + try std.testing.expect(flags.contains(Enum.b)); + try std.testing.expect(!flags.contains(Enum.a)); + + flags.toggle(Enum.a); + try std.testing.expectEqual(0b0000_0011, flags.mask); + try std.testing.expect(flags.contains(Enum.a)); + try std.testing.expect(flags.contains(Enum.b)); + try std.testing.expect(!flags.contains(Enum.c)); +} + +fn testFormat(expected: []const u8, flags: EnumFlags(Enum)) !void { + var out_buf: [1024]u8 = undefined; + var out: std.Io.Writer = .fixed(&out_buf); + + try flags.format(&out); + + try std.testing.expectEqualStrings(expected, out.buffered()); +} + +test "EnumFlags format" { + var flags: EnumFlags(Enum) = .empty; + + flags.toggle(Enum.a); + flags.toggle(Enum.b); + flags.toggle(Enum.c); + try testFormat("a | b | c", flags); + + flags.toggle(Enum.b); + try testFormat("a | c", flags); + + flags.remove(Enum.c); + try testFormat("a", flags); + + flags.insert(Enum.b); + try testFormat("a | b", flags); +} + From 176be5dad8ee5e418ef3eda0e1eb9be0eba5b2e5 Mon Sep 17 00:00:00 2001 From: ktkk Date: Fri, 4 Sep 2026 15:14:11 +0000 Subject: [PATCH 4/6] Parse all op types --- src/Class.zig | 2 + src/Class/AttributeInfo.zig | 27 +- src/Class/AttributeInfo/Code.zig | 26 - src/Class/AttributeInfo/Instructions.zig | 582 ------------ src/Class/Code/Instruction.zig | 1072 ++++++++++++++++++++++ testsuite/classes/java/Main.java | 46 + testsuite/classes/java/Object.class | Bin 2412 -> 0 bytes 7 files changed, 1142 insertions(+), 613 deletions(-) delete mode 100644 src/Class/AttributeInfo/Code.zig delete mode 100644 src/Class/AttributeInfo/Instructions.zig create mode 100644 src/Class/Code/Instruction.zig delete mode 100644 testsuite/classes/java/Object.class diff --git a/src/Class.zig b/src/Class.zig index 09a3846..ff1a581 100644 --- a/src/Class.zig +++ b/src/Class.zig @@ -36,6 +36,8 @@ pub const ClassAccessFlags = EnumFlags(enum(u16) { pub const ParseError = std.Io.Reader.Error || std.mem.Allocator.Error || error { InvalidMagicNumber, InvalidTag, + InvalidOp, + InvalidArrayType, }; pub const ResolveError = error { diff --git a/src/Class/AttributeInfo.zig b/src/Class/AttributeInfo.zig index 5fbdc9f..bf1feb5 100644 --- a/src/Class/AttributeInfo.zig +++ b/src/Class/AttributeInfo.zig @@ -319,7 +319,7 @@ pub const AttributeInfo = union(enum) { defer instr_indent -= 1; try w.splatByteAll(' ', depth * instr_indent); - try w.print("{d}: 0x{x}\n", .{ i, instr }); + try w.print("{d}: {t}\n", .{ i, instr }); } try w.splatByteAll(' ', depth * indent); @@ -690,12 +690,14 @@ pub const ConstantValue = struct { pub const Code = struct { max_stack: u16, max_locals: u16, - code: []u8, + code: []Instruction, exception_table: []ExceptionHandler, attributes: []AttributeInfo, const Self = @This(); + pub const Instruction = @import("Code/Instruction.zig").Instruction; + pub const ExceptionHandler = struct { start_pc: u16, end_pc: u16, @@ -736,8 +738,20 @@ pub const Code = struct { const max_locals = try input.takeInt(u16, .big); const code_length = try input.takeInt(u32, .big); - const code = try input.readAlloc(allocator, code_length); - errdefer allocator.free(code); + var code_reader_buf: [1024]u8 = undefined; + var code_reader = input.limited(.limited(code_length), &code_reader_buf); + var code = &code_reader.interface; + var byte_index: usize = 0; + var instructions: std.ArrayList(Instruction) = .empty; + defer instructions.deinit(allocator); + loop: while (true) { + _ = code.peekByte() catch |err| switch (err) { + error.EndOfStream => break :loop, + else => return err, + }; + + try instructions.append(allocator, try .parse(code, allocator, &byte_index)); + } const exception_table_length = try input.takeInt(u16, .big); const exception_table = try allocator.alloc(ExceptionHandler, exception_table_length); @@ -761,13 +775,16 @@ pub const Code = struct { return .{ .max_stack = max_stack, .max_locals = max_locals, - .code = code, + .code = try instructions.toOwnedSlice(allocator), .exception_table = exception_table, .attributes = attributes, }; } pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + for (self.code) |*instruction| { + instruction.deinit(allocator); + } allocator.free(self.code); allocator.free(self.exception_table); for (self.attributes) |*attr| { diff --git a/src/Class/AttributeInfo/Code.zig b/src/Class/AttributeInfo/Code.zig deleted file mode 100644 index f91a8ce..0000000 --- a/src/Class/AttributeInfo/Code.zig +++ /dev/null @@ -1,26 +0,0 @@ -const std = @import("std"); - -const Class = @import("../../Class.zig"); -const AttributeInfo = Class.AttributeInfo; -const ParseError = Class.ParseError; - -const Instructions = @import("Instructions.zig").Instructions; - -max_stack: u16, -max_locals: u16, -code: []Instructions, -exception_table: []ExceptionHandler, -attributes: []AttributeInfo, - -const Self = @This(); - -pub const ExceptionHandler = struct { - start_pc: u16, - end_pc: u16, - handler_pc: u16, - catch_type: u16, -}; - -pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { -} - diff --git a/src/Class/AttributeInfo/Instructions.zig b/src/Class/AttributeInfo/Instructions.zig deleted file mode 100644 index b4ec6b6..0000000 --- a/src/Class/AttributeInfo/Instructions.zig +++ /dev/null @@ -1,582 +0,0 @@ -const std = @import("std"); - -const InstructionSet = enum(u8) { - aaload = 0x32, - aastore = 0x53, - aconst_null = 0x1, - aload = 0x19, - aload_0 = 0x2a, - aload_1 = 0x2b, - aload_2 = 0x2c, - aload_3 = 0x2d, - anewarray = 0xbd, - areturn = 0xb0, - arraylength = 0xbe, - astore = 0x3a, - astore_0 = 0x4b, - astore_1 = 0x4c, - astore_2 = 0x4d, - astore_3 = 0x4e, - athrow = 0xbf, - baload = 0x33, - bastore = 0x54, - bipush = 0x10, - caload = 0x34, - castore = 0x55, - checkcast = 0xc0, - d2f = 0x90, - d2i = 0x8e, - d2l = 0x8f, - dadd = 0x63, - daload = 0x31, - dastore = 0x52, - dcmpg = 0x98, - dcmpl = 0x97, - dconst_0 = 0xe, - dconst_1 = 0xf, - ddiv = 0x6f, - dload = 0x18, - dload_0 = 0x26, - dload_1 = 0x27, - dload_2 = 0x28, - dload_3 = 0x29, - dmul = 0x6b, - dneg = 0x77, - drem = 0x73, - dreturn = 0xaf, - dstore = 0x39, - dstore_0 = 0x47, - dstore_1 = 0x48, - dstore_2 = 0x49, - dstore_3 = 0x4a, - dsub = 0x67, - dup = 0x59, - dup_x1 = 0x5a, - dup_x2 = 0x5b, - dup2 = 0x5c, - dup2_x1 = 0x5d, - dup2_x2 = 0x5e, - f2d = 0x8d, - f2i = 0x8b, - f2l = 0x8c, - fadd = 0x62, - faload = 0x30, - fastore = 0x51, - fcmpg = 0x96, - fcmpl = 0x95, - fconst_0 = 0xb, - fconst_1 = 0xc, - fconst_2 = 0xd, - fdiv = 0x6e, - fload = 0x17, - fload_0 = 0x22, - fload_1 = 0x23, - fload_2 = 0x24, - fload_3 = 0x25, - fmul = 0x6a, - fneg = 0x76, - frem = 0x72, - freturn = 0xae, - fstore = 0x38, - fstore_0 = 0x43, - fstore_1 = 0x44, - fstore_2 = 0x45, - fstore_3 = 0x46, - fsub = 0x66, - getfield = 0xb4, - getstatic = 0xb2, - goto = 0xa7, - goto_w = 0xc8, - i2b = 0x91, - i2c = 0x92, - i2d = 0x87, - i2f = 0x86, - i2l = 0x85, - i2s = 0x93, - iadd = 0x60, - iaload = 0x2e, - iand = 0x7e, - iastore = 0x4f, - iconst_m1 = 0x2, - iconst_0 = 0x3, - iconst_1 = 0x4, - iconst_2 = 0x5, - iconst_3 = 0x6, - iconst_4 = 0x7, - iconst_5 = 0x8, - idiv = 0x6c, - if_acmpeq = 0xa5, - if_acmpne = 0xa6, - if_icmpeq = 0x9f, - if_icmpne = 0xa0, - if_icmplt = 0xa1, - if_icmpge = 0xa2, - if_icmpgt = 0xa3, - if_icmple = 0xa4, - ifeq = 0x99, - ifne = 0x9a, - iflt = 0x9b, - ifge = 0x9c, - ifgt = 0x9d, - ifle = 0x9e, - ifnonnull = 0xc7, - ifnull = 0xc6, - iinc = 0x84, - iload = 0x15, - iload_0 = 0x1a, - iload_1 = 0x1b, - iload_2 = 0x1c, - iload_3 = 0x1d, - imul = 0x68, - ineg = 0x74, - instanceof = 0xc1, - invokedynamic = 0xba, - invokeinterface = 0xb9, - invokespecial = 0xb7, - invokestatic = 0xb8, - invokevirtual = 0xb6, - ior = 0x80, - irem = 0x70, - ireturn = 0xac, - ishl = 0x78, - ishr = 0x7a, - istore = 0x36, - istore_0 = 0x3b, - istore_1 = 0x3c, - istore_2 = 0x3d, - istore_3 = 0x3e, - isub = 0x64, - iushr = 0x7c, - ixor = 0x82, - jsr = 0xa8, - jsr_2 = 0xc9, - l2d = 0x8a, - l2f = 0x89, - l2i = 0x88, - ladd = 0x61, - laload = 0x2f, - land = 0x7f, - lastore = 0x50, - lcmp = 0x94, - lconst_0 = 0x9, - lconst_1 = 0xa, - ldc = 0x12, - ldc_2 = 0x13, - ldc2_w = 0x14, - ldiv = 0x6d, - lload = 0x16, - lload_0 = 0x1e, - lload_1 = 0x1f, - lload_2 = 0x20, - lload_3 = 0x21, - lmul = 0x69, - lneg = 0x75, - lookupswitch = 0xab, - lor = 0x81, - lrem = 0x71, - lreturn = 0xad, - lshl = 0x79, - lshr = 0x7b, - lstore = 0x37, - lstore_0 = 0x3f, - lstore_1 = 0x40, - lstore_2 = 0x41, - lstore_3 = 0x42, - lsub = 0x65, - lushr = 0x7d, - lxor = 0x83, - monitorenter = 0xc2, - monitorexit = 0xc3, - multianewarray = 0xc5, - new = 0xbb, - newarray = 0xbc, - nop = 0x0, - pop = 0x57, - pop2 = 0x58, - putfield = 0xb5, - putstatic = 0xb3, - ret = 0xa9, - @"return" = 0xb1, - saload = 0x35, - sastore = 0x56, - sipush = 0x11, - swap = 0x5f, - tableswitch = 0xaa, - wide = 0xc4, -}; - -const IndexU8 = struct { - index: u8, -}; - -const IndexU16 = struct { - index: u16, - - const Self = @This(); - - pub fn from(b1: u8, b2: u8) Self { - return .{ .index = (b1 << 8) | b2 }; - } -}; - -const IndexU32 = struct { - index: u32, - - const Self = @This(); - - pub fn from(b1: u8, b2: u8, b3: u8, b4: u8) Self { - return .{ .index = (b1 << 24) | (b2 << 16) | (b3 << 8) | b4 }; - } -}; - -const Byte = struct { - byte: u8, -}; - -const BranchU16 = struct { - branch: u16, - - const Self = @This(); - - pub fn from(b1: u8, b2: u8) Self { - return .{ .index = (b1 << 8) | b2 }; - } -}; - -const BranchU32 = struct { - branch: u32, - - const Self = @This(); - - pub fn from(b1: u8, b2: u8, b3: u8, b4: u8) Self { - return .{ .branch = (b1 << 24) | (b2 << 16) | (b3 << 8) | b4 }; - } -}; - -const IInc = struct { - index: u8, - @"const": u8, -}; - -const InvokeDynamic = struct { - index: u16, - - const Self = @This(); - - pub fn from(b1: u8, b2: u8, pad1: u8, pad2: u8) Self { - _ = pad1; - _ = pad2; - return .{ .index = (b1 << 8) | b2 }; - } -}; - -const InvokeInterface = struct { - index: u16, - count: u8, - - const Self = @This(); - - pub fn from(b1: u8, b2: u8, count: u8, pad: u8) Self { - _ = pad; - return .{ .index = (b1 << 8) | b2, .count = count }; - } -}; - -const LookupSwitch = struct { - default: i32, - pairs: []Pair, - - const Pair = struct { - match: u32, - offset: i32, - }; -}; - -const MultiANewArray = struct { - index: u16, - dimensions: u8, - - const Self = @This(); - - pub fn from(b1: u8, b2: u8, dimensions: u8) Self { - return .{ .index = (b1 << 8) | b2, .dimensions = dimensions }; - } -}; - -const NewArray = struct { - array_type: ArrayType, - - const Self = @This(); - - const ArrayType = enum(u8) { - boolean = 4, - char = 5, - float = 6, - double = 7, - byte = 8, - short = 9, - int = 10, - long = 11, - }; - - pub fn from(atype: u8) Self { - const array_type = std.enums.fromInt(ArrayType, atype) orelse { - std.log.err("Invalid array type"); - std.process.exit(1); - }; - return .{ .array_type = array_type }; - } -}; - -const Short = struct { - short: u16, - - const Self = @This(); - - pub fn from(b1: u8, b2: u8) Self { - return .{ .short = (b1 << 8) | b2 }; - } -}; - -const TableSwitch = struct { - default: i32, - low: i32, - hight: i32, - offsets: []i32, -}; - -const Wide = union { - form1: struct { - opcode: ModifiableInstruction, - index: u16, - }, - form2: struct { - // Should always be iinc - opcode: ModifiableInstruction, - index: u16, - @"const": u16, - }, - - const Self = @This(); - - const ModifiableInstruction = enum(u8) { - iload = 0x15, - fload = 0x17, - aload = 0x19, - lload = 0x16, - dload = 0x18, - istore = 0x36, - fstore = 0x38, - astore = 0x3a, - lstore = 0x37, - dstore = 0x39, - ret = 0xa9, - iinc = 0x84, - }; -}; - -const Instructions = union(InstructionSet) { - aaload, - aastore, - aconst_null, - aload: IndexU8, - aload_0, - aload_1, - aload_2, - aload_3, - anewarray: IndexU16, - areturn, - arraylength, - astore: IndexU8, - astore_0, - astore_1, - astore_2, - astore_3, - athrow, - baload, - bastore, - bipush: Byte, - caload, - castore, - checkcast: IndexU16, - d2f, - d2i, - d2l, - dadd, - daload, - dastore, - dcmpg, - dcmpl, - dconst_0, - dconst_1, - ddiv, - dload: IndexU8, - dload_0, - dload_1, - dload_2, - dload_3, - dmul, - dneg, - drem, - dreturn, - dstore: IndexU8, - dstore_0, - dstore_1, - dstore_2, - dstore_3, - dsub, - dup, - dup_x1, - dup_x2, - dup2, - dup2_x1, - dup2_x2, - f2d, - f2i, - f2l, - fadd, - faload, - fastore, - fcmpg, - fcmpl, - fconst_0, - fconst_1, - fconst_2, - fdiv, - fload: IndexU8, - fload_0, - fload_1, - fload_2, - fload_3, - fmul, - fneg, - frem, - freturn, - fstore: IndexU8, - fstore_0, - fstore_1, - fstore_2, - fstore_3, - fsub, - getfield: IndexU16, - getstatic: IndexU16, - goto: IndexU16, - goto_w: IndexU32, - i2b, - i2c, - i2d, - i2f, - i2l, - i2s, - iadd, - iaload, - iand, - iastore, - iconst_m1, - iconst_0, - iconst_1, - iconst_2, - iconst_3, - iconst_4, - iconst_5, - idiv, - if_acmpeq: BranchU16, - if_acmpne: BranchU16, - if_icmpeq: BranchU16, - if_icmpne: BranchU16, - if_icmplt: BranchU16, - if_icmpge: BranchU16, - if_icmpgt: BranchU16, - if_icmple: BranchU16, - ifeq: BranchU16, - ifne: BranchU16, - iflt: BranchU16, - ifge: BranchU16, - ifgt: BranchU16, - ifle: BranchU16, - ifnonnull: BranchU16, - ifnull: BranchU16, - iinc: IInc, - iload: IndexU8, - iload_0, - iload_1, - iload_2, - iload_3, - imul, - ineg, - instanceof: IndexU16, - invokedynamic: InvokeDynamic, - invokeinterface: InvokeInterface, - invokespecial: IndexU16, - invokestatic: IndexU16, - invokevirtual: IndexU16, - ior, - irem, - ireturn, - ishl, - ishr, - istore: IndexU8, - istore_0, - istore_1, - istore_2, - istore_3, - isub, - iushr, - ixor, - jsr: BranchU16, - jsr_w: BranchU32, - l2d, - l2f, - l2i, - ladd, - laload, - land, - lastore, - lcmp, - lconst_0, - lconst_1, - ldc: IndexU8, - ldc_w: IndexU16, - ldc2_w: IndexU16, - ldiv, - lload: IndexU8, - lload_0, - lload_1, - lload_2, - lload_3, - lmul, - lneg, - lookupswitch: LookupSwitch, - lor, - lrem, - lreturn, - lshl, - lshr, - lstore: IndexU8, - lstore_0, - lstore_1, - lstore_2, - lstore_3, - lsub, - lushr, - lxor, - monitorenter, - monitorexit, - mulitanewarray: MultiANewArray, - new: IndexU16, - newarray: NewArray, - nop, - pop, - pop2, - putfield: IndexU16, - putstatic: IndexU16, - ret: IndexU8, - @"return", - saload, - sastore, - sipush: Short, - swap, - tableswitch: TableSwitch, - wide: Wide, -}; - diff --git a/src/Class/Code/Instruction.zig b/src/Class/Code/Instruction.zig new file mode 100644 index 0000000..878f837 --- /dev/null +++ b/src/Class/Code/Instruction.zig @@ -0,0 +1,1072 @@ +const std = @import("std"); + +const Class = @import("../../Class.zig"); +const ParseError = Class.ParseError; + +const Op = enum(u8) { + aaload = 0x32, + aastore = 0x53, + aconst_null = 0x1, + aload = 0x19, + aload_0 = 0x2a, + aload_1 = 0x2b, + aload_2 = 0x2c, + aload_3 = 0x2d, + anewarray = 0xbd, + areturn = 0xb0, + arraylength = 0xbe, + astore = 0x3a, + astore_0 = 0x4b, + astore_1 = 0x4c, + astore_2 = 0x4d, + astore_3 = 0x4e, + athrow = 0xbf, + baload = 0x33, + bastore = 0x54, + bipush = 0x10, + caload = 0x34, + castore = 0x55, + checkcast = 0xc0, + d2f = 0x90, + d2i = 0x8e, + d2l = 0x8f, + dadd = 0x63, + daload = 0x31, + dastore = 0x52, + dcmpg = 0x98, + dcmpl = 0x97, + dconst_0 = 0xe, + dconst_1 = 0xf, + ddiv = 0x6f, + dload = 0x18, + dload_0 = 0x26, + dload_1 = 0x27, + dload_2 = 0x28, + dload_3 = 0x29, + dmul = 0x6b, + dneg = 0x77, + drem = 0x73, + dreturn = 0xaf, + dstore = 0x39, + dstore_0 = 0x47, + dstore_1 = 0x48, + dstore_2 = 0x49, + dstore_3 = 0x4a, + dsub = 0x67, + dup = 0x59, + dup_x1 = 0x5a, + dup_x2 = 0x5b, + dup2 = 0x5c, + dup2_x1 = 0x5d, + dup2_x2 = 0x5e, + f2d = 0x8d, + f2i = 0x8b, + f2l = 0x8c, + fadd = 0x62, + faload = 0x30, + fastore = 0x51, + fcmpg = 0x96, + fcmpl = 0x95, + fconst_0 = 0xb, + fconst_1 = 0xc, + fconst_2 = 0xd, + fdiv = 0x6e, + fload = 0x17, + fload_0 = 0x22, + fload_1 = 0x23, + fload_2 = 0x24, + fload_3 = 0x25, + fmul = 0x6a, + fneg = 0x76, + frem = 0x72, + freturn = 0xae, + fstore = 0x38, + fstore_0 = 0x43, + fstore_1 = 0x44, + fstore_2 = 0x45, + fstore_3 = 0x46, + fsub = 0x66, + getfield = 0xb4, + getstatic = 0xb2, + goto = 0xa7, + goto_w = 0xc8, + i2b = 0x91, + i2c = 0x92, + i2d = 0x87, + i2f = 0x86, + i2l = 0x85, + i2s = 0x93, + iadd = 0x60, + iaload = 0x2e, + iand = 0x7e, + iastore = 0x4f, + iconst_m1 = 0x2, + iconst_0 = 0x3, + iconst_1 = 0x4, + iconst_2 = 0x5, + iconst_3 = 0x6, + iconst_4 = 0x7, + iconst_5 = 0x8, + idiv = 0x6c, + if_acmpeq = 0xa5, + if_acmpne = 0xa6, + if_icmpeq = 0x9f, + if_icmpne = 0xa0, + if_icmplt = 0xa1, + if_icmpge = 0xa2, + if_icmpgt = 0xa3, + if_icmple = 0xa4, + ifeq = 0x99, + ifne = 0x9a, + iflt = 0x9b, + ifge = 0x9c, + ifgt = 0x9d, + ifle = 0x9e, + ifnonnull = 0xc7, + ifnull = 0xc6, + iinc = 0x84, + iload = 0x15, + iload_0 = 0x1a, + iload_1 = 0x1b, + iload_2 = 0x1c, + iload_3 = 0x1d, + imul = 0x68, + ineg = 0x74, + instanceof = 0xc1, + invokedynamic = 0xba, + invokeinterface = 0xb9, + invokespecial = 0xb7, + invokestatic = 0xb8, + invokevirtual = 0xb6, + ior = 0x80, + irem = 0x70, + ireturn = 0xac, + ishl = 0x78, + ishr = 0x7a, + istore = 0x36, + istore_0 = 0x3b, + istore_1 = 0x3c, + istore_2 = 0x3d, + istore_3 = 0x3e, + isub = 0x64, + iushr = 0x7c, + ixor = 0x82, + jsr = 0xa8, + jsr_w = 0xc9, + l2d = 0x8a, + l2f = 0x89, + l2i = 0x88, + ladd = 0x61, + laload = 0x2f, + land = 0x7f, + lastore = 0x50, + lcmp = 0x94, + lconst_0 = 0x9, + lconst_1 = 0xa, + ldc = 0x12, + ldc_w = 0x13, + ldc2_w = 0x14, + ldiv = 0x6d, + lload = 0x16, + lload_0 = 0x1e, + lload_1 = 0x1f, + lload_2 = 0x20, + lload_3 = 0x21, + lmul = 0x69, + lneg = 0x75, + lookupswitch = 0xab, + lor = 0x81, + lrem = 0x71, + lreturn = 0xad, + lshl = 0x79, + lshr = 0x7b, + lstore = 0x37, + lstore_0 = 0x3f, + lstore_1 = 0x40, + lstore_2 = 0x41, + lstore_3 = 0x42, + lsub = 0x65, + lushr = 0x7d, + lxor = 0x83, + monitorenter = 0xc2, + monitorexit = 0xc3, + multianewarray = 0xc5, + new = 0xbb, + newarray = 0xbc, + nop = 0x0, + pop = 0x57, + pop2 = 0x58, + putfield = 0xb5, + putstatic = 0xb3, + ret = 0xa9, + @"return" = 0xb1, + saload = 0x35, + sastore = 0x56, + sipush = 0x11, + swap = 0x5f, + tableswitch = 0xaa, + wide = 0xc4, +}; + +const IndexU8 = struct { + index: u8, + + const Self = @This(); + + pub fn parse(input: *std.Io.Reader, byte_index: *usize) ParseError!Self { + defer byte_index.* += 1; + + return .{ + .index = try input.takeByte(), + }; + } +}; + +const IndexU16 = struct { + index: u16, + + const Self = @This(); + + pub fn parse(input: *std.Io.Reader, byte_index: *usize) ParseError!Self { + defer byte_index.* += 2; + + return .{ + .index = try input.takeInt(u16, .big), + }; + } +}; + +const IndexU32 = struct { + index: u32, + + const Self = @This(); + + pub fn parse(input: *std.Io.Reader, byte_index: *usize) ParseError!Self { + defer byte_index.* += 4; + + return .{ + .index = try input.takeInt(u32, .big), + }; + } +}; + +const Byte = struct { + byte: u8, + + const Self = @This(); + + pub fn parse(input: *std.Io.Reader, byte_index: *usize) ParseError!Self { + defer byte_index.* += 1; + + return .{ + .byte = try input.takeByte(), + }; + } +}; + +const BranchU16 = struct { + branch: u16, + + const Self = @This(); + + pub fn parse(input: *std.Io.Reader, byte_index: *usize) ParseError!Self { + defer byte_index.* += 2; + + return .{ + .branch = try input.takeInt(u16, .big), + }; + } +}; + +const BranchU32 = struct { + branch: u32, + + const Self = @This(); + + pub fn parse(input: *std.Io.Reader, byte_index: *usize) ParseError!Self { + defer byte_index.* += 4; + + return .{ + .branch = try input.takeInt(u32, .big), + }; + } +}; + +const IInc = struct { + index: u8, + @"const": u8, + + const Self = @This(); + + pub fn parse(input: *std.Io.Reader, byte_index: *usize) ParseError!Self { + defer byte_index.* += 2; + + return .{ + .index = try input.takeByte(), + .@"const" = try input.takeByte(), + }; + } +}; + +const InvokeDynamic = struct { + index: u16, + + const Self = @This(); + + pub fn parse(input: *std.Io.Reader, byte_index: *usize) ParseError!Self { + const index = try input.takeInt(u16, .big); + try input.discardAll(2); + + defer byte_index.* += 4; + + return .{ + .index = index, + }; + } +}; + +const InvokeInterface = struct { + index: u16, + count: u8, + + const Self = @This(); + + pub fn parse(input: *std.Io.Reader, byte_index: *usize) ParseError!Self { + const index = try input.takeInt(u16, .big); + const count = try input.takeByte(); + try input.discardAll(1); + + defer byte_index.* += 4; + + return .{ + .index = index, + .count = count, + }; + } +}; + +const LookupSwitch = struct { + default: i32, + pairs: []Pair, + + const Pair = struct { + match: i32, + offset: i32, + }; + + const Self = @This(); + + pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator, byte_index: *usize) ParseError!Self { + const padded_byte_index = std.mem.alignForward(usize, byte_index.*, 4); + const offset = padded_byte_index - byte_index.*; + try input.discardAll(offset); + const default = try input.takeInt(i32, .big); + const pairs_length: usize = @intCast(try input.takeInt(i32, .big)); + const pairs = try allocator.alloc(Pair, pairs_length); + errdefer allocator.free(pairs); + for (pairs) |*pair| { + pair.* = .{ + .match = try input.takeInt(i32, .big), + .offset = try input.takeInt(i32, .big), + }; + } + + defer byte_index.* += offset + 4 + 4 + ((4 + 4) * pairs_length); + + return .{ + .default = default, + .pairs = pairs, + }; + } + + pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + allocator.free(self.pairs); + } +}; + +const MultiANewArray = struct { + index: u16, + dimensions: u8, + + const Self = @This(); + + pub fn parse(input: *std.Io.Reader, byte_index: *usize) ParseError!Self { + defer byte_index.* += 3; + + return .{ + .index = try input.takeInt(u16, .big), + .dimensions = try input.takeByte(), + }; + } +}; + +const NewArray = struct { + array_type: ArrayType, + + const ArrayType = enum(u8) { + boolean = 4, + char = 5, + float = 6, + double = 7, + byte = 8, + short = 9, + int = 10, + long = 11, + }; + + const Self = @This(); + + pub fn parse(input: *std.Io.Reader, byte_index: *usize) ParseError!Self { + const b = try input.takeByte(); + const array_type = std.enums.fromInt(ArrayType, b) orelse return ParseError.InvalidArrayType; + + defer byte_index.* += 1; + + return .{ + .array_type = array_type, + }; + } +}; + +const Short = struct { + short: u16, + + const Self = @This(); + + pub fn parse(input: *std.Io.Reader, byte_index: *usize) ParseError!Self { + defer byte_index.* += 2; + + return .{ + .short = try input.takeInt(u16, .big), + }; + } +}; + +const TableSwitch = struct { + default: i32, + low: i32, + high: i32, + offsets: []i32, + + const Self = @This(); + + pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator, byte_index: *usize) ParseError!Self { + const padded_byte_index = std.mem.alignForward(usize, byte_index.*, 4); + const offset = padded_byte_index - byte_index.*; + try input.discardAll(offset); + const default = try input.takeInt(i32, .big); + const low = try input.takeInt(i32, .big); + const high = try input.takeInt(i32, .big); + const offsets_length: usize = @intCast(high - low + 1); + const offsets = try allocator.alloc(i32, offsets_length); + errdefer allocator.free(offsets); + for (offsets) |*o| { + o.* = try input.takeInt(i32, .big); + } + + defer byte_index.* += offset + 4 + 4 + 4 + (4 * offsets_length); + + return .{ + .default = default, + .low = low, + .high = high, + .offsets = offsets, + }; + } + + pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + allocator.free(self.offsets); + } +}; + +const Wide = union { + form1: struct { + opcode: ModifiableOp, + index: u16, + }, + form2: struct { + opcode: ModifiableOp, // Should always be iinc + index: u16, + @"const": u16, + }, + + const ModifiableOp = enum(u8) { + iload = 0x15, + fload = 0x17, + aload = 0x19, + lload = 0x16, + dload = 0x18, + istore = 0x36, + fstore = 0x38, + astore = 0x3a, + lstore = 0x37, + dstore = 0x39, + ret = 0xa9, + iinc = 0x84, + }; + + const Self = @This(); + + pub fn parse(input: *std.Io.Reader, byte_index: *usize) ParseError!Self { + const op_byte = try input.takeByte(); + const op = std.enums.fromInt(ModifiableOp, op_byte) orelse return ParseError.InvalidOp; + if (op == .iinc) { + defer byte_index.* += 5; + + return .{ + .form2 = .{ + .opcode = op, + .index = try input.takeInt(u16, .big), + .@"const" = try input.takeInt(u16, .big), + }, + }; + } else { + defer byte_index.* += 3; + + return .{ + .form1 = .{ + .opcode = op, + .index = try input.takeInt(u16, .big), + }, + }; + } + } +}; + +pub const Instruction = union(Op) { + aaload, + aastore, + aconst_null, + aload: IndexU8, + aload_0, + aload_1, + aload_2, + aload_3, + anewarray: IndexU16, + areturn, + arraylength, + astore: IndexU8, + astore_0, + astore_1, + astore_2, + astore_3, + athrow, + baload, + bastore, + bipush: Byte, + caload, + castore, + checkcast: IndexU16, + d2f, + d2i, + d2l, + dadd, + daload, + dastore, + dcmpg, + dcmpl, + dconst_0, + dconst_1, + ddiv, + dload: IndexU8, + dload_0, + dload_1, + dload_2, + dload_3, + dmul, + dneg, + drem, + dreturn, + dstore: IndexU8, + dstore_0, + dstore_1, + dstore_2, + dstore_3, + dsub, + dup, + dup_x1, + dup_x2, + dup2, + dup2_x1, + dup2_x2, + f2d, + f2i, + f2l, + fadd, + faload, + fastore, + fcmpg, + fcmpl, + fconst_0, + fconst_1, + fconst_2, + fdiv, + fload: IndexU8, + fload_0, + fload_1, + fload_2, + fload_3, + fmul, + fneg, + frem, + freturn, + fstore: IndexU8, + fstore_0, + fstore_1, + fstore_2, + fstore_3, + fsub, + getfield: IndexU16, + getstatic: IndexU16, + goto: IndexU16, + goto_w: IndexU32, + i2b, + i2c, + i2d, + i2f, + i2l, + i2s, + iadd, + iaload, + iand, + iastore, + iconst_m1, + iconst_0, + iconst_1, + iconst_2, + iconst_3, + iconst_4, + iconst_5, + idiv, + if_acmpeq: BranchU16, + if_acmpne: BranchU16, + if_icmpeq: BranchU16, + if_icmpne: BranchU16, + if_icmplt: BranchU16, + if_icmpge: BranchU16, + if_icmpgt: BranchU16, + if_icmple: BranchU16, + ifeq: BranchU16, + ifne: BranchU16, + iflt: BranchU16, + ifge: BranchU16, + ifgt: BranchU16, + ifle: BranchU16, + ifnonnull: BranchU16, + ifnull: BranchU16, + iinc: IInc, + iload: IndexU8, + iload_0, + iload_1, + iload_2, + iload_3, + imul, + ineg, + instanceof: IndexU16, + invokedynamic: InvokeDynamic, + invokeinterface: InvokeInterface, + invokespecial: IndexU16, + invokestatic: IndexU16, + invokevirtual: IndexU16, + ior, + irem, + ireturn, + ishl, + ishr, + istore: IndexU8, + istore_0, + istore_1, + istore_2, + istore_3, + isub, + iushr, + ixor, + jsr: BranchU16, + jsr_w: BranchU32, + l2d, + l2f, + l2i, + ladd, + laload, + land, + lastore, + lcmp, + lconst_0, + lconst_1, + ldc: IndexU8, + ldc_w: IndexU16, + ldc2_w: IndexU16, + ldiv, + lload: IndexU8, + lload_0, + lload_1, + lload_2, + lload_3, + lmul, + lneg, + lookupswitch: LookupSwitch, + lor, + lrem, + lreturn, + lshl, + lshr, + lstore: IndexU8, + lstore_0, + lstore_1, + lstore_2, + lstore_3, + lsub, + lushr, + lxor, + monitorenter, + monitorexit, + multianewarray: MultiANewArray, + new: IndexU16, + newarray: NewArray, + nop, + pop, + pop2, + putfield: IndexU16, + putstatic: IndexU16, + ret: IndexU8, + @"return", + saload, + sastore, + sipush: Short, + swap, + tableswitch: TableSwitch, + wide: Wide, + + const Self = @This(); + + pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator, byte_index: *usize) ParseError!Self { + const op_byte = try input.takeByte(); + const op = std.enums.fromInt(Op, op_byte) orelse return ParseError.InvalidOp; + + byte_index.* += 1; + + return switch (op) { + .aaload => .aaload, + .aastore => .aastore, + .aconst_null => .aconst_null, + .aload => .{ + .aload = try .parse(input, byte_index), + }, + .aload_0 => .aload_0, + .aload_1 => .aload_1, + .aload_2 => .aload_2, + .aload_3 => .aload_3, + .anewarray => .{ + .anewarray = try .parse(input, byte_index), + }, + .areturn => .areturn, + .arraylength => .arraylength, + .astore => .{ + .astore = try .parse(input, byte_index), + }, + .astore_0 => .astore_0, + .astore_1 => .astore_1, + .astore_2 => .astore_2, + .astore_3 => .astore_3, + .athrow => .athrow, + .baload => .baload, + .bastore => .bastore, + .bipush => .{ + .bipush = try .parse(input, byte_index), + }, + .caload => .caload, + .castore => .castore, + .checkcast => .{ + .checkcast = try .parse(input, byte_index), + }, + .d2f => .d2f, + .d2i => .d2i, + .d2l => .d2l, + .dadd => .dadd, + .daload => .daload, + .dastore => .dastore, + .dcmpg => .dcmpg, + .dcmpl => .dcmpl, + .dconst_0 => .dconst_0, + .dconst_1 => .dconst_1, + .ddiv => .ddiv, + .dload => .{ + .dload = try .parse(input, byte_index), + }, + .dload_0 => .dload_0, + .dload_1 => .dload_1, + .dload_2 => .dload_2, + .dload_3 => .dload_3, + .dmul => .dmul, + .dneg => .dneg, + .drem => .drem, + .dreturn => .dreturn, + .dstore => .{ + .dstore = try .parse(input, byte_index), + }, + .dstore_0 => .dstore_0, + .dstore_1 => .dstore_1, + .dstore_2 => .dstore_2, + .dstore_3 => .dstore_3, + .dsub => .dsub, + .dup => .dup, + .dup_x1 => .dup_x1, + .dup_x2 => .dup_x2, + .dup2 => .dup2, + .dup2_x1 => .dup2_x1, + .dup2_x2 => .dup2_x2, + .f2d => .f2d, + .f2i => .f2i, + .f2l => .f2l, + .fadd => .fadd, + .faload => .faload, + .fastore => .fastore, + .fcmpg => .fcmpg, + .fcmpl => .fcmpl, + .fconst_0 => .fconst_0, + .fconst_1 => .fconst_1, + .fconst_2 => .fconst_2, + .fdiv => .fdiv, + .fload => .{ + .fload = try .parse(input, byte_index), + }, + .fload_0 => .fload_0, + .fload_1 => .fload_1, + .fload_2 => .fload_2, + .fload_3 => .fload_3, + .fmul => .fmul, + .fneg => .fneg, + .frem => .frem, + .freturn => .freturn, + .fstore => .{ + .fstore = try .parse(input, byte_index), + }, + .fstore_0 => .fstore_0, + .fstore_1 => .fstore_1, + .fstore_2 => .fstore_2, + .fstore_3 => .fstore_3, + .fsub => .fsub, + .getfield => .{ + .getfield = try .parse(input, byte_index), + }, + .getstatic => .{ + .getstatic = try .parse(input, byte_index), + }, + .goto => .{ + .goto = try .parse(input, byte_index), + }, + .goto_w => .{ + .goto_w = try .parse(input, byte_index), + }, + .i2b => .i2b, + .i2c => .i2c, + .i2d => .i2d, + .i2f => .i2f, + .i2l => .i2l, + .i2s => .i2s, + .iadd => .iadd, + .iaload => .iaload, + .iand => .iand, + .iastore => .iastore, + .iconst_m1 => .iconst_m1, + .iconst_0 => .iconst_0, + .iconst_1 => .iconst_1, + .iconst_2 => .iconst_2, + .iconst_3 => .iconst_3, + .iconst_4 => .iconst_4, + .iconst_5 => .iconst_5, + .idiv => .idiv, + .if_acmpeq => .{ + .if_acmpeq = try .parse(input, byte_index), + }, + .if_acmpne => .{ + .if_acmpne = try .parse(input, byte_index), + }, + .if_icmpeq => .{ + .if_icmpeq = try .parse(input, byte_index), + }, + .if_icmpne => .{ + .if_icmpne = try .parse(input, byte_index), + }, + .if_icmplt => .{ + .if_icmplt = try .parse(input, byte_index), + }, + .if_icmpge => .{ + .if_icmpge = try .parse(input, byte_index), + }, + .if_icmpgt => .{ + .if_icmpgt = try .parse(input, byte_index), + }, + .if_icmple => .{ + .if_icmple = try .parse(input, byte_index), + }, + .ifeq => .{ + .ifeq = try .parse(input, byte_index), + }, + .ifne => .{ + .ifne = try .parse(input, byte_index), + }, + .iflt => .{ + .iflt = try .parse(input, byte_index), + }, + .ifge => .{ + .ifge = try .parse(input, byte_index), + }, + .ifgt => .{ + .ifgt = try .parse(input, byte_index), + }, + .ifle => .{ + .ifle = try .parse(input, byte_index), + }, + .ifnonnull => .{ + .ifnonnull = try .parse(input, byte_index), + }, + .ifnull => .{ + .ifnull = try .parse(input, byte_index), + }, + .iinc => .{ + .iinc = try .parse(input, byte_index), + }, + .iload => .{ + .iload = try .parse(input, byte_index), + }, + .iload_0 => .iload_0, + .iload_1 => .iload_1, + .iload_2 => .iload_2, + .iload_3 => .iload_3, + .imul => .imul, + .ineg => .ineg, + .instanceof => .{ + .instanceof = try .parse(input, byte_index), + }, + .invokedynamic => .{ + .invokedynamic = try .parse(input, byte_index), + }, + .invokeinterface => .{ + .invokeinterface = try .parse(input, byte_index), + }, + .invokespecial => .{ + .invokespecial = try .parse(input, byte_index), + }, + .invokestatic => .{ + .invokestatic = try .parse(input, byte_index), + }, + .invokevirtual => .{ + .invokevirtual = try .parse(input, byte_index), + }, + .ior => .ior, + .irem => .irem, + .ireturn => .ireturn, + .ishl => .ishl, + .ishr => .ishr, + .istore => .{ + .istore = try .parse(input, byte_index), + }, + .istore_0 => .istore_0, + .istore_1 => .istore_1, + .istore_2 => .istore_2, + .istore_3 => .istore_3, + .isub => .isub, + .iushr => .iushr, + .ixor => .ixor, + .jsr => .{ + .jsr = try .parse(input, byte_index), + }, + .jsr_w => .{ + .jsr_w = try .parse(input, byte_index), + }, + .l2d => .l2d, + .l2f => .l2f, + .l2i => .l2i, + .ladd => .ladd, + .laload => .laload, + .land => .land, + .lastore => .lastore, + .lcmp => .lcmp, + .lconst_0 => .lconst_0, + .lconst_1 => .lconst_1, + .ldc => .{ + .ldc = try .parse(input, byte_index), + }, + .ldc_w => .{ + .ldc_w = try .parse(input, byte_index), + }, + .ldc2_w => .{ + .ldc2_w = try .parse(input, byte_index), + }, + .ldiv => .ldiv, + .lload => .{ + .lload = try .parse(input, byte_index), + }, + .lload_0 => .lload_0, + .lload_1 => .lload_1, + .lload_2 => .lload_2, + .lload_3 => .lload_3, + .lmul => .lmul, + .lneg => .lneg, + .lookupswitch => .{ + .lookupswitch = try .parse(input, allocator, byte_index), + }, + .lor => .lor, + .lrem => .lrem, + .lreturn => .lreturn, + .lshl => .lshl, + .lshr => .lshr, + .lstore => .{ + .lstore = try .parse(input, byte_index), + }, + .lstore_0 => .lstore_0, + .lstore_1 => .lstore_1, + .lstore_2 => .lstore_2, + .lstore_3 => .lstore_3, + .lsub => .lsub, + .lushr => .lushr, + .lxor => .lxor, + .monitorenter => .monitorenter, + .monitorexit => .monitorexit, + .multianewarray => .{ + .multianewarray = try .parse(input, byte_index), + }, + .new => .{ + .new = try .parse(input, byte_index), + }, + .newarray => .{ + .newarray = try .parse(input, byte_index), + }, + .nop => .nop, + .pop => .pop, + .pop2 => .pop2, + .putfield => .{ + .putfield = try .parse(input, byte_index), + }, + .putstatic => .{ + .putstatic = try .parse(input, byte_index), + }, + .ret => .{ + .ret = try .parse(input, byte_index), + }, + .@"return" => .@"return", + .saload => .saload, + .sastore => .sastore, + .sipush => .{ + .sipush = try .parse(input, byte_index), + }, + .swap => .swap, + .tableswitch => .{ + .tableswitch = try .parse(input, allocator, byte_index), + }, + .wide => .{ + .wide = try .parse(input, byte_index), + }, + }; + } + + pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + switch (self.*) { + .lookupswitch => |*lookupswitch| lookupswitch.deinit(allocator), + .tableswitch => |*tableswitch| tableswitch.deinit(allocator), + else => {}, + } + } +}; + diff --git a/testsuite/classes/java/Main.java b/testsuite/classes/java/Main.java index 0f33998..9b9cfa6 100644 --- a/testsuite/classes/java/Main.java +++ b/testsuite/classes/java/Main.java @@ -17,6 +17,52 @@ public class Main implements Runnable, Supplier, Interface { } finally { System.err.println("Finally block"); } + + var a = 8; + a <<= 3; + final var b = a >> 1; + switch (b) { + case 1: + break; + case 32: + System.out.println("Good"); + break; + default: + System.out.println("Number " + a); + break; + } + + final var o = getObject(); + switch (o) { + case Integer i: + switch (i) { + case 1: + System.out.println("One"); + break; + case 2: + System.out.println("Two"); + break; + case 3: + System.out.println("Three"); + break; + default: + break; + } + break; + default: + System.out.println("Something else"); + break; + } + + inc(5); + } + + private static Object getObject() { + return 3; + } + + private static void inc(int i) { + i += 1000; } @Override diff --git a/testsuite/classes/java/Object.class b/testsuite/classes/java/Object.class deleted file mode 100644 index d33531f70deffc2ee62a65b89a7a44e7773be646..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2412 zcmX^0Z`VEs1_oD#Vs-{5Mh5Y$#Ii*FoW#6z{os101ty8gAgMFKbkxv1ABUEiC@9#URHZ&(5I0!=T8Z#K<6saHMBmNosm(5hDY4NxnyF1;k)R22l-9O#8rYRpDV! z1$mUCBp;#}WUV?6g9d{pBLho$VrB^=1B-^2W*9qzHY0-)l1(`|sp*M1jz#IExv6<2 zt`*6t1tppJc^nM7j0}<`nYpR?r6mewi8-aI3Yo>LGnnu&m@=55L}5rqQEFleBLi=8X;D#XUI~nk8W9jtYjy?;l=KspSyWP* zm;;dodD4o9!5ZXAR*)wR*cogY8RU?>msgTnR8(3}l9~ed8W)2(gFO#}1A`+YgGhR6 ziDO=hb53ev5h&2XYQeD+#lhgr$RH050pHA=oXld(aA0QOce3YTaARap%}dP7FHTL) z&r4B&YJw^U1z>)fLQ!H~dMZ1E2O|R?!hJsZdFib6{~>^pfd!H{_k4| z*5sW0JdAt@)yBxc1&_C4c7_;61{H*7ok1G?@=JnC3kvd!kfWH9fh{k;Br~m&k%1G! zaLmbJWME8XWKcj#tne5|N*&gW4D4kk&?G8}q!=aTSu-;5f`T`{v;>kg7#SG77#UbK zygW6-7#Uc>iGh)U(UXyZBP}y8F(I{BlrqGBR+vq!tvVCMTAp zrZ6&yAz6wLw`OEuEzZnKPGw|Zv}9!9O3N<_O3lqLOUz+rU|?WmWZ(+UFD*(=b;|^$ z7j8)S>49uxP-Ng>fB;?wMo?+Xz{KFqz`&pl(g?yE85kIt7*OT8GF z7?>Cs7`U~xmoc!jEN5V0UcH-5DzNop5M@y785p=3!WjG+7#YGD7#LU?m^d96 z8Ce)27$O;%7@`;$7+4q>8KTi_Rb>!@+Um!^1on-}ZU)B4?F`IX+ZfolG4O6<5Ec^O z#vrVzy=L& z69#6m8`!isw=t+8!ki1_QO0-%R>lM*o7f;W!Tn|fHcJy~)>#G?237_J25WiFDRUTj zcQa^33h8WT(A&R*!O)6DlI0+S`Ai0BNtOc)=6+i8+ZZgDGYDC+=x$@M3zuZs4;IAUB*TRBgQ5MQ^r;X zE5>%PL$w%K7<3sp7~&Zc7+4sT8H5-T8Il-4$cm@V*hGd2mXsk&vun7ETkYZqF zXW(FAXE0}AWM{DX#lXpe@Fb%Us5EC_M2LXBoeK5#0tOZa76t|eDPx# From 76699b87e6c8d532b7aa7be034c45dcc2755d8f7 Mon Sep 17 00:00:00 2001 From: ktkk Date: Sun, 6 Sep 2026 17:14:54 +0000 Subject: [PATCH 5/6] Dedupe Index & Branch --- src/Class/Code/Instruction.zig | 181 ++++++++++++++------------------- 1 file changed, 79 insertions(+), 102 deletions(-) diff --git a/src/Class/Code/Instruction.zig b/src/Class/Code/Instruction.zig index 878f837..d1af3d7 100644 --- a/src/Class/Code/Instruction.zig +++ b/src/Class/Code/Instruction.zig @@ -1,6 +1,7 @@ const std = @import("std"); const Class = @import("../../Class.zig"); +const ConstantPool = Class.ConstantPool; const ParseError = Class.ParseError; const Op = enum(u8) { @@ -208,47 +209,29 @@ const Op = enum(u8) { wide = 0xc4, }; -const IndexU8 = struct { - index: u8, - - const Self = @This(); - - pub fn parse(input: *std.Io.Reader, byte_index: *usize) ParseError!Self { - defer byte_index.* += 1; - - return .{ - .index = try input.takeByte(), - }; +fn Index(comptime T: type) type { + const bits = @typeInfo(T).int.bits; + if (bits % 8 != 0) { + @compileError("Bits should be multiple of 8"); } -}; + const bytes = bits / 8; -const IndexU16 = struct { - index: u16, + return struct { + index: T, - const Self = @This(); + const Self = @This(); - pub fn parse(input: *std.Io.Reader, byte_index: *usize) ParseError!Self { - defer byte_index.* += 2; + pub fn parse(input: *std.Io.Reader, byte_index: *usize) ParseError!Self { + defer byte_index.* += bytes; - return .{ - .index = try input.takeInt(u16, .big), - }; - } -}; + const index = try if (bytes == 1) input.takeByte() else input.takeInt(T, .big); -const IndexU32 = struct { - index: u32, - - const Self = @This(); - - pub fn parse(input: *std.Io.Reader, byte_index: *usize) ParseError!Self { - defer byte_index.* += 4; - - return .{ - .index = try input.takeInt(u32, .big), - }; - } -}; + return .{ + .index = index, + }; + } + }; +} const Byte = struct { byte: u8, @@ -264,33 +247,27 @@ const Byte = struct { } }; -const BranchU16 = struct { - branch: u16, - - const Self = @This(); - - pub fn parse(input: *std.Io.Reader, byte_index: *usize) ParseError!Self { - defer byte_index.* += 2; - - return .{ - .branch = try input.takeInt(u16, .big), - }; +fn Branch(comptime T: type) type { + const bits = @typeInfo(T).int.bits; + if (bits % 8 != 0) { + @compileError("Bits should be multiple of 8"); } -}; + const bytes = bits / 8; -const BranchU32 = struct { - branch: u32, + return struct { + branch: T, - const Self = @This(); + const Self = @This(); - pub fn parse(input: *std.Io.Reader, byte_index: *usize) ParseError!Self { - defer byte_index.* += 4; + pub fn parse(input: *std.Io.Reader, byte_index: *usize) ParseError!Self { + defer byte_index.* += bytes; - return .{ - .branch = try input.takeInt(u32, .big), - }; - } -}; + return .{ + .branch = try input.takeInt(u16, .big), + }; + } + }; +} const IInc = struct { index: u8, @@ -537,15 +514,15 @@ pub const Instruction = union(Op) { aaload, aastore, aconst_null, - aload: IndexU8, + aload: Index(u8), aload_0, aload_1, aload_2, aload_3, - anewarray: IndexU16, + anewarray: Index(u16), areturn, arraylength, - astore: IndexU8, + astore: Index(u8), astore_0, astore_1, astore_2, @@ -556,7 +533,7 @@ pub const Instruction = union(Op) { bipush: Byte, caload, castore, - checkcast: IndexU16, + checkcast: Index(u16), d2f, d2i, d2l, @@ -568,7 +545,7 @@ pub const Instruction = union(Op) { dconst_0, dconst_1, ddiv, - dload: IndexU8, + dload: Index(u8), dload_0, dload_1, dload_2, @@ -577,7 +554,7 @@ pub const Instruction = union(Op) { dneg, drem, dreturn, - dstore: IndexU8, + dstore: Index(u8), dstore_0, dstore_1, dstore_2, @@ -601,7 +578,7 @@ pub const Instruction = union(Op) { fconst_1, fconst_2, fdiv, - fload: IndexU8, + fload: Index(u8), fload_0, fload_1, fload_2, @@ -610,16 +587,16 @@ pub const Instruction = union(Op) { fneg, frem, freturn, - fstore: IndexU8, + fstore: Index(u8), fstore_0, fstore_1, fstore_2, fstore_3, fsub, - getfield: IndexU16, - getstatic: IndexU16, - goto: IndexU16, - goto_w: IndexU32, + getfield: Index(u16), + getstatic: Index(u16), + goto: Index(u16), + goto_w: Index(u32), i2b, i2c, i2d, @@ -638,42 +615,42 @@ pub const Instruction = union(Op) { iconst_4, iconst_5, idiv, - if_acmpeq: BranchU16, - if_acmpne: BranchU16, - if_icmpeq: BranchU16, - if_icmpne: BranchU16, - if_icmplt: BranchU16, - if_icmpge: BranchU16, - if_icmpgt: BranchU16, - if_icmple: BranchU16, - ifeq: BranchU16, - ifne: BranchU16, - iflt: BranchU16, - ifge: BranchU16, - ifgt: BranchU16, - ifle: BranchU16, - ifnonnull: BranchU16, - ifnull: BranchU16, + if_acmpeq: Branch(u16), + if_acmpne: Branch(u16), + if_icmpeq: Branch(u16), + if_icmpne: Branch(u16), + if_icmplt: Branch(u16), + if_icmpge: Branch(u16), + if_icmpgt: Branch(u16), + if_icmple: Branch(u16), + ifeq: Branch(u16), + ifne: Branch(u16), + iflt: Branch(u16), + ifge: Branch(u16), + ifgt: Branch(u16), + ifle: Branch(u16), + ifnonnull: Branch(u16), + ifnull: Branch(u16), iinc: IInc, - iload: IndexU8, + iload: Index(u8), iload_0, iload_1, iload_2, iload_3, imul, ineg, - instanceof: IndexU16, + instanceof: Index(u16), invokedynamic: InvokeDynamic, invokeinterface: InvokeInterface, - invokespecial: IndexU16, - invokestatic: IndexU16, - invokevirtual: IndexU16, + invokespecial: Index(u16), + invokestatic: Index(u16), + invokevirtual: Index(u16), ior, irem, ireturn, ishl, ishr, - istore: IndexU8, + istore: Index(u8), istore_0, istore_1, istore_2, @@ -681,8 +658,8 @@ pub const Instruction = union(Op) { isub, iushr, ixor, - jsr: BranchU16, - jsr_w: BranchU32, + jsr: Branch(u16), + jsr_w: Branch(u32), l2d, l2f, l2i, @@ -693,11 +670,11 @@ pub const Instruction = union(Op) { lcmp, lconst_0, lconst_1, - ldc: IndexU8, - ldc_w: IndexU16, - ldc2_w: IndexU16, + ldc: Index(u8), + ldc_w: Index(u16), + ldc2_w: Index(u16), ldiv, - lload: IndexU8, + lload: Index(u8), lload_0, lload_1, lload_2, @@ -710,7 +687,7 @@ pub const Instruction = union(Op) { lreturn, lshl, lshr, - lstore: IndexU8, + lstore: Index(u8), lstore_0, lstore_1, lstore_2, @@ -721,14 +698,14 @@ pub const Instruction = union(Op) { monitorenter, monitorexit, multianewarray: MultiANewArray, - new: IndexU16, + new: Index(u16), newarray: NewArray, nop, pop, pop2, - putfield: IndexU16, - putstatic: IndexU16, - ret: IndexU8, + putfield: Index(u16), + putstatic: Index(u16), + ret: Index(u8), @"return", saload, sastore, From 6d8e9d05f972cbe371ceb0f59a0cd5b7a365c0f4 Mon Sep 17 00:00:00 2001 From: ktkk Date: Mon, 7 Sep 2026 13:35:46 +0000 Subject: [PATCH 6/6] Format instructions --- src/Class/AttributeInfo.zig | 14 +-- src/Class/Code/Instruction.zig | 188 ++++++++++++++++++++++++++++++++- 2 files changed, 191 insertions(+), 11 deletions(-) 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 => {}, + } + } };