From eacf0381c944178fabf423bef9739c2241f7a706 Mon Sep 17 00:00:00 2001 From: ktkk Date: Wed, 24 Jun 2026 14:32:27 +0000 Subject: [PATCH] Modularize --- src/Class.zig | 1441 +---------------- src/Class/AttributeInfo.zig | 1040 ++++++++++++ src/Class/ConstantPoolInfo/Class.zig | 16 + src/Class/ConstantPoolInfo/FieldRef.zig | 25 + .../ConstantPoolInfo/InterfaceMethodRef.zig | 25 + src/Class/ConstantPoolInfo/InvokeDynamic.zig | 16 + src/Class/ConstantPoolInfo/MethodHandle.zig | 41 + src/Class/ConstantPoolInfo/MethodRef.zig | 25 + src/Class/ConstantPoolInfo/MethodType.zig | 16 + src/Class/ConstantPoolInfo/NameAndType.zig | 25 + src/Class/ConstantPoolInfo/String.zig | 16 + src/Class/FieldInfo.zig | 101 ++ src/Class/MethodInfo.zig | 104 ++ 13 files changed, 1492 insertions(+), 1399 deletions(-) create mode 100644 src/Class/AttributeInfo.zig create mode 100644 src/Class/ConstantPoolInfo/Class.zig create mode 100644 src/Class/ConstantPoolInfo/FieldRef.zig create mode 100644 src/Class/ConstantPoolInfo/InterfaceMethodRef.zig create mode 100644 src/Class/ConstantPoolInfo/InvokeDynamic.zig create mode 100644 src/Class/ConstantPoolInfo/MethodHandle.zig create mode 100644 src/Class/ConstantPoolInfo/MethodRef.zig create mode 100644 src/Class/ConstantPoolInfo/MethodType.zig create mode 100644 src/Class/ConstantPoolInfo/NameAndType.zig create mode 100644 src/Class/ConstantPoolInfo/String.zig create mode 100644 src/Class/FieldInfo.zig create mode 100644 src/Class/MethodInfo.zig diff --git a/src/Class.zig b/src/Class.zig index 29baeff..198ff72 100644 --- a/src/Class.zig +++ b/src/Class.zig @@ -2,12 +2,16 @@ const std = @import("std"); 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, magic: u32, minor_version: u16, major_version: u16, -constant_pool: []?CpInfo, +constant_pool: []?ConstantPoolInfo, access_flags: ClassAccessFlags, this_class: u16, super_class: u16, @@ -18,12 +22,25 @@ attributes: []AttributeInfo, const Self = @This(); -const ParseError = std.Io.Reader.Error || std.mem.Allocator.Error || error { +pub const ClassFlags = enum(u16) { + public = 0x0001, + final = 0x0010, + super = 0x0020, + interface = 0x0200, + abstract = 0x0400, + synthetic = 0x1000, + annotation = 0x2000, + @"enum" = 0x4000, +}; + +pub const ClassAccessFlags = EnumFlags(ClassFlags); + +pub const ParseError = std.Io.Reader.Error || std.mem.Allocator.Error || error { InvalidMagicNumber, InvalidTag, }; -const ResolveError = error { +pub const ResolveError = error { InvalidConstantPoolIndex, InvalidConstantType, }; @@ -93,16 +110,16 @@ pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Sel }; } -fn parseConstantPool(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError![]?CpInfo { +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(?CpInfo, constant_pool_size - 1); + 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 CpInfo.parse(input, allocator); + const cp_info = try ConstantPoolInfo.parse(input, allocator); constant_pool[i] = cp_info; switch (cp_info) { @@ -153,7 +170,7 @@ fn parseMethods(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError! return methods; } -fn parseAttributes(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError![]AttributeInfo { +pub fn parseAttributes(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError![]AttributeInfo { const attributes_count = try input.takeInt(u16, .big); const attributes = try allocator.alloc(AttributeInfo, attributes_count); errdefer allocator.free(attributes); @@ -243,7 +260,7 @@ pub fn format(self: *const Self, w: *std.Io.Writer) std.Io.Writer.Error!void { if (interface >= self.constant_pool.len) return error.WriteFailed; const cp_info = self.constant_pool[interface - 1]; - if (cp_info == null or @as(CpInfo.Tag, cp_info.?) != .class) return error.WriteFailed; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return error.WriteFailed; try w.splatByteAll(' ', depth * indent); try w.print("{d}:\n", .{ i }); @@ -281,15 +298,15 @@ pub fn format(self: *const Self, w: *std.Io.Writer) std.Io.Writer.Error!void { } } -pub fn thisClass(self: *const Self) ResolveError!CpInfo { +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(CpInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; return cp_info.?; } -pub fn superClass(self: *const Self) ResolveError!?CpInfo { +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; @@ -299,7 +316,7 @@ pub fn superClass(self: *const Self) ResolveError!?CpInfo { 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(CpInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; + 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; @@ -311,7 +328,7 @@ pub fn superClass(self: *const Self) ResolveError!?CpInfo { } } -pub const CpInfo = union(CpInfo.Tag) { +pub const ConstantPoolInfo = union(ConstantPoolInfo.Tag) { class: Class, field_ref: FieldRef, method_ref: MethodRef, @@ -344,92 +361,11 @@ pub const CpInfo = union(CpInfo.Tag) { invoke_dynamic = 18, }; - pub const Class = struct { - name_index: u16, - - pub fn name(self: *const Class, constant_pool: []const ?CpInfo) ResolveError!CpInfo { - 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 ?CpInfo) ResolveError!CpInfo { - 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 ?CpInfo) ResolveError!CpInfo { - 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 ?CpInfo) ResolveError!CpInfo { - 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 ?CpInfo) ResolveError!CpInfo { - 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 ?CpInfo) ResolveError!CpInfo { - 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 ?CpInfo) ResolveError!CpInfo { - 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 ?CpInfo) ResolveError!CpInfo { - 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 Class = @import("Class/ConstantPoolInfo/Class.zig"); + pub const FieldRef = @import("Class/ConstantPoolInfo/FieldRef.zig"); + pub const MethodRef = @import("Class/ConstantPoolInfo/MethodRef.zig"); + pub const InterfaceMethodRef = @import("Class/ConstantPoolInfo/InterfaceMethodRef.zig"); + pub const String = @import("Class/ConstantPoolInfo/String.zig"); pub const Integer = struct { bytes: u32, @@ -449,94 +385,17 @@ pub const CpInfo = union(CpInfo.Tag) { low_bytes: u32, }; - pub const NameAndType = struct { - name_index: u16, - descriptor_index: u16, - - pub fn name(self: *const NameAndType, constant_pool: []const ?CpInfo) ResolveError!CpInfo { - 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 ?CpInfo) ResolveError!CpInfo { - 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 NameAndType = @import("Class/ConstantPoolInfo/NameAndType.zig"); pub const Utf8 = struct { bytes: []const u8, }; - pub const MethodHandle = struct { - reference_kind: Kind, - reference_index: u16, + pub const MethodHandle = @import("Class/ConstantPoolInfo/MethodHandle.zig"); + pub const MethodType = @import("Class/ConstantPoolInfo/MethodType.zig"); + pub const InvokeDynamic = @import("Class/ConstantPoolInfo/InvokeDynamic.zig"); - 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 ?CpInfo) ResolveError!CpInfo { - 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 ?CpInfo) ResolveError!CpInfo { - 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 ?CpInfo) ResolveError!CpInfo { - 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!CpInfo { + 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; @@ -632,14 +491,14 @@ pub const CpInfo = union(CpInfo.Tag) { }; } - pub fn deinit(self: *CpInfo, allocator: std.mem.Allocator) void { + pub fn deinit(self: *ConstantPoolInfo, allocator: std.mem.Allocator) void { switch (self.*) { .utf8 => |info| allocator.free(info.bytes), else => {}, } } - pub fn format(self: *const CpInfo, w: *std.Io.Writer, depth: usize, indent: u8, constant_pool: []const ?CpInfo) std.Io.Writer.Error!void { + 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.*) }); @@ -750,1219 +609,3 @@ pub const CpInfo = union(CpInfo.Tag) { } }; -pub const ClassFlags = enum(u16) { - public = 0x0001, - final = 0x0010, - super = 0x0020, - interface = 0x0200, - abstract = 0x0400, - synthetic = 0x1000, - annotation = 0x2000, - @"enum" = 0x4000, -}; - -pub const ClassAccessFlags = EnumFlags(ClassFlags); - -pub const FieldInfo = struct { - access_flags: FieldAccessFlags, - name_index: u16, - descriptor_index: u16, - attributes: []AttributeInfo, - - pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) !FieldInfo { - const access_flags: FieldAccessFlags = .{ .mask = try input.takeInt(u16, .big) }; - const name_index = try input.takeInt(u16, .big); - const descriptor_index = try input.takeInt(u16, .big); - const attributes = try parseAttributes(input, allocator); - return .{ - .access_flags = access_flags, - .name_index = name_index, - .descriptor_index = descriptor_index, - .attributes = attributes, - }; - } - - pub fn deinit(self: *FieldInfo, allocator: std.mem.Allocator) void { - for (self.attributes) |*attr_info| { - attr_info.deinit(allocator); - } - allocator.free(self.attributes); - } - - pub fn format(self: *const FieldInfo, w: *std.Io.Writer, depth: usize, indent: u8, allocator: std.mem.Allocator, constant_pool: []const ?CpInfo) std.Io.Writer.Error!void { - try w.splatByteAll(' ', depth * indent); - try w.print("flags: {f}\n", .{ self.access_flags }); - - 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 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 w.splatByteAll(' ', depth * indent); - _ = try w.write("attributes:\n"); - var attr_indent = indent; - for (self.attributes, 0..) |*attribute, j| { - attr_indent += 1; - defer attr_indent -= 1; - - try w.splatByteAll(' ', depth * attr_indent); - try w.print("{d}:\n", .{ j }); - - try attribute.format(w, depth, attr_indent + 1, allocator, constant_pool); - } - } - - pub fn name(self: *const FieldInfo, constant_pool: []const ?CpInfo) ResolveError!CpInfo { - 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(CpInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; - } - - pub fn descriptor(self: *const FieldInfo, constant_pool: []const ?CpInfo) ResolveError!CpInfo { - 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(CpInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; - } -}; - -pub const FieldFlags = enum(u16) { - public = 0x0001, - private = 0x0002, - protected = 0x0004, - static = 0x0008, - final = 0x0010, - @"volatile" = 0x0040, - transient = 0x0080, - synthetic = 0x1000, - @"enum" = 0x4000, -}; - -pub const FieldAccessFlags = EnumFlags(FieldFlags); - -pub const MethodInfo = struct { - access_flags: MethodAccessFlags, - name_index: u16, - descriptor_index: u16, - attributes: []AttributeInfo, - - pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) !MethodInfo { - const access_flags: MethodAccessFlags = .{ .mask = try input.takeInt(u16, .big) }; - const name_index = try input.takeInt(u16, .big); - const descriptor_index = try input.takeInt(u16, .big); - const attributes = try parseAttributes(input, allocator); - return .{ - .access_flags = access_flags, - .name_index = name_index, - .descriptor_index = descriptor_index, - .attributes = attributes, - }; - } - - pub fn deinit(self: *MethodInfo, allocator: std.mem.Allocator) void { - for (self.attributes) |*attr_info| { - attr_info.deinit(allocator); - } - allocator.free(self.attributes); - } - - pub fn format(self: *const MethodInfo, w: *std.Io.Writer, depth: usize, indent: u8, allocator: std.mem.Allocator, constant_pool: []const ?CpInfo) std.Io.Writer.Error!void { - try w.splatByteAll(' ', depth * indent); - try w.print("flags: {f}\n", .{ self.access_flags }); - - 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 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 w.splatByteAll(' ', depth * indent); - _ = try w.write("attributes:\n"); - var attr_indent = indent; - for (self.attributes, 0..) |*attribute, j| { - attr_indent += 1; - defer attr_indent -= 1; - - try w.splatByteAll(' ', depth * attr_indent); - try w.print("{d}:\n", .{ j }); - - try attribute.format(w, depth, attr_indent + 1, allocator, constant_pool); - } - } - - pub fn name(self: *const MethodInfo, constant_pool: []const ?CpInfo) ResolveError!CpInfo { - 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(CpInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; - } - - pub fn descriptor(self: *const MethodInfo, constant_pool: []const ?CpInfo) ResolveError!CpInfo { - 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(CpInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; - } -}; - -pub const MethodFlags = enum(u16) { - public = 0x0001, - private = 0x0002, - protected = 0x0004, - static = 0x0008, - final = 0x0010, - synchronized = 0x0020, - bridge = 0x0040, - varargs = 0x0080, - native = 0x0100, - abstract = 0x0400, - strict = 0x0800, - synthetic = 0x1000, -}; - -pub const MethodAccessFlags = EnumFlags(MethodFlags); - -pub const AttributeInfo = struct { - attribute_name_index: u16, - info: []u8, - - 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 const ConstantValue = struct { - constant_value_index: u16, - - pub fn constantValue(self: *const ConstantValue, constant_pool: []const ?CpInfo) ResolveError!CpInfo { - 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; - - return cp_info.?; - } - }; - - pub const Code = struct { - max_stack: u16, - max_locals: u16, - code: []u8, - exception_table: []ExceptionHandler, - attributes: []AttributeInfo, - - pub const ExceptionHandler = struct { - start_pc: u16, - end_pc: u16, - handler_pc: u16, - catch_type: u16, - }; - }; - - pub const StackMapTable = struct { - entries: []StackMapFrame, - - pub const StackMapFrame = union(enum) { - // 0-63 - same_frame, - // 64-127 - same_locals_1_stack_item_frame: struct { - stack: [1]VerificationTypeInfo, - }, - // 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, - }; - - 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; - - return switch (tag) { - .top => .top, - .integer => .integer, - .float => .float, - .double => .double, - .long => .long, - .null => .null, - .uninitialized_this => .uninitialized_this, - .object => .{ - .object = .{ - .cpool_index = try input.takeInt(u16, .big), - }, - }, - .uninitialized => .{ - .uninitialized = .{ - .cpool_index = try input.takeInt(u16, .big), - }, - }, - }; - } - }; - }; - - pub const Exceptions = struct { - exception_index_table: []u16, - }; - - pub const InnerClasses = struct { - classes: []InnerClass, - - pub const InnerClass = struct { - inner_class_info_index: u16, - outer_class_info_index: u16, - inner_name_index: u16, - inner_class_access_flags: InnerClassAccessFlags, - - pub fn format(self: *const InnerClass, w: *std.Io.Writer, depth: usize, indent: u8, constant_pool: []const ?CpInfo) 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 ?CpInfo) ResolveError!CpInfo { - 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(CpInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; - return cp_info.?; - } - - pub fn outerClass(self: *const InnerClass, constant_pool: []const ?CpInfo) ResolveError!?CpInfo { - 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(CpInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; - return cp_info.?; - } - - pub fn innerName(self: *const InnerClass, constant_pool: []const ?CpInfo) ResolveError!?CpInfo { - 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(CpInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; - } - }; - - pub const InnerClassFlags = enum(u16) { - public = 0x0001, - private = 0x0002, - protected = 0x0004, - static = 0x0008, - final = 0x0010, - interface = 0x0200, - abstract = 0x0400, - synthetic = 0x1000, - annotation = 0x2000, - @"enum" = 0x4000, - }; - - pub const InnerClassAccessFlags = EnumFlags(InnerClassFlags); - }; - - pub const EnclosingMethod = struct { - class_index: u16, - method_index: u16, - - pub fn class(self: *const EnclosingMethod, constant_pool: []const ?CpInfo) ResolveError!CpInfo { - 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(CpInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; - return cp_info.?; - } - - pub fn method(self: *const EnclosingMethod, constant_pool: []const ?CpInfo) ResolveError!?CpInfo { - 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(CpInfo.Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType; - return cp_info.?; - } - }; - - pub const Synthetic = struct { }; - - pub const Signature = struct { - signature_index: u16, - - pub fn signature(self: *const Signature, constant_pool: []const ?CpInfo) ResolveError!CpInfo { - 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(CpInfo.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 ?CpInfo) ResolveError!CpInfo { - 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(CpInfo.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 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 const Annotation = struct { - type_index: u16, - element_value_pairs: []ElementValuePair, - - pub const ElementValuePair = struct { - element_name_index: u16, - value: ElementValue, - }; - - 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 const RuntimeVisibleAnnotations = struct { - annotations: []Annotation, - }; - - pub const RuntimeInvisibleAnnotations = struct { - annotations: []Annotation, - }; - - pub const ParameterAnnotation = struct { - annotations: []Annotation, - }; - - pub const RuntimeVisibleParameterAnnotations = struct { - parameter_annotations: []ParameterAnnotation, - }; - - pub const RuntimeInvisibleParameterAnnotations = struct { - parameter_annotations: []ParameterAnnotation, - }; - - pub const AnnotationDefault = struct { - default_value: ElementValue, - }; - - pub const BootstrapMethods = struct { - bootstrap_methods: []BootstrapMethod, - - pub const BootstrapMethod = struct { - bootstrap_method_ref: u16, - bootstrap_arguments: []u16, - }; - }; - - pub fn deinit(self: *Predefined, allocator: std.mem.Allocator) void { - switch (self.*) { - .code => |attribute| { - allocator.free(attribute.code); - allocator.free(attribute.exception_table); - for (attribute.attributes) |*attr| { - attr.deinit(allocator); - } - 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); - }, - else => {}, - } - } - }; - - pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!AttributeInfo { - const attribute_name_index = try input.takeInt(u16, .big); - const attribute_length = try input.takeInt(u32, .big); - const info = try input.readAlloc(allocator, attribute_length); - return .{ - .attribute_name_index = attribute_name_index, - .info = info, - }; - } - - pub fn deinit(self: *AttributeInfo, allocator: std.mem.Allocator) void { - allocator.free(self.info); - } - - pub fn format(self: *const AttributeInfo, w: *std.Io.Writer, depth: usize, indent: u8, allocator: std.mem.Allocator, constant_pool: []const ?CpInfo) 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(CpInfo.Tag, cp_info.?) != .class) return error.WriteFailed; - - try w.splatByteAll(' ', depth * exception_indent); - try w.print("{d}:\n", .{ i }); - try cp_info.?.format(w, depth, exception_indent + 1, constant_pool); - } - }, - .inner_classes => |attr| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("classes:\n"); - var class_indent = indent; - for (attr.classes, 0..) |*class, i| { - class_indent += 1; - defer class_indent -= 1; - - try w.splatByteAll(' ', depth * class_indent); - try w.print("{d}:\n", .{ i }); - try class.format(w, depth, class_indent + 1, constant_pool); - } - }, - .enclosing_method => |attr| { - const class = attr.class(constant_pool) catch return error.WriteFailed; - try w.splatByteAll(' ', depth * indent); - _ = try w.write("class:\n"); - try class.format(w, depth, indent + 1, constant_pool); - - const method = attr.method(constant_pool) catch return error.WriteFailed; - if (method) |m| { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("method:\n"); - try m.format(w, depth, indent + 1, constant_pool); - } - }, - .synthetic => {}, - .signature => |attr| { - const signature = attr.signature(constant_pool) catch return error.WriteFailed; - try signature.format(w, depth, indent + 1, constant_pool); - }, - .source_file => |attr| { - const source_file = attr.sourceFile(constant_pool) catch return error.WriteFailed; - try source_file.format(w, depth, indent + 1, constant_pool); - }, - //.source_debug_extension, - //.line_number_table, - //.local_variable_table, - //.local_variable_type_table, - .deprecated => {}, - //.runtime_visible_annotations, - //.runtime_invisible_annotations, - //.runtime_visible_parameter_annotations, - //.runtime_invisible_parameter_annotations, - //.annotation_default, - //.bootstrap_methods, - else => {}, - } - } else { - try w.splatByteAll(' ', depth * indent); - _ = try w.write("predefined: no\n"); - } - } - - pub fn attributeName(self: *const AttributeInfo, constant_pool: []const ?CpInfo) ResolveError!CpInfo { - 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(CpInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; - return cp_info.?; - } - - pub fn resolve(self: *const AttributeInfo, allocator: std.mem.Allocator, constant_pool: []const ?CpInfo) (ResolveError || ParseError)!?Predefined { - const attribute_name = (try self.attributeName(constant_pool)).utf8; - - var r: std.Io.Reader = .fixed(self.info); - - const eql = std.mem.eql; - if (eql(u8, attribute_name.bytes, "ConstantValue")) { - return .{ - .constant_value = .{ - .constant_value_index = try r.takeInt(u16, .big), - }, - }; - } else if (eql(u8, attribute_name.bytes, "Code")) { - const max_stack = try r.takeInt(u16, .big); - const max_locals = try r.takeInt(u16, .big); - - const code_length = try r.takeInt(u32, .big); - const code = try r.readAlloc(allocator, code_length); - errdefer allocator.free(code); - - const exception_table_length = try r.takeInt(u16, .big); - const exception_table = try allocator.alloc(Predefined.Code.ExceptionHandler, exception_table_length); - errdefer allocator.free(exception_table); - for (exception_table) |*exception_handler| { - exception_handler.* = .{ - .start_pc = try r.takeInt(u16, .big), - .end_pc = try r.takeInt(u16, .big), - .handler_pc = try r.takeInt(u16, .big), - .catch_type = try r.takeInt(u16, .big), - }; - } - - const attribute_count = try r.takeInt(u16, .big); - const attributes = try allocator.alloc(AttributeInfo, attribute_count); - errdefer allocator.free(attributes); - for (attributes) |*attribute| { - attribute.* = try .parse(&r, allocator); - } - - return .{ - .code = .{ - .max_stack = max_stack, - .max_locals = max_locals, - .code = code, - .exception_table = exception_table, - .attributes = attributes, - }, - }; - } else if (eql(u8, attribute_name.bytes, "StackMapTable")) { - const number_of_entries = try r.takeInt(u16, .big); - const entries = try allocator.alloc(Predefined.StackMapTable.StackMapFrame, number_of_entries); - errdefer allocator.free(entries); - for (entries) |*entry| { - entry.* = try .parse(&r, allocator); - } - - return .{ - .stack_map_table = .{ - .entries = entries, - }, - }; - } else if (eql(u8, attribute_name.bytes, "Exceptions")) { - const number_of_exceptions = try r.takeInt(u16, .big); - const exception_index_table = try allocator.alloc(u16, number_of_exceptions); - errdefer allocator.free(exception_index_table); - for (exception_index_table) |*exception_index| { - exception_index.* = try r.takeInt(u16, .big); - } - - return .{ - .exceptions = .{ - .exception_index_table = exception_index_table, - }, - }; - } else if (eql(u8, attribute_name.bytes, "InnerClasses")) { - const number_of_classes = try r.takeInt(u16, .big); - const classes = try allocator.alloc(Predefined.InnerClasses.InnerClass, number_of_classes); - errdefer allocator.free(classes); - for (classes) |*class| { - class.* = .{ - .inner_class_info_index = try r.takeInt(u16, .big), - .outer_class_info_index = try r.takeInt(u16, .big), - .inner_name_index = try r.takeInt(u16, .big), - .inner_class_access_flags = .{ .mask = try r.takeInt(u16, .big) }, - }; - } - - return .{ - .inner_classes = .{ - .classes = classes, - }, - }; - } else if (eql(u8, attribute_name.bytes, "EnclosingMethod")) { - return .{ - .enclosing_method = .{ - .class_index = try r.takeInt(u16, .big), - .method_index = try r.takeInt(u16, .big), - }, - }; - } else if (eql(u8, attribute_name.bytes, "Synthetic")) { - return .{ - .synthetic = .{}, - }; - } else if (eql(u8, attribute_name.bytes, "Signature")) { - return .{ - .signature = .{ - .signature_index = try r.takeInt(u16, .big), - }, - }; - } else if (eql(u8, attribute_name.bytes, "SourceFile")) { - return .{ - .source_file = .{ - .source_file_index = try r.takeInt(u16, .big), - }, - }; - } else if (eql(u8, attribute_name.bytes, "SourceDebugExtension")) { - return .{ - .source_debug_extension = .{ - .debug_extension = try allocator.dupe(u8, self.info), - }, - }; - } else if (eql(u8, attribute_name.bytes, "LineNumberTable")) { - const line_number_table_length = try r.takeInt(u16, .big); - const line_number_table = try allocator.alloc(Predefined.LineNumberTable.LineNumber, line_number_table_length); - errdefer allocator.free(line_number_table); - for (line_number_table) |*line_number| { - line_number.* = .{ - .start_pc = try r.takeInt(u16, .big), - .line_number = try r.takeInt(u16, .big), - }; - } - - return .{ - .line_number_table = .{ - .line_number_table = line_number_table, - }, - }; - } else if (eql(u8, attribute_name.bytes, "LocalVariableTable")) { - const local_variable_table_length = try r.takeInt(u16, .big); - const local_variable_table = try allocator.alloc(Predefined.LocalVariableTable.LocalVariable, local_variable_table_length); - errdefer allocator.free(local_variable_table); - for (local_variable_table) |*local_variable| { - local_variable.* = .{ - .start_pc = try r.takeInt(u16, .big), - .length = try r.takeInt(u16, .big), - .name_index = try r.takeInt(u16, .big), - .descriptor_index = try r.takeInt(u16, .big), - .index = try r.takeInt(u16, .big), - }; - } - - return .{ - .local_variable_table = .{ - .local_variable_table = local_variable_table, - }, - }; - } else if (eql(u8, attribute_name.bytes, "LocalVariableTypeTable")) { - const local_variable_type_table_length = try r.takeInt(u16, .big); - const local_variable_type_table = try allocator.alloc(Predefined.LocalVariableTypeTable.LocalVariableType, local_variable_type_table_length); - errdefer allocator.free(local_variable_type_table); - for (local_variable_type_table) |*local_variable_type| { - local_variable_type.* = .{ - .start_pc = try r.takeInt(u16, .big), - .length = try r.takeInt(u16, .big), - .name_index = try r.takeInt(u16, .big), - .signature_index = try r.takeInt(u16, .big), - .index = try r.takeInt(u16, .big), - }; - } - - return .{ - .local_variable_type_table = .{ - .local_variable_type_table = local_variable_type_table, - }, - }; - } else if (eql(u8, attribute_name.bytes, "Deprecated")) { - return .{ - .deprecated = .{}, - }; - } else if (eql(u8, attribute_name.bytes, "RuntimeVisibleAnnotations")) { - const num_annotations = try r.takeInt(u16, .big); - const annotations = try allocator.alloc(Predefined.Annotation, num_annotations); - errdefer allocator.free(annotations); - for (annotations) |*annotation| { - annotation.* = try .parse(&r, allocator); - } - - return .{ - .runtime_visible_annotations = .{ - .annotations = annotations, - }, - }; - } else if (eql(u8, attribute_name.bytes, "RuntimeInvisibleAnnotations")) { - const num_annotations = try r.takeInt(u16, .big); - const annotations = try allocator.alloc(Predefined.Annotation, num_annotations); - errdefer allocator.free(annotations); - for (annotations) |*annotation| { - annotation.* = try .parse(&r, allocator); - } - - return .{ - .runtime_invisible_annotations = .{ - .annotations = annotations, - }, - }; - } else if (eql(u8, attribute_name.bytes, "RuntimeVisibleParameterAnnotations")) { - const num_parameters = try r.takeByte(); - const parameter_annotations = try allocator.alloc(Predefined.ParameterAnnotation, num_parameters); - errdefer allocator.free(parameter_annotations); - for (parameter_annotations) |*parameter_annotation| { - const num_annotations = try r.takeInt(u16, .big); - const annotations = try allocator.alloc(Predefined.Annotation, num_annotations); - errdefer allocator.free(annotations); - for (annotations) |*annotation| { - annotation.* = try .parse(&r, allocator); - } - - parameter_annotation.* = .{ - .annotations = annotations, - }; - } - - return .{ - .runtime_visible_parameter_annotations = .{ - .parameter_annotations = parameter_annotations, - }, - }; - } else if (eql(u8, attribute_name.bytes, "RuntimeInvisibleParameterAnnotations")) { - const num_parameters = try r.takeByte(); - const parameter_annotations = try allocator.alloc(Predefined.ParameterAnnotation, num_parameters); - errdefer allocator.free(parameter_annotations); - for (parameter_annotations) |*parameter_annotation| { - const num_annotations = try r.takeInt(u16, .big); - const annotations = try allocator.alloc(Predefined.Annotation, num_annotations); - errdefer allocator.free(annotations); - for (annotations) |*annotation| { - annotation.* = try .parse(&r, allocator); - } - - parameter_annotation.* = .{ - .annotations = annotations, - }; - } - - return .{ - .runtime_invisible_parameter_annotations = .{ - .parameter_annotations = parameter_annotations, - }, - }; - } else if (eql(u8, attribute_name.bytes, "AnnotationDefault")) { - unreachable; - } else if (eql(u8, attribute_name.bytes, "BootstrapMethods")) { - const num_bootstrap_methods = try r.takeInt(u16, .big); - const bootstrap_methods = try allocator.alloc(Predefined.BootstrapMethods.BootstrapMethod, num_bootstrap_methods); - errdefer allocator.free(bootstrap_methods); - for (bootstrap_methods) |*bootstrap_method| { - const bootstrap_method_ref = try r.takeInt(u16, .big); - - const num_bootstrap_arguments = try r.takeInt(u16, .big); - const bootstrap_arguments = try allocator.alloc(u16, num_bootstrap_arguments); - errdefer allocator.free(bootstrap_arguments); - for (bootstrap_arguments) |*bootstrap_argument| { - bootstrap_argument.* = try r.takeInt(u16, .big); - } - - bootstrap_method.* = .{ - .bootstrap_method_ref = bootstrap_method_ref, - .bootstrap_arguments = bootstrap_arguments, - }; - } - - return .{ - .bootstrap_methods = .{ - .bootstrap_methods = bootstrap_methods, - }, - }; - } else { - return null; - } - } -}; - diff --git a/src/Class/AttributeInfo.zig b/src/Class/AttributeInfo.zig new file mode 100644 index 0000000..66c750c --- /dev/null +++ b/src/Class/AttributeInfo.zig @@ -0,0 +1,1040 @@ +const std = @import("std"); + +const EnumFlags = @import("../EnumFlags.zig").EnumFlags; + +const Class = @import("../Class.zig"); +const ConstantPoolInfo = Class.ConstantPoolInfo; +const AttributeInfo = Class.AttributeInfo; +const ParseError = Class.ParseError; +const ResolveError = Class.ResolveError; + +attribute_name_index: u16, +info: []u8, + +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 const ConstantValue = struct { + constant_value_index: u16, + + 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; + + return cp_info.?; + } + }; + + pub const Code = struct { + max_stack: u16, + max_locals: u16, + code: []u8, + exception_table: []ExceptionHandler, + attributes: []AttributeInfo, + + pub const ExceptionHandler = struct { + start_pc: u16, + end_pc: u16, + handler_pc: u16, + catch_type: u16, + }; + }; + + pub const StackMapTable = struct { + entries: []StackMapFrame, + + pub const StackMapFrame = union(enum) { + // 0-63 + same_frame, + // 64-127 + same_locals_1_stack_item_frame: struct { + stack: [1]VerificationTypeInfo, + }, + // 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, + }; + + 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; + + return switch (tag) { + .top => .top, + .integer => .integer, + .float => .float, + .double => .double, + .long => .long, + .null => .null, + .uninitialized_this => .uninitialized_this, + .object => .{ + .object = .{ + .cpool_index = try input.takeInt(u16, .big), + }, + }, + .uninitialized => .{ + .uninitialized = .{ + .cpool_index = try input.takeInt(u16, .big), + }, + }, + }; + } + }; + }; + + pub const Exceptions = struct { + exception_index_table: []u16, + }; + + pub const InnerClasses = struct { + classes: []InnerClass, + + pub const InnerClass = struct { + inner_class_info_index: u16, + outer_class_info_index: u16, + inner_name_index: u16, + inner_class_access_flags: InnerClassAccessFlags, + + 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 const InnerClassFlags = enum(u16) { + public = 0x0001, + private = 0x0002, + protected = 0x0004, + static = 0x0008, + final = 0x0010, + interface = 0x0200, + abstract = 0x0400, + synthetic = 0x1000, + annotation = 0x2000, + @"enum" = 0x4000, + }; + + pub const InnerClassAccessFlags = EnumFlags(InnerClassFlags); + }; + + pub const EnclosingMethod = struct { + class_index: u16, + method_index: u16, + + pub fn class(self: *const EnclosingMethod, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const class_index = self.class_index - 1; + if (class_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[class_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; + return cp_info.?; + } + + pub fn 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 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 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 const Annotation = struct { + type_index: u16, + element_value_pairs: []ElementValuePair, + + pub const ElementValuePair = struct { + element_name_index: u16, + value: ElementValue, + }; + + 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 const RuntimeVisibleAnnotations = struct { + annotations: []Annotation, + }; + + pub const RuntimeInvisibleAnnotations = struct { + annotations: []Annotation, + }; + + pub const ParameterAnnotation = struct { + annotations: []Annotation, + }; + + pub const RuntimeVisibleParameterAnnotations = struct { + parameter_annotations: []ParameterAnnotation, + }; + + pub const RuntimeInvisibleParameterAnnotations = struct { + parameter_annotations: []ParameterAnnotation, + }; + + pub const AnnotationDefault = struct { + default_value: ElementValue, + }; + + pub const BootstrapMethods = struct { + bootstrap_methods: []BootstrapMethod, + + pub const BootstrapMethod = struct { + bootstrap_method_ref: u16, + bootstrap_arguments: []u16, + }; + }; + + pub fn deinit(self: *Predefined, allocator: std.mem.Allocator) void { + switch (self.*) { + .code => |attribute| { + allocator.free(attribute.code); + allocator.free(attribute.exception_table); + for (attribute.attributes) |*attr| { + attr.deinit(allocator); + } + 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); + }, + else => {}, + } + } +}; + +pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!AttributeInfo { + const attribute_name_index = try input.takeInt(u16, .big); + const attribute_length = try input.takeInt(u32, .big); + const info = try input.readAlloc(allocator, attribute_length); + return .{ + .attribute_name_index = attribute_name_index, + .info = info, + }; +} + +pub fn deinit(self: *AttributeInfo, allocator: std.mem.Allocator) void { + allocator.free(self.info); +} + +pub fn format(self: *const AttributeInfo, w: *std.Io.Writer, depth: usize, indent: u8, allocator: std.mem.Allocator, constant_pool: []const ?ConstantPoolInfo) std.Io.Writer.Error!void { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("name:\n"); + const attribute_name = self.attributeName(constant_pool) catch return error.WriteFailed; + try attribute_name.format(w, depth, indent + 1, constant_pool); + + var predefined = self.resolve(allocator, constant_pool) catch return error.WriteFailed; + if (predefined) |*p| { + defer p.deinit(allocator); + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("predefined: yes\n"); + + try w.splatByteAll(' ', depth * indent); + try w.print("type: {s}\n", .{ @tagName(p.*) }); + + switch (p.*) { + .constant_value => |attr| { + const constant_value = attr.constantValue(constant_pool) catch return error.WriteFailed; + try constant_value.format(w, depth, indent + 1, constant_pool); + }, + .code => |attr| { + try w.splatByteAll(' ', depth * indent); + try w.print("max_stack: {d}\n", .{ attr.max_stack }); + + try w.splatByteAll(' ', depth * indent); + try w.print("max_locals: {d}\n", .{ attr.max_locals }); + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("code:\n"); + var instr_indent = indent; + for (attr.code, 0..) |instr, i| { + instr_indent += 1; + defer instr_indent -= 1; + + try w.splatByteAll(' ', depth * instr_indent); + try w.print("{d}: 0x{x}\n", .{ i, instr }); + } + + //try w.splatByteAll(' ', depth * indent); + //_ = try w.write("exception_table:\n"); + //var exception_indent = indent; + //for (attr.exception_table, 0..) |*exception, i| { + // exception_indent += 1; + // defer exception_indent -= 1; + + // try w.splatByteAll(' ', depth * exception_indent); + // try w.print("{d}:\n", .{ i }); + + // try self.formatExceptionHandler(w, depth, exception_indent + 1, exception); + //} + + try w.splatByteAll(' ', depth * indent); + _ = try w.write("attributes:\n"); + var attr_indent = indent; + for (attr.attributes, 0..) |*a, i| { + attr_indent += 1; + defer attr_indent -= 1; + + try w.splatByteAll(' ', depth * attr_indent); + try w.print("{d}:\n", .{ i }); + + try a.format(w, depth, attr_indent + 1, allocator, constant_pool); + } + }, + //.stack_map_table, + .exceptions => |attr| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("exceptions:\n"); + var exception_indent = indent; + for (attr.exception_index_table, 0..) |exception, i| { + exception_indent += 1; + defer exception_indent -= 1; + + if (exception >= constant_pool.len) return error.WriteFailed; + const cp_info = constant_pool[exception - 1]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return error.WriteFailed; + + try w.splatByteAll(' ', depth * exception_indent); + try w.print("{d}:\n", .{ i }); + try cp_info.?.format(w, depth, exception_indent + 1, constant_pool); + } + }, + .inner_classes => |attr| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("classes:\n"); + var class_indent = indent; + for (attr.classes, 0..) |*class, i| { + class_indent += 1; + defer class_indent -= 1; + + try w.splatByteAll(' ', depth * class_indent); + try w.print("{d}:\n", .{ i }); + try class.format(w, depth, class_indent + 1, constant_pool); + } + }, + .enclosing_method => |attr| { + const class = attr.class(constant_pool) catch return error.WriteFailed; + try w.splatByteAll(' ', depth * indent); + _ = try w.write("class:\n"); + try class.format(w, depth, indent + 1, constant_pool); + + const method = attr.method(constant_pool) catch return error.WriteFailed; + if (method) |m| { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("method:\n"); + try m.format(w, depth, indent + 1, constant_pool); + } + }, + .synthetic => {}, + .signature => |attr| { + const signature = attr.signature(constant_pool) catch return error.WriteFailed; + try signature.format(w, depth, indent + 1, constant_pool); + }, + .source_file => |attr| { + const source_file = attr.sourceFile(constant_pool) catch return error.WriteFailed; + try source_file.format(w, depth, indent + 1, constant_pool); + }, + //.source_debug_extension, + //.line_number_table, + //.local_variable_table, + //.local_variable_type_table, + .deprecated => {}, + //.runtime_visible_annotations, + //.runtime_invisible_annotations, + //.runtime_visible_parameter_annotations, + //.runtime_invisible_parameter_annotations, + //.annotation_default, + //.bootstrap_methods, + else => {}, + } + } else { + try w.splatByteAll(' ', depth * indent); + _ = try w.write("predefined: no\n"); + } +} + +pub fn attributeName(self: *const AttributeInfo, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const attribute_name_index = self.attribute_name_index - 1; + if (attribute_name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[attribute_name_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; +} + +pub fn resolve(self: *const AttributeInfo, allocator: std.mem.Allocator, constant_pool: []const ?ConstantPoolInfo) (ResolveError || ParseError)!?Predefined { + const attribute_name = (try self.attributeName(constant_pool)).utf8; + + var r: std.Io.Reader = .fixed(self.info); + + const eql = std.mem.eql; + if (eql(u8, attribute_name.bytes, "ConstantValue")) { + return .{ + .constant_value = .{ + .constant_value_index = try r.takeInt(u16, .big), + }, + }; + } else if (eql(u8, attribute_name.bytes, "Code")) { + const max_stack = try r.takeInt(u16, .big); + const max_locals = try r.takeInt(u16, .big); + + const code_length = try r.takeInt(u32, .big); + const code = try r.readAlloc(allocator, code_length); + errdefer allocator.free(code); + + const exception_table_length = try r.takeInt(u16, .big); + const exception_table = try allocator.alloc(Predefined.Code.ExceptionHandler, exception_table_length); + errdefer allocator.free(exception_table); + for (exception_table) |*exception_handler| { + exception_handler.* = .{ + .start_pc = try r.takeInt(u16, .big), + .end_pc = try r.takeInt(u16, .big), + .handler_pc = try r.takeInt(u16, .big), + .catch_type = try r.takeInt(u16, .big), + }; + } + + const attribute_count = try r.takeInt(u16, .big); + const attributes = try allocator.alloc(AttributeInfo, attribute_count); + errdefer allocator.free(attributes); + for (attributes) |*attribute| { + attribute.* = try .parse(&r, allocator); + } + + return .{ + .code = .{ + .max_stack = max_stack, + .max_locals = max_locals, + .code = code, + .exception_table = exception_table, + .attributes = attributes, + }, + }; + } else if (eql(u8, attribute_name.bytes, "StackMapTable")) { + const number_of_entries = try r.takeInt(u16, .big); + const entries = try allocator.alloc(Predefined.StackMapTable.StackMapFrame, number_of_entries); + errdefer allocator.free(entries); + for (entries) |*entry| { + entry.* = try .parse(&r, allocator); + } + + return .{ + .stack_map_table = .{ + .entries = entries, + }, + }; + } else if (eql(u8, attribute_name.bytes, "Exceptions")) { + const number_of_exceptions = try r.takeInt(u16, .big); + const exception_index_table = try allocator.alloc(u16, number_of_exceptions); + errdefer allocator.free(exception_index_table); + for (exception_index_table) |*exception_index| { + exception_index.* = try r.takeInt(u16, .big); + } + + return .{ + .exceptions = .{ + .exception_index_table = exception_index_table, + }, + }; + } else if (eql(u8, attribute_name.bytes, "InnerClasses")) { + const number_of_classes = try r.takeInt(u16, .big); + const classes = try allocator.alloc(Predefined.InnerClasses.InnerClass, number_of_classes); + errdefer allocator.free(classes); + for (classes) |*class| { + class.* = .{ + .inner_class_info_index = try r.takeInt(u16, .big), + .outer_class_info_index = try r.takeInt(u16, .big), + .inner_name_index = try r.takeInt(u16, .big), + .inner_class_access_flags = .{ .mask = try r.takeInt(u16, .big) }, + }; + } + + return .{ + .inner_classes = .{ + .classes = classes, + }, + }; + } else if (eql(u8, attribute_name.bytes, "EnclosingMethod")) { + return .{ + .enclosing_method = .{ + .class_index = try r.takeInt(u16, .big), + .method_index = try r.takeInt(u16, .big), + }, + }; + } else if (eql(u8, attribute_name.bytes, "Synthetic")) { + return .{ + .synthetic = .{}, + }; + } else if (eql(u8, attribute_name.bytes, "Signature")) { + return .{ + .signature = .{ + .signature_index = try r.takeInt(u16, .big), + }, + }; + } else if (eql(u8, attribute_name.bytes, "SourceFile")) { + return .{ + .source_file = .{ + .source_file_index = try r.takeInt(u16, .big), + }, + }; + } else if (eql(u8, attribute_name.bytes, "SourceDebugExtension")) { + return .{ + .source_debug_extension = .{ + .debug_extension = try allocator.dupe(u8, self.info), + }, + }; + } else if (eql(u8, attribute_name.bytes, "LineNumberTable")) { + const line_number_table_length = try r.takeInt(u16, .big); + const line_number_table = try allocator.alloc(Predefined.LineNumberTable.LineNumber, line_number_table_length); + errdefer allocator.free(line_number_table); + for (line_number_table) |*line_number| { + line_number.* = .{ + .start_pc = try r.takeInt(u16, .big), + .line_number = try r.takeInt(u16, .big), + }; + } + + return .{ + .line_number_table = .{ + .line_number_table = line_number_table, + }, + }; + } else if (eql(u8, attribute_name.bytes, "LocalVariableTable")) { + const local_variable_table_length = try r.takeInt(u16, .big); + const local_variable_table = try allocator.alloc(Predefined.LocalVariableTable.LocalVariable, local_variable_table_length); + errdefer allocator.free(local_variable_table); + for (local_variable_table) |*local_variable| { + local_variable.* = .{ + .start_pc = try r.takeInt(u16, .big), + .length = try r.takeInt(u16, .big), + .name_index = try r.takeInt(u16, .big), + .descriptor_index = try r.takeInt(u16, .big), + .index = try r.takeInt(u16, .big), + }; + } + + return .{ + .local_variable_table = .{ + .local_variable_table = local_variable_table, + }, + }; + } else if (eql(u8, attribute_name.bytes, "LocalVariableTypeTable")) { + const local_variable_type_table_length = try r.takeInt(u16, .big); + const local_variable_type_table = try allocator.alloc(Predefined.LocalVariableTypeTable.LocalVariableType, local_variable_type_table_length); + errdefer allocator.free(local_variable_type_table); + for (local_variable_type_table) |*local_variable_type| { + local_variable_type.* = .{ + .start_pc = try r.takeInt(u16, .big), + .length = try r.takeInt(u16, .big), + .name_index = try r.takeInt(u16, .big), + .signature_index = try r.takeInt(u16, .big), + .index = try r.takeInt(u16, .big), + }; + } + + return .{ + .local_variable_type_table = .{ + .local_variable_type_table = local_variable_type_table, + }, + }; + } else if (eql(u8, attribute_name.bytes, "Deprecated")) { + return .{ + .deprecated = .{}, + }; + } else if (eql(u8, attribute_name.bytes, "RuntimeVisibleAnnotations")) { + const num_annotations = try r.takeInt(u16, .big); + const annotations = try allocator.alloc(Predefined.Annotation, num_annotations); + errdefer allocator.free(annotations); + for (annotations) |*annotation| { + annotation.* = try .parse(&r, allocator); + } + + return .{ + .runtime_visible_annotations = .{ + .annotations = annotations, + }, + }; + } else if (eql(u8, attribute_name.bytes, "RuntimeInvisibleAnnotations")) { + const num_annotations = try r.takeInt(u16, .big); + const annotations = try allocator.alloc(Predefined.Annotation, num_annotations); + errdefer allocator.free(annotations); + for (annotations) |*annotation| { + annotation.* = try .parse(&r, allocator); + } + + return .{ + .runtime_invisible_annotations = .{ + .annotations = annotations, + }, + }; + } else if (eql(u8, attribute_name.bytes, "RuntimeVisibleParameterAnnotations")) { + const num_parameters = try r.takeByte(); + const parameter_annotations = try allocator.alloc(Predefined.ParameterAnnotation, num_parameters); + errdefer allocator.free(parameter_annotations); + for (parameter_annotations) |*parameter_annotation| { + const num_annotations = try r.takeInt(u16, .big); + const annotations = try allocator.alloc(Predefined.Annotation, num_annotations); + errdefer allocator.free(annotations); + for (annotations) |*annotation| { + annotation.* = try .parse(&r, allocator); + } + + parameter_annotation.* = .{ + .annotations = annotations, + }; + } + + return .{ + .runtime_visible_parameter_annotations = .{ + .parameter_annotations = parameter_annotations, + }, + }; + } else if (eql(u8, attribute_name.bytes, "RuntimeInvisibleParameterAnnotations")) { + const num_parameters = try r.takeByte(); + const parameter_annotations = try allocator.alloc(Predefined.ParameterAnnotation, num_parameters); + errdefer allocator.free(parameter_annotations); + for (parameter_annotations) |*parameter_annotation| { + const num_annotations = try r.takeInt(u16, .big); + const annotations = try allocator.alloc(Predefined.Annotation, num_annotations); + errdefer allocator.free(annotations); + for (annotations) |*annotation| { + annotation.* = try .parse(&r, allocator); + } + + parameter_annotation.* = .{ + .annotations = annotations, + }; + } + + return .{ + .runtime_invisible_parameter_annotations = .{ + .parameter_annotations = parameter_annotations, + }, + }; + } else if (eql(u8, attribute_name.bytes, "AnnotationDefault")) { + unreachable; + } else if (eql(u8, attribute_name.bytes, "BootstrapMethods")) { + const num_bootstrap_methods = try r.takeInt(u16, .big); + const bootstrap_methods = try allocator.alloc(Predefined.BootstrapMethods.BootstrapMethod, num_bootstrap_methods); + errdefer allocator.free(bootstrap_methods); + for (bootstrap_methods) |*bootstrap_method| { + const bootstrap_method_ref = try r.takeInt(u16, .big); + + const num_bootstrap_arguments = try r.takeInt(u16, .big); + const bootstrap_arguments = try allocator.alloc(u16, num_bootstrap_arguments); + errdefer allocator.free(bootstrap_arguments); + for (bootstrap_arguments) |*bootstrap_argument| { + bootstrap_argument.* = try r.takeInt(u16, .big); + } + + bootstrap_method.* = .{ + .bootstrap_method_ref = bootstrap_method_ref, + .bootstrap_arguments = bootstrap_arguments, + }; + } + + return .{ + .bootstrap_methods = .{ + .bootstrap_methods = bootstrap_methods, + }, + }; + } else { + return null; + } +} + diff --git a/src/Class/ConstantPoolInfo/Class.zig b/src/Class/ConstantPoolInfo/Class.zig new file mode 100644 index 0000000..e22266b --- /dev/null +++ b/src/Class/ConstantPoolInfo/Class.zig @@ -0,0 +1,16 @@ +const Class = @import("../../Class.zig"); +const ConstantPoolInfo = Class.ConstantPoolInfo; +const ResolveError = Class.ResolveError; + +name_index: u16, + +const Self = @This(); + +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.?; +} + diff --git a/src/Class/ConstantPoolInfo/FieldRef.zig b/src/Class/ConstantPoolInfo/FieldRef.zig new file mode 100644 index 0000000..7f5da75 --- /dev/null +++ b/src/Class/ConstantPoolInfo/FieldRef.zig @@ -0,0 +1,25 @@ +const Class = @import("../../Class.zig"); +const ConstantPoolInfo = Class.ConstantPoolInfo; +const ResolveError = Class.ResolveError; + +class_index: u16, +name_and_type_index: u16, + +const Self = @This(); + +pub fn class(self: *const Self, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const class_index = self.class_index - 1; + if (class_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[class_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; + return cp_info.?; +} + +pub fn nameAndType(self: *const Self, 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(ConstantPoolInfo.Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType; + return cp_info.?; +} + diff --git a/src/Class/ConstantPoolInfo/InterfaceMethodRef.zig b/src/Class/ConstantPoolInfo/InterfaceMethodRef.zig new file mode 100644 index 0000000..7f5da75 --- /dev/null +++ b/src/Class/ConstantPoolInfo/InterfaceMethodRef.zig @@ -0,0 +1,25 @@ +const Class = @import("../../Class.zig"); +const ConstantPoolInfo = Class.ConstantPoolInfo; +const ResolveError = Class.ResolveError; + +class_index: u16, +name_and_type_index: u16, + +const Self = @This(); + +pub fn class(self: *const Self, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const class_index = self.class_index - 1; + if (class_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[class_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; + return cp_info.?; +} + +pub fn nameAndType(self: *const Self, 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(ConstantPoolInfo.Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType; + return cp_info.?; +} + diff --git a/src/Class/ConstantPoolInfo/InvokeDynamic.zig b/src/Class/ConstantPoolInfo/InvokeDynamic.zig new file mode 100644 index 0000000..eb3970c --- /dev/null +++ b/src/Class/ConstantPoolInfo/InvokeDynamic.zig @@ -0,0 +1,16 @@ +const Class = @import("../../Class.zig"); +const ConstantPoolInfo = Class.ConstantPoolInfo; +const ResolveError = Class.ResolveError; + +bootstrap_method_attr_index: u16, +name_and_type_index: u16, + +const Self = @This(); + +pub fn nameAndType(self: *const Self, 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(ConstantPoolInfo.Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType; + return cp_info.?; +} diff --git a/src/Class/ConstantPoolInfo/MethodHandle.zig b/src/Class/ConstantPoolInfo/MethodHandle.zig new file mode 100644 index 0000000..b7fab85 --- /dev/null +++ b/src/Class/ConstantPoolInfo/MethodHandle.zig @@ -0,0 +1,41 @@ +const Class = @import("../../Class.zig"); +const ConstantPoolInfo = Class.ConstantPoolInfo; +const ResolveError = Class.ResolveError; + +reference_kind: Kind, +reference_index: u16, + +const Self = @This(); + +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 Self, 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(ConstantPoolInfo.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(ConstantPoolInfo.Tag, cp_info.?) != .method_ref) break :blk ResolveError.InvalidConstantType; + break :blk cp_info.?; + }, + .invoke_interface => blk: { + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .interface_method_ref) break :blk ResolveError.InvalidConstantType; + break :blk cp_info.?; + }, + }; +} + diff --git a/src/Class/ConstantPoolInfo/MethodRef.zig b/src/Class/ConstantPoolInfo/MethodRef.zig new file mode 100644 index 0000000..7f5da75 --- /dev/null +++ b/src/Class/ConstantPoolInfo/MethodRef.zig @@ -0,0 +1,25 @@ +const Class = @import("../../Class.zig"); +const ConstantPoolInfo = Class.ConstantPoolInfo; +const ResolveError = Class.ResolveError; + +class_index: u16, +name_and_type_index: u16, + +const Self = @This(); + +pub fn class(self: *const Self, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const class_index = self.class_index - 1; + if (class_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[class_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType; + return cp_info.?; +} + +pub fn nameAndType(self: *const Self, 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(ConstantPoolInfo.Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType; + return cp_info.?; +} + diff --git a/src/Class/ConstantPoolInfo/MethodType.zig b/src/Class/ConstantPoolInfo/MethodType.zig new file mode 100644 index 0000000..de6a423 --- /dev/null +++ b/src/Class/ConstantPoolInfo/MethodType.zig @@ -0,0 +1,16 @@ +const Class = @import("../../Class.zig"); +const ConstantPoolInfo = Class.ConstantPoolInfo; +const ResolveError = Class.ResolveError; + +descriptor_index: u16, + +const Self = @This(); + +pub fn descriptor(self: *const Self, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const descriptor_index = self.descriptor_index - 1; + if (descriptor_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[descriptor_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; +} + diff --git a/src/Class/ConstantPoolInfo/NameAndType.zig b/src/Class/ConstantPoolInfo/NameAndType.zig new file mode 100644 index 0000000..7423065 --- /dev/null +++ b/src/Class/ConstantPoolInfo/NameAndType.zig @@ -0,0 +1,25 @@ +const Class = @import("../../Class.zig"); +const ConstantPoolInfo = Class.ConstantPoolInfo; +const ResolveError = Class.ResolveError; + +name_index: u16, +descriptor_index: u16, + +const Self = @This(); + +pub fn name(self: *const Self, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const name_index = self.name_index - 1; + if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[name_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; +} + +pub fn descriptor(self: *const Self, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const descriptor_index = self.descriptor_index - 1; + if (descriptor_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[descriptor_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; +} + diff --git a/src/Class/ConstantPoolInfo/String.zig b/src/Class/ConstantPoolInfo/String.zig new file mode 100644 index 0000000..0e096aa --- /dev/null +++ b/src/Class/ConstantPoolInfo/String.zig @@ -0,0 +1,16 @@ +const Class = @import("../../Class.zig"); +const ConstantPoolInfo = Class.ConstantPoolInfo; +const ResolveError = Class.ResolveError; + +string_index: u16, + +const Self = @This(); + +pub fn string(self: *const Self, 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(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; +} + diff --git a/src/Class/FieldInfo.zig b/src/Class/FieldInfo.zig new file mode 100644 index 0000000..565efb0 --- /dev/null +++ b/src/Class/FieldInfo.zig @@ -0,0 +1,101 @@ +const std = @import("std"); + +const EnumFlags = @import("../EnumFlags.zig").EnumFlags; + +const Class = @import("../Class.zig"); +const ConstantPoolInfo = Class.ConstantPoolInfo; +const AttributeInfo = Class.AttributeInfo; +const ResolveError = Class.ResolveError; + +access_flags: FieldAccessFlags, +name_index: u16, +descriptor_index: u16, +attributes: []AttributeInfo, + +const Self = @This(); + +pub const FieldFlags = enum(u16) { + public = 0x0001, + private = 0x0002, + protected = 0x0004, + static = 0x0008, + final = 0x0010, + @"volatile" = 0x0040, + 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) }; + const name_index = try input.takeInt(u16, .big); + const descriptor_index = try input.takeInt(u16, .big); + const attributes = try Class.parseAttributes(input, allocator); + return .{ + .access_flags = access_flags, + .name_index = name_index, + .descriptor_index = descriptor_index, + .attributes = attributes, + }; +} + +pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + for (self.attributes) |*attr_info| { + attr_info.deinit(allocator); + } + allocator.free(self.attributes); +} + +pub fn format( + self: *const Self, + 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.print("flags: {f}\n", .{ self.access_flags }); + + 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 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 w.splatByteAll(' ', depth * indent); + _ = try w.write("attributes:\n"); + var attr_indent = indent; + for (self.attributes, 0..) |*attribute, j| { + attr_indent += 1; + defer attr_indent -= 1; + + try w.splatByteAll(' ', depth * attr_indent); + try w.print("{d}:\n", .{ j }); + + try attribute.format(w, depth, attr_indent + 1, allocator, 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 descriptor(self: *const Self, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo { + const descriptor_index = self.descriptor_index - 1; + if (descriptor_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex; + const cp_info = constant_pool[descriptor_index]; + if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType; + return cp_info.?; +} + diff --git a/src/Class/MethodInfo.zig b/src/Class/MethodInfo.zig new file mode 100644 index 0000000..0699c46 --- /dev/null +++ b/src/Class/MethodInfo.zig @@ -0,0 +1,104 @@ +const std = @import("std"); + +const EnumFlags = @import("../EnumFlags.zig").EnumFlags; + +const Class = @import("../Class.zig"); +const ConstantPoolInfo = Class.ConstantPoolInfo; +const AttributeInfo = Class.AttributeInfo; +const ResolveError = Class.ResolveError; + +access_flags: MethodAccessFlags, +name_index: u16, +descriptor_index: u16, +attributes: []AttributeInfo, + +const Self = @This(); + +pub const MethodFlags = enum(u16) { + public = 0x0001, + private = 0x0002, + protected = 0x0004, + static = 0x0008, + final = 0x0010, + synchronized = 0x0020, + bridge = 0x0040, + varargs = 0x0080, + native = 0x0100, + 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) }; + const name_index = try input.takeInt(u16, .big); + const descriptor_index = try input.takeInt(u16, .big); + const attributes = try Class.parseAttributes(input, allocator); + return .{ + .access_flags = access_flags, + .name_index = name_index, + .descriptor_index = descriptor_index, + .attributes = attributes, + }; +} + +pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + for (self.attributes) |*attr_info| { + attr_info.deinit(allocator); + } + allocator.free(self.attributes); +} + +pub fn format( + self: *const Self, + 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.print("flags: {f}\n", .{ self.access_flags }); + + 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 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 w.splatByteAll(' ', depth * indent); + _ = try w.write("attributes:\n"); + var attr_indent = indent; + for (self.attributes, 0..) |*attribute, j| { + attr_indent += 1; + defer attr_indent -= 1; + + try w.splatByteAll(' ', depth * attr_indent); + try w.print("{d}:\n", .{ j }); + + try attribute.format(w, depth, attr_indent + 1, allocator, 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 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.?; +} +