diff --git a/flake.nix b/flake.nix index c48263a..b1ef715 100644 --- a/flake.nix +++ b/flake.nix @@ -19,6 +19,8 @@ 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 2f09ba7..09a3846 100644 --- a/src/Class.zig +++ b/src/Class.zig @@ -4,14 +4,13 @@ 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"); - -allocator: std.mem.Allocator, +pub const AttributeInfo = @import("Class/AttributeInfo.zig").AttributeInfo; +pub const ConstantPool = @import("Class/ConstantPool.zig"); magic: u32, minor_version: u16, major_version: u16, -constant_pool: []?ConstantPoolInfo, +constant_pool: ConstantPool, access_flags: ClassAccessFlags, this_class: u16, super_class: u16, @@ -22,7 +21,7 @@ attributes: []AttributeInfo, const Self = @This(); -pub const ClassFlags = enum(u16) { +pub const ClassAccessFlags = EnumFlags(enum(u16) { public = 0x0001, final = 0x0010, super = 0x0020, @@ -31,9 +30,8 @@ pub const ClassFlags = enum(u16) { synthetic = 0x1000, annotation = 0x2000, @"enum" = 0x4000, -}; - -pub const ClassAccessFlags = EnumFlags(ClassFlags); + module = 0x8000, +}); pub const ParseError = std.Io.Reader.Error || std.mem.Allocator.Error || error { InvalidMagicNumber, @@ -45,7 +43,7 @@ pub const ResolveError = error { InvalidConstantType, }; -pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { +pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) (ParseError || ResolveError)!Self { const magic = try input.takeInt(u32, .big); if (magic != 0xCAFEBABE) { return ParseError.InvalidMagicNumber; @@ -54,15 +52,10 @@ pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Sel const minor_version = try input.takeInt(u16, .big); const major_version = try input.takeInt(u16, .big); - const constant_pool = try parseConstantPool(input, allocator); - errdefer { - for (constant_pool) |*cp_info| { - if (cp_info.*) |*c| c.deinit(allocator); - } - allocator.free(constant_pool); - } + var constant_pool: ConstantPool = try .parse(input, allocator); + errdefer constant_pool.deinit(allocator); - const access_flags: ClassAccessFlags = .{ .mask = try input.takeInt(u16, .big) }; + const access_flags: ClassAccessFlags = .from(try input.takeInt(u16, .big)); const this_class = try input.takeInt(u16, .big); const super_class = try input.takeInt(u16, .big); @@ -70,7 +63,7 @@ pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Sel const interfaces = try parseInterfaces(input, allocator); errdefer allocator.free(interfaces); - const fields = try parseFields(input, allocator); + const fields = try parseFields(input, constant_pool, allocator); errdefer { for (fields) |*field| { field.deinit(allocator); @@ -78,7 +71,7 @@ pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Sel allocator.free(fields); } - const methods = try parseMethods(input, allocator); + const methods = try parseMethods(input, constant_pool, allocator); errdefer { for (methods) |*method| { method.deinit(allocator); @@ -86,7 +79,7 @@ pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Sel allocator.free(methods); } - const attributes = try parseAttributes(input, allocator); + const attributes = try parseAttributes(input, constant_pool, allocator); errdefer { for (attributes) |*attribute| { attribute.deinit(allocator); @@ -95,7 +88,6 @@ pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Sel } return .{ - .allocator = allocator, .magic = magic, .minor_version = minor_version, .major_version = major_version, @@ -110,30 +102,6 @@ pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Sel }; } -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); @@ -146,80 +114,73 @@ fn parseInterfaces(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseErr return interfaces; } -fn parseFields(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError![]FieldInfo { +fn parseFields(input: *std.Io.Reader, constant_pool: ConstantPool, allocator: std.mem.Allocator) (ParseError || ResolveError)![]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, allocator); + fields[i] = try FieldInfo.parse(input, constant_pool, allocator); } return fields; } -fn parseMethods(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError![]MethodInfo { +fn parseMethods(input: *std.Io.Reader, constant_pool: ConstantPool, allocator: std.mem.Allocator) (ParseError || ResolveError)![]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 MethodInfo.parse(input, allocator); + methods[i] = try .parse(input, constant_pool, allocator); } return methods; } -pub fn parseAttributes(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError![]AttributeInfo { +pub fn parseAttributes(input: *std.Io.Reader, constant_pool: ConstantPool, allocator: std.mem.Allocator) (ParseError || ResolveError)![]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, allocator); + attributes[i] = try AttributeInfo.parse(input, constant_pool, allocator); } return attributes; } -pub fn deinit(self: *Self) void { - self.freeConstantPool(); - self.freeInterfaces(); - self.freeFields(); - self.freeMethods(); - self.freeAttributes(); +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); } -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 freeInterfaces(self: *Self, allocator: std.mem.Allocator) void { + allocator.free(self.interfaces); } -fn freeInterfaces(self: *Self) void { - self.allocator.free(self.interfaces); -} - -fn freeFields(self: *Self) void { +fn freeFields(self: *Self, allocator: std.mem.Allocator) void { for (self.fields) |*field_info| { - field_info.deinit(self.allocator); + field_info.deinit(allocator); } - self.allocator.free(self.fields); + allocator.free(self.fields); } -fn freeMethods(self: *Self) void { +fn freeMethods(self: *Self, allocator: std.mem.Allocator) void { for (self.methods) |*method_info| { - method_info.deinit(self.allocator); + method_info.deinit(allocator); } - self.allocator.free(self.methods); + allocator.free(self.methods); } -fn freeAttributes(self: *Self) void { +fn freeAttributes(self: *Self, allocator: std.mem.Allocator) void { for (self.attributes) |*attr| { - attr.deinit(self.allocator); + attr.deinit(allocator); } - self.allocator.free(self.attributes); + allocator.free(self.attributes); } pub fn jsonStringify(self: *const Self, jws: *std.json.Stringify) !void { @@ -246,11 +207,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.format(w, depth, indent + 1, self.constant_pool); + try this_class.indentedFormat(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.format(w, depth, indent + 1, self.constant_pool); + try super_class.indentedFormat(w, depth, indent + 1, self.constant_pool); } _ = try w.write("interfaces:\n"); @@ -258,13 +219,11 @@ pub fn format(self: *const Self, w: *std.Io.Writer) std.Io.Writer.Error!void { indent += 1; defer indent -= 1; - 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; + const info = self.constant_pool.getTag(interface, .class) catch return error.WriteFailed; try w.splatByteAll(' ', depth * indent); try w.print("{d}:\n", .{ i }); - try cp_info.?.format(w, depth, indent + 1, self.constant_pool); + try info.indentedFormat(w, depth, indent + 1, self.constant_pool); } _ = try w.write("fields:\n"); @@ -274,7 +233,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.format(w, depth, indent + 1, self.allocator, self.constant_pool); + try field.indentedFormat(w, depth, indent + 1, self.constant_pool); } _ = try w.write("methods:\n"); @@ -284,7 +243,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.format(w, depth, indent + 1, self.allocator, self.constant_pool); + try method.indentedFormat(w, depth, indent + 1, self.constant_pool); } _ = try w.write("attributes:\n"); @@ -294,476 +253,15 @@ 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.format(w, depth, indent + 1, self.allocator, self.constant_pool); + try attribute.indentedFormat(w, depth, indent + 1, self.constant_pool); } } -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 thisClass(self: *const Self) ResolveError!ConstantPool.Info { + return self.constant_pool.getTag(self.this_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 fn superClass(self: *const Self) ResolveError!?ConstantPool.Info { + return self.constant_pool.getTagOptional(self.super_class, .class); } -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 a3276da..5fbdc9f 100644 --- a/src/Class/AttributeInfo.zig +++ b/src/Class/AttributeInfo.zig @@ -3,300 +3,1139 @@ const std = @import("std"); const EnumFlags = @import("../EnumFlags.zig").EnumFlags; const Class = @import("../Class.zig"); -const ConstantPoolInfo = Class.ConstantPoolInfo; -const AttributeInfo = Class.AttributeInfo; +const ConstantPool = Class.ConstantPool; const ParseError = Class.ParseError; const ResolveError = Class.ResolveError; -attribute_name_index: u16, -info: []u8, +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, + }, -const Self = @This(); + const Self = @This(); -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, + 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 ConstantValue = struct { - constant_value_index: u16, + const attribute_length = try input.takeInt(u32, .big); - 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; + 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; - return cp_info.?; + 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), + }; + } + } + + 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 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); + }, + .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 }); + + 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 }); + } + + 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 exception.indentedFormat(w, depth, exception_indent + 1, constant_pool); + } + + 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); + } + }, + .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; + + 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 const NewAttribute = struct { + attribute_name_index: u16, + info: []u8, + + const Self = @This(); + + 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), + }; + } + + pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + allocator.free(self.info); + } + + pub fn attributeName(self: *const Self, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.attribute_name_index, .utf8); + } +}; + +pub const ConstantValue = struct { + constant_value_index: u16, + + 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| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("catch_type:\n"); + try ct.indentedFormat(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 const Code = struct { - max_stack: u16, - max_locals: u16, - code: []u8, - exception_table: []ExceptionHandler, - attributes: []AttributeInfo, + 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); - pub const ExceptionHandler = struct { - start_pc: u16, - end_pc: u16, - handler_pc: u16, - catch_type: u16, - }; - }; + const code_length = try input.takeInt(u32, .big); + const code = try input.readAlloc(allocator, code_length); + errdefer allocator.free(code); - 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, - }, - // 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), - }, - }, - 252...254 => blk: { - const offset_delta = try input.takeInt(u16, .big); - - 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); - } - - 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(Predefined.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(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, - }, - }; - }, - 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 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 Tag = enum(u8) { - top = 0, - integer = 1, - float = 2, - double = 3, - long = 4, - null = 5, - uninitialized_this = 6, - object = 7, - uninitialized = 8, + 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), }; + } - 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; + 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 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), - }, - }, - }; - } + 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 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.*) }); + + 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 => {}, + } + } }; - pub const Exceptions = struct { - exception_index_table: []u16, - }; + pub const VerificationTypeInfo = union(VerificationTypeInfo.Tag) { + top, + integer, + float, + double, + long, + null, + uninitialized_this, + object: Object, + uninitialized: Uninitialized, - pub const InnerClasses = struct { - classes: []InnerClass, + 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 InnerClass = struct { - inner_class_info_index: u16, - outer_class_info_index: u16, - inner_name_index: u16, - inner_class_access_flags: InnerClassAccessFlags, + pub const Object = struct { + cpool_index: u16, - 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.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 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 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; - - 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 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; - - 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 fn class(self: *const Object, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.cpool_index, .class); } }; - pub const InnerClassFlags = enum(u16) { + 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) { public = 0x0001, private = 0x0002, protected = 0x0004, @@ -307,719 +1146,1285 @@ pub const Predefined = union(enum) { synthetic = 0x1000, annotation = 0x2000, @"enum" = 0x4000, - }; + }); - pub const InnerClassAccessFlags = EnumFlags(InnerClassFlags); - }; + 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 EnclosingMethod = struct { - class_index: u16, - method_index: u16, + 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 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.?; + 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 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 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 const Synthetic = struct { }; - - pub const Signature = struct { - signature_index: u16, - - 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 const SourceFile = struct { - source_file_index: u16, - - 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 SourceDebugExtension = struct { - debug_extension: []u8, - }; - - pub const LineNumberTable = struct { - line_number_table: []const LineNumber, - - pub const LineNumber = struct { - start_pc: u16, - line_number: u16, - }; - }; - - pub const LocalVariableTable = struct { - local_variable_table: []LocalVariable, - - pub const LocalVariable = struct { - start_pc: u16, - length: u16, - name_index: u16, - descriptor_index: u16, - index: u16, - }; - }; - - pub const LocalVariableTypeTable = struct { - local_variable_type_table: []LocalVariableType, - - pub const LocalVariableType = struct { - start_pc: u16, - length: u16, - name_index: u16, - signature_index: u16, - index: u16, - }; - }; - - pub const Deprecated = struct { }; - - 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 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); - } - - break :blk .{ - .values = values, - }; - }, - }, - else => unreachable, + 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 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); + 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, + 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 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), + }; + } + + 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 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 ClassConstValue = struct { + index: u16, + + pub fn descriptor(self: *const ClassConstValue, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.index, .utf8); + } + }; + + 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); } - allocator.free(element_value.values); + + break :blk .{ + .values = values, + }; + }, + }, + else => unreachable, + }; + } + + 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 TypeParameter = struct { + type_parameter_index: u8, + }; + + pub const SuperType = struct { + supertype_index: u16, + }; + + pub const TypeParameterBound = struct { + type_parameter_index: u8, + bound_index: u8, + }; + + pub const Empty = struct { }; + + 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 }); + } + }; + }; + + 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.* }); + + 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); + } + }, + .@"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 }); }, else => {}, } } }; - pub const Annotation = struct { - type_index: u16, - element_value_pairs: []ElementValuePair, + pub const TypePath = struct { + paths: []Path, - pub const ElementValuePair = struct { - element_name_index: u16, - value: ElementValue, + 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 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), + 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(), }; } return .{ - .type_index = type_index, - .element_value_pairs = element_value_pairs, + .paths = paths, }; } - pub fn deinit(self: *Annotation, allocator: std.mem.Allocator) void { - for (self.element_value_pairs) |*element_value_pair| { - element_value_pair.value.deinit(allocator); + 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); } - allocator.free(self.element_value_pairs); } }; - pub const RuntimeVisibleAnnotations = struct { - annotations: []Annotation, - }; + 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 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, + break :blk .{ + .localvar = .{ + .table = table, + }, + }; + }, + .type_in_exception_parameter => .{ + .@"catch" = . { + .exception_table_index = try input.takeInt(u16, .big), + }, + }, + .type_in_instanceof, .type_in_new, .type_in_method_reference_new, .type_in_method_reference_identifier => .{ + .offset = .{ + .offset = try input.takeInt(u16, .big), + }, + }, + .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(), + }, + }, }; - }; - 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); - } - allocator.free(attribute.attributes); - }, - .stack_map_table => |attribute| { - for (attribute.entries) |*entry| { - entry.deinit(allocator); - } - allocator.free(attribute.entries); - }, - .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); - }, - .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 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 const RuntimeVisibleTypeAnnotations = struct { + type_annotations: []TypeAnnotation, -pub fn deinit(self: *AttributeInfo, allocator: std.mem.Allocator) void { - allocator.free(self.info); -} + const Self = @This(); -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 => {}, + 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); } - } else { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("predefined: no\n"); + + return .{ + .type_annotations = type_annotations, + }; } -} -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), - }; + 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); + } +}; - 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); +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 .{ - .code = .{ - .max_stack = max_stack, - .max_locals = max_locals, - .code = code, - .exception_table = exception_table, - .attributes = attributes, - }, + .type_annotations = type_annotations, }; - } 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); + 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); + } +}; - 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) }, - }; - } +pub const AnnotationDefault = struct { + default_value: ElementValue, - 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), - }; - } + const Self = @This(); + pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { return .{ - .line_number_table = .{ - .line_number_table = line_number_table, - }, + .default_value = try .parse(input, allocator), }; - } 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), - }; - } + pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + self.default_value.deinit(allocator); + } +}; - 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); - } +pub const BootstrapMethods = struct { + bootstrap_methods: []BootstrapMethod, - 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); - } + const Self = @This(); - 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); + 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); } - - 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, - }; + pub fn bootstrapMethod(self: *const BootstrapMethod, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.bootstrap_method_ref, .method_handle); } + }; - 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); + 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); errdefer allocator.free(bootstrap_methods); for (bootstrap_methods) |*bootstrap_method| { - const bootstrap_method_ref = try r.takeInt(u16, .big); + const bootstrap_method_ref = try input.takeInt(u16, .big); - const num_bootstrap_arguments = try r.takeInt(u16, .big); + const num_bootstrap_arguments = try input.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 r.takeInt(u16, .big); + bootstrap_argument.* = try input.takeInt(u16, .big); } bootstrap_method.* = .{ @@ -1029,12 +2434,658 @@ pub fn resolve(self: *const AttributeInfo, allocator: std.mem.Allocator, constan } 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/ConstantPool.zig b/src/Class/ConstantPool.zig new file mode 100644 index 0000000..54c233f --- /dev/null +++ b/src/Class/ConstantPool.zig @@ -0,0 +1,547 @@ +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 565efb0..fb16e55 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 ConstantPoolInfo = Class.ConstantPoolInfo; +const ConstantPool = Class.ConstantPool; const AttributeInfo = Class.AttributeInfo; const ResolveError = Class.ResolveError; @@ -14,7 +14,7 @@ attributes: []AttributeInfo, const Self = @This(); -pub const FieldFlags = enum(u16) { +pub const FieldAccessFlags = EnumFlags(enum(u16) { public = 0x0001, private = 0x0002, protected = 0x0004, @@ -24,15 +24,13 @@ pub const FieldFlags = enum(u16) { transient = 0x0080, synthetic = 0x1000, @"enum" = 0x4000, -}; +}); -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) }; +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)); const name_index = try input.takeInt(u16, .big); const descriptor_index = try input.takeInt(u16, .big); - const attributes = try Class.parseAttributes(input, allocator); + const attributes = try Class.parseAttributes(input, constant_pool, allocator); return .{ .access_flags = access_flags, .name_index = name_index, @@ -48,13 +46,12 @@ pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { allocator.free(self.attributes); } -pub fn format( +pub fn indentedFormat( self: *const Self, w: *std.Io.Writer, depth: usize, indent: u8, - allocator: std.mem.Allocator, - constant_pool: []const ?ConstantPoolInfo, + constant_pool: ConstantPool, ) std.Io.Writer.Error!void { try w.splatByteAll(' ', depth * indent); try w.print("flags: {f}\n", .{ self.access_flags }); @@ -62,12 +59,12 @@ pub fn format( try w.splatByteAll(' ', depth * indent); _ = try w.write("name:\n"); const field_name = self.name(constant_pool) catch return error.WriteFailed; - try field_name.format(w, depth, indent + 1, constant_pool); + try field_name.indentedFormat(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.format(w, depth, indent + 1, constant_pool); + try field_descriptor.indentedFormat(w, depth, indent + 1, constant_pool); try w.splatByteAll(' ', depth * indent); _ = try w.write("attributes:\n"); @@ -79,23 +76,15 @@ pub fn format( try w.splatByteAll(' ', depth * attr_indent); try w.print("{d}:\n", .{ j }); - try attribute.format(w, depth, attr_indent + 1, allocator, constant_pool); + try attribute.indentedFormat(w, depth, attr_indent + 1, constant_pool); } } -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 name(self: *const Self, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.name_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.?; +pub fn descriptor(self: *const Self, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.descriptor_index, .utf8); } diff --git a/src/Class/MethodInfo.zig b/src/Class/MethodInfo.zig index 0699c46..7d97300 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 ConstantPoolInfo = Class.ConstantPoolInfo; +const ConstantPool = Class.ConstantPool; const AttributeInfo = Class.AttributeInfo; const ResolveError = Class.ResolveError; @@ -14,7 +14,7 @@ attributes: []AttributeInfo, const Self = @This(); -pub const MethodFlags = enum(u16) { +pub const MethodAccessFlags = EnumFlags(enum(u16) { public = 0x0001, private = 0x0002, protected = 0x0004, @@ -27,15 +27,13 @@ pub const MethodFlags = enum(u16) { abstract = 0x0400, strict = 0x0800, synthetic = 0x1000, -}; +}); -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) }; +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)); const name_index = try input.takeInt(u16, .big); const descriptor_index = try input.takeInt(u16, .big); - const attributes = try Class.parseAttributes(input, allocator); + const attributes = try Class.parseAttributes(input, constant_pool, allocator); return .{ .access_flags = access_flags, .name_index = name_index, @@ -51,13 +49,12 @@ pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { allocator.free(self.attributes); } -pub fn format( +pub fn indentedFormat( self: *const Self, w: *std.Io.Writer, depth: usize, indent: u8, - allocator: std.mem.Allocator, - constant_pool: []const ?ConstantPoolInfo, + constant_pool: ConstantPool, ) std.Io.Writer.Error!void { try w.splatByteAll(' ', depth * indent); try w.print("flags: {f}\n", .{ self.access_flags }); @@ -65,12 +62,12 @@ pub fn format( try w.splatByteAll(' ', depth * indent); _ = try w.write("name:\n"); const method_name = self.name(constant_pool) catch return error.WriteFailed; - try method_name.format(w, depth, indent + 1, constant_pool); + try method_name.indentedFormat(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.format(w, depth, indent + 1, constant_pool); + try method_descriptor.indentedFormat(w, depth, indent + 1, constant_pool); try w.splatByteAll(' ', depth * indent); _ = try w.write("attributes:\n"); @@ -82,23 +79,14 @@ pub fn format( try w.splatByteAll(' ', depth * attr_indent); try w.print("{d}:\n", .{ j }); - try attribute.format(w, depth, attr_indent + 1, allocator, constant_pool); + try attribute.indentedFormat(w, depth, attr_indent + 1, constant_pool); } } -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 name(self: *const Self, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.name_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.?; +pub fn descriptor(self: *const Self, constant_pool: ConstantPool) ResolveError!ConstantPool.Info { + return constant_pool.getTag(self.descriptor_index, .utf8); } - diff --git a/src/EnumFlags.zig b/src/EnumFlags.zig index 9b5da99..80c7e40 100644 --- a/src/EnumFlags.zig +++ b/src/EnumFlags.zig @@ -10,6 +10,12 @@ 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 2dde461..bf3f1f1 100644 --- a/src/main.zig +++ b/src/main.zig @@ -50,10 +50,9 @@ pub fn main(init: std.process.Init) !void { }, else => return err, }; - defer class.deinit(); + defer class.deinit(allocator); try stdout.print("{f}", .{ class }); try stdout.flush(); } - diff --git a/testsuite/.gitignore b/testsuite/.gitignore new file mode 100644 index 0000000..415ec0f --- /dev/null +++ b/testsuite/.gitignore @@ -0,0 +1,3 @@ +*.class +!Object.class +META-INF diff --git a/testsuite/classes/java/CustomAnnotation.java b/testsuite/classes/java/CustomAnnotation.java new file mode 100644 index 0000000..394a3f8 --- /dev/null +++ b/testsuite/classes/java/CustomAnnotation.java @@ -0,0 +1,14 @@ +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 new file mode 100644 index 0000000..67da5e7 --- /dev/null +++ b/testsuite/classes/java/CustomAnnotationAnnotation.java @@ -0,0 +1,9 @@ +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 new file mode 100644 index 0000000..77d0b61 --- /dev/null +++ b/testsuite/classes/java/CustomAnnotationType.java @@ -0,0 +1,5 @@ +public enum CustomAnnotationType { + ONE, + TWO, +} + diff --git a/testsuite/classes/java/CustomParameterAnnotation.java b/testsuite/classes/java/CustomParameterAnnotation.java new file mode 100644 index 0000000..b031772 --- /dev/null +++ b/testsuite/classes/java/CustomParameterAnnotation.java @@ -0,0 +1,9 @@ +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 new file mode 100644 index 0000000..b6572f7 --- /dev/null +++ b/testsuite/classes/java/CustomTypeAnnotation.java @@ -0,0 +1,9 @@ +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 new file mode 100644 index 0000000..151a15a --- /dev/null +++ b/testsuite/classes/java/GenericClass.java @@ -0,0 +1,14 @@ +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 new file mode 100644 index 0000000..a5a4fbf --- /dev/null +++ b/testsuite/classes/java/Hello.java @@ -0,0 +1,6 @@ +class Hello { + public static void main(String[] args) { + System.out.println("Hello, world!"); + } +} + diff --git a/Interface.java b/testsuite/classes/java/Interface.java similarity index 100% rename from Interface.java rename to testsuite/classes/java/Interface.java diff --git a/Main.java b/testsuite/classes/java/Main.java similarity index 61% rename from Main.java rename to testsuite/classes/java/Main.java index a3c6d9b..0f33998 100644 --- a/Main.java +++ b/testsuite/classes/java/Main.java @@ -1,17 +1,27 @@ 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() { - new Main().sayHello(); + 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"); + } } - + @Override public void run() { - Main.main(); + Main.main(new String[] { "Hello" }); } @Override @@ -37,4 +47,3 @@ public class Main implements Runnable, Supplier, Interface { private static final String name = "Inner John"; } } - diff --git a/testsuite/classes/java/MultiThreading.java b/testsuite/classes/java/MultiThreading.java new file mode 100644 index 0000000..62d84f5 --- /dev/null +++ b/testsuite/classes/java/MultiThreading.java @@ -0,0 +1,22 @@ +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 new file mode 100644 index 0000000..d33531f Binary files /dev/null and b/testsuite/classes/java/Object.class differ diff --git a/testsuite/classes/java/Record.java b/testsuite/classes/java/Record.java new file mode 100644 index 0000000..4eb2159 --- /dev/null +++ b/testsuite/classes/java/Record.java @@ -0,0 +1,2 @@ +public record Record(String name, int age) {} + diff --git a/testsuite/classes/kotlin/Main.kt b/testsuite/classes/kotlin/Main.kt new file mode 100644 index 0000000..ca75765 --- /dev/null +++ b/testsuite/classes/kotlin/Main.kt @@ -0,0 +1,12 @@ +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 new file mode 100755 index 0000000..ca384d8 --- /dev/null +++ b/testsuite/modules/compile-simple-modules.sh @@ -0,0 +1,2 @@ +#!/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 new file mode 100755 index 0000000..533d45a --- /dev/null +++ b/testsuite/modules/run-simple-module-app.sh @@ -0,0 +1,2 @@ +#!/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 new file mode 100644 index 0000000..b98b718 --- /dev/null +++ b/testsuite/modules/simple-modules/hello.modules/dev/katkak/modules/hello/HelloInterface.java @@ -0,0 +1,6 @@ +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 new file mode 100644 index 0000000..cdbc53a --- /dev/null +++ b/testsuite/modules/simple-modules/hello.modules/dev/katkak/modules/hello/HelloModules.java @@ -0,0 +1,12 @@ +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 new file mode 100644 index 0000000..4dfc3e0 --- /dev/null +++ b/testsuite/modules/simple-modules/hello.modules/module-info.java @@ -0,0 +1,6 @@ +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 new file mode 100644 index 0000000..ac4eda3 --- /dev/null +++ b/testsuite/modules/simple-modules/main.app/dev/katkak/modules/main/MainApp.java @@ -0,0 +1,17 @@ +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 new file mode 100644 index 0000000..d7e9447 --- /dev/null +++ b/testsuite/modules/simple-modules/main.app/module-info.java @@ -0,0 +1,6 @@ +module main.app { + requires hello.modules; + + uses dev.katkak.modules.hello.HelloInterface; +} +