[[mrubyを読む]]

#contents

*はじめに [#o2d08338]

初期化の際に呼び出されるのはmrb_open()関数のようです。

* mrb_open(state.c) [#y0e781ad]

それでは早速mrb_open()を見てみましょう。

#code(C){{
mrb_state*
mrb_open()
{
  mrb_state *mrb = mrb_open_allocf(allocf);

  return mrb;
}
}}

というわけでmrb_open_allocf()に委譲です。

*allocf(state.c) [#vd2aeb9f]

mrb_open_allocf()に進む前にallocfとは何者なのか見てみましょう。allocfはmrb_open()のすぐ上に書かれています。

#code(C){{
static void*
allocf(mrb_state *mrb, void *p, size_t size)
{
  if (size == 0) {
    free(p);
    return NULL;
  }
  else {
    return realloc(p, size);
  }
}
}}

というわけでallocfとはメモリ確保・解放関数のようです。mrubyは組み込み向け((組み込み業界に関わっているので組み込みと聞くと機械に組み込むことを考えてしまうのですが、mrubyはどちらかというとすでにApacheに組み込まれているようにソフトの機能拡張としての組み込み言語という意味合いが強いような気がします))なので独自のメモリアロケーターを使いやすいようになっているのでしょう。

allocfに書いてあることをそのまま書くとallocf、というかmrb_open_allocf()に渡すmrb_allocfの仕様は以下のようになっているようです。

-第2引数で指定されているメモリ領域を第3引数で指定した量に拡張する(第2引数がNULLの場合は単純に第3引数分メモリ確保する)
-第3引数が0の場合は第2引数が指すメモリ領域を開放する

2つ目の仕様がなんか気にくわないですがallocfの呼び出しをwrapした馴染みの関数が定義されているのでメモリ解放時に0指定で呼び出すという気持ち悪いことはしないでもOKです。

gc.c
#code(C){{
void*
mrb_realloc(mrb_state *mrb, void *p, size_t len)
{
  return (mrb->allocf)(mrb, p, len);
}

void*
mrb_malloc(mrb_state *mrb, size_t len)
{
  return (mrb->allocf)(mrb, 0, len);
}

void*
mrb_calloc(mrb_state *mrb, size_t nelem, size_t len)
{
  void *p = (mrb->allocf)(mrb, 0, nelem*len);

  memset(p, 0, nelem*len);
  return p;
}

void*
mrb_free(mrb_state *mrb, void *p)
{
  return (mrb->allocf)(mrb, p, 0);
}
}}

ちなみに、freeが別関数になっていないのは気持ち悪いとか0サイズ指定だとfreeなのは気にくわないとか書いてますが、少しでもmrb_state構造体のサイズを減らす努力かもと思います。う〜ん、でもそんなにメモリが厳しい環境って、allocf中のif文の方が時間的・空間的につらくなるような気も。

*mrb_open_allocf(state.c) [#r6a38647]

さて、話が長くなりましたがmrb_open_allocf()です。

#code(C){{
mrb_state*
mrb_open_allocf(mrb_allocf f)
{
  mrb_state *mrb = (f)(NULL, NULL, sizeof(mrb_state));

  memset(mrb, 0, sizeof(mrb_state));
  mrb->allocf = f;
  mrb->current_white_part = MRB_GC_WHITE_A;

  mrb_init_heap(mrb);
  mrb_init_core(mrb);
  mrb_init_ext(mrb);
  return mrb;
}
}}

見ての通り、mrb_state構造体を確保した後、ヒープやコアライブラリと思えるものを初期化しています。なお、current_white_partというのはGC関連の変数です。

mrb_init_heap()は普通にヒープを初期化しているだけなので飛ばしてmrb_init_core()に進みます。

*mrb_init_core(init.c) [#x374ac89]

mrb_init_core()ですが予想通りにRubyオブジェクトスペースの構築を行っているようです。それらに入っていく前に一つ面白い点があったので取り上げます。

#code(C){{
void
mrb_init_core(mrb_state *mrb)
{
  mrb_init_symtbl(mrb);

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

  mrb_init_symbols(mrb);
  mrb_init_proc(mrb);
  mrb_init_string(mrb);
  mrb_init_array(mrb);
  mrb_init_hash(mrb);
  mrb_init_numeric(mrb);
  mrb_init_range(mrb);
  mrb_init_struct(mrb);
  mrb_init_gc(mrb);
#ifdef INCLUDE_REGEXP
  mrb_init_regexp(mrb);
  mrb_init_encoding(mrb);
#endif
  mrb_init_exception(mrb);
  mrb_init_print(mrb);
  mrb_init_time(mrb);
  mrb_init_math(mrb);

  mrb_init_mrblib(mrb);

  mrb_gc_arena_restore(mrb, 0);
}
}}

RegexpとEncodingの構築は条件コンパイルになっています。正規表現や文字エンコーディングの処理は大きめなので容易にカットできるようにしているのでしょう。その場合、リテラルで書いてある正規表現とかがどうなるかは興味深いところですがそれはまたパーサを読むときに見ることにします。

さて、各initに進みます。まず初めのmrb_init_symtbl()は予想通りに名前からシンボルID、シンボルIDから名前のハッシュテーブルを作成しています。

**mrb_init_class(class.c) [#jb38f539]

次にmrb_init_class()を見てみます。BasicObject, Object, Module, Classを構築しています。普通のクラスを定義するための関数はmrb_define_class()ですが、BasicObject, Object, Module, Classの構築ではmrb_define_class()中のクラス構築ステップをばらして呼ばれています。mrb_define_class()と照らし合わせて見てみましょう。

#code(C){{
  /* boot class hierarchy */
  bob = boot_defclass(mrb, 0);
  obj = boot_defclass(mrb, bob); mrb->object_class = obj;
  mod = boot_defclass(mrb, obj); mrb->module_class = mod;/* obj -> mod */
  cls = boot_defclass(mrb, mod); mrb->class_class = cls; /* obj -> cls */
  /* fix-up loose ends */
  bob->c = obj->c = mod->c = cls->c = cls;
  make_metaclass(mrb, bob);
  make_metaclass(mrb, obj);
  make_metaclass(mrb, mod);
  make_metaclass(mrb, cls);

  /* name basic classes */
  mrb_define_const(mrb, obj, "BasicObject", mrb_obj_value(bob));
  mrb_define_const(mrb, obj, "Object", mrb_obj_value(obj));
  mrb_define_const(mrb, obj, "Module", mrb_obj_value(mod));
  mrb_define_const(mrb, obj, "Class", mrb_obj_value(cls));

  /* name each classes */
  mrb_name_class(mrb, bob, mrb_intern(mrb, "BasicObject"));
  mrb_name_class(mrb, obj, mrb_intern(mrb, "Object"));
  mrb_name_class(mrb, mod, mrb_intern(mrb, "Module"));
  mrb_name_class(mrb, cls, mrb_intern(mrb, "Class"));
}}

mrb_define_class()とmrb_define_class()から呼ばれる関数は以下のようになります。

#code(C){{
struct RClass*
mrb_define_class(mrb_state *mrb, const char *name, struct RClass *super)
{
  struct RClass *c;
  c = mrb_define_class_id(mrb, mrb_intern(mrb, name), super);
  return c;
}

struct RClass*
mrb_define_class_id(mrb_state *mrb, mrb_sym name, struct RClass *super)
{
  struct RClass *c = mrb_class_new(mrb, super);

  mrb_obj_iv_set(mrb, (struct RObject*)mrb->object_class,
                 name, mrb_obj_value(c));
  mrb_name_class(mrb, c, name);

  return c;
}

struct RClass *
mrb_class_new(mrb_state *mrb, struct RClass *super)
{
  struct RClass *c;

  if (super) {
    mrb_check_inheritable(mrb, super);
  }
  c = boot_defclass(mrb, super);
  make_metaclass(mrb, c);

  return c;
}

static void
make_metaclass(mrb_state *mrb, struct RClass *c)
{
  struct RClass *sc;

  if (c->c->tt == MRB_TT_SCLASS) {
    return;
  }
  sc = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_SCLASS, mrb->class_class);
  sc->mt = 0;
  if (!c->super) {
    sc->super = mrb->class_class;
  }
  else {
    sc->super = c->super->c;
  }
  c->c = sc;
  mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)sc);
  mrb_field_write_barrier(mrb, (struct RBasic*)sc, (struct RBasic*)sc->super);
}
}}

さて、というわけでBasicObject, Object, Module, Classがmrb_define_class()で作れない理由がおわかりいただけたかと思います。つまり、

-クラス名をobject_classに登録しようにもobject_classがまだ生成されていない(mrb_define_const(mrb, obj, "BasicObject", mrb_obj_value(bob))とmrb_obj_iv_set(mrb, (struct RObject*)mrb->object_class, name, mrb_obj_value(c))は等価です)
-メタクラスを設定しようにもclass_classがまだ生成されていない

というわけです。

(今ここ!)

トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS