547 lines
19 KiB
Zig
547 lines
19 KiB
Zig
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);
|
|
}
|