Compare commits

...
Sign in to create a new pull request.

31 commits

Author SHA1 Message Date
bc8d525a3e Don't store allocator 2026-07-16 08:34:33 +00:00
10c8c9b98f Update record attribute formatting 2026-07-16 08:19:34 +00:00
39fcd51a7c Add EnumFlags.from 2026-07-14 10:13:26 +00:00
5ff7a358f9 Format runtime visible/invisible type annotations attributes 2026-07-08 22:22:17 +02:00
76c436714f Fix method parameter attribute name 2026-07-08 19:24:29 +02:00
a47405e903 Add runtime visible/invisible type annotations attributes 2026-07-08 15:27:34 +00:00
89e548c8cc Introduce constant pool info formatting helper 2026-07-07 23:23:38 +02:00
f12bcd2911 Format annotation attributes 2026-07-07 13:38:46 +00:00
3a5fd90d14 Format stack map table attribute 2026-07-06 21:55:57 +02:00
a451327a6a Add annotation attribute constant pool info getters 2026-07-06 20:26:28 +02:00
114b9b099c Put testsuite in its own directory 2026-07-06 11:29:34 +00:00
0cf25ebaf4 Fix module name tag check 2026-07-06 10:13:15 +00:00
0cc72ac2d9 Remove this_class and super_class verification
The plan is to implement a separate "verify" function that is separate
from parsing or formatting.
2026-07-06 09:52:42 +00:00
cb43efcbb1 Add is method to ConstantPool.Info 2026-07-06 08:52:24 +00:00
146d90ccec Refactor constant pool 2026-07-05 23:29:37 +02:00
9135e95dbd Refactor constant pool 2026-07-05 21:28:53 +02:00
04cf7663af Format bootstrap methods attribute 2026-07-05 20:54:23 +02:00
a8c87dc421 Format local variable type table attribute 2026-07-05 20:29:06 +02:00
b8c63f726e Format local variable table attribute 2026-07-05 20:17:10 +02:00
5c816759bb Format line number table attribute 2026-07-05 19:54:34 +02:00
41aad5e7b8 Format source debug extension attribute 2026-07-05 16:08:32 +02:00
6971dce705 Format code attribute exception table 2026-07-05 16:05:46 +02:00
0fbdea320c Add module class access flag 2026-07-05 15:31:24 +02:00
eefe96292e Format module attribute info 2026-07-05 15:31:00 +02:00
21644ef7f0 Add module attribute 2026-07-05 13:58:11 +02:00
7cc907dc19 Inline flag definitions 2026-07-05 01:08:13 +02:00
f8b1f968e0 Format additional predefined attribute infos 2026-07-05 00:25:25 +02:00
9af766e495 Add additional predefined attribute infos 2026-07-04 18:10:33 +02:00
75d023cfe1 Fix new attribute name index 2026-07-04 01:43:29 +02:00
07d43f2fe7 Dump constant pool in debug mode 2026-07-04 01:43:11 +02:00
a18ae9c265 Refactor AttributeInfo 2026-07-03 23:59:28 +02:00
29 changed files with 3769 additions and 1524 deletions

View file

@ -19,6 +19,8 @@
zls
javaPackages.compiler.openjdk25
jdt-language-server
kotlin
kotlin-language-server
];
buildInputs = with pkgs; [];

View file

@ -4,14 +4,13 @@ const EnumFlags = @import("EnumFlags.zig").EnumFlags;
pub const FieldInfo = @import("Class/FieldInfo.zig");
pub const MethodInfo = @import("Class/MethodInfo.zig");
pub const AttributeInfo = @import("Class/AttributeInfo.zig");
allocator: std.mem.Allocator,
pub const AttributeInfo = @import("Class/AttributeInfo.zig").AttributeInfo;
pub const ConstantPool = @import("Class/ConstantPool.zig");
magic: u32,
minor_version: u16,
major_version: u16,
constant_pool: []?ConstantPoolInfo,
constant_pool: ConstantPool,
access_flags: ClassAccessFlags,
this_class: u16,
super_class: u16,
@ -22,7 +21,7 @@ attributes: []AttributeInfo,
const Self = @This();
pub const ClassFlags = enum(u16) {
pub const ClassAccessFlags = EnumFlags(enum(u16) {
public = 0x0001,
final = 0x0010,
super = 0x0020,
@ -31,9 +30,8 @@ pub const ClassFlags = enum(u16) {
synthetic = 0x1000,
annotation = 0x2000,
@"enum" = 0x4000,
};
pub const ClassAccessFlags = EnumFlags(ClassFlags);
module = 0x8000,
});
pub const ParseError = std.Io.Reader.Error || std.mem.Allocator.Error || error {
InvalidMagicNumber,
@ -45,7 +43,7 @@ pub const ResolveError = error {
InvalidConstantType,
};
pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self {
pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) (ParseError || ResolveError)!Self {
const magic = try input.takeInt(u32, .big);
if (magic != 0xCAFEBABE) {
return ParseError.InvalidMagicNumber;
@ -54,15 +52,10 @@ pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Sel
const minor_version = try input.takeInt(u16, .big);
const major_version = try input.takeInt(u16, .big);
const constant_pool = try parseConstantPool(input, allocator);
errdefer {
for (constant_pool) |*cp_info| {
if (cp_info.*) |*c| c.deinit(allocator);
}
allocator.free(constant_pool);
}
var constant_pool: ConstantPool = try .parse(input, allocator);
errdefer constant_pool.deinit(allocator);
const access_flags: ClassAccessFlags = .{ .mask = try input.takeInt(u16, .big) };
const access_flags: ClassAccessFlags = .from(try input.takeInt(u16, .big));
const this_class = try input.takeInt(u16, .big);
const super_class = try input.takeInt(u16, .big);
@ -70,7 +63,7 @@ pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Sel
const interfaces = try parseInterfaces(input, allocator);
errdefer allocator.free(interfaces);
const fields = try parseFields(input, allocator);
const fields = try parseFields(input, constant_pool, allocator);
errdefer {
for (fields) |*field| {
field.deinit(allocator);
@ -78,7 +71,7 @@ pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Sel
allocator.free(fields);
}
const methods = try parseMethods(input, allocator);
const methods = try parseMethods(input, constant_pool, allocator);
errdefer {
for (methods) |*method| {
method.deinit(allocator);
@ -86,7 +79,7 @@ pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Sel
allocator.free(methods);
}
const attributes = try parseAttributes(input, allocator);
const attributes = try parseAttributes(input, constant_pool, allocator);
errdefer {
for (attributes) |*attribute| {
attribute.deinit(allocator);
@ -95,7 +88,6 @@ pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Sel
}
return .{
.allocator = allocator,
.magic = magic,
.minor_version = minor_version,
.major_version = major_version,
@ -110,30 +102,6 @@ pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Sel
};
}
fn parseConstantPool(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError![]?ConstantPoolInfo {
const constant_pool_size = try input.takeInt(u16, .big);
const constant_pool = try allocator.alloc(?ConstantPoolInfo, constant_pool_size - 1);
errdefer allocator.free(constant_pool);
var i: usize = 0;
while (i < constant_pool_size - 1) {
defer i += 1;
const cp_info = try ConstantPoolInfo.parse(input, allocator);
constant_pool[i] = cp_info;
switch (cp_info) {
.long, .double => {
i += 1;
constant_pool[i] = null;
},
else => {},
}
}
return constant_pool;
}
fn parseInterfaces(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError![]u16 {
const interfaces_count = try input.takeInt(u16, .big);
const interfaces = try allocator.alloc(u16, interfaces_count);
@ -146,80 +114,73 @@ fn parseInterfaces(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseErr
return interfaces;
}
fn parseFields(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError![]FieldInfo {
fn parseFields(input: *std.Io.Reader, constant_pool: ConstantPool, allocator: std.mem.Allocator) (ParseError || ResolveError)![]FieldInfo {
const fields_count = try input.takeInt(u16, .big);
const fields = try allocator.alloc(FieldInfo, fields_count);
errdefer allocator.free(fields);
for (0..fields_count) |i| {
fields[i] = try FieldInfo.parse(input, allocator);
fields[i] = try FieldInfo.parse(input, constant_pool, allocator);
}
return fields;
}
fn parseMethods(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError![]MethodInfo {
fn parseMethods(input: *std.Io.Reader, constant_pool: ConstantPool, allocator: std.mem.Allocator) (ParseError || ResolveError)![]MethodInfo {
const methods_count = try input.takeInt(u16, .big);
const methods = try allocator.alloc(MethodInfo, methods_count);
errdefer allocator.free(methods);
for (0..methods_count) |i| {
methods[i] = try MethodInfo.parse(input, allocator);
methods[i] = try .parse(input, constant_pool, allocator);
}
return methods;
}
pub fn parseAttributes(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError![]AttributeInfo {
pub fn parseAttributes(input: *std.Io.Reader, constant_pool: ConstantPool, allocator: std.mem.Allocator) (ParseError || ResolveError)![]AttributeInfo {
const attributes_count = try input.takeInt(u16, .big);
const attributes = try allocator.alloc(AttributeInfo, attributes_count);
errdefer allocator.free(attributes);
for (0..attributes_count) |i| {
attributes[i] = try AttributeInfo.parse(input, allocator);
attributes[i] = try AttributeInfo.parse(input, constant_pool, allocator);
}
return attributes;
}
pub fn deinit(self: *Self) void {
self.freeConstantPool();
self.freeInterfaces();
self.freeFields();
self.freeMethods();
self.freeAttributes();
pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
self.constant_pool.deinit(allocator);
self.freeInterfaces(allocator);
self.freeFields(allocator);
self.freeMethods(allocator);
self.freeAttributes(allocator);
}
fn freeConstantPool(self: *Self) void {
for (self.constant_pool) |*cp_info| {
if (cp_info.*) |*c| c.deinit(self.allocator);
}
self.allocator.free(self.constant_pool);
fn freeInterfaces(self: *Self, allocator: std.mem.Allocator) void {
allocator.free(self.interfaces);
}
fn freeInterfaces(self: *Self) void {
self.allocator.free(self.interfaces);
}
fn freeFields(self: *Self) void {
fn freeFields(self: *Self, allocator: std.mem.Allocator) void {
for (self.fields) |*field_info| {
field_info.deinit(self.allocator);
field_info.deinit(allocator);
}
self.allocator.free(self.fields);
allocator.free(self.fields);
}
fn freeMethods(self: *Self) void {
fn freeMethods(self: *Self, allocator: std.mem.Allocator) void {
for (self.methods) |*method_info| {
method_info.deinit(self.allocator);
method_info.deinit(allocator);
}
self.allocator.free(self.methods);
allocator.free(self.methods);
}
fn freeAttributes(self: *Self) void {
fn freeAttributes(self: *Self, allocator: std.mem.Allocator) void {
for (self.attributes) |*attr| {
attr.deinit(self.allocator);
attr.deinit(allocator);
}
self.allocator.free(self.attributes);
allocator.free(self.attributes);
}
pub fn jsonStringify(self: *const Self, jws: *std.json.Stringify) !void {
@ -246,11 +207,11 @@ pub fn format(self: *const Self, w: *std.Io.Writer) std.Io.Writer.Error!void {
const this_class = self.thisClass() catch return error.WriteFailed;
_ = try w.write("this class:\n");
try this_class.format(w, depth, indent + 1, self.constant_pool);
try this_class.indentedFormat(w, depth, indent + 1, self.constant_pool);
_ = try w.write("super class:\n");
if (self.superClass() catch return error.WriteFailed) |super_class| {
try super_class.format(w, depth, indent + 1, self.constant_pool);
try super_class.indentedFormat(w, depth, indent + 1, self.constant_pool);
}
_ = try w.write("interfaces:\n");
@ -258,13 +219,11 @@ pub fn format(self: *const Self, w: *std.Io.Writer) std.Io.Writer.Error!void {
indent += 1;
defer indent -= 1;
if (interface >= self.constant_pool.len) return error.WriteFailed;
const cp_info = self.constant_pool[interface - 1];
if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return error.WriteFailed;
const info = self.constant_pool.getTag(interface, .class) catch return error.WriteFailed;
try w.splatByteAll(' ', depth * indent);
try w.print("{d}:\n", .{ i });
try cp_info.?.format(w, depth, indent + 1, self.constant_pool);
try info.indentedFormat(w, depth, indent + 1, self.constant_pool);
}
_ = try w.write("fields:\n");
@ -274,7 +233,7 @@ pub fn format(self: *const Self, w: *std.Io.Writer) std.Io.Writer.Error!void {
try w.splatByteAll(' ', depth * indent);
try w.print("{d}:\n", .{ i });
try field.format(w, depth, indent + 1, self.allocator, self.constant_pool);
try field.indentedFormat(w, depth, indent + 1, self.constant_pool);
}
_ = try w.write("methods:\n");
@ -284,7 +243,7 @@ pub fn format(self: *const Self, w: *std.Io.Writer) std.Io.Writer.Error!void {
try w.splatByteAll(' ', depth * indent);
try w.print("{d}:\n", .{ i });
try method.format(w, depth, indent + 1, self.allocator, self.constant_pool);
try method.indentedFormat(w, depth, indent + 1, self.constant_pool);
}
_ = try w.write("attributes:\n");
@ -294,476 +253,15 @@ pub fn format(self: *const Self, w: *std.Io.Writer) std.Io.Writer.Error!void {
try w.splatByteAll(' ', depth * indent);
try w.print("{d}:\n", .{ i });
try attribute.format(w, depth, indent + 1, self.allocator, self.constant_pool);
try attribute.indentedFormat(w, depth, indent + 1, self.constant_pool);
}
}
pub fn thisClass(self: *const Self) ResolveError!ConstantPoolInfo {
const this_class = self.this_class - 1;
if (this_class >= self.constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = self.constant_pool[this_class];
if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType;
return cp_info.?;
pub fn thisClass(self: *const Self) ResolveError!ConstantPool.Info {
return self.constant_pool.getTag(self.this_class, .class);
}
pub fn superClass(self: *const Self) ResolveError!?ConstantPoolInfo {
if (self.super_class == 0) {
const this_class = (try self.thisClass()).class;
const this_class_name = (try this_class.name(self.constant_pool)).utf8;
if (!std.mem.eql(u8, this_class_name.bytes, "java/lang/Object")) return ResolveError.InvalidConstantType;
return null;
} else {
const super_class = self.super_class - 1;
if (super_class >= self.constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = self.constant_pool[super_class];
if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType;
if (self.access_flags.contains(.interface)) {
const super_class_name = (try cp_info.?.class.name(self.constant_pool)).utf8;
if (!std.mem.eql(u8, super_class_name.bytes, "java/lang/Object")) return ResolveError.InvalidConstantType;
} else {
// TODO: Assert that superclass does not have ACC_FINAL flag set.
}
return cp_info.?;
}
pub fn superClass(self: *const Self) ResolveError!?ConstantPool.Info {
return self.constant_pool.getTagOptional(self.super_class, .class);
}
pub const ConstantPoolInfo = union(ConstantPoolInfo.Tag) {
class: Class,
field_ref: FieldRef,
method_ref: MethodRef,
interface_method_ref: InterfaceMethodRef,
string: String,
integer: Integer,
float: Float,
long: Long,
double: Double,
name_and_type: NameAndType,
utf8: Utf8,
method_handle: MethodHandle,
method_type: MethodType,
invoke_dynamic: InvokeDynamic,
pub const Tag = enum(u8) {
class = 7,
field_ref = 9,
method_ref = 10,
interface_method_ref = 11,
string = 8,
integer = 3,
float = 4,
long = 5,
double = 6,
name_and_type = 12,
utf8 = 1,
method_handle = 15,
method_type = 16,
invoke_dynamic = 18,
};
pub const Class = struct {
name_index: u16,
pub fn name(self: *const Class, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo {
const name_index = self.name_index - 1;
if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[name_index];
if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType;
return cp_info.?;
}
};
pub const FieldRef = struct {
class_index: u16,
name_and_type_index: u16,
pub fn class(self: *const FieldRef, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo {
const class_index = self.class_index - 1;
if (class_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[class_index];
if (cp_info == null or @as(Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType;
return cp_info.?;
}
pub fn nameAndType(self: *const FieldRef, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo {
const name_and_type_index = self.name_and_type_index - 1;
if (name_and_type_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[name_and_type_index];
if (cp_info == null or @as(Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType;
return cp_info.?;
}
};
pub const MethodRef = struct {
class_index: u16,
name_and_type_index: u16,
pub fn class(self: *const MethodRef, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo {
const class_index = self.class_index - 1;
if (class_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[class_index];
if (cp_info == null or @as(Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType;
return cp_info.?;
}
pub fn nameAndType(self: *const MethodRef, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo {
const name_and_type_index = self.name_and_type_index - 1;
if (name_and_type_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[name_and_type_index];
if (cp_info == null or @as(Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType;
return cp_info.?;
}
};
pub const InterfaceMethodRef = struct {
class_index: u16,
name_and_type_index: u16,
pub fn class(self: *const InterfaceMethodRef, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo {
const class_index = self.class_index - 1;
if (class_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[class_index];
if (cp_info == null or @as(Tag, cp_info.?) != .class) return ResolveError.InvalidConstantType;
return cp_info.?;
}
pub fn nameAndType(self: *const InterfaceMethodRef, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo {
const name_and_type_index = self.name_and_type_index - 1;
if (name_and_type_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[name_and_type_index];
if (cp_info == null or @as(Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType;
return cp_info.?;
}
};
pub const String = struct {
string_index: u16,
pub fn string(self: *const String, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo {
const string_index = self.string_index - 1;
if (string_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[string_index];
if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType;
return cp_info.?;
}
};
pub const Integer = struct {
bytes: u32,
};
pub const Float = struct {
bytes: u32,
};
pub const Long = struct {
high_bytes: u32,
low_bytes: u32,
};
pub const Double = struct {
high_bytes: u32,
low_bytes: u32,
};
pub const NameAndType = struct {
name_index: u16,
descriptor_index: u16,
pub fn name(self: *const NameAndType, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo {
const name_index = self.name_index - 1;
if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[name_index];
if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType;
return cp_info.?;
}
pub fn descriptor(self: *const NameAndType, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo {
const descriptor_index = self.descriptor_index - 1;
if (descriptor_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[descriptor_index];
if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType;
return cp_info.?;
}
};
pub const Utf8 = struct {
bytes: []const u8,
};
pub const MethodHandle = struct {
reference_kind: Kind,
reference_index: u16,
pub const Kind = enum(u8) {
get_field = 1,
get_static = 2,
put_field = 3,
put_static = 4,
invoke_virtual = 5,
invoke_static = 6,
invoke_special = 7,
new_invoke_special = 8,
invoke_interface = 9,
};
pub fn reference(self: *const MethodHandle, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo {
const reference_index = self.reference_index - 1;
if (reference_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[reference_index];
return switch (self.reference_kind) {
.get_field, .get_static, .put_field, .put_static => blk: {
if (cp_info == null or @as(Tag, cp_info.?) != .field_ref) break :blk ResolveError.InvalidConstantType;
break :blk cp_info.?;
},
.invoke_virtual, .invoke_static, .invoke_special, .new_invoke_special => blk: {
if (cp_info == null or @as(Tag, cp_info.?) != .method_ref) break :blk ResolveError.InvalidConstantType;
break :blk cp_info.?;
},
.invoke_interface => blk: {
if (cp_info == null or @as(Tag, cp_info.?) != .interface_method_ref) break :blk ResolveError.InvalidConstantType;
break :blk cp_info.?;
},
};
}
};
pub const MethodType = struct {
descriptor_index: u16,
pub fn descriptor(self: *const MethodType, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo {
const descriptor_index = self.descriptor_index - 1;
if (descriptor_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[descriptor_index];
if (cp_info == null or @as(Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType;
return cp_info.?;
}
};
pub const InvokeDynamic = struct {
bootstrap_method_attr_index: u16,
name_and_type_index: u16,
pub fn nameAndType(self: *const InvokeDynamic, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo {
const name_and_type_index = self.name_and_type_index - 1;
if (name_and_type_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[name_and_type_index];
if (cp_info == null or @as(Tag, cp_info.?) != .name_and_type) return ResolveError.InvalidConstantType;
return cp_info.?;
}
};
pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!ConstantPoolInfo {
const tag_byte = try input.takeByte();
const tag = std.enums.fromInt(Tag, tag_byte) orelse return ParseError.InvalidTag;
return switch (tag) {
.class => .{
.class = .{
.name_index = try input.takeInt(u16, .big),
},
},
.field_ref => .{
.field_ref = .{
.class_index = try input.takeInt(u16, .big),
.name_and_type_index = try input.takeInt(u16, .big),
},
},
.method_ref => .{
.method_ref = .{
.class_index = try input.takeInt(u16, .big),
.name_and_type_index = try input.takeInt(u16, .big),
},
},
.interface_method_ref => .{
.interface_method_ref = .{
.class_index = try input.takeInt(u16, .big),
.name_and_type_index = try input.takeInt(u16, .big),
},
},
.string => .{
.string = .{
.string_index = try input.takeInt(u16, .big),
},
},
.integer => .{
.integer = .{
.bytes = try input.takeInt(u32, .big),
},
},
.float => .{
.integer = .{
.bytes = try input.takeInt(u32, .big),
},
},
.long => .{
.long = .{
.high_bytes = try input.takeInt(u32, .big),
.low_bytes = try input.takeInt(u32, .big),
},
},
.double => .{
.double = .{
.high_bytes = try input.takeInt(u32, .big),
.low_bytes = try input.takeInt(u32, .big),
},
},
.name_and_type => .{
.name_and_type = .{
.name_index = try input.takeInt(u16, .big),
.descriptor_index = try input.takeInt(u16, .big),
},
},
.utf8 => blk: {
const length = try input.takeInt(u16, .big);
const bytes = try input.readAlloc(allocator, length);
break :blk .{
.utf8 = .{
.bytes = bytes,
},
};
},
.method_handle => blk: {
const kind_byte = try input.takeByte();
const kind = std.enums.fromInt(MethodHandle.Kind, kind_byte) orelse return ParseError.InvalidTag;
break :blk .{
.method_handle = .{
.reference_kind = kind,
.reference_index = try input.takeInt(u16, .big),
},
};
},
.method_type => .{
.method_type = .{
.descriptor_index = try input.takeInt(u16, .big),
},
},
.invoke_dynamic => .{
.invoke_dynamic = .{
.bootstrap_method_attr_index = try input.takeInt(u16, .big),
.name_and_type_index = try input.takeInt(u16, .big),
},
},
};
}
pub fn deinit(self: *ConstantPoolInfo, allocator: std.mem.Allocator) void {
switch (self.*) {
.utf8 => |info| allocator.free(info.bytes),
else => {},
}
}
pub fn format(self: *const ConstantPoolInfo, w: *std.Io.Writer, depth: usize, indent: u8, constant_pool: []const ?ConstantPoolInfo) std.Io.Writer.Error!void {
try w.splatByteAll(' ', depth * indent);
try w.print("constant_value_type: {s}\n", .{ @tagName(self.*) });
switch (self.*) {
.class => |class| {
try w.splatByteAll(' ', depth * indent);
_ = try w.write("name:\n");
const class_name = class.name(constant_pool) catch return error.WriteFailed;
try class_name.format(w, depth, indent + 1, constant_pool);
},
.field_ref => |field_ref| {
try w.splatByteAll(' ', depth * indent);
_ = try w.write("class:\n");
const class = field_ref.class(constant_pool) catch return error.WriteFailed;
try class.format(w, depth, indent + 1, constant_pool);
try w.splatByteAll(' ', depth * indent);
_ = try w.write("name_and_type:\n");
const name_and_type = field_ref.nameAndType(constant_pool) catch return error.WriteFailed;
try name_and_type.format(w, depth, indent + 1, constant_pool);
},
.method_ref => |method_ref| {
try w.splatByteAll(' ', depth * indent);
_ = try w.write("class:\n");
const class = method_ref.class(constant_pool) catch return error.WriteFailed;
try class.format(w, depth, indent + 1, constant_pool);
try w.splatByteAll(' ', depth * indent);
_ = try w.write("name_and_type:\n");
const name_and_type = method_ref.nameAndType(constant_pool) catch return error.WriteFailed;
try name_and_type.format(w, depth, indent + 1, constant_pool);
},
.interface_method_ref => |interface_method_ref| {
try w.splatByteAll(' ', depth * indent);
_ = try w.write("class:\n");
const class = interface_method_ref.class(constant_pool) catch return error.WriteFailed;
try class.format(w, depth, indent + 1, constant_pool);
try w.splatByteAll(' ', depth * indent);
_ = try w.write("name_and_type:\n");
const name_and_type = interface_method_ref.nameAndType(constant_pool) catch return error.WriteFailed;
try name_and_type.format(w, depth, indent + 1, constant_pool);
},
.string => |string| {
const s = string.string(constant_pool) catch return error.WriteFailed;
try s.format(w, depth, indent + 1, constant_pool);
},
.integer => |integer| {
try w.splatByteAll(' ', depth * indent);
try w.print("integer: {d}\n", .{ integer.bytes });
},
.float => |float| {
const f: f32 = @bitCast(float.bytes);
try w.splatByteAll(' ', depth * indent);
try w.print("float: {d}\n", .{ f });
},
.long => |long| {
const l: u64 = (@as(u64, long.high_bytes) << 32) | long.low_bytes;
try w.splatByteAll(' ', depth * indent);
try w.print("long: {d}\n", .{ l });
},
.double => |double| {
const l: u64 = (@as(u64, double.high_bytes) << 32) | double.low_bytes;
const d: f64 = @bitCast(l);
try w.splatByteAll(' ', depth * indent);
try w.print("double: {d}\n", .{ d });
},
.name_and_type => |name_and_type| {
try w.splatByteAll(' ', depth * indent);
_ = try w.write("name:\n");
const name = name_and_type.name(constant_pool) catch return error.WriteFailed;
try name.format(w, depth, indent + 1, constant_pool);
try w.splatByteAll(' ', depth * indent);
_ = try w.write("descriptor:\n");
const descriptor = name_and_type.descriptor(constant_pool) catch return error.WriteFailed;
try descriptor.format(w, depth, indent + 1, constant_pool);
},
.utf8 => |utf8| {
try w.splatByteAll(' ', depth * indent);
try w.print("bytes: \"{s}\"\n", .{ utf8.bytes });
},
.method_handle => |method_handle| {
try w.splatByteAll(' ', depth * indent);
try w.print("kind: {t}\n", .{ method_handle.reference_kind });
try w.splatByteAll(' ', depth * indent);
_ = try w.write("reference:\n");
const reference = method_handle.reference(constant_pool) catch return error.WriteFailed;
try reference.format(w, depth, indent + 1, constant_pool);
},
.method_type => |method_type| {
try w.splatByteAll(' ', depth * indent);
_ = try w.write("descriptor:\n");
const descriptor = method_type.descriptor(constant_pool) catch return error.WriteFailed;
try descriptor.format(w, depth, indent + 1, constant_pool);
},
.invoke_dynamic => |invoke_dynamic| {
try w.splatByteAll(' ', depth * indent);
_ = try w.write("name_and_type:\n");
const name_and_type = invoke_dynamic.nameAndType(constant_pool) catch return error.WriteFailed;
try name_and_type.format(w, depth, indent + 1, constant_pool);
},
}
}
};

File diff suppressed because it is too large Load diff

547
src/Class/ConstantPool.zig Normal file
View file

@ -0,0 +1,547 @@
const std = @import("std");
const Class = @import("../Class.zig");
const AttributeInfo = Class.AttributeInfo;
const ParseError = Class.ParseError;
const ResolveError = Class.ResolveError;
constant_pool: []?Info,
const Self = @This();
pub const Info = union(Info.Tag) {
class: Info.Class,
field_ref: FieldRef,
method_ref: MethodRef,
interface_method_ref: InterfaceMethodRef,
string: String,
integer: Integer,
float: Float,
long: Long,
double: Double,
name_and_type: NameAndType,
utf8: Utf8,
method_handle: MethodHandle,
method_type: MethodType,
dynamic: Dynamic,
invoke_dynamic: InvokeDynamic,
module: Module,
package: Package,
pub const Tag = enum(u8) {
class = 7,
field_ref = 9,
method_ref = 10,
interface_method_ref = 11,
string = 8,
integer = 3,
float = 4,
long = 5,
double = 6,
name_and_type = 12,
utf8 = 1,
method_handle = 15,
method_type = 16,
dynamic = 17,
invoke_dynamic = 18,
module = 19,
package = 20,
pub fn loadable(self: Tag) bool {
return switch (self) {
.integer, .float, .long, .double, .class, .string, .method_handle, .method_type, .dynamic => true,
else => false,
};
}
};
pub const Class = struct {
name_index: u16,
pub fn name(self: *const Info.Class, constant_pool: Self) ResolveError!Info {
return constant_pool.getTag(self.name_index, .utf8);
}
};
pub const FieldRef = struct {
class_index: u16,
name_and_type_index: u16,
pub fn class(self: *const FieldRef, constant_pool: Self) ResolveError!Info {
return constant_pool.getTag(self.class_index, .class);
}
pub fn nameAndType(self: *const FieldRef, constant_pool: Self) ResolveError!Info {
return constant_pool.getTag(self.name_and_type_index, .name_and_type);
}
};
pub const MethodRef = struct {
class_index: u16,
name_and_type_index: u16,
pub fn class(self: *const MethodRef, constant_pool: Self) ResolveError!Info {
return constant_pool.getTag(self.class_index, .class);
}
pub fn nameAndType(self: *const MethodRef, constant_pool: Self) ResolveError!Info {
return constant_pool.getTag(self.name_and_type_index, .name_and_type);
}
};
pub const InterfaceMethodRef = struct {
class_index: u16,
name_and_type_index: u16,
pub fn class(self: *const InterfaceMethodRef, constant_pool: Self) ResolveError!Info {
return constant_pool.getTag(self.class_index, .class);
}
pub fn nameAndType(self: *const InterfaceMethodRef, constant_pool: Self) ResolveError!Info {
return constant_pool.getTag(self.name_and_type_index, .name_and_type);
}
};
pub const String = struct {
string_index: u16,
pub fn string(self: *const String, constant_pool: Self) ResolveError!Info {
return constant_pool.getTag(self.string_index, .utf8);
}
};
pub const Integer = struct {
bytes: u32,
};
pub const Float = struct {
bytes: u32,
pub fn float(self: *const Float) f32 {
return @bitCast(self.bytes);
}
};
pub const Long = struct {
high_bytes: u32,
low_bytes: u32,
pub fn long(self: *const Long) u64 {
return (@as(u64, self.high_bytes) << 32) | self.low_bytes;
}
};
pub const Double = struct {
high_bytes: u32,
low_bytes: u32,
pub fn double(self: *const Double) f64 {
return @bitCast((@as(u64, self.high_bytes) << 32) | self.low_bytes);
}
};
pub const NameAndType = struct {
name_index: u16,
descriptor_index: u16,
pub fn name(self: *const NameAndType, constant_pool: Self) ResolveError!Info {
return constant_pool.getTag(self.name_index, .utf8);
}
pub fn descriptor(self: *const NameAndType, constant_pool: Self) ResolveError!Info {
return constant_pool.getTag(self.descriptor_index, .utf8);
}
};
pub const Utf8 = struct {
bytes: []const u8,
};
pub const MethodHandle = struct {
reference_kind: Kind,
reference_index: u16,
pub const Kind = enum(u8) {
get_field = 1,
get_static = 2,
put_field = 3,
put_static = 4,
invoke_virtual = 5,
invoke_static = 6,
invoke_special = 7,
new_invoke_special = 8,
invoke_interface = 9,
};
pub fn reference(self: *const MethodHandle, constant_pool: Self) ResolveError!Info {
const info = try constant_pool.get(self.reference_index);
return switch (self.reference_kind) {
.get_field, .get_static, .put_field, .put_static => blk: {
if (!info.is(.field_ref)) break :blk ResolveError.InvalidConstantType;
break :blk info;
},
.invoke_virtual, .invoke_static, .invoke_special, .new_invoke_special => blk: {
if (!info.is(.method_ref)) break :blk ResolveError.InvalidConstantType;
break :blk info;
},
.invoke_interface => blk: {
if (!info.is(.interface_method_ref)) break :blk ResolveError.InvalidConstantType;
break :blk info;
},
};
}
};
pub const MethodType = struct {
descriptor_index: u16,
pub fn descriptor(self: *const MethodType, constant_pool: Self) ResolveError!Info {
return constant_pool.getTag(self.descriptor_index, .utf8);
}
};
pub const Dynamic = struct {
bootstrap_method_attr_index: u16,
name_and_type_index: u16,
pub fn nameAndType(self: *const Dynamic, constant_pool: Self) ResolveError!Info {
return constant_pool.getTag(self.name_and_type_index, .name_and_type);
}
};
pub const InvokeDynamic = struct {
bootstrap_method_attr_index: u16,
name_and_type_index: u16,
pub fn nameAndType(self: *const InvokeDynamic, constant_pool: Self) ResolveError!Info {
return constant_pool.getTag(self.name_and_type_index, .name_and_type);
}
};
pub const Module = struct {
name_index: u16,
pub fn name(self: *const Module, constant_pool: Self) ResolveError!Info {
return constant_pool.getTag(self.name_index, .utf8);
}
};
pub const Package = struct {
name_index: u16,
pub fn name(self: *const Package, constant_pool: Self) ResolveError!Info {
return constant_pool.getTag(self.name_index, .utf8);
}
};
pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Info {
const tag_byte = try input.takeByte();
const tag = std.enums.fromInt(Tag, tag_byte) orelse return ParseError.InvalidTag;
return switch (tag) {
.class => .{
.class = .{
.name_index = try input.takeInt(u16, .big),
},
},
.field_ref => .{
.field_ref = .{
.class_index = try input.takeInt(u16, .big),
.name_and_type_index = try input.takeInt(u16, .big),
},
},
.method_ref => .{
.method_ref = .{
.class_index = try input.takeInt(u16, .big),
.name_and_type_index = try input.takeInt(u16, .big),
},
},
.interface_method_ref => .{
.interface_method_ref = .{
.class_index = try input.takeInt(u16, .big),
.name_and_type_index = try input.takeInt(u16, .big),
},
},
.string => .{
.string = .{
.string_index = try input.takeInt(u16, .big),
},
},
.integer => .{
.integer = .{
.bytes = try input.takeInt(u32, .big),
},
},
.float => .{
.integer = .{
.bytes = try input.takeInt(u32, .big),
},
},
.long => .{
.long = .{
.high_bytes = try input.takeInt(u32, .big),
.low_bytes = try input.takeInt(u32, .big),
},
},
.double => .{
.double = .{
.high_bytes = try input.takeInt(u32, .big),
.low_bytes = try input.takeInt(u32, .big),
},
},
.name_and_type => .{
.name_and_type = .{
.name_index = try input.takeInt(u16, .big),
.descriptor_index = try input.takeInt(u16, .big),
},
},
.utf8 => blk: {
const length = try input.takeInt(u16, .big);
const bytes = try input.readAlloc(allocator, length);
break :blk .{
.utf8 = .{
.bytes = bytes,
},
};
},
.method_handle => blk: {
const kind_byte = try input.takeByte();
const kind = std.enums.fromInt(MethodHandle.Kind, kind_byte) orelse return ParseError.InvalidTag;
break :blk .{
.method_handle = .{
.reference_kind = kind,
.reference_index = try input.takeInt(u16, .big),
},
};
},
.method_type => .{
.method_type = .{
.descriptor_index = try input.takeInt(u16, .big),
},
},
.dynamic => .{
.dynamic = .{
.bootstrap_method_attr_index = try input.takeInt(u16, .big),
.name_and_type_index = try input.takeInt(u16, .big),
},
},
.invoke_dynamic => .{
.invoke_dynamic = .{
.bootstrap_method_attr_index = try input.takeInt(u16, .big),
.name_and_type_index = try input.takeInt(u16, .big),
},
},
.module => .{
.module = .{
.name_index = try input.takeInt(u16, .big),
},
},
.package => .{
.package = .{
.name_index = try input.takeInt(u16, .big),
},
},
};
}
pub fn deinit(self: *Info, allocator: std.mem.Allocator) void {
switch (self.*) {
.utf8 => |info| allocator.free(info.bytes),
else => {},
}
}
pub fn format(self: *const Info, w: *std.Io.Writer) std.Io.Writer.Error!void {
switch (self.*) {
.class => |class| try w.print("#{d}", .{ class.name_index }),
.field_ref => |field_ref| try w.print("#{d}.#{d}", .{ field_ref.class_index, field_ref.name_and_type_index }),
.method_ref => |method_ref| try w.print("#{d}.#{d}", .{ method_ref.class_index, method_ref.name_and_type_index }),
.interface_method_ref => |interface_method_ref| try w.print("#{d}.#{d}", .{ interface_method_ref.class_index, interface_method_ref.name_and_type_index }),
.string => |string| try w.print("#{d}", .{ string.string_index }),
.integer => |integer| try w.print("{d}", .{ integer.bytes }),
.float => |float| try w.print("{d}", .{ float.float() }),
.long => |long| try w.print("{d}", .{ long.long() }),
.double => |double| try w.print("{d}", .{ double.double() }),
.name_and_type => |name_and_type| try w.print("#{d}:#{d}", .{ name_and_type.name_index, name_and_type.descriptor_index }),
.utf8 => |utf8| try w.print("\"{s}\"", .{ utf8.bytes }),
.method_handle => |method_handle| try w.print("{t} #{d}", .{ method_handle.reference_kind, method_handle.reference_index }),
.method_type => |method_type| try w.print("#{d}", .{ method_type.descriptor_index }),
.dynamic => |dynamic| try w.print("#{d} #{d}", .{ dynamic.bootstrap_method_attr_index, dynamic.name_and_type_index }),
.invoke_dynamic => |invoke_dynamic| try w.print("#{d} #{d}", .{ invoke_dynamic.bootstrap_method_attr_index, invoke_dynamic.name_and_type_index }),
.module => |module| try w.print("#{d}", .{ module.name_index }),
.package => |package| try w.print("#{d}", .{ package.name_index }),
}
}
pub fn indentedFormat(
self: *const Info,
w: *std.Io.Writer,
depth: usize,
indent: u8,
constant_pool: Self,
) std.Io.Writer.Error!void {
try w.splatByteAll(' ', depth * indent);
try w.print("constant_value_type: {t}\n", .{ self.* });
switch (self.*) {
.class => |*class| {
try formatInfo(w, depth, indent, "name", class, .name, constant_pool);
},
.field_ref => |*field_ref| {
try formatInfo(w, depth, indent, "class", field_ref, .class, constant_pool);
try formatInfo(w, depth, indent, "name_and_type", field_ref, .nameAndType, constant_pool);
},
.method_ref => |*method_ref| {
try formatInfo(w, depth, indent, "class", method_ref, .class, constant_pool);
try formatInfo(w, depth, indent, "name_and_type", method_ref, .nameAndType, constant_pool);
},
.interface_method_ref => |*interface_method_ref| {
try formatInfo(w, depth, indent, "class", interface_method_ref, .class, constant_pool);
try formatInfo(w, depth, indent, "name_and_type", interface_method_ref, .nameAndType, constant_pool);
},
.string => |*string| {
try formatInfo(w, depth, indent, "string", string, .string, constant_pool);
},
.integer => |*integer| {
try w.splatByteAll(' ', depth * indent);
try w.print("integer: {d}\n", .{ integer.bytes });
},
.float => |*float| {
try w.splatByteAll(' ', depth * indent);
try w.print("float: {d}\n", .{ float.float() });
},
.long => |*long| {
try w.splatByteAll(' ', depth * indent);
try w.print("long: {d}\n", .{ long.long() });
},
.double => |*double| {
try w.splatByteAll(' ', depth * indent);
try w.print("double: {d}\n", .{ double.double() });
},
.name_and_type => |*name_and_type| {
try formatInfo(w, depth, indent, "name", name_and_type, .name, constant_pool);
try formatInfo(w, depth, indent, "descriptor", name_and_type, .descriptor, constant_pool);
},
.utf8 => |*utf8| {
try w.splatByteAll(' ', depth * indent);
try w.print("bytes: \"{s}\"\n", .{ utf8.bytes });
},
.method_handle => |*method_handle| {
try w.splatByteAll(' ', depth * indent);
try w.print("kind: {t}\n", .{ method_handle.reference_kind });
try formatInfo(w, depth, indent, "descriptor", method_handle, .reference, constant_pool);
},
.method_type => |*method_type| {
try formatInfo(w, depth, indent, "descriptor", method_type, .descriptor, constant_pool);
},
.dynamic => |*dynamic| {
try formatInfo(w, depth, indent, "name_and_type", dynamic, .nameAndType, constant_pool);
},
.invoke_dynamic => |*invoke_dynamic| {
try formatInfo(w, depth, indent, "name_and_type", invoke_dynamic, .nameAndType, constant_pool);
},
.module => |*module| {
try formatInfo(w, depth, indent, "name", module, .name, constant_pool);
},
.package => |*package| {
try formatInfo(w, depth, indent, "name", package, .name, constant_pool);
},
}
}
pub fn is(self: *const Info, tag: Tag) bool {
return @as(Tag, self.*) == tag;
}
};
pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) ParseError!Self {
const constant_pool_size = try input.takeInt(u16, .big);
const constant_pool = try allocator.alloc(?Info, constant_pool_size - 1);
errdefer allocator.free(constant_pool);
var i: usize = 0;
while (i < constant_pool_size - 1) {
defer i += 1;
const info: Info = try .parse(input, allocator);
constant_pool[i] = info;
switch (info) {
.long, .double => {
i += 1;
constant_pool[i] = null;
},
else => {},
}
}
return .{
.constant_pool = constant_pool,
};
}
pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
for (self.constant_pool) |*info| {
if (info.*) |*c| c.deinit(allocator);
}
allocator.free(self.constant_pool);
}
pub fn get(self: *const Self, index: u16) ResolveError!Info {
const actual_index = index - 1;
if (actual_index >= self.constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const info = self.constant_pool[actual_index];
if (info == null) return ResolveError.InvalidConstantType;
return info.?;
}
pub fn getOptional(self: *const Self, index: u16) ResolveError!?Info {
if (index == 0) return null;
return self.get(index);
}
pub fn getTag(self: *const Self, index: u16, expected_tag: Info.Tag) ResolveError!Info {
const info = try self.get(index);
if (!info.is(expected_tag)) return ResolveError.InvalidConstantType;
return info;
}
pub fn getTagOptional(self: *const Self, index: u16, expected_tag: Info.Tag) ResolveError!?Info {
if (index == 0) return null;
return try self.getTag(index, expected_tag);
}
pub fn getLoadable(self: *const Self, index: u16) ResolveError!Info {
const info = try self.get(index);
if (!@as(Info.Tag, info).loadable()) return ResolveError.InvalidConstantType;
return info;
}
pub fn getLoadableOptional(self: *const Self, index: u16) ResolveError!?Info {
if (index == 0) return null;
return self.getLoadable(index);
}
pub fn formatInfo(
w: *std.Io.Writer,
depth: usize,
indent: u8,
comptime name: []const u8,
context: anytype,
comptime getter: std.meta.DeclEnum(@TypeOf(context.*)),
constant_pool: Self,
) std.Io.Writer.Error!void {
const func = @field(@TypeOf(context.*), @tagName(getter));
try w.splatByteAll(' ', depth * indent);
_ = try w.write(name ++ ":\n");
const info = func(context, constant_pool) catch return error.WriteFailed;
try info.indentedFormat(w, depth, indent + 1, constant_pool);
}

View file

@ -3,7 +3,7 @@ const std = @import("std");
const EnumFlags = @import("../EnumFlags.zig").EnumFlags;
const Class = @import("../Class.zig");
const ConstantPoolInfo = Class.ConstantPoolInfo;
const ConstantPool = Class.ConstantPool;
const AttributeInfo = Class.AttributeInfo;
const ResolveError = Class.ResolveError;
@ -14,7 +14,7 @@ attributes: []AttributeInfo,
const Self = @This();
pub const FieldFlags = enum(u16) {
pub const FieldAccessFlags = EnumFlags(enum(u16) {
public = 0x0001,
private = 0x0002,
protected = 0x0004,
@ -24,15 +24,13 @@ pub const FieldFlags = enum(u16) {
transient = 0x0080,
synthetic = 0x1000,
@"enum" = 0x4000,
};
});
pub const FieldAccessFlags = EnumFlags(FieldFlags);
pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) !Self {
const access_flags: FieldAccessFlags = .{ .mask = try input.takeInt(u16, .big) };
pub fn parse(input: *std.Io.Reader, constant_pool: ConstantPool, allocator: std.mem.Allocator) !Self {
const access_flags: FieldAccessFlags = .from(try input.takeInt(u16, .big));
const name_index = try input.takeInt(u16, .big);
const descriptor_index = try input.takeInt(u16, .big);
const attributes = try Class.parseAttributes(input, allocator);
const attributes = try Class.parseAttributes(input, constant_pool, allocator);
return .{
.access_flags = access_flags,
.name_index = name_index,
@ -48,13 +46,12 @@ pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
allocator.free(self.attributes);
}
pub fn format(
pub fn indentedFormat(
self: *const Self,
w: *std.Io.Writer,
depth: usize,
indent: u8,
allocator: std.mem.Allocator,
constant_pool: []const ?ConstantPoolInfo,
constant_pool: ConstantPool,
) std.Io.Writer.Error!void {
try w.splatByteAll(' ', depth * indent);
try w.print("flags: {f}\n", .{ self.access_flags });
@ -62,12 +59,12 @@ pub fn format(
try w.splatByteAll(' ', depth * indent);
_ = try w.write("name:\n");
const field_name = self.name(constant_pool) catch return error.WriteFailed;
try field_name.format(w, depth, indent + 1, constant_pool);
try field_name.indentedFormat(w, depth, indent + 1, constant_pool);
try w.splatByteAll(' ', depth * indent);
_ = try w.write("descriptor:\n");
const field_descriptor = self.descriptor(constant_pool) catch return error.WriteFailed;
try field_descriptor.format(w, depth, indent + 1, constant_pool);
try field_descriptor.indentedFormat(w, depth, indent + 1, constant_pool);
try w.splatByteAll(' ', depth * indent);
_ = try w.write("attributes:\n");
@ -79,23 +76,15 @@ pub fn format(
try w.splatByteAll(' ', depth * attr_indent);
try w.print("{d}:\n", .{ j });
try attribute.format(w, depth, attr_indent + 1, allocator, constant_pool);
try attribute.indentedFormat(w, depth, attr_indent + 1, constant_pool);
}
}
pub fn name(self: *const Self, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo {
const name_index = self.name_index - 1;
if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[name_index];
if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType;
return cp_info.?;
pub fn name(self: *const Self, constant_pool: ConstantPool) ResolveError!ConstantPool.Info {
return constant_pool.getTag(self.name_index, .utf8);
}
pub fn descriptor(self: *const Self, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo {
const descriptor_index = self.descriptor_index - 1;
if (descriptor_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[descriptor_index];
if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType;
return cp_info.?;
pub fn descriptor(self: *const Self, constant_pool: ConstantPool) ResolveError!ConstantPool.Info {
return constant_pool.getTag(self.descriptor_index, .utf8);
}

View file

@ -3,7 +3,7 @@ const std = @import("std");
const EnumFlags = @import("../EnumFlags.zig").EnumFlags;
const Class = @import("../Class.zig");
const ConstantPoolInfo = Class.ConstantPoolInfo;
const ConstantPool = Class.ConstantPool;
const AttributeInfo = Class.AttributeInfo;
const ResolveError = Class.ResolveError;
@ -14,7 +14,7 @@ attributes: []AttributeInfo,
const Self = @This();
pub const MethodFlags = enum(u16) {
pub const MethodAccessFlags = EnumFlags(enum(u16) {
public = 0x0001,
private = 0x0002,
protected = 0x0004,
@ -27,15 +27,13 @@ pub const MethodFlags = enum(u16) {
abstract = 0x0400,
strict = 0x0800,
synthetic = 0x1000,
};
});
pub const MethodAccessFlags = EnumFlags(MethodFlags);
pub fn parse(input: *std.Io.Reader, allocator: std.mem.Allocator) !Self {
const access_flags: MethodAccessFlags = .{ .mask = try input.takeInt(u16, .big) };
pub fn parse(input: *std.Io.Reader, constant_pool: ConstantPool, allocator: std.mem.Allocator) !Self {
const access_flags: MethodAccessFlags = .from(try input.takeInt(u16, .big));
const name_index = try input.takeInt(u16, .big);
const descriptor_index = try input.takeInt(u16, .big);
const attributes = try Class.parseAttributes(input, allocator);
const attributes = try Class.parseAttributes(input, constant_pool, allocator);
return .{
.access_flags = access_flags,
.name_index = name_index,
@ -51,13 +49,12 @@ pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
allocator.free(self.attributes);
}
pub fn format(
pub fn indentedFormat(
self: *const Self,
w: *std.Io.Writer,
depth: usize,
indent: u8,
allocator: std.mem.Allocator,
constant_pool: []const ?ConstantPoolInfo,
constant_pool: ConstantPool,
) std.Io.Writer.Error!void {
try w.splatByteAll(' ', depth * indent);
try w.print("flags: {f}\n", .{ self.access_flags });
@ -65,12 +62,12 @@ pub fn format(
try w.splatByteAll(' ', depth * indent);
_ = try w.write("name:\n");
const method_name = self.name(constant_pool) catch return error.WriteFailed;
try method_name.format(w, depth, indent + 1, constant_pool);
try method_name.indentedFormat(w, depth, indent + 1, constant_pool);
try w.splatByteAll(' ', depth * indent);
_ = try w.write("descriptor:\n");
const method_descriptor = self.descriptor(constant_pool) catch return error.WriteFailed;
try method_descriptor.format(w, depth, indent + 1, constant_pool);
try method_descriptor.indentedFormat(w, depth, indent + 1, constant_pool);
try w.splatByteAll(' ', depth * indent);
_ = try w.write("attributes:\n");
@ -82,23 +79,14 @@ pub fn format(
try w.splatByteAll(' ', depth * attr_indent);
try w.print("{d}:\n", .{ j });
try attribute.format(w, depth, attr_indent + 1, allocator, constant_pool);
try attribute.indentedFormat(w, depth, attr_indent + 1, constant_pool);
}
}
pub fn name(self: *const Self, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo {
const name_index = self.name_index - 1;
if (name_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[name_index];
if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType;
return cp_info.?;
pub fn name(self: *const Self, constant_pool: ConstantPool) ResolveError!ConstantPool.Info {
return constant_pool.getTag(self.name_index, .utf8);
}
pub fn descriptor(self: *const Self, constant_pool: []const ?ConstantPoolInfo) ResolveError!ConstantPoolInfo {
const descriptor_index = self.descriptor_index - 1;
if (descriptor_index >= constant_pool.len) return ResolveError.InvalidConstantPoolIndex;
const cp_info = constant_pool[descriptor_index];
if (cp_info == null or @as(ConstantPoolInfo.Tag, cp_info.?) != .utf8) return ResolveError.InvalidConstantType;
return cp_info.?;
pub fn descriptor(self: *const Self, constant_pool: ConstantPool) ResolveError!ConstantPool.Info {
return constant_pool.getTag(self.descriptor_index, .utf8);
}

View file

@ -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;
}

View file

@ -50,10 +50,9 @@ pub fn main(init: std.process.Init) !void {
},
else => return err,
};
defer class.deinit();
defer class.deinit(allocator);
try stdout.print("{f}", .{ class });
try stdout.flush();
}

3
testsuite/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
*.class
!Object.class
META-INF

View file

@ -0,0 +1,14 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@CustomAnnotationAnnotation
public @interface CustomAnnotation {
public CustomAnnotationType type();
public String name() default "Bob";
}

View file

@ -0,0 +1,9 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface CustomAnnotationAnnotation { }

View file

@ -0,0 +1,5 @@
public enum CustomAnnotationType {
ONE,
TWO,
}

View file

@ -0,0 +1,9 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface CustomParameterAnnotation { }

View file

@ -0,0 +1,9 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_PARAMETER)
public @interface CustomTypeAnnotation {
}

View file

@ -0,0 +1,14 @@
import java.util.function.Supplier;
public class GenericClass<@CustomTypeAnnotation T> {
public <T> Supplier<T> getSupplier(T value) {
return new Supplier<T>() {
@Override
public T get() {
return value;
}
};
}
}

View file

@ -0,0 +1,6 @@
class Hello {
public static void main(String[] args) {
System.out.println("Hello, world!");
}
}

View file

@ -1,17 +1,27 @@
import java.util.function.Supplier;
@CustomAnnotation(type = CustomAnnotationType.ONE, name = "Alice")
public class Main implements Runnable, Supplier<Integer>, Interface {
private static final String name = "John";
private static final int number = 42;
private static final double funnyNumber = 6.9;
public static void main() {
new Main().sayHello();
public static void main(@CustomParameterAnnotation String[] args) {
final var main = new Main();
main.sayHello();
try {
main.throwSomething();
} catch (Throwable e) {
System.err.println("Caught " + e.getMessage());
} finally {
System.err.println("Finally block");
}
}
@Override
public void run() {
Main.main();
Main.main(new String[] { "Hello" });
}
@Override
@ -37,4 +47,3 @@ public class Main implements Runnable, Supplier<Integer>, Interface {
private static final String name = "Inner John";
}
}

View file

@ -0,0 +1,22 @@
import java.util.concurrent.Executors;
import java.util.stream.IntStream;
import java.util.concurrent.TimeUnit;
public class MultiThreading {
private int number = 0;
public static void main(String[] args) throws InterruptedException {
final var multiThreading = new MultiThreading();
final var service = Executors.newFixedThreadPool(3);
IntStream.range(0, 1000)
.forEach(count -> service.submit(multiThreading::increment));
service.awaitTermination(1000, TimeUnit.MILLISECONDS);
System.out.println(multiThreading.number);
}
public synchronized void increment() {
number += 1;
}
}

Binary file not shown.

View file

@ -0,0 +1,2 @@
public record Record(String name, int age) {}

View file

@ -0,0 +1,12 @@
fun main() {
println("Hello, world!")
Test().apply {
test = true
}
}
class Test {
var test: Boolean = false
}

View file

@ -0,0 +1,2 @@
#!/usr/bin/env bash
javac -g -d outDir --module-source-path simple-modules $(find simple-modules -name "*.java")

View file

@ -0,0 +1,2 @@
#!/usr/bin/env bash
java --module-path outDir -m main.app/dev.katkak.modules.main.MainApp

View file

@ -0,0 +1,6 @@
package dev.katkak.modules.hello;
public interface HelloInterface {
void sayHello();
}

View file

@ -0,0 +1,12 @@
package dev.katkak.modules.hello;
public class HelloModules implements HelloInterface {
public static void doSomething() {
System.out.println("Hello, Modules!");
}
public void sayHello() {
System.out.println("Hello!");
}
}

View file

@ -0,0 +1,6 @@
module hello.modules {
exports dev.katkak.modules.hello;
provides dev.katkak.modules.hello.HelloInterface with dev.katkak.modules.hello.HelloModules;
}

View file

@ -0,0 +1,17 @@
package dev.katkak.modules.main;
import dev.katkak.modules.hello.HelloModules;
import dev.katkak.modules.hello.HelloInterface;
import java.util.ServiceLoader;
public class MainApp {
public static void main(String[] args) {
HelloModules.doSomething();
final var services = ServiceLoader.load(HelloInterface.class);
final var service = services.iterator().next();
service.sayHello();
}
}

View file

@ -0,0 +1,6 @@
module main.app {
requires hello.modules;
uses dev.katkak.modules.hello.HelloInterface;
}