From ce6ce6575d1df7f5d82eb7bf80374dea58f6b5c9 Mon Sep 17 00:00:00 2001 From: ktkk Date: Thu, 9 Jul 2026 14:20:21 +0000 Subject: [PATCH 1/4] WIP: Formatter --- src/Class.zig | 15 ++++++++----- src/Formatter.zig | 57 +++++++++++++++++++++++++++++++++++++++++++++++ src/main.zig | 6 ++++- 3 files changed, 72 insertions(+), 6 deletions(-) create mode 100644 src/Formatter.zig diff --git a/src/Class.zig b/src/Class.zig index d244335..15488c6 100644 --- a/src/Class.zig +++ b/src/Class.zig @@ -1,11 +1,7 @@ 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").AttributeInfo; -pub const ConstantPool = @import("Class/ConstantPool.zig"); +const Formatter = @import("Formatter.zig"); allocator: std.mem.Allocator, @@ -23,6 +19,11 @@ attributes: []AttributeInfo, const Self = @This(); +pub const FieldInfo = @import("Class/FieldInfo.zig"); +pub const MethodInfo = @import("Class/MethodInfo.zig"); +pub const AttributeInfo = @import("Class/AttributeInfo.zig").AttributeInfo; +pub const ConstantPool = @import("Class/ConstantPool.zig"); + pub const ClassAccessFlags = EnumFlags(enum(u16) { public = 0x0001, final = 0x0010, @@ -198,6 +199,10 @@ pub fn jsonStringify(self: *const Self, jws: *std.json.Stringify) !void { try jws.endObject(); } +pub fn acceptFormatter(self: *const Self, formatter: *Formatter) Formatter.Error!void { + try formatter.printField("magic", "0x{X}", .{ self.magic }); +} + pub fn format(self: *const Self, w: *std.Io.Writer) std.Io.Writer.Error!void { const depth = 2; var indent: u8 = 0; diff --git a/src/Formatter.zig b/src/Formatter.zig new file mode 100644 index 0000000..a38a5eb --- /dev/null +++ b/src/Formatter.zig @@ -0,0 +1,57 @@ +const std = @import("std"); + +pub const Error = error {}; + +vtable: *const VTable, +w: *std.Io.Writer, + +const Self = @This(); + +pub const VTable = struct { + printField: *const fn ( + ptr: *Self, + comptime field: []const u8, + comptime fmt: []const u8, + args: anytype, + ) Error!void, +}; + +pub fn printField( + self: *const Self, + comptime field: []const u8, + value: anytype, +) Error!void { + self.vtable.field(self, field); + self.vtable.value(self, value); +} + +pub const IndentedFormatter = struct { + indent: u8 = 0, + depth: usize = 0, + + interface: Self, + + pub fn init(w: *std.Io.Writer) IndentedFormatter { + return .{ + .interface = .{ + .vtable = &.{ + .printField = IndentedFormatter.printField, + }, + .w = w, + }, + }; + } + + fn printField( + f: *Self, + comptime field: []const u8, + comptime fmt: []const u8, + args: anytype, + ) Error!void { + const self: *IndentedFormatter = @alignCast(@fieldParentPtr("interface", f)); + + _ = try self.interface.w.splatByteAll(' ', self.indent * self.depth); + try self.interface.w.print(field ++ ": " ++ fmt ++ "\n", args); + } +}; + diff --git a/src/main.zig b/src/main.zig index 8c4831e..051ee6d 100644 --- a/src/main.zig +++ b/src/main.zig @@ -2,6 +2,8 @@ const std = @import("std"); const Class = @import("Class.zig"); +const Formatter = @import("Formatter.zig"); + pub fn main(init: std.process.Init) !void { const io = init.io; const allocator = init.gpa; @@ -52,7 +54,9 @@ pub fn main(init: std.process.Init) !void { }; defer class.deinit(); - try stdout.print("{f}", .{ class }); + var indented_formatter: Formatter.IndentedFormatter = .init(stdout); + const formatter = &indented_formatter.interface; + try class.acceptFormatter(formatter); try stdout.flush(); } From 39fcd51a7c3a05e90a5cc881b078501793e8e1bc Mon Sep 17 00:00:00 2001 From: ktkk Date: Tue, 14 Jul 2026 10:13:26 +0000 Subject: [PATCH 2/4] Add EnumFlags.from --- src/Class.zig | 2 +- src/Class/AttributeInfo.zig | 12 ++++++------ src/Class/FieldInfo.zig | 2 +- src/Class/MethodInfo.zig | 2 +- src/EnumFlags.zig | 6 ++++++ 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/Class.zig b/src/Class.zig index d244335..e0d0244 100644 --- a/src/Class.zig +++ b/src/Class.zig @@ -57,7 +57,7 @@ pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) (ParseError || var constant_pool: ConstantPool = try .parse(input, allocator); errdefer constant_pool.deinit(allocator); - const access_flags: ClassAccessFlags = .{ .mask = try input.takeInt(u16, .big) }; + const access_flags: ClassAccessFlags = .from(try input.takeInt(u16, .big)); const this_class = try input.takeInt(u16, .big); const super_class = try input.takeInt(u16, .big); diff --git a/src/Class/AttributeInfo.zig b/src/Class/AttributeInfo.zig index ceadc65..45eec02 100644 --- a/src/Class/AttributeInfo.zig +++ b/src/Class/AttributeInfo.zig @@ -1200,7 +1200,7 @@ pub const InnerClasses = struct { .inner_class_info_index = try input.takeInt(u16, .big), .outer_class_info_index = try input.takeInt(u16, .big), .inner_name_index = try input.takeInt(u16, .big), - .inner_class_access_flags = .{ .mask = try input.takeInt(u16, .big) }, + .inner_class_access_flags = .from(try input.takeInt(u16, .big)), }; } @@ -2491,7 +2491,7 @@ pub const MethodParameters = struct { for (parameters) |*parameter| { parameter.* = .{ .name_index = try input.takeInt(u16, .big), - .access_flags = .{ .mask = try input.takeInt(u16, .big) }, + .access_flags = .from(try input.takeInt(u16, .big)), }; } @@ -2695,7 +2695,7 @@ pub const Module = struct { pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self { const module_name_index = try input.takeInt(u16, .big); - const module_flags: ModuleFlags = .{ .mask = try input.takeInt(u16, .big) }; + const module_flags: ModuleFlags = .from(try input.takeInt(u16, .big)); const module_version_index = try input.takeInt(u16, .big); const requires_count = try input.takeInt(u16, .big); @@ -2703,7 +2703,7 @@ pub const Module = struct { errdefer allocator.free(requires); for (requires) |*r| { const requires_index = try input.takeInt(u16, .big); - const requires_flags: Requires.RequiresFlags = .{ .mask = try input.takeInt(u16, .big) }; + const requires_flags: Requires.RequiresFlags = .from(try input.takeInt(u16, .big)); const requires_version_index = try input.takeInt(u16, .big); r.* = .{ .requires_index = requires_index, @@ -2717,7 +2717,7 @@ pub const Module = struct { errdefer allocator.free(exports); for (exports) |*e| { const exports_index = try input.takeInt(u16, .big); - const exports_flags: Exports.ExportsFlags = .{ .mask = try input.takeInt(u16, .big) }; + const exports_flags: Exports.ExportsFlags = .from(try input.takeInt(u16, .big)); const exports_to_count = try input.takeInt(u16, .big); const exports_to_indeces = try allocator.alloc(u16, exports_to_count); errdefer allocator.free(exports_to_indeces); @@ -2736,7 +2736,7 @@ pub const Module = struct { errdefer allocator.free(opens); for (opens) |*o| { const opens_index = try input.takeInt(u16, .big); - const opens_flags: Opens.OpensFlags = .{ .mask = try input.takeInt(u16, .big) }; + const opens_flags: Opens.OpensFlags = .from(try input.takeInt(u16, .big)); const opens_to_count = try input.takeInt(u16, .big); const opens_to_indeces = try allocator.alloc(u16, opens_to_count); errdefer allocator.free(opens_to_indeces); diff --git a/src/Class/FieldInfo.zig b/src/Class/FieldInfo.zig index 46746e6..fb16e55 100644 --- a/src/Class/FieldInfo.zig +++ b/src/Class/FieldInfo.zig @@ -27,7 +27,7 @@ pub const FieldAccessFlags = EnumFlags(enum(u16) { }); pub fn parse(input: *std.Io.Reader, constant_pool: ConstantPool, allocator: std.mem.Allocator) !Self { - const access_flags: FieldAccessFlags = .{ .mask = try input.takeInt(u16, .big) }; + const access_flags: FieldAccessFlags = .from(try input.takeInt(u16, .big)); const name_index = try input.takeInt(u16, .big); const descriptor_index = try input.takeInt(u16, .big); const attributes = try Class.parseAttributes(input, constant_pool, allocator); diff --git a/src/Class/MethodInfo.zig b/src/Class/MethodInfo.zig index 2650061..7d97300 100644 --- a/src/Class/MethodInfo.zig +++ b/src/Class/MethodInfo.zig @@ -30,7 +30,7 @@ pub const MethodAccessFlags = EnumFlags(enum(u16) { }); pub fn parse(input: *std.Io.Reader, constant_pool: ConstantPool, allocator: std.mem.Allocator) !Self { - const access_flags: MethodAccessFlags = .{ .mask = try input.takeInt(u16, .big) }; + const access_flags: MethodAccessFlags = .from(try input.takeInt(u16, .big)); const name_index = try input.takeInt(u16, .big); const descriptor_index = try input.takeInt(u16, .big); const attributes = try Class.parseAttributes(input, constant_pool, allocator); diff --git a/src/EnumFlags.zig b/src/EnumFlags.zig index 9b5da99..80c7e40 100644 --- a/src/EnumFlags.zig +++ b/src/EnumFlags.zig @@ -10,6 +10,12 @@ pub fn EnumFlags(comptime E: type) type { pub const empty: Self = .{ .data = 0 }; + pub fn from(mask: BackingInt) Self { + return .{ + .mask = mask, + }; + } + pub fn contains(self: Self, flag: E) bool { return (self.mask & @intFromEnum(flag)) != 0; } From 10c8c9b98f936b655f6fce82b771f3c10dc16093 Mon Sep 17 00:00:00 2001 From: ktkk Date: Thu, 16 Jul 2026 08:19:34 +0000 Subject: [PATCH 3/4] Update record attribute formatting --- src/Class/AttributeInfo.zig | 2 +- testsuite/classes/java/Record.java | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 testsuite/classes/java/Record.java diff --git a/src/Class/AttributeInfo.zig b/src/Class/AttributeInfo.zig index 45eec02..5fbdc9f 100644 --- a/src/Class/AttributeInfo.zig +++ b/src/Class/AttributeInfo.zig @@ -625,7 +625,7 @@ pub const AttributeInfo = union(enum) { try w.splatByteAll(' ', depth * component_indent); try w.print("{d}:\n", .{ i }); - try component.indentedFormat(w, depth, component_indent, constant_pool); + try component.indentedFormat(w, depth, component_indent + 1, constant_pool); } }, .permitted_subclasses => |attr| { diff --git a/testsuite/classes/java/Record.java b/testsuite/classes/java/Record.java new file mode 100644 index 0000000..4eb2159 --- /dev/null +++ b/testsuite/classes/java/Record.java @@ -0,0 +1,2 @@ +public record Record(String name, int age) {} + From bc8d525a3e419fed3943d9dfa7c523b7881dd89e Mon Sep 17 00:00:00 2001 From: ktkk Date: Thu, 16 Jul 2026 08:34:33 +0000 Subject: [PATCH 4/4] Don't store allocator --- src/Class.zig | 37 +++++++++++++++++-------------------- src/main.zig | 2 +- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/Class.zig b/src/Class.zig index e0d0244..09a3846 100644 --- a/src/Class.zig +++ b/src/Class.zig @@ -7,8 +7,6 @@ pub const MethodInfo = @import("Class/MethodInfo.zig"); pub const AttributeInfo = @import("Class/AttributeInfo.zig").AttributeInfo; pub const ConstantPool = @import("Class/ConstantPool.zig"); -allocator: std.mem.Allocator, - magic: u32, minor_version: u16, major_version: u16, @@ -90,7 +88,6 @@ pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) (ParseError || } return .{ - .allocator = allocator, .magic = magic, .minor_version = minor_version, .major_version = major_version, @@ -153,37 +150,37 @@ pub fn parseAttributes(input: *std.Io.Reader, constant_pool: ConstantPool, alloc return attributes; } -pub fn deinit(self: *Self) void { - self.constant_pool.deinit(self.allocator); - self.freeInterfaces(); - self.freeFields(); - self.freeMethods(); - self.freeAttributes(); +pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { + self.constant_pool.deinit(allocator); + self.freeInterfaces(allocator); + self.freeFields(allocator); + self.freeMethods(allocator); + self.freeAttributes(allocator); } -fn freeInterfaces(self: *Self) void { - self.allocator.free(self.interfaces); +fn freeInterfaces(self: *Self, allocator: std.mem.Allocator) void { + allocator.free(self.interfaces); } -fn freeFields(self: *Self) void { +fn freeFields(self: *Self, allocator: std.mem.Allocator) void { for (self.fields) |*field_info| { - field_info.deinit(self.allocator); + field_info.deinit(allocator); } - self.allocator.free(self.fields); + allocator.free(self.fields); } -fn freeMethods(self: *Self) void { +fn freeMethods(self: *Self, allocator: std.mem.Allocator) void { for (self.methods) |*method_info| { - method_info.deinit(self.allocator); + method_info.deinit(allocator); } - self.allocator.free(self.methods); + allocator.free(self.methods); } -fn freeAttributes(self: *Self) void { +fn freeAttributes(self: *Self, allocator: std.mem.Allocator) void { for (self.attributes) |*attr| { - attr.deinit(self.allocator); + attr.deinit(allocator); } - self.allocator.free(self.attributes); + allocator.free(self.attributes); } pub fn jsonStringify(self: *const Self, jws: *std.json.Stringify) !void { diff --git a/src/main.zig b/src/main.zig index 8c4831e..bf3f1f1 100644 --- a/src/main.zig +++ b/src/main.zig @@ -50,7 +50,7 @@ pub fn main(init: std.process.Init) !void { }, else => return err, }; - defer class.deinit(); + defer class.deinit(allocator); try stdout.print("{f}", .{ class });