NODE_DEFN 等
まず、クラスやメソッドを定義する文のコンパイルです。
ノード | コード |
---|---|
NODE_DEFN | def mname(...) |
NODE_DEFS | def obj.mname(...) |
NODE_MODULE | module MName |
NODE_CLASS | class CName |
NODE_SCLASS | class ≪obj |
例えばJavaやC++のような静的な言語では、この手の文はコンパイル時に解析されるので、ここまでで扱ってきたような実行時の文とは全く違う処理が必要となります。しかし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; }
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