/** ##skip -*- mode:c; style:ruby; coding: utf-8 -*- | |
insns.def - YARV instruction definitions | |
$Author: $ | |
created at: 04/01/01 01:17:55 JST | |
Copyright (C) 2004-2007 Koichi Sasada | |
*/ | |
/** ##skip | |
instruction comment | |
@c: category | |
@e: english description | |
@j: japanese description | |
instruction form: | |
DEFINE_INSN | |
instruction_name | |
(instruction_operands, ..) | |
(pop_values, ..) | |
(return value) | |
{ | |
.. // insn body | |
} | |
*/ | |
/** | |
@c nop | |
@e nop | |
@j nop | |
*/ | |
DEFINE_INSN | |
nop | |
() | |
() | |
() | |
{ | |
/* none */ | |
} | |
/**********************************************************/ | |
/* deal with variables */ | |
/**********************************************************/ | |
/** | |
@c variable | |
@e Get local variable (pointed by `idx' and `level'). | |
'level' indicates the nesting depth from the current block. | |
@j level, idx で指定されたローカル変数の値をスタックに置く。 | |
level はブロックのネストレベルで、何段上かを示す。 | |
*/ | |
DEFINE_INSN | |
getlocal | |
(lindex_t idx, rb_num_t level) | |
() | |
(VALUE val) | |
{ | |
int i, lev = (int)level; | |
VALUE *ep = GET_EP(); | |
/* optimized insns generated for level == (0|1) in defs/opt_operand.def */ | |
for (i = 0; i < lev; i++) { | |
ep = GET_PREV_EP(ep); | |
} | |
val = *(ep - idx); | |
} | |
/** | |
@c variable | |
@e Set a local variable (pointed to by 'idx') as val. | |
'level' indicates the nesting depth from the current block. | |
@j level, idx で指定されたローカル変数の値を val にする。 | |
level はブロックのネストレベルで、何段上かを示す。 | |
*/ | |
DEFINE_INSN | |
setlocal | |
(lindex_t idx, rb_num_t level) | |
(VALUE val) | |
() | |
{ | |
int i, lev = (int)level; | |
VALUE *ep = GET_EP(); | |
/* optimized insns generated for level == (0|1) in defs/opt_operand.def */ | |
for (i = 0; i < lev; i++) { | |
ep = GET_PREV_EP(ep); | |
} | |
*(ep - idx) = val; | |
} | |
/** | |
@c variable | |
@e Get value of special local variable ($~, $_, ..). | |
@j 特殊なローカル変数($~, $_, ...)の値を得る。 | |
*/ | |
DEFINE_INSN | |
getspecial | |
(rb_num_t key, rb_num_t type) | |
() | |
(VALUE val) | |
{ | |
val = vm_getspecial(th, GET_LEP(), key, type); | |
} | |
/** | |
@c variable | |
@e Set value of special local variable ($~, $_, ...) to obj. | |
@j 特別なローカル変数($~, $_, ...)の値を設定する。 | |
*/ | |
DEFINE_INSN | |
setspecial | |
(rb_num_t key) | |
(VALUE obj) | |
() | |
{ | |
lep_svar_set(th, GET_LEP(), key, obj); | |
} | |
/** | |
@c variable | |
@e Get value of instance variable id of self. | |
If is_local is not 0, get value of class local variable. | |
@j self のインスタンス変数 id の値を得る。 | |
*/ | |
DEFINE_INSN | |
getinstancevariable | |
(ID id, IC ic) | |
() | |
(VALUE val) | |
{ | |
val = vm_getinstancevariable(GET_SELF(), id, ic); | |
} | |
/** | |
@c variable | |
@e Set value of instance variable id of self to val. | |
If is_local is not 0, set value of class local variable. | |
@j self のインスタンス変数 id を val にする。 | |
*/ | |
DEFINE_INSN | |
setinstancevariable | |
(ID id, IC ic) | |
(VALUE val) | |
() | |
{ | |
vm_setinstancevariable(GET_SELF(), id, val, ic); | |
} | |
/** | |
@c variable | |
@e Get value of class variable id of klass as val. | |
@j 現在のスコープのクラス変数 id の値を得る。 | |
*/ | |
DEFINE_INSN | |
getclassvariable | |
(ID id) | |
() | |
(VALUE val) | |
{ | |
NODE *cref = rb_vm_get_cref(GET_ISEQ(), GET_EP()); | |
val = rb_cvar_get(vm_get_cvar_base(cref, GET_CFP()), id); | |
} | |
/** | |
@c variable | |
@e Set value of class variable id of klass as val. | |
@j klass のクラス変数 id を val にする。 | |
*/ | |
DEFINE_INSN | |
setclassvariable | |
(ID id) | |
(VALUE val) | |
() | |
{ | |
NODE *cref = rb_vm_get_cref(GET_ISEQ(), GET_EP()); | |
rb_cvar_set(vm_get_cvar_base(cref, GET_CFP()), id, val); | |
} | |
/** | |
@c variable | |
@e | |
Get constant variable id. If klass is Qnil, constants | |
are searched in the current scope. If klass is Qfalse, constants | |
are searched as top level constants. Otherwise, get constant under klass | |
class or module. | |
@j 定数 id の値を得る。 | |
klass が Qnil なら、そのスコープで得られる定数の値を得る。 | |
Qfalse なら、トップレベルスコープを得る。 | |
それ以外なら、klass クラスの下の定数を得る。 | |
*/ | |
DEFINE_INSN | |
getconstant | |
(ID id) | |
(VALUE klass) | |
(VALUE val) | |
{ | |
val = vm_get_ev_const(th, GET_ISEQ(), klass, id, 0); | |
} | |
/** | |
@c variable | |
@e | |
Set constant variable id. If klass is Qfalse, constant | |
is able to access in this scope. if klass is Qnil, set | |
top level constant. otherwise, set constant under klass | |
class or module. | |
@j 定数 id の値を val にする。 | |
klass が Qfalse なら、そのスコープで得られる定数 id の値を設定する。 | |
Qnil なら、トップレベルスコープの値を設定する。 | |
それ以外なら、klass クラスの下の定数を設定する。 | |
*/ | |
DEFINE_INSN | |
setconstant | |
(ID id) | |
(VALUE val, VALUE cbase) | |
() | |
{ | |
vm_check_if_namespace(cbase); | |
rb_const_set(cbase, id, val); | |
} | |
/** | |
@c variable | |
@e get global variable id. | |
@j グローバル変数 id の値を得る。 | |
*/ | |
DEFINE_INSN | |
getglobal | |
(GENTRY entry) | |
() | |
(VALUE val) | |
{ | |
val = GET_GLOBAL((VALUE)entry); | |
} | |
/** | |
@c variable | |
@e set global variable id as val. | |
@j グローバル変数 id の値を設定する。 | |
*/ | |
DEFINE_INSN | |
setglobal | |
(GENTRY entry) | |
(VALUE val) | |
() | |
{ | |
SET_GLOBAL((VALUE)entry, val); | |
} | |
/**********************************************************/ | |
/* deal with values */ | |
/**********************************************************/ | |
/** | |
@c put | |
@e put nil to stack. | |
@j スタックに nil をプッシュする。 | |
*/ | |
DEFINE_INSN | |
putnil | |
() | |
() | |
(VALUE val) | |
{ | |
val = Qnil; | |
} | |
/** | |
@c put | |
@e put self. | |
@j スタックに self をプッシュする。 | |
*/ | |
DEFINE_INSN | |
putself | |
() | |
() | |
(VALUE val) | |
{ | |
val = GET_SELF(); | |
} | |
/** | |
@c put | |
@e put some object. | |
i.e. Fixnum, true, false, nil, and so on. | |
@j オブジェクト val をスタックにプッシュする。 | |
i.e. Fixnum, true, false, nil, and so on. | |
*/ | |
DEFINE_INSN | |
putobject | |
(VALUE val) | |
() | |
(VALUE val) | |
{ | |
/* */ | |
} | |
/** | |
@c put | |
@e put special object. "value_type" is for expansion. | |
@j 特別なオブジェクト val をスタックにプッシュする。 | |
オブジェクトの種類は value_type による. | |
*/ | |
DEFINE_INSN | |
putspecialobject | |
(rb_num_t value_type) | |
() | |
(VALUE val) | |
{ | |
enum vm_special_object_type type = (enum vm_special_object_type)value_type; | |
switch (type) { | |
case VM_SPECIAL_OBJECT_VMCORE: | |
val = rb_mRubyVMFrozenCore; | |
break; | |
case VM_SPECIAL_OBJECT_CBASE: | |
val = vm_get_cbase(GET_ISEQ(), GET_EP()); | |
break; | |
case VM_SPECIAL_OBJECT_CONST_BASE: | |
val = vm_get_const_base(GET_ISEQ(), GET_EP()); | |
break; | |
default: | |
rb_bug("putspecialobject insn: unknown value_type"); | |
} | |
} | |
/** | |
@c put | |
@e put iseq value. | |
@j put iseq value. | |
*/ | |
DEFINE_INSN | |
putiseq | |
(ISEQ iseq) | |
() | |
(VALUE ret) | |
{ | |
ret = iseq->self; | |
} | |
/** | |
@c put | |
@e put string val. string will be copied. | |
@j 文字列をコピーしてスタックにプッシュする。 | |
*/ | |
DEFINE_INSN | |
putstring | |
(VALUE str) | |
() | |
(VALUE val) | |
{ | |
val = rb_str_resurrect(str); | |
} | |
/** | |
@c put | |
@e put concatenate strings | |
@j スタックトップの文字列を n 個連結し,結果をスタックにプッシュする。 | |
*/ | |
DEFINE_INSN | |
concatstrings | |
(rb_num_t num) | |
(...) | |
(VALUE val) // inc += 1 - num; | |
{ | |
rb_num_t i = num - 1; | |
val = rb_str_resurrect(TOPN(i)); | |
while (i-- > 0) { | |
const VALUE v = TOPN(i); | |
rb_str_append(val, v); | |
} | |
POPN(num); | |
} | |
/** | |
@c put | |
@e to_str | |
@j to_str の結果をスタックにプッシュする。 | |
*/ | |
DEFINE_INSN | |
tostring | |
() | |
(VALUE val) | |
(VALUE val) | |
{ | |
val = rb_obj_as_string(val); | |
} | |
/** | |
@c put | |
@e to Regexp | |
@j 文字列 str を正規表現にコンパイルしてスタックにプッシュする。 | |
コンパイル時,opt を正規表現のオプションとする。 | |
*/ | |
DEFINE_INSN | |
toregexp | |
(rb_num_t opt, rb_num_t cnt) | |
(...) | |
(VALUE val) // inc += 1 - cnt; | |
{ | |
VALUE rb_reg_new_ary(VALUE ary, int options); | |
rb_num_t i; | |
const VALUE ary = rb_ary_tmp_new(cnt); | |
for (i = 0; i < cnt; i++) { | |
rb_ary_store(ary, cnt-i-1, TOPN(i)); | |
} | |
POPN(cnt); | |
val = rb_reg_new_ary(ary, (int)opt); | |
rb_ary_clear(ary); | |
} | |
/** | |
@c put | |
@e put new array. | |
@j 新しい配列をスタック上の num 個の値で初期化して生成しプッシュする。 | |
*/ | |
DEFINE_INSN | |
newarray | |
(rb_num_t num) | |
(...) | |
(VALUE val) // inc += 1 - num; | |
{ | |
val = rb_ary_new4((long)num, STACK_ADDR_FROM_TOP(num)); | |
POPN(num); | |
} | |
/** | |
@c put | |
@e dup array | |
@j 配列 ary を dup してスタックにプッシュする。 | |
*/ | |
DEFINE_INSN | |
duparray | |
(VALUE ary) | |
() | |
(VALUE val) | |
{ | |
val = rb_ary_resurrect(ary); | |
} | |
/** | |
@c put | |
@e expand array to num objects. | |
@j スタックトップのオブジェクトが配列であれば、それを展開する。 | |
配列オブジェクトの要素数が num以下ならば、代わりに nil を積む。num以上なら、 | |
num以上の要素は切り捨てる。 | |
配列オブジェクトでなければ、num - 1 個の nil を積む。 | |
もし flag が真なら、残り要素の配列を積む | |
flag: 0x01 - 最後を配列に | |
flag: 0x02 - postarg 用 | |
flag: 0x04 - reverse? | |
*/ | |
DEFINE_INSN | |
expandarray | |
(rb_num_t num, rb_num_t flag) | |
(..., VALUE ary) | |
(...) // inc += num - 1 + (flag & 1 ? 1 : 0); | |
{ | |
vm_expandarray(GET_CFP(), ary, num, (int)flag); | |
} | |
/** | |
@c put | |
@e concat two arrays | |
@j 二つの配列 ary1, ary2 を連結しスタックへプッシュする。 | |
*/ | |
DEFINE_INSN | |
concatarray | |
() | |
(VALUE ary1, VALUE ary2st) | |
(VALUE ary) | |
{ | |
const VALUE ary2 = ary2st; | |
VALUE tmp1 = rb_check_convert_type(ary1, T_ARRAY, "Array", "to_a"); | |
VALUE tmp2 = rb_check_convert_type(ary2, T_ARRAY, "Array", "to_a"); | |
if (NIL_P(tmp1)) { | |
tmp1 = rb_ary_new3(1, ary1); | |
} | |
if (NIL_P(tmp2)) { | |
tmp2 = rb_ary_new3(1, ary2); | |
} | |
if (tmp1 == ary1) { | |
tmp1 = rb_ary_dup(ary1); | |
} | |
ary = rb_ary_concat(tmp1, tmp2); | |
} | |
/** | |
@c put | |
@e splat array | |
@j 配列 ary に対して to_a を呼び出す。 | |
*/ | |
DEFINE_INSN | |
splatarray | |
(VALUE flag) | |
(VALUE ary) | |
(VALUE obj) | |
{ | |
VALUE tmp = rb_check_convert_type(ary, T_ARRAY, "Array", "to_a"); | |
if (NIL_P(tmp)) { | |
tmp = rb_ary_new3(1, ary); | |
} | |
else if (RTEST(flag)) { | |
tmp = rb_ary_dup(tmp); | |
} | |
obj = tmp; | |
} | |
/** | |
@c put | |
@e put new Hash. | |
@j 新しいハッシュをスタックトップの n 個を初期値として生成する。 | |
n はキーと値のペアなので 2 の倍数でなければならない。 | |
*/ | |
DEFINE_INSN | |
newhash | |
(rb_num_t num) | |
(...) | |
(VALUE val) // inc += 1 - num; | |
{ | |
rb_num_t i; | |
if(RUBY_DTRACE_HASH_CREATE_ENABLED()) { | |
RUBY_DTRACE_HASH_CREATE(num, rb_sourcefile(), rb_sourceline()); | |
} | |
val = rb_hash_new(); | |
for (i = num; i > 0; i -= 2) { | |
const VALUE v = TOPN(i - 2); | |
const VALUE k = TOPN(i - 1); | |
rb_hash_aset(val, k, v); | |
} | |
POPN(num); | |
} | |
/** | |
@c put | |
@e put new Range object.(Range.new(low, high, flag)) | |
@j Range.new(low, high, flag) のようなオブジェクトを生成しスタックにプッシュする。 | |
*/ | |
DEFINE_INSN | |
newrange | |
(rb_num_t flag) | |
(VALUE low, VALUE high) | |
(VALUE val) | |
{ | |
val = rb_range_new(low, high, (int)flag); | |
} | |
/**********************************************************/ | |
/* deal with stack operation */ | |
/**********************************************************/ | |
/** | |
@c stack | |
@e pop from stack. | |
@j スタックから一つポップする。 | |
*/ | |
DEFINE_INSN | |
pop | |
() | |
(VALUE val) | |
() | |
{ | |
(void)val; | |
/* none */ | |
} | |
/** | |
@c stack | |
@e duplicate stack top. | |
@j スタックトップをコピーしてスタックにプッシュする。 | |
*/ | |
DEFINE_INSN | |
dup | |
() | |
(VALUE val) | |
(VALUE val1, VALUE val2) | |
{ | |
val1 = val2 = val; | |
} | |
/** | |
@c stack | |
@e duplicate stack top n elements | |
@j スタックトップの n 個をコピーしてスタックにプッシュする。 | |
*/ | |
DEFINE_INSN | |
dupn | |
(rb_num_t n) | |
(...) | |
(...) // inc += n; | |
{ | |
rb_num_t i; | |
VALUE *sp = STACK_ADDR_FROM_TOP(n); | |
for (i = 0; i < n; i++) { | |
GET_SP()[i] = sp[i]; | |
} | |
INC_SP(n); | |
} | |
/** | |
@c stack | |
@e swap top 2 vals | |
@j スタックトップの 2 つの値を交換する。 | |
*/ | |
DEFINE_INSN | |
swap | |
() | |
(VALUE val, VALUE obj) | |
(VALUE obj, VALUE val) | |
{ | |
/* none */ | |
} | |
/** | |
@c stack | |
@e for stack caching. | |
@j スタックキャッシングの状態を調整するために必要な命令。 | |
*/ | |
DEFINE_INSN | |
reput | |
() | |
(..., VALUE val) | |
(VALUE val) // inc += 0; | |
{ | |
/* none */ | |
} | |
/** | |
@c stack | |
@e get nth stack value from stack top | |
@j スタックトップから n 個目をスタックにプッシュする。 | |
*/ | |
DEFINE_INSN | |
topn | |
(rb_num_t n) | |
(...) | |
(VALUE val) // inc += 1; | |
{ | |
val = TOPN(n); | |
} | |
/** | |
@c stack | |
@e set Nth stack entry to stack top | |
@j スタックトップの値を n 個目のスタックにコピー | |
*/ | |
DEFINE_INSN | |
setn | |
(rb_num_t n) | |
(..., VALUE val) | |
(VALUE val) // inc += 0 | |
{ | |
TOPN(n-1) = val; | |
} | |
/** | |
@c stack | |
@e empt current stack | |
@j current stack を空にする。 | |
*/ | |
DEFINE_INSN | |
adjuststack | |
(rb_num_t n) | |
(...) | |
(...) // inc -= n | |
{ | |
DEC_SP(n); | |
} | |
/**********************************************************/ | |
/* deal with setting */ | |
/**********************************************************/ | |
/** | |
@c setting | |
@e defined? | |
@j defined? を行う。 | |
*/ | |
DEFINE_INSN | |
defined | |
(rb_num_t op_type, VALUE obj, VALUE needstr) | |
(VALUE v) | |
(VALUE val) | |
{ | |
VALUE klass; | |
enum defined_type expr_type = 0; | |
enum defined_type type = (enum defined_type)op_type; | |
val = Qnil; | |
switch (type) { | |
case DEFINED_IVAR: | |
if (rb_ivar_defined(GET_SELF(), SYM2ID(obj))) { | |
expr_type = DEFINED_IVAR; | |
} | |
break; | |
case DEFINED_IVAR2: | |
klass = vm_get_cbase(GET_ISEQ(), GET_EP()); | |
break; | |
case DEFINED_GVAR: | |
if (rb_gvar_defined(rb_global_entry(SYM2ID(obj)))) { | |
expr_type = DEFINED_GVAR; | |
} | |
break; | |
case DEFINED_CVAR: { | |
NODE *cref = rb_vm_get_cref(GET_ISEQ(), GET_EP()); | |
klass = vm_get_cvar_base(cref, GET_CFP()); | |
if (rb_cvar_defined(klass, SYM2ID(obj))) { | |
expr_type = DEFINED_CVAR; | |
} | |
break; | |
} | |
case DEFINED_CONST: | |
klass = v; | |
if (vm_get_ev_const(th, GET_ISEQ(), klass, SYM2ID(obj), 1)) { | |
expr_type = DEFINED_CONST; | |
} | |
break; | |
case DEFINED_FUNC: | |
klass = CLASS_OF(v); | |
if (rb_method_boundp(klass, SYM2ID(obj), 0)) { | |
expr_type = DEFINED_METHOD; | |
} | |
break; | |
case DEFINED_METHOD:{ | |
VALUE klass = CLASS_OF(v); | |
const rb_method_entry_t *me = rb_method_entry(klass, SYM2ID(obj), 0); | |
if (me) { | |
if (!(me->flag & NOEX_PRIVATE)) { | |
if (!((me->flag & NOEX_PROTECTED) && | |
!rb_obj_is_kind_of(GET_SELF(), | |
rb_class_real(klass)))) { | |
expr_type = DEFINED_METHOD; | |
} | |
} | |
} | |
{ | |
VALUE args[2]; | |
VALUE r; | |
args[0] = obj; args[1] = Qfalse; | |
r = rb_check_funcall(v, idRespond_to_missing, 2, args); | |
if (r != Qundef && RTEST(r)) | |
expr_type = DEFINED_METHOD; | |
} | |
break; | |
} | |
case DEFINED_YIELD: | |
if (GET_BLOCK_PTR()) { | |
expr_type = DEFINED_YIELD; | |
} | |
break; | |
case DEFINED_ZSUPER:{ | |
rb_call_info_t cit; | |
if (vm_search_superclass(GET_CFP(), GET_ISEQ(), Qnil, &cit) == 0) { | |
VALUE klass = cit.klass; | |
ID id = cit.mid; | |
if (rb_method_boundp(klass, id, 0)) { | |
expr_type = DEFINED_ZSUPER; | |
} | |
} | |
break; | |
} | |
case DEFINED_REF:{ | |
val = vm_getspecial(th, GET_LEP(), Qfalse, FIX2INT(obj)); | |
if (val != Qnil) { | |
expr_type = DEFINED_GVAR; | |
} | |
break; | |
} | |
default: | |
rb_bug("unimplemented defined? type (VM)"); | |
break; | |
} | |
if (expr_type != 0) { | |
if (needstr != Qfalse) { | |
val = rb_iseq_defined_string(expr_type); | |
} | |
else { | |
val = Qtrue; | |
} | |
} | |
} | |
/** | |
@c setting | |
@e check `target' matches `pattern'. | |
`flag & VM_CHECKMATCH_TYPE_MASK' describe how to check pattern. | |
VM_CHECKMATCH_TYPE_WHEN: ignore target and check pattern is truthy. | |
VM_CHECKMATCH_TYPE_CASE: check `patten === target'. | |
VM_CHECKMATCH_TYPE_RESCUE: check `pattern.kind_op?(Module) && pattern == target'. | |
if `flag & VM_CHECKMATCH_ARRAY' is not 0, then `patten' is array of patterns. | |
@j see above comments. | |
*/ | |
DEFINE_INSN | |
checkmatch | |
(rb_num_t flag) | |
(VALUE target, VALUE pattern) | |
(VALUE result) | |
{ | |
enum vm_check_match_type checkmatch_type = | |
(enum vm_check_match_type)(flag & VM_CHECKMATCH_TYPE_MASK); | |
result = Qfalse; | |
if (flag & VM_CHECKMATCH_ARRAY) { | |
int i; | |
for (i = 0; i < RARRAY_LEN(pattern); i++) { | |
if (RTEST(check_match(RARRAY_AREF(pattern, i), target, checkmatch_type))) { | |
result = Qtrue; | |
break; | |
} | |
} | |
} | |
else { | |
if (RTEST(check_match(pattern, target, checkmatch_type))) { | |
result = Qtrue; | |
} | |
} | |
} | |
/** | |
@c setting | |
@e check keywords are specified or not. | |
@j キーワードが指定されているかどうかチェックする | |
*/ | |
DEFINE_INSN | |
checkkeyword | |
(lindex_t kw_bits_index, rb_num_t keyword_index) | |
() | |
(VALUE ret) | |
{ | |
const VALUE *ep = GET_EP(); | |
const VALUE kw_bits = *(ep - kw_bits_index); | |
if (FIXNUM_P(kw_bits)) { | |
int bits = FIX2INT(kw_bits); | |
ret = (bits & (0x01 << keyword_index)) ? Qfalse : Qtrue; | |
} | |
else { | |
assert(RB_TYPE_P(kw_bits, T_HASH)); | |
ret = rb_hash_has_key(kw_bits, INT2FIX(keyword_index)) ? Qfalse : Qtrue; | |
} | |
} | |
/** | |
@c setting | |
@e trace | |
@j trace 用の命令。 | |
*/ | |
DEFINE_INSN | |
trace | |
(rb_num_t nf) | |
() | |
() | |
{ | |
rb_event_flag_t flag = (rb_event_flag_t)nf; | |
if (RUBY_DTRACE_METHOD_ENTRY_ENABLED() || | |
RUBY_DTRACE_METHOD_RETURN_ENABLED() || | |
RUBY_DTRACE_CMETHOD_ENTRY_ENABLED() || | |
RUBY_DTRACE_CMETHOD_RETURN_ENABLED()) { | |
switch(flag) { | |
case RUBY_EVENT_CALL: | |
RUBY_DTRACE_METHOD_ENTRY_HOOK(th, 0, 0); | |
break; | |
case RUBY_EVENT_C_CALL: | |
RUBY_DTRACE_CMETHOD_ENTRY_HOOK(th, 0, 0); | |
break; | |
case RUBY_EVENT_RETURN: | |
RUBY_DTRACE_METHOD_RETURN_HOOK(th, 0, 0); | |
break; | |
case RUBY_EVENT_C_RETURN: | |
RUBY_DTRACE_CMETHOD_RETURN_HOOK(th, 0, 0); | |
break; | |
} | |
} | |
EXEC_EVENT_HOOK(th, flag, GET_SELF(), 0, 0 /* id and klass are resolved at callee */, | |
(flag & (RUBY_EVENT_RETURN | RUBY_EVENT_B_RETURN)) ? TOPN(0) : Qundef); | |
} | |
/**********************************************************/ | |
/* deal with control flow 1: class/module */ | |
/**********************************************************/ | |
/** | |
@c class/module | |
@e | |
enter class definition scope. if super is Qfalse, and class | |
"klass" is defined, it's redefine. otherwise, define "klass" class. | |
@j クラス定義スコープへ移行する。 | |
もし super が Qfalse で klassクラスが定義されていれば再定義である。 | |
そうでなければ、klass クラスを定義する。 | |
*/ | |
DEFINE_INSN | |
defineclass | |
(ID id, ISEQ class_iseq, rb_num_t flags) | |
(VALUE cbase, VALUE super) | |
(VALUE val) | |
{ | |
VALUE klass; | |
rb_vm_defineclass_type_t type = VM_DEFINECLASS_TYPE(flags); | |
switch (type) { | |
case VM_DEFINECLASS_TYPE_CLASS: | |
/* val is dummy. classdef returns class scope value */ | |
if (VM_DEFINECLASS_HAS_SUPERCLASS_P(flags) && | |
!RB_TYPE_P(super, T_CLASS)) { | |
rb_raise(rb_eTypeError, "superclass must be a Class (%s given)", | |
rb_obj_classname(super)); | |
} | |
if (super == Qnil) { | |
super = rb_cObject; | |
} | |
vm_check_if_namespace(cbase); | |
/* find klass */ | |
rb_autoload_load(cbase, id); | |
if ((klass = vm_search_const_defined_class(cbase, id)) != 0) { | |
/* already exist */ | |
klass = VM_DEFINECLASS_SCOPED_P(flags) ? | |
rb_public_const_get_at(klass, id) : rb_const_get_at(klass, id); | |
if (!RB_TYPE_P(klass, T_CLASS)) { | |
rb_raise(rb_eTypeError, "%s is not a class", rb_id2name(id)); | |
} | |
if (super != rb_cObject) { | |
VALUE tmp; | |
tmp = rb_class_real(RCLASS_SUPER(klass)); | |
if (tmp != super) { | |
rb_raise(rb_eTypeError, "superclass mismatch for class %s", | |
rb_id2name(id)); | |
} | |
} | |
} | |
else { | |
/* new class declaration */ | |
klass = rb_define_class_id(id, super); | |
rb_set_class_path_string(klass, cbase, rb_id2str(id)); | |
rb_const_set(cbase, id, klass); | |
rb_class_inherited(super, klass); | |
} | |
break; | |
case VM_DEFINECLASS_TYPE_SINGLETON_CLASS: | |
/* val is dummy. classdef returns class scope value */ | |
/* super is dummy */ | |
klass = rb_singleton_class(cbase); | |
break; | |
case VM_DEFINECLASS_TYPE_MODULE: | |
/* val is dummy. classdef returns class scope value */ | |
/* super is dummy */ | |
vm_check_if_namespace(cbase); | |
/* find klass */ | |
if ((klass = vm_search_const_defined_class(cbase, id)) != 0) { | |
klass = VM_DEFINECLASS_SCOPED_P(flags) ? | |
rb_public_const_get_at(klass, id) : rb_const_get_at(klass, id); | |
/* already exist */ | |
if (!RB_TYPE_P(klass, T_MODULE)) { | |
rb_raise(rb_eTypeError, "%s is not a module", rb_id2name(id)); | |
} | |
} | |
else { | |
/* new module declaration */ | |
klass = rb_define_module_id(id); | |
rb_set_class_path_string(klass, cbase, rb_id2str(id)); | |
rb_const_set(cbase, id, klass); | |
} | |
break; | |
default: | |
rb_bug("unknown defineclass type: %d", (int)type); | |
} | |
COPY_CREF(class_iseq->cref_stack, vm_cref_push(th, klass, NOEX_PUBLIC, NULL)); | |
/* enter scope */ | |
vm_push_frame(th, class_iseq, VM_FRAME_MAGIC_CLASS, | |
klass, 0, VM_ENVVAL_BLOCK_PTR(GET_BLOCK_PTR()), | |
class_iseq->iseq_encoded, GET_SP(), | |
class_iseq->local_size, 0, class_iseq->stack_max); | |
RESTORE_REGS(); | |
NEXT_INSN(); | |
} | |
/**********************************************************/ | |
/* deal with control flow 2: method/iterator */ | |
/**********************************************************/ | |
/** | |
@c method/iterator | |
@e invoke method. | |
@j メソッド呼び出しを行う。ci に必要な情報が格納されている。 | |
*/ | |
DEFINE_INSN | |
send | |
(CALL_INFO ci) | |
(...) | |
(VALUE val) // inc += - (int)(ci->orig_argc + ((ci->flag & VM_CALL_ARGS_BLOCKARG) ? 1 : 0)); | |
{ | |
ci->argc = ci->orig_argc; | |
vm_caller_setup_arg_block(th, reg_cfp, ci, FALSE); | |
vm_search_method(ci, ci->recv = TOPN(ci->argc)); | |
CALL_METHOD(ci); | |
} | |
DEFINE_INSN | |
opt_str_freeze | |
(VALUE str) | |
() | |
(VALUE val) | |
{ | |
if (BASIC_OP_UNREDEFINED_P(BOP_FREEZE, STRING_REDEFINED_OP_FLAG)) { | |
val = str; | |
} | |
else { | |
val = rb_funcall(rb_str_resurrect(str), idFreeze, 0); | |
} | |
} | |
/** | |
@c optimize | |
@e Invoke method without block | |
@j Invoke method without block | |
*/ | |
DEFINE_INSN | |
opt_send_without_block | |
(CALL_INFO ci) | |
(...) | |
(VALUE val) // inc += -ci->orig_argc; | |
{ | |
ci->argc = ci->orig_argc; | |
vm_search_method(ci, ci->recv = TOPN(ci->argc)); | |
CALL_METHOD(ci); | |
} | |
/** | |
@c method/iterator | |
@e super(args) # args.size => num | |
@j super を実行する。ci に必要な情報が格納されている。 | |
*/ | |
DEFINE_INSN | |
invokesuper | |
(CALL_INFO ci) | |
(...) | |
(VALUE val) // inc += - (int)(ci->orig_argc + ((ci->flag & VM_CALL_ARGS_BLOCKARG) ? 1 : 0)); | |
{ | |
ci->argc = ci->orig_argc; | |
vm_caller_setup_arg_block(th, reg_cfp, ci, TRUE); | |
ci->recv = GET_SELF(); | |
vm_search_super_method(th, GET_CFP(), ci); | |
CALL_METHOD(ci); | |
} | |
/** | |
@c method/iterator | |
@e yield(args) | |
@j yield を実行する。 | |
*/ | |
DEFINE_INSN | |
invokeblock | |
(CALL_INFO ci) | |
(...) | |
(VALUE val) // inc += 1 - ci->orig_argc; | |
{ | |
ci->argc = ci->orig_argc; | |
ci->blockptr = 0; | |
ci->recv = GET_SELF(); | |
val = vm_invoke_block(th, GET_CFP(), ci); | |
if (val == Qundef) { | |
RESTORE_REGS(); | |
NEXT_INSN(); | |
} | |
} | |
/** | |
@c method/iterator | |
@e return from this scope. | |
@j このスコープから抜ける。 | |
*/ | |
DEFINE_INSN | |
leave | |
() | |
(VALUE val) | |
(VALUE val) | |
{ | |
if (OPT_CHECKED_RUN) { | |
if (reg_cfp->sp != vm_base_ptr(reg_cfp)) { | |
rb_bug("Stack consistency error (sp: %"PRIdPTRDIFF", bp: %"PRIdPTRDIFF")", | |
VM_SP_CNT(th, reg_cfp->sp), VM_SP_CNT(th, vm_base_ptr(reg_cfp))); | |
} | |
} | |
RUBY_VM_CHECK_INTS(th); | |
if (UNLIKELY(VM_FRAME_TYPE_FINISH_P(GET_CFP()))) { | |
vm_pop_frame(th); | |
#if OPT_CALL_THREADED_CODE | |
th->retval = val; | |
return 0; | |
#else | |
return val; | |
#endif | |
} | |
else { | |
vm_pop_frame(th); | |
RESTORE_REGS(); | |
} | |
} | |
/**********************************************************/ | |
/* deal with control flow 3: exception */ | |
/**********************************************************/ | |
/** | |
@c exception | |
@e longjump | |
@j 大域ジャンプを行う。 | |
*/ | |
DEFINE_INSN | |
throw | |
(rb_num_t throw_state) | |
(VALUE throwobj) | |
(VALUE val) | |
{ | |
RUBY_VM_CHECK_INTS(th); | |
val = vm_throw(th, GET_CFP(), throw_state, throwobj); | |
THROW_EXCEPTION(val); | |
/* unreachable */ | |
} | |
/**********************************************************/ | |
/* deal with control flow 4: local jump */ | |
/**********************************************************/ | |
/** | |
@c jump | |
@e set PC to (PC + dst). | |
@j PC を (PC + dst) にする。 | |
*/ | |
DEFINE_INSN | |
jump | |
(OFFSET dst) | |
() | |
() | |
{ | |
RUBY_VM_CHECK_INTS(th); | |
JUMP(dst); | |
} | |
/** | |
@c jump | |
@e if val is not false or nil, set PC to (PC + dst). | |
@j もし val が false か nil でなければ、PC を (PC + dst) にする。 | |
*/ | |
DEFINE_INSN | |
branchif | |
(OFFSET dst) | |
(VALUE val) | |
() | |
{ | |
if (RTEST(val)) { | |
RUBY_VM_CHECK_INTS(th); | |
JUMP(dst); | |
} | |
} | |
/** | |
@c jump | |
@e if val is false or nil, set PC to (PC + dst). | |
@j もし val が false か nil ならば、PC を (PC + dst) にする。 | |
*/ | |
DEFINE_INSN | |
branchunless | |
(OFFSET dst) | |
(VALUE val) | |
() | |
{ | |
if (!RTEST(val)) { | |
RUBY_VM_CHECK_INTS(th); | |
JUMP(dst); | |
} | |
} | |
/**********************************************************/ | |
/* for optimize */ | |
/**********************************************************/ | |
/** | |
@c optimize | |
@e push inline-cached value and go to dst if it is valid | |
@j インラインキャッシュが有効なら、値をスタックにプッシュして dst へジャンプする。 | |
*/ | |
DEFINE_INSN | |
getinlinecache | |
(OFFSET dst, IC ic) | |
() | |
(VALUE val) | |
{ | |
if (ic->ic_serial == GET_GLOBAL_CONSTANT_STATE()) { | |
val = ic->ic_value.value; | |
JUMP(dst); | |
} | |
else { | |
/* none */ | |
val = Qnil; | |
} | |
} | |
/** | |
@c optimize | |
@e set inline cache | |
@j インラインキャッシュの値を設定する。 | |
*/ | |
DEFINE_INSN | |
setinlinecache | |
(IC ic) | |
(VALUE val) | |
(VALUE val) | |
{ | |
if (ic->ic_value.value == Qundef) { | |
rb_iseq_add_mark_object(GET_ISEQ(), val); | |
} | |
ic->ic_value.value = val; | |
ic->ic_serial = GET_GLOBAL_CONSTANT_STATE() - ruby_vm_const_missing_count; | |
ruby_vm_const_missing_count = 0; | |
} | |
/** | |
@c optimize | |
@e run iseq only once | |
@j once を実現する。 | |
*/ | |
DEFINE_INSN | |
once | |
(ISEQ iseq, IC ic) | |
() | |
(VALUE val) | |
{ | |
union iseq_inline_storage_entry *is = (union iseq_inline_storage_entry *)ic; | |
#define RUNNING_THREAD_ONCE_DONE ((rb_thread_t *)(0x1)) | |
retry: | |
if (is->once.running_thread == RUNNING_THREAD_ONCE_DONE) { | |
val = is->once.value; | |
} | |
else if (is->once.running_thread == NULL) { | |
is->once.running_thread = th; | |
val = is->once.value = rb_ensure(vm_once_exec, (VALUE)iseq, vm_once_clear, (VALUE)is); | |
/* is->once.running_thread is cleared by vm_once_clear() */ | |
is->once.running_thread = RUNNING_THREAD_ONCE_DONE; /* success */ | |
rb_iseq_add_mark_object(GET_ISEQ(), val); | |
} | |
else if (is->once.running_thread == th) { | |
/* recursive once */ | |
val = vm_once_exec((VALUE)iseq); | |
} | |
else { | |
/* waiting for finish */ | |
RUBY_VM_CHECK_INTS(th); | |
rb_thread_schedule(); | |
goto retry; | |
} | |
} | |
/** | |
@c optimize | |
@e case dispatcher, jump by table if possible | |
@j case 文で、可能なら表引きでジャンプする。 | |
*/ | |
DEFINE_INSN | |
opt_case_dispatch | |
(CDHASH hash, OFFSET else_offset) | |
(..., VALUE key) | |
() // inc += -1; | |
{ | |
switch(TYPE(key)) { | |
case T_FLOAT: { | |
double ival; | |
if (modf(RFLOAT_VALUE(key), &ival) == 0.0) { | |
key = FIXABLE(ival) ? LONG2FIX((long)ival) : rb_dbl2big(ival); | |
} | |
} | |
case T_SYMBOL: /* fall through */ | |
case T_FIXNUM: | |
case T_BIGNUM: | |
case T_STRING: | |
if (BASIC_OP_UNREDEFINED_P(BOP_EQQ, | |
SYMBOL_REDEFINED_OP_FLAG | | |
FIXNUM_REDEFINED_OP_FLAG | | |
BIGNUM_REDEFINED_OP_FLAG | | |
STRING_REDEFINED_OP_FLAG)) { | |
st_data_t val; | |
if (st_lookup(RHASH_TBL_RAW(hash), key, &val)) { | |
JUMP(FIX2INT((VALUE)val)); | |
} | |
else { | |
JUMP(else_offset); | |
} | |
break; | |
} | |
default: | |
break; | |
} | |
} | |
/** simple functions */ | |
/** | |
@c optimize | |
@e optimized X+Y. | |
@j 最適化された X+Y。 | |
*/ | |
DEFINE_INSN | |
opt_plus | |
(CALL_INFO ci) | |
(VALUE recv, VALUE obj) | |
(VALUE val) | |
{ | |
if (FIXNUM_2_P(recv, obj) && | |
BASIC_OP_UNREDEFINED_P(BOP_PLUS,FIXNUM_REDEFINED_OP_FLAG)) { | |
/* fixnum + fixnum */ | |
#ifndef LONG_LONG_VALUE | |
val = (recv + (obj & (~1))); | |
if ((~(recv ^ obj) & (recv ^ val)) & | |
((VALUE)0x01 << ((sizeof(VALUE) * CHAR_BIT) - 1))) { | |
val = rb_big_plus(rb_int2big(FIX2LONG(recv)), | |
rb_int2big(FIX2LONG(obj))); | |
} | |
#else | |
long a, b, c; | |
a = FIX2LONG(recv); | |
b = FIX2LONG(obj); | |
c = a + b; | |
if (FIXABLE(c)) { | |
val = LONG2FIX(c); | |
} | |
else { | |
val = rb_big_plus(rb_int2big(a), rb_int2big(b)); | |
} | |
#endif | |
} | |
else if (FLONUM_2_P(recv, obj) && | |
BASIC_OP_UNREDEFINED_P(BOP_PLUS, FLOAT_REDEFINED_OP_FLAG)) { | |
val = DBL2NUM(RFLOAT_VALUE(recv) + RFLOAT_VALUE(obj)); | |
} | |
else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) { | |
if (RBASIC_CLASS(recv) == rb_cFloat && RBASIC_CLASS(obj) == rb_cFloat && | |
BASIC_OP_UNREDEFINED_P(BOP_PLUS, FLOAT_REDEFINED_OP_FLAG)) { | |
val = DBL2NUM(RFLOAT_VALUE(recv) + RFLOAT_VALUE(obj)); | |
} | |
else if (RBASIC_CLASS(recv) == rb_cString && RBASIC_CLASS(obj) == rb_cString && | |
BASIC_OP_UNREDEFINED_P(BOP_PLUS, STRING_REDEFINED_OP_FLAG)) { | |
val = rb_str_plus(recv, obj); | |
} | |
else if (RBASIC_CLASS(recv) == rb_cArray && | |
BASIC_OP_UNREDEFINED_P(BOP_PLUS, ARRAY_REDEFINED_OP_FLAG)) { | |
val = rb_ary_plus(recv, obj); | |
} | |
else { | |
goto INSN_LABEL(normal_dispatch); | |
} | |
} | |
else { | |
INSN_LABEL(normal_dispatch): | |
PUSH(recv); | |
PUSH(obj); | |
CALL_SIMPLE_METHOD(recv); | |
} | |
} | |
/** | |
@c optimize | |
@e optimized X-Y. | |
@j 最適化された X-Y。 | |
*/ | |
DEFINE_INSN | |
opt_minus | |
(CALL_INFO ci) | |
(VALUE recv, VALUE obj) | |
(VALUE val) | |
{ | |
if (FIXNUM_2_P(recv, obj) && | |
BASIC_OP_UNREDEFINED_P(BOP_MINUS, FIXNUM_REDEFINED_OP_FLAG)) { | |
long a, b, c; | |
a = FIX2LONG(recv); | |
b = FIX2LONG(obj); | |
c = a - b; | |
if (FIXABLE(c)) { | |
val = LONG2FIX(c); | |
} | |
else { | |
val = rb_big_minus(rb_int2big(a), rb_int2big(b)); | |
} | |
} | |
else if (FLONUM_2_P(recv, obj) && | |
BASIC_OP_UNREDEFINED_P(BOP_MINUS, FLOAT_REDEFINED_OP_FLAG)) { | |
val = DBL2NUM(RFLOAT_VALUE(recv) - RFLOAT_VALUE(obj)); | |
} | |
else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) { | |
if (RBASIC_CLASS(recv) == rb_cFloat && RBASIC_CLASS(obj) == rb_cFloat && | |
BASIC_OP_UNREDEFINED_P(BOP_MINUS, FLOAT_REDEFINED_OP_FLAG)) { | |
val = DBL2NUM(RFLOAT_VALUE(recv) - RFLOAT_VALUE(obj)); | |
} | |
else { | |
goto INSN_LABEL(normal_dispatch); | |
} | |
} | |
else { | |
/* other */ | |
INSN_LABEL(normal_dispatch): | |
PUSH(recv); | |
PUSH(obj); | |
CALL_SIMPLE_METHOD(recv); | |
} | |
} | |
/** | |
@c optimize | |
@e optimized X*Y. | |
@j 最適化された X*Y。 | |
*/ | |
DEFINE_INSN | |
opt_mult | |
(CALL_INFO ci) | |
(VALUE recv, VALUE obj) | |
(VALUE val) | |
{ | |
if (FIXNUM_2_P(recv, obj) && | |
BASIC_OP_UNREDEFINED_P(BOP_MULT, FIXNUM_REDEFINED_OP_FLAG)) { | |
long a, b; | |
a = FIX2LONG(recv); | |
if (a == 0) { | |
val = recv; | |
} | |
else { | |
b = FIX2LONG(obj); | |
if (MUL_OVERFLOW_FIXNUM_P(a, b)) { | |
val = rb_big_mul(rb_int2big(a), rb_int2big(b)); | |
} | |
else { | |
val = LONG2FIX(a * b); | |
} | |
} | |
} | |
else if (FLONUM_2_P(recv, obj) && | |
BASIC_OP_UNREDEFINED_P(BOP_MULT, FLOAT_REDEFINED_OP_FLAG)) { | |
val = DBL2NUM(RFLOAT_VALUE(recv) * RFLOAT_VALUE(obj)); | |
} | |
else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) { | |
if (RBASIC_CLASS(recv) == rb_cFloat && RBASIC_CLASS(obj) == rb_cFloat && | |
BASIC_OP_UNREDEFINED_P(BOP_MULT, FLOAT_REDEFINED_OP_FLAG)) { | |
val = DBL2NUM(RFLOAT_VALUE(recv) * RFLOAT_VALUE(obj)); | |
} | |
else { | |
goto INSN_LABEL(normal_dispatch); | |
} | |
} | |
else { | |
INSN_LABEL(normal_dispatch): | |
PUSH(recv); | |
PUSH(obj); | |
CALL_SIMPLE_METHOD(recv); | |
} | |
} | |
/** | |
@c optimize | |
@e optimized X/Y. | |
@j 最適化された X/Y。 | |
*/ | |
DEFINE_INSN | |
opt_div | |
(CALL_INFO ci) | |
(VALUE recv, VALUE obj) | |
(VALUE val) | |
{ | |
if (FIXNUM_2_P(recv, obj) && | |
BASIC_OP_UNREDEFINED_P(BOP_DIV, FIXNUM_REDEFINED_OP_FLAG)) { | |
long x, y, div; | |
x = FIX2LONG(recv); | |
y = FIX2LONG(obj); | |
{ | |
/* copied from numeric.c#fixdivmod */ | |
long mod; | |
if (y == 0) | |
goto INSN_LABEL(normal_dispatch); | |
if (y < 0) { | |
if (x < 0) | |
div = -x / -y; | |
else | |
div = -(x / -y); | |
} | |
else { | |
if (x < 0) | |
div = -(-x / y); | |
else | |
div = x / y; | |
} | |
mod = x - div * y; | |
if ((mod < 0 && y > 0) || (mod > 0 && y < 0)) { | |
mod += y; | |
div -= 1; | |
} | |
} | |
val = LONG2NUM(div); | |
} | |
else if (FLONUM_2_P(recv, obj) && | |
BASIC_OP_UNREDEFINED_P(BOP_DIV, FLOAT_REDEFINED_OP_FLAG)) { | |
val = DBL2NUM(RFLOAT_VALUE(recv) / RFLOAT_VALUE(obj)); | |
} | |
else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) { | |
if (RBASIC_CLASS(recv) == rb_cFloat && RBASIC_CLASS(obj) == rb_cFloat && | |
BASIC_OP_UNREDEFINED_P(BOP_DIV, FLOAT_REDEFINED_OP_FLAG)) { | |
val = DBL2NUM(RFLOAT_VALUE(recv) / RFLOAT_VALUE(obj)); | |
} | |
else { | |
goto INSN_LABEL(normal_dispatch); | |
} | |
} | |
else { | |
INSN_LABEL(normal_dispatch): | |
PUSH(recv); | |
PUSH(obj); | |
CALL_SIMPLE_METHOD(recv); | |
} | |
} | |
/** | |
@c optimize | |
@e optimized X%Y. | |
@j 最適化された X%Y。 | |
*/ | |
DEFINE_INSN | |
opt_mod | |
(CALL_INFO ci) | |
(VALUE recv, VALUE obj) | |
(VALUE val) | |
{ | |
if (FIXNUM_2_P(recv, obj) && | |
BASIC_OP_UNREDEFINED_P(BOP_MOD, FIXNUM_REDEFINED_OP_FLAG )) { | |
long x, y; | |
x = FIX2LONG(recv); | |
y = FIX2LONG(obj); | |
if (x > 0 && y > 0) { | |
val = LONG2FIX(x % y); | |
} | |
else { | |
/* copied from numeric.c#fixdivmod */ | |
long div, mod; | |
if (y == 0) | |
rb_num_zerodiv(); | |
if (y < 0) { | |
if (x < 0) | |
div = -x / -y; | |
else | |
div = -(x / -y); | |
} | |
else { | |
if (x < 0) | |
div = -(-x / y); | |
else | |
div = x / y; | |
} | |
mod = x - div * y; | |
if ((mod < 0 && y > 0) || (mod > 0 && y < 0)) { | |
mod += y; | |
div -= 1; | |
} | |
val = LONG2FIX(mod); | |
} | |
} | |
else if (FLONUM_2_P(recv, obj) && | |
BASIC_OP_UNREDEFINED_P(BOP_MOD, FLOAT_REDEFINED_OP_FLAG)) { | |
val = DBL2NUM(ruby_float_mod(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj))); | |
} | |
else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) { | |
if (RBASIC_CLASS(recv) == rb_cFloat && RBASIC_CLASS(obj) == rb_cFloat && | |
BASIC_OP_UNREDEFINED_P(BOP_MOD, FLOAT_REDEFINED_OP_FLAG)) { | |
val = DBL2NUM(ruby_float_mod(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj))); | |
} | |
else { | |
goto INSN_LABEL(normal_dispatch); | |
} | |
} | |
else { | |
INSN_LABEL(normal_dispatch): | |
PUSH(recv); | |
PUSH(obj); | |
CALL_SIMPLE_METHOD(recv); | |
} | |
} | |
/** | |
@c optimize | |
@e optimized X==Y. | |
@j 最適化された X==Y。 | |
*/ | |
DEFINE_INSN | |
opt_eq | |
(CALL_INFO ci) | |
(VALUE recv, VALUE obj) | |
(VALUE val) | |
{ | |
val = opt_eq_func(recv, obj, ci); | |
if (val == Qundef) { | |
/* other */ | |
PUSH(recv); | |
PUSH(obj); | |
CALL_SIMPLE_METHOD(recv); | |
} | |
} | |
/** | |
@c optimize | |
@e optimized X!=Y. | |
@j 最適化された X!=Y。 | |
*/ | |
DEFINE_INSN | |
opt_neq | |
(CALL_INFO ci, CALL_INFO ci_eq) | |
(VALUE recv, VALUE obj) | |
(VALUE val) | |
{ | |
extern VALUE rb_obj_not_equal(VALUE obj1, VALUE obj2); | |
vm_search_method(ci, recv); | |
val = Qundef; | |
if (check_cfunc(ci->me, rb_obj_not_equal)) { | |
val = opt_eq_func(recv, obj, ci_eq); | |
if (val != Qundef) { | |
val = RTEST(val) ? Qfalse : Qtrue; | |
} | |
} | |
if (val == Qundef) { | |
/* other */ | |
PUSH(recv); | |
PUSH(obj); | |
CALL_SIMPLE_METHOD(recv); | |
} | |
} | |
/** | |
@c optimize | |
@e optimized X<Y. | |
@j 最適化された X<Y。 | |
*/ | |
DEFINE_INSN | |
opt_lt | |
(CALL_INFO ci) | |
(VALUE recv, VALUE obj) | |
(VALUE val) | |
{ | |
if (FIXNUM_2_P(recv, obj) && | |
BASIC_OP_UNREDEFINED_P(BOP_LT, FIXNUM_REDEFINED_OP_FLAG)) { | |
SIGNED_VALUE a = recv, b = obj; | |
if (a < b) { | |
val = Qtrue; | |
} | |
else { | |
val = Qfalse; | |
} | |
} | |
else if (FLONUM_2_P(recv, obj) && | |
BASIC_OP_UNREDEFINED_P(BOP_LT, FLOAT_REDEFINED_OP_FLAG)) { | |
/* flonum is not NaN */ | |
val = RFLOAT_VALUE(recv) < RFLOAT_VALUE(obj) ? Qtrue : Qfalse; | |
} | |
else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) { | |
if (RBASIC_CLASS(recv) == rb_cFloat && RBASIC_CLASS(obj) == rb_cFloat && | |
BASIC_OP_UNREDEFINED_P(BOP_LT, FLOAT_REDEFINED_OP_FLAG)) { | |
val = double_cmp_lt(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj)); | |
} | |
else { | |
goto INSN_LABEL(normal_dispatch); | |
} | |
} | |
else { | |
INSN_LABEL(normal_dispatch): | |
PUSH(recv); | |
PUSH(obj); | |
CALL_SIMPLE_METHOD(recv); | |
} | |
} | |
/** | |
@c optimize | |
@e optimized X<=Y. | |
@j 最適化された X<=Y。 | |
*/ | |
DEFINE_INSN | |
opt_le | |
(CALL_INFO ci) | |
(VALUE recv, VALUE obj) | |
(VALUE val) | |
{ | |
if (FIXNUM_2_P(recv, obj) && | |
BASIC_OP_UNREDEFINED_P(BOP_LE, FIXNUM_REDEFINED_OP_FLAG)) { | |
SIGNED_VALUE a = recv, b = obj; | |
if (a <= b) { | |
val = Qtrue; | |
} | |
else { | |
val = Qfalse; | |
} | |
} | |
else if (FLONUM_2_P(recv, obj) && | |
BASIC_OP_UNREDEFINED_P(BOP_LE, FLOAT_REDEFINED_OP_FLAG)) { | |
/* flonum is not NaN */ | |
val = RFLOAT_VALUE(recv) <= RFLOAT_VALUE(obj) ? Qtrue : Qfalse; | |
} | |
else { | |
/* other */ | |
PUSH(recv); | |
PUSH(obj); | |
CALL_SIMPLE_METHOD(recv); | |
} | |
} | |
/** | |
@c optimize | |
@e optimized X>Y. | |
@j 最適化された X>Y。 | |
*/ | |
DEFINE_INSN | |
opt_gt | |
(CALL_INFO ci) | |
(VALUE recv, VALUE obj) | |
(VALUE val) | |
{ | |
if (FIXNUM_2_P(recv, obj) && | |
BASIC_OP_UNREDEFINED_P(BOP_GT, FIXNUM_REDEFINED_OP_FLAG)) { | |
SIGNED_VALUE a = recv, b = obj; | |
if (a > b) { | |
val = Qtrue; | |
} | |
else { | |
val = Qfalse; | |
} | |
} | |
else if (FLONUM_2_P(recv, obj) && | |
BASIC_OP_UNREDEFINED_P(BOP_GT, FLOAT_REDEFINED_OP_FLAG)) { | |
/* flonum is not NaN */ | |
val = RFLOAT_VALUE(recv) > RFLOAT_VALUE(obj) ? Qtrue : Qfalse; | |
} | |
else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) { | |
if (RBASIC_CLASS(recv) == rb_cFloat && RBASIC_CLASS(obj) == rb_cFloat && | |
BASIC_OP_UNREDEFINED_P(BOP_GT, FLOAT_REDEFINED_OP_FLAG)) { | |
val = double_cmp_gt(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj)); | |
} | |
else { | |
goto INSN_LABEL(normal_dispatch); | |
} | |
} | |
else { | |
INSN_LABEL(normal_dispatch): | |
PUSH(recv); | |
PUSH(obj); | |
CALL_SIMPLE_METHOD(recv); | |
} | |
} | |
/** | |
@c optimize | |
@e optimized X>=Y. | |
@j 最適化された X>=Y。 | |
*/ | |
DEFINE_INSN | |
opt_ge | |
(CALL_INFO ci) | |
(VALUE recv, VALUE obj) | |
(VALUE val) | |
{ | |
if (FIXNUM_2_P(recv, obj) && | |
BASIC_OP_UNREDEFINED_P(BOP_GE, FIXNUM_REDEFINED_OP_FLAG)) { | |
SIGNED_VALUE a = recv, b = obj; | |
if (a >= b) { | |
val = Qtrue; | |
} | |
else { | |
val = Qfalse; | |
} | |
} | |
else if (FLONUM_2_P(recv, obj) && | |
BASIC_OP_UNREDEFINED_P(BOP_GE, FLOAT_REDEFINED_OP_FLAG)) { | |
/* flonum is not NaN */ | |
val = RFLOAT_VALUE(recv) >= RFLOAT_VALUE(obj) ? Qtrue : Qfalse; | |
} | |
else { | |
PUSH(recv); | |
PUSH(obj); | |
CALL_SIMPLE_METHOD(recv); | |
} | |
} | |
/** | |
@c optimize | |
@e << | |
@j 最適化された X<<Y。 | |
*/ | |
DEFINE_INSN | |
opt_ltlt | |
(CALL_INFO ci) | |
(VALUE recv, VALUE obj) | |
(VALUE val) | |
{ | |
if (!SPECIAL_CONST_P(recv)) { | |
if (RBASIC_CLASS(recv) == rb_cString && | |
BASIC_OP_UNREDEFINED_P(BOP_LTLT, STRING_REDEFINED_OP_FLAG)) { | |
val = rb_str_concat(recv, obj); | |
} | |
else if (RBASIC_CLASS(recv) == rb_cArray && | |
BASIC_OP_UNREDEFINED_P(BOP_LTLT, ARRAY_REDEFINED_OP_FLAG)) { | |
val = rb_ary_push(recv, obj); | |
} | |
else { | |
goto INSN_LABEL(normal_dispatch); | |
} | |
} | |
else { | |
INSN_LABEL(normal_dispatch): | |
PUSH(recv); | |
PUSH(obj); | |
CALL_SIMPLE_METHOD(recv); | |
} | |
} | |
/** | |
@c optimize | |
@e [] | |
@j 最適化された recv[obj]。 | |
*/ | |
DEFINE_INSN | |
opt_aref | |
(CALL_INFO ci) | |
(VALUE recv, VALUE obj) | |
(VALUE val) | |
{ | |
if (!SPECIAL_CONST_P(recv)) { | |
if (RBASIC_CLASS(recv) == rb_cArray && BASIC_OP_UNREDEFINED_P(BOP_AREF, ARRAY_REDEFINED_OP_FLAG) && FIXNUM_P(obj)) { | |
val = rb_ary_entry(recv, FIX2LONG(obj)); | |
} | |
else if (RBASIC_CLASS(recv) == rb_cHash && BASIC_OP_UNREDEFINED_P(BOP_AREF, HASH_REDEFINED_OP_FLAG)) { | |
val = rb_hash_aref(recv, obj); | |
} | |
else { | |
goto INSN_LABEL(normal_dispatch); | |
} | |
} | |
else { | |
INSN_LABEL(normal_dispatch): | |
PUSH(recv); | |
PUSH(obj); | |
CALL_SIMPLE_METHOD(recv); | |
} | |
} | |
/** | |
@c optimize | |
@e recv[obj] = set | |
@j 最適化された recv[obj] = set。 | |
*/ | |
DEFINE_INSN | |
opt_aset | |
(CALL_INFO ci) | |
(VALUE recv, VALUE obj, VALUE set) | |
(VALUE val) | |
{ | |
if (!SPECIAL_CONST_P(recv)) { | |
if (RBASIC_CLASS(recv) == rb_cArray && BASIC_OP_UNREDEFINED_P(BOP_ASET, ARRAY_REDEFINED_OP_FLAG) && FIXNUM_P(obj)) { | |
rb_ary_store(recv, FIX2LONG(obj), set); | |
val = set; | |
} | |
else if (RBASIC_CLASS(recv) == rb_cHash && BASIC_OP_UNREDEFINED_P(BOP_ASET, HASH_REDEFINED_OP_FLAG)) { | |
rb_hash_aset(recv, obj, set); | |
val = set; | |
} | |
else { | |
goto INSN_LABEL(normal_dispatch); | |
} | |
} | |
else { | |
INSN_LABEL(normal_dispatch): | |
PUSH(recv); | |
PUSH(obj); | |
PUSH(set); | |
CALL_SIMPLE_METHOD(recv); | |
} | |
} | |
/** | |
@c optimize | |
@e recv[str] = set | |
@j 最適化された recv[str] = set。 | |
*/ | |
DEFINE_INSN | |
opt_aset_with | |
(CALL_INFO ci, VALUE key) | |
(VALUE recv, VALUE val) | |
(VALUE val) | |
{ | |
if (!SPECIAL_CONST_P(recv) && RBASIC_CLASS(recv) == rb_cHash && BASIC_OP_UNREDEFINED_P(BOP_ASET, HASH_REDEFINED_OP_FLAG)) { | |
rb_hash_aset(recv, key, val); | |
} | |
else { | |
PUSH(recv); | |
PUSH(rb_str_resurrect(key)); | |
PUSH(val); | |
CALL_SIMPLE_METHOD(recv); | |
} | |
} | |
/** | |
@c optimize | |
@e recv[str] | |
@j 最適化された recv[str]。 | |
*/ | |
DEFINE_INSN | |
opt_aref_with | |
(CALL_INFO ci, VALUE key) | |
(VALUE recv) | |
(VALUE val) | |
{ | |
if (!SPECIAL_CONST_P(recv) && RBASIC_CLASS(recv) == rb_cHash && BASIC_OP_UNREDEFINED_P(BOP_AREF, HASH_REDEFINED_OP_FLAG)) { | |
val = rb_hash_aref(recv, key); | |
} | |
else { | |
PUSH(recv); | |
PUSH(rb_str_resurrect(key)); | |
CALL_SIMPLE_METHOD(recv); | |
} | |
} | |
/** | |
@c optimize | |
@e optimized length | |
@j 最適化された recv.length()。 | |
*/ | |
DEFINE_INSN | |
opt_length | |
(CALL_INFO ci) | |
(VALUE recv) | |
(VALUE val) | |
{ | |
if (!SPECIAL_CONST_P(recv)) { | |
if (RBASIC_CLASS(recv) == rb_cString && | |
BASIC_OP_UNREDEFINED_P(BOP_LENGTH, STRING_REDEFINED_OP_FLAG)) { | |
val = rb_str_length(recv); | |
} | |
else if (RBASIC_CLASS(recv) == rb_cArray && | |
BASIC_OP_UNREDEFINED_P(BOP_LENGTH, ARRAY_REDEFINED_OP_FLAG)) { | |
val = LONG2NUM(RARRAY_LEN(recv)); | |
} | |
else if (RBASIC_CLASS(recv) == rb_cHash && | |
BASIC_OP_UNREDEFINED_P(BOP_LENGTH, HASH_REDEFINED_OP_FLAG)) { | |
val = INT2FIX(RHASH_SIZE(recv)); | |
} | |
else { | |
goto INSN_LABEL(normal_dispatch); | |
} | |
} | |
else { | |
INSN_LABEL(normal_dispatch): | |
PUSH(recv); | |
CALL_SIMPLE_METHOD(recv); | |
} | |
} | |
/** | |
@c optimize | |
@e optimized size | |
@j 最適化された recv.size()。 | |
*/ | |
DEFINE_INSN | |
opt_size | |
(CALL_INFO ci) | |
(VALUE recv) | |
(VALUE val) | |
{ | |
if (!SPECIAL_CONST_P(recv)) { | |
if (RBASIC_CLASS(recv) == rb_cString && | |
BASIC_OP_UNREDEFINED_P(BOP_SIZE, STRING_REDEFINED_OP_FLAG)) { | |
val = rb_str_length(recv); | |
} | |
else if (RBASIC_CLASS(recv) == rb_cArray && | |
BASIC_OP_UNREDEFINED_P(BOP_SIZE, ARRAY_REDEFINED_OP_FLAG)) { | |
val = LONG2NUM(RARRAY_LEN(recv)); | |
} | |
else if (RBASIC_CLASS(recv) == rb_cHash && | |
BASIC_OP_UNREDEFINED_P(BOP_SIZE, HASH_REDEFINED_OP_FLAG)) { | |
val = INT2FIX(RHASH_SIZE(recv)); | |
} | |
else { | |
goto INSN_LABEL(normal_dispatch); | |
} | |
} | |
else { | |
INSN_LABEL(normal_dispatch): | |
PUSH(recv); | |
CALL_SIMPLE_METHOD(recv); | |
} | |
} | |
/** | |
@c optimize | |
@e optimized empty? | |
@j 最適化された recv.empty?()。 | |
*/ | |
DEFINE_INSN | |
opt_empty_p | |
(CALL_INFO ci) | |
(VALUE recv) | |
(VALUE val) | |
{ | |
if (!SPECIAL_CONST_P(recv)) { | |
if (RBASIC_CLASS(recv) == rb_cString && | |
BASIC_OP_UNREDEFINED_P(BOP_EMPTY_P, STRING_REDEFINED_OP_FLAG)) { | |
if (RSTRING_LEN(recv) == 0) val = Qtrue; | |
else val = Qfalse; | |
} | |
else if (RBASIC_CLASS(recv) == rb_cArray && | |
BASIC_OP_UNREDEFINED_P(BOP_EMPTY_P, ARRAY_REDEFINED_OP_FLAG)) { | |
if (RARRAY_LEN(recv) == 0) val = Qtrue; | |
else val = Qfalse; | |
} | |
else if (RBASIC_CLASS(recv) == rb_cHash && | |
BASIC_OP_UNREDEFINED_P(BOP_EMPTY_P, HASH_REDEFINED_OP_FLAG)) { | |
if (RHASH_EMPTY_P(recv)) val = Qtrue; | |
else val = Qfalse; | |
} | |
else { | |
goto INSN_LABEL(normal_dispatch); | |
} | |
} | |
else { | |
INSN_LABEL(normal_dispatch): | |
PUSH(recv); | |
CALL_SIMPLE_METHOD(recv); | |
} | |
} | |
/** | |
@c optimize | |
@e optimized succ | |
@j 最適化された recv.succ()。 | |
*/ | |
DEFINE_INSN | |
opt_succ | |
(CALL_INFO ci) | |
(VALUE recv) | |
(VALUE val) | |
{ | |
if (SPECIAL_CONST_P(recv)) { | |
if (FIXNUM_P(recv) && | |
BASIC_OP_UNREDEFINED_P(BOP_SUCC, FIXNUM_REDEFINED_OP_FLAG)) { | |
const VALUE obj = INT2FIX(1); | |
/* fixnum + INT2FIX(1) */ | |
val = (recv + (obj & (~1))); | |
if ((~(recv ^ obj) & (recv ^ val)) & ((unsigned long)LONG_MAX + 1)) { | |
val = rb_big_plus(rb_int2big(FIX2LONG(recv)), | |
rb_int2big(FIX2LONG(obj))); | |
} | |
} | |
else { | |
goto INSN_LABEL(normal_dispatch); | |
} | |
} | |
else { | |
if (RBASIC_CLASS(recv) == rb_cString && | |
BASIC_OP_UNREDEFINED_P(BOP_SUCC, STRING_REDEFINED_OP_FLAG)) { | |
val = rb_str_succ(recv); | |
} | |
else | |
{ | |
goto INSN_LABEL(normal_dispatch); | |
} | |
} | |
if (0) { | |
INSN_LABEL(normal_dispatch): | |
PUSH(recv); | |
CALL_SIMPLE_METHOD(recv); | |
} | |
} | |
/** | |
@c optimize | |
@e optimized not | |
@j 最適化された recv.!()。 | |
*/ | |
DEFINE_INSN | |
opt_not | |
(CALL_INFO ci) | |
(VALUE recv) | |
(VALUE val) | |
{ | |
extern VALUE rb_obj_not(VALUE obj); | |
vm_search_method(ci, recv); | |
if (check_cfunc(ci->me, rb_obj_not)) { | |
val = RTEST(recv) ? Qfalse : Qtrue; | |
} | |
else { | |
PUSH(recv); | |
CALL_SIMPLE_METHOD(recv); | |
} | |
} | |
/** | |
@c optimize | |
@e optimized regexp match | |
@j 最適化された正規表現マッチ。 | |
*/ | |
DEFINE_INSN | |
opt_regexpmatch1 | |
(VALUE r) | |
(VALUE obj) | |
(VALUE val) | |
{ | |
if (BASIC_OP_UNREDEFINED_P(BOP_MATCH, REGEXP_REDEFINED_OP_FLAG)) { | |
val = rb_reg_match(r, obj); | |
} | |
else { | |
val = rb_funcall(r, idEqTilde, 1, obj); | |
} | |
} | |
/** | |
@c optimize | |
@e optimized regexp match 2 | |
@j 最適化された正規表現マッチ 2 | |
*/ | |
DEFINE_INSN | |
opt_regexpmatch2 | |
(CALL_INFO ci) | |
(VALUE obj2, VALUE obj1) | |
(VALUE val) | |
{ | |
if (CLASS_OF(obj2) == rb_cString && | |
BASIC_OP_UNREDEFINED_P(BOP_MATCH, STRING_REDEFINED_OP_FLAG)) { | |
val = rb_reg_match(obj1, obj2); | |
} | |
else { | |
PUSH(obj2); | |
PUSH(obj1); | |
CALL_SIMPLE_METHOD(obj2); | |
} | |
} | |
/** | |
@c optimize | |
@e call native compiled method | |
@j ネイティブコンパイルしたメソッドを起動。 | |
*/ | |
DEFINE_INSN | |
opt_call_c_function | |
(rb_insn_func_t funcptr) | |
() | |
() | |
{ | |
reg_cfp = (funcptr)(th, reg_cfp); | |
if (reg_cfp == 0) { | |
VALUE err = th->errinfo; | |
th->errinfo = Qnil; | |
THROW_EXCEPTION(err); | |
} | |
RESTORE_REGS(); | |
NEXT_INSN(); | |
} | |
/** | |
@c joke | |
@e BLT | |
@j BLT | |
*/ | |
DEFINE_INSN | |
bitblt | |
() | |
() | |
(VALUE ret) | |
{ | |
ret = rb_str_new2("a bit of bacon, lettuce and tomato"); | |
} | |
/** | |
@c joke | |
@e The Answer to Life, the Universe, and Everything | |
@j 人生、宇宙、すべての答え。 | |
*/ | |
DEFINE_INSN | |
answer | |
() | |
() | |
(VALUE ret) | |
{ | |
ret = INT2FIX(42); | |
} | |