尝试写一个jvm吧

共 3566字,需浏览 8分钟

 ·

2023-05-09 21:38

为啥要尝试写一个呢,自从工作后,感觉就很少接触github了,也没精力去玩啥开源了,但自己还是喜欢学习其他东西,可能多动症???

尝试写一个jvm吧,本次选用语言是rust。

其实我个人也是rust初学者,到现在,仍然只是会一些简单语法,对于其宏、多线程仍然是跨不过去的门槛,所以本次 只会用一些基础语法来进行,目前应该java coder和go coder比较多,我本人也是一个java coder,所以这篇文章,仍然是以java coder能够看懂的情况下来开发。

目前准备分为这么几个部分:

  1. command line processing
  2. find class file
  3. parse class file
  4. implements runtime_data
  5. instruction set and interpreter
  6. heap and object
  7. method invoke
  8. array and string
  9. native
  10. exception
  11. ...

大概这么几个步骤,可能没有看到GC,我这儿想说的是,选rust的原因就在于它是一门无GC语言,当然如果想模仿一下也是可以,无非就是自己实现一套buffer的申请和回收,这个可以考虑,简单实现难度不大,如果加入动态GC、安全点啥的就挺有难度了,后面再说趴~

最后要说的就是我估计进度会很慢,毕竟公司是真滴挺卷的,基本只有周末时间来开发一点点~

命令行处理

预期效果 我可以通过类似java -v 或者 xxx -help的方式看到一些命令, 我的二进制包叫azh, 所以命令如下

./target/debug/azh -h

39ca1015c0f5475f9479e245360be07d.webp

./target/debug/azh -v

af9990a42ee4c46787d5fae264e07b87.webp

一、环境准备

主要是一些必备的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的方式就可以复现我们刚才的命令了~


浏览 45
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报