mruby mruby 源码浏览

pynix · 发布于 2014年6月16日 · 最后由 pynix 回复于 2014年6月18日 · 2301 次阅读
9800

说不上源码剖析,就叫浏览吧。

工欲善其事,必先利其器。

安装ctags,mruby项目根目录执行ctags -R

vim ctags命令:

ctrl+]跳转到定义,这个是最常用的。

:ta[gs]列出tags栈。

:ts patterntag 搜索。

冰冻三尺,非一日之寒。

阅读源代码前,当然要先构建一遍。项目根目录执行makerake,以默认配置构建。

构建结果在build/host/目录下,bin目录下有三个可执行文件,同时也被链接到了项目根目录的bin目录下。

mruby和mirb就和CRuby一样。

执行bin/mirb就可以输入表达式了。

下面正式浏览源代码,先看目录结构。

benchmark --------------------跑分 bin --------------------可执行文件 build --------------------构建产物 build_config.rb --------------------构建配置,包括是否启用特性和gems。 doc --------------------文档 examples --------------------示例:c扩展和gem示例。 include --------------------头文件 Makefile minirake -------------------- rake

mrbgems --------------------gems mrblib --------------------stdlib(ruby代码,直接编译成字节码嵌入c文件) Rakefile src --------------------核心代码

test --------------------测试

项目根目录打开vim,以便正确找到tags文件,:ts main

我们都知道,通常情况下c语言的入口都是main,我们拿main开刀。从图从可以看到果然不止一个main函数。

四个有正常参数的看文件名就知道了。中间还有一个乱入的。

从mruby的main函数开始吧。输入数字,跳转到文件。

int
main(int argc, char **argv)
{
  mrb_state *mrb = mrb_open();
  int n = -1;
  int i;
  struct _args args;
  mrb_value ARGV;
  mrbc_context *c;
  mrb_value v;

  if (mrb == NULL) {
    fputs("Invalid mrb_state, exiting mruby\n", stderr);
    return EXIT_FAILURE;
  }

  n = parse_args(mrb, argc, argv, &args);
  if (n == EXIT_FAILURE || (args.cmdline == NULL && args.rfp == NULL)) {
    cleanup(mrb, &args);
    usage(argv[0]);
    return n;
  }

  ARGV = mrb_ary_new_capa(mrb, args.argc);
  for (i = 0; i < args.argc; i++) {
    mrb_ary_push(mrb, ARGV, mrb_str_new(mrb, args.argv[i], strlen(args.argv[i])));
  }
  mrb_define_global_const(mrb, "ARGV", ARGV);

  c = mrbc_context_new(mrb);
  if (args.verbose)
    c->dump_result = 1;
  if (args.check_syntax)
    c->no_exec = 1;
  if (args.mrbfile) {
    v = mrb_load_irep_file_cxt(mrb, args.rfp, c);
  }
  else {
    mrb_sym zero_sym = mrb_intern_lit(mrb, "$0");

    if (args.rfp) {
      char *cmdline;
      cmdline = args.cmdline ? args.cmdline : "-";
      mrbc_filename(mrb, c, cmdline);
      mrb_gv_set(mrb, zero_sym, mrb_str_new_cstr(mrb, cmdline));
      v = mrb_load_file_cxt(mrb, args.rfp, c);
    }
    else {
      mrbc_filename(mrb, c, "-e");
      mrb_gv_set(mrb, zero_sym, mrb_str_new(mrb, "-e", 2));
      v = mrb_load_string_cxt(mrb, args.cmdline, c);
    }
  }
  mrbc_context_free(mrb, c);
  if (mrb->exc) {
    if (!mrb_undef_p(v)) {
      mrb_print_error(mrb);
    }
    n = -1;
  }
  else if (args.check_syntax) {
    printf("Syntax OK\n");
  }
  cleanup(mrb, &args);

  return n == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}

大致流程:

初始化mruby执行环境 解析命令行参数 根据参数执行相应的动作 清理退出

main函数一开始的mrb_open就是初始化mruby执行环境。 进入函数,来到mrb_open_allocf,看来只是一个壳,继续。 进入后往下走,mrb_init_core,这个函数很可疑哦,必须进去探个究竟。

void
mrb_init_core(mrb_state *mrb)
{
  mrb_init_symtbl(mrb); DONE;

  mrb_init_class(mrb); DONE;
  mrb_init_object(mrb); DONE;
  mrb_init_kernel(mrb); DONE;
  mrb_init_comparable(mrb); DONE;
  mrb_init_enumerable(mrb); DONE;

  mrb_init_symbol(mrb); DONE;
  mrb_init_exception(mrb); DONE;
  mrb_init_proc(mrb); DONE;
  mrb_init_string(mrb); DONE;
  mrb_init_array(mrb); DONE;
  mrb_init_hash(mrb); DONE;
  mrb_init_numeric(mrb); DONE;
  mrb_init_range(mrb); DONE;
  mrb_init_gc(mrb); DONE;
  mrb_init_mrblib(mrb); DONE;
#ifndef DISABLE_GEMS
  mrb_init_mrbgems(mrb); DONE;
#endif
}

初始化符号表类型系统。

mrb_init_mrblib,我对这个函数很感兴趣,因为mrblib里面的代码是ruby写的,被编译成了字节码,这个函数是不是要执行字节码了呢?果断进去。 mrb_load_irep从这个函数名或许可以看出点什么?keep going。 mrb_load_irep_cxt,往下走。 有个mrb_context_run,函数名有run,可能真的要开始“跑“了。

山重水复疑无路,柳暗花明又一村。

初极狭,才通人。复行数十步,豁然开朗。土地平旷,屋舍俨(yǎn)然,有良田,美池,桑竹之属。阡陌(qiān mò)交通,鸡犬相闻。其中往来种作,男女衣著(zhuó),悉如外人。黄发垂髫(tiáo),并怡然自乐。

一个大大的switch语句呈现在我们眼前,仿佛进入了世外桃源。字节码在这里有序执行,男女衣著,怡然自乐。

在这里吃喝玩乐数天,该出去了。

ctrl+t一路返回到main函数。

关键代码: c = mrbc_context_new(mrb);

v = mrb_load_file_cxt(mrb, args.rfp, c); v = mrb_load_string_cxt(mrb, args.cmdline, c);

带上太守和村民寻找桃花源。 两个函数跟进,最终都到达了mrb_context_run

侯门一入深如海,从此萧郎是路人。

以上就是大致的执行流程,我没有深入语法分析部分,那里面的水实在太深,涉及编译原理,可以另外开篇了。

以前读过一篇python源码分析,看完mruby的代码,感觉和python很像,或许大部分解释器都是这个流程,只是我没看过而已。

API: mrb_openmrb_close初始化和资源清理。 mrb_load_filemrb_load_file_cxt执行文件,后者带上下文。 mrb_load_stringmrb_load_string_cxt执行字符串。

共收到 16 条回复
1553
Peter · #1 · 2014年6月16日

才子,真有闲!

12260
xiongxin8802 · #2 · 2014年6月16日

牛逼!

2466
rasefon · #3 · 2014年6月16日

lz的vim配色真重口味。

9800
pynix · #4 · 2014年6月16日

#3楼 @rasefon 默认配色。。。。

2602
deathking · #5 · 2014年6月16日

大有发展成mruby hacking guide之势。_^

9800
pynix · #6 · 2014年6月16日

#5楼 @DeathKing 是吗?

2602
deathking · #7 · 2014年6月16日 1 个赞

#6楼 @pynix 既然都做了这么多工作了,可以继续分析下去,做做Diagram,理一下整个逻辑(解释流程)可能会好一点。我目前看过最漂亮的C语言实现的解释器应该是Lua,云风大神也写过Lua的源码分析。

还有,RHG(Ruby Hacking Guide)是一本很好的书,分析地也比较透彻。作者不是简单地向读者介绍如何去分析源码,而是在理解源码的接触上,又自己整理了一下。照这个势头,你完全可以写成mruby hacking guide。

mruby的词法、语法分析可以略过,记忆中Ruby是拿yacc整的,其实也挺麻烦的。我觉得详细分析一下Ruby中的数据类型在C语言层面上是如何表示的。之前在RHG上看到RString和RArray里面有个aux域,感觉有点精妙。

读源码是件乐事,可以从中学到很多东西,之前为了在实现一个Scheme解释器的时候,我就参考了Lua和Ruby的实现代码(Python方面则是参考一个师兄之前做的讲座有介绍CPython的实现),偷学了不少技巧,就比如Ruby中对VALUE的处理。受用无穷。

楼主加油!

9800
pynix · #8 · 2014年6月16日

#7楼 @DeathKing 刚刚看了一下,词法分析是手动写的,语法分析是yacc(bison),我参考你说的资料整理一下。

9800
pynix · #9 · 2014年6月16日

#7楼 再看看lua分析。

9800
pynix · #10 · 2014年6月16日

#7楼 @DeathKing 风云大神原来是网易游戏的主力啊,进他的博客看了一下,基本都是lua。

146
skandhas · #11 · 2014年6月16日

看源码的话,建议先从 Ruby 的对象模型入手, 这个看明白之后,所有所谓的什么 “元编程” 就都没什么神秘了。 然后有兴趣可以再看看 Proc 的实现或你其他感兴的 Class。 再就可以看 mruby 的 VM 和 GC 了。 至词词法,语法,同意 @DeathKing 的看法,先略过就好。 mruby 的词法分析就是从 CRuby 那里拿来的,略微调整了一下。 再往后,有时间可以看看 字节码的生成等。。。

7733
yukihiro_matz · #12 · 2014年6月16日

#11楼 @skandhas 大牛要多多科普啊

9800
pynix · #13 · 2014年6月16日

#11楼 @skandhas

大学编译原理没学好,看来还是需求驱动学习。

146
skandhas · #14 · 2014年6月16日

#12楼 @yukihiro_matz 给跪了!mruby 可是出自你手啊!还是你来更合适!;) #13楼 @pynix mruby 的源码不算多,一点一点看就可以了,期待你的源码分析笔记。

9800
pynix · #15 · 2014年6月16日
9800
pynix · #16 · 2014年6月18日

这几天浏览下来最不满意的就语法分析了。

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册