ひとり勉強会

ひとり楽しく勉強会

NODE_DEFN 等

まず、クラスやメソッドを定義する文のコンパイルです。

ノード コード
NODE_DEFN def mname(...)
NODE_DEFS def obj.mname(...)
NODE_MODULE module MName
NODE_CLASS class CName
NODE_SCLASS class ≪obj

例えばJavaC++のような静的な言語では、この手の文はコンパイル時に解析されるので、ここまでで扱ってきたような実行時の文とは全く違う処理が必要となります。しかしRubyの場合は、クラス定義やメソッド定義も、実行時に左上から右下に実行される、単なる実行文です。なので、むしろ他の命令よりコンパイル自体は簡単かもしれません。

NODE_DEFN

def method()
  本体
end

は、

putnil
definemethod  :method, "本体"のコンパイル結果オブジェクト, 0

コンパイルされます。definemethod は、実行時に「現在のスコープになっているクラスに:methodメソッドを追加する」という動作をするYARV命令。最後の 0 は、「普通の」メソッド定義なことを表しています。

case NODE_DEFN:{
  VALUE iseqval = NEW_ISEQVAL(node->nd_defn,
                    rb_str_new2(rb_id2name(node->nd_mid)),
                    ISEQ_TYPE_METHOD);

  debugp_param("defn/iseq", iseqval);

  ADD_INSN (ret, nd_line(node), putnil);
  ADD_INSN3(ret, nd_line(node), definemethod,
    ID2SYM(node->nd_mid), iseqval, INT2FIX(0));
  if (!poped) {
      ADD_INSN(ret, nd_line(node), putnil);
  }
  debugp_param("defn", iseqval);
  break;
}

nd_defnが本体の構文木で、nd_midがメソッド名の入ったノードです。コンパイラのコードも、そのまんまですね。

NODE_DEFS

def expr.method()
  本体
end

NODE_DEFS は、特異メソッドの定義に対応するノードです。これは

exprを評価
definemethod :method, "本体", 1

こうなります。最後の 1 は特異メソッドの定義であることを意味します。スタックの一番上に置かれたオブジェクトに、特異メソッドが追加されます。

NODE_CLASS

class Klass < Super
  本体
end

は、大ざっぱにいうと、次のように defineclass 命令になります。

Klassの置かれる定数パスを評価
Superを評価
defineclass :Klass, "本体", 0

"本体"はdef文によるメソッド定義が並んでいることが多いですが、そこは"本体"のコンパイル中でdefinemethod命令の列になるので、クラス定義ではそれを単純に参照するだけで済みます。

case NODE_CLASS:{
  VALUE iseqval =
    NEW_CHILD_ISEQVAL(
      node->nd_body,
      make_name_with_str("<class:%s>",
        rb_id2name(node->nd_cpath->nd_mid)),
      ISEQ_TYPE_CLASS);
  compile_cpath(ret, iseq, node->nd_cpath);
  COMPILE(ret, "super", node->nd_super);
  ADD_INSN3(ret, nd_line(node), defineclass,
            ID2SYM(node->nd_cpath->nd_mid), iseqval,
            INT2FIX(0));

  if (poped) {
    ADD_INSN(ret, nd_line(node), pop);
  }
  break;
}

NODE_CDECL の説明のときも適当に飛ばしちゃいましたが、どこのスコープに定数(この場合はクラス名)を定義するのかは結構面倒な問題だそうで、その辺を頑張る命令列を生成するのが、compile_cpath関数です。あとはそのまんまな感じです。

NODE_SCLASS

class << expr
  本体
end

NODE_SCLASSは、特異クラスの定義です。これは最後の引数を 1 にした defineclass 命令です。

exprを評価
putnil
defineclass :singletonclass, "本体", 1

NODE_MODULE

module MBase::Module
  本体
end

NODE_MODULE はモジュール定義で、これも、似たようなものなので defineclass 命令になります。最後の引数 2 で区別されています。

MBaseを評価
putnil
defineclass :Module, "本体", 2