#jvm #class #bytecode #parse #parser

class_file

用于解析 JVM 类文件的库

1 个不稳定版本

0.1.0 2018年12月31日

#2202解析器实现

MIT 许可证

53KB
1.5K SLoC

用于解析 JVM 类文件的库。
它使用 nom 实现其惊人的速度,所以请感谢作者。

截至编写时,这个包“基本上功能完整”。

  • 所有标准属性均如 JVMS 11 所声明。
  • 所有标准常量池条目均如 JVMS 11 所声明。
  • 类型检查的常量池索引

关于实现的解释并不多。
它使用 nom 和很多宏...
并不仅限于 nom...

这个包应该是对规范的一个完整的逐一生成实现。

如果规范说某个二进制结构定义了这样一个结构,而这个结构又定义了另一个结构,依此类推。
那么在这里,它将被复制。
这个包的设计不是面向用户,而是供其他库使用。

它非常底层。

有几件事需要注意

常量池索引携带有关其内容的信息。
因此,如果您从常量池中获取一个索引,您将获得一个特定的值。

一个展示它的简单例子是

extern crate class_file;

use class_file::*;

fn main() {
	let data = include_bytes!("Main.class");
	let class_file = ClassFile::parse(data)
    		.expect("Failed to parse \"Main.class\"")
    		.1; // This is needed because I actually return a nom result, which means you'll get back the remaining bytes.... Still undecided if I should hide that...

	let cp = &class_file.constant_pool;

	// So if you don't know, this_class is an index into the constant pool.
	// Specifically, it's an index to a ClassInfo at that location.
	// So the type of this_class is something like `CPIndex<'_, ClassInfo<'_>>`
	let this_class = class_file.this_class;

	// This information is used when you try fetching data from the constant pool.

	// This returns a ClassInfo, and from there, you'll start going down the rabbit hole that is the specification.
	let class_info = cp.index(this_class);

	// For example, to fetch the name of the class itself, you have to do something like:
	let class_name: &mstr = {
		let this_class = class_file.this_class;
		let class_info = cp.index(this_class)
			.expect("Unable to locate \"this_class\" inside of constant pool.");
		let info = cp.index(class_info.name_index)
			.expect("Unable to locate \"this_class.name_index\" in constant pool");
		info.data
	};

	// But as you might have noticed, that's not a str, but a mstr.
	// This is because the JVM classfile uses MUTF8 strings.
	// So to save converting literally every string a classfile has, it's returned as a mstr.
	// You can easily call .to_utf8() on it, and it'll convert it to a `Cow<'_, str>`.
	println!("This class: {}", class_name.to_utf8());
	// Side-note: mstr doesn't implement Display, because I'm a lazy fuck, but I'll get around to it...
}

如果您想了解更多,请查看我有的一个测试...
尽管如此,它基本上与上面的代码相同...

一个相当重要的注释
目前,一些东西是 pub 的,直到我扩展这个包的内部和易用性...

这个包还做了哪些疯狂的事情...

嗯,它为 JVM 中的大多数东西提供了常量。
大多数东西...
有些东西还缺失,我将在适当的时候添加它们。
例如,我知道 attr 模块中还有一些常量我还没有移动到那里...

嗯,还有其他什么...

属性只是一个特质,因此您可以实现自己的属性。
如果您使用宏,这很简单。
实际上,即使不使用宏,这也相当简单。
您只需要将 nom 的结果映射到可选值。
并实际实现解析部分。

目标

MUTF-8 处理并不完美……我想扩展它并使其更加无缝。

这可能是更疯狂的事情,但理想情况下,我想能够序列化整个结构。
想法是您可以使用这个库来解析一个类文件,修改它,然后将其导出。
甚至更疯狂的是,能够从头创建这些结构。
关于这一点,我有一些想法。

我一直在想一个类似objectasm的库,虽然这仍然是一个选择,但我不确定我是否喜欢它的最终效果。
访问者模式与Rust的语义产生了奇怪的行为。
最终结果是,尝试使用你通常会用objectasm做的操作变得非常混乱。

所以,我想在清理这个和mutf8之后,尝试另一个想法。
基本上,使用syn和quote这些crate相同的技巧,创建一些接受创建和转换classfiles语法的宏库。

Jasmin非常适合这个类别,使其成为完美的目标。
我会研究一下,看看效果如何。
理想情况下,我想将这个和理论上的jasmin crate结合起来,以便轻松创建和修改classfiles。

  • 改进MUTF-8处理
  • 开始使用这个crate,也许是一个类似quote的crate,带有类似jasmin的语法。

我可能会忘记改变这个列表,但截至目前,这些是我的主要目标。

依赖项

~1MB
~18K SLoC