ひとり勉強会

ひとり楽しく勉強会

変数/定数読み取り

Rubyには変数にも色々種類があります...というのは、代入文のところでやりました。

種類 NODE 命令
メソッドローカル変数 NODE_LVAR getlocal
ブロックローカル変数 NODE_DVAR getdynamic
グローバル変数 NODE_GVAR getglobal
インスタンス変数 NODE_IVAR getinstancevariable
クラス変数 NODE_CVAR getclassvariable

変数の値を読む式は、変数の種類に応じて、対応するYARVの1命令にコンパイルされます。例えばローカル変数ならこんな感じ。

case NODE_LVAR:{
  if (!poped) {
    int idx = iseq->local_iseq->local_size + 2 - node->nd_cnt;
    debugs("idx: %d\n", idx);
    ADD_INSN1(ret, nd_line(node), getlocal, INT2FIX(idx));
  }
  break;
}
種類 NODE 命令
定数 NODE_CONST getconstant
obj::定数 NODE_COLON2 getconstant
::定数 NODE_COLON3 getconstant
ありの場合は少しややこしいですが、定数も基本は getconstant 1命令へコンパイルされます定数を取ってくる対象のクラスオブジェクトをスタックに積んで、getconstant する、という命令列にコンパイルされます(nilが積んであればカレントスコープ、falseが積んであればトップレベルスコープから検索。)。。ただし、「定数インラインキャッシュ」の最適化が有効になっていると、こんな感じにキャッシュチェック命令が入ります。
lstart:
  getinlinecache lend
  getconstant    :定数名
  setinlinecache
lend:

Rubyの定数はあとから幾らでも書き換えられるので、あまり「定数」っぽくはありません。それでも「実際には一度代入したらあとは変更しない」という使われ方が多いようです。ということで、前回値を読んだときから定数書き換えがおこっていなければ、定数の検索をせずにキャッシュから直接値を返す、という流れ。そのための特別な命令が、getinlinecache と setinlinecache です。詳しい動作はVMの方のコードを読むときにでも。参考:るびま

種類 NODE 命令
$1,$2,... NODE_NTH_REF getspecial
$&,$+,$',$` NODE_BACK_REF getspecial

組み込み変数(プログラミング言語 Ruby リファレンスマニュアル)のうち、ほとんどはgetglobal命令で値のとれる、グローバル変数です。ただし、正規表現マッチで使われる$1,$2,...だけは他のグローバル変数とは区別されて、getspecialというYARV命令に落ちるようです。

種類 NODE 命令
rescue C=>v NODE_ERRINFO -

変数の項でまとめる話じゃないかもしれませんが、resuce節で変数と例外のクラスを宣言した場合、NODE_ERRINFOというノードがくっついてきます。詳細略ですが、getglobal $! して、クラスチェックをした後変数vにsetdynamicするコードにコンパイルされています。ぜんぜん違いました!rescue C=>v; は、getdynamic $! -> setlocal v という流れになります。