VALUE yarv_invoke_Integer_times_special_block(VALUE num) { rb_thread_t *th = GET_THREAD(); rb_block_t *orig_block = GC_GUARDED_PTR_REF(th->cfp->lfp[0]); if (orig_block && BUILTIN_TYPE(orig_block->iseq) != T_NODE) { VALUE tsiseqval = yarv_iseq_special_block(orig_block->iseq, build_Integer_times_node); rb_iseq_t *tsiseq; VALUE argv[2], val; if (tsiseqval) { rb_block_t block = *orig_block; GetISeqPtr(tsiseqval, tsiseq); block.iseq = tsiseq; th->cfp->lfp[0] = GC_GUARDED_PTR(&block); argv[0] = INT2FIX(0); argv[1] = num; val = th_invoke_yield(th, 2, argv); if (val == Qundef) { return num; } else { return val; } } } return Qundef; }
VALUE yarv_invoke_Range_each_special_block(VALUE range, VALUE beg, VALUE end, int excl) { rb_thread_t *th = GET_THREAD(); rb_block_t *orig_block = GC_GUARDED_PTR_REF(th->cfp->lfp[0]); if (BUILTIN_TYPE(orig_block->iseq) != T_NODE) { void *builder = excl ? build_Range_each_node_LT : build_Range_each_node_LE; VALUE tsiseqval = yarv_iseq_special_block(orig_block->iseq, builder); rb_iseq_t *tsiseq; VALUE argv[2]; if (tsiseqval) { VALUE val; rb_block_t block = *orig_block; GetISeqPtr(tsiseqval, tsiseq); block.iseq = tsiseq; th->cfp->lfp[0] = GC_GUARDED_PTR(&block); argv[0] = beg; argv[1] = end; val = th_invoke_yield(th, 2, argv); if (val == Qundef) { return range; } else { return val; } } } return Qundef; }
VALUE yarv_invoke_Array_each_special_block(VALUE ary) { rb_thread_t *th = GET_THREAD(); rb_block_t *orig_block = GC_GUARDED_PTR_REF(th->cfp->lfp[0]); if (BUILTIN_TYPE(orig_block->iseq) != T_NODE) { VALUE tsiseqval = yarv_iseq_special_block(orig_block->iseq, build_Array_each_node); rb_iseq_t *tsiseq; VALUE argv[2]; if (tsiseqval) { VALUE val; rb_block_t block = *orig_block; GetISeqPtr(tsiseqval, tsiseq); block.iseq = tsiseq; th->cfp->lfp[0] = GC_GUARDED_PTR(&block); argv[0] = 0; argv[1] = ary; val = th_invoke_yield(th, 2, argv); if (val == Qundef) { return ary; } else { return val; } } } return Qundef; }
int rb_node_arity(NODE* body) { switch (nd_type(body)) { case NODE_CFUNC: if (body->nd_argc < 0) return -1; return body->nd_argc; case NODE_ZSUPER: return -1; case NODE_ATTRSET: return 1; case NODE_IVAR: return 0; case NODE_BMETHOD: return rb_proc_arity(body->nd_cval); case RUBY_VM_METHOD_NODE: { rb_iseq_t *iseq; GetISeqPtr((VALUE)body->nd_body, iseq); if (iseq->arg_rest == -1 && iseq->arg_opts == 0) { return iseq->argc; } else { return -(iseq->argc + 1 + iseq->arg_post_len); } } default: rb_raise(rb_eArgError, "invalid node 0x%x", nd_type(body)); } }
static void vm_define_method(rb_thread_t *th, VALUE obj, ID id, VALUE iseqval, rb_num_t is_singleton, NODE *cref) { VALUE klass = cref->nd_clss; int noex = (int)cref->nd_visi; rb_iseq_t *miseq; GetISeqPtr(iseqval, miseq); if (miseq->klass) { iseqval = rb_iseq_clone(iseqval, 0); RB_GC_GUARD(iseqval); GetISeqPtr(iseqval, miseq); } if (NIL_P(klass)) { rb_raise(rb_eTypeError, "no class/module to add method"); } if (is_singleton) { if (FIXNUM_P(obj) || SYMBOL_P(obj)) { rb_raise(rb_eTypeError, "can't define singleton method \"%s\" for %s", rb_id2name(id), rb_obj_classname(obj)); } rb_check_frozen(obj); klass = rb_singleton_class(obj); noex = NOEX_PUBLIC; } /* dup */ COPY_CREF(miseq->cref_stack, cref); miseq->cref_stack->nd_visi = NOEX_PUBLIC; miseq->klass = klass; miseq->defined_method_id = id; rb_add_method(klass, id, VM_METHOD_TYPE_ISEQ, miseq, noex); if (!is_singleton && noex == NOEX_MODFUNC) { rb_add_method(rb_singleton_class(klass), id, VM_METHOD_TYPE_ISEQ, miseq, NOEX_PUBLIC); } INC_VM_STATE_VERSION(); }
static inline void vm_setup_method(rb_thread_t *th, rb_control_frame_t *cfp, const int argc, const rb_block_t *blockptr, const VALUE flag, const VALUE iseqval, const VALUE recv) { rb_iseq_t *iseq; int opt_pc, i; VALUE *sp, *rsp = cfp->sp - argc; /* TODO: eliminate it */ GetISeqPtr(iseqval, iseq); VM_CALLEE_SETUP_ARG(opt_pc, th, iseq, argc, rsp, &blockptr); /* stack overflow check */ CHECK_STACK_OVERFLOW(cfp, iseq->stack_max); sp = rsp + iseq->arg_size; if (LIKELY(!(flag & VM_CALL_TAILCALL_BIT))) { if (0) printf("local_size: %d, arg_size: %d\n", iseq->local_size, iseq->arg_size); /* clear local variables */ for (i = 0; i < iseq->local_size - iseq->arg_size; i++) { *sp++ = Qnil; } vm_push_frame(th, iseq, VM_FRAME_MAGIC_METHOD, recv, (VALUE) blockptr, iseq->iseq_encoded + opt_pc, sp, 0, 0); cfp->sp = rsp - 1 /* recv */; } else { VALUE *p_rsp; th->cfp++; /* pop cf */ p_rsp = th->cfp->sp; /* copy arguments */ for (i=0; i < (sp - rsp); i++) { p_rsp[i] = rsp[i]; } sp -= rsp - p_rsp; /* clear local variables */ for (i = 0; i < iseq->local_size - iseq->arg_size; i++) { *sp++ = Qnil; } vm_push_frame(th, iseq, VM_FRAME_MAGIC_METHOD, recv, (VALUE) blockptr, iseq->iseq_encoded + opt_pc, sp, 0, 0); } }
static void clone_method(VALUE klass, ID mid, const rb_method_entry_t *me) { VALUE newiseqval; if (me->def && me->def->type == VM_METHOD_TYPE_ISEQ) { rb_iseq_t *iseq; newiseqval = rb_iseq_clone(me->def->body.iseq->self, klass); GetISeqPtr(newiseqval, iseq); OBJ_WRITE(iseq->self, &iseq->cref_stack, rewrite_cref_stack(me->def->body.iseq->cref_stack, me->klass, klass)); rb_add_method(klass, mid, VM_METHOD_TYPE_ISEQ, iseq, me->flag); RB_GC_GUARD(newiseqval); } else { rb_method_entry_set(klass, mid, me, me->flag); } }
static int clone_method(ID mid, const rb_method_entry_t *me, struct clone_method_data *data) { VALUE newiseqval; if (me->def && me->def->type == VM_METHOD_TYPE_ISEQ) { rb_iseq_t *iseq; newiseqval = rb_iseq_clone(me->def->body.iseq->self, data->klass); GetISeqPtr(newiseqval, iseq); rb_add_method(data->klass, mid, VM_METHOD_TYPE_ISEQ, iseq, me->flag); RB_GC_GUARD(newiseqval); } else { rb_method_entry_set(data->klass, mid, me, me->flag); } return ST_CONTINUE; }
static int clone_method(ID mid, const rb_method_entry_t *me, struct clone_method_data *data) { switch (me->type) { case VM_METHOD_TYPE_ISEQ: { VALUE newiseqval = rb_iseq_clone(me->body.iseq->self, data->klass); rb_iseq_t *iseq; GetISeqPtr(newiseqval, iseq); rb_add_method(data->klass, mid, VM_METHOD_TYPE_ISEQ, iseq, me->flag); break; } default: rb_add_method_me(data->klass, mid, me, me->flag); break; } return ST_CONTINUE; }
/* From iseq.c */ static rb_iseq_t * iseq_check(VALUE val) { rb_iseq_t *iseq; if(!rb_obj_is_kind_of(val, rb_cISeq)) { rb_raise( rb_eTypeError, "Expected VM::InstructionSequence, but got %s", rb_class2name(CLASS_OF(val))); } GetISeqPtr(val, iseq); if (!iseq->name) { rb_raise(rb_eTypeError, "uninitialized InstructionSequence"); } return iseq; }
static rb_iseq_t * get_method_iseq(VALUE method) { struct METHOD *data; NODE *body; rb_iseq_t *iseq; Data_Get_Struct(method, struct METHOD, data); body = data->body; switch (nd_type(body)) { case RUBY_VM_METHOD_NODE: GetISeqPtr((VALUE)body->nd_body, iseq); if (RUBY_VM_NORMAL_ISEQ_P(iseq)) break; default: return 0; } return iseq; }
static void vm_set_top_stack(rb_thread_t * th, VALUE iseqval) { rb_iseq_t *iseq; GetISeqPtr(iseqval, iseq); if (iseq->type != ISEQ_TYPE_TOP) { rb_raise(rb_eTypeError, "Not a toplevel InstructionSequence"); } /* for return */ rb_vm_set_finish_env(th); vm_push_frame(th, iseq, VM_FRAME_MAGIC_TOP, th->top_self, 0, iseq->iseq_encoded, th->cfp->sp, 0, iseq->local_size); CHECK_STACK_OVERFLOW(th->cfp, iseq->stack_max); }
static void vm_set_eval_stack(rb_thread_t * th, VALUE iseqval, const NODE *cref) { rb_iseq_t *iseq; rb_block_t * const block = th->base_block; GetISeqPtr(iseqval, iseq); /* for return */ rb_vm_set_finish_env(th); vm_push_frame(th, iseq, VM_FRAME_MAGIC_EVAL, block->self, GC_GUARDED_PTR(block->dfp), iseq->iseq_encoded, th->cfp->sp, block->lfp, iseq->local_size); if (cref) { th->cfp->dfp[-1] = (VALUE)cref; } CHECK_STACK_OVERFLOW(th->cfp, iseq->stack_max); }
int rb_node_arity(NODE* body) { int n; switch (nd_type(body)) { case NODE_CFUNC: if (body->nd_argc < 0) return -1; return body->nd_argc; case NODE_ZSUPER: return -1; case NODE_ATTRSET: return 1; case NODE_IVAR: return 0; case NODE_BMETHOD: return rb_proc_arity(body->nd_cval); case NODE_SCOPE: body = body->nd_next; /* skip NODE_SCOPE */ if (nd_type(body) == NODE_BLOCK) body = body->nd_head; if (!body) return 0; n = body->nd_frml ? RARRAY_LEN(body->nd_frml) : 0; if (body->nd_opt || body->nd_rest) n = -n - 1; return n; case RUBY_VM_METHOD_NODE:{ rb_iseq_t *iseq; GetISeqPtr((VALUE)body->nd_body, iseq); if (iseq->arg_rest == 0 && iseq->arg_opts == 0) { return iseq->argc; } else { return -iseq->argc - 1; } } default: rb_raise(rb_eArgError, "invalid node 0x%x", nd_type(body)); } }
static void vm_set_main_stack(rb_thread_t *th, VALUE iseqval) { VALUE toplevel_binding = rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING")); rb_binding_t *bind; rb_iseq_t *iseq; rb_env_t *env; GetBindingPtr(toplevel_binding, bind); GetEnvPtr(bind->env, env); th->base_block = &env->block; vm_set_eval_stack(th, iseqval, 0); th->base_block = 0; /* save binding */ GetISeqPtr(iseqval, iseq); if (bind && iseq->local_size > 0) { bind->env = rb_vm_make_env_object(th, th->cfp); } CHECK_STACK_OVERFLOW(th->cfp, iseq->stack_max); }
static VALUE vm_exec(rb_thread_t *th) { int state; VALUE result, err; VALUE initial = 0; VALUE *escape_dfp = NULL; TH_PUSH_TAG(th); _tag.retval = Qnil; if ((state = EXEC_TAG()) == 0) { vm_loop_start: result = vm_exec_core(th, initial); if ((state = th->state) != 0) { err = result; th->state = 0; goto exception_handler; } } else { int i; struct iseq_catch_table_entry *entry; unsigned long epc, cont_pc, cont_sp; VALUE catch_iseqval; rb_control_frame_t *cfp; VALUE type; err = th->errinfo; exception_handler: cont_pc = cont_sp = catch_iseqval = 0; while (th->cfp->pc == 0 || th->cfp->iseq == 0) { if (UNLIKELY(VM_FRAME_TYPE(th->cfp) == VM_FRAME_MAGIC_CFUNC)) { const rb_method_entry_t *me = th->cfp->me; EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, th->cfp->self, me->called_id, me->klass); } th->cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp); } cfp = th->cfp; epc = cfp->pc - cfp->iseq->iseq_encoded; if (state == TAG_BREAK || state == TAG_RETURN) { escape_dfp = GET_THROWOBJ_CATCH_POINT(err); if (cfp->dfp == escape_dfp) { if (state == TAG_RETURN) { if ((cfp + 1)->pc != &finish_insn_seq[0]) { SET_THROWOBJ_CATCH_POINT(err, (VALUE)(cfp + 1)->dfp); SET_THROWOBJ_STATE(err, state = TAG_BREAK); } else { for (i = 0; i < cfp->iseq->catch_table_size; i++) { entry = &cfp->iseq->catch_table[i]; if (entry->start < epc && entry->end >= epc) { if (entry->type == CATCH_TYPE_ENSURE) { catch_iseqval = entry->iseq; cont_pc = entry->cont; cont_sp = entry->sp; break; } } } if (!catch_iseqval) { result = GET_THROWOBJ_VAL(err); th->errinfo = Qnil; th->cfp += 2; goto finish_vme; } } /* through */ } else { /* TAG_BREAK */ #if OPT_STACK_CACHING initial = (GET_THROWOBJ_VAL(err)); #else *th->cfp->sp++ = (GET_THROWOBJ_VAL(err)); #endif th->errinfo = Qnil; goto vm_loop_start; } } } if (state == TAG_RAISE) { for (i = 0; i < cfp->iseq->catch_table_size; i++) { entry = &cfp->iseq->catch_table[i]; if (entry->start < epc && entry->end >= epc) { if (entry->type == CATCH_TYPE_RESCUE || entry->type == CATCH_TYPE_ENSURE) { catch_iseqval = entry->iseq; cont_pc = entry->cont; cont_sp = entry->sp; break; } } } } else if (state == TAG_RETRY) { for (i = 0; i < cfp->iseq->catch_table_size; i++) { entry = &cfp->iseq->catch_table[i]; if (entry->start < epc && entry->end >= epc) { if (entry->type == CATCH_TYPE_ENSURE) { catch_iseqval = entry->iseq; cont_pc = entry->cont; cont_sp = entry->sp; break; } else if (entry->type == CATCH_TYPE_RETRY) { VALUE *escape_dfp; escape_dfp = GET_THROWOBJ_CATCH_POINT(err); if (cfp->dfp == escape_dfp) { cfp->pc = cfp->iseq->iseq_encoded + entry->cont; th->errinfo = Qnil; goto vm_loop_start; } } } } } else if (state == TAG_BREAK && ((VALUE)escape_dfp & ~0x03) == 0) { type = CATCH_TYPE_BREAK; search_restart_point: for (i = 0; i < cfp->iseq->catch_table_size; i++) { entry = &cfp->iseq->catch_table[i]; if (entry->start < epc && entry->end >= epc) { if (entry->type == CATCH_TYPE_ENSURE) { catch_iseqval = entry->iseq; cont_pc = entry->cont; cont_sp = entry->sp; break; } else if (entry->type == type) { cfp->pc = cfp->iseq->iseq_encoded + entry->cont; cfp->sp = cfp->bp + entry->sp; if (state != TAG_REDO) { #if OPT_STACK_CACHING initial = (GET_THROWOBJ_VAL(err)); #else *th->cfp->sp++ = (GET_THROWOBJ_VAL(err)); #endif } th->errinfo = Qnil; goto vm_loop_start; } } } } else if (state == TAG_REDO) { type = CATCH_TYPE_REDO; goto search_restart_point; } else if (state == TAG_NEXT) { type = CATCH_TYPE_NEXT; goto search_restart_point; } else { for (i = 0; i < cfp->iseq->catch_table_size; i++) { entry = &cfp->iseq->catch_table[i]; if (entry->start < epc && entry->end >= epc) { if (entry->type == CATCH_TYPE_ENSURE) { catch_iseqval = entry->iseq; cont_pc = entry->cont; cont_sp = entry->sp; break; } } } } if (catch_iseqval != 0) { /* found catch table */ rb_iseq_t *catch_iseq; /* enter catch scope */ GetISeqPtr(catch_iseqval, catch_iseq); cfp->sp = cfp->bp + cont_sp; cfp->pc = cfp->iseq->iseq_encoded + cont_pc; /* push block frame */ cfp->sp[0] = err; vm_push_frame(th, catch_iseq, VM_FRAME_MAGIC_BLOCK, cfp->self, (VALUE)cfp->dfp, catch_iseq->iseq_encoded, cfp->sp + 1 /* push value */, cfp->lfp, catch_iseq->local_size - 1); state = 0; th->state = 0; th->errinfo = Qnil; goto vm_loop_start; } else { /* skip frame */ switch (VM_FRAME_TYPE(th->cfp)) { case VM_FRAME_MAGIC_METHOD: EXEC_EVENT_HOOK(th, RUBY_EVENT_RETURN, th->cfp->self, 0, 0); break; case VM_FRAME_MAGIC_CLASS: EXEC_EVENT_HOOK(th, RUBY_EVENT_END, th->cfp->self, 0, 0); break; } th->cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp); if (VM_FRAME_TYPE(th->cfp) != VM_FRAME_MAGIC_FINISH) { goto exception_handler; } else { vm_pop_frame(th); th->errinfo = err; TH_POP_TAG2(); JUMP_TAG(state); } } } finish_vme: TH_POP_TAG(); return result; }
/*RHO static*/ VALUE eval_string_with_cref(VALUE self, VALUE src, VALUE scope, NODE *cref, const char *file, int line) { int state; VALUE result = Qundef; VALUE envval; rb_binding_t *bind = 0; rb_thread_t *th = GET_THREAD(); rb_env_t *env = NULL; rb_block_t block; volatile int parse_in_eval; volatile int mild_compile_error; if (file == 0) { file = rb_sourcefile(); line = rb_sourceline(); } parse_in_eval = th->parse_in_eval; mild_compile_error = th->mild_compile_error; PUSH_TAG(); if ((state = EXEC_TAG()) == 0) { rb_iseq_t *iseq; volatile VALUE iseqval; if (scope != Qnil) { if (rb_obj_is_kind_of(scope, rb_cBinding)) { GetBindingPtr(scope, bind); envval = bind->env; } else { rb_raise(rb_eTypeError, "wrong argument type %s (expected Binding)", rb_obj_classname(scope)); } GetEnvPtr(envval, env); th->base_block = &env->block; } else { rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp); if (cfp != 0) { block = *RUBY_VM_GET_BLOCK_PTR_IN_CFP(cfp); th->base_block = █ th->base_block->self = self; th->base_block->iseq = cfp->iseq; /* TODO */ } else { rb_raise(rb_eRuntimeError, "Can't eval on top of Fiber or Thread"); } } //RHO if ( TYPE(src) != T_STRING ){ iseqval = src; }else //RHO { /* make eval iseq */ th->parse_in_eval++; th->mild_compile_error++; iseqval = rb_iseq_compile(src, rb_str_new2(file), INT2FIX(line)); th->mild_compile_error--; th->parse_in_eval--; } vm_set_eval_stack(th, iseqval, cref); th->base_block = 0; if (0) { /* for debug */ printf("%s\n", RSTRING_PTR(rb_iseq_disasm(iseqval))); } /* save new env */ GetISeqPtr(iseqval, iseq); if (bind && iseq->local_size > 0) { bind->env = rb_vm_make_env_object(th, th->cfp); } /* kick */ CHECK_STACK_OVERFLOW(th->cfp, iseq->stack_max); result = vm_exec(th); } POP_TAG(); th->mild_compile_error = mild_compile_error; th->parse_in_eval = parse_in_eval; if (state) { if (state == TAG_RAISE) { VALUE errinfo = th->errinfo; if (strcmp(file, "(eval)") == 0) { VALUE mesg, errat, bt2; extern VALUE rb_get_backtrace(VALUE info); ID id_mesg; CONST_ID(id_mesg, "mesg"); errat = rb_get_backtrace(errinfo); mesg = rb_attr_get(errinfo, id_mesg); if (!NIL_P(errat) && TYPE(errat) == T_ARRAY && (bt2 = vm_backtrace(th, -2), RARRAY_LEN(bt2) > 0)) { if (!NIL_P(mesg) && TYPE(mesg) == T_STRING && !RSTRING_LEN(mesg)) { if (OBJ_FROZEN(mesg)) { VALUE m = rb_str_cat(rb_str_dup(RARRAY_PTR(errat)[0]), ": ", 2); rb_ivar_set(errinfo, id_mesg, rb_str_append(m, mesg)); } else { rb_str_update(mesg, 0, 0, rb_str_new2(": ")); rb_str_update(mesg, 0, 0, RARRAY_PTR(errat)[0]); } } RARRAY_PTR(errat)[0] = RARRAY_PTR(bt2)[0]; } } rb_exc_raise(errinfo); } JUMP_TAG(state); } return result; }