diff --git a/src/Class.zig b/src/Class.zig index ff1a581..09a3846 100644 --- a/src/Class.zig +++ b/src/Class.zig @@ -36,8 +36,6 @@ 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 e138b98..5fbdc9f 100644 --- a/src/Class/AttributeInfo.zig +++ b/src/Class/AttributeInfo.zig @@ -319,22 +319,20 @@ pub const AttributeInfo = union(enum) { defer instr_indent -= 1; try w.splatByteAll(' ', depth * instr_indent); - try w.print("{d}:\n", .{ i }); - - try instr.indentedFormat(w, depth, instr_indent + 1, constant_pool); + try w.print("{d}: 0x{x}\n", .{ i, instr }); } try w.splatByteAll(' ', depth * indent); _ = try w.write("exception_table:\n"); var exception_indent = indent; for (attr.exception_table, 0..) |*exception, i| { - exception_indent += 1; - defer exception_indent -= 1; + 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); @@ -692,14 +690,12 @@ pub const ConstantValue = struct { pub const Code = struct { max_stack: u16, max_locals: u16, - code: []Instruction, + code: []u8, 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, @@ -740,20 +736,8 @@ pub const Code = struct { const max_locals = try input.takeInt(u16, .big); const code_length = try input.takeInt(u32, .big); - 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 code = try input.readAlloc(allocator, code_length); + errdefer allocator.free(code); const exception_table_length = try input.takeInt(u16, .big); const exception_table = try allocator.alloc(ExceptionHandler, exception_table_length); @@ -777,16 +761,13 @@ pub const Code = struct { return .{ .max_stack = max_stack, .max_locals = max_locals, - .code = try instructions.toOwnedSlice(allocator), + .code = code, .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/Code/Instruction.zig b/src/Class/Code/Instruction.zig deleted file mode 100644 index 494dc8b..0000000 --- a/src/Class/Code/Instruction.zig +++ /dev/null @@ -1,1227 +0,0 @@ -const std = @import("std"); - -const Class = @import("../../Class.zig"); -const ConstantPool = Class.ConstantPool; -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, -}; - -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; - - return struct { - index: T, - - const Self = @This(); - - pub fn parse(input: *std.Io.Reader, byte_index: *usize) ParseError!Self { - defer byte_index.* += bytes; - - const index = try if (bytes == 1) input.takeByte() else input.takeInt(T, .big); - - return .{ - .index = index, - }; - } - }; -} - -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(), - }; - } -}; - -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; - - return struct { - branch_offset: T, - - const Self = @This(); - - pub fn parse(input: *std.Io.Reader, byte_index: *usize) ParseError!Self { - defer byte_index.* += bytes; - - return .{ - .branch_offset = try input.takeInt(u16, .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(enum) { - 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: Index(u8), - aload_0, - aload_1, - aload_2, - aload_3, - anewarray: Index(u16), - areturn, - arraylength, - astore: Index(u8), - astore_0, - astore_1, - astore_2, - astore_3, - athrow, - baload, - bastore, - bipush: Byte, - caload, - castore, - checkcast: Index(u16), - d2f, - d2i, - d2l, - dadd, - daload, - dastore, - dcmpg, - dcmpl, - dconst_0, - dconst_1, - ddiv, - dload: Index(u8), - dload_0, - dload_1, - dload_2, - dload_3, - dmul, - dneg, - drem, - dreturn, - dstore: Index(u8), - 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: Index(u8), - fload_0, - fload_1, - fload_2, - fload_3, - fmul, - fneg, - frem, - freturn, - fstore: Index(u8), - fstore_0, - fstore_1, - fstore_2, - fstore_3, - fsub, - getfield: Index(u16), - getstatic: Index(u16), - goto: Branch(u16), - goto_w: Branch(u32), - 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: 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: Index(u8), - iload_0, - iload_1, - iload_2, - iload_3, - imul, - ineg, - instanceof: Index(u16), - invokedynamic: InvokeDynamic, - invokeinterface: InvokeInterface, - invokespecial: Index(u16), - invokestatic: Index(u16), - invokevirtual: Index(u16), - ior, - irem, - ireturn, - ishl, - ishr, - istore: Index(u8), - istore_0, - istore_1, - istore_2, - istore_3, - isub, - iushr, - ixor, - jsr: Branch(u16), - jsr_w: Branch(u32), - l2d, - l2f, - l2i, - ladd, - laload, - land, - lastore, - lcmp, - lconst_0, - lconst_1, - ldc: Index(u8), - ldc_w: Index(u16), - ldc2_w: Index(u16), - ldiv, - lload: Index(u8), - lload_0, - lload_1, - lload_2, - lload_3, - lmul, - lneg, - lookupswitch: LookupSwitch, - lor, - lrem, - lreturn, - lshl, - lshr, - lstore: Index(u8), - lstore_0, - lstore_1, - lstore_2, - lstore_3, - lsub, - lushr, - lxor, - monitorenter, - monitorexit, - multianewarray: MultiANewArray, - new: Index(u16), - newarray: NewArray, - nop, - pop, - pop2, - putfield: Index(u16), - putstatic: Index(u16), - ret: Index(u8), - @"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 => {}, - } - } - - 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 => {}, - } - } -}; - 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); +} + diff --git a/testsuite/classes/java/Main.java b/testsuite/classes/java/Main.java index 9b9cfa6..0f33998 100644 --- a/testsuite/classes/java/Main.java +++ b/testsuite/classes/java/Main.java @@ -17,52 +17,6 @@ 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 new file mode 100644 index 0000000..d33531f Binary files /dev/null and b/testsuite/classes/java/Object.class differ