変数/定数読み取り
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 という流れになります。