diff --git a/testsuite/classes/java/Interface.java b/Interface.java similarity index 100% rename from testsuite/classes/java/Interface.java rename to Interface.java diff --git a/testsuite/classes/java/Main.java b/Main.java similarity index 61% rename from testsuite/classes/java/Main.java rename to Main.java index 0f33998..a3c6d9b 100644 --- a/testsuite/classes/java/Main.java +++ b/Main.java @@ -1,27 +1,17 @@ import java.util.function.Supplier; -@CustomAnnotation(type = CustomAnnotationType.ONE, name = "Alice") public class Main implements Runnable, Supplier, Interface { private static final String name = "John"; private static final int number = 42; private static final double funnyNumber = 6.9; - public static void main(@CustomParameterAnnotation String[] args) { - final var main = new Main(); - main.sayHello(); - - try { - main.throwSomething(); - } catch (Throwable e) { - System.err.println("Caught " + e.getMessage()); - } finally { - System.err.println("Finally block"); - } + public static void main() { + new Main().sayHello(); } - + @Override public void run() { - Main.main(new String[] { "Hello" }); + Main.main(); } @Override @@ -47,3 +37,4 @@ public class Main implements Runnable, Supplier, Interface { private static final String name = "Inner John"; } } + diff --git a/flake.nix b/flake.nix index b1ef715..c48263a 100644 --- a/flake.nix +++ b/flake.nix @@ -19,8 +19,6 @@ zls javaPackages.compiler.openjdk25 jdt-language-server - kotlin - kotlin-language-server ]; buildInputs = with pkgs; []; diff --git a/src/Class.zig b/src/Class.zig index 09a3846..2f09ba7 100644 --- a/src/Class.zig +++ b/src/Class.zig @@ -4,13 +4,14 @@ const EnumFlags = @import("EnumFlags.zig").EnumFlags; pub const FieldInfo = @import("Class/FieldInfo.zig"); pub const MethodInfo = @import("Class/MethodInfo.zig"); -pub const AttributeInfo = @import("Class/AttributeInfo.zig").AttributeInfo; -pub const ConstantPool = @import("Class/ConstantPool.zig"); +pub const AttributeInfo = @import("Class/AttributeInfo.zig"); + +allocator: std.mem.Allocator, magic: u32, minor_version: u16, major_version: u16, -constant_pool: ConstantPool, +constant_pool: []?ConstantPoolInfo, access_flags: ClassAccessFlags, this_class: u16, super_class: u16, @@ -21,7 +22,7 @@ attributes: []AttributeInfo, const Self = @This(); -pub const ClassAccessFlags = EnumFlags(enum(u16) { +pub const ClassFlags = enum(u16) { public = 0x0001, final = 0x0010, super = 0x0020, @@ -30,8 +31,9 @@ pub const ClassAccessFlags = EnumFlags(enum(u16) { synthetic = 0x1000, annotation = 0x2000, @"enum" = 0x4000, - module = 0x8000, -}); +}; + +pub const ClassAccessFlags = EnumFlags(ClassFlags); pub const ParseError = std.Io.Reader.Error || std.mem.Allocator.Error || error { InvalidMagicNumber, @@ -43,7 +45,7 @@ pub const ResolveError = error { InvalidConstantType, }; -pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) (ParseError || ResolveError)!Self { +pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { const magic = try input.takeInt(u32, .big); if (magic != 0xCAFEBABE) { return ParseError.InvalidMagicNumber; @@ -52,10 +54,15 @@ pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) (ParseError || const minor_version = try input.takeInt(u16, .big); const major_version = try input.takeInt(u16, .big); - var constant_pool: ConstantPool = try .parse(input, allocator); - errdefer constant_pool.deinit(allocator); + const constant_pool = try parseConstantPool(input, allocator); + errdefer { + for (constant_pool) |*cp_info| { + if (cp_info.*) |*c| c.deinit(allocator); + } + allocator.free(constant_pool); + } - const access_flags: ClassAccessFlags = .from(try input.takeInt(u16, .big)); + const access_flags: ClassAccessFlags = .{ .mask = try input.takeInt(u16, .big) }; const this_class = try input.takeInt(u16, .big); const super_class = try input.takeInt(u16, .big); @@ -63,7 +70,7 @@ pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) (ParseError || const interfaces = try parseInterfaces(input, allocator); errdefer allocator.free(interfaces); - const fields = try parseFields(input, constant_pool, allocator); + const fields = try parseFields(input, allocator); errdefer { for (fields) |*field| { field.deinit(allocator); @@ -71,7 +78,7 @@ pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) (ParseError || allocator.free(fields); } - const methods = try parseMethods(input, constant_pool, allocator); + const methods = try parseMethods(input, allocator); errdefer { for (methods) |*method| { method.deinit(allocator); @@ -79,7 +86,7 @@ pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) (ParseError || allocator.free(methods); } - const attributes = try parseAttributes(input, constant_pool, allocator); + const attributes = try parseAttributes(input, allocator); errdefer { for (attributes) |*attribute| { attribute.deinit(allocator); @@ -88,6 +95,7 @@ pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) (ParseError || } return .{ + .allocator = allocator, .magic = magic, .minor_version = minor_version, .major_version = major_version, @@ -102,6 +110,30 @@ pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) (ParseError || }; } +fn parseConstantPool(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError![]?ConstantPoolInfo { + const constant_pool_size = try input.takeInt(u16, .big); + const constant_pool = try allocator.alloc(?ConstantPoolInfo, constant_pool_size - 1); + errdefer allocator.free(constant_pool); + + var i: usize = 0; + while (i < constant_pool_size - 1) { + defer i += 1; + + const cp_info = try ConstantPoolInfo.parse(input, allocator); + constant_pool[i] = cp_info; + + switch (cp_info) { + .long, .double => { + i += 1; + constant_pool[i] = null; + }, + else => {}, + } + } + + return constant_pool; +} + fn parseInterfaces(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError![]u16 { const interfaces_count = try input.takeInt(u16, .big); const interfaces = try allocator.alloc(u16, interfaces_count); @@ -114,73 +146,80 @@ fn parseInterfaces(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseErr return interfaces; } -fn parseFields(input: *std.Io.Reader, constant_pool: ConstantPool, allocator: std.mem.Allocator) (ParseError || ResolveError)![]FieldInfo { +fn parseFields(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError![]FieldInfo { const fields_count = try input.takeInt(u16, .big); const fields = try allocator.alloc(FieldInfo, fields_count); errdefer allocator.free(fields); for (0..fields_count) |i| { - fields[i] = try FieldInfo.parse(input, constant_pool, allocator); + fields[i] = try FieldInfo.parse(input, allocator); } return fields; } -fn parseMethods(input: *std.Io.Reader, constant_pool: ConstantPool, allocator: std.mem.Allocator) (ParseError || ResolveError)![]MethodInfo { +fn parseMethods(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError![]MethodInfo { const methods_count = try input.takeInt(u16, .big); const methods = try allocator.alloc(MethodInfo, methods_count); errdefer allocator.free(methods); for (0..methods_count) |i| { - methods[i] = try .parse(input, constant_pool, allocator); + methods[i] = try MethodInfo.parse(input, allocator); } return methods; } -pub fn parseAttributes(input: *std.Io.Reader, constant_pool: ConstantPool, allocator: std.mem.Allocator) (ParseError || ResolveError)![]AttributeInfo { +pub fn parseAttributes(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError![]AttributeInfo { const attributes_count = try input.takeInt(u16, .big); const attributes = try allocator.alloc(AttributeInfo, attributes_count); errdefer allocator.free(attributes); for (0..attributes_count) |i| { - attributes[i] = try AttributeInfo.parse(input, constant_pool, allocator); + attributes[i] = try AttributeInfo.parse(input, allocator); } return attributes; } -pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { - self.constant_pool.deinit(allocator); - self.freeInterfaces(allocator); - self.freeFields(allocator); - self.freeMethods(allocator); - self.freeAttributes(allocator); +pub fn deinit(self: *Self) void { + self.freeConstantPool(); + self.freeInterfaces(); + self.freeFields(); + self.freeMethods(); + self.freeAttributes(); } -fn freeInterfaces(self: *Self, allocator: std.mem.Allocator) void { - allocator.free(self.interfaces); +fn freeConstantPool(self: *Self) void { + for (self.constant_pool) |*cp_info| { + if (cp_info.*) |*c| c.deinit(self.allocator); + } + self.allocator.free(self.constant_pool); } -fn freeFields(self: *Self, allocator: std.mem.Allocator) void { +fn freeInterfaces(self: *Self) void { + self.allocator.free(self.interfaces); +} + +fn freeFields(self: *Self) void { for (self.fields) |*field_info| { - field_info.deinit(allocator); + field_info.deinit(self.allocator); } - allocator.free(self.fields); + self.allocator.free(self.fields); } -fn freeMethods(self: *Self, allocator: std.mem.Allocator) void { +fn freeMethods(self: *Self) void { for (self.methods) |*method_info| { - method_info.deinit(allocator); + method_info.deinit(self.allocator); } - allocator.free(self.methods); + self.allocator.free(self.methods); } -fn freeAttributes(self: *Self, allocator: std.mem.Allocator) void { +fn freeAttributes(self: *Self) void { for (self.attributes) |*attr| { - attr.deinit(allocator); + attr.deinit(self.allocator); } - allocator.free(self.attributes); + self.allocator.free(self.attributes); } pub fn jsonStringify(self: *const Self, jws: *std.json.Stringify) !void { @@ -207,11 +246,11 @@ pub fn format(self: *const Self, w: *std.Io.Writer) std.Io.Writer.Error!void { const this_class = self.thisClass() catch return error.WriteFailed; _ = try w.write("this class:\n"); - try this_class.indentedFormat(w, depth, indent + 1, self.constant_pool); + try this_class.format(w, depth, indent + 1, self.constant_pool); _ = try w.write("super class:\n"); if (self.superClass() catch return error.WriteFailed) |super_class| { - try super_class.indentedFormat(w, depth, indent + 1, self.constant_pool); + try super_class.format(w, depth, indent + 1, self.constant_pool); } _ = try w.write("interfaces:\n"); @@ -219,11 +258,13 @@ pub fn format(self: *const Self, w: *std.Io.Writer) std.Io.Writer.Error!void { indent += 1; defer indent -= 1; - const info = self.constant_pool.getTag(interface, .class) catch return error.WriteFailed; + if (interface >= self.constant_pool.len) return error.WriteFailed; + const cp_info = self.constant_pool[interface - 1]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return error.WriteFailed; try w.splatByteAll(' ', depth * indent); try w.print("{d}:\n", .{ i }); - try info.indentedFormat(w, depth, indent + 1, self.constant_pool); + try cp_info.?.format(w, depth, indent + 1, self.constant_pool); } _ = try w.write("fields:\n"); @@ -233,7 +274,7 @@ pub fn format(self: *const Self, w: *std.Io.Writer) std.Io.Writer.Error!void { try w.splatByteAll(' ', depth * indent); try w.print("{d}:\n", .{ i }); - try field.indentedFormat(w, depth, indent + 1, self.constant_pool); + try field.format(w, depth, indent + 1, self.allocator, self.constant_pool); } _ = try w.write("methods:\n"); @@ -243,7 +284,7 @@ pub fn format(self: *const Self, w: *std.Io.Writer) std.Io.Writer.Error!void { try w.splatByteAll(' ', depth * indent); try w.print("{d}:\n", .{ i }); - try method.indentedFormat(w, depth, indent + 1, self.constant_pool); + try method.format(w, depth, indent + 1, self.allocator, self.constant_pool); } _ = try w.write("attributes:\n"); @@ -253,15 +294,476 @@ pub fn format(self: *const Self, w: *std.Io.Writer) std.Io.Writer.Error!void { try w.splatByteAll(' ', depth * indent); try w.print("{d}:\n", .{ i }); - try attribute.indentedFormat(w, depth, indent + 1, self.constant_pool); + try attribute.format(w, depth, indent + 1, self.allocator, self.constant_pool); } } -pub fn thisClass(self: *const Self) ResolveError!ConstantPool.Info { - return self.constant_pool.getTag(self.this_class, .class); +pub fn thisClass(self: *const Self) ResolveError!ConstantPoolInfo { + const this_class = self.this_class - 1; + if (this_class >= self.constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = self.constant_pool[this_class]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; + return cp_info.?; } -pub fn superClass(self: *const Self) ResolveError!?ConstantPool.Info { - return self.constant_pool.getTagOptional(self.super_class, .class); +pub fn superClass(self: *const Self) ResolveError!?ConstantPoolInfo { + if (self.super_class == 0) { + const this_class = (try self.thisClass()).class; + const this_class_name = (try this_class.name(self.constant_pool)).utf8; + if (!std.mem.eql(u8, this_class_name.bytes, "java/lang/Object")) return ResolveError.InvalidConstantType; + return null; + } else { + const super_class = self.super_class - 1; + if (super_class >= self.constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = self.constant_pool[super_class]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; + + if (self.access_flags.contains(.interface)) { + const super_class_name = (try cp_info.?.class.name(self.constant_pool)).utf8; + if (!std.mem.eql(u8, super_class_name.bytes, "java/lang/Object")) return ResolveError.InvalidConstantType; + } else { + // TODO: Assert that superclass does not have ACC_FINAL flag set. + } + return cp_info.?; + } } +pub const ConstantPoolInfo = union(ConstantPoolInfo.Tag) { + class: Class, + field_ref: FieldRef, + method_ref: MethodRef, + interface_method_ref: InterfaceMethodRef, + string: String, + integer: Integer, + float: Float, + long: Long, + double: Double, + name_and_type: NameAndType, + utf8: Utf8, + method_handle: MethodHandle, + method_type: MethodType, + invoke_dynamic: InvokeDynamic, + + pub const Tag = enum(u8) { + class = 7, + field_ref = 9, + method_ref = 10, + interface_method_ref = 11, + string = 8, + integer = 3, + float = 4, + long = 5, + double = 6, + name_and_type = 12, + utf8 = 1, + method_handle = 15, + method_type = 16, + invoke_dynamic = 18, + }; + + pub const Class = struct { + name_index: u16, + + pub fn name(self: *const Class, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const name_index = self.name_index - 1; + if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[name_index]; + if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; + } + }; + + pub const FieldRef = struct { + class_index: u16, + name_and_type_index: u16, + + pub fn class(self: *const FieldRef, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const class_index = self.class_index - 1; + if (class_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[class_index]; + if (cp_info == null or @as(Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; + return cp_info.?; + } + + pub fn nameAndType(self: *const FieldRef, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const name_and_type_index = self.name_and_type_index - 1; + if (name_and_type_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[name_and_type_index]; + if (cp_info == null or @as(Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType; + return cp_info.?; + } + }; + + pub const MethodRef = struct { + class_index: u16, + name_and_type_index: u16, + + pub fn class(self: *const MethodRef, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const class_index = self.class_index - 1; + if (class_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[class_index]; + if (cp_info == null or @as(Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; + return cp_info.?; + } + + pub fn nameAndType(self: *const MethodRef, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const name_and_type_index = self.name_and_type_index - 1; + if (name_and_type_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[name_and_type_index]; + if (cp_info == null or @as(Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType; + return cp_info.?; + } + }; + + pub const InterfaceMethodRef = struct { + class_index: u16, + name_and_type_index: u16, + + pub fn class(self: *const InterfaceMethodRef, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const class_index = self.class_index - 1; + if (class_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[class_index]; + if (cp_info == null or @as(Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; + return cp_info.?; + } + + pub fn nameAndType(self: *const InterfaceMethodRef, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const name_and_type_index = self.name_and_type_index - 1; + if (name_and_type_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[name_and_type_index]; + if (cp_info == null or @as(Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType; + return cp_info.?; + } + }; + + pub const String = struct { + string_index: u16, + + pub fn string(self: *const String, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const string_index = self.string_index - 1; + if (string_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[string_index]; + if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; + } + }; + + pub const Integer = struct { + bytes: u32, + }; + + pub const Float = struct { + bytes: u32, + }; + + pub const Long = struct { + high_bytes: u32, + low_bytes: u32, + }; + + pub const Double = struct { + high_bytes: u32, + low_bytes: u32, + }; + + pub const NameAndType = struct { + name_index: u16, + descriptor_index: u16, + + pub fn name(self: *const NameAndType, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const name_index = self.name_index - 1; + if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[name_index]; + if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; + } + + pub fn descriptor(self: *const NameAndType, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const descriptor_index = self.descriptor_index - 1; + if (descriptor_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[descriptor_index]; + if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; + } + }; + + pub const Utf8 = struct { + bytes: []const u8, + }; + + pub const MethodHandle = struct { + reference_kind: Kind, + reference_index: u16, + + pub const Kind = enum(u8) { + get_field = 1, + get_static = 2, + put_field = 3, + put_static = 4, + invoke_virtual = 5, + invoke_static = 6, + invoke_special = 7, + new_invoke_special = 8, + invoke_interface = 9, + }; + + pub fn reference(self: *const MethodHandle, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const reference_index = self.reference_index - 1; + if (reference_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[reference_index]; + return switch (self.reference_kind) { + .get_field, .get_static, .put_field, .put_static => blk: { + if (cp_info == null or @as(Tag, cp_info.?) != .field_ref) break :blk ResolveError.InvalidConstantType; + break :blk cp_info.?; + }, + .invoke_virtual, .invoke_static, .invoke_special, .new_invoke_special => blk: { + if (cp_info == null or @as(Tag, cp_info.?) != .method_ref) break :blk ResolveError.InvalidConstantType; + break :blk cp_info.?; + }, + .invoke_interface => blk: { + if (cp_info == null or @as(Tag, cp_info.?) != .interface_method_ref) break :blk ResolveError.InvalidConstantType; + break :blk cp_info.?; + }, + }; + } + }; + + pub const MethodType = struct { + descriptor_index: u16, + + pub fn descriptor(self: *const MethodType, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const descriptor_index = self.descriptor_index - 1; + if (descriptor_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[descriptor_index]; + if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; + } + }; + + pub const InvokeDynamic = struct { + bootstrap_method_attr_index: u16, + name_and_type_index: u16, + + pub fn nameAndType(self: *const InvokeDynamic, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const name_and_type_index = self.name_and_type_index - 1; + if (name_and_type_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[name_and_type_index]; + if (cp_info == null or @as(Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType; + return cp_info.?; + } + }; + + pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!ConstantPoolInfo { + const tag_byte = try input.takeByte(); + const tag = std.enums.fromInt(Tag, tag_byte) orelse return ParseError.InvalidTag; + + return switch (tag) { + .class => .{ + .class = .{ + .name_index = try input.takeInt(u16, .big), + }, + }, + .field_ref => .{ + .field_ref = .{ + .class_index = try input.takeInt(u16, .big), + .name_and_type_index = try input.takeInt(u16, .big), + }, + }, + .method_ref => .{ + .method_ref = .{ + .class_index = try input.takeInt(u16, .big), + .name_and_type_index = try input.takeInt(u16, .big), + }, + }, + .interface_method_ref => .{ + .interface_method_ref = .{ + .class_index = try input.takeInt(u16, .big), + .name_and_type_index = try input.takeInt(u16, .big), + }, + }, + .string => .{ + .string = .{ + .string_index = try input.takeInt(u16, .big), + }, + }, + .integer => .{ + .integer = .{ + .bytes = try input.takeInt(u32, .big), + }, + }, + .float => .{ + .integer = .{ + .bytes = try input.takeInt(u32, .big), + }, + }, + .long => .{ + .long = .{ + .high_bytes = try input.takeInt(u32, .big), + .low_bytes = try input.takeInt(u32, .big), + }, + }, + .double => .{ + .double = .{ + .high_bytes = try input.takeInt(u32, .big), + .low_bytes = try input.takeInt(u32, .big), + }, + }, + .name_and_type => .{ + .name_and_type = .{ + .name_index = try input.takeInt(u16, .big), + .descriptor_index = try input.takeInt(u16, .big), + }, + }, + .utf8 => blk: { + const length = try input.takeInt(u16, .big); + const bytes = try input.readAlloc(allocator, length); + + break :blk .{ + .utf8 = .{ + .bytes = bytes, + }, + }; + }, + .method_handle => blk: { + const kind_byte = try input.takeByte(); + const kind = std.enums.fromInt(MethodHandle.Kind, kind_byte) orelse return ParseError.InvalidTag; + + break :blk .{ + .method_handle = .{ + .reference_kind = kind, + .reference_index = try input.takeInt(u16, .big), + }, + }; + }, + .method_type => .{ + .method_type = .{ + .descriptor_index = try input.takeInt(u16, .big), + }, + }, + .invoke_dynamic => .{ + .invoke_dynamic = .{ + .bootstrap_method_attr_index = try input.takeInt(u16, .big), + .name_and_type_index = try input.takeInt(u16, .big), + }, + }, + }; + } + + pub fn deinit(self: *ConstantPoolInfo, allocator: std.mem.Allocator) void { + switch (self.*) { + .utf8 => |info| allocator.free(info.bytes), + else => {}, + } + } + + pub fn format(self: *const ConstantPoolInfo, w: *std.Io.Writer, depth: usize, indent: u8, constant_pool: []const ?ConstantPoolInfo) std.Io.Writer.Error!void { + try w.splatByteAll(' ', depth * indent); + try w.print("constant_value_type: {s}\n", .{ @tagName(self.*) }); + + switch (self.*) { + .class => |class| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("name:\n"); + const class_name = class.name(constant_pool) catch return error.WriteFailed; + try class_name.format(w, depth, indent + 1, constant_pool); + }, + .field_ref => |field_ref| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("class:\n"); + const class = field_ref.class(constant_pool) catch return error.WriteFailed; + try class.format(w, depth, indent + 1, constant_pool); + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("name_and_type:\n"); + const name_and_type = field_ref.nameAndType(constant_pool) catch return error.WriteFailed; + try name_and_type.format(w, depth, indent + 1, constant_pool); + }, + .method_ref => |method_ref| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("class:\n"); + const class = method_ref.class(constant_pool) catch return error.WriteFailed; + try class.format(w, depth, indent + 1, constant_pool); + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("name_and_type:\n"); + const name_and_type = method_ref.nameAndType(constant_pool) catch return error.WriteFailed; + try name_and_type.format(w, depth, indent + 1, constant_pool); + }, + .interface_method_ref => |interface_method_ref| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("class:\n"); + const class = interface_method_ref.class(constant_pool) catch return error.WriteFailed; + try class.format(w, depth, indent + 1, constant_pool); + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("name_and_type:\n"); + const name_and_type = interface_method_ref.nameAndType(constant_pool) catch return error.WriteFailed; + try name_and_type.format(w, depth, indent + 1, constant_pool); + }, + .string => |string| { + const s = string.string(constant_pool) catch return error.WriteFailed; + try s.format(w, depth, indent + 1, constant_pool); + }, + .integer => |integer| { + try w.splatByteAll(' ', depth * indent); + try w.print("integer: {d}\n", .{ integer.bytes }); + }, + .float => |float| { + const f: f32 = @bitCast(float.bytes); + + try w.splatByteAll(' ', depth * indent); + try w.print("float: {d}\n", .{ f }); + }, + .long => |long| { + const l: u64 = (@as(u64, long.high_bytes) << 32) | long.low_bytes; + + try w.splatByteAll(' ', depth * indent); + try w.print("long: {d}\n", .{ l }); + }, + .double => |double| { + const l: u64 = (@as(u64, double.high_bytes) << 32) | double.low_bytes; + const d: f64 = @bitCast(l); + + try w.splatByteAll(' ', depth * indent); + try w.print("double: {d}\n", .{ d }); + }, + .name_and_type => |name_and_type| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("name:\n"); + const name = name_and_type.name(constant_pool) catch return error.WriteFailed; + try name.format(w, depth, indent + 1, constant_pool); + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("descriptor:\n"); + const descriptor = name_and_type.descriptor(constant_pool) catch return error.WriteFailed; + try descriptor.format(w, depth, indent + 1, constant_pool); + }, + .utf8 => |utf8| { + try w.splatByteAll(' ', depth * indent); + try w.print("bytes: \"{s}\"\n", .{ utf8.bytes }); + }, + .method_handle => |method_handle| { + try w.splatByteAll(' ', depth * indent); + try w.print("kind: {t}\n", .{ method_handle.reference_kind }); + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("reference:\n"); + const reference = method_handle.reference(constant_pool) catch return error.WriteFailed; + try reference.format(w, depth, indent + 1, constant_pool); + }, + .method_type => |method_type| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("descriptor:\n"); + const descriptor = method_type.descriptor(constant_pool) catch return error.WriteFailed; + try descriptor.format(w, depth, indent + 1, constant_pool); + }, + .invoke_dynamic => |invoke_dynamic| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("name_and_type:\n"); + const name_and_type = invoke_dynamic.nameAndType(constant_pool) catch return error.WriteFailed; + try name_and_type.format(w, depth, indent + 1, constant_pool); + }, + } + } +}; + diff --git a/src/Class/AttributeInfo.zig b/src/Class/AttributeInfo.zig index 5fbdc9f..a3276da 100644 --- a/src/Class/AttributeInfo.zig +++ b/src/Class/AttributeInfo.zig @@ -3,1139 +3,300 @@ const std = @import("std"); const EnumFlags = @import("../EnumFlags.zig").EnumFlags; const Class = @import("../Class.zig"); -const ConstantPool = Class.ConstantPool; +const ConstantPoolInfo = Class.ConstantPoolInfo; +const AttributeInfo = Class.AttributeInfo; const ParseError = Class.ParseError; const ResolveError = Class.ResolveError; -pub const AttributeInfo = union(enum) { - new: NewAttribute, - predefined: union(enum) { - constant_value: ConstantValue, - code: Code, - stack_map_table: StackMapTable, - exceptions: Exceptions, - inner_classes: InnerClasses, - enclosing_method: EnclosingMethod, - synthetic: Synthetic, - signature: Signature, - source_file: SourceFile, - source_debug_extension: SourceDebugExtension, - line_number_table: LineNumberTable, - local_variable_table: LocalVariableTable, - local_variable_type_table: LocalVariableTypeTable, - deprecated: Deprecated, - runtime_visible_annotations: RuntimeVisibleAnnotations, - runtime_invisible_annotations: RuntimeInvisibleAnnotations, - runtime_visible_parameter_annotations: RuntimeVisibleParameterAnnotations, - runtime_invisible_parameter_annotations: RuntimeInvisibleParameterAnnotations, - runtime_visible_type_annotations: RuntimeVisibleTypeAnnotations, - runtime_invisible_type_annotations: RuntimeInvisibleTypeAnnotations, - annotation_default: AnnotationDefault, - bootstrap_methods: BootstrapMethods, - method_parameters: MethodParameters, - module: Module, - module_packages: ModulePackages, - module_main_class: ModuleMainClass, - nest_host: NestHost, - nest_members: NestMembers, - record: Record, - permitted_subclasses: PermittedSubclasses, - }, +attribute_name_index: u16, +info: []u8, - const Self = @This(); +const Self = @This(); - pub fn parse(input: *std.Io.Reader, constant_pool: ConstantPool, allocator: std.mem.Allocator) (ParseError || ResolveError)!AttributeInfo { - const attribute_name_index = try input.takeInt(u16, .big); - const attribute_name = try constant_pool.getTag(attribute_name_index, .utf8); - const name = attribute_name.utf8.bytes; +pub const Predefined = union(enum) { + constant_value: ConstantValue, + code: Code, + stack_map_table: StackMapTable, + exceptions: Exceptions, + inner_classes: InnerClasses, + enclosing_method: EnclosingMethod, + synthetic: Synthetic, + signature: Signature, + source_file: SourceFile, + source_debug_extension: SourceDebugExtension, + line_number_table: LineNumberTable, + local_variable_table: LocalVariableTable, + local_variable_type_table: LocalVariableTypeTable, + deprecated: Deprecated, + runtime_visible_annotations: RuntimeVisibleAnnotations, + runtime_invisible_annotations: RuntimeInvisibleAnnotations, + runtime_visible_parameter_annotations: RuntimeVisibleParameterAnnotations, + runtime_invisible_parameter_annotations: RuntimeInvisibleParameterAnnotations, + annotation_default: AnnotationDefault, + bootstrap_methods: BootstrapMethods, - const attribute_length = try input.takeInt(u32, .big); + pub const ConstantValue = struct { + constant_value_index: u16, - var limited_buf: [1024]u8 = undefined; - var limited_reader: std.Io.Reader.Limited = .init(input, .limited(attribute_length), &limited_buf); - const limited = &limited_reader.interface; + pub fn constantValue(self: *const ConstantValue, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const constant_value_index = self.constant_value_index - 1; + if (constant_value_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[constant_value_index]; + if (cp_info == null) return ResolveError.InvalidConstantType; - if (std.mem.eql(u8, name, "ConstantValue")) { - return .{ - .predefined = .{ - .constant_value = try ConstantValue.parse(limited), - }, - }; - } else if (std.mem.eql(u8, name, "Code")) { - return .{ - .predefined = .{ - .code = try Code.parse(limited, constant_pool, allocator), - }, - }; - } else if (std.mem.eql(u8, name, "StackMapTable")) { - return .{ - .predefined = .{ - .stack_map_table = try StackMapTable.parse(limited, allocator), - }, - }; - } else if (std.mem.eql(u8, name, "Exceptions")) { - return .{ - .predefined = .{ - .exceptions = try Exceptions.parse(limited, allocator), - }, - }; - } else if (std.mem.eql(u8, name, "InnerClasses")) { - return .{ - .predefined = .{ - .inner_classes = try InnerClasses.parse(limited, allocator), - }, - }; - } else if (std.mem.eql(u8, name, "EnclosingMethod")) { - return .{ - .predefined = .{ - .enclosing_method = try EnclosingMethod.parse(limited), - }, - }; - } else if (std.mem.eql(u8, name, "Synthetic")) { - return .{ - .predefined = .{ - .synthetic = .{}, - }, - }; - } else if (std.mem.eql(u8, name, "Signature")) { - return .{ - .predefined = .{ - .signature = try Signature.parse(limited), - }, - }; - } else if (std.mem.eql(u8, name, "SourceFile")) { - return .{ - .predefined = .{ - .source_file = try SourceFile.parse(limited), - }, - }; - } else if (std.mem.eql(u8, name, "SourceDebugExtension")) { - return .{ - .predefined = .{ - .source_debug_extension = try SourceDebugExtension.parse(limited, attribute_length, allocator), - }, - }; - } else if (std.mem.eql(u8, name, "LineNumberTable")) { - return .{ - .predefined = .{ - .line_number_table = try LineNumberTable.parse(limited, allocator), - }, - }; - } else if (std.mem.eql(u8, name, "LocalVariableTable")) { - return .{ - .predefined = .{ - .local_variable_table = try LocalVariableTable.parse(limited, allocator), - }, - }; - } else if (std.mem.eql(u8, name, "LocalVariableTypeTable")) { - return .{ - .predefined = .{ - .local_variable_type_table = try LocalVariableTypeTable.parse(limited, allocator), - }, - }; - } else if (std.mem.eql(u8, name, "Deprecated")) { - return .{ - .predefined = .{ - .deprecated = .{}, - }, - }; - } else if (std.mem.eql(u8, name, "RuntimeVisibleAnnotations")) { - return .{ - .predefined = .{ - .runtime_visible_annotations = try RuntimeVisibleAnnotations.parse(limited, allocator), - }, - }; - } else if (std.mem.eql(u8, name, "RuntimeInvisibleAnnotations")) { - return .{ - .predefined = .{ - .runtime_invisible_annotations = try RuntimeInvisibleAnnotations.parse(limited, allocator), - }, - }; - } else if (std.mem.eql(u8, name, "RuntimeVisibleParameterAnnotations")) { - return .{ - .predefined = .{ - .runtime_visible_parameter_annotations = try RuntimeVisibleParameterAnnotations.parse(limited, allocator), - }, - }; - } else if (std.mem.eql(u8, name, "RuntimeInvisibleParameterAnnotations")) { - return .{ - .predefined = .{ - .runtime_invisible_parameter_annotations = try RuntimeInvisibleParameterAnnotations.parse(limited, allocator), - }, - }; - } else if (std.mem.eql(u8, name, "RuntimeVisibleTypeAnnotations")) { - return .{ - .predefined = .{ - .runtime_invisible_type_annotations = try RuntimeInvisibleTypeAnnotations.parse(limited, allocator), - }, - }; - } else if (std.mem.eql(u8, name, "RuntimeInvisibleTypeAnnotations")) { - return .{ - .predefined = .{ - .runtime_invisible_type_annotations = try RuntimeInvisibleTypeAnnotations.parse(limited, allocator), - }, - }; - } else if (std.mem.eql(u8, name, "AnnotationDefault")) { - return .{ - .predefined = .{ - .annotation_default = try AnnotationDefault.parse(limited, allocator), - }, - }; - } else if (std.mem.eql(u8, name, "BootstrapMethods")) { - return .{ - .predefined = .{ - .bootstrap_methods = try BootstrapMethods.parse(limited, allocator), - }, - }; - } else if (std.mem.eql(u8, name, "MethodParameters")) { - return .{ - .predefined = .{ - .method_parameters = try MethodParameters.parse(limited, allocator), - }, - }; - } else if (std.mem.eql(u8, name, "Module")) { - return .{ - .predefined = .{ - .module = try Module.parse(limited, allocator), - }, - }; - } else if (std.mem.eql(u8, name, "ModulePackages")) { - return .{ - .predefined = .{ - .module_packages = try ModulePackages.parse(limited, allocator), - }, - }; - } else if (std.mem.eql(u8, name, "ModuleMainClass")) { - return .{ - .predefined = .{ - .module_main_class = try ModuleMainClass.parse(limited), - }, - }; - } else if (std.mem.eql(u8, name, "NestHost")) { - return .{ - .predefined = .{ - .nest_host = try NestHost.parse(limited), - }, - }; - } else if (std.mem.eql(u8, name, "NestMembers")) { - return .{ - .predefined = .{ - .nest_members = try NestMembers.parse(limited, allocator), - }, - }; - } else if (std.mem.eql(u8, name, "Record")) { - return .{ - .predefined = .{ - .record = try Record.parse(limited, constant_pool, allocator), - }, - }; - } else if (std.mem.eql(u8, name, "PermittedSubclasses")) { - return .{ - .predefined = .{ - .permitted_subclasses = try PermittedSubclasses.parse(limited, allocator), - }, - }; - } else { - return .{ - .new = try NewAttribute.parse(limited, attribute_name_index + 1, attribute_length, allocator), - }; + return cp_info.?; } - } + }; - pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { - switch (self.*) { - .new => |*new| new.deinit(allocator), - .predefined => |*predefined| switch (predefined.*) { - .code => |*code| code.deinit(allocator), - .stack_map_table => |*stack_map_table| stack_map_table.deinit(allocator), - .exceptions => |*exceptions| exceptions.deinit(allocator), - .inner_classes => |*inner_classes| inner_classes.deinit(allocator), - .source_debug_extension => |*source_debug_extension| source_debug_extension.deinit(allocator), - .line_number_table => |*line_number_table| line_number_table.deinit(allocator), - .local_variable_table => |*local_variable_table| local_variable_table.deinit(allocator), - .local_variable_type_table => |*local_variable_type_table| local_variable_type_table.deinit(allocator), - .runtime_visible_annotations => |*runtime_visible_annotations| runtime_visible_annotations.deinit(allocator), - .runtime_invisible_annotations => |*runtime_invisible_annotations| runtime_invisible_annotations.deinit(allocator), - .runtime_visible_parameter_annotations => |*runtime_visible_parameter_annotations| runtime_visible_parameter_annotations.deinit(allocator), - .runtime_invisible_parameter_annotations => |*runtime_invisible_parameter_annotations| runtime_invisible_parameter_annotations.deinit(allocator), - .runtime_visible_type_annotations => |*runtime_visible_type_annotations| runtime_visible_type_annotations.deinit(allocator), - .runtime_invisible_type_annotations => |*runtime_invisible_type_annotations| runtime_invisible_type_annotations.deinit(allocator), - .annotation_default => |*annotation_default| annotation_default.deinit(allocator), - .bootstrap_methods => |*bootstrap_methods| bootstrap_methods.deinit(allocator), - .method_parameters => |*method_parameters| method_parameters.deinit(allocator), - .module => |*module| module.deinit(allocator), - .module_packages => |*module_packages| module_packages.deinit(allocator), - .nest_members => |*nest_members| nest_members.deinit(allocator), - .record => |*record| record.deinit(allocator), - .permitted_subclasses => |*permitted_subclasses| permitted_subclasses.deinit(allocator), - else => {}, + pub const Code = struct { + max_stack: u16, + max_locals: u16, + code: []u8, + exception_table: []ExceptionHandler, + attributes: []AttributeInfo, + + pub const ExceptionHandler = struct { + start_pc: u16, + end_pc: u16, + handler_pc: u16, + catch_type: u16, + }; + }; + + pub const StackMapTable = struct { + entries: []StackMapFrame, + + pub const StackMapFrame = union(enum) { + // 0-63 + same_frame, + // 64-127 + same_locals_1_stack_item_frame: struct { + stack: [1]VerificationTypeInfo, }, - } - } - - pub fn indentedFormat( - self: *const Self, - w: *std.Io.Writer, - depth: usize, - indent: u8, - constant_pool: ConstantPool, - ) std.Io.Writer.Error!void { - switch (self.*) { - .new => |new| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("predefined: no\n"); - - try w.splatByteAll(' ', depth * indent); - _ = try w.write("name:\n"); - const attribute_name = new.attributeName(constant_pool) catch return error.WriteFailed; - try attribute_name.indentedFormat(w, depth, indent + 1, constant_pool); + // 247 + same_locals_1_stack_item_frame_extended: struct { + offset_delta: u16, + stack: [1]VerificationTypeInfo, + }, + // 248-250 + chop_frame: struct { + offset_delta: u16, + }, + // 251 + same_frame_extended: struct { + offset_delta: u16, + }, + // 252-254 + append_frame: struct { + offset_delta: u16, + locals: []const VerificationTypeInfo, + }, + // 255 + full_frame: struct { + offset_delta: u16, + locals: []const VerificationTypeInfo, + stack: []const VerificationTypeInfo, }, - .predefined => |predefined| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("predefined: yes\n"); - try w.splatByteAll(' ', depth * indent); - try w.print("type: {t}\n", .{ predefined }); + pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!StackMapFrame { + const frame_type = try input.takeByte(); + return switch (frame_type) { + 0...63 => .same_frame, + 64...127 => .{ + .same_locals_1_stack_item_frame = .{ + .stack = .{ try .parse(input) }, + }, + }, + 247 => .{ + .same_locals_1_stack_item_frame_extended = .{ + .offset_delta = try input.takeInt(u16, .big), + .stack = .{ try .parse(input) }, + }, + }, + 248...250 => .{ + .chop_frame = .{ + .offset_delta = try input.takeInt(u16, .big), + }, + }, + 252...254 => blk: { + const offset_delta = try input.takeInt(u16, .big); - switch (predefined) { - .constant_value => |attr| { - const constant_value = attr.constantValue(constant_pool) catch return error.WriteFailed; - try w.splatByteAll(' ', depth * indent); - _ = try w.write("constant_value:\n"); - try constant_value.indentedFormat(w, depth, indent + 1, constant_pool); - }, - .code => |attr| { - try w.splatByteAll(' ', depth * indent); - try w.print("max_stack: {d}\n", .{ attr.max_stack }); - - try w.splatByteAll(' ', depth * indent); - try w.print("max_locals: {d}\n", .{ attr.max_locals }); - - try w.splatByteAll(' ', depth * indent); - _ = try w.write("code:\n"); - var instr_indent = indent; - for (attr.code, 0..) |instr, i| { - instr_indent += 1; - defer instr_indent -= 1; - - try w.splatByteAll(' ', depth * instr_indent); - try w.print("{d}: 0x{x}\n", .{ i, instr }); + const length = frame_type - 251; + const locals = try allocator.alloc(Predefined.StackMapTable.VerificationTypeInfo, length); + errdefer allocator.free(locals); + for (locals) |*local| { + local.* = try .parse(input); } - 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; + break :blk .{ + .append_frame = .{ + .offset_delta = offset_delta, + .locals = locals, + }, + }; + }, + 255 => blk: { + const offset_delta = try input.takeInt(u16, .big); - try w.splatByteAll(' ', depth * exception_indent); - try w.print("{d}:\n", .{ i }); - - try exception.indentedFormat(w, depth, exception_indent + 1, constant_pool); + const number_of_locals = try input.takeInt(u16, .big); + const locals = try allocator.alloc(Predefined.StackMapTable.VerificationTypeInfo, number_of_locals); + errdefer allocator.free(locals); + for (locals) |*local| { + local.* = try .parse(input); } - try w.splatByteAll(' ', depth * indent); - _ = try w.write("attributes:\n"); - var attr_indent = indent; - for (attr.attributes, 0..) |*a, i| { - attr_indent += 1; - defer attr_indent -= 1; - - try w.splatByteAll(' ', depth * attr_indent); - try w.print("{d}:\n", .{ i }); - - try a.indentedFormat(w, depth, attr_indent + 1, constant_pool); + const number_of_stack_items = try input.takeInt(u16, .big); + const stack = try allocator.alloc(Predefined.StackMapTable.VerificationTypeInfo, number_of_stack_items); + errdefer allocator.free(stack); + for (stack) |*stack_item| { + stack_item.* = try .parse(input); } + + break :blk .{ + .full_frame = .{ + .offset_delta = offset_delta, + .locals = locals, + .stack = stack, + }, + }; }, - .stack_map_table => |attr| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("entries:\n"); - var stack_map_frame_indent = indent; - for (attr.entries, 0..) |*stack_map_frame, i| { - stack_map_frame_indent += 1; - defer stack_map_frame_indent -= 1; + else => unreachable, + }; + } - try w.splatByteAll(' ', depth * stack_map_frame_indent); - try w.print("{d}:\n", .{ i }); - - try stack_map_frame.indentedFormat(w, depth, stack_map_frame_indent + 1, constant_pool); - } - }, - .exceptions => |attr| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("exceptions:\n"); - var exception_indent = indent; - for (attr.exception_index_table, 0..) |exception_index, i| { - exception_indent += 1; - defer exception_indent -= 1; - - const info = constant_pool.getTag(exception_index, .class) catch return error.WriteFailed; - - try w.splatByteAll(' ', depth * exception_indent); - try w.print("{d}:\n", .{ i }); - try info.indentedFormat(w, depth, exception_indent + 1, constant_pool); - } - }, - .inner_classes => |attr| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("classes:\n"); - var class_indent = indent; - for (attr.classes, 0..) |*class, i| { - class_indent += 1; - defer class_indent -= 1; - - try w.splatByteAll(' ', depth * class_indent); - try w.print("{d}:\n", .{ i }); - try class.indentedFormat(w, depth, class_indent + 1, constant_pool); - } - }, - .enclosing_method => |attr| { - const class = attr.class(constant_pool) catch return error.WriteFailed; - try w.splatByteAll(' ', depth * indent); - _ = try w.write("class:\n"); - try class.indentedFormat(w, depth, indent + 1, constant_pool); - - const method = attr.method(constant_pool) catch return error.WriteFailed; - if (method) |m| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("method:\n"); - try m.indentedFormat(w, depth, indent + 1, constant_pool); - } - }, - .synthetic => {}, - .signature => |attr| { - const signature = attr.signature(constant_pool) catch return error.WriteFailed; - try w.splatByteAll(' ', depth * indent); - _ = try w.write("signature:\n"); - try signature.indentedFormat(w, depth, indent + 1, constant_pool); - }, - .source_file => |attr| { - const source_file = attr.sourceFile(constant_pool) catch return error.WriteFailed; - try w.splatByteAll(' ', depth * indent); - _ = try w.write("source_file:\n"); - try source_file.indentedFormat(w, depth, indent + 1, constant_pool); - }, - .source_debug_extension => |attr| { - try w.splatByteAll(' ', depth * indent); - try w.print("debug_extension: {s}\n", .{ attr.debug_extension }); - }, - .line_number_table => |attr| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("line_number_table:\n"); - var line_number_indent = indent; - for (attr.line_number_table, 0..) |*line_number, i| { - line_number_indent += 1; - defer line_number_indent -= 1; - - try w.splatByteAll(' ', depth * line_number_indent); - try w.print("{d}:\n", .{ i }); - - try line_number.indentedFormat(w, depth, line_number_indent + 1); - } - }, - .local_variable_table => |attr| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("local_variable_table:\n"); - var local_variable_indent = indent; - for (attr.local_variable_table, 0..) |*local_variable, i| { - local_variable_indent += 1; - defer local_variable_indent -= 1; - - try w.splatByteAll(' ', depth * local_variable_indent); - try w.print("{d}:\n", .{ i }); - - try local_variable.indentedFormat(w, depth, local_variable_indent + 1, constant_pool); - } - }, - .local_variable_type_table => |attr| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("local_variable_type_table:\n"); - var local_variable_type_indent = indent; - for (attr.local_variable_type_table, 0..) |*local_variable_type, i| { - local_variable_type_indent += 1; - defer local_variable_type_indent -= 1; - - try w.splatByteAll(' ', depth * local_variable_type_indent); - try w.print("{d}:\n", .{ i }); - - try local_variable_type.indentedFormat(w, depth, local_variable_type_indent + 1, constant_pool); - } - }, - .deprecated => {}, - .runtime_visible_annotations => |attr| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("annotations:\n"); - var annotation_indent = indent; - for (attr.annotations, 0..) |annotation, i| { - annotation_indent += 1; - defer annotation_indent -= 1; - - try w.splatByteAll(' ', depth * annotation_indent); - try w.print("{d}:\n", .{ i }); - try annotation.indentedFormat(w, depth, annotation_indent + 1, constant_pool); - } - }, - .runtime_invisible_annotations => |attr| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("annotations:\n"); - var annotation_indent = indent; - for (attr.annotations, 0..) |annotation, i| { - annotation_indent += 1; - defer annotation_indent -= 1; - - try w.splatByteAll(' ', depth * annotation_indent); - try w.print("{d}:\n", .{ i }); - try annotation.indentedFormat(w, depth, annotation_indent + 1, constant_pool); - } - }, - .runtime_visible_parameter_annotations => |attr| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("parameter_annotations:\n"); - var parameter_annotation_indent = indent; - for (attr.parameter_annotations, 0..) |parameter_annotation, i| { - parameter_annotation_indent += 1; - defer parameter_annotation_indent -= 1; - - try w.splatByteAll(' ', depth * parameter_annotation_indent); - try w.print("{d}:\n", .{ i }); - try parameter_annotation.indentedFormat(w, depth, parameter_annotation_indent + 1, constant_pool); - } - }, - .runtime_invisible_parameter_annotations => |attr| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("parameter_annotations:\n"); - var parameter_annotation_indent = indent; - for (attr.parameter_annotations, 0..) |parameter_annotation, i| { - parameter_annotation_indent += 1; - defer parameter_annotation_indent -= 1; - - try w.splatByteAll(' ', depth * parameter_annotation_indent); - try w.print("{d}:\n", .{ i }); - try parameter_annotation.indentedFormat(w, depth, parameter_annotation_indent + 1, constant_pool); - } - }, - .runtime_visible_type_annotations => |attr| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("type_annotations:\n"); - var type_annotation_indent = indent; - for (attr.type_annotations, 0..) |type_annotation, i| { - type_annotation_indent += 1; - defer type_annotation_indent -= 1; - - try w.splatByteAll(' ', depth * type_annotation_indent); - try w.print("{d}:\n", .{ i }); - try type_annotation.indentedFormat(w, depth, type_annotation_indent + 1, constant_pool); - } - }, - .runtime_invisible_type_annotations => |attr| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("type_annotations:\n"); - var type_annotation_indent = indent; - for (attr.type_annotations, 0..) |type_annotation, i| { - type_annotation_indent += 1; - defer type_annotation_indent -= 1; - - try w.splatByteAll(' ', depth * type_annotation_indent); - try w.print("{d}:\n", .{ i }); - try type_annotation.indentedFormat(w, depth, type_annotation_indent + 1, constant_pool); - } - }, - .annotation_default => |attr| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("default_value:\n"); - try attr.default_value.indentedFormat(w, depth, indent + 1, constant_pool); - }, - .bootstrap_methods => |attr| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("bootstrap_methods:\n"); - var bootstrap_method_indent = indent; - for (attr.bootstrap_methods, 0..) |bootstrap_method, i| { - bootstrap_method_indent += 1; - defer bootstrap_method_indent -= 1; - - try w.splatByteAll(' ', depth * bootstrap_method_indent); - try w.print("{d}:\n", .{ i }); - try bootstrap_method.indentedFormat(w, depth, bootstrap_method_indent + 1, constant_pool); - } - }, - .method_parameters => |attr| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("parameters:\n"); - var parameter_indent = indent; - for (attr.parameters, 0..) |parameter, i| { - parameter_indent += 1; - defer parameter_indent -= 1; - - try w.splatByteAll(' ', depth * parameter_indent); - try w.print("{d}:\n", .{ i }); - try parameter.indentedFormat(w, depth, parameter_indent + 1, constant_pool); - } - }, - .module => |attr| try attr.indentedFormat(w, depth, indent, constant_pool), - .module_packages => |attr| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("packages:\n"); - var package_indent = indent; - for (attr.package_indeces, 0..) |package_index, i| { - package_indent += 1; - defer package_indent -= 1; - - const info = constant_pool.getTag(package_index, .package) catch return error.WriteFailed; - - try w.splatByteAll(' ', depth * package_indent); - try w.print("{d}:\n", .{ i }); - try info.indentedFormat(w, depth, package_indent + 1, constant_pool); - } - }, - .module_main_class => |attr| { - const main_class = attr.mainClass(constant_pool) catch return error.WriteFailed; - try w.splatByteAll(' ', depth * indent); - _ = try w.write("main_class:\n"); - try main_class.indentedFormat(w, depth, indent + 1, constant_pool); - }, - .nest_host => |attr| { - const host_class = attr.hostClass(constant_pool) catch return error.WriteFailed; - try w.splatByteAll(' ', depth * indent); - _ = try w.write("host_class:\n"); - try host_class.indentedFormat(w, depth, indent + 1, constant_pool); - }, - .nest_members => |attr| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("classes:\n"); - var class_indent = indent; - for (attr.classes, 0..) |class_index, i| { - class_indent += 1; - defer class_indent -= 1; - - const info = constant_pool.getTag(class_index, .class) catch return error.WriteFailed; - - try w.splatByteAll(' ', depth * class_indent); - try w.print("{d}:\n", .{ i }); - try info.indentedFormat(w, depth, class_indent + 1, constant_pool); - } - }, - .record => |attr| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("components:\n"); - var component_indent = indent; - for (attr.components, 0..) |component, i| { - component_indent += 1; - defer component_indent -= 1; - - try w.splatByteAll(' ', depth * component_indent); - try w.print("{d}:\n", .{ i }); - try component.indentedFormat(w, depth, component_indent + 1, constant_pool); - } - }, - .permitted_subclasses => |attr| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("classes:\n"); - var class_indent = indent; - for (attr.classes, 0..) |class_index, i| { - class_indent += 1; - defer class_indent -= 1; - - const info = constant_pool.getTag(class_index, .class) catch return error.WriteFailed; - - try w.splatByteAll(' ', depth * class_indent); - try w.print("{d}:\n", .{ i }); - try info.indentedFormat(w, depth, class_indent + 1, constant_pool); - } + pub fn deinit(self: *StackMapFrame, allocator: std.mem.Allocator) void { + switch (self.*) { + .append_frame => |frame| allocator.free(frame.locals), + .full_frame => |frame| { + allocator.free(frame.locals); + allocator.free(frame.stack); }, + else => {}, } + } + }; + + pub const VerificationTypeInfo = union(VerificationTypeInfo.Tag) { + top, + integer, + float, + double, + long, + null, + uninitialized_this, + object: struct { + cpool_index: u16, + }, + uninitialized: struct { + cpool_index: u16, }, - } - } -}; -pub const NewAttribute = struct { - attribute_name_index: u16, - info: []u8, + pub const Tag = enum(u8) { + top = 0, + integer = 1, + float = 2, + double = 3, + long = 4, + null = 5, + uninitialized_this = 6, + object = 7, + uninitialized = 8, + }; - const Self = @This(); + pub fn parse(input: *std.Io.Reader) ParseError!VerificationTypeInfo { + const tag_byte = try input.takeByte(); + const tag = std.enums.fromInt(Predefined.StackMapTable.VerificationTypeInfo.Tag, tag_byte) orelse return ParseError.InvalidTag; - pub fn parse(input: *std.Io.Reader, attribute_name_index: u16, attribute_length: u32, allocator: std.mem.Allocator) ParseError!Self { - return .{ - .attribute_name_index = attribute_name_index, - .info = try input.readAlloc(allocator, attribute_length), + return switch (tag) { + .top => .top, + .integer => .integer, + .float => .float, + .double => .double, + .long => .long, + .null => .null, + .uninitialized_this => .uninitialized_this, + .object => .{ + .object = .{ + .cpool_index = try input.takeInt(u16, .big), + }, + }, + .uninitialized => .{ + .uninitialized = .{ + .cpool_index = try input.takeInt(u16, .big), + }, + }, + }; + } }; - } + }; - pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { - allocator.free(self.info); - } + pub const Exceptions = struct { + exception_index_table: []u16, + }; - pub fn attributeName(self: *const Self, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { - return constant_pool.getTag(self.attribute_name_index, .utf8); - } -}; + pub const InnerClasses = struct { + classes: []InnerClass, -pub const ConstantValue = struct { - constant_value_index: u16, + pub const InnerClass = struct { + inner_class_info_index: u16, + outer_class_info_index: u16, + inner_name_index: u16, + inner_class_access_flags: InnerClassAccessFlags, - const Self = @This(); - - pub fn parse(input: *std.Io.Reader) ParseError!Self { - return .{ - .constant_value_index = try input.takeInt(u16, .big), - }; - } - - pub fn constantValue(self: *const Self, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { - return constant_pool.get(self.constant_value_index); - } -}; - -pub const Code = struct { - max_stack: u16, - max_locals: u16, - code: []u8, - 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 indentedFormat( - self: *const ExceptionHandler, - w: *std.Io.Writer, - depth: usize, - indent: u8, - constant_pool: ConstantPool, - ) std.Io.Writer.Error!void { - try w.splatByteAll(' ', depth * indent); - try w.print("start_pc: {d}\n", .{ self.start_pc }); - - try w.splatByteAll(' ', depth * indent); - try w.print("end_pc: {d}\n", .{ self.end_pc }); - - try w.splatByteAll(' ', depth * indent); - try w.print("handler_pc: {d}\n", .{ self.handler_pc }); - - const catch_type = self.catchType(constant_pool) catch return error.WriteFailed; - if (catch_type) |ct| { + pub fn format(self: *const InnerClass, w: *std.Io.Writer, depth: usize, indent: u8, constant_pool: []const ?ConstantPoolInfo) std.Io.Writer.Error!void { try w.splatByteAll(' ', depth * indent); - _ = try w.write("catch_type:\n"); - try ct.indentedFormat(w, depth, indent + 1, constant_pool); + try w.print("flags: {f}\n", .{ self.inner_class_access_flags }); + + const inner_class = self.innerClass(constant_pool) catch return error.WriteFailed; + try w.splatByteAll(' ', depth * indent); + _ = try w.write("inner_class:\n"); + try inner_class.format(w, depth, indent + 1, constant_pool); + + const outer_class = self.outerClass(constant_pool) catch return error.WriteFailed; + if (outer_class) |oc| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("outer_class:\n"); + try oc.format(w, depth, indent + 1, constant_pool); + } + + const inner_name = self.innerName(constant_pool) catch return error.WriteFailed; + if (inner_name) |in| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("inner_name:\n"); + try in.format(w, depth, indent + 1, constant_pool); + } } - } - pub fn catchType(self: *const ExceptionHandler, constant_pool: ConstantPool) ResolveError!?ConstantPool.Info { - return constant_pool.getTagOptional(self.catch_type, .class); - } - }; - - pub fn parse(input: *std.Io.Reader, constant_pool: ConstantPool, allocator: std.mem.Allocator) (ParseError || ResolveError)!Self { - const max_stack = try input.takeInt(u16, .big); - 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); - - const exception_table_length = try input.takeInt(u16, .big); - const exception_table = try allocator.alloc(ExceptionHandler, exception_table_length); - errdefer allocator.free(exception_table); - for (exception_table) |*exception_handler| { - exception_handler.* = .{ - .start_pc = try input.takeInt(u16, .big), - .end_pc = try input.takeInt(u16, .big), - .handler_pc = try input.takeInt(u16, .big), - .catch_type = try input.takeInt(u16, .big), - }; - } - - const attribute_count = try input.takeInt(u16, .big); - const attributes = try allocator.alloc(AttributeInfo, attribute_count); - errdefer allocator.free(attributes); - for (attributes) |*attribute| { - attribute.* = try .parse(input, constant_pool, allocator); - } - - return .{ - .max_stack = max_stack, - .max_locals = max_locals, - .code = code, - .exception_table = exception_table, - .attributes = attributes, - }; - } - - pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { - allocator.free(self.code); - allocator.free(self.exception_table); - for (self.attributes) |*attr| { - attr.deinit(allocator); - } - allocator.free(self.attributes); - } -}; - -pub const StackMapTable = struct { - entries: []StackMapFrame, - - const Self = @This(); - - pub const StackMapFrame = union(enum) { - // 0-63 - same_frame, - // 64-127 - same_locals_1_stack_item_frame: struct { - stack: [1]VerificationTypeInfo, - }, - // 247 - same_locals_1_stack_item_frame_extended: struct { - offset_delta: u16, - stack: [1]VerificationTypeInfo, - }, - // 248-250 - chop_frame: struct { - offset_delta: u16, - }, - // 251 - same_frame_extended: struct { - offset_delta: u16, - }, - // 252-254 - append_frame: struct { - offset_delta: u16, - locals: []const VerificationTypeInfo, - }, - // 255 - full_frame: struct { - offset_delta: u16, - locals: []const VerificationTypeInfo, - stack: []const VerificationTypeInfo, - }, - - pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!StackMapFrame { - const frame_type = try input.takeByte(); - return switch (frame_type) { - 0...63 => .same_frame, - 64...127 => .{ - .same_locals_1_stack_item_frame = .{ - .stack = .{ try .parse(input) }, - }, - }, - 247 => .{ - .same_locals_1_stack_item_frame_extended = .{ - .offset_delta = try input.takeInt(u16, .big), - .stack = .{ try .parse(input) }, - }, - }, - 248...250 => .{ - .chop_frame = .{ - .offset_delta = try input.takeInt(u16, .big), - }, - }, - 251 => .{ - .same_frame_extended = .{ - .offset_delta = try input.takeInt(u16, .big), - }, - }, - 252...254 => blk: { - const offset_delta = try input.takeInt(u16, .big); - - const length = frame_type - 251; - const locals = try allocator.alloc(StackMapTable.VerificationTypeInfo, length); - errdefer allocator.free(locals); - for (locals) |*local| { - local.* = try .parse(input); - } - - break :blk .{ - .append_frame = .{ - .offset_delta = offset_delta, - .locals = locals, - }, - }; - }, - 255 => blk: { - const offset_delta = try input.takeInt(u16, .big); - - const number_of_locals = try input.takeInt(u16, .big); - const locals = try allocator.alloc(StackMapTable.VerificationTypeInfo, number_of_locals); - errdefer allocator.free(locals); - for (locals) |*local| { - local.* = try .parse(input); - } - - const number_of_stack_items = try input.takeInt(u16, .big); - const stack = try allocator.alloc(StackMapTable.VerificationTypeInfo, number_of_stack_items); - errdefer allocator.free(stack); - for (stack) |*stack_item| { - stack_item.* = try .parse(input); - } - - break :blk .{ - .full_frame = .{ - .offset_delta = offset_delta, - .locals = locals, - .stack = stack, - }, - }; - }, - else => unreachable, - }; - } - - pub fn deinit(self: *StackMapFrame, allocator: std.mem.Allocator) void { - switch (self.*) { - .append_frame => |frame| allocator.free(frame.locals), - .full_frame => |frame| { - allocator.free(frame.locals); - allocator.free(frame.stack); - }, - else => {}, + pub fn innerClass(self: *const InnerClass, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const inner_class_info_index = self.inner_class_info_index - 1; + if (inner_class_info_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[inner_class_info_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; + return cp_info.?; } - } - pub fn indentedFormat( - self: *const StackMapFrame, - w: *std.Io.Writer, - depth: usize, - indent: u8, - constant_pool: ConstantPool, - ) std.Io.Writer.Error!void { - try w.splatByteAll(' ', depth * indent); - try w.print("frame_type: {s}\n", .{ @tagName(self.*) }); + pub fn outerClass(self: *const InnerClass, constant_pool: []const ?ConstantPoolInfo) ResolveError!?ConstantPoolInfo { + if (self.outer_class_info_index == 0) return null; + const outer_class_info_index = self.outer_class_info_index - 1; + if (outer_class_info_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - switch (self.*) { - .same_locals_1_stack_item_frame => |same_locals_1_stack_item_frame| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("stack:\n"); - var stack_indent = indent; - for (&same_locals_1_stack_item_frame.stack, 0..) |*info, i| { - stack_indent += 1; - defer stack_indent -= 1; - - try w.splatByteAll(' ', depth * stack_indent); - try w.print("{d}:\n", .{ i }); - - try info.indentedFormat(w, depth, stack_indent + 1, constant_pool); - } - }, - .same_locals_1_stack_item_frame_extended => |same_locals_1_stack_item_frame_extended| { - try w.splatByteAll(' ', depth * indent); - try w.print("offset_delta: {d}\n", .{ same_locals_1_stack_item_frame_extended.offset_delta }); - - try w.splatByteAll(' ', depth * indent); - _ = try w.write("stack:\n"); - var stack_indent = indent; - for (&same_locals_1_stack_item_frame_extended.stack, 0..) |*info, i| { - stack_indent += 1; - defer stack_indent -= 1; - - try w.splatByteAll(' ', depth * stack_indent); - try w.print("{d}:\n", .{ i }); - - try info.indentedFormat(w, depth, stack_indent + 1, constant_pool); - } - }, - .chop_frame => |chop_frame| { - try w.splatByteAll(' ', depth * indent); - try w.print("offset_delta: {d}\n", .{ chop_frame.offset_delta }); - }, - .append_frame => |append_frame| { - try w.splatByteAll(' ', depth * indent); - try w.print("offset_delta: {d}\n", .{ append_frame.offset_delta }); - - try w.splatByteAll(' ', depth * indent); - _ = try w.write("locals:\n"); - var locals_indent = indent; - for (append_frame.locals, 0..) |*info, i| { - locals_indent += 1; - defer locals_indent -= 1; - - try w.splatByteAll(' ', depth * locals_indent); - try w.print("{d}:\n", .{ i }); - - try info.indentedFormat(w, depth, locals_indent + 1, constant_pool); - } - }, - .full_frame => |full_frame| { - try w.splatByteAll(' ', depth * indent); - try w.print("offset_delta: {d}\n", .{ full_frame.offset_delta }); - - try w.splatByteAll(' ', depth * indent); - _ = try w.write("locals:\n"); - var locals_indent = indent; - for (full_frame.locals, 0..) |*info, i| { - locals_indent += 1; - defer locals_indent -= 1; - - try w.splatByteAll(' ', depth * locals_indent); - try w.print("{d}:\n", .{ i }); - - try info.indentedFormat(w, depth, locals_indent + 1, constant_pool); - } - - try w.splatByteAll(' ', depth * indent); - _ = try w.write("stack:\n"); - var stack_indent = indent; - for (full_frame.stack, 0..) |*info, i| { - stack_indent += 1; - defer stack_indent -= 1; - - try w.splatByteAll(' ', depth * stack_indent); - try w.print("{d}:\n", .{ i }); - - try info.indentedFormat(w, depth, stack_indent + 1, constant_pool); - } - }, - else => {}, + const cp_info = constant_pool[outer_class_info_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; + return cp_info.?; } - } - }; - pub const VerificationTypeInfo = union(VerificationTypeInfo.Tag) { - top, - integer, - float, - double, - long, - null, - uninitialized_this, - object: Object, - uninitialized: Uninitialized, + pub fn innerName(self: *const InnerClass, constant_pool: []const ?ConstantPoolInfo) ResolveError!?ConstantPoolInfo { + if (self.inner_name_index == 0) return null; + const inner_name_index = self.inner_name_index - 1; + if (inner_name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - pub const Tag = enum(u8) { - top = 0, - integer = 1, - float = 2, - double = 3, - long = 4, - null = 5, - uninitialized_this = 6, - object = 7, - uninitialized = 8, - }; - - pub const Object = struct { - cpool_index: u16, - - pub fn class(self: *const Object, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { - return constant_pool.getTag(self.cpool_index, .class); + const cp_info = constant_pool[inner_name_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; } }; - pub const Uninitialized = struct { - offset: u16, - }; - - pub fn parse(input: *std.Io.Reader) ParseError!VerificationTypeInfo { - const tag_byte = try input.takeByte(); - const tag = std.enums.fromInt(StackMapTable.VerificationTypeInfo.Tag, tag_byte) orelse return ParseError.InvalidTag; - - return switch (tag) { - .top => .top, - .integer => .integer, - .float => .float, - .double => .double, - .long => .long, - .null => .null, - .uninitialized_this => .uninitialized_this, - .object => .{ - .object = .{ - .cpool_index = try input.takeInt(u16, .big), - }, - }, - .uninitialized => .{ - .uninitialized = .{ - .offset = try input.takeInt(u16, .big), - }, - }, - }; - } - - pub fn indentedFormat( - self: *const VerificationTypeInfo, - w: *std.Io.Writer, - depth: usize, - indent: u8, - constant_pool: ConstantPool, - ) std.Io.Writer.Error!void { - try w.splatByteAll(' ', depth * indent); - try w.print("tag: {s}\n", .{ @tagName(self.*) }); - - switch (self.*) { - .object => |object| { - const class = object.class(constant_pool) catch return error.WriteFailed; - try w.splatByteAll(' ', depth * indent); - _ = try w.write("class:\n"); - try class.indentedFormat(w, depth, indent + 1, constant_pool); - }, - .uninitialized => |uninitialized| { - try w.splatByteAll(' ', depth * indent); - try w.print("offset: {d}\n", .{ uninitialized.offset }); - }, - else => {}, - } - } - }; - - pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { - const number_of_entries = try input.takeInt(u16, .big); - const entries = try allocator.alloc(StackMapTable.StackMapFrame, number_of_entries); - errdefer allocator.free(entries); - for (entries) |*entry| { - entry.* = try .parse(input, allocator); - } - - return .{ - .entries = entries, - }; - } - - pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { - for (self.entries) |*entry| { - entry.deinit(allocator); - } - allocator.free(self.entries); - } -}; - -pub const Exceptions = struct { - exception_index_table: []u16, - - const Self = @This(); - - pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { - const number_of_exceptions = try input.takeInt(u16, .big); - const exception_index_table = try allocator.alloc(u16, number_of_exceptions); - errdefer allocator.free(exception_index_table); - for (exception_index_table) |*exception_index| { - exception_index.* = try input.takeInt(u16, .big); - } - - return .{ - .exception_index_table = exception_index_table, - }; - } - - pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { - allocator.free(self.exception_index_table); - } -}; - -pub const InnerClasses = struct { - classes: []InnerClass, - - const Self = @This(); - - pub const InnerClass = struct { - inner_class_info_index: u16, - outer_class_info_index: u16, - inner_name_index: u16, - inner_class_access_flags: InnerClassAccessFlags, - - pub const InnerClassAccessFlags = EnumFlags(enum(u16) { + pub const InnerClassFlags = enum(u16) { public = 0x0001, private = 0x0002, protected = 0x0004, @@ -1146,1285 +307,719 @@ pub const InnerClasses = struct { synthetic = 0x1000, annotation = 0x2000, @"enum" = 0x4000, - }); + }; - pub fn indentedFormat( - self: *const InnerClass, - w: *std.Io.Writer, - depth: usize, - indent: u8, - constant_pool: ConstantPool, - ) std.Io.Writer.Error!void { - try w.splatByteAll(' ', depth * indent); - try w.print("flags: {f}\n", .{ self.inner_class_access_flags }); + pub const InnerClassAccessFlags = EnumFlags(InnerClassFlags); + }; - const inner_class = self.innerClass(constant_pool) catch return error.WriteFailed; - try w.splatByteAll(' ', depth * indent); - _ = try w.write("inner_class:\n"); - try inner_class.indentedFormat(w, depth, indent + 1, constant_pool); + pub const EnclosingMethod = struct { + class_index: u16, + method_index: u16, - const outer_class = self.outerClass(constant_pool) catch return error.WriteFailed; - if (outer_class) |oc| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("outer_class:\n"); - try oc.indentedFormat(w, depth, indent + 1, constant_pool); - } - - const inner_name = self.innerName(constant_pool) catch return error.WriteFailed; - if (inner_name) |in| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("inner_name:\n"); - try in.indentedFormat(w, depth, indent + 1, constant_pool); - } + pub fn class(self: *const EnclosingMethod, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const class_index = self.class_index - 1; + if (class_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[class_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; + return cp_info.?; } - pub fn innerClass(self: *const InnerClass, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { - return constant_pool.getTag(self.inner_class_info_index, .class); - } - - pub fn outerClass(self: *const InnerClass, constant_pool: ConstantPool) ResolveError!?ConstantPool.Info { - return constant_pool.getTagOptional(self.outer_class_info_index, .class); - } - - pub fn innerName(self: *const InnerClass, constant_pool: ConstantPool) ResolveError!?ConstantPool.Info { - return constant_pool.getTagOptional(self.inner_name_index, .utf8); + pub fn method(self: *const EnclosingMethod, constant_pool: []const ?ConstantPoolInfo) ResolveError!?ConstantPoolInfo { + if (self.method_index == 0) return null; + const method_index = self.method_index - 1; + if (method_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[method_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType; + return cp_info.?; } }; - pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { - const number_of_classes = try input.takeInt(u16, .big); - const classes = try allocator.alloc(InnerClass, number_of_classes); - errdefer allocator.free(classes); - for (classes) |*class| { - class.* = .{ - .inner_class_info_index = try input.takeInt(u16, .big), - .outer_class_info_index = try input.takeInt(u16, .big), - .inner_name_index = try input.takeInt(u16, .big), - .inner_class_access_flags = .from(try input.takeInt(u16, .big)), - }; - } + pub const Synthetic = struct { }; - return .{ - .classes = classes, - }; - } - - pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { - allocator.free(self.classes); - } -}; - -pub const EnclosingMethod = struct { - class_index: u16, - method_index: u16, - - const Self = @This(); - - pub fn parse(input: *std.Io.Reader) ParseError!Self { - return .{ - .class_index = try input.takeInt(u16, .big), - .method_index = try input.takeInt(u16, .big), - }; - } - - pub fn class(self: *const EnclosingMethod, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { - return constant_pool.getTag(self.class_index, .class); - } - - pub fn method(self: *const EnclosingMethod, constant_pool: ConstantPool) ResolveError!?ConstantPool.Info { - return constant_pool.getTagOptional(self.method_index, .name_and_type); - } -}; - -pub const Synthetic = struct { }; - -pub const Signature = struct { - signature_index: u16, - - const Self = @This(); - - pub fn parse(input: *std.Io.Reader) ParseError!Self { - return .{ - .signature_index = try input.takeInt(u16, .big), - }; - } - - pub fn signature(self: *const Signature, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { - return constant_pool.getTag(self.signature_index, .utf8); - } -}; - -pub const SourceFile = struct { - source_file_index: u16, - - const Self = @This(); - - pub fn parse(input: *std.Io.Reader) ParseError!Self { - return .{ - .source_file_index = try input.takeInt(u16, .big), - }; - } - - pub fn sourceFile(self: *const SourceFile, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { - return constant_pool.getTag(self.source_file_index, .utf8); - } -}; - -pub const SourceDebugExtension = struct { - debug_extension: []u8, - - const Self = @This(); - - pub fn parse(input: *std.Io.Reader, length: usize, allocator: std.mem.Allocator) ParseError!Self { - return .{ - .debug_extension = try input.readAlloc(allocator, length), - }; - } - - pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { - allocator.free(self.debug_extension); - } -}; - -pub const LineNumberTable = struct { - line_number_table: []const LineNumber, - - const Self = @This(); - - pub const LineNumber = struct { - start_pc: u16, - line_number: u16, - - pub fn indentedFormat( - self: *const LineNumber, - w: *std.Io.Writer, - depth: usize, - indent: u8, - ) std.Io.Writer.Error!void { - try w.splatByteAll(' ', depth * indent); - try w.print("start_pc: {d}\n", .{ self.start_pc }); - - try w.splatByteAll(' ', depth * indent); - try w.print("line_number: {d}\n", .{ self.line_number }); - } - }; - - pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { - const line_number_table_length = try input.takeInt(u16, .big); - const line_number_table = try allocator.alloc(LineNumber, line_number_table_length); - errdefer allocator.free(line_number_table); - for (line_number_table) |*line_number| { - line_number.* = .{ - .start_pc = try input.takeInt(u16, .big), - .line_number = try input.takeInt(u16, .big), - }; - } - - return .{ - .line_number_table = line_number_table, - }; - } - - pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { - allocator.free(self.line_number_table); - } -}; - -pub const LocalVariableTable = struct { - local_variable_table: []LocalVariable, - - const Self = @This(); - - pub const LocalVariable = struct { - start_pc: u16, - length: u16, - name_index: u16, - descriptor_index: u16, - index: u16, - - pub fn indentedFormat( - self: *const LocalVariable, - w: *std.Io.Writer, - depth: usize, - indent: u8, - constant_pool: ConstantPool, - ) std.Io.Writer.Error!void { - try w.splatByteAll(' ', depth * indent); - try w.print("start_pc: {d}\n", .{ self.start_pc }); - - try w.splatByteAll(' ', depth * indent); - try w.print("length: {d}\n", .{ self.length }); - - const name_ = self.name(constant_pool) catch return error.WriteFailed; - try w.splatByteAll(' ', depth * indent); - _ = try w.write("name:\n"); - try name_.indentedFormat(w, depth, indent + 1, constant_pool); - - const descriptor_ = self.descriptor(constant_pool) catch return error.WriteFailed; - try w.splatByteAll(' ', depth * indent); - _ = try w.write("descriptor:\n"); - try descriptor_.indentedFormat(w, depth, indent + 1, constant_pool); - } - - pub fn name(self: *const LocalVariable, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { - return constant_pool.getTag(self.name_index, .utf8); - } - - pub fn descriptor(self: *const LocalVariable, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { - return constant_pool.getTag(self.descriptor_index, .utf8); - } - }; - - pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { - const local_variable_table_length = try input.takeInt(u16, .big); - const local_variable_table = try allocator.alloc(LocalVariable, local_variable_table_length); - errdefer allocator.free(local_variable_table); - for (local_variable_table) |*local_variable| { - local_variable.* = .{ - .start_pc = try input.takeInt(u16, .big), - .length = try input.takeInt(u16, .big), - .name_index = try input.takeInt(u16, .big), - .descriptor_index = try input.takeInt(u16, .big), - .index = try input.takeInt(u16, .big), - }; - } - - return .{ - .local_variable_table = local_variable_table, - }; - } - - pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { - allocator.free(self.local_variable_table); - } -}; - -pub const LocalVariableTypeTable = struct { - local_variable_type_table: []LocalVariableType, - - const Self = @This(); - - pub const LocalVariableType = struct { - start_pc: u16, - length: u16, - name_index: u16, + pub const Signature = struct { signature_index: u16, - index: u16, - pub fn indentedFormat( - self: *const LocalVariableType, - w: *std.Io.Writer, - depth: usize, - indent: u8, - constant_pool: ConstantPool, - ) std.Io.Writer.Error!void { - try w.splatByteAll(' ', depth * indent); - try w.print("start_pc: {d}\n", .{ self.start_pc }); - - try w.splatByteAll(' ', depth * indent); - try w.print("length: {d}\n", .{ self.length }); - - const name_ = self.name(constant_pool) catch return error.WriteFailed; - try w.splatByteAll(' ', depth * indent); - _ = try w.write("name:\n"); - try name_.indentedFormat(w, depth, indent + 1, constant_pool); - - const signature_ = self.signature(constant_pool) catch return error.WriteFailed; - try w.splatByteAll(' ', depth * indent); - _ = try w.write("signature:\n"); - try signature_.indentedFormat(w, depth, indent + 1, constant_pool); - } - - pub fn name(self: *const LocalVariableType, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { - return constant_pool.getTag(self.name_index, .utf8); - } - - pub fn signature(self: *const LocalVariableType, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { - return constant_pool.getTag(self.signature_index, .utf8); + pub fn signature(self: *const Signature, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const signature_index = self.signature_index - 1; + if (signature_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[signature_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; } }; - pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { - const local_variable_type_table_length = try input.takeInt(u16, .big); - const local_variable_type_table = try allocator.alloc(LocalVariableType, local_variable_type_table_length); - errdefer allocator.free(local_variable_type_table); - for (local_variable_type_table) |*local_variable_type| { - local_variable_type.* = .{ - .start_pc = try input.takeInt(u16, .big), - .length = try input.takeInt(u16, .big), - .name_index = try input.takeInt(u16, .big), - .signature_index = try input.takeInt(u16, .big), - .index = try input.takeInt(u16, .big), - }; - } + pub const SourceFile = struct { + source_file_index: u16, - return .{ - .local_variable_type_table = local_variable_type_table, - }; - } - - pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { - allocator.free(self.local_variable_type_table); - } -}; - -pub const Deprecated = struct { }; - -pub const ElementValue = union(enum) { - // B, C, D, F, I, J, S, Z, s - const_value: ConstValue, - // e - enum_const_value: EnumConstValue, - // c - class_const_value: ClassConstValue, - // @ - annotation_value: Annotation, - // [ - array_value: struct { - values: []ElementValue, - }, - - pub const ConstValue = struct { - @"type": Type, - index: u16, - - pub const Type = enum(u8) { - byte = 'B', - char = 'C', - double = 'D', - float = 'F', - int = 'I', - long = 'J', - short = 'S', - boolean = 'Z', - string = 's', - }; - - pub fn value(self: *const ConstValue, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { - const value_ = try constant_pool.get(self.index); - return switch (self.@"type") { - .byte, .char, .int, .short, .boolean => blk: { - if (!value_.is(.integer)) break :blk ResolveError.InvalidConstantType; - break :blk value_; - }, - .double => blk: { - if (!value_.is(.double)) break :blk ResolveError.InvalidConstantType; - break :blk value_; - }, - .float => blk: { - if (!value_.is(.float)) break :blk ResolveError.InvalidConstantType; - break :blk value_; - }, - .long => blk: { - if (!value_.is(.long)) break :blk ResolveError.InvalidConstantType; - break :blk value_; - }, - .string => blk: { - if (!value_.is(.utf8)) break :blk ResolveError.InvalidConstantType; - break :blk value_; - }, - }; + pub fn sourceFile(self: *const SourceFile, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const source_file_index = self.source_file_index - 1; + if (source_file_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[source_file_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; } }; - pub const EnumConstValue = struct { - type_name_index: u16, - const_name_index: u16, - - pub fn descriptor(self: *const EnumConstValue, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { - return constant_pool.getTag(self.type_name_index, .utf8); - } - - pub fn name(self: *const EnumConstValue, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { - return constant_pool.getTag(self.const_name_index, .utf8); - } + pub const SourceDebugExtension = struct { + debug_extension: []u8, }; - pub const ClassConstValue = struct { - index: u16, + pub const LineNumberTable = struct { + line_number_table: []const LineNumber, - pub fn descriptor(self: *const ClassConstValue, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { - return constant_pool.getTag(self.index, .utf8); - } + pub const LineNumber = struct { + start_pc: u16, + line_number: u16, + }; }; - pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!ElementValue { - const tag = try input.takeByte(); - return switch (tag) { - 'B', 'C', 'D', 'F', 'I', 'J', 'S', 'Z', 's' => .{ - .const_value = .{ - .@"type" = @enumFromInt(tag), - .index = try input.takeInt(u16, .big), - }, - }, - 'e' => .{ - .enum_const_value = .{ - .type_name_index = try input.takeInt(u16, .big), - .const_name_index = try input.takeInt(u16, .big), - }, - }, - 'c' => .{ - .class_const_value = .{ - .index = try input.takeInt(u16, .big), - }, - }, - '@' => .{ - .annotation_value = try .parse(input, allocator), - }, - '[' => .{ - .array_value = blk: { - const num_values = try input.takeInt(u16, .big); - const values = try allocator.alloc(ElementValue, num_values); - errdefer allocator.free(values); - for (values) |*value| { - value.* = try .parse(input, allocator); - } + pub const LocalVariableTable = struct { + local_variable_table: []LocalVariable, - break :blk .{ - .values = values, - }; - }, - }, - else => unreachable, + pub const LocalVariable = struct { + start_pc: u16, + length: u16, + name_index: u16, + descriptor_index: u16, + index: u16, }; - } - - pub fn deinit(self: *ElementValue, allocator: std.mem.Allocator) void { - switch (self.*) { - .annotation_value => |*element_value| element_value.deinit(allocator), - .array_value => |element_value| { - for (element_value.values) |*value| { - value.deinit(allocator); - } - allocator.free(element_value.values); - }, - else => {}, - } - } - - pub fn indentedFormat( - self: *const ElementValue, - 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: {s}\n", .{ @tagName(self.*) }); - - switch (self.*) { - .const_value => |const_value| { - const value = const_value.value(constant_pool) catch return error.WriteFailed; - try w.splatByteAll(' ', depth * indent); - _ = try w.write("value:\n"); - try value.indentedFormat(w, depth, indent + 1, constant_pool); - }, - .enum_const_value => |enum_const_value| { - const descriptor = enum_const_value.descriptor(constant_pool) catch return error.WriteFailed; - try w.splatByteAll(' ', depth * indent); - _ = try w.write("descriptor:\n"); - try descriptor.indentedFormat(w, depth, indent + 1, constant_pool); - - const name = enum_const_value.name(constant_pool) catch return error.WriteFailed; - try w.splatByteAll(' ', depth * indent); - _ = try w.write("name:\n"); - try name.indentedFormat(w, depth, indent + 1, constant_pool); - }, - .class_const_value => |class_const_value| { - const descriptor = class_const_value.descriptor(constant_pool) catch return error.WriteFailed; - try w.splatByteAll(' ', depth * indent); - _ = try w.write("descriptor:\n"); - try descriptor.indentedFormat(w, depth, indent + 1, constant_pool); - }, - .annotation_value => |annotation_value| { - _ = try w.write("annotation:\n"); - try annotation_value.indentedFormat(w, depth, indent + 1, constant_pool); - }, - .array_value => |array_value| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("values:\n"); - var value_indent = indent; - for (array_value.values, 0..) |*value, i| { - value_indent += 1; - defer value_indent -= 1; - - try w.splatByteAll(' ', depth * value_indent); - try w.print("{d}:\n", .{ i }); - - try value.indentedFormat(w, depth, value_indent + 1, constant_pool); - } - }, - } - } -}; - -pub const ElementValuePair = struct { - element_name_index: u16, - value: ElementValue, - - const Self = @This(); - - pub fn indentedFormat( - self: *const ElementValuePair, - w: *std.Io.Writer, - depth: usize, - indent: u8, - constant_pool: ConstantPool, - ) std.Io.Writer.Error!void { - const element_name = self.elementName(constant_pool) catch return error.WriteFailed; - try w.splatByteAll(' ', depth * indent); - _ = try w.write("name:\n"); - try element_name.indentedFormat(w, depth, indent + 1, constant_pool); - - try w.splatByteAll(' ', depth * indent); - _ = try w.write("value:\n"); - try self.value.indentedFormat(w, depth, indent + 1, constant_pool); - } - - pub fn elementName(self: *const Self, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { - return constant_pool.getTag(self.element_name_index, .utf8); - } -}; - -pub const Annotation = struct { - type_index: u16, - element_value_pairs: []ElementValuePair, - - const Self = @This(); - - pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Annotation { - const type_index = try input.takeInt(u16, .big); - - const num_element_value_pairs = try input.takeInt(u16, .big); - const element_value_pairs = try allocator.alloc(ElementValuePair, num_element_value_pairs); - errdefer allocator.free(element_value_pairs); - for (element_value_pairs) |*element_value_pair| { - element_value_pair.* = .{ - .element_name_index = try input.takeInt(u16, .big), - .value = try .parse(input, allocator), - }; - } - - return .{ - .type_index = type_index, - .element_value_pairs = element_value_pairs, - }; - } - - pub fn deinit(self: *Annotation, allocator: std.mem.Allocator) void { - for (self.element_value_pairs) |*element_value_pair| { - element_value_pair.value.deinit(allocator); - } - allocator.free(self.element_value_pairs); - } - - pub fn indentedFormat( - self: *const Annotation, - w: *std.Io.Writer, - depth: usize, - indent: u8, - constant_pool: ConstantPool, - ) std.Io.Writer.Error!void { - const type_ = self.@"type"(constant_pool) 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.write("element_value_pairs:\n"); - var element_value_pair_indent = indent; - for (self.element_value_pairs, 0..) |element_value_pair, i| { - element_value_pair_indent += 1; - defer element_value_pair_indent -= 1; - - try w.splatByteAll(' ', depth * element_value_pair_indent); - try w.print("{d}:\n", .{ i }); - try element_value_pair.indentedFormat(w, depth, element_value_pair_indent + 1, constant_pool); - } - } - - pub fn @"type"(self: *const Self, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { - return constant_pool.getTag(self.type_index, .utf8); - } -}; - -pub const RuntimeVisibleAnnotations = struct { - annotations: []Annotation, - - const Self = @This(); - - pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { - const num_annotations = try input.takeInt(u16, .big); - const annotations = try allocator.alloc(Annotation, num_annotations); - errdefer allocator.free(annotations); - for (annotations) |*annotation| { - annotation.* = try .parse(input, allocator); - } - - return .{ - .annotations = annotations, - }; - } - - pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { - for (self.annotations) |*annotation| { - annotation.deinit(allocator); - } - allocator.free(self.annotations); - } -}; - -pub const RuntimeInvisibleAnnotations = struct { - annotations: []Annotation, - - const Self = @This(); - - pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { - const num_annotations = try input.takeInt(u16, .big); - const annotations = try allocator.alloc(Annotation, num_annotations); - errdefer allocator.free(annotations); - for (annotations) |*annotation| { - annotation.* = try .parse(input, allocator); - } - - return .{ - .annotations = annotations, - }; - } - - pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { - for (self.annotations) |*annotation| { - annotation.deinit(allocator); - } - allocator.free(self.annotations); - } -}; - -pub const ParameterAnnotation = struct { - annotations: []Annotation, - - pub fn indentedFormat( - self: *const ParameterAnnotation, - w: *std.Io.Writer, - depth: usize, - indent: u8, - constant_pool: ConstantPool, - ) std.Io.Writer.Error!void { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("annotations:\n"); - var annotation_indent = indent; - for (self.annotations, 0..) |annotation, i| { - annotation_indent += 1; - defer annotation_indent -= 1; - - try w.splatByteAll(' ', depth * annotation_indent); - try w.print("{d}:\n", .{ i }); - try annotation.indentedFormat(w, depth, annotation_indent + 1, constant_pool); - } - } -}; - -pub const RuntimeVisibleParameterAnnotations = struct { - parameter_annotations: []ParameterAnnotation, - - const Self = @This(); - - pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { - const num_parameters = try input.takeByte(); - const parameter_annotations = try allocator.alloc(ParameterAnnotation, num_parameters); - errdefer allocator.free(parameter_annotations); - for (parameter_annotations) |*parameter_annotation| { - const num_annotations = try input.takeInt(u16, .big); - const annotations = try allocator.alloc(Annotation, num_annotations); - errdefer allocator.free(annotations); - for (annotations) |*annotation| { - annotation.* = try .parse(input, allocator); - } - - parameter_annotation.* = .{ - .annotations = annotations, - }; - } - - return .{ - .parameter_annotations = parameter_annotations, - }; - } - - pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { - for (self.parameter_annotations) |parameter_annotation| { - for (parameter_annotation.annotations) |*annotation| { - annotation.deinit(allocator); - } - allocator.free(parameter_annotation.annotations); - } - allocator.free(self.parameter_annotations); - } -}; - -pub const RuntimeInvisibleParameterAnnotations = struct { - parameter_annotations: []ParameterAnnotation, - - const Self = @This(); - - pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { - const num_parameters = try input.takeByte(); - const parameter_annotations = try allocator.alloc(ParameterAnnotation, num_parameters); - errdefer allocator.free(parameter_annotations); - for (parameter_annotations) |*parameter_annotation| { - const num_annotations = try input.takeInt(u16, .big); - const annotations = try allocator.alloc(Annotation, num_annotations); - errdefer allocator.free(annotations); - for (annotations) |*annotation| { - annotation.* = try .parse(input, allocator); - } - - parameter_annotation.* = .{ - .annotations = annotations, - }; - } - - return .{ - .parameter_annotations = parameter_annotations, - }; - } - - pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { - for (self.parameter_annotations) |parameter_annotation| { - for (parameter_annotation.annotations) |*annotation| { - annotation.deinit(allocator); - } - allocator.free(parameter_annotation.annotations); - } - allocator.free(self.parameter_annotations); - } -}; - -pub const TypeAnnotation = struct { - target_type: TargetType, - target_info: TargetInfo, - target_path: TypePath, - type_index: u16, - element_value_pairs: []ElementValuePair, - - const Self = @This(); - - pub const TargetType = enum(u8) { - /// type parameter declaration of generic class or interface - type_parameter_generic_class_interface = 0x00, - /// type parameter declaration of generic method or constructor - type_parameter_generic_method_constructor = 0x01, - /// type in extends or implements clause of class declaration - /// (including the direct superclass or direct superinterface of an anonymous class declaration), - /// or in extends clause of interface declaration - type_in_extends_implements = 0x10, - /// type in bound of type parameter declaration of generic class or interface - type_in_bound_generic_class_interface = 0x11, - /// type in bound of type parameter declaration of generic method or constructor - type_in_bound_generic_method_constructor = 0x12, - /// type in field or record component declaration - type_in_field_record_component = 0x13, - /// return type of method, or type of newly constructed object - return_type_method_newly_constructed_object = 0x14, - /// receiver type of method or constructor - receiver_type_method_constructor = 0x15, - /// type in formal parameter declaration of method, constructor, or lambda expression - type_in_formal_parameter_declaration_method_constructor_lambda = 0x16, - /// type in throws clause of method or constructor - type_in_throws_method_constructor = 0x17, - /// type in local variable declaration - type_in_local_variable = 0x40, - /// type in resource variable declaration - type_in_resource_variable = 0x41, - /// type in exception parameter declaration - type_in_exception_parameter = 0x42, - /// type in instanceof expression - type_in_instanceof = 0x43, - /// type in new expression - type_in_new = 0x44, - /// type in method reference expression using ::new - type_in_method_reference_new = 0x45, - /// type in method reference expression using ::Identifier - type_in_method_reference_identifier = 0x46, - /// type in cast expression - type_in_cast = 0x47, - /// type argument for generic constructor in new expression or explicit constructor invocation statement - type_generic_constructor_new_explicit = 0x48, - /// type argument for generic method in method invocation expression - type_generic_method_invocation = 0x49, - /// type argument for generic constructor in method reference expression using ::new - type_generic_constructor_method_reference_new = 0x4a, - /// type argument for generic method in method reference expression using ::Identifier - type_generic_method_method_reference_identifier = 0x4b, }; - pub const TargetInfo = union(enum) { - type_parameter: TypeParameter, - supertype: SuperType, - type_parameter_bound: TypeParameterBound, - empty: Empty, - formal_parameter: FormalParameter, - throws: Throws, - localvar: Localvar, - @"catch": Catch, - offset: Offset, - type_argument: TypeArgument, + pub const LocalVariableTypeTable = struct { + local_variable_type_table: []LocalVariableType, - pub const TypeParameter = struct { - type_parameter_index: u8, + pub const LocalVariableType = struct { + start_pc: u16, + length: u16, + name_index: u16, + signature_index: u16, + index: u16, }; + }; - pub const SuperType = struct { - supertype_index: u16, - }; + pub const Deprecated = struct { }; - pub const TypeParameterBound = struct { - type_parameter_index: u8, - bound_index: u8, - }; + pub const ElementValue = union(enum) { + // B, C, D, F, I, J, S, Z, s + const_value_index: u16, + // e + enum_const_value: struct { + type_name_index: u16, + const_name_index: u16, + }, + // c + class_info_index: u16, + // @ + annotation_value: Annotation, + // [ + array_value: struct { + values: []ElementValue, + }, - pub const Empty = struct { }; + pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!ElementValue { + const tag = try input.takeByte(); + return switch (tag) { + 'B', 'C', 'D', 'F', 'I', 'J', 'S', 'Z', 's' => .{ + .const_value_index = try input.takeInt(u16, .big), + }, + 'e' => .{ + .enum_const_value = .{ + .type_name_index = try input.takeInt(u16, .big), + .const_name_index = try input.takeInt(u16, .big), + }, + }, + 'c' => .{ + .class_info_index = try input.takeInt(u16, .big), + }, + '@' => .{ + .annotation_value = try .parse(input, allocator), + }, + '[' => .{ + .array_value = blk: { + const num_values = try input.takeInt(u16, .big); + const values = try allocator.alloc(ElementValue, num_values); + errdefer allocator.free(values); + for (values) |*value| { + value.* = try .parse(input, allocator); + } - pub const FormalParameter = struct { - formal_parameter_index: u8, - }; - - pub const Throws = struct { - throws_type_index: u16, - }; - - pub const Localvar = struct { - table: []Entry, - - pub const Entry = struct { - start_pc: u16, - length: u16, - index: u16, - - pub fn indentedFormat( - self: *const Entry, - w: *std.Io.Writer, - depth: usize, - indent: u8, - ) std.Io.Writer.Error!void { - try w.splatByteAll(' ', depth * indent); - try w.print("start_pc: {d}\n", .{ self.start_pc }); - - try w.splatByteAll(' ', depth * indent); - try w.print("length: {d}\n", .{ self.length }); - - try w.splatByteAll(' ', depth * indent); - try w.print("start_pc: {d}\n", .{ self.index }); - } + break :blk .{ + .values = values, + }; + }, + }, + else => unreachable, }; - }; - - pub const Catch = struct { - exception_table_index: u16, - }; - - pub const Offset = struct { - offset: u16, - }; - - pub const TypeArgument = struct { - offset: u16, - type_argument_index: u8, - }; - - pub fn indentedFormat( - self: *const TargetInfo, - w: *std.Io.Writer, - depth: usize, - indent: u8, - ) std.Io.Writer.Error!void { - try w.splatByteAll(' ', depth * indent); - try w.print("target_info_type: {t}\n", .{ self.* }); + } + pub fn deinit(self: *ElementValue, allocator: std.mem.Allocator) void { switch (self.*) { - .type_parameter => |*type_parameter| { - try w.splatByteAll(' ', depth * indent); - try w.print("type_parameter_index: {d}\n", .{ type_parameter.type_parameter_index }); - }, - .supertype => |*supertype| { - try w.splatByteAll(' ', depth * indent); - try w.print("supertype_index: {d}\n", .{ supertype.supertype_index }); - }, - .type_parameter_bound => |*type_parameter_bound| { - try w.splatByteAll(' ', depth * indent); - try w.print("type_parameter_index: {d}\n", .{ type_parameter_bound.type_parameter_index }); - - try w.splatByteAll(' ', depth * indent); - try w.print("bound_index: {d}\n", .{ type_parameter_bound.bound_index }); - }, - .formal_parameter => |*formal_parameter| { - try w.splatByteAll(' ', depth * indent); - try w.print("formal_parameter_index: {d}\n", .{ formal_parameter.formal_parameter_index }); - }, - .throws => |*throws| { - try w.splatByteAll(' ', depth * indent); - try w.print("throws_type_index: {d}\n", .{ throws.throws_type_index }); - }, - .localvar => |*localvar| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("table:\n"); - var entry_indent = indent; - for (localvar.table, 0..) |*entry, i| { - entry_indent += 1; - defer entry_indent -= 1; - - try w.splatByteAll(' ', depth * entry_indent); - try w.print("{d}:\n", .{ i }); - - try entry.indentedFormat(w, depth, indent); + .annotation_value => |*element_value| element_value.deinit(allocator), + .array_value => |element_value| { + for (element_value.values) |*value| { + value.deinit(allocator); } - }, - .@"catch" => |*@"catch"| { - try w.splatByteAll(' ', depth * indent); - try w.print("exception_table_index: {d}\n", .{ @"catch".exception_table_index }); - }, - .offset => |*offset| { - try w.splatByteAll(' ', depth * indent); - try w.print("offset: {d}\n", .{ offset.offset }); - }, - .type_argument => |*type_argument| { - try w.splatByteAll(' ', depth * indent); - try w.print("offset: {d}\n", .{ type_argument.offset }); - - try w.splatByteAll(' ', depth * indent); - try w.print("type_argument_index: {d}\n", .{ type_argument.type_argument_index }); + allocator.free(element_value.values); }, else => {}, } } }; - pub const TypePath = struct { - paths: []Path, + pub const Annotation = struct { + type_index: u16, + element_value_pairs: []ElementValuePair, - pub const Path = struct { - type_path_kind: u8, - type_argument_index: u8, - - pub fn indentedFormat( - self: *const Path, - w: *std.Io.Writer, - depth: usize, - indent: u8, - ) std.Io.Writer.Error!void { - try w.splatByteAll(' ', depth * indent); - try w.print("type_path_kind: {d}\n", .{ self.type_path_kind }); - - try w.splatByteAll(' ', depth * indent); - try w.print("type_argument_index: {d}\n", .{ self.type_argument_index }); - } + pub const ElementValuePair = struct { + element_name_index: u16, + value: ElementValue, }; - pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!TypePath { - const length = try input.takeByte(); - const paths = try allocator.alloc(Path, length); - errdefer allocator.free(paths); - for (paths) |*path| { - path.* = .{ - .type_path_kind = try input.takeByte(), - .type_argument_index = try input.takeByte(), + pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Annotation { + const type_index = try input.takeInt(u16, .big); + + const num_element_value_pairs = try input.takeInt(u16, .big); + const element_value_pairs = try allocator.alloc(ElementValuePair, num_element_value_pairs); + errdefer allocator.free(element_value_pairs); + for (element_value_pairs) |*element_value_pair| { + element_value_pair.* = .{ + .element_name_index = try input.takeInt(u16, .big), + .value = try .parse(input, allocator), }; } return .{ - .paths = paths, + .type_index = type_index, + .element_value_pairs = element_value_pairs, }; } - pub fn deinit(self: *TypePath, allocator: std.mem.Allocator) void { - allocator.free(self.paths); - } - - pub fn indentedFormat( - self: *const TypePath, - w: *std.Io.Writer, - depth: usize, - indent: u8, - ) std.Io.Writer.Error!void { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("paths:\n"); - var path_indent = indent; - for (self.paths, 0..) |path, i| { - path_indent += 1; - defer path_indent -= 1; - - try w.splatByteAll(' ', depth * path_indent); - try w.print("{d}:\n", .{ i }); - try path.indentedFormat(w, depth, path_indent + 1); + pub fn deinit(self: *Annotation, allocator: std.mem.Allocator) void { + for (self.element_value_pairs) |*element_value_pair| { + element_value_pair.value.deinit(allocator); } + allocator.free(self.element_value_pairs); } }; - pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { - const target_type: TargetType = @enumFromInt(try input.takeInt(u8, .big)); - const target_info: TargetInfo = switch (target_type) { - .type_parameter_generic_class_interface, .type_parameter_generic_method_constructor => .{ - .type_parameter = .{ - .type_parameter_index = try input.takeByte(), - }, - }, - .type_in_extends_implements => .{ - .supertype = .{ - .supertype_index = try input.takeInt(u16, .big), - }, - }, - .type_in_bound_generic_class_interface, .type_in_bound_generic_method_constructor => .{ - .type_parameter_bound = .{ - .type_parameter_index = try input.takeByte(), - .bound_index = try input.takeByte(), - }, - }, - .type_in_field_record_component, .return_type_method_newly_constructed_object, .receiver_type_method_constructor => .{ - .empty = .{}, - }, - .type_in_formal_parameter_declaration_method_constructor_lambda => .{ - .formal_parameter = .{ - .formal_parameter_index = try input.takeByte(), - }, - }, - .type_in_throws_method_constructor => .{ - .throws = .{ - .throws_type_index = try input.takeInt(u16, .big), - }, - }, - .type_in_local_variable, .type_in_resource_variable => blk: { - const table_length = try input.takeInt(u16, .big); - const table = try allocator.alloc(TargetInfo.Localvar.Entry, table_length); - errdefer allocator.free(table); - for (table) |*entry| { - entry.* = .{ - .start_pc = try input.takeInt(u16, .big), - .length = try input.takeInt(u16, .big), - .index = try input.takeInt(u16, .big), - }; + pub const RuntimeVisibleAnnotations = struct { + annotations: []Annotation, + }; + + pub const RuntimeInvisibleAnnotations = struct { + annotations: []Annotation, + }; + + pub const ParameterAnnotation = struct { + annotations: []Annotation, + }; + + pub const RuntimeVisibleParameterAnnotations = struct { + parameter_annotations: []ParameterAnnotation, + }; + + pub const RuntimeInvisibleParameterAnnotations = struct { + parameter_annotations: []ParameterAnnotation, + }; + + pub const AnnotationDefault = struct { + default_value: ElementValue, + }; + + pub const BootstrapMethods = struct { + bootstrap_methods: []BootstrapMethod, + + pub const BootstrapMethod = struct { + bootstrap_method_ref: u16, + bootstrap_arguments: []u16, + }; + }; + + pub fn deinit(self: *Predefined, allocator: std.mem.Allocator) void { + switch (self.*) { + .code => |attribute| { + allocator.free(attribute.code); + allocator.free(attribute.exception_table); + for (attribute.attributes) |*attr| { + attr.deinit(allocator); } - - break :blk .{ - .localvar = .{ - .table = table, - }, - }; + allocator.free(attribute.attributes); }, - .type_in_exception_parameter => .{ - .@"catch" = . { - .exception_table_index = try input.takeInt(u16, .big), - }, + .stack_map_table => |attribute| { + for (attribute.entries) |*entry| { + entry.deinit(allocator); + } + allocator.free(attribute.entries); }, - .type_in_instanceof, .type_in_new, .type_in_method_reference_new, .type_in_method_reference_identifier => .{ - .offset = .{ - .offset = try input.takeInt(u16, .big), - }, + .exceptions => |attribute| allocator.free(attribute.exception_index_table), + .inner_classes => |attribute| allocator.free(attribute.classes), + .source_debug_extension => |attribute| allocator.free(attribute.debug_extension), + .line_number_table => |attribute| allocator.free(attribute.line_number_table), + .local_variable_table => |attribute| allocator.free(attribute.local_variable_table), + .local_variable_type_table => |attribute| allocator.free(attribute.local_variable_type_table), + .runtime_visible_annotations => |attribute| { + for (attribute.annotations) |*annotation| { + annotation.deinit(allocator); + } + allocator.free(attribute.annotations); }, - .type_in_cast, .type_generic_constructor_new_explicit, .type_generic_method_invocation, .type_generic_constructor_method_reference_new, .type_generic_method_method_reference_identifier => .{ - .type_argument = .{ - .offset = try input.takeInt(u16, .big), - .type_argument_index = try input.takeByte(), - }, + .runtime_invisible_annotations => |attribute| { + for (attribute.annotations) |*annotation| { + annotation.deinit(allocator); + } + allocator.free(attribute.annotations); + }, + .runtime_visible_parameter_annotations => |attribute| { + for (attribute.parameter_annotations) |parameter_annotation| { + for (parameter_annotation.annotations) |*annotation| { + annotation.deinit(allocator); + } + allocator.free(parameter_annotation.annotations); + } + allocator.free(attribute.parameter_annotations); + }, + .runtime_invisible_parameter_annotations => |attribute| { + for (attribute.parameter_annotations) |parameter_annotation| { + for (parameter_annotation.annotations) |*annotation| { + annotation.deinit(allocator); + } + allocator.free(parameter_annotation.annotations); + } + allocator.free(attribute.parameter_annotations); + }, + .bootstrap_methods => |attribute| { + for (attribute.bootstrap_methods) |bootstrap_method| { + allocator.free(bootstrap_method.bootstrap_arguments); + } + allocator.free(attribute.bootstrap_methods); }, - }; - - var target_path: TypePath = try .parse(input, allocator); - errdefer target_path.deinit(allocator); - - const type_index = try input.takeInt(u16, .big); - - const num_element_value_pairs = try input.takeInt(u16, .big); - const element_value_pairs = try allocator.alloc(ElementValuePair, num_element_value_pairs); - errdefer allocator.free(element_value_pairs); - for (element_value_pairs) |*element_value_pair| { - element_value_pair.* = .{ - .element_name_index = try input.takeInt(u16, .big), - .value = try .parse(input, allocator), - }; - } - - return .{ - .target_type = target_type, - .target_info = target_info, - .target_path = target_path, - .type_index = type_index, - .element_value_pairs = element_value_pairs, - }; - } - - pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { - switch (self.target_info) { - .localvar => |localvar| allocator.free(localvar.table), else => {}, } - for (self.element_value_pairs) |*element_value_pair| { - element_value_pair.value.deinit(allocator); - } - allocator.free(self.element_value_pairs); - } - - 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("target_type: {t}\n", .{ self.target_type }); - - try w.splatByteAll(' ', depth * indent); - _ = try w.write("target_info:\n"); - try self.target_info.indentedFormat(w, depth + 1, indent); - - try w.splatByteAll(' ', depth * indent); - _ = try w.write("target_path:\n"); - try self.target_path.indentedFormat(w, depth + 1, indent); - - const type_ = self.@"type"(constant_pool) 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.write("element_value_pairs:\n"); - var element_value_pair_indent = indent; - for (self.element_value_pairs, 0..) |element_value_pair, i| { - element_value_pair_indent += 1; - defer element_value_pair_indent -= 1; - - try w.splatByteAll(' ', depth * element_value_pair_indent); - try w.print("{d}:\n", .{ i }); - try element_value_pair.indentedFormat(w, depth, element_value_pair_indent + 1, constant_pool); - } - } - - pub fn @"type"(self: *const Self, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { - return constant_pool.getTag(self.type_index, .utf8); } }; -pub const RuntimeVisibleTypeAnnotations = struct { - type_annotations: []TypeAnnotation, - - const Self = @This(); - - pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { - const num_annotations = try input.takeInt(u16, .big); - const type_annotations = try allocator.alloc(TypeAnnotation, num_annotations); - errdefer allocator.free(type_annotations); - for (type_annotations) |*type_annotation| { - type_annotation.* = try .parse(input, allocator); - } - - return .{ - .type_annotations = type_annotations, - }; - } - - pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { - for (self.type_annotations) |*type_annotation| { - type_annotation.deinit(allocator); - } - allocator.free(self.type_annotations); - } -}; - -pub const RuntimeInvisibleTypeAnnotations = struct { - type_annotations: []TypeAnnotation, - - const Self = @This(); - - pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { - const num_annotations = try input.takeInt(u16, .big); - const type_annotations = try allocator.alloc(TypeAnnotation, num_annotations); - errdefer allocator.free(type_annotations); - for (type_annotations) |*type_annotation| { - type_annotation.* = try .parse(input, allocator); - } - - return .{ - .type_annotations = type_annotations, - }; - } - - pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { - for (self.type_annotations) |*type_annotation| { - type_annotation.deinit(allocator); - } - allocator.free(self.type_annotations); - } -}; - -pub const AnnotationDefault = struct { - default_value: ElementValue, - - const Self = @This(); - - pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { - return .{ - .default_value = try .parse(input, allocator), - }; - } - - pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { - self.default_value.deinit(allocator); - } -}; - -pub const BootstrapMethods = struct { - bootstrap_methods: []BootstrapMethod, - - const Self = @This(); - - pub const BootstrapMethod = struct { - bootstrap_method_ref: u16, - bootstrap_arguments: []u16, - - pub fn indentedFormat( - self: *const BootstrapMethod, - w: *std.Io.Writer, - depth: usize, - indent: u8, - constant_pool: ConstantPool, - ) std.Io.Writer.Error!void { - const bootstrap_method = self.bootstrapMethod(constant_pool) catch return error.WriteFailed; - try w.splatByteAll(' ', depth * indent); - _ = try w.write("bootstrap_method:\n"); - try bootstrap_method.indentedFormat(w, depth, indent + 1, constant_pool); - - try w.splatByteAll(' ', depth * indent); - _ = try w.write("bootstrap_arguments:\n"); - var bootstrap_argument_indent = indent; - for (self.bootstrap_arguments, 0..) |bootstrap_argument, i| { - bootstrap_argument_indent += 1; - defer bootstrap_argument_indent -= 1; - - const info = constant_pool.getLoadable(bootstrap_argument) catch return error.WriteFailed; - - try w.splatByteAll(' ', depth * bootstrap_argument_indent); - try w.print("{d}:\n", .{ i }); - try info.indentedFormat(w, depth, bootstrap_argument_indent + 1, constant_pool); - } - } - - pub fn bootstrapMethod(self: *const BootstrapMethod, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { - return constant_pool.getTag(self.bootstrap_method_ref, .method_handle); - } +pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!AttributeInfo { + const attribute_name_index = try input.takeInt(u16, .big); + const attribute_length = try input.takeInt(u32, .big); + const info = try input.readAlloc(allocator, attribute_length); + return .{ + .attribute_name_index = attribute_name_index, + .info = info, }; +} - pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { - const num_bootstrap_methods = try input.takeInt(u16, .big); - const bootstrap_methods = try allocator.alloc(BootstrapMethod, num_bootstrap_methods); +pub fn deinit(self: *AttributeInfo, allocator: std.mem.Allocator) void { + allocator.free(self.info); +} + +pub fn format(self: *const AttributeInfo, w: *std.Io.Writer, depth: usize, indent: u8, allocator: std.mem.Allocator, constant_pool: []const ?ConstantPoolInfo) std.Io.Writer.Error!void { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("name:\n"); + const attribute_name = self.attributeName(constant_pool) catch return error.WriteFailed; + try attribute_name.format(w, depth, indent + 1, constant_pool); + + var predefined = self.resolve(allocator, constant_pool) catch return error.WriteFailed; + if (predefined) |*p| { + defer p.deinit(allocator); + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("predefined: yes\n"); + + try w.splatByteAll(' ', depth * indent); + try w.print("type: {s}\n", .{ @tagName(p.*) }); + + switch (p.*) { + .constant_value => |attr| { + const constant_value = attr.constantValue(constant_pool) catch return error.WriteFailed; + try constant_value.format(w, depth, indent + 1, constant_pool); + }, + .code => |attr| { + try w.splatByteAll(' ', depth * indent); + try w.print("max_stack: {d}\n", .{ attr.max_stack }); + + try w.splatByteAll(' ', depth * indent); + try w.print("max_locals: {d}\n", .{ attr.max_locals }); + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("code:\n"); + var instr_indent = indent; + for (attr.code, 0..) |instr, i| { + instr_indent += 1; + defer instr_indent -= 1; + + try w.splatByteAll(' ', depth * instr_indent); + 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; + + // try w.splatByteAll(' ', depth * exception_indent); + // try w.print("{d}:\n", .{ i }); + + // try self.formatExceptionHandler(w, depth, exception_indent + 1, exception); + //} + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("attributes:\n"); + var attr_indent = indent; + for (attr.attributes, 0..) |*a, i| { + attr_indent += 1; + defer attr_indent -= 1; + + try w.splatByteAll(' ', depth * attr_indent); + try w.print("{d}:\n", .{ i }); + + try a.format(w, depth, attr_indent + 1, allocator, constant_pool); + } + }, + //.stack_map_table, + .exceptions => |attr| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("exceptions:\n"); + var exception_indent = indent; + for (attr.exception_index_table, 0..) |exception, i| { + exception_indent += 1; + defer exception_indent -= 1; + + if (exception >= constant_pool.len) return error.WriteFailed; + const cp_info = constant_pool[exception - 1]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return error.WriteFailed; + + try w.splatByteAll(' ', depth * exception_indent); + try w.print("{d}:\n", .{ i }); + try cp_info.?.format(w, depth, exception_indent + 1, constant_pool); + } + }, + .inner_classes => |attr| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("classes:\n"); + var class_indent = indent; + for (attr.classes, 0..) |*class, i| { + class_indent += 1; + defer class_indent -= 1; + + try w.splatByteAll(' ', depth * class_indent); + try w.print("{d}:\n", .{ i }); + try class.format(w, depth, class_indent + 1, constant_pool); + } + }, + .enclosing_method => |attr| { + const class = attr.class(constant_pool) catch return error.WriteFailed; + try w.splatByteAll(' ', depth * indent); + _ = try w.write("class:\n"); + try class.format(w, depth, indent + 1, constant_pool); + + const method = attr.method(constant_pool) catch return error.WriteFailed; + if (method) |m| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("method:\n"); + try m.format(w, depth, indent + 1, constant_pool); + } + }, + .synthetic => {}, + .signature => |attr| { + const signature = attr.signature(constant_pool) catch return error.WriteFailed; + try signature.format(w, depth, indent + 1, constant_pool); + }, + .source_file => |attr| { + const source_file = attr.sourceFile(constant_pool) catch return error.WriteFailed; + try source_file.format(w, depth, indent + 1, constant_pool); + }, + //.source_debug_extension, + //.line_number_table, + //.local_variable_table, + //.local_variable_type_table, + .deprecated => {}, + //.runtime_visible_annotations, + //.runtime_invisible_annotations, + //.runtime_visible_parameter_annotations, + //.runtime_invisible_parameter_annotations, + //.annotation_default, + //.bootstrap_methods, + else => {}, + } + } else { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("predefined: no\n"); + } +} + +pub fn attributeName(self: *const AttributeInfo, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const attribute_name_index = self.attribute_name_index - 1; + if (attribute_name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[attribute_name_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; +} + +pub fn resolve(self: *const AttributeInfo, allocator: std.mem.Allocator, constant_pool: []const ?ConstantPoolInfo) (ResolveError || ParseError)!?Predefined { + const attribute_name = (try self.attributeName(constant_pool)).utf8; + + var r: std.Io.Reader = .fixed(self.info); + + const eql = std.mem.eql; + if (eql(u8, attribute_name.bytes, "ConstantValue")) { + return .{ + .constant_value = .{ + .constant_value_index = try r.takeInt(u16, .big), + }, + }; + } else if (eql(u8, attribute_name.bytes, "Code")) { + const max_stack = try r.takeInt(u16, .big); + const max_locals = try r.takeInt(u16, .big); + + const code_length = try r.takeInt(u32, .big); + const code = try r.readAlloc(allocator, code_length); + errdefer allocator.free(code); + + const exception_table_length = try r.takeInt(u16, .big); + const exception_table = try allocator.alloc(Predefined.Code.ExceptionHandler, exception_table_length); + errdefer allocator.free(exception_table); + for (exception_table) |*exception_handler| { + exception_handler.* = .{ + .start_pc = try r.takeInt(u16, .big), + .end_pc = try r.takeInt(u16, .big), + .handler_pc = try r.takeInt(u16, .big), + .catch_type = try r.takeInt(u16, .big), + }; + } + + const attribute_count = try r.takeInt(u16, .big); + const attributes = try allocator.alloc(AttributeInfo, attribute_count); + errdefer allocator.free(attributes); + for (attributes) |*attribute| { + attribute.* = try .parse(&r, allocator); + } + + return .{ + .code = .{ + .max_stack = max_stack, + .max_locals = max_locals, + .code = code, + .exception_table = exception_table, + .attributes = attributes, + }, + }; + } else if (eql(u8, attribute_name.bytes, "StackMapTable")) { + const number_of_entries = try r.takeInt(u16, .big); + const entries = try allocator.alloc(Predefined.StackMapTable.StackMapFrame, number_of_entries); + errdefer allocator.free(entries); + for (entries) |*entry| { + entry.* = try .parse(&r, allocator); + } + + return .{ + .stack_map_table = .{ + .entries = entries, + }, + }; + } else if (eql(u8, attribute_name.bytes, "Exceptions")) { + const number_of_exceptions = try r.takeInt(u16, .big); + const exception_index_table = try allocator.alloc(u16, number_of_exceptions); + errdefer allocator.free(exception_index_table); + for (exception_index_table) |*exception_index| { + exception_index.* = try r.takeInt(u16, .big); + } + + return .{ + .exceptions = .{ + .exception_index_table = exception_index_table, + }, + }; + } else if (eql(u8, attribute_name.bytes, "InnerClasses")) { + const number_of_classes = try r.takeInt(u16, .big); + const classes = try allocator.alloc(Predefined.InnerClasses.InnerClass, number_of_classes); + errdefer allocator.free(classes); + for (classes) |*class| { + class.* = .{ + .inner_class_info_index = try r.takeInt(u16, .big), + .outer_class_info_index = try r.takeInt(u16, .big), + .inner_name_index = try r.takeInt(u16, .big), + .inner_class_access_flags = .{ .mask = try r.takeInt(u16, .big) }, + }; + } + + return .{ + .inner_classes = .{ + .classes = classes, + }, + }; + } else if (eql(u8, attribute_name.bytes, "EnclosingMethod")) { + return .{ + .enclosing_method = .{ + .class_index = try r.takeInt(u16, .big), + .method_index = try r.takeInt(u16, .big), + }, + }; + } else if (eql(u8, attribute_name.bytes, "Synthetic")) { + return .{ + .synthetic = .{}, + }; + } else if (eql(u8, attribute_name.bytes, "Signature")) { + return .{ + .signature = .{ + .signature_index = try r.takeInt(u16, .big), + }, + }; + } else if (eql(u8, attribute_name.bytes, "SourceFile")) { + return .{ + .source_file = .{ + .source_file_index = try r.takeInt(u16, .big), + }, + }; + } else if (eql(u8, attribute_name.bytes, "SourceDebugExtension")) { + return .{ + .source_debug_extension = .{ + .debug_extension = try allocator.dupe(u8, self.info), + }, + }; + } else if (eql(u8, attribute_name.bytes, "LineNumberTable")) { + const line_number_table_length = try r.takeInt(u16, .big); + const line_number_table = try allocator.alloc(Predefined.LineNumberTable.LineNumber, line_number_table_length); + errdefer allocator.free(line_number_table); + for (line_number_table) |*line_number| { + line_number.* = .{ + .start_pc = try r.takeInt(u16, .big), + .line_number = try r.takeInt(u16, .big), + }; + } + + return .{ + .line_number_table = .{ + .line_number_table = line_number_table, + }, + }; + } else if (eql(u8, attribute_name.bytes, "LocalVariableTable")) { + const local_variable_table_length = try r.takeInt(u16, .big); + const local_variable_table = try allocator.alloc(Predefined.LocalVariableTable.LocalVariable, local_variable_table_length); + errdefer allocator.free(local_variable_table); + for (local_variable_table) |*local_variable| { + local_variable.* = .{ + .start_pc = try r.takeInt(u16, .big), + .length = try r.takeInt(u16, .big), + .name_index = try r.takeInt(u16, .big), + .descriptor_index = try r.takeInt(u16, .big), + .index = try r.takeInt(u16, .big), + }; + } + + return .{ + .local_variable_table = .{ + .local_variable_table = local_variable_table, + }, + }; + } else if (eql(u8, attribute_name.bytes, "LocalVariableTypeTable")) { + const local_variable_type_table_length = try r.takeInt(u16, .big); + const local_variable_type_table = try allocator.alloc(Predefined.LocalVariableTypeTable.LocalVariableType, local_variable_type_table_length); + errdefer allocator.free(local_variable_type_table); + for (local_variable_type_table) |*local_variable_type| { + local_variable_type.* = .{ + .start_pc = try r.takeInt(u16, .big), + .length = try r.takeInt(u16, .big), + .name_index = try r.takeInt(u16, .big), + .signature_index = try r.takeInt(u16, .big), + .index = try r.takeInt(u16, .big), + }; + } + + return .{ + .local_variable_type_table = .{ + .local_variable_type_table = local_variable_type_table, + }, + }; + } else if (eql(u8, attribute_name.bytes, "Deprecated")) { + return .{ + .deprecated = .{}, + }; + } else if (eql(u8, attribute_name.bytes, "RuntimeVisibleAnnotations")) { + const num_annotations = try r.takeInt(u16, .big); + const annotations = try allocator.alloc(Predefined.Annotation, num_annotations); + errdefer allocator.free(annotations); + for (annotations) |*annotation| { + annotation.* = try .parse(&r, allocator); + } + + return .{ + .runtime_visible_annotations = .{ + .annotations = annotations, + }, + }; + } else if (eql(u8, attribute_name.bytes, "RuntimeInvisibleAnnotations")) { + const num_annotations = try r.takeInt(u16, .big); + const annotations = try allocator.alloc(Predefined.Annotation, num_annotations); + errdefer allocator.free(annotations); + for (annotations) |*annotation| { + annotation.* = try .parse(&r, allocator); + } + + return .{ + .runtime_invisible_annotations = .{ + .annotations = annotations, + }, + }; + } else if (eql(u8, attribute_name.bytes, "RuntimeVisibleParameterAnnotations")) { + const num_parameters = try r.takeByte(); + const parameter_annotations = try allocator.alloc(Predefined.ParameterAnnotation, num_parameters); + errdefer allocator.free(parameter_annotations); + for (parameter_annotations) |*parameter_annotation| { + const num_annotations = try r.takeInt(u16, .big); + const annotations = try allocator.alloc(Predefined.Annotation, num_annotations); + errdefer allocator.free(annotations); + for (annotations) |*annotation| { + annotation.* = try .parse(&r, allocator); + } + + parameter_annotation.* = .{ + .annotations = annotations, + }; + } + + return .{ + .runtime_visible_parameter_annotations = .{ + .parameter_annotations = parameter_annotations, + }, + }; + } else if (eql(u8, attribute_name.bytes, "RuntimeInvisibleParameterAnnotations")) { + const num_parameters = try r.takeByte(); + const parameter_annotations = try allocator.alloc(Predefined.ParameterAnnotation, num_parameters); + errdefer allocator.free(parameter_annotations); + for (parameter_annotations) |*parameter_annotation| { + const num_annotations = try r.takeInt(u16, .big); + const annotations = try allocator.alloc(Predefined.Annotation, num_annotations); + errdefer allocator.free(annotations); + for (annotations) |*annotation| { + annotation.* = try .parse(&r, allocator); + } + + parameter_annotation.* = .{ + .annotations = annotations, + }; + } + + return .{ + .runtime_invisible_parameter_annotations = .{ + .parameter_annotations = parameter_annotations, + }, + }; + } else if (eql(u8, attribute_name.bytes, "AnnotationDefault")) { + unreachable; + } else if (eql(u8, attribute_name.bytes, "BootstrapMethods")) { + const num_bootstrap_methods = try r.takeInt(u16, .big); + const bootstrap_methods = try allocator.alloc(Predefined.BootstrapMethods.BootstrapMethod, num_bootstrap_methods); errdefer allocator.free(bootstrap_methods); for (bootstrap_methods) |*bootstrap_method| { - const bootstrap_method_ref = try input.takeInt(u16, .big); + const bootstrap_method_ref = try r.takeInt(u16, .big); - const num_bootstrap_arguments = try input.takeInt(u16, .big); + const num_bootstrap_arguments = try r.takeInt(u16, .big); const bootstrap_arguments = try allocator.alloc(u16, num_bootstrap_arguments); errdefer allocator.free(bootstrap_arguments); for (bootstrap_arguments) |*bootstrap_argument| { - bootstrap_argument.* = try input.takeInt(u16, .big); + bootstrap_argument.* = try r.takeInt(u16, .big); } bootstrap_method.* = .{ @@ -2434,658 +1029,12 @@ pub const BootstrapMethods = struct { } return .{ - .bootstrap_methods = bootstrap_methods, + .bootstrap_methods = .{ + .bootstrap_methods = bootstrap_methods, + }, }; + } else { + return null; } +} - pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { - for (self.bootstrap_methods) |bootstrap_method| { - allocator.free(bootstrap_method.bootstrap_arguments); - } - allocator.free(self.bootstrap_methods); - } -}; - -pub const MethodParameters = struct { - parameters: []Parameter, - - const Self = @This(); - - pub const Parameter = struct { - name_index: u16, - access_flags: ParameterAccessFlags, - - pub const ParameterAccessFlags = EnumFlags(enum(u16) { - final = 0x0010, - synthetic = 0x1000, - mandated = 0x8000, - }); - - pub fn indentedFormat( - self: *const Parameter, - w: *std.Io.Writer, - depth: usize, - indent: u8, - constant_pool: ConstantPool, - ) std.Io.Writer.Error!void { - try w.splatByteAll(' ', depth * indent); - try w.print("flags: {f}\n", .{ self.access_flags }); - - const name_ = self.name(constant_pool) catch return error.WriteFailed; - if (name_) |n| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("name:\n"); - try n.indentedFormat(w, depth, indent + 1, constant_pool); - } - } - - pub fn name(self: *const Parameter, constant_pool: ConstantPool) ResolveError!?ConstantPool.Info { - return constant_pool.getTagOptional(self.name_index, .utf8); - } - }; - - pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { - const parameter_count = try input.takeByte(); - const parameters = try allocator.alloc(Parameter, parameter_count); - errdefer allocator.free(parameters); - for (parameters) |*parameter| { - parameter.* = .{ - .name_index = try input.takeInt(u16, .big), - .access_flags = .from(try input.takeInt(u16, .big)), - }; - } - - return .{ - .parameters = parameters, - }; - } - - pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { - allocator.free(self.parameters); - } -}; - -pub const Module = struct { - module_name_index: u16, - module_flags: ModuleFlags, - module_version_index: u16, - requires: []Requires, - exports: []Exports, - opens: []Opens, - uses_indeces: []u16, - provides: []Provides, - - const Self = @This(); - - pub const ModuleFlags = EnumFlags(enum(u16) { - open = 0x0020, - sythetic = 0x1000, - mandated = 0x8000, - }); - - pub const Requires = struct { - requires_index: u16, - requires_flags: RequiresFlags, - requires_version_index: u16, - - pub const RequiresFlags = EnumFlags(enum(u16) { - transitive = 0x0020, - static_phase = 0x0040, - synthetic = 0x1000, - mandated = 0x8000, - }); - - pub fn indentedFormat( - self: *const Requires, - w: *std.Io.Writer, - depth: usize, - indent: u8, - constant_pool: ConstantPool, - ) std.Io.Writer.Error!void { - const requires_ = self.requires(constant_pool) catch return error.WriteFailed; - try w.splatByteAll(' ', depth * indent); - _ = try w.write("requires:\n"); - try requires_.indentedFormat(w, depth, indent + 1, constant_pool); - - try w.splatByteAll(' ', depth * indent); - try w.print("flags: {f}\n", .{ self.requires_flags }); - - const version = self.requiresVersion(constant_pool) catch return error.WriteFailed; - if (version) |v| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("version:\n"); - try v.indentedFormat(w, depth, indent + 1, constant_pool); - } - } - - pub fn requires(self: *const Requires, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { - return constant_pool.getTag(self.requires_index, .module); - } - - pub fn requiresVersion(self: *const Requires, constant_pool: ConstantPool) ResolveError!?ConstantPool.Info { - return constant_pool.getTagOptional(self.requires_version_index, .utf8); - } - }; - - pub const Exports = struct { - exports_index: u16, - exports_flags: ExportsFlags, - exports_to_indeces: []u16, - - pub const ExportsFlags = EnumFlags(enum(u16) { - synthetic = 0x1000, - mandated = 0x8000, - }); - - pub fn indentedFormat( - self: *const Exports, - w: *std.Io.Writer, - depth: usize, - indent: u8, - constant_pool: ConstantPool, - ) std.Io.Writer.Error!void { - const exports_ = self.exports(constant_pool) catch return error.WriteFailed; - try w.splatByteAll(' ', depth * indent); - _ = try w.write("exports:\n"); - try exports_.indentedFormat(w, depth, indent + 1, constant_pool); - - try w.splatByteAll(' ', depth * indent); - try w.print("flags: {f}\n", .{ self.exports_flags }); - - try w.splatByteAll(' ', depth * indent); - _ = try w.write("exports_to:\n"); - var exports_to_indent = indent; - for (self.exports_to_indeces, 0..) |exports_to_index, i| { - exports_to_indent += 1; - defer exports_to_indent -= 1; - - const info = constant_pool.getTag(exports_to_index, .module) catch return error.WriteFailed; - - try w.splatByteAll(' ', depth * exports_to_indent); - try w.print("{d}:\n", .{ i }); - try info.indentedFormat(w, depth, exports_to_indent + 1, constant_pool); - } - } - - pub fn exports(self: *const Exports, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { - return constant_pool.getTag(self.exports_index, .package); - } - }; - - pub const Opens = struct { - opens_index: u16, - opens_flags: OpensFlags, - opens_to_indeces: []u16, - - pub const OpensFlags = EnumFlags(enum(u16) { - synthetic = 0x1000, - mandated = 0x8000, - }); - - pub fn indentedFormat( - self: *const Opens, - w: *std.Io.Writer, - depth: usize, - indent: u8, - constant_pool: ConstantPool, - ) std.Io.Writer.Error!void { - const opens_ = self.opens(constant_pool) catch return error.WriteFailed; - try w.splatByteAll(' ', depth * indent); - _ = try w.write("opens:\n"); - try opens_.indentedFormat(w, depth, indent + 1, constant_pool); - - try w.splatByteAll(' ', depth * indent); - try w.print("flags: {f}\n", .{ self.opens_flags }); - - try w.splatByteAll(' ', depth * indent); - _ = try w.write("opens_to:\n"); - var opens_to_indent = indent; - for (self.opens_to_indeces, 0..) |opens_to_index, i| { - opens_to_indent += 1; - defer opens_to_indent -= 1; - - const info = constant_pool.getTag(opens_to_index, .module) catch return error.WriteFailed; - - try w.splatByteAll(' ', depth * opens_to_indent); - try w.print("{d}:\n", .{ i }); - try info.indentedFormat(w, depth, opens_to_indent + 1, constant_pool); - } - } - - pub fn opens(self: *const Opens, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { - return constant_pool.getTag(self.opens_index, .package); - } - }; - - pub const Provides = struct { - provides_index: u16, - provides_with_indeces: []u16, - - pub fn indentedFormat( - self: *const Provides, - w: *std.Io.Writer, - depth: usize, - indent: u8, - constant_pool: ConstantPool, - ) std.Io.Writer.Error!void { - const provides_ = self.provides(constant_pool) catch return error.WriteFailed; - try w.splatByteAll(' ', depth * indent); - _ = try w.write("provides:\n"); - try provides_.indentedFormat(w, depth, indent + 1, constant_pool); - - try w.splatByteAll(' ', depth * indent); - _ = try w.write("provides_with:\n"); - var provides_with_indent = indent; - for (self.provides_with_indeces, 0..) |provides_with_index, i| { - provides_with_indent += 1; - defer provides_with_indent -= 1; - - const info = constant_pool.getTag(provides_with_index, .class) catch return error.WriteFailed; - - try w.splatByteAll(' ', depth * provides_with_indent); - try w.print("{d}:\n", .{ i }); - try info.indentedFormat(w, depth, provides_with_indent + 1, constant_pool); - } - } - - pub fn provides(self: *const Provides, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { - return constant_pool.getTag(self.provides_index, .class); - } - }; - - pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { - const module_name_index = try input.takeInt(u16, .big); - const module_flags: ModuleFlags = .from(try input.takeInt(u16, .big)); - const module_version_index = try input.takeInt(u16, .big); - - const requires_count = try input.takeInt(u16, .big); - const requires = try allocator.alloc(Requires, requires_count); - errdefer allocator.free(requires); - for (requires) |*r| { - const requires_index = try input.takeInt(u16, .big); - const requires_flags: Requires.RequiresFlags = .from(try input.takeInt(u16, .big)); - const requires_version_index = try input.takeInt(u16, .big); - r.* = .{ - .requires_index = requires_index, - .requires_flags = requires_flags, - .requires_version_index = requires_version_index, - }; - } - - const exports_count = try input.takeInt(u16, .big); - const exports = try allocator.alloc(Exports, exports_count); - errdefer allocator.free(exports); - for (exports) |*e| { - const exports_index = try input.takeInt(u16, .big); - const exports_flags: Exports.ExportsFlags = .from(try input.takeInt(u16, .big)); - const exports_to_count = try input.takeInt(u16, .big); - const exports_to_indeces = try allocator.alloc(u16, exports_to_count); - errdefer allocator.free(exports_to_indeces); - for (exports_to_indeces) |*i| { - i.* = try input.takeInt(u16, .big); - } - e.* = .{ - .exports_index = exports_index, - .exports_flags = exports_flags, - .exports_to_indeces = exports_to_indeces, - }; - } - - const opens_count = try input.takeInt(u16, .big); - const opens = try allocator.alloc(Opens, opens_count); - errdefer allocator.free(opens); - for (opens) |*o| { - const opens_index = try input.takeInt(u16, .big); - const opens_flags: Opens.OpensFlags = .from(try input.takeInt(u16, .big)); - const opens_to_count = try input.takeInt(u16, .big); - const opens_to_indeces = try allocator.alloc(u16, opens_to_count); - errdefer allocator.free(opens_to_indeces); - for (opens_to_indeces) |*i| { - i.* = try input.takeInt(u16, .big); - } - o.* = .{ - .opens_index = opens_index, - .opens_flags = opens_flags, - .opens_to_indeces = opens_to_indeces, - }; - } - - const uses_count = try input.takeInt(u16, .big); - const uses_indeces = try allocator.alloc(u16, uses_count); - errdefer allocator.free(uses_indeces); - for (uses_indeces) |*i| { - i.* = try input.takeInt(u16, .big); - } - - const provides_count = try input.takeInt(u16, .big); - const provides = try allocator.alloc(Provides, provides_count); - errdefer allocator.free(provides); - for (provides) |*p| { - const provides_index = try input.takeInt(u16, .big); - const provides_with_count = try input.takeInt(u16, .big); - const provides_with_indeces = try allocator.alloc(u16, provides_with_count); - errdefer allocator.free(provides_with_indeces); - for (provides_with_indeces) |*i| { - i.* = try input.takeInt(u16, .big); - } - p.* = .{ - .provides_index = provides_index, - .provides_with_indeces = provides_with_indeces, - }; - } - - return .{ - .module_name_index = module_name_index, - .module_flags = module_flags, - .module_version_index = module_version_index, - .requires = requires, - .exports = exports, - .opens = opens, - .uses_indeces = uses_indeces, - .provides = provides, - }; - } - - pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { - allocator.free(self.requires); - for (self.exports) |*e| { - allocator.free(e.exports_to_indeces); - } - allocator.free(self.exports); - for (self.opens) |*o| { - allocator.free(o.opens_to_indeces); - } - allocator.free(self.opens); - allocator.free(self.uses_indeces); - for (self.provides) |*p| { - allocator.free(p.provides_with_indeces); - } - allocator.free(self.provides); - } - - pub fn indentedFormat( - self: *const Module, - w: *std.Io.Writer, - depth: usize, - indent: u8, - constant_pool: ConstantPool, - ) std.Io.Writer.Error!void { - const module_name = self.moduleName(constant_pool) catch return error.WriteFailed; - try w.splatByteAll(' ', depth * indent); - _ = try w.write("name:\n"); - try module_name.indentedFormat(w, depth, indent + 1, constant_pool); - - try w.splatByteAll(' ', depth * indent); - try w.print("flags: {f}\n", .{ self.module_flags }); - - const module_version = self.moduleVersion(constant_pool) catch return error.WriteFailed; - if (module_version) |m| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("version:\n"); - try m.indentedFormat(w, depth, indent + 1, constant_pool); - } - - try w.splatByteAll(' ', depth * indent); - _ = try w.write("requires:\n"); - var requires_indent = indent; - for (self.requires, 0..) |requires, i| { - requires_indent += 1; - defer requires_indent -= 1; - - try w.splatByteAll(' ', depth * requires_indent); - try w.print("{d}:\n", .{ i }); - try requires.indentedFormat(w, depth, requires_indent + 1, constant_pool); - } - - try w.splatByteAll(' ', depth * indent); - _ = try w.write("exports:\n"); - var exports_indent = indent; - for (self.exports, 0..) |exports, i| { - exports_indent += 1; - defer exports_indent -= 1; - - try w.splatByteAll(' ', depth * exports_indent); - try w.print("{d}:\n", .{ i }); - try exports.indentedFormat(w, depth, exports_indent + 1, constant_pool); - } - - try w.splatByteAll(' ', depth * indent); - _ = try w.write("opens:\n"); - var opens_indent = indent; - for (self.opens, 0..) |opens, i| { - opens_indent += 1; - defer opens_indent -= 1; - - try w.splatByteAll(' ', depth * opens_indent); - try w.print("{d}:\n", .{ i }); - try opens.indentedFormat(w, depth, opens_indent + 1, constant_pool); - } - - try w.splatByteAll(' ', depth * indent); - _ = try w.write("uses:\n"); - var uses_indent = indent; - for (self.uses_indeces, 0..) |uses_index, i| { - uses_indent += 1; - defer uses_indent -= 1; - - const info = constant_pool.getTag(uses_index, .class) catch return error.WriteFailed; - - try w.splatByteAll(' ', depth * uses_indent); - try w.print("{d}:\n", .{ i }); - try info.indentedFormat(w, depth, uses_indent + 1, constant_pool); - } - - try w.splatByteAll(' ', depth * indent); - _ = try w.write("provides:\n"); - var provides_indent = indent; - for (self.provides, 0..) |provides, i| { - provides_indent += 1; - defer provides_indent -= 1; - - try w.splatByteAll(' ', depth * provides_indent); - try w.print("{d}:\n", .{ i }); - try provides.indentedFormat(w, depth, provides_indent + 1, constant_pool); - } - } - - pub fn moduleName(self: *const Module, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { - return constant_pool.getTag(self.module_name_index, .module); - } - - pub fn moduleVersion(self: *const Module, constant_pool: ConstantPool) ResolveError!?ConstantPool.Info { - return constant_pool.getTagOptional(self.module_version_index, .utf8); - } -}; - -pub const ModulePackages = struct { - package_indeces: []u16, - - const Self = @This(); - - pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { - const package_count = try input.takeInt(u16, .big); - const package_indeces = try allocator.alloc(u16, package_count); - errdefer allocator.free(package_indeces); - for (package_indeces) |*package_index| { - package_index.* = try input.takeInt(u16, .big); - } - - return .{ - .package_indeces = package_indeces, - }; - } - - pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { - allocator.free(self.package_indeces); - } -}; - -pub const ModuleMainClass = struct { - main_class_index: u16, - - const Self = @This(); - - pub fn parse(input: *std.Io.Reader) ParseError!Self { - return .{ - .main_class_index = try input.takeInt(u16, .big), - }; - } - - pub fn mainClass(self: *const ModuleMainClass, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { - return constant_pool.getTag(self.main_class_index, .class); - } -}; - -pub const NestHost = struct { - host_class_index: u16, - - const Self = @This(); - - pub fn parse(input: *std.Io.Reader) ParseError!Self { - return .{ - .host_class_index = try input.takeInt(u16, .big), - }; - } - - pub fn hostClass(self: *const NestHost, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { - return constant_pool.getTag(self.host_class_index, .class); - } -}; - -pub const NestMembers = struct { - classes: []u16, - - const Self = @This(); - - pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { - const number_of_classes = try input.takeInt(u16, .big); - const classes = try allocator.alloc(u16, number_of_classes); - for (classes) |*class| { - class.* = try input.takeInt(u16, .big); - } - - return .{ - .classes = classes, - }; - } - - pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { - allocator.free(self.classes); - } -}; - -pub const Record = struct { - components: []RecordComponentInfo, - - const Self = @This(); - - pub const RecordComponentInfo = struct { - name_index: u16, - descriptor_index: u16, - attributes: []AttributeInfo, - - pub fn parse(input: *std.Io.Reader, constant_pool: ConstantPool, allocator: std.mem.Allocator) (ParseError || ResolveError)!RecordComponentInfo { - const name_index = try input.takeInt(u16, .big); - const descriptor_index = try input.takeInt(u16, .big); - - const attribute_count = try input.takeInt(u16, .big); - const attributes = try allocator.alloc(AttributeInfo, attribute_count); - errdefer allocator.free(attributes); - for (attributes) |*attribute| { - attribute.* = try .parse(input, constant_pool, allocator); - } - - return .{ - .name_index = name_index, - .descriptor_index = descriptor_index, - .attributes = attributes, - }; - } - - pub fn deinit(self: *RecordComponentInfo, allocator: std.mem.Allocator) void { - for (self.attributes) |*attribute| { - attribute.deinit(allocator); - } - allocator.free(self.attributes); - } - - pub fn indentedFormat( - self: *const RecordComponentInfo, - w: *std.Io.Writer, - depth: usize, - indent: u8, - constant_pool: ConstantPool, - ) std.Io.Writer.Error!void { - const name_ = self.name(constant_pool) catch return error.WriteFailed; - try w.splatByteAll(' ', depth * indent); - _ = try w.write("name:\n"); - try name_.indentedFormat(w, depth, indent + 1, constant_pool); - - const descriptor_ = self.descriptor(constant_pool) catch return error.WriteFailed; - try w.splatByteAll(' ', depth * indent); - _ = try w.write("descriptor:\n"); - try descriptor_.indentedFormat(w, depth, indent + 1, constant_pool); - - try w.splatByteAll(' ', depth * indent); - _ = try w.write("attributes:\n"); - var attr_indent = indent; - for (self.attributes, 0..) |*attr, i| { - attr_indent += 1; - defer attr_indent -= 1; - - try w.splatByteAll(' ', depth * attr_indent); - try w.print("{d}:\n", .{ i }); - try attr.indentedFormat(w, depth, attr_indent + 1, constant_pool); - } - } - - pub fn name(self: *const RecordComponentInfo, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { - return constant_pool.getTag(self.name_index, .utf8); - } - - pub fn descriptor(self: *const RecordComponentInfo, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { - return constant_pool.getTag(self.descriptor_index, .utf8); - } - }; - - pub fn parse(input: *std.Io.Reader, constant_pool: ConstantPool, allocator: std.mem.Allocator) (ParseError || ResolveError)!Self { - const components_count = try input.takeInt(u16, .big); - const components = try allocator.alloc(RecordComponentInfo, components_count); - for (components) |*component| { - component.* = try .parse(input, constant_pool, allocator); - } - - return .{ - .components = components, - }; - } - - pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { - for (self.components) |*component| { - component.deinit(allocator); - } - allocator.free(self.components); - } -}; - -pub const PermittedSubclasses = struct { - classes: []u16, - - const Self = @This(); - - pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { - const number_of_classes = try input.takeInt(u16, .big); - const classes = try allocator.alloc(u16, number_of_classes); - for (classes) |*class| { - class.* = try input.takeInt(u16, .big); - } - - return .{ - .classes = classes, - }; - } - - pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { - allocator.free(self.classes); - } -}; 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/Class/ConstantPool.zig b/src/Class/ConstantPool.zig deleted file mode 100644 index 54c233f..0000000 --- a/src/Class/ConstantPool.zig +++ /dev/null @@ -1,547 +0,0 @@ -const std = @import("std"); - -const Class = @import("../Class.zig"); -const AttributeInfo = Class.AttributeInfo; -const ParseError = Class.ParseError; -const ResolveError = Class.ResolveError; - -constant_pool: []?Info, - -const Self = @This(); - -pub const Info = union(Info.Tag) { - class: Info.Class, - field_ref: FieldRef, - method_ref: MethodRef, - interface_method_ref: InterfaceMethodRef, - string: String, - integer: Integer, - float: Float, - long: Long, - double: Double, - name_and_type: NameAndType, - utf8: Utf8, - method_handle: MethodHandle, - method_type: MethodType, - dynamic: Dynamic, - invoke_dynamic: InvokeDynamic, - module: Module, - package: Package, - - pub const Tag = enum(u8) { - class = 7, - field_ref = 9, - method_ref = 10, - interface_method_ref = 11, - string = 8, - integer = 3, - float = 4, - long = 5, - double = 6, - name_and_type = 12, - utf8 = 1, - method_handle = 15, - method_type = 16, - dynamic = 17, - invoke_dynamic = 18, - module = 19, - package = 20, - - pub fn loadable(self: Tag) bool { - return switch (self) { - .integer, .float, .long, .double, .class, .string, .method_handle, .method_type, .dynamic => true, - else => false, - }; - } - }; - - pub const Class = struct { - name_index: u16, - - pub fn name(self: *const Info.Class, constant_pool: Self) ResolveError!Info { - return constant_pool.getTag(self.name_index, .utf8); - } - }; - - pub const FieldRef = struct { - class_index: u16, - name_and_type_index: u16, - - pub fn class(self: *const FieldRef, constant_pool: Self) ResolveError!Info { - return constant_pool.getTag(self.class_index, .class); - } - - pub fn nameAndType(self: *const FieldRef, constant_pool: Self) ResolveError!Info { - return constant_pool.getTag(self.name_and_type_index, .name_and_type); - } - }; - - pub const MethodRef = struct { - class_index: u16, - name_and_type_index: u16, - - pub fn class(self: *const MethodRef, constant_pool: Self) ResolveError!Info { - return constant_pool.getTag(self.class_index, .class); - } - - pub fn nameAndType(self: *const MethodRef, constant_pool: Self) ResolveError!Info { - return constant_pool.getTag(self.name_and_type_index, .name_and_type); - } - }; - - pub const InterfaceMethodRef = struct { - class_index: u16, - name_and_type_index: u16, - - pub fn class(self: *const InterfaceMethodRef, constant_pool: Self) ResolveError!Info { - return constant_pool.getTag(self.class_index, .class); - } - - pub fn nameAndType(self: *const InterfaceMethodRef, constant_pool: Self) ResolveError!Info { - return constant_pool.getTag(self.name_and_type_index, .name_and_type); - } - }; - - pub const String = struct { - string_index: u16, - - pub fn string(self: *const String, constant_pool: Self) ResolveError!Info { - return constant_pool.getTag(self.string_index, .utf8); - } - }; - - pub const Integer = struct { - bytes: u32, - }; - - pub const Float = struct { - bytes: u32, - - pub fn float(self: *const Float) f32 { - return @bitCast(self.bytes); - } - }; - - pub const Long = struct { - high_bytes: u32, - low_bytes: u32, - - pub fn long(self: *const Long) u64 { - return (@as(u64, self.high_bytes) << 32) | self.low_bytes; - } - }; - - pub const Double = struct { - high_bytes: u32, - low_bytes: u32, - - pub fn double(self: *const Double) f64 { - return @bitCast((@as(u64, self.high_bytes) << 32) | self.low_bytes); - } - }; - - pub const NameAndType = struct { - name_index: u16, - descriptor_index: u16, - - pub fn name(self: *const NameAndType, constant_pool: Self) ResolveError!Info { - return constant_pool.getTag(self.name_index, .utf8); - } - - pub fn descriptor(self: *const NameAndType, constant_pool: Self) ResolveError!Info { - return constant_pool.getTag(self.descriptor_index, .utf8); - } - }; - - pub const Utf8 = struct { - bytes: []const u8, - }; - - pub const MethodHandle = struct { - reference_kind: Kind, - reference_index: u16, - - pub const Kind = enum(u8) { - get_field = 1, - get_static = 2, - put_field = 3, - put_static = 4, - invoke_virtual = 5, - invoke_static = 6, - invoke_special = 7, - new_invoke_special = 8, - invoke_interface = 9, - }; - - pub fn reference(self: *const MethodHandle, constant_pool: Self) ResolveError!Info { - const info = try constant_pool.get(self.reference_index); - return switch (self.reference_kind) { - .get_field, .get_static, .put_field, .put_static => blk: { - if (!info.is(.field_ref)) break :blk ResolveError.InvalidConstantType; - break :blk info; - }, - .invoke_virtual, .invoke_static, .invoke_special, .new_invoke_special => blk: { - if (!info.is(.method_ref)) break :blk ResolveError.InvalidConstantType; - break :blk info; - }, - .invoke_interface => blk: { - if (!info.is(.interface_method_ref)) break :blk ResolveError.InvalidConstantType; - break :blk info; - }, - }; - } - }; - - pub const MethodType = struct { - descriptor_index: u16, - - pub fn descriptor(self: *const MethodType, constant_pool: Self) ResolveError!Info { - return constant_pool.getTag(self.descriptor_index, .utf8); - } - }; - - pub const Dynamic = struct { - bootstrap_method_attr_index: u16, - name_and_type_index: u16, - - pub fn nameAndType(self: *const Dynamic, constant_pool: Self) ResolveError!Info { - return constant_pool.getTag(self.name_and_type_index, .name_and_type); - } - }; - - pub const InvokeDynamic = struct { - bootstrap_method_attr_index: u16, - name_and_type_index: u16, - - pub fn nameAndType(self: *const InvokeDynamic, constant_pool: Self) ResolveError!Info { - return constant_pool.getTag(self.name_and_type_index, .name_and_type); - } - }; - - pub const Module = struct { - name_index: u16, - - pub fn name(self: *const Module, constant_pool: Self) ResolveError!Info { - return constant_pool.getTag(self.name_index, .utf8); - } - }; - - pub const Package = struct { - name_index: u16, - - pub fn name(self: *const Package, constant_pool: Self) ResolveError!Info { - return constant_pool.getTag(self.name_index, .utf8); - } - }; - - pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Info { - const tag_byte = try input.takeByte(); - const tag = std.enums.fromInt(Tag, tag_byte) orelse return ParseError.InvalidTag; - - return switch (tag) { - .class => .{ - .class = .{ - .name_index = try input.takeInt(u16, .big), - }, - }, - .field_ref => .{ - .field_ref = .{ - .class_index = try input.takeInt(u16, .big), - .name_and_type_index = try input.takeInt(u16, .big), - }, - }, - .method_ref => .{ - .method_ref = .{ - .class_index = try input.takeInt(u16, .big), - .name_and_type_index = try input.takeInt(u16, .big), - }, - }, - .interface_method_ref => .{ - .interface_method_ref = .{ - .class_index = try input.takeInt(u16, .big), - .name_and_type_index = try input.takeInt(u16, .big), - }, - }, - .string => .{ - .string = .{ - .string_index = try input.takeInt(u16, .big), - }, - }, - .integer => .{ - .integer = .{ - .bytes = try input.takeInt(u32, .big), - }, - }, - .float => .{ - .integer = .{ - .bytes = try input.takeInt(u32, .big), - }, - }, - .long => .{ - .long = .{ - .high_bytes = try input.takeInt(u32, .big), - .low_bytes = try input.takeInt(u32, .big), - }, - }, - .double => .{ - .double = .{ - .high_bytes = try input.takeInt(u32, .big), - .low_bytes = try input.takeInt(u32, .big), - }, - }, - .name_and_type => .{ - .name_and_type = .{ - .name_index = try input.takeInt(u16, .big), - .descriptor_index = try input.takeInt(u16, .big), - }, - }, - .utf8 => blk: { - const length = try input.takeInt(u16, .big); - const bytes = try input.readAlloc(allocator, length); - - break :blk .{ - .utf8 = .{ - .bytes = bytes, - }, - }; - }, - .method_handle => blk: { - const kind_byte = try input.takeByte(); - const kind = std.enums.fromInt(MethodHandle.Kind, kind_byte) orelse return ParseError.InvalidTag; - - break :blk .{ - .method_handle = .{ - .reference_kind = kind, - .reference_index = try input.takeInt(u16, .big), - }, - }; - }, - .method_type => .{ - .method_type = .{ - .descriptor_index = try input.takeInt(u16, .big), - }, - }, - .dynamic => .{ - .dynamic = .{ - .bootstrap_method_attr_index = try input.takeInt(u16, .big), - .name_and_type_index = try input.takeInt(u16, .big), - }, - }, - .invoke_dynamic => .{ - .invoke_dynamic = .{ - .bootstrap_method_attr_index = try input.takeInt(u16, .big), - .name_and_type_index = try input.takeInt(u16, .big), - }, - }, - .module => .{ - .module = .{ - .name_index = try input.takeInt(u16, .big), - }, - }, - .package => .{ - .package = .{ - .name_index = try input.takeInt(u16, .big), - }, - }, - }; - } - - pub fn deinit(self: *Info, allocator: std.mem.Allocator) void { - switch (self.*) { - .utf8 => |info| allocator.free(info.bytes), - else => {}, - } - } - - pub fn format(self: *const Info, w: *std.Io.Writer) std.Io.Writer.Error!void { - switch (self.*) { - .class => |class| try w.print("#{d}", .{ class.name_index }), - .field_ref => |field_ref| try w.print("#{d}.#{d}", .{ field_ref.class_index, field_ref.name_and_type_index }), - .method_ref => |method_ref| try w.print("#{d}.#{d}", .{ method_ref.class_index, method_ref.name_and_type_index }), - .interface_method_ref => |interface_method_ref| try w.print("#{d}.#{d}", .{ interface_method_ref.class_index, interface_method_ref.name_and_type_index }), - .string => |string| try w.print("#{d}", .{ string.string_index }), - .integer => |integer| try w.print("{d}", .{ integer.bytes }), - .float => |float| try w.print("{d}", .{ float.float() }), - .long => |long| try w.print("{d}", .{ long.long() }), - .double => |double| try w.print("{d}", .{ double.double() }), - .name_and_type => |name_and_type| try w.print("#{d}:#{d}", .{ name_and_type.name_index, name_and_type.descriptor_index }), - .utf8 => |utf8| try w.print("\"{s}\"", .{ utf8.bytes }), - .method_handle => |method_handle| try w.print("{t} #{d}", .{ method_handle.reference_kind, method_handle.reference_index }), - .method_type => |method_type| try w.print("#{d}", .{ method_type.descriptor_index }), - .dynamic => |dynamic| try w.print("#{d} #{d}", .{ dynamic.bootstrap_method_attr_index, dynamic.name_and_type_index }), - .invoke_dynamic => |invoke_dynamic| try w.print("#{d} #{d}", .{ invoke_dynamic.bootstrap_method_attr_index, invoke_dynamic.name_and_type_index }), - .module => |module| try w.print("#{d}", .{ module.name_index }), - .package => |package| try w.print("#{d}", .{ package.name_index }), - } - } - - pub fn indentedFormat( - self: *const Info, - w: *std.Io.Writer, - depth: usize, - indent: u8, - constant_pool: Self, - ) std.Io.Writer.Error!void { - try w.splatByteAll(' ', depth * indent); - try w.print("constant_value_type: {t}\n", .{ self.* }); - - switch (self.*) { - .class => |*class| { - try formatInfo(w, depth, indent, "name", class, .name, constant_pool); - }, - .field_ref => |*field_ref| { - try formatInfo(w, depth, indent, "class", field_ref, .class, constant_pool); - - try formatInfo(w, depth, indent, "name_and_type", field_ref, .nameAndType, constant_pool); - }, - .method_ref => |*method_ref| { - try formatInfo(w, depth, indent, "class", method_ref, .class, constant_pool); - - try formatInfo(w, depth, indent, "name_and_type", method_ref, .nameAndType, constant_pool); - }, - .interface_method_ref => |*interface_method_ref| { - try formatInfo(w, depth, indent, "class", interface_method_ref, .class, constant_pool); - - try formatInfo(w, depth, indent, "name_and_type", interface_method_ref, .nameAndType, constant_pool); - }, - .string => |*string| { - try formatInfo(w, depth, indent, "string", string, .string, constant_pool); - }, - .integer => |*integer| { - try w.splatByteAll(' ', depth * indent); - try w.print("integer: {d}\n", .{ integer.bytes }); - }, - .float => |*float| { - try w.splatByteAll(' ', depth * indent); - try w.print("float: {d}\n", .{ float.float() }); - }, - .long => |*long| { - try w.splatByteAll(' ', depth * indent); - try w.print("long: {d}\n", .{ long.long() }); - }, - .double => |*double| { - try w.splatByteAll(' ', depth * indent); - try w.print("double: {d}\n", .{ double.double() }); - }, - .name_and_type => |*name_and_type| { - try formatInfo(w, depth, indent, "name", name_and_type, .name, constant_pool); - - try formatInfo(w, depth, indent, "descriptor", name_and_type, .descriptor, constant_pool); - }, - .utf8 => |*utf8| { - try w.splatByteAll(' ', depth * indent); - try w.print("bytes: \"{s}\"\n", .{ utf8.bytes }); - }, - .method_handle => |*method_handle| { - try w.splatByteAll(' ', depth * indent); - try w.print("kind: {t}\n", .{ method_handle.reference_kind }); - - try formatInfo(w, depth, indent, "descriptor", method_handle, .reference, constant_pool); - }, - .method_type => |*method_type| { - try formatInfo(w, depth, indent, "descriptor", method_type, .descriptor, constant_pool); - }, - .dynamic => |*dynamic| { - try formatInfo(w, depth, indent, "name_and_type", dynamic, .nameAndType, constant_pool); - }, - .invoke_dynamic => |*invoke_dynamic| { - try formatInfo(w, depth, indent, "name_and_type", invoke_dynamic, .nameAndType, constant_pool); - }, - .module => |*module| { - try formatInfo(w, depth, indent, "name", module, .name, constant_pool); - }, - .package => |*package| { - try formatInfo(w, depth, indent, "name", package, .name, constant_pool); - }, - } - } - - pub fn is(self: *const Info, tag: Tag) bool { - return @as(Tag, self.*) == tag; - } -}; - -pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { - const constant_pool_size = try input.takeInt(u16, .big); - const constant_pool = try allocator.alloc(?Info, constant_pool_size - 1); - errdefer allocator.free(constant_pool); - - var i: usize = 0; - while (i < constant_pool_size - 1) { - defer i += 1; - - const info: Info = try .parse(input, allocator); - constant_pool[i] = info; - - switch (info) { - .long, .double => { - i += 1; - constant_pool[i] = null; - }, - else => {}, - } - } - - return .{ - .constant_pool = constant_pool, - }; -} - -pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { - for (self.constant_pool) |*info| { - if (info.*) |*c| c.deinit(allocator); - } - allocator.free(self.constant_pool); -} - -pub fn get(self: *const Self, index: u16) ResolveError!Info { - const actual_index = index - 1; - if (actual_index >= self.constant_pool.len) return ResolveError.InvalidConstantPoolIndex; - const info = self.constant_pool[actual_index]; - if (info == null) return ResolveError.InvalidConstantType; - return info.?; -} - -pub fn getOptional(self: *const Self, index: u16) ResolveError!?Info { - if (index == 0) return null; - return self.get(index); -} - -pub fn getTag(self: *const Self, index: u16, expected_tag: Info.Tag) ResolveError!Info { - const info = try self.get(index); - if (!info.is(expected_tag)) return ResolveError.InvalidConstantType; - return info; -} - -pub fn getTagOptional(self: *const Self, index: u16, expected_tag: Info.Tag) ResolveError!?Info { - if (index == 0) return null; - return try self.getTag(index, expected_tag); -} - -pub fn getLoadable(self: *const Self, index: u16) ResolveError!Info { - const info = try self.get(index); - if (!@as(Info.Tag, info).loadable()) return ResolveError.InvalidConstantType; - return info; -} - -pub fn getLoadableOptional(self: *const Self, index: u16) ResolveError!?Info { - if (index == 0) return null; - return self.getLoadable(index); -} - -pub fn formatInfo( - w: *std.Io.Writer, - depth: usize, - indent: u8, - comptime name: []const u8, - context: anytype, - comptime getter: std.meta.DeclEnum(@TypeOf(context.*)), - constant_pool: Self, -) std.Io.Writer.Error!void { - const func = @field(@TypeOf(context.*), @tagName(getter)); - - try w.splatByteAll(' ', depth * indent); - _ = try w.write(name ++ ":\n"); - const info = func(context, constant_pool) catch return error.WriteFailed; - try info.indentedFormat(w, depth, indent + 1, constant_pool); -} diff --git a/src/Class/FieldInfo.zig b/src/Class/FieldInfo.zig index fb16e55..565efb0 100644 --- a/src/Class/FieldInfo.zig +++ b/src/Class/FieldInfo.zig @@ -3,7 +3,7 @@ const std = @import("std"); const EnumFlags = @import("../EnumFlags.zig").EnumFlags; const Class = @import("../Class.zig"); -const ConstantPool = Class.ConstantPool; +const ConstantPoolInfo = Class.ConstantPoolInfo; const AttributeInfo = Class.AttributeInfo; const ResolveError = Class.ResolveError; @@ -14,7 +14,7 @@ attributes: []AttributeInfo, const Self = @This(); -pub const FieldAccessFlags = EnumFlags(enum(u16) { +pub const FieldFlags = enum(u16) { public = 0x0001, private = 0x0002, protected = 0x0004, @@ -24,13 +24,15 @@ pub const FieldAccessFlags = EnumFlags(enum(u16) { transient = 0x0080, synthetic = 0x1000, @"enum" = 0x4000, -}); +}; -pub fn parse(input: *std.Io.Reader, constant_pool: ConstantPool, allocator: std.mem.Allocator) !Self { - const access_flags: FieldAccessFlags = .from(try input.takeInt(u16, .big)); +pub const FieldAccessFlags = EnumFlags(FieldFlags); + +pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) !Self { + const access_flags: FieldAccessFlags = .{ .mask = try input.takeInt(u16, .big) }; const name_index = try input.takeInt(u16, .big); const descriptor_index = try input.takeInt(u16, .big); - const attributes = try Class.parseAttributes(input, constant_pool, allocator); + const attributes = try Class.parseAttributes(input, allocator); return .{ .access_flags = access_flags, .name_index = name_index, @@ -46,12 +48,13 @@ pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { allocator.free(self.attributes); } -pub fn indentedFormat( +pub fn format( self: *const Self, w: *std.Io.Writer, depth: usize, indent: u8, - constant_pool: ConstantPool, + allocator: std.mem.Allocator, + constant_pool: []const ?ConstantPoolInfo, ) std.Io.Writer.Error!void { try w.splatByteAll(' ', depth * indent); try w.print("flags: {f}\n", .{ self.access_flags }); @@ -59,12 +62,12 @@ pub fn indentedFormat( try w.splatByteAll(' ', depth * indent); _ = try w.write("name:\n"); const field_name = self.name(constant_pool) catch return error.WriteFailed; - try field_name.indentedFormat(w, depth, indent + 1, constant_pool); + try field_name.format(w, depth, indent + 1, constant_pool); try w.splatByteAll(' ', depth * indent); _ = try w.write("descriptor:\n"); const field_descriptor = self.descriptor(constant_pool) catch return error.WriteFailed; - try field_descriptor.indentedFormat(w, depth, indent + 1, constant_pool); + try field_descriptor.format(w, depth, indent + 1, constant_pool); try w.splatByteAll(' ', depth * indent); _ = try w.write("attributes:\n"); @@ -76,15 +79,23 @@ pub fn indentedFormat( try w.splatByteAll(' ', depth * attr_indent); try w.print("{d}:\n", .{ j }); - try attribute.indentedFormat(w, depth, attr_indent + 1, constant_pool); + try attribute.format(w, depth, attr_indent + 1, allocator, constant_pool); } } -pub fn name(self: *const Self, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { - return constant_pool.getTag(self.name_index, .utf8); +pub fn name(self: *const Self, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const name_index = self.name_index - 1; + if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[name_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; } -pub fn descriptor(self: *const Self, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { - return constant_pool.getTag(self.descriptor_index, .utf8); +pub fn descriptor(self: *const Self, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const descriptor_index = self.descriptor_index - 1; + if (descriptor_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[descriptor_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; } diff --git a/src/Class/MethodInfo.zig b/src/Class/MethodInfo.zig index 7d97300..0699c46 100644 --- a/src/Class/MethodInfo.zig +++ b/src/Class/MethodInfo.zig @@ -3,7 +3,7 @@ const std = @import("std"); const EnumFlags = @import("../EnumFlags.zig").EnumFlags; const Class = @import("../Class.zig"); -const ConstantPool = Class.ConstantPool; +const ConstantPoolInfo = Class.ConstantPoolInfo; const AttributeInfo = Class.AttributeInfo; const ResolveError = Class.ResolveError; @@ -14,7 +14,7 @@ attributes: []AttributeInfo, const Self = @This(); -pub const MethodAccessFlags = EnumFlags(enum(u16) { +pub const MethodFlags = enum(u16) { public = 0x0001, private = 0x0002, protected = 0x0004, @@ -27,13 +27,15 @@ pub const MethodAccessFlags = EnumFlags(enum(u16) { abstract = 0x0400, strict = 0x0800, synthetic = 0x1000, -}); +}; -pub fn parse(input: *std.Io.Reader, constant_pool: ConstantPool, allocator: std.mem.Allocator) !Self { - const access_flags: MethodAccessFlags = .from(try input.takeInt(u16, .big)); +pub const MethodAccessFlags = EnumFlags(MethodFlags); + +pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) !Self { + const access_flags: MethodAccessFlags = .{ .mask = try input.takeInt(u16, .big) }; const name_index = try input.takeInt(u16, .big); const descriptor_index = try input.takeInt(u16, .big); - const attributes = try Class.parseAttributes(input, constant_pool, allocator); + const attributes = try Class.parseAttributes(input, allocator); return .{ .access_flags = access_flags, .name_index = name_index, @@ -49,12 +51,13 @@ pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { allocator.free(self.attributes); } -pub fn indentedFormat( +pub fn format( self: *const Self, w: *std.Io.Writer, depth: usize, indent: u8, - constant_pool: ConstantPool, + allocator: std.mem.Allocator, + constant_pool: []const ?ConstantPoolInfo, ) std.Io.Writer.Error!void { try w.splatByteAll(' ', depth * indent); try w.print("flags: {f}\n", .{ self.access_flags }); @@ -62,12 +65,12 @@ pub fn indentedFormat( try w.splatByteAll(' ', depth * indent); _ = try w.write("name:\n"); const method_name = self.name(constant_pool) catch return error.WriteFailed; - try method_name.indentedFormat(w, depth, indent + 1, constant_pool); + try method_name.format(w, depth, indent + 1, constant_pool); try w.splatByteAll(' ', depth * indent); _ = try w.write("descriptor:\n"); const method_descriptor = self.descriptor(constant_pool) catch return error.WriteFailed; - try method_descriptor.indentedFormat(w, depth, indent + 1, constant_pool); + try method_descriptor.format(w, depth, indent + 1, constant_pool); try w.splatByteAll(' ', depth * indent); _ = try w.write("attributes:\n"); @@ -79,14 +82,23 @@ pub fn indentedFormat( try w.splatByteAll(' ', depth * attr_indent); try w.print("{d}:\n", .{ j }); - try attribute.indentedFormat(w, depth, attr_indent + 1, constant_pool); + try attribute.format(w, depth, attr_indent + 1, allocator, constant_pool); } } -pub fn name(self: *const Self, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { - return constant_pool.getTag(self.name_index, .utf8); +pub fn name(self: *const Self, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const name_index = self.name_index - 1; + if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[name_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; } -pub fn descriptor(self: *const Self, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { - return constant_pool.getTag(self.descriptor_index, .utf8); +pub fn descriptor(self: *const Self, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const descriptor_index = self.descriptor_index - 1; + if (descriptor_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[descriptor_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; } + diff --git a/src/EnumFlags.zig b/src/EnumFlags.zig index 80c7e40..9b5da99 100644 --- a/src/EnumFlags.zig +++ b/src/EnumFlags.zig @@ -10,12 +10,6 @@ pub fn EnumFlags(comptime E: type) type { pub const empty: Self = .{ .data = 0 }; - pub fn from(mask: BackingInt) Self { - return .{ - .mask = mask, - }; - } - pub fn contains(self: Self, flag: E) bool { return (self.mask & @intFromEnum(flag)) != 0; } diff --git a/src/main.zig b/src/main.zig index bf3f1f1..2dde461 100644 --- a/src/main.zig +++ b/src/main.zig @@ -50,9 +50,10 @@ pub fn main(init: std.process.Init) !void { }, else => return err, }; - defer class.deinit(allocator); + defer class.deinit(); try stdout.print("{f}", .{ class }); try stdout.flush(); } + diff --git a/testsuite/.gitignore b/testsuite/.gitignore deleted file mode 100644 index 415ec0f..0000000 --- a/testsuite/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.class -!Object.class -META-INF diff --git a/testsuite/classes/java/CustomAnnotation.java b/testsuite/classes/java/CustomAnnotation.java deleted file mode 100644 index 394a3f8..0000000 --- a/testsuite/classes/java/CustomAnnotation.java +++ /dev/null @@ -1,14 +0,0 @@ -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.lang.annotation.ElementType; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -@CustomAnnotationAnnotation -public @interface CustomAnnotation { - public CustomAnnotationType type(); - - public String name() default "Bob"; -} - diff --git a/testsuite/classes/java/CustomAnnotationAnnotation.java b/testsuite/classes/java/CustomAnnotationAnnotation.java deleted file mode 100644 index 67da5e7..0000000 --- a/testsuite/classes/java/CustomAnnotationAnnotation.java +++ /dev/null @@ -1,9 +0,0 @@ -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.lang.annotation.ElementType; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.ANNOTATION_TYPE) -public @interface CustomAnnotationAnnotation { } - diff --git a/testsuite/classes/java/CustomAnnotationType.java b/testsuite/classes/java/CustomAnnotationType.java deleted file mode 100644 index 77d0b61..0000000 --- a/testsuite/classes/java/CustomAnnotationType.java +++ /dev/null @@ -1,5 +0,0 @@ -public enum CustomAnnotationType { - ONE, - TWO, -} - diff --git a/testsuite/classes/java/CustomParameterAnnotation.java b/testsuite/classes/java/CustomParameterAnnotation.java deleted file mode 100644 index b031772..0000000 --- a/testsuite/classes/java/CustomParameterAnnotation.java +++ /dev/null @@ -1,9 +0,0 @@ -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.lang.annotation.ElementType; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.PARAMETER) -public @interface CustomParameterAnnotation { } - diff --git a/testsuite/classes/java/CustomTypeAnnotation.java b/testsuite/classes/java/CustomTypeAnnotation.java deleted file mode 100644 index b6572f7..0000000 --- a/testsuite/classes/java/CustomTypeAnnotation.java +++ /dev/null @@ -1,9 +0,0 @@ -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.lang.annotation.ElementType; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE_PARAMETER) -public @interface CustomTypeAnnotation { -} diff --git a/testsuite/classes/java/GenericClass.java b/testsuite/classes/java/GenericClass.java deleted file mode 100644 index 151a15a..0000000 --- a/testsuite/classes/java/GenericClass.java +++ /dev/null @@ -1,14 +0,0 @@ -import java.util.function.Supplier; - -public class GenericClass<@CustomTypeAnnotation T> { - - public Supplier getSupplier(T value) { - return new Supplier() { - @Override - public T get() { - return value; - } - }; - } - -} diff --git a/testsuite/classes/java/Hello.java b/testsuite/classes/java/Hello.java deleted file mode 100644 index a5a4fbf..0000000 --- a/testsuite/classes/java/Hello.java +++ /dev/null @@ -1,6 +0,0 @@ -class Hello { - public static void main(String[] args) { - System.out.println("Hello, world!"); - } -} - diff --git a/testsuite/classes/java/MultiThreading.java b/testsuite/classes/java/MultiThreading.java deleted file mode 100644 index 62d84f5..0000000 --- a/testsuite/classes/java/MultiThreading.java +++ /dev/null @@ -1,22 +0,0 @@ -import java.util.concurrent.Executors; -import java.util.stream.IntStream; -import java.util.concurrent.TimeUnit; - -public class MultiThreading { - private int number = 0; - - public static void main(String[] args) throws InterruptedException { - final var multiThreading = new MultiThreading(); - - final var service = Executors.newFixedThreadPool(3); - IntStream.range(0, 1000) - .forEach(count -> service.submit(multiThreading::increment)); - service.awaitTermination(1000, TimeUnit.MILLISECONDS); - - System.out.println(multiThreading.number); - } - - public synchronized void increment() { - number += 1; - } -} diff --git a/testsuite/classes/java/Object.class b/testsuite/classes/java/Object.class deleted file mode 100644 index d33531f..0000000 Binary files a/testsuite/classes/java/Object.class and /dev/null differ diff --git a/testsuite/classes/java/Record.java b/testsuite/classes/java/Record.java deleted file mode 100644 index 4eb2159..0000000 --- a/testsuite/classes/java/Record.java +++ /dev/null @@ -1,2 +0,0 @@ -public record Record(String name, int age) {} - diff --git a/testsuite/classes/kotlin/Main.kt b/testsuite/classes/kotlin/Main.kt deleted file mode 100644 index ca75765..0000000 --- a/testsuite/classes/kotlin/Main.kt +++ /dev/null @@ -1,12 +0,0 @@ -fun main() { - println("Hello, world!") - - Test().apply { - test = true - } -} - -class Test { - var test: Boolean = false -} - diff --git a/testsuite/modules/compile-simple-modules.sh b/testsuite/modules/compile-simple-modules.sh deleted file mode 100755 index ca384d8..0000000 --- a/testsuite/modules/compile-simple-modules.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env bash -javac -g -d outDir --module-source-path simple-modules $(find simple-modules -name "*.java") diff --git a/testsuite/modules/run-simple-module-app.sh b/testsuite/modules/run-simple-module-app.sh deleted file mode 100755 index 533d45a..0000000 --- a/testsuite/modules/run-simple-module-app.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env bash -java --module-path outDir -m main.app/dev.katkak.modules.main.MainApp diff --git a/testsuite/modules/simple-modules/hello.modules/dev/katkak/modules/hello/HelloInterface.java b/testsuite/modules/simple-modules/hello.modules/dev/katkak/modules/hello/HelloInterface.java deleted file mode 100644 index b98b718..0000000 --- a/testsuite/modules/simple-modules/hello.modules/dev/katkak/modules/hello/HelloInterface.java +++ /dev/null @@ -1,6 +0,0 @@ -package dev.katkak.modules.hello; - -public interface HelloInterface { - void sayHello(); -} - diff --git a/testsuite/modules/simple-modules/hello.modules/dev/katkak/modules/hello/HelloModules.java b/testsuite/modules/simple-modules/hello.modules/dev/katkak/modules/hello/HelloModules.java deleted file mode 100644 index cdbc53a..0000000 --- a/testsuite/modules/simple-modules/hello.modules/dev/katkak/modules/hello/HelloModules.java +++ /dev/null @@ -1,12 +0,0 @@ -package dev.katkak.modules.hello; - -public class HelloModules implements HelloInterface { - public static void doSomething() { - System.out.println("Hello, Modules!"); - } - - public void sayHello() { - System.out.println("Hello!"); - } -} - diff --git a/testsuite/modules/simple-modules/hello.modules/module-info.java b/testsuite/modules/simple-modules/hello.modules/module-info.java deleted file mode 100644 index 4dfc3e0..0000000 --- a/testsuite/modules/simple-modules/hello.modules/module-info.java +++ /dev/null @@ -1,6 +0,0 @@ -module hello.modules { - exports dev.katkak.modules.hello; - - provides dev.katkak.modules.hello.HelloInterface with dev.katkak.modules.hello.HelloModules; -} - diff --git a/testsuite/modules/simple-modules/main.app/dev/katkak/modules/main/MainApp.java b/testsuite/modules/simple-modules/main.app/dev/katkak/modules/main/MainApp.java deleted file mode 100644 index ac4eda3..0000000 --- a/testsuite/modules/simple-modules/main.app/dev/katkak/modules/main/MainApp.java +++ /dev/null @@ -1,17 +0,0 @@ -package dev.katkak.modules.main; - -import dev.katkak.modules.hello.HelloModules; -import dev.katkak.modules.hello.HelloInterface; - -import java.util.ServiceLoader; - -public class MainApp { - public static void main(String[] args) { - HelloModules.doSomething(); - - final var services = ServiceLoader.load(HelloInterface.class); - final var service = services.iterator().next(); - service.sayHello(); - } -} - diff --git a/testsuite/modules/simple-modules/main.app/module-info.java b/testsuite/modules/simple-modules/main.app/module-info.java deleted file mode 100644 index d7e9447..0000000 --- a/testsuite/modules/simple-modules/main.app/module-info.java +++ /dev/null @@ -1,6 +0,0 @@ -module main.app { - requires hello.modules; - - uses dev.katkak.modules.hello.HelloInterface; -} -