diff options
Diffstat (limited to 'src/vm.c')
| -rw-r--r-- | src/vm.c | 235 |
1 files changed, 176 insertions, 59 deletions
@@ -40,6 +40,11 @@ void abort(void); #define MRB_STACK_GROWTH 128 #endif +/* Maximum mrb_funcall() depth. Should be set lower on memory constrained systems. */ +#ifndef MRB_FUNCALL_DEPTH_MAX +#define MRB_FUNCALL_DEPTH_MAX 512 +#endif + /* Maximum stack depth. Should be set lower on memory constrained systems. The value below allows about 60000 recursive calls in the simplest case. */ #ifndef MRB_STACK_MAX @@ -54,6 +59,10 @@ The value below allows about 60000 recursive calls in the simplest case. */ #define ARENA_RESTORE(mrb,ai) (mrb)->gc.arena_idx = (ai) +#define CALL_MAXARGS 127 + +void mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args); + static inline void stack_clear(mrb_value *from, size_t count) { @@ -130,9 +139,10 @@ static void stack_extend_alloc(mrb_state *mrb, int room, int keep) { mrb_value *oldbase = mrb->c->stbase; - int size = mrb->c->stend - mrb->c->stbase; - int off = mrb->c->stack - mrb->c->stbase; + size_t size = mrb->c->stend - mrb->c->stbase; + size_t off = mrb->c->stack - mrb->c->stbase; + if (off > size) size = off; #ifdef MRB_STACK_EXTEND_DOUBLING if (room <= size) size *= 2; @@ -157,13 +167,16 @@ stack_extend_alloc(mrb_state *mrb, int room, int keep) to prevent infinite recursion. However, do this only after resizing the stack, so mrb_raise has stack space to work with. */ if (size > MRB_STACK_MAX) { init_new_stack_space(mrb, room, keep); - mrb_raise(mrb, E_SYSSTACK_ERROR, "stack level too deep. (limit=" MRB_STRINGIZE(MRB_STACK_MAX) ")"); + mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err)); } } static inline void stack_extend(mrb_state *mrb, int room, int keep) { + if (room < keep) { + room = keep; + } if (mrb->c->stack + room >= mrb->c->stend) { stack_extend_alloc(mrb, room, keep); } @@ -234,6 +247,7 @@ cipush(mrb_state *mrb) ci->pc = 0; ci->err = 0; ci->proc = 0; + ci->acc = 0; return ci; } @@ -274,13 +288,17 @@ ecall(mrb_state *mrb, int i) mrb_callinfo *ci; mrb_value *self = mrb->c->stack; struct RObject *exc; + int cioff; + mrb_value *nstk; if (i<0) return; p = mrb->c->ensure[i]; if (!p) return; if (mrb->c->ci->eidx > i) mrb->c->ci->eidx = i; + cioff = mrb->c->ci - mrb->c->cibase; ci = cipush(mrb); + nstk = ci->stackent; ci->stackent = mrb->c->stack; ci->mid = ci[-1].mid; ci->acc = CI_ACC_SKIP; @@ -292,6 +310,8 @@ ecall(mrb_state *mrb, int i) exc = mrb->exc; mrb->exc = 0; mrb_run(mrb, p, *self); mrb->c->ensure[i] = NULL; + ci->stackent = nstk; + mrb->c->ci = mrb->c->cibase + cioff; if (!mrb->exc) mrb->exc = exc; } @@ -362,10 +382,17 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc c = mrb_class(mrb, self); p = mrb_method_search_vm(mrb, &c, mid); if (!p) { + mrb_sym missing = mrb_intern_lit(mrb, "method_missing"); + p = mrb_method_search_vm(mrb, &c, missing); + if (!p) { + mrb_value args = mrb_ary_new_from_values(mrb, argc, argv); + mrb_method_missing(mrb, mid, self, args); + } undef = mid; - mid = mrb_intern_lit(mrb, "method_missing"); - p = mrb_method_search_vm(mrb, &c, mid); - n++; argc++; + argc++; + } + if (mrb->c->ci - mrb->c->cibase > MRB_FUNCALL_DEPTH_MAX) { + mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err)); } ci = cipush(mrb); ci->mid = mid; @@ -381,8 +408,18 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc ci->nregs = argc + 2; stack_extend(mrb, ci->nregs, 0); } + else if (argc >= CALL_MAXARGS) { + mrb_value args = mrb_ary_new_from_values(mrb, argc, argv); + stack_extend(mrb, ci->nregs, 0); + mrb->c->stack[1] = args; + if (undef) { + mrb_ary_unshift(mrb, mrb->c->stack[1], mrb_symbol_value(undef)); + } + ci->argc = -1; + argc = 1; + } else { - ci->nregs = p->body.irep->nregs + n; + ci->nregs = p->body.irep->nregs + argc; stack_extend(mrb, ci->nregs, argc+2); } if (voff >= 0) { @@ -395,7 +432,7 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc stack_copy(mrb->c->stack+2, argv, argc-1); } } - else if (argc > 0) { + else if (ci->argc > 0) { stack_copy(mrb->c->stack+1, argv, argc); } mrb->c->stack[argc+1] = blk; @@ -424,6 +461,33 @@ mrb_funcall_argv(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc, cons return mrb_funcall_with_block(mrb, self, mid, argc, argv, mrb_nil_value()); } +mrb_value +mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p) +{ + mrb_callinfo *ci = mrb->c->ci; + + ci->proc = p; + if (MRB_PROC_CFUNC_P(p)) { + return p->body.func(mrb, self); + } + if (ci->argc < 0) { + stack_extend(mrb, (p->body.irep->nregs < 3) ? 3 : p->body.irep->nregs, 3); + } + else { + stack_extend(mrb, p->body.irep->nregs, ci->argc+2); + } + + ci->nregs = p->body.irep->nregs; + ci = cipush(mrb); + ci->nregs = 0; + ci->target_class = 0; + ci->pc = p->body.irep->iseq; + ci->stackent = mrb->c->stack; + ci->acc = 0; + + return self; +} + /* 15.3.1.3.4 */ /* 15.3.1.3.44 */ /* @@ -465,7 +529,6 @@ mrb_f_send(mrb_state *mrb, mrb_value self) ci = mrb->c->ci; ci->mid = name; ci->target_class = c; - ci->proc = p; regs = mrb->c->stack+1; /* remove first symbol from arguments */ if (ci->argc >= 0) { @@ -478,26 +541,7 @@ mrb_f_send(mrb_state *mrb, mrb_value self) mrb_ary_shift(mrb, regs[0]); } - if (MRB_PROC_CFUNC_P(p)) { - return p->body.func(mrb, self); - } - - if (ci->argc < 0) { - stack_extend(mrb, (p->body.irep->nregs < 3) ? 3 : p->body.irep->nregs, 3); - } - else { - stack_extend(mrb, p->body.irep->nregs, ci->argc+2); - } - - ci->nregs = p->body.irep->nregs; - ci = cipush(mrb); - ci->nregs = 0; - ci->target_class = 0; - ci->pc = p->body.irep->iseq; - ci->stackent = mrb->c->stack; - ci->acc = 0; - - return self; + return mrb_exec_irep(mrb, self, p); } static mrb_value @@ -646,11 +690,13 @@ mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value if (MRB_PROC_CFUNC_P(p)) { val = p->body.func(mrb, self); mrb->c->stack = mrb->c->ci->stackent; - cipop(mrb); } else { + int cioff = mrb->c->ci - mrb->c->cibase; val = mrb_run(mrb, p, self); + mrb->c->ci = mrb->c->cibase + cioff; } + cipop(mrb); return val; } @@ -697,15 +743,22 @@ argnum_error(mrb_state *mrb, mrb_int num) { mrb_value exc; mrb_value str; + mrb_int argc = mrb->c->ci->argc; + if (argc < 0) { + mrb_value args = mrb->c->stack[1]; + if (mrb_array_p(args)) { + argc = RARRAY_LEN(args); + } + } if (mrb->c->ci->mid) { str = mrb_format(mrb, "'%S': wrong number of arguments (%S for %S)", mrb_sym2str(mrb, mrb->c->ci->mid), - mrb_fixnum_value(mrb->c->ci->argc), mrb_fixnum_value(num)); + mrb_fixnum_value(argc), mrb_fixnum_value(num)); } else { str = mrb_format(mrb, "wrong number of arguments (%S for %S)", - mrb_fixnum_value(mrb->c->ci->argc), mrb_fixnum_value(num)); + mrb_fixnum_value(argc), mrb_fixnum_value(num)); } exc = mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str); mrb_exc_set(mrb, exc); @@ -749,10 +802,6 @@ argnum_error(mrb_state *mrb, mrb_int num) #endif -#define CALL_MAXARGS 127 - -void mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args); - MRB_API mrb_value mrb_vm_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int stack_keep) { @@ -831,7 +880,15 @@ RETRY_TRY_BLOCK: CASE(OP_LOADL) { /* A Bx R(A) := Pool(Bx) */ +#ifdef MRB_WORD_BOXING + mrb_value val = pool[GETARG_Bx(i)]; + if (mrb_float_p(val)) { + val = mrb_float_value(mrb, mrb_float(val)); + } + regs[GETARG_A(i)] = val; +#else regs[GETARG_A(i)] = pool[GETARG_Bx(i)]; +#endif NEXT; } @@ -1105,12 +1162,14 @@ RETRY_TRY_BLOCK: } if (GET_OPCODE(i) != OP_SENDB) { SET_NIL_VALUE(regs[bidx]); + bidx = 0; } else { mrb_value blk = regs[bidx]; if (!mrb_nil_p(blk) && mrb_type(blk) != MRB_TT_PROC) { regs[bidx] = mrb_convert_type(mrb, blk, MRB_TT_PROC, "Proc", "to_proc"); } + bidx = 1; } c = mrb_class(mrb, recv); m = mrb_method_search_vm(mrb, &c, mid); @@ -1131,12 +1190,17 @@ RETRY_TRY_BLOCK: mrb_method_missing(mrb, mid, recv, args); } mid = missing; + if (n == CALL_MAXARGS-1) { + regs[a+1] = mrb_ary_new_from_values(mrb, n, regs+a+1); + n++; + } if (n == CALL_MAXARGS) { mrb_ary_unshift(mrb, regs[a+1], sym); } else { - value_move(regs+a+2, regs+a+1, ++n); + value_move(regs+a+2, regs+a+1, n+bidx); regs[a+1] = sym; + n++; } } @@ -1263,7 +1327,9 @@ RETRY_TRY_BLOCK: else { stack_extend(mrb, irep->nregs, ci->argc+2); } - regs[0] = m->env->stack[0]; + if(m->env) { + regs[0] = m->env->stack[0]; + } pc = irep->iseq; JUMP; } @@ -1279,7 +1345,7 @@ RETRY_TRY_BLOCK: int a = GETARG_A(i); int n = GETARG_C(i); - if (mid == 0) { + if (mid == 0 || !mrb->c->ci->target_class) { mrb_value exc; exc = mrb_exc_new_str_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method"); @@ -1290,8 +1356,24 @@ RETRY_TRY_BLOCK: c = mrb->c->ci->target_class->super; m = mrb_method_search_vm(mrb, &c, mid); if (!m) { - mid = mrb_intern_lit(mrb, "method_missing"); - m = mrb_method_search_vm(mrb, &c, mid); + mrb_sym missing = mrb_intern_lit(mrb, "method_missing"); + m = mrb_method_search_vm(mrb, &c, missing); + if (!m) { + mrb_value args; + + if (n == CALL_MAXARGS) { + args = regs[a+1]; + } + else { + args = mrb_ary_new_from_values(mrb, n, regs+a+1); + } + mrb_method_missing(mrb, mid, recv, args); + } + mid = missing; + if (n == CALL_MAXARGS-1) { + regs[a+1] = mrb_ary_new_from_values(mrb, n, regs+a+1); + n++; + } if (n == CALL_MAXARGS) { mrb_ary_unshift(mrb, regs[a+1], mrb_symbol_value(ci->mid)); } @@ -1306,11 +1388,22 @@ RETRY_TRY_BLOCK: ci->mid = mid; ci->proc = m; ci->stackent = mrb->c->stack; - if (n == CALL_MAXARGS) { - ci->argc = -1; - } - else { - ci->argc = n; + { + int bidx; + mrb_value blk; + + if (n == CALL_MAXARGS) { + ci->argc = -1; + bidx = a+2; + } + else { + ci->argc = n; + bidx = a+n+1; + } + blk = regs[bidx]; + if (!mrb_nil_p(blk) && mrb_type(blk) != MRB_TT_PROC) { + regs[bidx] = mrb_convert_type(mrb, blk, MRB_TT_PROC, "Proc", "to_proc"); + } } ci->target_class = c; ci->pc = pc + 1; @@ -1578,7 +1671,7 @@ RETRY_TRY_BLOCK: switch (GETARG_B(i)) { case OP_R_RETURN: /* Fall through to OP_R_NORMAL otherwise */ - if (proc->env && !MRB_PROC_STRICT_P(proc)) { + if (ci->acc >=0 && proc->env && !MRB_PROC_STRICT_P(proc)) { struct REnv *e = top_env(mrb, proc); if (!MRB_ENV_STACK_SHARED_P(e)) { @@ -1647,10 +1740,11 @@ RETRY_TRY_BLOCK: mrb->jmp = prev_jmp; return v; } - cipop(mrb); + ci = mrb->c->ci; acc = ci->acc; mrb->c->stack = ci->stackent; - if (acc == CI_ACC_SKIP) { + cipop(mrb); + if (acc == CI_ACC_SKIP || acc == CI_ACC_DIRECT) { mrb->jmp = prev_jmp; return v; } @@ -1681,9 +1775,20 @@ RETRY_TRY_BLOCK: m = mrb_method_search_vm(mrb, &c, mid); if (!m) { mrb_value sym = mrb_symbol_value(mid); + mrb_sym missing = mrb_intern_lit(mrb, "method_missing"); + m = mrb_method_search_vm(mrb, &c, missing); + if (!m) { + mrb_value args; - mid = mrb_intern_lit(mrb, "method_missing"); - m = mrb_method_search_vm(mrb, &c, mid); + if (n == CALL_MAXARGS) { + args = regs[a+1]; + } + else { + args = mrb_ary_new_from_values(mrb, n, regs+a+1); + } + mrb_method_missing(mrb, mid, recv, args); + } + mid = missing; if (n == CALL_MAXARGS) { mrb_ary_unshift(mrb, regs[a+1], sym); } @@ -2134,8 +2239,8 @@ RETRY_TRY_BLOCK: CASE(OP_ARYCAT) { /* A B mrb_ary_concat(R(A),R(B)) */ - mrb_ary_concat(mrb, regs[GETARG_A(i)], - mrb_ary_splat(mrb, regs[GETARG_B(i)])); + mrb_value splat = mrb_ary_splat(mrb, regs[GETARG_B(i)]); + mrb_ary_concat(mrb, regs[GETARG_A(i)], splat); ARENA_RESTORE(mrb, ai); NEXT; } @@ -2261,7 +2366,7 @@ RETRY_TRY_BLOCK: CASE(OP_CLASS) { /* A B R(A) := newclass(R(A),Syms(B),R(A+1)) */ - struct RClass *c = 0; + struct RClass *c = 0, *baseclass; int a = GETARG_A(i); mrb_value base, super; mrb_sym id = syms[GETARG_B(i)]; @@ -2269,7 +2374,10 @@ RETRY_TRY_BLOCK: base = regs[a]; super = regs[a+1]; if (mrb_nil_p(base)) { - base = mrb_obj_value(mrb->c->ci->target_class); + baseclass = mrb->c->ci->proc->target_class; + if (!baseclass) baseclass = mrb->c->ci->target_class; + + base = mrb_obj_value(baseclass); } c = mrb_vm_define_class(mrb, base, super, id); regs[a] = mrb_obj_value(c); @@ -2279,14 +2387,17 @@ RETRY_TRY_BLOCK: CASE(OP_MODULE) { /* A B R(A) := newmodule(R(A),Syms(B)) */ - struct RClass *c = 0; + struct RClass *c = 0, *baseclass; int a = GETARG_A(i); mrb_value base; mrb_sym id = syms[GETARG_B(i)]; base = regs[a]; if (mrb_nil_p(base)) { - base = mrb_obj_value(mrb->c->ci->target_class); + baseclass = mrb->c->ci->proc->target_class; + if (!baseclass) baseclass = mrb->c->ci->target_class; + + base = mrb_obj_value(baseclass); } c = mrb_vm_define_module(mrb, base, id); regs[a] = mrb_obj_value(c); @@ -2373,7 +2484,8 @@ RETRY_TRY_BLOCK: CASE(OP_RANGE) { /* A B C R(A) := range_new(R(B),R(B+1),C) */ int b = GETARG_B(i); - regs[GETARG_A(i)] = mrb_range_new(mrb, regs[b], regs[b+1], GETARG_C(i)); + mrb_value val = mrb_range_new(mrb, regs[b], regs[b+1], GETARG_C(i)); + regs[GETARG_A(i)] = val; ARENA_RESTORE(mrb, ai); NEXT; } @@ -2439,7 +2551,12 @@ RETRY_TRY_BLOCK: MRB_API mrb_value mrb_run(mrb_state *mrb, struct RProc *proc, mrb_value self) { - return mrb_vm_run(mrb, proc, self, mrb->c->ci->argc + 2); /* argc + 2 (receiver and block) */ + if (mrb->c->ci->argc < 0) { + return mrb_vm_run(mrb, proc, self, 3); /* receiver, args and block) */ + } + else { + return mrb_vm_run(mrb, proc, self, mrb->c->ci->argc + 2); /* argc + 2 (receiver and block) */ + } } MRB_API mrb_value |
