Rubyの初期化について昔調べたので書きます。
Rubyのtop self、あるいはmainオブジェクトについて
琉球大学の学生(@_attonさん)が立ち上げたGitHubのOrganization、ie-developersに以下の質問が立っていた。
個人的にも気になっていたので色々な実装を読んでいた。
mainオブジェクト · Issue #8 · ie-developers/ie-questions
MRI
void
Init_top_self(void)
{
rb_vm_t *vm = GET_VM();
vm->top_self = rb_obj_alloc(rb_cObject);
rb_define_singleton_method(rb_vm_top_self(), "to_s", main_to_s, 0);
rb_define_alias(rb_singleton_class(rb_vm_top_self()), "inspect", "to_s");
/* initialize mark object array, hash */
vm->mark_object_ary = rb_ary_tmp_new(1);
}
CRubyではInit_
のプレフィックスがついているものは初期化時によばれる。
Ruby界でのクラスはCではだいたいrb_c
のプレフィックスがついている。
RubyのメソッドはCレベルではrb_<ここにクラス名>_<メソッド名>
みたいになってることが多い。
トップレベルで
puts self
したときの動作もこれをみれば一目瞭然ですね。
起動まではたぶんusaさんのブログでめっちゃ書かれてる
Rubinius
かいつまむとこのあたり。コメントから溢れ出る天地創造感。
/* Creates the rubinius object universe from scratch. */
void VM::bootstrap_ontology(STATE) {
/* (中略) */
Object* main = new_object<Object>(G(object));
GO(main).set(main);
G(object)->set_const(state, "MAIN", main); // HACK test hooking up MAIN
/* (後略) */
}
たどるとこんな感じ。環境があって、状態があって、そこに世界がハマる。
https://github.com/rubinius/rubinius/blob/v2.4.0/vm/vm.cpp#L134
https://github.com/rubinius/rubinius/blob/v2.4.0/vm/environment.cpp#L460
https://github.com/rubinius/rubinius/blob/v2.4.0/vm/environment.cpp#L800
https://github.com/rubinius/rubinius/blob/v2.4.0/vm/drivers/cli.cpp#L55
mainオブジェクト自体の定義はこのあたり。
世界を作る際に定数としてMAINが作られていてそこに足される感じですね。
https://github.com/rubinius/rubinius/blob/v2.4.0/kernel/common/main.rb
JRuby
実装がいくつかある。
Truffle
Truffleについては以下
https://github.com/jruby/jruby/wiki/Truffle
// CoreLibraryのinitializeの中
mainObject = new RubyBasicObject(objectClass);
たどると
https://github.com/jruby/jruby/blob/f66d751a35e8dda462489a6d402114b2e42d1c40/core/src/main/java/org/jruby/truffle/runtime/RubyContext.java#L94
https://github.com/jruby/jruby/blob/f66d751a35e8dda462489a6d402114b2e42d1c40/core/src/main/java/org/jruby/truffle/TruffleBridgeImpl.java#L48
https://github.com/jruby/jruby/blob/f66d751a35e8dda462489a6d402114b2e42d1c40/core/src/main/java/org/jruby/Ruby.java#L899,L900
https://github.com/jruby/jruby/blob/f66d751a35e8dda462489a6d402114b2e42d1c40/core/src/main/java/org/jruby/Ruby.java#L831
(略)
Truffleじゃないほう
private void initRoot() {
// Object is ready, create top self
topSelf = TopSelfFactory.createTopSelf(this);
Ruby. newInstance
でインスタンスができ、そのときinit、initRootとよばれtopSelfがうまれる。
https://github.com/jruby/jruby/blob/f66d751a35e8dda462489a6d402114b2e42d1c40/core/src/main/java/org/jruby/Ruby.java#L1199
https://github.com/jruby/jruby/blob/f66d751a35e8dda462489a6d402114b2e42d1c40/core/src/main/java/org/jruby/Ruby.java#L315
(略)
MRuby
MRubyだけめっちゃ新鮮で、topselfあるかないかチェックしてないときだけ初期化してる。
MRB_API mrb_value
mrb_top_self(mrb_state *mrb)
{
if (!mrb->top_self) {
mrb->top_self = (struct RObject*)mrb_obj_alloc(mrb, MRB_TT_OBJECT, mrb->object_class);
mrb_define_singleton_method(mrb, mrb->top_self, "inspect", inspect_main, MRB_ARGS_NONE());
mrb_define_singleton_method(mrb, mrb->top_self, "to_s", inspect_main, MRB_ARGS_NONE());
}
return mrb_obj_value(mrb->top_self);
}
たぶんmrb_state
は使い回されるんだろうな。で毎回初期化処理は通るんだけどすでに初期化されていればなにもしない、みたいな。
このコミットでなんかスタックの位置保存するみたいなやつはいってて、stack_keepの分だけさすスタック伸ばしてから実行してるっぽい(くわしくはしらない)
https://github.com/mruby/mruby/commit/6bf1ee78c8cdc5c1b0265694c404ada0ec32cb28
まとめ
Ruby実装いっぱいあって面白い