ruby code readingメモ
概要
ちょっと知りたいことがあって、ふとrubyのコードをみたのでメモ。
ちなみに初めてみるのと、そんなに舐め回すようにみたわけじゃないので、
間違ってるかもしれないです。
ただ、メモっておかないと、次みるとき忘れちゃうので・・・
なぜそんなのをみることになったか。
railsのコードの中でよく出てくる、ClassMethodsモジュールって何かね?
っていうのを調べてて、こんな記事に辿り着いたため。
Rails4 - Rails の module ClassMethods がやっている事 - Qiita
記事中に、include文の中で、append_featuresが呼ばれるんだよね、って書いてあったので、
どれどれ、どこなのかな、と思ってみてみることにしました。
詳細
なにはともあれ、rubyのソースをcloneします。
$ cd <WORKDIR> $ git clone https://github.com/ruby/ruby.git
rubyっていうディレクトリができるので、入って、lsしてみる。
$ cd ruby $ ls BSDL README.md compile.c dmydln.c eval_jump.c include man parse.y regerror.c sparc.c thread_pthread.h vm.c vm_trace.c CONTRIBUTING.md aclocal.m4 complex.c dmyenc.c ext inits.c marshal.c prelude.rb regexec.c spec thread_sync.c vm_args.c vsnprintf.c COPYING addr2line.c configure.in dmyext.c file.c insns.def math.c probes.d regint.h sprintf.c thread_win32.c vm_backtrace.c win32 COPYING.ja addr2line.h constant.h doc gc.c internal.h method.h probes_helper.h regparse.c st.c thread_win32.h vm_core.h ChangeLog array.c cont.c enc gc.h io.c miniinit.c proc.c regparse.h strftime.c time.c vm_debug.h GPL benchmark coverage encindex.h gem_prelude.rb iseq.c misc process.c regsyntax.c string.c timev.h vm_dump.c KNOWNBUGS.rb bignum.c cygwin encoding.c gems iseq.h missing random.c ruby.c struct.c tool vm_eval.c LEGAL bin debug.c enum.c golf_prelude.rb lex.c.blt nacl range.c ruby_atomic.h symbol.c transcode.c vm_exec.c Makefile.in bootstraptest defs enumerator.c goruby.c lib node.c rational.c safe.c symbol.h transcode_data.h vm_exec.h NEWS ccan dir.c error.c hash.c load.c node.h re.c sample template util.c vm_insnhelper.c README.EXT class.c dln.c eval.c ia64.s loadpath.c numeric.c regcomp.c signal.c test variable.c vm_insnhelper.h README.EXT.ja common.mk dln.h eval_error.c id_table.c localeinit.c object.c regenc.c siphash.c thread.c version.c vm_method.c README.ja.md compar.c dln_find.c eval_intern.h id_table.h main.c pack.c regenc.h siphash.h thread_pthread.c version.h vm_opts.h
おお、直下に沢山。
Cとか読むの10年ぶりくらいですけど大丈夫でしょうか。
遠い記憶を掘り起こして・・・Cはまずmain.cだ!!!
$ cat main.c /********************************************************************** main.c - $Author$ created at: Fri Aug 19 13:19:58 JST 1994 Copyright (C) 1993-2007 Yukihiro Matsumoto **********************************************************************/ #undef RUBY_EXPORT #include "ruby.h" #include "vm_debug.h" #ifdef HAVE_LOCALE_H #include <locale.h> #endif #ifdef RUBY_DEBUG_ENV #include <stdlib.h> #endif int main(int argc, char **argv) { #ifdef RUBY_DEBUG_ENV ruby_set_debug_option(getenv("RUBY_DEBUG")); #endif #ifdef HAVE_LOCALE_H setlocale(LC_CTYPE, ""); #endif ruby_sysinit(&argc, &argv); { RUBY_INIT_STACK; ruby_init(); return ruby_run_node(ruby_options(argc, argv)); } }
色々書いてあるけど、
ruby_init(); return ruby_run_node(ruby_options(argc, argv));
が次かな。ここから色々grep。
$ grep -E 'ruby_init' *.c | grep -v 'ruby_init_' debug.c: SET_WHEN("gc_stress", *ruby_initial_gc_stress_ptr, Qtrue); eval.c:ruby_init(void) gc.c:#define ruby_initial_gc_stress gc_params.gc_stress gc.c:VALUE *ruby_initial_gc_stress_ptr = &ruby_initial_gc_stress; gc.c: gc_stress_set(objspace, ruby_initial_gc_stress); loadpath.c:const char ruby_initial_load_paths[] = main.c: ruby_init(); miniinit.c:const char ruby_initial_load_paths[] = ""; ruby.c: const char *paths = ruby_initial_load_paths;
ファイル名的に怪しいのは、eval.cである。で、開いてみたところ、
起動時のキーになりそうな関数が沢山。
int ruby_setup(void) void ruby_init(void) void * ruby_options(int argc, char **argv) static void ruby_finalize_0(void) などなど
ruby_init()でなにやってるかというと・・・
書いてあるとおり、
Calls ruby_setup() and check error. Prints errors and calls exit(3) if an error occurred.
です。てコメントに書いてあるけど、EXIT_FAILUREは、defineで 1 になってる気がする。まぁいい。
/* Calls ruby_setup() and check error. * * Prints errors and calls exit(3) if an error occurred. */ void ruby_init(void) { int state = ruby_setup(); if (state) { if (RTEST(ruby_debug)) error_print(); exit(EXIT_FAILURE); } }
ruby_setup()は・・・Ruby VMとビルトインライブラリの初期化、と。
Initializes the Ruby VM and builtin libraries.
/* Initializes the Ruby VM and builtin libraries. * @retval 0 if succeeded. * @retval non-zero an error occurred. */ int ruby_setup(void) { int state; if (GET_VM()) return 0; ruby_init_stack((void *)&state); Init_BareVM(); Init_heap(); Init_vm_objects(); PUSH_TAG(); if ((state = EXEC_TAG()) == 0) { rb_call_inits(); ruby_prog_init(); GET_VM()->running = 1; } POP_TAG(); return state; }
ここから、さっと追ってみて気になったのは、rb_call_inits()くらいかな。
inits.cに定義されている。
$ cat inits.c /********************************************************************** inits.c - $Author$ created at: Tue Dec 28 16:01:58 JST 1993 Copyright (C) 1993-2007 Yukihiro Matsumoto **********************************************************************/ #include "internal.h" #define CALL(n) {void Init_##n(void); Init_##n();} void rb_call_inits(void) { CALL(Method); CALL(RandomSeed); CALL(sym); CALL(var_tables); CALL(Object); CALL(top_self); CALL(Encoding); CALL(Comparable); CALL(Enumerable); CALL(String); CALL(Exception); CALL(eval); CALL(safe); CALL(jump); CALL(Numeric); CALL(Bignum); CALL(syserr); CALL(Array); CALL(Hash); CALL(Struct); CALL(Regexp); CALL(pack); CALL(transcode); CALL(marshal); CALL(Range); CALL(IO); CALL(Dir); CALL(Time); CALL(Random); CALL(signal); CALL(load); CALL(Proc); CALL(Binding); CALL(Math); CALL(GC); CALL(Enumerator); CALL(VM); CALL(ISeq); CALL(Thread); CALL(process); CALL(Cont); CALL(Rational); CALL(Complex); CALL(version); CALL(vm_trace); } #undef CALL
ひたすら、Init_XXXXっていうのを呼び出す関数のようで。
その中の、Init_evalってのをみてみると、
Init_eval(void) { .......... ...中略... .......... rb_define_method(rb_cModule, "include", rb_mod_include, -1); rb_define_method(rb_cModule, "prepend", rb_mod_prepend, -1); rb_define_private_method(rb_cModule, "append_features", rb_mod_append_features, 1); rb_define_private_method(rb_cModule, "extend_object", rb_mod_extend_object, 1); rb_define_private_method(rb_cModule, "prepend_features", rb_mod_prepend_features, 1); rb_define_private_method(rb_cModule, "refine", rb_mod_refine, 1); rb_define_private_method(rb_cModule, "using", mod_using, 1); .......... ...中略... .......... }
・・・・append_featuresがいる。includeも。
rb_define_private_methodは、名前からして、"append_features"って言ったら、rb_mod_append_featuresってことだよ。
なんだろねきっと。
そうすると、rb_mod_includeと、rb_mod_prependを調べると何かわかりそう。
$ grep rb_mod_include *.c class.c:rb_mod_included_modules(VALUE mod) class.c:rb_mod_include_p(VALUE mod, VALUE mod2) eval.c:rb_mod_include(int argc, VALUE *argv, VALUE module) eval.c: return rb_mod_include(argc, argv, th->top_wrapper); eval.c: return rb_mod_include(argc, argv, rb_cObject); eval.c: rb_define_method(rb_cModule, "include", rb_mod_include, -1); object.c: rb_define_method(rb_cModule, "included_modules", rb_mod_included_modules, 0); /* in class.c */ object.c: rb_define_method(rb_cModule, "include?", rb_mod_include_p, 1); /* in class.c */
またeval.cかな。
static VALUE rb_mod_include(int argc, VALUE *argv, VALUE module) { int i; ID id_append_features, id_included; CONST_ID(id_append_features, "append_features"); CONST_ID(id_included, "included"); for (i = 0; i < argc; i++) Check_Type(argv[i], T_MODULE); while (argc--) { rb_funcall(argv[argc], id_append_features, 1, module); rb_funcall(argv[argc], id_included, 1, module); } return module; }
というわけで、includeされたらきっとこれが呼ばれるのかな。
んで、紆余曲折あって、
rb_funcall(argv[argc], id_append_features, 1, module); rb_funcall(argv[argc], id_included, 1, module);
きっとこれで、append_featuresが呼ばれるのだろう。
これ以上みると夜になりそうなので、今日はここでおしまい。