尝试写一个jvm吧
为啥要尝试写一个呢,自从工作后,感觉就很少接触github了,也没精力去玩啥开源了,但自己还是喜欢学习其他东西,可能多动症???
尝试写一个jvm吧,本次选用语言是rust。
其实我个人也是rust初学者,到现在,仍然只是会一些简单语法,对于其宏、多线程仍然是跨不过去的门槛,所以本次 只会用一些基础语法来进行,目前应该java coder和go coder比较多,我本人也是一个java coder,所以这篇文章,仍然是以java coder能够看懂的情况下来开发。
目前准备分为这么几个部分:
- command line processing
- find class file
- parse class file
- implements runtime_data
- instruction set and interpreter
- heap and object
- method invoke
- array and string
- native
- exception
- ...
大概这么几个步骤,可能没有看到GC,我这儿想说的是,选rust的原因就在于它是一门无GC语言,当然如果想模仿一下也是可以,无非就是自己实现一套buffer的申请和回收,这个可以考虑,简单实现难度不大,如果加入动态GC、安全点啥的就挺有难度了,后面再说趴~
最后要说的就是我估计进度会很慢,毕竟公司是真滴挺卷的,基本只有周末时间来开发一点点~
命令行处理
预期效果 我可以通过类似java -v 或者 xxx -help的方式看到一些命令, 我的二进制包叫azh, 所以命令如下
./target/debug/azh -h
./target/debug/azh -v
一、环境准备
主要是一些必备的tracing日志包、env环境等包的引入
[package]
name = "azh" # 放你自己想要的名字,build时二进制包的名字
version = "0.1.0"
edition = "2021"
[dependencies]
getopts = "0.2.21"
tracing = "0.1.37"
tracing-subscriber = "0.3.16"
二、主要开发
2.1 定义Command命令行结构体
#[derive(Debug)]
pub struct Command {
pub help_flag: bool,
pub version_flag: bool,
pub info_flag: bool,
}
其中三个属性分别对应命令的help、version、info。
2.2 实现command解析: parse_command()方法
- 获取命令行参数
let args: Vec<String> = env::args().collect();
let program = args[0].clone();
let mut opts = Options::new();
- 定义参数的解析方式
let opts = opts
.parsing_style(ParsingStyle::StopAtFirstFree)
.long_only(true);
opts.optflag("h", "help", "Print help message");
opts.optflag("v", "version", "Print version and exit");
opts.optflag("", "info", "Print info about azh-jvm");
rust是标准的函数式编程,可以在学习过程中慢慢发现它的美。 上面代码中有两个点得注意下:
ParsingStyle::StopAtFirstFree
: 解析时剩余参数不作为标记参数的一部分
long_only
: 为true时允许使用 -xxx
- 对参数进行匹配
let matches = match opts.parse(&args[1..]) {
Ok(m) => m,
Err(err) => {
print_introduction_message(&program, opts);
panic!("{}", err.to_string());
}
};
// 匹配help
if matches.opt_present("help") {
command.help_flag = true;
}
if matches.opt_present("version") {
command.version_flag = true;
}
if matches.opt_present("info") {
command.info_flag = true;
}
rust是门很严格的语言,上面的match代码段就是做一个判断,如果parse错误我们要选择什么处理方式,rust的特点就是任何代码,要么success,要么就err,所以当我们考虑了所有的场景后,如果你的代码build成功,基本就不会出现线上问题。相信java coder很容易遇到npe问题(NullPointerException, 我在公司线上遇到过,一个npe 1块钱,我们并发很高,记得那天还是没有直播带货的场景,1s就出现了300来个npe... 当时就请各位同事周会喝奶茶...)
- print_introduction_message() 方法
fn print_introduction_message(program: &str, opts: &mut Options) {
let brief = format!("Usage: {} [-options] class [args...]", program);
info!("{}", opts.usage(&brief));
}
impl Command {
pub fn print_introduction_message(&self) {
let args: Vec<String> = env::args().collect();
info!(
"------ Usage: {} [-options] class [args...] ------",
args[0]
);
}
}
上面就基本完成了,最后你只需要
cargo build + cargo fmt 后如果没有error就构建二进制包成功了,最好也不要有waring、一个好的开发者不应该有任何一个waring出现在屏幕上。
然后通过你的二进制包,输入./xxx -h的方式就可以复现我们刚才的命令了~