Files
mruby-mruby/src/vm.c
T
Kouhei Sutou a561bdb25f Support backtrace after method calls
GitHub: fix #2902, #2917

The current implementation traverses stack to retrieve backtrace. But
stack will be changed when some operations are occurred. It means that
backtrace may be broken after some operations.

This change (1) saves the minimum information to retrieve backtrace when
exception is raised and (2) restores backtrace from the minimum
information when backtrace is needed. It reduces overhead for creating
backtrace Ruby objects.

The space for the minimum information is reused by multiple
exceptions. So memory allocation isn't occurred for each exception.
2015-12-29 20:36:12 +09:00

2430 lines
61 KiB
C

/*
** vm.c - virtual machine for mruby
**
** See Copyright Notice in mruby.h
*/
#include <stddef.h>
#include <stdarg.h>
#include <math.h>
#include <mruby.h>
#include <mruby/array.h>
#include <mruby/class.h>
#include <mruby/hash.h>
#include <mruby/irep.h>
#include <mruby/numeric.h>
#include <mruby/proc.h>
#include <mruby/range.h>
#include <mruby/string.h>
#include <mruby/variable.h>
#include <mruby/error.h>
#include <mruby/opcode.h>
#include "value_array.h"
#include <mruby/throw.h>
#ifndef MRB_DISABLE_STDIO
#if defined(__cplusplus)
extern "C" {
#endif
void abort(void);
#if defined(__cplusplus)
} /* extern "C" { */
#endif
#endif
#define STACK_INIT_SIZE 128
#define CALLINFO_INIT_SIZE 32
/* Define amount of linear stack growth. */
#ifndef MRB_STACK_GROWTH
#define MRB_STACK_GROWTH 128
#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
#define MRB_STACK_MAX (0x40000 - MRB_STACK_GROWTH)
#endif
#ifdef VM_DEBUG
# define DEBUG(x) (x)
#else
# define DEBUG(x)
#endif
#define ARENA_RESTORE(mrb,ai) (mrb)->gc.arena_idx = (ai)
static inline void
stack_clear(mrb_value *from, size_t count)
{
#ifndef MRB_NAN_BOXING
const mrb_value mrb_value_zero = { { 0 } };
while (count-- > 0) {
*from++ = mrb_value_zero;
}
#else
while (count-- > 0) {
SET_NIL_VALUE(*from);
from++;
}
#endif
}
static inline void
stack_copy(mrb_value *dst, const mrb_value *src, size_t size)
{
while (size-- > 0) {
*dst++ = *src++;
}
}
static void
stack_init(mrb_state *mrb)
{
struct mrb_context *c = mrb->c;
/* mrb_assert(mrb->stack == NULL); */
c->stbase = (mrb_value *)mrb_calloc(mrb, STACK_INIT_SIZE, sizeof(mrb_value));
c->stend = c->stbase + STACK_INIT_SIZE;
c->stack = c->stbase;
/* mrb_assert(ci == NULL); */
c->cibase = (mrb_callinfo *)mrb_calloc(mrb, CALLINFO_INIT_SIZE, sizeof(mrb_callinfo));
c->ciend = c->cibase + CALLINFO_INIT_SIZE;
c->ci = c->cibase;
c->ci->target_class = mrb->object_class;
c->ci->stackent = c->stack;
}
static inline void
envadjust(mrb_state *mrb, mrb_value *oldbase, mrb_value *newbase)
{
mrb_callinfo *ci = mrb->c->cibase;
if (newbase == oldbase) return;
while (ci <= mrb->c->ci) {
struct REnv *e = ci->env;
if (e && MRB_ENV_STACK_SHARED_P(e)) {
ptrdiff_t off = e->stack - oldbase;
e->stack = newbase + off;
}
ci->stackent = newbase + (ci->stackent - oldbase);
ci++;
}
}
static inline void
init_new_stack_space(mrb_state *mrb, int room, int keep)
{
if (room > keep) {
/* do not leave uninitialized malloc region */
stack_clear(&(mrb->c->stack[keep]), room - keep);
}
}
/** def rec ; $deep =+ 1 ; if $deep > 1000 ; return 0 ; end ; rec ; end */
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;
#ifdef MRB_STACK_EXTEND_DOUBLING
if (room <= size)
size *= 2;
else
size += room;
#else
/* Use linear stack growth.
It is slightly slower than doubling the stack space,
but it saves memory on small devices. */
if (room <= MRB_STACK_GROWTH)
size += MRB_STACK_GROWTH;
else
size += room;
#endif
mrb->c->stbase = (mrb_value *)mrb_realloc(mrb, mrb->c->stbase, sizeof(mrb_value) * size);
mrb->c->stack = mrb->c->stbase + off;
mrb->c->stend = mrb->c->stbase + size;
envadjust(mrb, oldbase, mrb->c->stbase);
/* Raise an exception if the new stack size will be too large,
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) ")");
}
}
static inline void
stack_extend(mrb_state *mrb, int room, int keep)
{
if (mrb->c->stack + room >= mrb->c->stend) {
stack_extend_alloc(mrb, room, keep);
}
init_new_stack_space(mrb, room, keep);
}
static inline struct REnv*
uvenv(mrb_state *mrb, int up)
{
struct REnv *e = mrb->c->ci->proc->env;
while (up--) {
if (!e) return NULL;
e = (struct REnv*)e->c;
}
return e;
}
static inline mrb_bool
is_strict(mrb_state *mrb, struct REnv *e)
{
int cioff = e->cioff;
if (MRB_ENV_STACK_SHARED_P(e) && mrb->c->cibase[cioff].proc &&
MRB_PROC_STRICT_P(mrb->c->cibase[cioff].proc)) {
return TRUE;
}
return FALSE;
}
static inline struct REnv*
top_env(mrb_state *mrb, struct RProc *proc)
{
struct REnv *e = proc->env;
if (is_strict(mrb, e)) return e;
while (e->c) {
e = (struct REnv*)e->c;
if (is_strict(mrb, e)) return e;
}
return e;
}
#define CI_ACC_SKIP -1
#define CI_ACC_DIRECT -2
static mrb_callinfo*
cipush(mrb_state *mrb)
{
struct mrb_context *c = mrb->c;
mrb_callinfo *ci = c->ci;
int eidx = ci->eidx;
int ridx = ci->ridx;
if (ci + 1 == c->ciend) {
ptrdiff_t size = ci - c->cibase;
c->cibase = (mrb_callinfo *)mrb_realloc(mrb, c->cibase, sizeof(mrb_callinfo)*size*2);
c->ci = c->cibase + size;
c->ciend = c->cibase + size * 2;
}
ci = ++c->ci;
ci->eidx = eidx;
ci->ridx = ridx;
ci->env = 0;
ci->pc = 0;
ci->err = 0;
ci->proc = 0;
return ci;
}
static void
cipop(mrb_state *mrb)
{
struct mrb_context *c = mrb->c;
if (c->ci->env) {
struct REnv *e = c->ci->env;
size_t len = (size_t)MRB_ENV_STACK_LEN(e);
mrb_value *p = (mrb_value *)mrb_malloc(mrb, sizeof(mrb_value)*len);
MRB_ENV_UNSHARE_STACK(e);
if (len > 0) {
stack_copy(p, e->stack, len);
}
e->stack = p;
mrb_write_barrier(mrb, (struct RBasic *)e);
}
c->ci--;
}
void mrb_exc_set(mrb_state *mrb, mrb_value exc);
static void
ecall(mrb_state *mrb, int i)
{
struct RProc *p;
mrb_callinfo *ci;
mrb_value *self = mrb->c->stack;
struct RObject *exc;
if (i<0) return;
p = mrb->c->ensure[i];
if (!p) return;
if (mrb->c->ci->eidx > i)
mrb->c->ci->eidx = i;
ci = cipush(mrb);
ci->stackent = mrb->c->stack;
ci->mid = ci[-1].mid;
ci->acc = CI_ACC_SKIP;
ci->argc = 0;
ci->proc = p;
ci->nregs = p->body.irep->nregs;
ci->target_class = p->target_class;
mrb->c->stack = mrb->c->stack + ci[-1].nregs;
exc = mrb->exc; mrb->exc = 0;
mrb_run(mrb, p, *self);
mrb->c->ensure[i] = NULL;
if (!mrb->exc) mrb->exc = exc;
}
#ifndef MRB_FUNCALL_ARGC_MAX
#define MRB_FUNCALL_ARGC_MAX 16
#endif
MRB_API mrb_value
mrb_funcall(mrb_state *mrb, mrb_value self, const char *name, mrb_int argc, ...)
{
mrb_value argv[MRB_FUNCALL_ARGC_MAX];
va_list ap;
mrb_int i;
mrb_sym mid = mrb_intern_cstr(mrb, name);
if (argc > MRB_FUNCALL_ARGC_MAX) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "Too long arguments. (limit=" MRB_STRINGIZE(MRB_FUNCALL_ARGC_MAX) ")");
}
va_start(ap, argc);
for (i = 0; i < argc; i++) {
argv[i] = va_arg(ap, mrb_value);
}
va_end(ap);
return mrb_funcall_argv(mrb, self, mid, argc, argv);
}
MRB_API mrb_value
mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc, const mrb_value *argv, mrb_value blk)
{
mrb_value val;
if (!mrb->jmp) {
struct mrb_jmpbuf c_jmp;
ptrdiff_t nth_ci = mrb->c->ci - mrb->c->cibase;
MRB_TRY(&c_jmp) {
mrb->jmp = &c_jmp;
/* recursive call */
val = mrb_funcall_with_block(mrb, self, mid, argc, argv, blk);
mrb->jmp = 0;
}
MRB_CATCH(&c_jmp) { /* error */
while (nth_ci < (mrb->c->ci - mrb->c->cibase)) {
mrb->c->stack = mrb->c->ci->stackent;
cipop(mrb);
}
mrb->jmp = 0;
val = mrb_obj_value(mrb->exc);
}
MRB_END_EXC(&c_jmp);
}
else {
struct RProc *p;
struct RClass *c;
mrb_sym undef = 0;
mrb_callinfo *ci;
int n;
ptrdiff_t voff = -1;
if (!mrb->c->stack) {
stack_init(mrb);
}
n = mrb->c->ci->nregs;
if (argc < 0) {
mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative argc for funcall (%S)", mrb_fixnum_value(argc));
}
c = mrb_class(mrb, self);
p = mrb_method_search_vm(mrb, &c, mid);
if (!p) {
undef = mid;
mid = mrb_intern_lit(mrb, "method_missing");
p = mrb_method_search_vm(mrb, &c, mid);
n++; argc++;
}
ci = cipush(mrb);
ci->mid = mid;
ci->proc = p;
ci->stackent = mrb->c->stack;
ci->argc = argc;
ci->target_class = c;
mrb->c->stack = mrb->c->stack + n;
if (mrb->c->stbase <= argv && argv < mrb->c->stend) {
voff = argv - mrb->c->stbase;
}
if (MRB_PROC_CFUNC_P(p)) {
ci->nregs = argc + 2;
stack_extend(mrb, ci->nregs, 0);
}
else {
ci->nregs = p->body.irep->nregs + n;
stack_extend(mrb, ci->nregs, argc+2);
}
if (voff >= 0) {
argv = mrb->c->stbase + voff;
}
mrb->c->stack[0] = self;
if (undef) {
mrb->c->stack[1] = mrb_symbol_value(undef);
if (argc > 1) {
stack_copy(mrb->c->stack+2, argv, argc-1);
}
}
else if (argc > 0) {
stack_copy(mrb->c->stack+1, argv, argc);
}
mrb->c->stack[argc+1] = blk;
if (MRB_PROC_CFUNC_P(p)) {
int ai = mrb_gc_arena_save(mrb);
ci->acc = CI_ACC_DIRECT;
val = p->body.func(mrb, self);
mrb->c->stack = mrb->c->ci->stackent;
cipop(mrb);
mrb_gc_arena_restore(mrb, ai);
}
else {
ci->acc = CI_ACC_SKIP;
val = mrb_run(mrb, p, self);
}
}
mrb_gc_protect(mrb, val);
return val;
}
MRB_API mrb_value
mrb_funcall_argv(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc, const mrb_value *argv)
{
return mrb_funcall_with_block(mrb, self, mid, argc, argv, mrb_nil_value());
}
/* 15.3.1.3.4 */
/* 15.3.1.3.44 */
/*
* call-seq:
* obj.send(symbol [, args...]) -> obj
* obj.__send__(symbol [, args...]) -> obj
*
* Invokes the method identified by _symbol_, passing it any
* arguments specified. You can use <code>__send__</code> if the name
* +send+ clashes with an existing method in _obj_.
*
* class Klass
* def hello(*args)
* "Hello " + args.join(' ')
* end
* end
* k = Klass.new
* k.send :hello, "gentle", "readers" #=> "Hello gentle readers"
*/
MRB_API mrb_value
mrb_f_send(mrb_state *mrb, mrb_value self)
{
mrb_sym name;
mrb_value block, *argv, *regs;
mrb_int argc, i, len;
struct RProc *p;
struct RClass *c;
mrb_callinfo *ci;
mrb_get_args(mrb, "n*&", &name, &argv, &argc, &block);
c = mrb_class(mrb, self);
p = mrb_method_search_vm(mrb, &c, name);
if (!p) { /* call method_mising */
return mrb_funcall_with_block(mrb, self, name, argc, argv, block);
}
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) {
for (i=0,len=ci->argc; i<len; i++) {
regs[i] = regs[i+1];
}
ci->argc--;
}
else { /* variable length arguments */
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;
}
static mrb_value
eval_under(mrb_state *mrb, mrb_value self, mrb_value blk, struct RClass *c)
{
struct RProc *p;
mrb_callinfo *ci;
if (mrb_nil_p(blk)) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
}
ci = mrb->c->ci;
if (ci->acc == CI_ACC_DIRECT) {
return mrb_yield_with_class(mrb, blk, 0, 0, self, c);
}
ci->target_class = c;
p = mrb_proc_ptr(blk);
ci->proc = p;
if (MRB_PROC_CFUNC_P(p)) {
return p->body.func(mrb, self);
}
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.2.2.4.35 */
/*
* call-seq:
* mod.class_eval {| | block } -> obj
* mod.module_eval {| | block } -> obj
*
* Evaluates block in the context of _mod_. This can
* be used to add methods to a class. <code>module_eval</code> returns
* the result of evaluating its argument.
*/
mrb_value
mrb_mod_module_eval(mrb_state *mrb, mrb_value mod)
{
mrb_value a, b;
if (mrb_get_args(mrb, "|S&", &a, &b) == 1) {
mrb_raise(mrb, E_NOTIMP_ERROR, "module_eval/class_eval with string not implemented");
}
return eval_under(mrb, mod, b, mrb_class_ptr(mod));
}
/* 15.3.1.3.18 */
/*
* call-seq:
* obj.instance_eval {| | block } -> obj
*
* Evaluates the given block,within the context of the receiver (_obj_).
* In order to set the context, the variable +self+ is set to _obj_ while
* the code is executing, giving the code access to _obj_'s
* instance variables. In the version of <code>instance_eval</code>
* that takes a +String+, the optional second and third
* parameters supply a filename and starting line number that are used
* when reporting compilation errors.
*
* class KlassWithSecret
* def initialize
* @secret = 99
* end
* end
* k = KlassWithSecret.new
* k.instance_eval { @secret } #=> 99
*/
mrb_value
mrb_obj_instance_eval(mrb_state *mrb, mrb_value self)
{
mrb_value a, b;
mrb_value cv;
struct RClass *c;
if (mrb_get_args(mrb, "|S&", &a, &b) == 1) {
mrb_raise(mrb, E_NOTIMP_ERROR, "instance_eval with string not implemented");
}
switch (mrb_type(self)) {
case MRB_TT_SYMBOL:
case MRB_TT_FIXNUM:
case MRB_TT_FLOAT:
c = 0;
break;
default:
cv = mrb_singleton_class(mrb, self);
c = mrb_class_ptr(cv);
break;
}
return eval_under(mrb, self, b, c);
}
MRB_API mrb_value
mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv, mrb_value self, struct RClass *c)
{
struct RProc *p;
mrb_sym mid = mrb->c->ci->mid;
mrb_callinfo *ci;
int n = mrb->c->ci->nregs;
mrb_value val;
if (mrb_nil_p(b)) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
}
p = mrb_proc_ptr(b);
ci = cipush(mrb);
ci->mid = mid;
ci->proc = p;
ci->stackent = mrb->c->stack;
ci->argc = argc;
ci->target_class = c;
ci->acc = CI_ACC_SKIP;
mrb->c->stack = mrb->c->stack + n;
if (MRB_PROC_CFUNC_P(p)) {
ci->nregs = argc + 2;
stack_extend(mrb, ci->nregs, 0);
}
else {
ci->nregs = p->body.irep->nregs;
stack_extend(mrb, ci->nregs, argc+2);
}
mrb->c->stack[0] = self;
if (argc > 0) {
stack_copy(mrb->c->stack+1, argv, argc);
}
mrb->c->stack[argc+1] = mrb_nil_value();
if (MRB_PROC_CFUNC_P(p)) {
val = p->body.func(mrb, self);
mrb->c->stack = mrb->c->ci->stackent;
cipop(mrb);
}
else {
val = mrb_run(mrb, p, self);
}
return val;
}
MRB_API mrb_value
mrb_yield_argv(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv)
{
struct RProc *p = mrb_proc_ptr(b);
return mrb_yield_with_class(mrb, b, argc, argv, p->env->stack[0], p->target_class);
}
MRB_API mrb_value
mrb_yield(mrb_state *mrb, mrb_value b, mrb_value arg)
{
struct RProc *p = mrb_proc_ptr(b);
return mrb_yield_with_class(mrb, b, 1, &arg, p->env->stack[0], p->target_class);
}
typedef enum {
LOCALJUMP_ERROR_RETURN = 0,
LOCALJUMP_ERROR_BREAK = 1,
LOCALJUMP_ERROR_YIELD = 2
} localjump_error_kind;
static void
localjump_error(mrb_state *mrb, localjump_error_kind kind)
{
char kind_str[3][7] = { "return", "break", "yield" };
char kind_str_len[] = { 6, 5, 5 };
static const char lead[] = "unexpected ";
mrb_value msg;
mrb_value exc;
msg = mrb_str_buf_new(mrb, sizeof(lead) + 7);
mrb_str_cat(mrb, msg, lead, sizeof(lead) - 1);
mrb_str_cat(mrb, msg, kind_str[kind], kind_str_len[kind]);
exc = mrb_exc_new_str(mrb, E_LOCALJUMP_ERROR, msg);
mrb_exc_set(mrb, exc);
}
static void
argnum_error(mrb_state *mrb, mrb_int num)
{
mrb_value exc;
mrb_value str;
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));
}
else {
str = mrb_format(mrb, "wrong number of arguments (%S for %S)",
mrb_fixnum_value(mrb->c->ci->argc), mrb_fixnum_value(num));
}
exc = mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str);
mrb_exc_set(mrb, exc);
}
#define ERR_PC_SET(mrb, pc) mrb->c->ci->err = pc;
#define ERR_PC_CLR(mrb) mrb->c->ci->err = 0;
#ifdef MRB_ENABLE_DEBUG_HOOK
#define CODE_FETCH_HOOK(mrb, irep, pc, regs) if ((mrb)->code_fetch_hook) (mrb)->code_fetch_hook((mrb), (irep), (pc), (regs));
#else
#define CODE_FETCH_HOOK(mrb, irep, pc, regs)
#endif
#if defined __GNUC__ || defined __clang__ || defined __INTEL_COMPILER
#define DIRECT_THREADED
#endif
#ifndef DIRECT_THREADED
#define INIT_DISPATCH for (;;) { i = *pc; CODE_FETCH_HOOK(mrb, irep, pc, regs); switch (GET_OPCODE(i)) {
#define CASE(op) case op:
#define NEXT pc++; break
#define JUMP break
#define END_DISPATCH }}
#else
#define INIT_DISPATCH JUMP; return mrb_nil_value();
#define CASE(op) L_ ## op:
#define NEXT i=*++pc; CODE_FETCH_HOOK(mrb, irep, pc, regs); goto *optable[GET_OPCODE(i)]
#define JUMP i=*pc; CODE_FETCH_HOOK(mrb, irep, pc, regs); goto *optable[GET_OPCODE(i)]
#define END_DISPATCH
#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_context_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int stack_keep)
{
/* mrb_assert(mrb_proc_cfunc_p(proc)) */
mrb_irep *irep = proc->body.irep;
mrb_code *pc = irep->iseq;
mrb_value *pool = irep->pool;
mrb_sym *syms = irep->syms;
mrb_value *regs = NULL;
mrb_code i;
int ai = mrb_gc_arena_save(mrb);
struct mrb_jmpbuf *prev_jmp = mrb->jmp;
struct mrb_jmpbuf c_jmp;
#ifdef DIRECT_THREADED
static void *optable[] = {
&&L_OP_NOP, &&L_OP_MOVE,
&&L_OP_LOADL, &&L_OP_LOADI, &&L_OP_LOADSYM, &&L_OP_LOADNIL,
&&L_OP_LOADSELF, &&L_OP_LOADT, &&L_OP_LOADF,
&&L_OP_GETGLOBAL, &&L_OP_SETGLOBAL, &&L_OP_GETSPECIAL, &&L_OP_SETSPECIAL,
&&L_OP_GETIV, &&L_OP_SETIV, &&L_OP_GETCV, &&L_OP_SETCV,
&&L_OP_GETCONST, &&L_OP_SETCONST, &&L_OP_GETMCNST, &&L_OP_SETMCNST,
&&L_OP_GETUPVAR, &&L_OP_SETUPVAR,
&&L_OP_JMP, &&L_OP_JMPIF, &&L_OP_JMPNOT,
&&L_OP_ONERR, &&L_OP_RESCUE, &&L_OP_POPERR, &&L_OP_RAISE, &&L_OP_EPUSH, &&L_OP_EPOP,
&&L_OP_SEND, &&L_OP_SENDB, &&L_OP_FSEND,
&&L_OP_CALL, &&L_OP_SUPER, &&L_OP_ARGARY, &&L_OP_ENTER,
&&L_OP_KARG, &&L_OP_KDICT, &&L_OP_RETURN, &&L_OP_TAILCALL, &&L_OP_BLKPUSH,
&&L_OP_ADD, &&L_OP_ADDI, &&L_OP_SUB, &&L_OP_SUBI, &&L_OP_MUL, &&L_OP_DIV,
&&L_OP_EQ, &&L_OP_LT, &&L_OP_LE, &&L_OP_GT, &&L_OP_GE,
&&L_OP_ARRAY, &&L_OP_ARYCAT, &&L_OP_ARYPUSH, &&L_OP_AREF, &&L_OP_ASET, &&L_OP_APOST,
&&L_OP_STRING, &&L_OP_STRCAT, &&L_OP_HASH,
&&L_OP_LAMBDA, &&L_OP_RANGE, &&L_OP_OCLASS,
&&L_OP_CLASS, &&L_OP_MODULE, &&L_OP_EXEC,
&&L_OP_METHOD, &&L_OP_SCLASS, &&L_OP_TCLASS,
&&L_OP_DEBUG, &&L_OP_STOP, &&L_OP_ERR,
};
#endif
mrb_bool exc_catched = FALSE;
RETRY_TRY_BLOCK:
MRB_TRY(&c_jmp) {
if (exc_catched) {
exc_catched = FALSE;
goto L_RAISE;
}
mrb->jmp = &c_jmp;
if (!mrb->c->stack) {
stack_init(mrb);
}
stack_extend(mrb, irep->nregs, stack_keep);
mrb->c->ci->proc = proc;
mrb->c->ci->nregs = irep->nregs;
regs = mrb->c->stack;
regs[0] = self;
INIT_DISPATCH {
CASE(OP_NOP) {
/* do nothing */
NEXT;
}
CASE(OP_MOVE) {
/* A B R(A) := R(B) */
regs[GETARG_A(i)] = regs[GETARG_B(i)];
NEXT;
}
CASE(OP_LOADL) {
/* A Bx R(A) := Pool(Bx) */
regs[GETARG_A(i)] = pool[GETARG_Bx(i)];
NEXT;
}
CASE(OP_LOADI) {
/* A sBx R(A) := sBx */
SET_INT_VALUE(regs[GETARG_A(i)], GETARG_sBx(i));
NEXT;
}
CASE(OP_LOADSYM) {
/* A Bx R(A) := Syms(Bx) */
SET_SYM_VALUE(regs[GETARG_A(i)], syms[GETARG_Bx(i)]);
NEXT;
}
CASE(OP_LOADSELF) {
/* A R(A) := self */
regs[GETARG_A(i)] = regs[0];
NEXT;
}
CASE(OP_LOADT) {
/* A R(A) := true */
SET_TRUE_VALUE(regs[GETARG_A(i)]);
NEXT;
}
CASE(OP_LOADF) {
/* A R(A) := false */
SET_FALSE_VALUE(regs[GETARG_A(i)]);
NEXT;
}
CASE(OP_GETGLOBAL) {
/* A Bx R(A) := getglobal(Syms(Bx)) */
regs[GETARG_A(i)] = mrb_gv_get(mrb, syms[GETARG_Bx(i)]);
NEXT;
}
CASE(OP_SETGLOBAL) {
/* setglobal(Syms(Bx), R(A)) */
mrb_gv_set(mrb, syms[GETARG_Bx(i)], regs[GETARG_A(i)]);
NEXT;
}
CASE(OP_GETSPECIAL) {
/* A Bx R(A) := Special[Bx] */
regs[GETARG_A(i)] = mrb_vm_special_get(mrb, GETARG_Bx(i));
NEXT;
}
CASE(OP_SETSPECIAL) {
/* A Bx Special[Bx] := R(A) */
mrb_vm_special_set(mrb, GETARG_Bx(i), regs[GETARG_A(i)]);
NEXT;
}
CASE(OP_GETIV) {
/* A Bx R(A) := ivget(Bx) */
regs[GETARG_A(i)] = mrb_vm_iv_get(mrb, syms[GETARG_Bx(i)]);
NEXT;
}
CASE(OP_SETIV) {
/* ivset(Syms(Bx),R(A)) */
mrb_vm_iv_set(mrb, syms[GETARG_Bx(i)], regs[GETARG_A(i)]);
NEXT;
}
CASE(OP_GETCV) {
/* A Bx R(A) := cvget(Syms(Bx)) */
ERR_PC_SET(mrb, pc);
regs[GETARG_A(i)] = mrb_vm_cv_get(mrb, syms[GETARG_Bx(i)]);
ERR_PC_CLR(mrb);
NEXT;
}
CASE(OP_SETCV) {
/* cvset(Syms(Bx),R(A)) */
mrb_vm_cv_set(mrb, syms[GETARG_Bx(i)], regs[GETARG_A(i)]);
NEXT;
}
CASE(OP_GETCONST) {
/* A Bx R(A) := constget(Syms(Bx)) */
mrb_value val;
ERR_PC_SET(mrb, pc);
val = mrb_vm_const_get(mrb, syms[GETARG_Bx(i)]);
ERR_PC_CLR(mrb);
regs = mrb->c->stack;
regs[GETARG_A(i)] = val;
NEXT;
}
CASE(OP_SETCONST) {
/* A Bx constset(Syms(Bx),R(A)) */
mrb_vm_const_set(mrb, syms[GETARG_Bx(i)], regs[GETARG_A(i)]);
NEXT;
}
CASE(OP_GETMCNST) {
/* A Bx R(A) := R(A)::Syms(Bx) */
mrb_value val;
int a = GETARG_A(i);
ERR_PC_SET(mrb, pc);
val = mrb_const_get(mrb, regs[a], syms[GETARG_Bx(i)]);
ERR_PC_CLR(mrb);
regs = mrb->c->stack;
regs[a] = val;
NEXT;
}
CASE(OP_SETMCNST) {
/* A Bx R(A+1)::Syms(Bx) := R(A) */
int a = GETARG_A(i);
mrb_const_set(mrb, regs[a+1], syms[GETARG_Bx(i)], regs[a]);
NEXT;
}
CASE(OP_GETUPVAR) {
/* A B C R(A) := uvget(B,C) */
mrb_value *regs_a = regs + GETARG_A(i);
int up = GETARG_C(i);
struct REnv *e = uvenv(mrb, up);
if (!e) {
*regs_a = mrb_nil_value();
}
else {
int idx = GETARG_B(i);
*regs_a = e->stack[idx];
}
NEXT;
}
CASE(OP_SETUPVAR) {
/* A B C uvset(B,C,R(A)) */
int up = GETARG_C(i);
struct REnv *e = uvenv(mrb, up);
if (e) {
mrb_value *regs_a = regs + GETARG_A(i);
int idx = GETARG_B(i);
e->stack[idx] = *regs_a;
mrb_write_barrier(mrb, (struct RBasic*)e);
}
NEXT;
}
CASE(OP_JMP) {
/* sBx pc+=sBx */
pc += GETARG_sBx(i);
JUMP;
}
CASE(OP_JMPIF) {
/* A sBx if R(A) pc+=sBx */
if (mrb_test(regs[GETARG_A(i)])) {
pc += GETARG_sBx(i);
JUMP;
}
NEXT;
}
CASE(OP_JMPNOT) {
/* A sBx if !R(A) pc+=sBx */
if (!mrb_test(regs[GETARG_A(i)])) {
pc += GETARG_sBx(i);
JUMP;
}
NEXT;
}
CASE(OP_ONERR) {
/* sBx pc+=sBx on exception */
if (mrb->c->rsize <= mrb->c->ci->ridx) {
if (mrb->c->rsize == 0) mrb->c->rsize = 16;
else mrb->c->rsize *= 2;
mrb->c->rescue = (mrb_code **)mrb_realloc(mrb, mrb->c->rescue, sizeof(mrb_code*) * mrb->c->rsize);
}
mrb->c->rescue[mrb->c->ci->ridx++] = pc + GETARG_sBx(i);
NEXT;
}
CASE(OP_RESCUE) {
/* A R(A) := exc; clear(exc) */
SET_OBJ_VALUE(regs[GETARG_A(i)], mrb->exc);
mrb->exc = 0;
NEXT;
}
CASE(OP_POPERR) {
/* A A.times{rescue_pop()} */
int a = GETARG_A(i);
while (a--) {
mrb->c->ci->ridx--;
}
NEXT;
}
CASE(OP_RAISE) {
/* A raise(R(A)) */
mrb_exc_set(mrb, regs[GETARG_A(i)]);
goto L_RAISE;
}
CASE(OP_EPUSH) {
/* Bx ensure_push(SEQ[Bx]) */
struct RProc *p;
p = mrb_closure_new(mrb, irep->reps[GETARG_Bx(i)]);
/* push ensure_stack */
if (mrb->c->esize <= mrb->c->ci->eidx) {
if (mrb->c->esize == 0) mrb->c->esize = 16;
else mrb->c->esize *= 2;
mrb->c->ensure = (struct RProc **)mrb_realloc(mrb, mrb->c->ensure, sizeof(struct RProc*) * mrb->c->esize);
}
mrb->c->ensure[mrb->c->ci->eidx++] = p;
ARENA_RESTORE(mrb, ai);
NEXT;
}
CASE(OP_EPOP) {
/* A A.times{ensure_pop().call} */
int a = GETARG_A(i);
mrb_callinfo *ci = mrb->c->ci;
int n, eidx = ci->eidx;
for (n=0; n<a && (ci == mrb->c->cibase || eidx > ci[-1].eidx); n++) {
ecall(mrb, --eidx);
ARENA_RESTORE(mrb, ai);
}
NEXT;
}
CASE(OP_LOADNIL) {
/* A R(A) := nil */
int a = GETARG_A(i);
SET_NIL_VALUE(regs[a]);
NEXT;
}
CASE(OP_SENDB) {
/* A B C R(A) := call(R(A),Syms(B),R(A+1),...,R(A+C),&R(A+C+1))*/
/* fall through */
};
L_SEND:
CASE(OP_SEND) {
/* A B C R(A) := call(R(A),Syms(B),R(A+1),...,R(A+C)) */
int a = GETARG_A(i);
int n = GETARG_C(i);
struct RProc *m;
struct RClass *c;
mrb_callinfo *ci;
mrb_value recv, result;
mrb_sym mid = syms[GETARG_B(i)];
recv = regs[a];
if (GET_OPCODE(i) != OP_SENDB) {
if (n == CALL_MAXARGS) {
SET_NIL_VALUE(regs[a+2]);
}
else {
SET_NIL_VALUE(regs[a+n+1]);
}
}
c = mrb_class(mrb, recv);
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;
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);
}
else {
value_move(regs+a+2, regs+a+1, ++n);
regs[a+1] = sym;
}
}
/* push callinfo */
ci = cipush(mrb);
ci->mid = mid;
ci->proc = m;
ci->stackent = mrb->c->stack;
ci->target_class = c;
ci->pc = pc + 1;
ci->acc = a;
/* prepare stack */
mrb->c->stack += a;
if (MRB_PROC_CFUNC_P(m)) {
if (n == CALL_MAXARGS) {
ci->argc = -1;
ci->nregs = 3;
}
else {
ci->argc = n;
ci->nregs = n + 2;
}
result = m->body.func(mrb, recv);
mrb->c->stack[0] = result;
mrb_gc_arena_restore(mrb, ai);
if (mrb->exc) goto L_RAISE;
/* pop stackpos */
ci = mrb->c->ci;
if (!ci->target_class) { /* return from context modifying method (resume/yield) */
if (!MRB_PROC_CFUNC_P(ci[-1].proc)) {
proc = ci[-1].proc;
irep = proc->body.irep;
pool = irep->pool;
syms = irep->syms;
}
}
regs = mrb->c->stack = ci->stackent;
pc = ci->pc;
cipop(mrb);
JUMP;
}
else {
/* setup environment for calling method */
proc = mrb->c->ci->proc = m;
irep = m->body.irep;
pool = irep->pool;
syms = irep->syms;
ci->nregs = irep->nregs;
if (n == CALL_MAXARGS) {
ci->argc = -1;
stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs, 3);
}
else {
ci->argc = n;
stack_extend(mrb, irep->nregs, n+2);
}
regs = mrb->c->stack;
pc = irep->iseq;
JUMP;
}
}
CASE(OP_FSEND) {
/* A B C R(A) := fcall(R(A),Syms(B),R(A+1),... ,R(A+C-1)) */
NEXT;
}
CASE(OP_CALL) {
/* A R(A) := self.call(frame.argc, frame.argv) */
mrb_callinfo *ci;
mrb_value recv = mrb->c->stack[0];
struct RProc *m = mrb_proc_ptr(recv);
/* replace callinfo */
ci = mrb->c->ci;
ci->target_class = m->target_class;
ci->proc = m;
if (m->env) {
if (m->env->mid) {
ci->mid = m->env->mid;
}
if (!m->env->stack) {
m->env->stack = mrb->c->stack;
}
}
/* prepare stack */
if (MRB_PROC_CFUNC_P(m)) {
recv = m->body.func(mrb, recv);
mrb_gc_arena_restore(mrb, ai);
if (mrb->exc) goto L_RAISE;
/* pop stackpos */
ci = mrb->c->ci;
regs = mrb->c->stack = ci->stackent;
regs[ci->acc] = recv;
pc = ci->pc;
cipop(mrb);
irep = mrb->c->ci->proc->body.irep;
pool = irep->pool;
syms = irep->syms;
JUMP;
}
else {
/* setup environment for calling method */
proc = m;
irep = m->body.irep;
if (!irep) {
mrb->c->stack[0] = mrb_nil_value();
goto L_RETURN;
}
pool = irep->pool;
syms = irep->syms;
ci->nregs = irep->nregs;
if (ci->argc < 0) {
stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs, 3);
}
else {
stack_extend(mrb, irep->nregs, ci->argc+2);
}
regs = mrb->c->stack;
regs[0] = m->env->stack[0];
pc = irep->iseq;
JUMP;
}
}
CASE(OP_SUPER) {
/* A C R(A) := super(R(A+1),... ,R(A+C+1)) */
mrb_value recv;
mrb_callinfo *ci = mrb->c->ci;
struct RProc *m;
struct RClass *c;
mrb_sym mid = ci->mid;
int a = GETARG_A(i);
int n = GETARG_C(i);
if (mid == 0) {
mrb_value exc;
exc = mrb_exc_new_str_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method");
mrb_exc_set(mrb, exc);
goto L_RAISE;
}
recv = regs[0];
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);
if (n == CALL_MAXARGS) {
mrb_ary_unshift(mrb, regs[a+1], mrb_symbol_value(ci->mid));
}
else {
value_move(regs+a+2, regs+a+1, ++n);
SET_SYM_VALUE(regs[a+1], ci->mid);
}
}
/* push callinfo */
ci = cipush(mrb);
ci->mid = mid;
ci->proc = m;
ci->stackent = mrb->c->stack;
if (n == CALL_MAXARGS) {
ci->argc = -1;
}
else {
ci->argc = n;
}
ci->target_class = c;
ci->pc = pc + 1;
/* prepare stack */
mrb->c->stack += a;
mrb->c->stack[0] = recv;
if (MRB_PROC_CFUNC_P(m)) {
if (n == CALL_MAXARGS) {
ci->nregs = 3;
}
else {
ci->nregs = n + 2;
}
mrb->c->stack[0] = m->body.func(mrb, recv);
mrb_gc_arena_restore(mrb, ai);
if (mrb->exc) goto L_RAISE;
/* pop stackpos */
regs = mrb->c->stack = mrb->c->ci->stackent;
cipop(mrb);
NEXT;
}
else {
/* fill callinfo */
ci->acc = a;
/* setup environment for calling method */
ci->proc = m;
irep = m->body.irep;
pool = irep->pool;
syms = irep->syms;
ci->nregs = irep->nregs;
if (n == CALL_MAXARGS) {
stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs, 3);
}
else {
stack_extend(mrb, irep->nregs, ci->argc+2);
}
regs = mrb->c->stack;
pc = irep->iseq;
JUMP;
}
}
CASE(OP_ARGARY) {
/* A Bx R(A) := argument array (16=6:1:5:4) */
int a = GETARG_A(i);
int bx = GETARG_Bx(i);
int m1 = (bx>>10)&0x3f;
int r = (bx>>9)&0x1;
int m2 = (bx>>4)&0x1f;
int lv = (bx>>0)&0xf;
mrb_value *stack;
if (lv == 0) stack = regs + 1;
else {
struct REnv *e = uvenv(mrb, lv-1);
if (!e) {
mrb_value exc;
exc = mrb_exc_new_str_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method");
mrb_exc_set(mrb, exc);
goto L_RAISE;
}
stack = e->stack + 1;
}
if (r == 0) {
regs[a] = mrb_ary_new_from_values(mrb, m1+m2, stack);
}
else {
mrb_value *pp = NULL;
struct RArray *rest;
int len = 0;
if (mrb_array_p(stack[m1])) {
struct RArray *ary = mrb_ary_ptr(stack[m1]);
pp = ary->ptr;
len = ary->len;
}
regs[a] = mrb_ary_new_capa(mrb, m1+len+m2);
rest = mrb_ary_ptr(regs[a]);
if (m1 > 0) {
stack_copy(rest->ptr, stack, m1);
}
if (len > 0) {
stack_copy(rest->ptr+m1, pp, len);
}
if (m2 > 0) {
stack_copy(rest->ptr+m1+len, stack+m1+1, m2);
}
rest->len = m1+len+m2;
}
regs[a+1] = stack[m1+r+m2];
ARENA_RESTORE(mrb, ai);
NEXT;
}
CASE(OP_ENTER) {
/* Ax arg setup according to flags (23=5:5:1:5:5:1:1) */
/* number of optional arguments times OP_JMP should follow */
mrb_aspec ax = GETARG_Ax(i);
int m1 = MRB_ASPEC_REQ(ax);
int o = MRB_ASPEC_OPT(ax);
int r = MRB_ASPEC_REST(ax);
int m2 = MRB_ASPEC_POST(ax);
/* unused
int k = MRB_ASPEC_KEY(ax);
int kd = MRB_ASPEC_KDICT(ax);
int b = MRB_ASPEC_BLOCK(ax);
*/
int argc = mrb->c->ci->argc;
mrb_value *argv = regs+1;
mrb_value *argv0 = argv;
int len = m1 + o + r + m2;
mrb_value *blk = &argv[argc < 0 ? 1 : argc];
if (!mrb_nil_p(*blk) && mrb_type(*blk) != MRB_TT_PROC) {
*blk = mrb_convert_type(mrb, *blk, MRB_TT_PROC, "Proc", "to_proc");
}
if (argc < 0) {
struct RArray *ary = mrb_ary_ptr(regs[1]);
argv = ary->ptr;
argc = ary->len;
mrb_gc_protect(mrb, regs[1]);
}
if (mrb->c->ci->proc && MRB_PROC_STRICT_P(mrb->c->ci->proc)) {
if (argc >= 0) {
if (argc < m1 + m2 || (r == 0 && argc > len)) {
argnum_error(mrb, m1+m2);
goto L_RAISE;
}
}
}
else if (len > 1 && argc == 1 && mrb_array_p(argv[0])) {
mrb_gc_protect(mrb, argv[0]);
argc = mrb_ary_ptr(argv[0])->len;
argv = mrb_ary_ptr(argv[0])->ptr;
}
mrb->c->ci->argc = len;
if (argc < len) {
int mlen = m2;
if (argc < m1+m2) {
if (m1 < argc)
mlen = argc - m1;
else
mlen = 0;
}
regs[len+1] = *blk; /* move block */
SET_NIL_VALUE(regs[argc+1]);
if (argv0 != argv) {
value_move(&regs[1], argv, argc-mlen); /* m1 + o */
}
if (mlen) {
value_move(&regs[len-m2+1], &argv[argc-mlen], mlen);
}
if (r) {
regs[m1+o+1] = mrb_ary_new_capa(mrb, 0);
}
if (o == 0 || argc < m1+m2) pc++;
else
pc += argc - m1 - m2 + 1;
}
else {
int rnum = 0;
if (argv0 != argv) {
regs[len+1] = *blk; /* move block */
value_move(&regs[1], argv, m1+o);
}
if (r) {
rnum = argc-m1-o-m2;
regs[m1+o+1] = mrb_ary_new_from_values(mrb, rnum, argv+m1+o);
}
if (m2) {
if (argc-m2 > m1) {
value_move(&regs[m1+o+r+1], &argv[m1+o+rnum], m2);
}
}
if (argv0 == argv) {
regs[len+1] = *blk; /* move block */
}
pc += o + 1;
}
JUMP;
}
CASE(OP_KARG) {
/* A B C R(A) := kdict[Syms(B)]; if C kdict.rm(Syms(B)) */
/* if C == 2; raise unless kdict.empty? */
/* OP_JMP should follow to skip init code */
NEXT;
}
CASE(OP_KDICT) {
/* A C R(A) := kdict */
NEXT;
}
L_RETURN:
i = MKOP_AB(OP_RETURN, GETARG_A(i), OP_R_NORMAL);
/* fall through */
CASE(OP_RETURN) {
/* A B return R(A) (B=normal,in-block return/break) */
if (mrb->exc) {
mrb_callinfo *ci;
int eidx;
L_RAISE:
ci = mrb->c->ci;
mrb_obj_iv_ifnone(mrb, mrb->exc, mrb_intern_lit(mrb, "lastpc"), mrb_cptr_value(mrb, pc));
mrb_obj_iv_ifnone(mrb, mrb->exc, mrb_intern_lit(mrb, "ciidx"), mrb_fixnum_value(ci - mrb->c->cibase));
eidx = ci->eidx;
if (ci == mrb->c->cibase) {
if (ci->ridx == 0) goto L_STOP;
goto L_RESCUE;
}
while (ci[0].ridx == ci[-1].ridx) {
cipop(mrb);
ci = mrb->c->ci;
mrb->c->stack = ci[1].stackent;
if (ci[1].acc == CI_ACC_SKIP && prev_jmp) {
mrb->jmp = prev_jmp;
MRB_THROW(prev_jmp);
}
if (ci == mrb->c->cibase) {
while (eidx > 0) {
ecall(mrb, --eidx);
}
if (ci->ridx == 0) {
if (mrb->c == mrb->root_c) {
regs = mrb->c->stack = mrb->c->stbase;
goto L_STOP;
}
else {
struct mrb_context *c = mrb->c;
mrb->c = c->prev;
c->prev = NULL;
goto L_RAISE;
}
}
break;
}
/* call ensure only when we skip this callinfo */
if (ci[0].ridx == ci[-1].ridx) {
while (eidx > ci[-1].eidx) {
ecall(mrb, --eidx);
}
}
}
L_RESCUE:
if (ci->ridx == 0) goto L_STOP;
proc = ci->proc;
irep = proc->body.irep;
pool = irep->pool;
syms = irep->syms;
regs = mrb->c->stack = ci[1].stackent;
pc = mrb->c->rescue[--ci->ridx];
}
else {
mrb_callinfo *ci = mrb->c->ci;
int acc, eidx = mrb->c->ci->eidx;
mrb_value v = regs[GETARG_A(i)];
switch (GETARG_B(i)) {
case OP_R_RETURN:
/* Fall through to OP_R_NORMAL otherwise */
if (proc->env && !MRB_PROC_STRICT_P(proc)) {
struct REnv *e = top_env(mrb, proc);
if (!MRB_ENV_STACK_SHARED_P(e)) {
localjump_error(mrb, LOCALJUMP_ERROR_RETURN);
goto L_RAISE;
}
ci = mrb->c->cibase + e->cioff;
if (ci == mrb->c->cibase) {
localjump_error(mrb, LOCALJUMP_ERROR_RETURN);
goto L_RAISE;
}
mrb->c->stack = mrb->c->ci->stackent;
mrb->c->ci = ci;
break;
}
case OP_R_NORMAL:
if (ci == mrb->c->cibase) {
if (!mrb->c->prev) { /* toplevel return */
localjump_error(mrb, LOCALJUMP_ERROR_RETURN);
goto L_RAISE;
}
if (mrb->c->prev->ci == mrb->c->prev->cibase) {
mrb_value exc = mrb_exc_new_str_lit(mrb, E_FIBER_ERROR, "double resume");
mrb_exc_set(mrb, exc);
goto L_RAISE;
}
/* automatic yield at the end */
mrb->c->status = MRB_FIBER_TERMINATED;
mrb->c = mrb->c->prev;
mrb->c->status = MRB_FIBER_RUNNING;
}
ci = mrb->c->ci;
break;
case OP_R_BREAK:
if (!proc->env || !MRB_ENV_STACK_SHARED_P(proc->env)) {
localjump_error(mrb, LOCALJUMP_ERROR_BREAK);
goto L_RAISE;
}
/* break from fiber block */
if (mrb->c->ci == mrb->c->cibase && mrb->c->ci->pc) {
struct mrb_context *c = mrb->c;
mrb->c = c->prev;
c->prev = NULL;
}
ci = mrb->c->ci;
mrb->c->stack = ci->stackent;
mrb->c->ci = mrb->c->cibase + proc->env->cioff + 1;
while (ci > mrb->c->ci) {
if (ci[-1].acc == CI_ACC_SKIP) {
mrb->c->ci = ci;
break;
}
ci--;
}
break;
default:
/* cannot happen */
break;
}
while (eidx > mrb->c->ci[-1].eidx) {
ecall(mrb, --eidx);
}
cipop(mrb);
acc = ci->acc;
pc = ci->pc;
regs = mrb->c->stack = ci->stackent;
if (acc == CI_ACC_SKIP) {
mrb->jmp = prev_jmp;
return v;
}
DEBUG(printf("from :%s\n", mrb_sym2name(mrb, ci->mid)));
proc = mrb->c->ci->proc;
irep = proc->body.irep;
pool = irep->pool;
syms = irep->syms;
regs[acc] = v;
}
JUMP;
}
CASE(OP_TAILCALL) {
/* A B C return call(R(A),Syms(B),R(A+1),... ,R(A+C+1)) */
int a = GETARG_A(i);
int n = GETARG_C(i);
struct RProc *m;
struct RClass *c;
mrb_callinfo *ci;
mrb_value recv;
mrb_sym mid = syms[GETARG_B(i)];
recv = regs[a];
c = mrb_class(mrb, recv);
m = mrb_method_search_vm(mrb, &c, mid);
if (!m) {
mrb_value sym = mrb_symbol_value(mid);
mid = mrb_intern_lit(mrb, "method_missing");
m = mrb_method_search_vm(mrb, &c, mid);
if (n == CALL_MAXARGS) {
mrb_ary_unshift(mrb, regs[a+1], sym);
}
else {
value_move(regs+a+2, regs+a+1, ++n);
regs[a+1] = sym;
}
}
/* replace callinfo */
ci = mrb->c->ci;
ci->mid = mid;
ci->target_class = c;
if (n == CALL_MAXARGS) {
ci->argc = -1;
}
else {
ci->argc = n;
}
/* move stack */
value_move(mrb->c->stack, &regs[a], ci->argc+1);
if (MRB_PROC_CFUNC_P(m)) {
mrb->c->stack[0] = m->body.func(mrb, recv);
mrb_gc_arena_restore(mrb, ai);
goto L_RETURN;
}
else {
/* setup environment for calling method */
irep = m->body.irep;
pool = irep->pool;
syms = irep->syms;
if (ci->argc < 0) {
stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs, 3);
}
else {
stack_extend(mrb, irep->nregs, ci->argc+2);
}
regs = mrb->c->stack;
pc = irep->iseq;
}
JUMP;
}
CASE(OP_BLKPUSH) {
/* A Bx R(A) := block (16=6:1:5:4) */
int a = GETARG_A(i);
int bx = GETARG_Bx(i);
int m1 = (bx>>10)&0x3f;
int r = (bx>>9)&0x1;
int m2 = (bx>>4)&0x1f;
int lv = (bx>>0)&0xf;
mrb_value *stack;
if (lv == 0) stack = regs + 1;
else {
struct REnv *e = uvenv(mrb, lv-1);
if (!e) {
localjump_error(mrb, LOCALJUMP_ERROR_YIELD);
goto L_RAISE;
}
stack = e->stack + 1;
}
regs[a] = stack[m1+r+m2];
NEXT;
}
#define TYPES2(a,b) ((((uint16_t)(a))<<8)|(((uint16_t)(b))&0xff))
#define OP_MATH_BODY(op,v1,v2) do {\
v1(regs[a]) = v1(regs[a]) op v2(regs[a+1]);\
} while(0)
CASE(OP_ADD) {
/* A B C R(A) := R(A)+R(A+1) (Syms[B]=:+,C=1)*/
int a = GETARG_A(i);
/* need to check if op is overridden */
switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) {
case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM):
{
mrb_int x, y, z;
mrb_value *regs_a = regs + a;
x = mrb_fixnum(regs_a[0]);
y = mrb_fixnum(regs_a[1]);
if (mrb_int_add_overflow(x, y, &z)) {
SET_FLOAT_VALUE(mrb, regs_a[0], (mrb_float)x + (mrb_float)y);
break;
}
SET_INT_VALUE(regs[a], z);
}
break;
case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT):
{
mrb_int x = mrb_fixnum(regs[a]);
mrb_float y = mrb_float(regs[a+1]);
SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x + y);
}
break;
case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM):
#ifdef MRB_WORD_BOXING
{
mrb_float x = mrb_float(regs[a]);
mrb_int y = mrb_fixnum(regs[a+1]);
SET_FLOAT_VALUE(mrb, regs[a], x + y);
}
#else
OP_MATH_BODY(+,mrb_float,mrb_fixnum);
#endif
break;
case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT):
#ifdef MRB_WORD_BOXING
{
mrb_float x = mrb_float(regs[a]);
mrb_float y = mrb_float(regs[a+1]);
SET_FLOAT_VALUE(mrb, regs[a], x + y);
}
#else
OP_MATH_BODY(+,mrb_float,mrb_float);
#endif
break;
case TYPES2(MRB_TT_STRING,MRB_TT_STRING):
regs[a] = mrb_str_plus(mrb, regs[a], regs[a+1]);
break;
default:
goto L_SEND;
}
ARENA_RESTORE(mrb, ai);
NEXT;
}
CASE(OP_SUB) {
/* A B C R(A) := R(A)-R(A+1) (Syms[B]=:-,C=1)*/
int a = GETARG_A(i);
/* need to check if op is overridden */
switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) {
case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM):
{
mrb_int x, y, z;
x = mrb_fixnum(regs[a]);
y = mrb_fixnum(regs[a+1]);
if (mrb_int_sub_overflow(x, y, &z)) {
SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x - (mrb_float)y);
break;
}
SET_INT_VALUE(regs[a], z);
}
break;
case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT):
{
mrb_int x = mrb_fixnum(regs[a]);
mrb_float y = mrb_float(regs[a+1]);
SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x - y);
}
break;
case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM):
#ifdef MRB_WORD_BOXING
{
mrb_float x = mrb_float(regs[a]);
mrb_int y = mrb_fixnum(regs[a+1]);
SET_FLOAT_VALUE(mrb, regs[a], x - y);
}
#else
OP_MATH_BODY(-,mrb_float,mrb_fixnum);
#endif
break;
case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT):
#ifdef MRB_WORD_BOXING
{
mrb_float x = mrb_float(regs[a]);
mrb_float y = mrb_float(regs[a+1]);
SET_FLOAT_VALUE(mrb, regs[a], x - y);
}
#else
OP_MATH_BODY(-,mrb_float,mrb_float);
#endif
break;
default:
goto L_SEND;
}
NEXT;
}
CASE(OP_MUL) {
/* A B C R(A) := R(A)*R(A+1) (Syms[B]=:*,C=1)*/
int a = GETARG_A(i);
/* need to check if op is overridden */
switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) {
case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM):
{
mrb_value z;
z = mrb_fixnum_mul(mrb, regs[a], regs[a+1]);
switch (mrb_type(z)) {
case MRB_TT_FIXNUM:
{
SET_INT_VALUE(regs[a], mrb_fixnum(z));
}
break;
case MRB_TT_FLOAT:
{
SET_FLOAT_VALUE(mrb, regs[a], mrb_float(z));
}
break;
default:
/* cannot happen */
break;
}
}
break;
case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT):
{
mrb_int x = mrb_fixnum(regs[a]);
mrb_float y = mrb_float(regs[a+1]);
SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x * y);
}
break;
case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM):
#ifdef MRB_WORD_BOXING
{
mrb_float x = mrb_float(regs[a]);
mrb_int y = mrb_fixnum(regs[a+1]);
SET_FLOAT_VALUE(mrb, regs[a], x * y);
}
#else
OP_MATH_BODY(*,mrb_float,mrb_fixnum);
#endif
break;
case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT):
#ifdef MRB_WORD_BOXING
{
mrb_float x = mrb_float(regs[a]);
mrb_float y = mrb_float(regs[a+1]);
SET_FLOAT_VALUE(mrb, regs[a], x * y);
}
#else
OP_MATH_BODY(*,mrb_float,mrb_float);
#endif
break;
default:
goto L_SEND;
}
NEXT;
}
CASE(OP_DIV) {
/* A B C R(A) := R(A)/R(A+1) (Syms[B]=:/,C=1)*/
int a = GETARG_A(i);
/* need to check if op is overridden */
switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) {
case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM):
{
mrb_int x = mrb_fixnum(regs[a]);
mrb_int y = mrb_fixnum(regs[a+1]);
SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x / (mrb_float)y);
}
break;
case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT):
{
mrb_int x = mrb_fixnum(regs[a]);
mrb_float y = mrb_float(regs[a+1]);
SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x / y);
}
break;
case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM):
#ifdef MRB_WORD_BOXING
{
mrb_float x = mrb_float(regs[a]);
mrb_int y = mrb_fixnum(regs[a+1]);
SET_FLOAT_VALUE(mrb, regs[a], x / y);
}
#else
OP_MATH_BODY(/,mrb_float,mrb_fixnum);
#endif
break;
case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT):
#ifdef MRB_WORD_BOXING
{
mrb_float x = mrb_float(regs[a]);
mrb_float y = mrb_float(regs[a+1]);
SET_FLOAT_VALUE(mrb, regs[a], x / y);
}
#else
OP_MATH_BODY(/,mrb_float,mrb_float);
#endif
break;
default:
goto L_SEND;
}
#ifdef MRB_NAN_BOXING
if (isnan(mrb_float(regs[a]))) {
regs[a] = mrb_float_value(mrb, mrb_float(regs[a]));
}
#endif
NEXT;
}
CASE(OP_ADDI) {
/* A B C R(A) := R(A)+C (Syms[B]=:+)*/
int a = GETARG_A(i);
/* need to check if + is overridden */
switch (mrb_type(regs[a])) {
case MRB_TT_FIXNUM:
{
mrb_int x = mrb_fixnum(regs[a]);
mrb_int y = GETARG_C(i);
mrb_int z;
if (mrb_int_add_overflow(x, y, &z)) {
SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x + (mrb_float)y);
break;
}
SET_INT_VALUE(regs[a], z);
}
break;
case MRB_TT_FLOAT:
#ifdef MRB_WORD_BOXING
{
mrb_float x = mrb_float(regs[a]);
SET_FLOAT_VALUE(mrb, regs[a], x + GETARG_C(i));
}
#else
mrb_float(regs[a]) += GETARG_C(i);
#endif
break;
default:
SET_INT_VALUE(regs[a+1], GETARG_C(i));
i = MKOP_ABC(OP_SEND, a, GETARG_B(i), 1);
goto L_SEND;
}
NEXT;
}
CASE(OP_SUBI) {
/* A B C R(A) := R(A)-C (Syms[B]=:-)*/
int a = GETARG_A(i);
mrb_value *regs_a = regs + a;
/* need to check if + is overridden */
switch (mrb_type(regs_a[0])) {
case MRB_TT_FIXNUM:
{
mrb_int x = mrb_fixnum(regs_a[0]);
mrb_int y = GETARG_C(i);
mrb_int z;
if (mrb_int_sub_overflow(x, y, &z)) {
SET_FLOAT_VALUE(mrb, regs_a[0], (mrb_float)x - (mrb_float)y);
}
else {
SET_INT_VALUE(regs_a[0], z);
}
}
break;
case MRB_TT_FLOAT:
#ifdef MRB_WORD_BOXING
{
mrb_float x = mrb_float(regs[a]);
SET_FLOAT_VALUE(mrb, regs[a], x - GETARG_C(i));
}
#else
mrb_float(regs_a[0]) -= GETARG_C(i);
#endif
break;
default:
SET_INT_VALUE(regs_a[1], GETARG_C(i));
i = MKOP_ABC(OP_SEND, a, GETARG_B(i), 1);
goto L_SEND;
}
NEXT;
}
#define OP_CMP_BODY(op,v1,v2) (v1(regs[a]) op v2(regs[a+1]))
#define OP_CMP(op) do {\
int result;\
/* need to check if - is overridden */\
switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) {\
case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM):\
result = OP_CMP_BODY(op,mrb_fixnum,mrb_fixnum);\
break;\
case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT):\
result = OP_CMP_BODY(op,mrb_fixnum,mrb_float);\
break;\
case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM):\
result = OP_CMP_BODY(op,mrb_float,mrb_fixnum);\
break;\
case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT):\
result = OP_CMP_BODY(op,mrb_float,mrb_float);\
break;\
default:\
goto L_SEND;\
}\
if (result) {\
SET_TRUE_VALUE(regs[a]);\
}\
else {\
SET_FALSE_VALUE(regs[a]);\
}\
} while(0)
CASE(OP_EQ) {
/* A B C R(A) := R(A)==R(A+1) (Syms[B]=:==,C=1)*/
int a = GETARG_A(i);
if (mrb_obj_eq(mrb, regs[a], regs[a+1])) {
SET_TRUE_VALUE(regs[a]);
}
else {
OP_CMP(==);
}
NEXT;
}
CASE(OP_LT) {
/* A B C R(A) := R(A)<R(A+1) (Syms[B]=:<,C=1)*/
int a = GETARG_A(i);
OP_CMP(<);
NEXT;
}
CASE(OP_LE) {
/* A B C R(A) := R(A)<=R(A+1) (Syms[B]=:<=,C=1)*/
int a = GETARG_A(i);
OP_CMP(<=);
NEXT;
}
CASE(OP_GT) {
/* A B C R(A) := R(A)>R(A+1) (Syms[B]=:>,C=1)*/
int a = GETARG_A(i);
OP_CMP(>);
NEXT;
}
CASE(OP_GE) {
/* A B C R(A) := R(A)>=R(A+1) (Syms[B]=:>=,C=1)*/
int a = GETARG_A(i);
OP_CMP(>=);
NEXT;
}
CASE(OP_ARRAY) {
/* A B C R(A) := ary_new(R(B),R(B+1)..R(B+C)) */
regs[GETARG_A(i)] = mrb_ary_new_from_values(mrb, GETARG_C(i), &regs[GETARG_B(i)]);
ARENA_RESTORE(mrb, ai);
NEXT;
}
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)]));
ARENA_RESTORE(mrb, ai);
NEXT;
}
CASE(OP_ARYPUSH) {
/* A B R(A).push(R(B)) */
mrb_ary_push(mrb, regs[GETARG_A(i)], regs[GETARG_B(i)]);
NEXT;
}
CASE(OP_AREF) {
/* A B C R(A) := R(B)[C] */
int a = GETARG_A(i);
int c = GETARG_C(i);
mrb_value v = regs[GETARG_B(i)];
if (!mrb_array_p(v)) {
if (c == 0) {
regs[GETARG_A(i)] = v;
}
else {
SET_NIL_VALUE(regs[a]);
}
}
else {
regs[GETARG_A(i)] = mrb_ary_ref(mrb, v, c);
}
NEXT;
}
CASE(OP_ASET) {
/* A B C R(B)[C] := R(A) */
mrb_ary_set(mrb, regs[GETARG_B(i)], GETARG_C(i), regs[GETARG_A(i)]);
NEXT;
}
CASE(OP_APOST) {
/* A B C *R(A),R(A+1)..R(A+C) := R(A) */
int a = GETARG_A(i);
mrb_value v = regs[a];
int pre = GETARG_B(i);
int post = GETARG_C(i);
struct RArray *ary;
int len, idx;
if (!mrb_array_p(v)) {
v = mrb_ary_new_from_values(mrb, 1, &regs[a]);
}
ary = mrb_ary_ptr(v);
len = ary->len;
if (len > pre + post) {
regs[a++] = mrb_ary_new_from_values(mrb, len - pre - post, ary->ptr+pre);
while (post--) {
regs[a++] = ary->ptr[len-post-1];
}
}
else {
regs[a++] = mrb_ary_new_capa(mrb, 0);
for (idx=0; idx+pre<len; idx++) {
regs[a+idx] = ary->ptr[pre+idx];
}
while (idx < post) {
SET_NIL_VALUE(regs[a+idx]);
idx++;
}
}
ARENA_RESTORE(mrb, ai);
NEXT;
}
CASE(OP_STRING) {
/* A Bx R(A) := str_new(Lit(Bx)) */
regs[GETARG_A(i)] = mrb_str_dup(mrb, pool[GETARG_Bx(i)]);
ARENA_RESTORE(mrb, ai);
NEXT;
}
CASE(OP_STRCAT) {
/* A B R(A).concat(R(B)) */
mrb_str_concat(mrb, regs[GETARG_A(i)], regs[GETARG_B(i)]);
regs = mrb->c->stack;
NEXT;
}
CASE(OP_HASH) {
/* A B C R(A) := hash_new(R(B),R(B+1)..R(B+C)) */
int b = GETARG_B(i);
int c = GETARG_C(i);
int lim = b+c*2;
mrb_value hash = mrb_hash_new_capa(mrb, c);
while (b < lim) {
mrb_hash_set(mrb, hash, regs[b], regs[b+1]);
b+=2;
}
regs[GETARG_A(i)] = hash;
ARENA_RESTORE(mrb, ai);
NEXT;
}
CASE(OP_LAMBDA) {
/* A b c R(A) := lambda(SEQ[b],c) (b:c = 14:2) */
struct RProc *p;
int c = GETARG_c(i);
if (c & OP_L_CAPTURE) {
p = mrb_closure_new(mrb, irep->reps[GETARG_b(i)]);
}
else {
p = mrb_proc_new(mrb, irep->reps[GETARG_b(i)]);
}
if (c & OP_L_STRICT) p->flags |= MRB_PROC_STRICT;
regs[GETARG_A(i)] = mrb_obj_value(p);
ARENA_RESTORE(mrb, ai);
NEXT;
}
CASE(OP_OCLASS) {
/* A R(A) := ::Object */
regs[GETARG_A(i)] = mrb_obj_value(mrb->object_class);
NEXT;
}
CASE(OP_CLASS) {
/* A B R(A) := newclass(R(A),Syms(B),R(A+1)) */
struct RClass *c = 0;
int a = GETARG_A(i);
mrb_value base, super;
mrb_sym id = syms[GETARG_B(i)];
base = regs[a];
super = regs[a+1];
if (mrb_nil_p(base)) {
base = mrb_obj_value(mrb->c->ci->target_class);
}
c = mrb_vm_define_class(mrb, base, super, id);
regs[a] = mrb_obj_value(c);
ARENA_RESTORE(mrb, ai);
NEXT;
}
CASE(OP_MODULE) {
/* A B R(A) := newmodule(R(A),Syms(B)) */
struct RClass *c = 0;
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);
}
c = mrb_vm_define_module(mrb, base, id);
regs[a] = mrb_obj_value(c);
ARENA_RESTORE(mrb, ai);
NEXT;
}
CASE(OP_EXEC) {
/* A Bx R(A) := blockexec(R(A),SEQ[Bx]) */
int a = GETARG_A(i);
mrb_callinfo *ci;
mrb_value recv = regs[a];
struct RProc *p;
/* prepare stack */
ci = cipush(mrb);
ci->pc = pc + 1;
ci->acc = a;
ci->mid = 0;
ci->stackent = mrb->c->stack;
ci->argc = 0;
ci->target_class = mrb_class_ptr(recv);
/* prepare stack */
mrb->c->stack += a;
p = mrb_proc_new(mrb, irep->reps[GETARG_Bx(i)]);
p->target_class = ci->target_class;
ci->proc = p;
if (MRB_PROC_CFUNC_P(p)) {
ci->nregs = 0;
mrb->c->stack[0] = p->body.func(mrb, recv);
mrb_gc_arena_restore(mrb, ai);
if (mrb->exc) goto L_RAISE;
/* pop stackpos */
regs = mrb->c->stack = mrb->c->ci->stackent;
cipop(mrb);
NEXT;
}
else {
irep = p->body.irep;
pool = irep->pool;
syms = irep->syms;
stack_extend(mrb, irep->nregs, 1);
ci->nregs = irep->nregs;
regs = mrb->c->stack;
pc = irep->iseq;
JUMP;
}
}
CASE(OP_METHOD) {
/* A B R(A).newmethod(Syms(B),R(A+1)) */
int a = GETARG_A(i);
struct RClass *c = mrb_class_ptr(regs[a]);
struct RProc *p = mrb_proc_ptr(regs[a+1]);
mrb_define_method_raw(mrb, c, syms[GETARG_B(i)], p);
ARENA_RESTORE(mrb, ai);
NEXT;
}
CASE(OP_SCLASS) {
/* A B R(A) := R(B).singleton_class */
regs[GETARG_A(i)] = mrb_singleton_class(mrb, regs[GETARG_B(i)]);
ARENA_RESTORE(mrb, ai);
NEXT;
}
CASE(OP_TCLASS) {
/* A R(A) := target_class */
if (!mrb->c->ci->target_class) {
mrb_value exc = mrb_exc_new_str_lit(mrb, E_TYPE_ERROR, "no target class or module");
mrb_exc_set(mrb, exc);
goto L_RAISE;
}
regs[GETARG_A(i)] = mrb_obj_value(mrb->c->ci->target_class);
NEXT;
}
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));
ARENA_RESTORE(mrb, ai);
NEXT;
}
CASE(OP_DEBUG) {
/* A B C debug print R(A),R(B),R(C) */
#ifdef MRB_ENABLE_DEBUG_HOOK
mrb->debug_op_hook(mrb, irep, pc, regs);
#else
#ifndef MRB_DISABLE_STDIO
printf("OP_DEBUG %d %d %d\n", GETARG_A(i), GETARG_B(i), GETARG_C(i));
#else
abort();
#endif
#endif
NEXT;
}
CASE(OP_STOP) {
/* stop VM */
L_STOP:
{
int eidx_stop = mrb->c->ci == mrb->c->cibase ? 0 : mrb->c->ci[-1].eidx;
int eidx = mrb->c->ci->eidx;
while (eidx > eidx_stop) {
ecall(mrb, --eidx);
}
}
ERR_PC_CLR(mrb);
mrb->jmp = prev_jmp;
if (mrb->exc) {
return mrb_obj_value(mrb->exc);
}
return regs[irep->nlocals];
}
CASE(OP_ERR) {
/* Bx raise RuntimeError with message Lit(Bx) */
mrb_value msg = mrb_str_dup(mrb, pool[GETARG_Bx(i)]);
mrb_value exc;
if (GETARG_A(i) == 0) {
exc = mrb_exc_new_str(mrb, E_RUNTIME_ERROR, msg);
}
else {
exc = mrb_exc_new_str(mrb, E_LOCALJUMP_ERROR, msg);
}
mrb_exc_set(mrb, exc);
goto L_RAISE;
}
}
END_DISPATCH;
}
MRB_CATCH(&c_jmp) {
exc_catched = TRUE;
goto RETRY_TRY_BLOCK;
}
MRB_END_EXC(&c_jmp);
}
MRB_API mrb_value
mrb_run(mrb_state *mrb, struct RProc *proc, mrb_value self)
{
return mrb_context_run(mrb, proc, self, mrb->c->ci->argc + 2); /* argc + 2 (receiver and block) */
}
MRB_API mrb_value
mrb_toplevel_run_keep(mrb_state *mrb, struct RProc *proc, unsigned int stack_keep)
{
mrb_callinfo *ci;
mrb_value v;
if (!mrb->c->cibase || mrb->c->ci == mrb->c->cibase) {
return mrb_context_run(mrb, proc, mrb_top_self(mrb), stack_keep);
}
ci = cipush(mrb);
ci->nregs = 1; /* protect the receiver */
ci->acc = CI_ACC_SKIP;
ci->target_class = mrb->object_class;
v = mrb_context_run(mrb, proc, mrb_top_self(mrb), stack_keep);
cipop(mrb);
return v;
}
MRB_API mrb_value
mrb_toplevel_run(mrb_state *mrb, struct RProc *proc)
{
return mrb_toplevel_run_keep(mrb, proc, 0);
}