static RETSIGTYPE sigsegv(int sig SIGINFO_ARG) { check_reserved_signal("SEGV"); CHECK_STACK_OVERFLOW(); rb_bug_context(SIGINFO_CTX, "Segmentation fault" MESSAGE_FAULT_ADDRESS); }
static RETSIGTYPE sigill(int sig SIGINFO_ARG) { check_reserved_signal("ILL"); #if defined __APPLE__ CHECK_STACK_OVERFLOW(); #endif rb_bug_context(SIGINFO_CTX, "Illegal instruction" MESSAGE_FAULT_ADDRESS); }
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 RETSIGTYPE sigbus(int sig SIGINFO_ARG) { /* * Mac OS X makes KERN_PROTECTION_FAILURE when thread touch guard page. * and it's delivered as SIGBUS instead of SIGSEGV to userland. It's crazy * wrong IMHO. but anyway we have to care it. Sigh. */ #if defined __APPLE__ CHECK_STACK_OVERFLOW(); #endif rb_bug_context(SIGINFO_CTX, "Bus Error" MESSAGE_FAULT_ADDRESS); }
static RETSIGTYPE sigsegv(int sig SIGINFO_ARG) { if (segv_received) { char msg[] = "SEGV received in SEGV handler\n"; write(2, msg, sizeof(msg)); ruby_abort(); } CHECK_STACK_OVERFLOW(); segv_received = 1; ruby_disable_gc_stress = 1; rb_bug("Segmentation fault"); }
void c_parse_command(int num_arg) { svalue_t *arg; svalue_t *fp; int i; /* * type checking on first three required parameters to parse_command() */ arg = sp - 2; CHECK_TYPES(&arg[0], T_STRING, 1, F_PARSE_COMMAND); CHECK_TYPES(&arg[1], T_OBJECT | T_ARRAY, 2, F_PARSE_COMMAND); CHECK_TYPES(&arg[2], T_STRING, 3, F_PARSE_COMMAND); /* * allocate stack frame for rvalues and return value (number of matches); * perform some stack manipulation; */ fp = sp; CHECK_STACK_OVERFLOW(num_arg + 1); sp += num_arg + 1; arg = sp; *(arg--) = *(fp--); /* move pattern to top of stack */ *(arg--) = *(fp--); /* move source object or array to just below the pattern */ *(arg) = *(fp); /* move source string just below the object */ fp->type = T_NUMBER; /* * prep area for rvalues */ for (i = 1; i <= num_arg; i++) fp[i].type = T_INVALID; /* * do it... */ i = parse(arg[0].u.string, &arg[1], arg[2].u.string, &fp[1], num_arg); /* * remove mandatory parameters */ pop_3_elems(); /* * save return value on stack */ fp->u.number = i; }
static RETSIGTYPE sigsegv(int sig SIGINFO_ARG) { if (segv_received) { ssize_t RB_UNUSED_VAR(err); static const char msg[] = "SEGV received in SEGV handler\n"; err = write(2, msg, sizeof(msg)); ruby_abort(); } CHECK_STACK_OVERFLOW(); segv_received = 1; ruby_disable_gc = 1; rb_bug_context(SIGINFO_CTX, "Segmentation fault" MESSAGE_FAULT_ADDRESS); }
static inline VALUE invoke_block_from_c(rb_thread_t *th, const rb_block_t *block, VALUE self, int argc, const VALUE *argv, const rb_block_t *blockptr, const NODE *cref) { if (SPECIAL_CONST_P(block->iseq)) return Qnil; else if (BUILTIN_TYPE(block->iseq) != T_NODE) { const rb_iseq_t *iseq = block->iseq; const rb_control_frame_t *cfp; rb_control_frame_t *ncfp; int i, opt_pc, arg_size = iseq->arg_size; int type = block_proc_is_lambda(block->proc) ? VM_FRAME_MAGIC_LAMBDA : VM_FRAME_MAGIC_BLOCK; rb_vm_set_finish_env(th); cfp = th->cfp; CHECK_STACK_OVERFLOW(cfp, argc + iseq->stack_max); for (i=0; i<argc; i++) { cfp->sp[i] = argv[i]; } opt_pc = vm_yield_setup_args(th, iseq, argc, cfp->sp, blockptr, type == VM_FRAME_MAGIC_LAMBDA); ncfp = vm_push_frame(th, iseq, type, self, GC_GUARDED_PTR(block->dfp), iseq->iseq_encoded + opt_pc, cfp->sp + arg_size, block->lfp, iseq->local_size - arg_size); ncfp->me = th->passed_me; th->passed_me = 0; th->passed_block = blockptr; if (cref) { th->cfp->dfp[-1] = (VALUE)cref; } return vm_exec(th); } else { return vm_yield_with_cfunc(th, block, self, argc, argv, blockptr); } }
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); }
void c_expand_varargs(int where) { svalue_t *s, *t; array_t *arr; int n; s = sp - where; if (s->type != T_ARRAY) error("Item being expanded with ... is not an array\n"); arr = s->u.arr; n = arr->size; num_varargs += n - 1; if (!n) { t = s; while (t < sp) { *t = *(t + 1); t++; } sp--; } else if (n == 1) { assign_svalue_no_free(s, &arr->item[0]); } else { t = sp; CHECK_STACK_OVERFLOW(n - 1); sp += n - 1; while (t > s) { *(t + n - 1) = *t; t--; } t = s + n - 1; if (arr->ref == 1) { memcpy(s, arr->item, n * sizeof(svalue_t)); free_empty_array(arr); return; } else { while (n--) assign_svalue_no_free(t--, &arr->item[n]); } } free_array(arr); }
void c_sscanf(int num_arg) { svalue_t *fp; int i; /* * allocate stack frame for rvalues and return value (number of matches); * perform some stack manipulation; note: source and template strings are * already on the stack by this time */ fp = sp; CHECK_STACK_OVERFLOW(num_arg + 1); sp += num_arg + 1; *sp = *(fp--); /* move format description to top of stack */ *(sp - 1) = *(fp); /* move source string just below the format * desc. */ fp->type = T_NUMBER; /* this svalue isn't invalidated below, and * if we don't change it to something safe, * it will get freed twice if an error occurs */ /* * prep area for rvalues */ for (i = 1; i <= num_arg; i++) fp[i].type = T_INVALID; /* * do it... */ i = inter_sscanf(sp - 2, sp - 1, sp, num_arg); /* * remove source & template strings from top of stack */ pop_2_elems(); /* * save number of matches on stack */ fp->type = T_NUMBER; fp->u.number = i; }
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_invoke_block(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_num_t num, rb_num_t flag) { const rb_block_t *block = GET_BLOCK_PTR(); rb_iseq_t *iseq; int argc = (int)num; VALUE type = GET_ISEQ()->local_iseq->type; if ((type != ISEQ_TYPE_METHOD && type != ISEQ_TYPE_CLASS) || block == 0) { rb_vm_localjump_error("no block given (yield)", Qnil, 0); } iseq = block->iseq; argc = caller_setup_args(th, GET_CFP(), flag, argc, 0, 0); if (BUILTIN_TYPE(iseq) != T_NODE) { int opt_pc; const int arg_size = iseq->arg_size; VALUE * const rsp = GET_SP() - argc; SET_SP(rsp); CHECK_STACK_OVERFLOW(GET_CFP(), iseq->stack_max); opt_pc = vm_yield_setup_args(th, iseq, argc, rsp, 0, block_proc_is_lambda(block->proc)); vm_push_frame(th, iseq, VM_FRAME_MAGIC_BLOCK, block->self, (VALUE) block->dfp, iseq->iseq_encoded + opt_pc, rsp + arg_size, block->lfp, iseq->local_size - arg_size); return Qundef; } else { VALUE val = vm_yield_with_cfunc(th, block, block->self, argc, STACK_ADDR_FROM_TOP(argc), 0); POPN(argc); /* TODO: should put before C/yield? */ return val; } }
/* num_arg args are on the stack, and the args from the array vec should be * put in front of them. This is so that the order of arguments is logical. * * evaluate( (: f, a :), b) -> f(a,b) and not f(b, a) which would happen * if we simply pushed the args from vec at this point. (Note that the * old function pointers are broken in this regard) */ int merge_arg_lists (int num_arg, array_t * arr, int start) { int num_arr_arg = arr->size - start; svalue_t *sptr; if (num_arr_arg) { CHECK_STACK_OVERFLOW(num_arr_arg); sptr = (sp += num_arr_arg); if (num_arg) { /* We need to do some stack movement so that the order of arguments is logical */ while (num_arg--) { *sptr = *(sptr - num_arr_arg); sptr--; } } num_arg = arr->size; while (--num_arg >= start) assign_svalue_no_free(sptr--, &arr->item[num_arg]); /* could just return num_arr_arg if num_arg is 0 but .... -Sym */ return (sp - sptr); } return num_arg; }
static inline int caller_setup_args(const rb_thread_t *th, rb_control_frame_t *cfp, VALUE flag, int argc, rb_iseq_t *blockiseq, rb_block_t **block) { rb_block_t *blockptr = 0; if (block) { if (flag & VM_CALL_ARGS_BLOCKARG_BIT) { rb_proc_t *po; VALUE proc; proc = *(--cfp->sp); if (proc != Qnil) { if (!rb_obj_is_proc(proc)) { VALUE b = rb_check_convert_type(proc, T_DATA, "Proc", "to_proc"); if (NIL_P(b) || !rb_obj_is_proc(b)) { rb_raise(rb_eTypeError, "wrong argument type %s (expected Proc)", rb_obj_classname(proc)); } proc = b; } GetProcPtr(proc, po); blockptr = &po->block; RUBY_VM_GET_BLOCK_PTR_IN_CFP(cfp)->proc = proc; *block = blockptr; } } else if (blockiseq) { blockptr = RUBY_VM_GET_BLOCK_PTR_IN_CFP(cfp); blockptr->iseq = blockiseq; blockptr->proc = 0; *block = blockptr; } } /* expand top of stack? */ if (flag & VM_CALL_ARGS_SPLAT_BIT) { VALUE ary = *(cfp->sp - 1); VALUE *ptr; int i; VALUE tmp = rb_check_convert_type(ary, T_ARRAY, "Array", "to_a"); if (NIL_P(tmp)) { /* do nothing */ } else { long len = RARRAY_LEN(tmp); ptr = RARRAY_PTR(tmp); cfp->sp -= 1; CHECK_STACK_OVERFLOW(cfp, len); for (i = 0; i < len; i++) { *cfp->sp++ = ptr[i]; } argc += i-1; } } return argc; }
static inline VALUE vm_call0(rb_thread_t * th, VALUE klass, VALUE recv, VALUE id, ID oid, int argc, const VALUE *argv, const NODE *body, int nosuper) { VALUE val; rb_block_t *blockptr = 0; if (0) printf("id: %s, nd: %s, argc: %d, passed: %p\n", rb_id2name(id), ruby_node_name(nd_type(body)), argc, (void *)th->passed_block); if (th->passed_block) { blockptr = th->passed_block; th->passed_block = 0; } again: switch (nd_type(body)) { case RUBY_VM_METHOD_NODE:{ rb_control_frame_t *reg_cfp; VALUE iseqval = (VALUE)body->nd_body; int i; rb_vm_set_finish_env(th); reg_cfp = th->cfp; CHECK_STACK_OVERFLOW(reg_cfp, argc + 1); *reg_cfp->sp++ = recv; for (i = 0; i < argc; i++) { *reg_cfp->sp++ = argv[i]; } vm_setup_method(th, reg_cfp, argc, blockptr, 0, iseqval, recv); val = vm_exec(th); break; } case NODE_CFUNC: { EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, id, klass); { rb_control_frame_t *reg_cfp = th->cfp; rb_control_frame_t *cfp = vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC, recv, (VALUE)blockptr, 0, reg_cfp->sp, 0, 1); cfp->method_id = oid; cfp->method_class = klass; val = call_cfunc(body->nd_cfnc, recv, body->nd_argc, argc, argv); if (reg_cfp != th->cfp + 1) { SDR2(reg_cfp); SDR2(th->cfp-5); rb_bug("cfp consistency error - call0"); th->cfp = reg_cfp; } vm_pop_frame(th); } EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, recv, id, klass); break; } case NODE_ATTRSET:{ if (argc != 1) { rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)", argc); } val = rb_ivar_set(recv, body->nd_vid, argv[0]); break; } case NODE_IVAR: { if (argc != 0) { rb_raise(rb_eArgError, "wrong number of arguments (%d for 0)", argc); } val = rb_attr_get(recv, body->nd_vid); break; } case NODE_BMETHOD:{ val = vm_call_bmethod(th, oid, body->nd_cval, recv, klass, argc, (VALUE *)argv, blockptr); break; } case NODE_ZSUPER:{ klass = RCLASS_SUPER(klass); if (!klass || !(body = rb_method_node(klass, id))) { return method_missing(recv, id, argc, argv, 0); } RUBY_VM_CHECK_INTS(); nosuper = CALL_SUPER; body = body->nd_body; goto again; } default: rb_bug("unsupported: vm_call0(%s)", ruby_node_name(nd_type(body))); } RUBY_VM_CHECK_INTS(); return val; }
/*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; }
static inline int vm_yield_setup_block_args(rb_thread_t *th, const rb_iseq_t * iseq, int orig_argc, VALUE *argv, const rb_block_t *blockptr) { int i; int argc = orig_argc; const int m = iseq->argc; VALUE ary, arg0; int opt_pc = 0; th->mark_stack_len = argc; /* * yield [1, 2] * => {|a|} => a = [1, 2] * => {|a, b|} => a, b = [1, 2] */ arg0 = argv[0]; if (!(iseq->arg_simple & 0x02) && /* exclude {|a|} */ (m + iseq->arg_post_len) > 0 && /* this process is meaningful */ argc == 1 && !NIL_P(ary = rb_check_array_type(arg0))) { /* rhs is only an array */ th->mark_stack_len = argc = RARRAY_LENINT(ary); CHECK_STACK_OVERFLOW(th->cfp, argc); MEMCPY(argv, RARRAY_PTR(ary), VALUE, argc); } else { argv[0] = arg0; } for (i=argc; i<m; i++) { argv[i] = Qnil; } if (iseq->arg_rest == -1 && iseq->arg_opts == 0) { const int arg_size = iseq->arg_size; if (arg_size < argc) { /* * yield 1, 2 * => {|a|} # truncate */ th->mark_stack_len = argc = arg_size; } } else { int r = iseq->arg_rest; if (iseq->arg_post_len || iseq->arg_opts) { /* TODO: implement simple version for (iseq->arg_post_len==0 && iseq->arg_opts > 0) */ opt_pc = vm_yield_setup_block_args_complex(th, iseq, argc, argv); } else { if (argc < r) { /* yield 1 * => {|a, b, *r|} */ for (i=argc; i<r; i++) { argv[i] = Qnil; } argv[r] = rb_ary_new(); } else { argv[r] = rb_ary_new4(argc-r, &argv[r]); } } th->mark_stack_len = iseq->arg_size; } /* {|&b|} */ if (iseq->arg_block != -1) { VALUE procval = Qnil; if (blockptr) { if (blockptr->proc == 0) { procval = rb_vm_make_proc(th, blockptr, rb_cProc); } else { procval = blockptr->proc; } } argv[iseq->arg_block] = procval; } th->mark_stack_len = 0; return opt_pc; }