独習mrubyバイトコード[GETIV]
OP_GETIV
Arguments
- A: レジスタ番号
- Bx: シンボル番号
How it works?
R(A) := ivget(Syms(Bx))
シンボル[Bx]に対応するインスタンスオブジェクトを、レジスタ[A]にセットする
When used?
インスタンスオブジェクトを利用するときに用いられる。
class Test
def set_val(v)
@instance_val = v
end
def show_val
puts @instance_val
end
end
t = Test.new
t.set_val("value")
t.show_val
例えば上記のようなコードをバイトコンパイルすると、下記のようなバイトコードが出力される。ちょっと長い。
00001 NODE_SCOPE:
00010 local variables:
00010 t
00001 NODE_BEGIN:
00001 NODE_CLASS:
00008 :Test
00008 body:
00001 NODE_BEGIN:
00002 NODE_DEF:
00004 set_val
00002 local variables:
00002 v
00002 mandatory args:
00002 NODE_ARG v
00003 NODE_BEGIN:
00003 NODE_ASGN:
00003 lhs:
00003 NODE_IVAR @instance_val
00003 rhs:
00003 NODE_LVAR v
00005 NODE_DEF:
00007 show_val
00006 NODE_BEGIN:
00006 NODE_FCALL:
00006 NODE_SELF
00006 method='puts' (232)
00006 args:
00006 NODE_IVAR @instance_val
00010 NODE_ASGN:
00010 lhs:
00010 NODE_LVAR t
00010 rhs:
00010 NODE_CALL(.):
00010 NODE_CONST Test
00010 method='new' (15)
00011 NODE_CALL(.):
00011 NODE_LVAR t
00011 method='set_val' (228)
00011 args:
00011 NODE_STR "value" len 5
00012 NODE_CALL(.):
00012 NODE_LVAR t
00012 method='show_val' (231)
irep 0x600081450 nregs=5 nlocals=2 pools=1 syms=4 reps=1
file: d.rb
1 000 OP_LOADNIL R2
1 001 OP_LOADNIL R3
1 002 OP_CLASS R2 :Test
1 003 OP_EXEC R2 I(+1)
10 004 OP_GETCONST R2 :Test
10 005 OP_SEND R2 :new 0
10 006 OP_MOVE R1 R2 ; R1:t
11 007 OP_STRING R3 L(0) ; "value"
11 008 OP_SEND R2 :set_val 1
12 009 OP_MOVE R2 R1 ; R1:t
12 010 OP_SEND R2 :show_val 0
12 011 OP_STOP
irep 0x600087670 nregs=3 nlocals=1 pools=0 syms=2 reps=2
file: d.rb
2 000 OP_TCLASS R1
2 001 OP_LAMBDA R2 I(+1) method
2 002 OP_METHOD R1 :set_val
5 003 OP_TCLASS R1
5 004 OP_LAMBDA R2 I(+2) method
5 005 OP_METHOD R1 :show_val
5 006 OP_LOADSYM R1 :show_val
5 007 OP_RETURN R0 normal
irep 0x60008d870 nregs=4 nlocals=3 pools=0 syms=1 reps=0
file: d.rb
2 000 OP_ENTER 1:0:0:0:0:0:0
3 001 OP_SETIV @instance_val R1 ; R1:v
3 002 OP_RETURN R1 normal ; R1:v
irep 0x60008f410 nregs=5 nlocals=2 pools=0 syms=2 reps=0
file: d.rb
5 000 OP_ENTER 0:0:0:0:0:0:0
6 001 OP_LOADSELF R2
6 002 OP_GETIV R3 @instance_val
6 003 OP_SEND R2 :puts 1
6 004 OP_RETURN R2 normal
クラスの定義などの説明を割愛すると、show_valメソッドに対応する”irep 0x60008f410”のirepの中で、OP_GETIVが呼ばれている。
set_valメソッドに対応する”irep 0x60008d870”の中で、OP_SETIVでセットしたインスタンス変数@instance_valをOP_GETIVによって読み出してR3にセットしている。
Note
mruby/cの場合
mruby/cの場合、下記の流れで処理が行われる。
- irepのシンボル領域から、インスタンス変数名の文字列を引く
- その文字列をシンボルに変換する
- mbc_instance_getiv()を呼び、シンボルに対応したインスタンス変数を見つけ、valに代入
- valをR[A]へ代入
mbc_instance_getiv()の中では、対象オブジェクトのR[0]が持つインスタンス変数格納用のハッシュテーブル”instance->ivar”から入力されたシンボルをキーにして、対応するオブジェクトを探している。
int ra = GETARG_A(code);
int rb = GETARG_Bx(code);
const char *sym_name = mrbc_get_irep_symbol(vm->pc_irep->ptr_to_sym, rb);
mrb_sym sym_id = str_to_symid(sym_name+1); // skip '@'
mrb_value val = mrbc_instance_getiv(®s[0], sym_id);
mrbc_release(®s[ra]);
regs[ra] = val;
return 0;
参考:インスタンス構造体の定義
プリミティブなオブジェクト以外全般を表す構造体mrb_instanceは、下記のようなポインタ情報を持っている。
- MRBC_OBJECT_HEADER: ヘッダ領域(参照カウンタ、デバッグ用情報等が入る)
- cls: そのインスタンスに対応するクラスオブジェクト
- ivar: インスタンス変数格納用のテーブル
- data: 配列、文字列格納用(その他にも用途あるか?)
typedef struct RInstance {
MRBC_OBJECT_HEADER;
struct RClass *cls;
struct RKeyValueHandle *ivar;
uint8_t data[];
} mrb_instance;
mrubyの場合
mrubyの場合、syms[]に予め文字列をシンボルに変換したテーブルが用意されているので、mrb_vm_iv_getを呼ぶだけの処理になっている。
内部では最終的に、iv_get()が呼ばれている。iv_get()については、OP_GETGLOBALの説明を参照。
CASE(OP_GETIV) {
/* A Bx R(A) := ivget(Bx) */
int a = GETARG_A(i);
int bx = GETARG_Bx(i);
mrb_value val = mrb_vm_iv_get(mrb, syms[bx]);
regs[a] = val;
NEXT;
}
attr_accessorを利用した場合
例えば、attr_accessorを用いた場合はどうなるか見てみる。
class Test
attr_accessor :instance_val
end
t = Test.new
t.instance_val="value"
puts t.instance_val
このようなコードをバイトコンパイルすると下記のようになる。
00001 NODE_SCOPE:
00005 local variables:
00005 t
00001 NODE_BEGIN:
00001 NODE_CLASS:
00003 :Test
00003 body:
00001 NODE_BEGIN:
00002 NODE_FCALL:
00002 NODE_SELF
00002 method='attr_accessor' (228)
00002 args:
00002 NODE_SYM :instance_val (229)
00005 NODE_ASGN:
00005 lhs:
00005 NODE_LVAR t
00005 rhs:
00005 NODE_CALL(.):
00005 NODE_CONST Test
00005 method='new' (15)
00006 NODE_ASGN:
00006 lhs:
00006 NODE_CALL(.):
00006 NODE_LVAR t
00006 method='instance_val' (229)
00006 rhs:
00006 NODE_STR "value" len 5
00007 NODE_FCALL:
00007 NODE_SELF
00007 method='puts' (231)
00007 args:
00007 NODE_CALL(.):
00007 NODE_LVAR t
00007 method='instance_val' (229)
irep 0x600081430 nregs=6 nlocals=2 pools=1 syms=5 reps=1
file: e.rb
1 000 OP_LOADNIL R2
1 001 OP_LOADNIL R3
1 002 OP_CLASS R2 :Test
1 003 OP_EXEC R2 I(+1)
5 004 OP_GETCONST R2 :Test
5 005 OP_SEND R2 :new 0
5 006 OP_MOVE R1 R2 ; R1:t
6 007 OP_STRING R2 L(0) ; "value"
6 008 OP_MOVE R3 R1 ; R1:t
6 009 OP_MOVE R4 R2
6 010 OP_SEND R3 :instance_val= 1
7 011 OP_LOADSELF R2
7 012 OP_MOVE R3 R1 ; R1:t
7 013 OP_SEND R3 :instance_val 0
7 014 OP_SEND R2 :puts 1
7 015 OP_STOP
irep 0x600087630 nregs=4 nlocals=1 pools=0 syms=2 reps=0
file: e.rb
2 000 OP_LOADSELF R1
2 001 OP_LOADSYM R2 :instance_val
2 002 OP_SEND R1 :attr_accessor 1
2 003 OP_RETURN R0 normal
クラスの初期化のコードに対応する、”irep 0x600087630”の中で、シンボル:instance_valを引き数にとって、Testクラス(R[1])のattr_accessorメソッドが呼ばれている。
attr_accessorメソッドの中で、入力されたシンボルを元に2つのアクセサメソッドの定義が行われる。それぞれ、OP_GETIV、OP_SETIVの内部処理で呼ばれる関数をラップしたメソッドになる。
- instance_val=
- instance_val
Recent Comments