diff options
Diffstat (limited to 'vm_insnhelper.c')
-rw-r--r-- | vm_insnhelper.c | 866 |
1 files changed, 866 insertions, 0 deletions
diff --git a/vm_insnhelper.c b/vm_insnhelper.c index fd259fd428..a2367876dc 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -2880,3 +2880,869 @@ vm_defined(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_num_t op_type, VALUE return Qnil; } } + +static const VALUE * +vm_get_ep(const VALUE *const reg_ep, rb_num_t lv) +{ + rb_num_t i; + const VALUE *ep = reg_ep; + for (i = 0; i < lv; i++) { + ep = GET_PREV_EP(ep); + } + return ep; +} + +static VALUE +vm_get_special_object(const VALUE *const reg_ep, + enum vm_special_object_type type) +{ + switch (type) { + case VM_SPECIAL_OBJECT_VMCORE: + return rb_mRubyVMFrozenCore; + case VM_SPECIAL_OBJECT_CBASE: + return vm_get_cbase(reg_ep); + case VM_SPECIAL_OBJECT_CONST_BASE: + return vm_get_const_base(reg_ep); + default: + rb_bug("putspecialobject insn: unknown value_type %d", type); + } +} + +static void +vm_freezestring(VALUE str, VALUE debug) +{ + if (!NIL_P(debug)) { + rb_ivar_set(str, id_debug_created_info, debug); + } + rb_str_freeze(str); +} + +static VALUE +vm_concat_array(VALUE ary1, VALUE ary2st) +{ + 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); + } + return rb_ary_concat(tmp1, tmp2); +} + +static VALUE +vm_splat_array(VALUE flag, VALUE ary) +{ + VALUE tmp = rb_check_convert_type(ary, T_ARRAY, "Array", "to_a"); + if (NIL_P(tmp)) { + return rb_ary_new3(1, ary); + } + else if (RTEST(flag)) { + return rb_ary_dup(tmp); + } + else { + return tmp; + } +} + +static VALUE +vm_check_match(VALUE target, VALUE pattern, rb_num_t flag) +{ + enum vm_check_match_type type = ((int)flag) & VM_CHECKMATCH_TYPE_MASK; + + if (flag & VM_CHECKMATCH_ARRAY) { + long i; + const long n = RARRAY_LEN(pattern); + + for (i = 0; i < n; i++) { + VALUE v = RARRAY_AREF(pattern, i); + VALUE c = check_match(v, target, type); + + if (RTEST(c)) { + return c; + } + } + return Qfalse; + } + else { + return check_match(pattern, target, type); + } +} + +static VALUE +vm_check_keyword(lindex_t bits, lindex_t idx, const VALUE *ep) +{ + const VALUE kw_bits = *(ep - bits); + + if (FIXNUM_P(kw_bits)) { + int b = FIX2INT(kw_bits); + return (b & (0x01 << idx)) ? Qfalse : Qtrue; + } + else { + VM_ASSERT(RB_TYPE_P(kw_bits, T_HASH)); + return rb_hash_has_key(kw_bits, INT2FIX(idx)); + } +} + +static void +vm_dtrace(rb_event_flag_t flag, rb_thread_t *th) +{ + 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); + return; + case RUBY_EVENT_C_CALL: + RUBY_DTRACE_CMETHOD_ENTRY_HOOK(th, 0, 0); + return; + case RUBY_EVENT_RETURN: + RUBY_DTRACE_METHOD_RETURN_HOOK(th, 0, 0); + return; + case RUBY_EVENT_C_RETURN: + RUBY_DTRACE_CMETHOD_RETURN_HOOK(th, 0, 0); + return; + } + } +} + +static VALUE +vm_const_get_under(ID id, rb_num_t flags, VALUE cbase) +{ + VALUE ns; + + if ((ns = vm_search_const_defined_class(cbase, id)) == 0) { + return ns; + } + else if (VM_DEFINECLASS_SCOPED_P(flags)) { + return rb_public_const_get_at(ns, id); + } + else { + return rb_const_get_at(ns, id); + } +} + +static VALUE +vm_check_if_class(ID id, rb_num_t flags, VALUE super, VALUE klass) +{ + if (!RB_TYPE_P(klass, T_CLASS)) { + rb_raise(rb_eTypeError, "%"PRIsVALUE" is not a class", rb_id2str(id)); + } + else if (VM_DEFINECLASS_HAS_SUPERCLASS_P(flags)) { + VALUE tmp = rb_class_real(RCLASS_SUPER(klass)); + + if (tmp != super) { + rb_raise(rb_eTypeError, + "superclass mismatch for class %"PRIsVALUE"", + rb_id2str(id)); + } + else { + return klass; + } + } + else { + return klass; + } +} + +static VALUE +vm_check_if_module(ID id, VALUE mod) +{ + if (!RB_TYPE_P(mod, T_MODULE)) { + rb_raise(rb_eTypeError, "%"PRIsVALUE" is not a module", rb_id2str(id)); + } + else { + return mod; + } +} + +static VALUE +vm_declare_class(ID id, rb_num_t flags, VALUE cbase, VALUE super) +{ + /* new class declaration */ + VALUE s = VM_DEFINECLASS_HAS_SUPERCLASS_P(flags) ? super : rb_cObject; + VALUE c = rb_define_class_id(id, s); + + rb_set_class_path_string(c, cbase, rb_id2str(id)); + rb_const_set(cbase, id, c); + rb_class_inherited(s, c); + return c; +} + +static VALUE +vm_declare_module(ID id, VALUE cbase) +{ + /* new module declaration */ + VALUE mod = rb_define_module_id(id); + rb_set_class_path_string(mod, cbase, rb_id2str(id)); + rb_const_set(cbase, id, mod); + return mod; +} + +static VALUE +vm_define_class(ID id, rb_num_t flags, VALUE cbase, VALUE super) +{ + VALUE klass; + + if (VM_DEFINECLASS_HAS_SUPERCLASS_P(flags) && !RB_TYPE_P(super, T_CLASS)) { + rb_raise(rb_eTypeError, + "superclass must be a Class (%"PRIsVALUE" given)", + rb_obj_class(super)); + } + + vm_check_if_namespace(cbase); + + /* find klass */ + rb_autoload_load(cbase, id); + if ((klass = vm_const_get_under(id, flags, cbase)) != 0) { + return vm_check_if_class(id, flags, super, klass); + } + else { + return vm_declare_class(id, flags, cbase, super); + } +} + +static VALUE +vm_define_module(ID id, rb_num_t flags, VALUE cbase) +{ + VALUE mod; + + vm_check_if_namespace(cbase); + if ((mod = vm_const_get_under(id, flags, cbase)) != 0) { + return vm_check_if_module(id, mod); + } + else { + return vm_declare_module(id, cbase); + } +} + +static VALUE +vm_find_or_create_class_by_id(ID id, + rb_num_t flags, + VALUE cbase, + VALUE super) +{ + rb_vm_defineclass_type_t type = VM_DEFINECLASS_TYPE(flags); + + switch (type) { + case VM_DEFINECLASS_TYPE_CLASS: + /* classdef returns class scope value */ + return vm_define_class(id, flags, cbase, super); + + case VM_DEFINECLASS_TYPE_SINGLETON_CLASS: + /* classdef returns class scope value */ + return rb_singleton_class(cbase); + + case VM_DEFINECLASS_TYPE_MODULE: + /* classdef returns class scope value */ + return vm_define_module(id, flags, cbase); + + default: + rb_bug("unknown defineclass type: %d", (int)type); + } +} + +/* this macro is mandatory to use OPTIMIZED_CMP. What a design! */ +#define id_cmp idCmp + +static VALUE +vm_opt_newarray_max(rb_num_t num, const VALUE *ptr) +{ + if (BASIC_OP_UNREDEFINED_P(BOP_MAX, ARRAY_REDEFINED_OP_FLAG)) { + if (num == 0) { + return Qnil; + } + else { + struct cmp_opt_data cmp_opt = { 0, 0 }; + VALUE result = Qundef; + rb_num_t i = num - 1; + result = ptr[i]; + while (i-- > 0) { + const VALUE v = ptr[i]; + if (result == Qundef || OPTIMIZED_CMP(v, result, cmp_opt) > 0) { + result = v; + } + } + return result == Qundef ? Qnil : result; + } + } + else { + VALUE ary = rb_ary_new4(num, ptr); + return rb_funcall(ary, idMax, 0); + } +} + +static VALUE +vm_opt_newarray_min(rb_num_t num, const VALUE *ptr) +{ + if (BASIC_OP_UNREDEFINED_P(BOP_MIN, ARRAY_REDEFINED_OP_FLAG)) { + if (num == 0) { + return Qnil; + } + else { + struct cmp_opt_data cmp_opt = { 0, 0 }; + VALUE result = Qundef; + rb_num_t i = num - 1; + result = ptr[i]; + while (i-- > 0) { + const VALUE v = ptr[i]; + if (result == Qundef || OPTIMIZED_CMP(v, result, cmp_opt) < 0) { + result = v; + } + } + return result == Qundef ? Qnil : result; + } + } + else { + VALUE ary = rb_ary_new4(num, ptr); + return rb_funcall(ary, idMin, 0); + } +} + +#undef id_cmp + +static VALUE +vm_ic_hit_p(IC ic, const VALUE *reg_ep) +{ + if (ic->ic_serial == GET_GLOBAL_CONSTANT_STATE() && + (ic->ic_cref == NULL || ic->ic_cref == rb_vm_get_cref(reg_ep))) { + return ic->ic_value.value; + } + else { + return Qnil; + } +} + +static void +vm_ic_update(IC ic, VALUE val, const VALUE *reg_ep) +{ + VM_ASSERT(ic->ic_value.value != Qundef); + ic->ic_value.value = val; + ic->ic_serial = GET_GLOBAL_CONSTANT_STATE() - ruby_vm_const_missing_count; + ic->ic_cref = vm_get_const_key_cref(reg_ep); + ruby_vm_const_missing_count = 0; +} + +static VALUE +vm_once_dispatch(ISEQ iseq, IC ic, rb_thread_t *th) +{ + rb_thread_t *const RUNNING_THREAD_ONCE_DONE = (rb_thread_t *)(0x1); + union iseq_inline_storage_entry *is = (union iseq_inline_storage_entry *)ic; + + if (is->once.running_thread == RUNNING_THREAD_ONCE_DONE) { + return is->once.value; + } + else if (is->once.running_thread == NULL) { + VALUE val; + 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(th->cfp->iseq, val); + return val; + } + else if (is->once.running_thread == th) { + /* recursive once */ + return vm_once_exec((VALUE)iseq); + } + else { + /* waiting for finish */ + RUBY_VM_CHECK_INTS(th); + rb_thread_schedule(); + return vm_once_dispatch(iseq, ic, th); + } +} + +static OFFSET +vm_case_dispatch(CDHASH hash, OFFSET else_offset, VALUE key) +{ + switch (OBJ_BUILTIN_TYPE(key)) { + case -1: + case T_FLOAT: + case T_SYMBOL: + case T_BIGNUM: + case T_STRING: + if (BASIC_OP_UNREDEFINED_P(BOP_EQQ, + SYMBOL_REDEFINED_OP_FLAG | + INTEGER_REDEFINED_OP_FLAG | + FLOAT_REDEFINED_OP_FLAG | + NIL_REDEFINED_OP_FLAG | + TRUE_REDEFINED_OP_FLAG | + FALSE_REDEFINED_OP_FLAG | + STRING_REDEFINED_OP_FLAG)) { + st_data_t val; + if (RB_FLOAT_TYPE_P(key)) { + double kval = RFLOAT_VALUE(key); + if (!isinf(kval) && modf(kval, &kval) == 0.0) { + key = FIXABLE(kval) ? LONG2FIX((long)kval) : rb_dbl2big(kval); + } + } + if (st_lookup(RHASH_TBL_RAW(hash), key, &val)) { + return FIX2INT((VALUE)val); + } + else { + return else_offset; + } + } + } + return 0; +} + +static VALUE +vm_opt_plus(VALUE recv, VALUE obj) +{ + if (FIXNUM_2_P(recv, obj) && + BASIC_OP_UNREDEFINED_P(BOP_PLUS,INTEGER_REDEFINED_OP_FLAG)) { + return rb_fix_plus_fix(recv, obj); + } + else if (FLONUM_2_P(recv, obj) && + BASIC_OP_UNREDEFINED_P(BOP_PLUS, FLOAT_REDEFINED_OP_FLAG)) { + return DBL2NUM(RFLOAT_VALUE(recv) + RFLOAT_VALUE(obj)); + } + else if (FLOAT_INSTANCE_P(recv) && FLOAT_INSTANCE_P(obj) && + BASIC_OP_UNREDEFINED_P(BOP_PLUS, FLOAT_REDEFINED_OP_FLAG)) { + return DBL2NUM(RFLOAT_VALUE(recv) + RFLOAT_VALUE(obj)); + } + else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) { + if (RBASIC_CLASS(recv) == rb_cString && RBASIC_CLASS(obj) == rb_cString && + BASIC_OP_UNREDEFINED_P(BOP_PLUS, STRING_REDEFINED_OP_FLAG)) { + return rb_str_plus(recv, obj); + } + else if (RBASIC_CLASS(recv) == rb_cArray && + BASIC_OP_UNREDEFINED_P(BOP_PLUS, ARRAY_REDEFINED_OP_FLAG)) { + return rb_ary_plus(recv, obj); + } + else { + return Qundef; + } + } + else { + return Qundef; + } +} + +static VALUE +vm_opt_minus(VALUE recv, VALUE obj) +{ + if (FIXNUM_2_P(recv, obj) && + BASIC_OP_UNREDEFINED_P(BOP_MINUS, INTEGER_REDEFINED_OP_FLAG)) { + return rb_fix_minus_fix(recv, obj); + } + else if (FLONUM_2_P(recv, obj) && + BASIC_OP_UNREDEFINED_P(BOP_MINUS, FLOAT_REDEFINED_OP_FLAG)) { + return DBL2NUM(RFLOAT_VALUE(recv) - RFLOAT_VALUE(obj)); + } + else if (FLOAT_INSTANCE_P(recv) && FLOAT_INSTANCE_P(obj) && + BASIC_OP_UNREDEFINED_P(BOP_MINUS, FLOAT_REDEFINED_OP_FLAG)) { + return DBL2NUM(RFLOAT_VALUE(recv) - RFLOAT_VALUE(obj)); + } + else { + return Qundef; + } +} + +static VALUE +vm_opt_mult(VALUE recv, VALUE obj) +{ + if (FIXNUM_2_P(recv, obj) && + BASIC_OP_UNREDEFINED_P(BOP_MULT, INTEGER_REDEFINED_OP_FLAG)) { + return rb_fix_mul_fix(recv, obj); + } + else if (FLONUM_2_P(recv, obj) && + BASIC_OP_UNREDEFINED_P(BOP_MULT, FLOAT_REDEFINED_OP_FLAG)) { + return DBL2NUM(RFLOAT_VALUE(recv) * RFLOAT_VALUE(obj)); + } + else if (FLOAT_INSTANCE_P(recv) && FLOAT_INSTANCE_P(obj) && + BASIC_OP_UNREDEFINED_P(BOP_MULT, FLOAT_REDEFINED_OP_FLAG)) { + return DBL2NUM(RFLOAT_VALUE(recv) * RFLOAT_VALUE(obj)); + } + else { + return Qundef; + } +} + +static VALUE +vm_opt_div(VALUE recv, VALUE obj) +{ + if (FIXNUM_2_P(recv, obj) && + BASIC_OP_UNREDEFINED_P(BOP_DIV, INTEGER_REDEFINED_OP_FLAG)) { + if (FIX2LONG(obj) == 0) { + return Qundef; + } + else { + return rb_fix_div_fix(recv, obj); + } + } + else if (FLONUM_2_P(recv, obj) && + BASIC_OP_UNREDEFINED_P(BOP_DIV, FLOAT_REDEFINED_OP_FLAG)) { + return DBL2NUM(RFLOAT_VALUE(recv) / RFLOAT_VALUE(obj)); + } + else if (FLOAT_INSTANCE_P(recv) && FLOAT_INSTANCE_P(obj) && + BASIC_OP_UNREDEFINED_P(BOP_DIV, FLOAT_REDEFINED_OP_FLAG)) { + return DBL2NUM(RFLOAT_VALUE(recv) / RFLOAT_VALUE(obj)); + } + else { + return Qundef; + } +} + +static VALUE +vm_opt_mod(VALUE recv, VALUE obj) +{ + if (FIXNUM_2_P(recv, obj) && + BASIC_OP_UNREDEFINED_P(BOP_MOD, INTEGER_REDEFINED_OP_FLAG )) { + if (FIX2LONG(obj) == 0) { + return Qundef; + } + else { + return rb_fix_mod_fix(recv, obj); + } + } + else if (FLONUM_2_P(recv, obj) && + BASIC_OP_UNREDEFINED_P(BOP_MOD, FLOAT_REDEFINED_OP_FLAG)) { + return DBL2NUM(ruby_float_mod(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj))); + } + else if (FLOAT_INSTANCE_P(recv) && FLOAT_INSTANCE_P(obj) && + BASIC_OP_UNREDEFINED_P(BOP_MOD, FLOAT_REDEFINED_OP_FLAG)) { + return DBL2NUM(ruby_float_mod(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj))); + } + else { + return Qundef; + } +} + +static inline int +vm_method_cfunc_is(CALL_INFO ci, CALL_CACHE cc, + VALUE recv, VALUE (*func)()) +{ + vm_search_method(ci, cc, recv); + return check_cfunc(cc->me, func); +} + +static VALUE +vm_opt_neq(CALL_INFO ci, CALL_CACHE cc, + CALL_INFO ci_eq, CALL_CACHE cc_eq, + VALUE recv, VALUE obj) +{ + if (vm_method_cfunc_is(ci, cc, recv, rb_obj_not_equal)) { + VALUE val = opt_eq_func(recv, obj, ci_eq, cc_eq); + + if (val != Qundef) { + return RTEST(val) ? Qfalse : Qtrue; + } + } + + return Qundef; +} + +static VALUE +vm_opt_lt(VALUE recv, VALUE obj) +{ + if (FIXNUM_2_P(recv, obj) && + BASIC_OP_UNREDEFINED_P(BOP_LT, INTEGER_REDEFINED_OP_FLAG)) { + SIGNED_VALUE a = recv, b = obj; + + if (a < b) { + return Qtrue; + } + else { + return Qfalse; + } + } + else if (FLONUM_2_P(recv, obj) && + BASIC_OP_UNREDEFINED_P(BOP_LT, FLOAT_REDEFINED_OP_FLAG)) { + /* flonum is not NaN */ + return RFLOAT_VALUE(recv) < RFLOAT_VALUE(obj) ? Qtrue : Qfalse; + } + else if (FLOAT_INSTANCE_P(recv) && FLOAT_INSTANCE_P(obj) && + BASIC_OP_UNREDEFINED_P(BOP_LT, FLOAT_REDEFINED_OP_FLAG)) { + return double_cmp_lt(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj)); + } + else { + return Qundef; + } +} + +static VALUE +vm_opt_le(VALUE recv, VALUE obj) +{ + if (FIXNUM_2_P(recv, obj) && + BASIC_OP_UNREDEFINED_P(BOP_LE, INTEGER_REDEFINED_OP_FLAG)) { + SIGNED_VALUE a = recv, b = obj; + + if (a <= b) { + return Qtrue; + } + else { + return Qfalse; + } + } + else if (FLONUM_2_P(recv, obj) && + BASIC_OP_UNREDEFINED_P(BOP_LE, FLOAT_REDEFINED_OP_FLAG)) { + /* flonum is not NaN */ + return RFLOAT_VALUE(recv) <= RFLOAT_VALUE(obj) ? Qtrue : Qfalse; + } + else if (FLOAT_INSTANCE_P(recv) && FLOAT_INSTANCE_P(obj) && + BASIC_OP_UNREDEFINED_P(BOP_LT, FLOAT_REDEFINED_OP_FLAG)) { + return double_cmp_le(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj)); + } + else { + return Qundef; + } +} + +static VALUE +vm_opt_gt(VALUE recv, VALUE obj) +{ + if (FIXNUM_2_P(recv, obj) && + BASIC_OP_UNREDEFINED_P(BOP_GT, INTEGER_REDEFINED_OP_FLAG)) { + SIGNED_VALUE a = recv, b = obj; + + if (a > b) { + return Qtrue; + } + else { + return Qfalse; + } + } + else if (FLONUM_2_P(recv, obj) && + BASIC_OP_UNREDEFINED_P(BOP_GT, FLOAT_REDEFINED_OP_FLAG)) { + /* flonum is not NaN */ + return RFLOAT_VALUE(recv) > RFLOAT_VALUE(obj) ? Qtrue : Qfalse; + } + else if (FLOAT_INSTANCE_P(recv) && FLOAT_INSTANCE_P(obj) && + BASIC_OP_UNREDEFINED_P(BOP_GT, FLOAT_REDEFINED_OP_FLAG)) { + return double_cmp_gt(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj)); + } + else { + return Qundef; + } +} + +static VALUE +vm_opt_ge(VALUE recv, VALUE obj) +{ + if (FIXNUM_2_P(recv, obj) && + BASIC_OP_UNREDEFINED_P(BOP_GE, INTEGER_REDEFINED_OP_FLAG)) { + SIGNED_VALUE a = recv, b = obj; + + if (a >= b) { + return Qtrue; + } + else { + return Qfalse; + } + } + else if (FLONUM_2_P(recv, obj) && + BASIC_OP_UNREDEFINED_P(BOP_GE, FLOAT_REDEFINED_OP_FLAG)) { + /* flonum is not NaN */ + return RFLOAT_VALUE(recv) >= RFLOAT_VALUE(obj) ? Qtrue : Qfalse; + } + else if (FLOAT_INSTANCE_P(recv) && FLOAT_INSTANCE_P(obj) && + BASIC_OP_UNREDEFINED_P(BOP_GT, FLOAT_REDEFINED_OP_FLAG)) { + return double_cmp_ge(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj)); + } + else { + return Qundef; + } +} + +static VALUE +vm_opt_ltlt(VALUE recv, VALUE obj) +{ + if (SPECIAL_CONST_P(recv)) { + return Qundef; + } + else if (RBASIC_CLASS(recv) == rb_cString && + BASIC_OP_UNREDEFINED_P(BOP_LTLT, STRING_REDEFINED_OP_FLAG)) { + return rb_str_concat(recv, obj); + } + else if (RBASIC_CLASS(recv) == rb_cArray && + BASIC_OP_UNREDEFINED_P(BOP_LTLT, ARRAY_REDEFINED_OP_FLAG)) { + return rb_ary_push(recv, obj); + } + else { + return Qundef; + } +} + +static VALUE +vm_opt_aref(VALUE recv, VALUE obj) +{ + if (SPECIAL_CONST_P(recv)) { + return Qundef; + } + else if (RBASIC_CLASS(recv) == rb_cArray && + BASIC_OP_UNREDEFINED_P(BOP_AREF, ARRAY_REDEFINED_OP_FLAG) && + FIXNUM_P(obj)) { + return rb_ary_entry(recv, FIX2LONG(obj)); + } + else if (RBASIC_CLASS(recv) == rb_cHash && + BASIC_OP_UNREDEFINED_P(BOP_AREF, HASH_REDEFINED_OP_FLAG)) { + return rb_hash_aref(recv, obj); + } + else { + return Qundef; + } +} + +static VALUE +vm_opt_aset(VALUE recv, VALUE obj, VALUE set) +{ + if (SPECIAL_CONST_P(recv)) { + return Qundef; + } + else 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); + return set; + } + else if (RBASIC_CLASS(recv) == rb_cHash && + BASIC_OP_UNREDEFINED_P(BOP_ASET, HASH_REDEFINED_OP_FLAG)) { + rb_hash_aset(recv, obj, set); + return set; + } + else { + return Qundef; + } +} + +static VALUE +vm_opt_aref_with(VALUE recv, VALUE key) +{ + if (!SPECIAL_CONST_P(recv) && RBASIC_CLASS(recv) == rb_cHash && + BASIC_OP_UNREDEFINED_P(BOP_AREF, HASH_REDEFINED_OP_FLAG) && + rb_hash_compare_by_id_p(recv) == Qfalse) { + return rb_hash_aref(recv, key); + } + else { + return Qundef; + } +} + +static VALUE +vm_opt_aset_with(VALUE recv, VALUE key, VALUE val) +{ + if (!SPECIAL_CONST_P(recv) && RBASIC_CLASS(recv) == rb_cHash && + BASIC_OP_UNREDEFINED_P(BOP_ASET, HASH_REDEFINED_OP_FLAG) && + rb_hash_compare_by_id_p(recv) == Qfalse) { + return rb_hash_aset(recv, key, val); + } + else { + return Qundef; + } +} + +static VALUE +vm_opt_length(VALUE recv, int bop) +{ + if (SPECIAL_CONST_P(recv)) { + return Qundef; + } + else if (RBASIC_CLASS(recv) == rb_cString && + BASIC_OP_UNREDEFINED_P(bop, STRING_REDEFINED_OP_FLAG)) { + if (bop == BOP_EMPTY_P) { + return LONG2NUM(RSTRING_LEN(recv)); + } + else { + return rb_str_length(recv); + } + } + else if (RBASIC_CLASS(recv) == rb_cArray && + BASIC_OP_UNREDEFINED_P(bop, ARRAY_REDEFINED_OP_FLAG)) { + return LONG2NUM(RARRAY_LEN(recv)); + } + else if (RBASIC_CLASS(recv) == rb_cHash && + BASIC_OP_UNREDEFINED_P(bop, HASH_REDEFINED_OP_FLAG)) { + return INT2FIX(RHASH_SIZE(recv)); + } + else { + return Qundef; + } +} + +static VALUE +vm_opt_empty_p(VALUE recv) +{ + switch (vm_opt_length(recv, BOP_EMPTY_P)) { + case Qundef: return Qundef; + case INT2FIX(0): return Qtrue; + default: return Qfalse; + } +} + +static VALUE +vm_opt_succ(VALUE recv) +{ + if (SPECIAL_CONST_P(recv)) { + if (FIXNUM_P(recv) && + BASIC_OP_UNREDEFINED_P(BOP_SUCC, INTEGER_REDEFINED_OP_FLAG)) { + /* fixnum + INT2FIX(1) */ + if (recv == LONG2FIX(FIXNUM_MAX)) { + return LONG2NUM(FIXNUM_MAX + 1); + } + else { + return recv - 1 + INT2FIX(1); + } + } + else { + return Qundef; + } + } + else { + if (RBASIC_CLASS(recv) == rb_cString && + BASIC_OP_UNREDEFINED_P(BOP_SUCC, STRING_REDEFINED_OP_FLAG)) { + return rb_str_succ(recv); + } + else { + return Qundef; + } + } +} + +static VALUE +vm_opt_not(CALL_INFO ci, CALL_CACHE cc, VALUE recv) +{ + if (vm_method_cfunc_is(ci, cc, recv, rb_obj_not)) { + return RTEST(recv) ? Qfalse : Qtrue; + } + else { + return Qundef; + } +} + +static VALUE +vm_opt_regexpmatch1(VALUE recv, VALUE obj) +{ + if (BASIC_OP_UNREDEFINED_P(BOP_MATCH, REGEXP_REDEFINED_OP_FLAG)) { + return rb_reg_match(recv, obj); + } + else { + return rb_funcall(recv, idEqTilde, 1, obj); + } +} + +static VALUE +vm_opt_regexpmatch2(VALUE recv, VALUE obj) +{ + if (CLASS_OF(obj) == rb_cString && + BASIC_OP_UNREDEFINED_P(BOP_MATCH, STRING_REDEFINED_OP_FLAG)) { + return rb_reg_match(recv, obj); + } + else { + return Qundef; + } +} + |