ひとり勉強会

ひとり楽しく勉強会

NODE_DEFINED

これが最後!

defined? e

変数などなどが定義されているかどうかを返す式です。定義されていれば式の種別を表す文字列、いなければ偽を返します。コンパイルはdefined_expr補助関数が全面的に担当しています。

case NODE_DEFINED:{
  if (!poped) {
    LABEL *lfinish = NEW_LABEL(nd_line(node));
    defined_expr(iseq, ret, node->nd_head, lfinish, Qtrue);
    ADD_LABEL(ret, lfinish);
  }
  break;
}

基本的に、Rubyのdefined?式は、YARVのdefined命令に変換されます。

defined  種類,  変数名など,  返値文字列が必要か?

例えば defined?(@iv) なら、こうです。

case NODE_IVAR:
  ADD_INSN(ret, nd_line(node), putnil);
  ADD_INSN3(ret, nd_line(node), defined, INT2FIX(DEFINED_IVAR),
            ID2SYM(node->nd_vid), needstr);
  return 1;

インスタンス変数の定義を調べたい!という意味のフラグ DEFINED_IVAR が先頭のパラメータです。このようなフラグが変数などなどの種類に応じて全て定義されているので、それを使います。第二引数は、変数名を表すシンボルです。最後のneedstrは、ユーザーがコードに書いたdefined?をコンパイルする際は常にtrueになっています。
他には例えば、ブロックが渡されているかどうか調べる defined?(yield) では、別にフラグが用意されています。

case NODE_YIELD:
  ADD_INSN(ret, nd_line(node), putnil);
  ADD_INSN3(ret, nd_line(node), defined,
            INT2FIX(DEFINED_YIELD), 0,
    needstr);
  return 1;

メソッドのdefined?を調べる場合には少し注意が必要です。

defined?(aaa.bbb)

という式を評価するには、aaaがどういうオブジェクトか調べて(これはaaaを評価しなければわかりません)そこにbbbメソッドがあるかどうか判定する必要があります。ところが、bbb以前にaaaが定義されていない可能性もあります。そういう場合も何事もなく偽を返すために、

  • まずはaaaのdefinedを検査
  • aaaを評価する
  • aaa.bbbがdefinedかどうか検査

という3段の命令列を生成します。

case NODE_CALL:
case NODE_VCALL:
case NODE_FCALL:
  if (nd_type(node) == NODE_CALL) {
    LABEL *lcont = NEW_LABEL(nd_line(node));

    defined_expr(iseq, ret, node->nd_recv, lfinish, Qfalse);
    ADD_INSNL(ret, nd_line(node), branchif, lcont) ;
    ADD_INSN(ret, nd_line(node), putnil);
    ADD_INSNL(ret, nd_line(node), jump, lfinish);

    ADD_LABEL(ret, lcont);
    COMPILE(ret, "defined/recv", node->nd_recv);
    ADD_INSN3(ret, nd_line(node), defined,
      INT2FIX(DEFINED_METHOD),
      ID2SYM(node->nd_mid), needstr);
  }
  ...

最初のレシーバのdefined検査で偽が帰ってきた場合、すぐにlfinishへジャンプするコードを生成しています(ここではdefined結果の文字列は不要なので、needstrにQfalseを指定しています。)。そうでなければ、改めてrecvを評価して、defined命令で検査、と。
他の要素にdefined?する場合も似たような雰囲気なので、省略。