diff options
| -rw-r--r-- | include/mruby.h | 26 | ||||
| -rw-r--r-- | include/mruby/compile.h | 2 | ||||
| -rw-r--r-- | include/mruby/hash.h | 10 | ||||
| -rw-r--r-- | include/mruby/proc.h | 2 | ||||
| -rw-r--r-- | include/mruby/throw.h | 12 | ||||
| -rw-r--r-- | include/mruby/value.h | 11 | ||||
| -rw-r--r-- | mrbgems/mruby-bin-mirb/tools/mirb/mirb.c | 4 | ||||
| -rw-r--r-- | mrbgems/mruby-compiler/core/parse.y | 2 | ||||
| -rw-r--r-- | mrbgems/mruby-eval/src/eval.c | 8 | ||||
| -rw-r--r-- | mrbgems/mruby-fiber/src/fiber.c | 75 | ||||
| -rw-r--r-- | mrbgems/mruby-sprintf/src/sprintf.c | 44 | ||||
| -rw-r--r-- | src/backtrace.c | 223 | ||||
| -rw-r--r-- | src/error.c | 87 | ||||
| -rw-r--r-- | src/gc.c | 26 | ||||
| -rw-r--r-- | src/hash.c | 6 | ||||
| -rw-r--r-- | src/load.c | 8 | ||||
| -rw-r--r-- | src/state.c | 3 | ||||
| -rw-r--r-- | src/string.c | 19 | ||||
| -rw-r--r-- | src/symbol.c | 4 | ||||
| -rw-r--r-- | src/vm.c | 108 | ||||
| -rw-r--r-- | test/assert.rb | 6 | ||||
| -rw-r--r-- | test/t/exception.rb | 47 |
22 files changed, 582 insertions, 151 deletions
diff --git a/include/mruby.h b/include/mruby.h index 1ea0156e8..9a3b72c74 100644 --- a/include/mruby.h +++ b/include/mruby.h @@ -87,7 +87,7 @@ typedef struct { enum mrb_fiber_state { MRB_FIBER_CREATED = 0, MRB_FIBER_RUNNING, - MRB_FIBER_RESUMING, + MRB_FIBER_RESUMED, MRB_FIBER_SUSPENDED, MRB_FIBER_TRANSFERRED, MRB_FIBER_TERMINATED, @@ -108,11 +108,20 @@ struct mrb_context { int esize; enum mrb_fiber_state status; + mrb_bool vmexec; struct RFiber *fib; }; struct mrb_jmpbuf; +typedef struct { + const char *filename; + int lineno; + struct RClass *klass; + const char *sep; + mrb_sym method_id; +} mrb_backtrace_entry; + typedef void (*mrb_atexit_func)(struct mrb_state*); typedef struct mrb_state { @@ -125,6 +134,12 @@ typedef struct mrb_state { struct mrb_context *root_c; struct RObject *exc; /* exception */ + struct { + struct RObject *exc; + int n; + int n_allocated; + mrb_backtrace_entry *entries; + } backtrace; struct iv_tbl *globals; /* global variable table */ struct RObject *top_self; @@ -918,8 +933,13 @@ MRB_API void* mrb_default_allocf(mrb_state*, void*, size_t, void*); MRB_API mrb_value mrb_top_self(mrb_state *); MRB_API mrb_value mrb_run(mrb_state*, struct RProc*, mrb_value); -MRB_API mrb_value mrb_toplevel_run(mrb_state*, struct RProc*); -MRB_API mrb_value mrb_context_run(mrb_state*, struct RProc*, mrb_value, unsigned int); +MRB_API mrb_value mrb_top_run(mrb_state*, struct RProc*, mrb_value, unsigned int); +MRB_API mrb_value mrb_vm_run(mrb_state*, struct RProc*, mrb_value, unsigned int); +MRB_API mrb_value mrb_vm_exec(mrb_state*, struct RProc*, mrb_code*); +/* compatibility macros */ +#define mrb_toplevel_run_keep(m,p,k) mrb_top_run((m),(p),mrb_top_self(m),(k)) +#define mrb_toplevel_run(m,p) mrb_toplevel_run_keep((m),(p),0) +#define mrb_context_run(m,p,s,k) mrb_vm_run((m),(p),(s),(k)) MRB_API void mrb_p(mrb_state*, mrb_value); MRB_API mrb_int mrb_obj_id(mrb_value obj); diff --git a/include/mruby/compile.h b/include/mruby/compile.h index 8b813b1c1..633eb1b74 100644 --- a/include/mruby/compile.h +++ b/include/mruby/compile.h @@ -40,8 +40,6 @@ MRB_API void mrbc_context_free(mrb_state *mrb, mrbc_context *cxt); MRB_API const char *mrbc_filename(mrb_state *mrb, mrbc_context *c, const char *s); MRB_API void mrbc_partial_hook(mrb_state *mrb, mrbc_context *c, int (*partial_hook)(struct mrb_parser_state*), void*data); -MRB_API mrb_value mrb_toplevel_run_keep(mrb_state*, struct RProc*, unsigned int); - /* AST node structure */ typedef struct mrb_ast_node { struct mrb_ast_node *car, *cdr; diff --git a/include/mruby/hash.h b/include/mruby/hash.h index 1d00a33ab..21366c459 100644 --- a/include/mruby/hash.h +++ b/include/mruby/hash.h @@ -8,6 +8,7 @@ #define MRUBY_HASH_H #include "common.h" +#include <mruby/khash.h> /** * Hash class @@ -59,6 +60,15 @@ MRB_API mrb_value mrb_hash_empty_p(mrb_state *mrb, mrb_value self); */ MRB_API mrb_value mrb_hash_clear(mrb_state *mrb, mrb_value hash); +/* declaration of struct kh_ht */ +/* be careful when you touch the internal */ +typedef struct { + mrb_value v; + mrb_int n; +} mrb_hash_value; + +KHASH_DECLARE(ht, mrb_value, mrb_hash_value, TRUE) + /* RHASH_TBL allocates st_table if not available. */ #define RHASH(obj) ((struct RHash*)(mrb_ptr(obj))) #define RHASH_TBL(h) (RHASH(h)->ht) diff --git a/include/mruby/proc.h b/include/mruby/proc.h index 24f0f319f..5965a2b39 100644 --- a/include/mruby/proc.h +++ b/include/mruby/proc.h @@ -27,6 +27,8 @@ struct REnv { #define MRB_ENV_UNSHARE_STACK(e) ((e)->cioff = -1) #define MRB_ENV_STACK_SHARED_P(e) ((e)->cioff >= 0) +MRB_API void mrb_env_unshare(mrb_state*, struct REnv*); + struct RProc { MRB_OBJECT_HEADER; union { diff --git a/include/mruby/throw.h b/include/mruby/throw.h index 7f720d512..e4b0f1f2b 100644 --- a/include/mruby/throw.h +++ b/include/mruby/throw.h @@ -20,11 +20,19 @@ typedef mrb_int mrb_jmpbuf_impl; #include <setjmp.h> -#define MRB_TRY(buf) do { if (setjmp((buf)->impl) == 0) { +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#define MRB_SETJMP _setjmp +#define MRB_LONGJMP _longjmp +#else +#define MRB_SETJMP setjmp +#define MRB_LONGJMP longjmp +#endif + +#define MRB_TRY(buf) do { if (MRB_SETJMP((buf)->impl) == 0) { #define MRB_CATCH(buf) } else { #define MRB_END_EXC(buf) } } while(0) -#define MRB_THROW(buf) longjmp((buf)->impl, 1); +#define MRB_THROW(buf) MRB_LONGJMP((buf)->impl, 1); #define mrb_jmpbuf_impl jmp_buf #endif diff --git a/include/mruby/value.h b/include/mruby/value.h index 04e5644d2..0eac19df9 100644 --- a/include/mruby/value.h +++ b/include/mruby/value.h @@ -22,21 +22,32 @@ struct mrb_state; # error "You can't define MRB_INT16 and MRB_INT64 at the same time." #endif +#include <inttypes.h> + #if defined(MRB_INT64) typedef int64_t mrb_int; # define MRB_INT_BIT 64 # define MRB_INT_MIN (INT64_MIN>>MRB_FIXNUM_SHIFT) # define MRB_INT_MAX (INT64_MAX>>MRB_FIXNUM_SHIFT) +# define MRB_PRIo PRIo64 +# define MRB_PRId PRId64 +# define MRB_PRIx PRIx64 #elif defined(MRB_INT16) typedef int16_t mrb_int; # define MRB_INT_BIT 16 # define MRB_INT_MIN (INT16_MIN>>MRB_FIXNUM_SHIFT) # define MRB_INT_MAX (INT16_MAX>>MRB_FIXNUM_SHIFT) +# define MRB_PRIo PRIo16 +# define MRB_PRId PRId16 +# define MRB_PRIx PRIx16 #else typedef int32_t mrb_int; # define MRB_INT_BIT 32 # define MRB_INT_MIN (INT32_MIN>>MRB_FIXNUM_SHIFT) # define MRB_INT_MAX (INT32_MAX>>MRB_FIXNUM_SHIFT) +# define MRB_PRIo PRIo32 +# define MRB_PRId PRId32 +# define MRB_PRIx PRIx32 #endif #ifdef MRB_USE_FLOAT diff --git a/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c b/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c index bdba2d2eb..55bbc513b 100644 --- a/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c +++ b/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c @@ -359,7 +359,7 @@ ctrl_c_handler(int signo) int main(int argc, char **argv) { - char ruby_code[1024] = { 0 }; + char ruby_code[4096] = { 0 }; char last_code_line[1024] = { 0 }; #ifndef ENABLE_READLINE int last_char; @@ -546,7 +546,7 @@ done: } /* pass a proc for evaulation */ /* evaluate the bytecode */ - result = mrb_context_run(mrb, + result = mrb_vm_run(mrb, proc, mrb_top_self(mrb), stack_keep); diff --git a/mrbgems/mruby-compiler/core/parse.y b/mrbgems/mruby-compiler/core/parse.y index b08775cc7..301d1bf65 100644 --- a/mrbgems/mruby-compiler/core/parse.y +++ b/mrbgems/mruby-compiler/core/parse.y @@ -5634,7 +5634,7 @@ load_exec(mrb_state *mrb, parser_state *p, mrbc_context *c) if (mrb->c->ci) { mrb->c->ci->target_class = target; } - v = mrb_toplevel_run_keep(mrb, proc, keep); + v = mrb_top_run(mrb, proc, mrb_top_self(mrb), keep); if (mrb->exc) return mrb_nil_value(); return v; } diff --git a/mrbgems/mruby-eval/src/eval.c b/mrbgems/mruby-eval/src/eval.c index eef1f5046..7c5b08d1a 100644 --- a/mrbgems/mruby-eval/src/eval.c +++ b/mrbgems/mruby-eval/src/eval.c @@ -130,6 +130,8 @@ patch_irep(mrb_state *mrb, mrb_irep *irep, int bnest) } } +void mrb_codedump_all(mrb_state*, struct RProc*); + static struct RProc* create_proc_from_string(mrb_state *mrb, char *s, int len, mrb_value binding, char *file, mrb_int line) { @@ -209,7 +211,7 @@ f_eval(mrb_state *mrb, mrb_value self) mrb_get_args(mrb, "s|ozi", &s, &len, &binding, &file, &line); proc = create_proc_from_string(mrb, s, len, binding, file, line); - ret = mrb_toplevel_run(mrb, proc); + ret = mrb_top_run(mrb, proc, mrb->c->stack[0], 0); if (mrb->exc) { mrb_exc_raise(mrb, mrb_obj_value(mrb->exc)); } @@ -236,12 +238,14 @@ f_instance_eval(mrb_state *mrb, mrb_value self) char *file = NULL; mrb_int line = 1; mrb_value cv; + struct RProc *proc; mrb_get_args(mrb, "s|zi", &s, &len, &file, &line); c->ci->acc = CI_ACC_SKIP; cv = mrb_singleton_class(mrb, self); c->ci->target_class = mrb_class_ptr(cv); - return mrb_run(mrb, create_proc_from_string(mrb, s, len, mrb_nil_value(), file, line), self); + proc = create_proc_from_string(mrb, s, len, mrb_nil_value(), file, line); + return mrb_top_run(mrb, proc, mrb->c->stack[0], 0); } else { mrb_get_args(mrb, "&", &b); diff --git a/mrbgems/mruby-fiber/src/fiber.c b/mrbgems/mruby-fiber/src/fiber.c index c140b6eac..232c925d3 100644 --- a/mrbgems/mruby-fiber/src/fiber.c +++ b/mrbgems/mruby-fiber/src/fiber.c @@ -7,6 +7,7 @@ #define FIBER_STACK_INIT_SIZE 64 #define FIBER_CI_INIT_SIZE 8 +#define CI_ACC_RESUMED -3 /* * call-seq: @@ -155,10 +156,9 @@ fiber_result(mrb_state *mrb, const mrb_value *a, mrb_int len) /* mark return from context modifying method */ #define MARK_CONTEXT_MODIFY(c) (c)->ci->target_class = NULL -static mrb_value -fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mrb_bool resume) +static void +fiber_check_cfunc(mrb_state *mrb, struct mrb_context *c) { - struct mrb_context *c = fiber_check(mrb, self); mrb_callinfo *ci; for (ci = c->ci; ci >= c->cibase; ci--) { @@ -166,16 +166,26 @@ fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mr mrb_raise(mrb, E_FIBER_ERROR, "can't cross C function boundary"); } } +} + +static mrb_value +fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mrb_bool resume, mrb_bool vmexec) +{ + struct mrb_context *c = fiber_check(mrb, self); + struct mrb_context *old_c = mrb->c; + mrb_value value; + + fiber_check_cfunc(mrb, c); if (resume && c->status == MRB_FIBER_TRANSFERRED) { mrb_raise(mrb, E_FIBER_ERROR, "resuming transferred fiber"); } - if (c->status == MRB_FIBER_RUNNING || c->status == MRB_FIBER_RESUMING) { - mrb_raise(mrb, E_FIBER_ERROR, "double resume"); + if (c->status == MRB_FIBER_RUNNING || c->status == MRB_FIBER_RESUMED) { + mrb_raise(mrb, E_FIBER_ERROR, "double resume (fib)"); } if (c->status == MRB_FIBER_TERMINATED) { mrb_raise(mrb, E_FIBER_ERROR, "resuming dead fiber"); } - mrb->c->status = resume ? MRB_FIBER_RESUMING : MRB_FIBER_TRANSFERRED; + mrb->c->status = resume ? MRB_FIBER_RESUMED : MRB_FIBER_TRANSFERRED; c->prev = resume ? mrb->c : (c->prev ? c->prev : mrb->root_c); if (c->status == MRB_FIBER_CREATED) { mrb_value *b = c->stack+1; @@ -185,22 +195,24 @@ fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mr *b++ = *a++; } c->cibase->argc = len; - if (c->prev->fib) - mrb_field_write_barrier(mrb, (struct RBasic*)c->fib, (struct RBasic*)c->prev->fib); - mrb_write_barrier(mrb, (struct RBasic*)c->fib); - c->status = MRB_FIBER_RUNNING; - mrb->c = c; - - MARK_CONTEXT_MODIFY(c); - return c->ci->proc->env->stack[0]; + value = c->stack[0] = c->ci->proc->env->stack[0]; + } + else { + value = fiber_result(mrb, a, len); } - MARK_CONTEXT_MODIFY(c); - if (c->prev->fib) - mrb_field_write_barrier(mrb, (struct RBasic*)c->fib, (struct RBasic*)c->prev->fib); mrb_write_barrier(mrb, (struct RBasic*)c->fib); c->status = MRB_FIBER_RUNNING; mrb->c = c; - return fiber_result(mrb, a, len); + + if (vmexec) { + c->vmexec = TRUE; + value = mrb_vm_exec(mrb, c->ci[-1].proc, c->ci->pc); + mrb->c = old_c; + } + else { + MARK_CONTEXT_MODIFY(c); + } + return value; } /* @@ -223,16 +235,20 @@ fiber_resume(mrb_state *mrb, mrb_value self) { mrb_value *a; mrb_int len; + mrb_bool vmexec = FALSE; mrb_get_args(mrb, "*", &a, &len); - return fiber_switch(mrb, self, len, a, TRUE); + if (mrb->c->ci->acc < 0) { + vmexec = TRUE; + } + return fiber_switch(mrb, self, len, a, TRUE, vmexec); } /* resume thread with given arguments */ MRB_API mrb_value mrb_fiber_resume(mrb_state *mrb, mrb_value fib, mrb_int len, const mrb_value *a) { - return fiber_switch(mrb, fib, len, a, TRUE); + return fiber_switch(mrb, fib, len, a, TRUE, TRUE); } /* @@ -280,6 +296,7 @@ fiber_transfer(mrb_state *mrb, mrb_value self) mrb_value* a; mrb_int len; + fiber_check_cfunc(mrb, mrb->c); mrb_get_args(mrb, "*", &a, &len); if (c == mrb->root_c) { @@ -295,7 +312,7 @@ fiber_transfer(mrb_state *mrb, mrb_value self) return fiber_result(mrb, a, len); } - return fiber_switch(mrb, self, len, a, FALSE); + return fiber_switch(mrb, self, len, a, FALSE, FALSE); } /* yield values to the caller fiber */ @@ -304,13 +321,7 @@ MRB_API mrb_value mrb_fiber_yield(mrb_state *mrb, mrb_int len, const mrb_value *a) { struct mrb_context *c = mrb->c; - mrb_callinfo *ci; - for (ci = c->ci; ci >= c->cibase; ci--) { - if (ci->acc < 0) { - mrb_raise(mrb, E_FIBER_ERROR, "can't cross C function boundary"); - } - } if (!c->prev) { mrb_raise(mrb, E_FIBER_ERROR, "can't yield from root fiber"); } @@ -319,8 +330,12 @@ mrb_fiber_yield(mrb_state *mrb, mrb_int len, const mrb_value *a) c->status = MRB_FIBER_SUSPENDED; mrb->c = c->prev; c->prev = NULL; - MARK_CONTEXT_MODIFY(mrb->c); + if (c->vmexec) { + c->vmexec = FALSE; + mrb->c->ci->acc = CI_ACC_RESUMED; + } mrb_write_barrier(mrb, (struct RBasic*)c->fib); + MARK_CONTEXT_MODIFY(mrb->c); return fiber_result(mrb, a, len); } @@ -332,7 +347,9 @@ mrb_fiber_yield(mrb_state *mrb, mrb_int len, const mrb_value *a) * along any arguments that were passed to it. The fiber will resume * processing at this point when <code>resume</code> is called next. * Any arguments passed to the next <code>resume</code> will be the - * value that this <code>Fiber.yield</code> expression evaluates to. + * + * mruby limitation: Fiber resume/yield cannot cross C function boundary. + * thus you cannot yield from #initialize which is called by mrb_funcall(). */ static mrb_value fiber_yield(mrb_state *mrb, mrb_value self) diff --git a/mrbgems/mruby-sprintf/src/sprintf.c b/mrbgems/mruby-sprintf/src/sprintf.c index 33e0931f9..f8ca217ac 100644 --- a/mrbgems/mruby-sprintf/src/sprintf.c +++ b/mrbgems/mruby-sprintf/src/sprintf.c @@ -759,7 +759,7 @@ retry: case 'B': case 'u': { mrb_value val = GETARG(); - char fbuf[32], nbuf[68], *s; + char nbuf[68], *s; const char *prefix = NULL; int sign = 0, dots = 0; char sc = 0; @@ -836,8 +836,6 @@ retry: } } if (sign) { - char c = *p; - if (c == 'i') c = 'd'; /* %d and %i are identical */ if (v < 0) { v = -v; sc = '-'; @@ -851,28 +849,40 @@ retry: sc = ' '; width--; } - if (base == 2) { - snprintf(nbuf, sizeof(nbuf), "%s", RSTRING_PTR(val)); - } - else { - snprintf(fbuf, sizeof(fbuf), "%%l%c", c); - snprintf(nbuf, sizeof(nbuf), fbuf, v); + switch (base) { + case 2: + strncpy(nbuf, RSTRING_PTR(val), sizeof(nbuf)); + break; + case 8: + snprintf(nbuf, sizeof(nbuf), "%"MRB_PRIo, v); + break; + case 10: + snprintf(nbuf, sizeof(nbuf), "%"MRB_PRId, v); + break; + case 16: + snprintf(nbuf, sizeof(nbuf), "%"MRB_PRIx, v); + break; } s = nbuf; } else { - char c = *p; - if (c == 'X') c = 'x'; s = nbuf; if (v < 0) { dots = 1; } - if (base == 2) { - snprintf(++s, sizeof(nbuf) - 1, "%s", RSTRING_PTR(val)); - } - else { - snprintf(fbuf, sizeof(fbuf), "%%l%c", c); - snprintf(++s, sizeof(nbuf) - 1, fbuf, v); + switch (base) { + case 2: + strncpy(++s, RSTRING_PTR(val), sizeof(nbuf)-1); + break; + case 8: + snprintf(++s, sizeof(nbuf)-1, "%"MRB_PRIo, v); + break; + case 10: + snprintf(++s, sizeof(nbuf)-1, "%"MRB_PRId, v); + break; + case 16: + snprintf(++s, sizeof(nbuf)-1, "%"MRB_PRIx, v); + break; } if (v < 0) { char d; diff --git a/src/backtrace.c b/src/backtrace.c index 80a5e2935..2fce6645d 100644 --- a/src/backtrace.c +++ b/src/backtrace.c @@ -14,6 +14,15 @@ #include <mruby/error.h> #include <mruby/numeric.h> +struct backtrace_location_raw { + int i; + int lineno; + const char *filename; + mrb_sym method_id; + const char *sep; + struct RClass *klass; +}; + struct backtrace_location { int i; int lineno; @@ -23,6 +32,7 @@ struct backtrace_location { const char *class_name; }; +typedef void (*each_backtrace_func)(mrb_state*, struct backtrace_location_raw*, void*); typedef void (*output_stream_func)(mrb_state*, struct backtrace_location*, void*); #ifndef MRB_DISABLE_STDIO @@ -89,7 +99,7 @@ get_backtrace_i(mrb_state *mrb, struct backtrace_location *loc, void *data) } static void -output_backtrace(mrb_state *mrb, mrb_int ciidx, mrb_code *pc0, output_stream_func func, void *data) +each_backtrace(mrb_state *mrb, mrb_int ciidx, mrb_code *pc0, each_backtrace_func func, void *data) { int i; @@ -97,7 +107,7 @@ output_backtrace(mrb_state *mrb, mrb_int ciidx, mrb_code *pc0, output_stream_fun ciidx = 10; /* ciidx is broken... */ for (i = ciidx; i >= 0; i--) { - struct backtrace_location loc; + struct backtrace_location_raw loc; mrb_callinfo *ci; mrb_irep *irep; mrb_code *pc; @@ -134,13 +144,43 @@ output_backtrace(mrb_state *mrb, mrb_int ciidx, mrb_code *pc0, output_stream_fun loc.filename = "(unknown)"; } - loc.method = mrb_sym2name(mrb, ci->mid); - loc.class_name = mrb_class_name(mrb, ci->proc->target_class); + loc.method_id = ci->mid; + loc.klass = ci->proc->target_class; loc.i = i; func(mrb, &loc, data); } } +struct output_backtrace_args { + output_stream_func func; + void *data; +}; + +static void +output_backtrace_i(mrb_state *mrb, struct backtrace_location_raw *loc_raw, void *data) +{ + struct backtrace_location loc; + struct output_backtrace_args *args = data; + + loc.i = loc_raw->i; + loc.lineno = loc_raw->lineno; + loc.filename = loc_raw->filename; + loc.method = mrb_sym2name(mrb, loc_raw->method_id); + loc.sep = loc_raw->sep; + loc.class_name = mrb_class_name(mrb, loc_raw->klass); + + args->func(mrb, &loc, args->data); +} + +static void +output_backtrace(mrb_state *mrb, mrb_int ciidx, mrb_code *pc0, output_stream_func func, void *data) +{ + struct output_backtrace_args args; + args.func = func; + args.data = data; + each_backtrace(mrb, ciidx, pc0, output_backtrace_i, &args); +} + static void exc_output_backtrace(mrb_state *mrb, struct RObject *exc, output_stream_func func, void *stream) { @@ -167,18 +207,76 @@ exc_output_backtrace(mrb_state *mrb, struct RObject *exc, output_stream_func fun #ifndef MRB_DISABLE_STDIO +static void +print_backtrace(mrb_state *mrb, mrb_value backtrace) +{ + int i, n; + FILE *stream = stderr; + + fprintf(stream, "trace:\n"); + + n = RARRAY_LEN(backtrace); + for (i = 0; i < n; i++) { + mrb_value entry = RARRAY_PTR(backtrace)[i]; + + fprintf(stream, "\t[%d] %.*s\n", i, (int)RSTRING_LEN(entry), RSTRING_PTR(entry)); + } +} + +static void +print_backtrace_saved(mrb_state *mrb) +{ + int i; + FILE *stream = stderr; + + fprintf(stream, "trace:\n"); + for (i = 0; i < mrb->backtrace.n; i++) { + mrb_backtrace_entry *entry; + + entry = &(mrb->backtrace.entries[i]); + fprintf(stream, "\t[%d] %s:%d", i, entry->filename, entry->lineno); + + if (entry->method_id != 0) { + const char *method_name; + + method_name = mrb_sym2name(mrb, entry->method_id); + if (entry->klass) { + fprintf(stream, ":in %s%s%s", + mrb_class_name(mrb, entry->klass), + entry->sep, + method_name); + } + else { + fprintf(stream, ":in %s", method_name); + } + } + + fprintf(stream, "\n"); + } +} + MRB_API void mrb_print_backtrace(mrb_state *mrb) { - struct print_backtrace_args args; + mrb_value backtrace; if (!mrb->exc || mrb_obj_is_kind_of(mrb, mrb_obj_value(mrb->exc), E_SYSSTACK_ERROR)) { return; } - args.stream = stderr; - args.tracehead = TRUE; - exc_output_backtrace(mrb, mrb->exc, print_backtrace_i, (void*)&args); + backtrace = mrb_obj_iv_get(mrb, mrb->exc, mrb_intern_lit(mrb, "backtrace")); + if (!mrb_nil_p(backtrace)) { + print_backtrace(mrb, backtrace); + } + else if (mrb->backtrace.n > 0) { + print_backtrace_saved(mrb); + } + else { + struct print_backtrace_args args; + args.stream = stderr; + args.tracehead = TRUE; + exc_output_backtrace(mrb, mrb->exc, print_backtrace_i, (void*)&args); + } } #else @@ -215,3 +313,112 @@ mrb_get_backtrace(mrb_state *mrb) return ary; } + +void +mrb_free_backtrace(mrb_state *mrb) +{ + mrb->backtrace.exc = 0; + mrb->backtrace.n = 0; + mrb->backtrace.n_allocated = 0; + mrb_free(mrb, mrb->backtrace.entries); +} + +static void +save_backtrace_i(mrb_state *mrb, + struct backtrace_location_raw *loc_raw, + void *data) +{ + mrb_backtrace_entry *entry; + + if (loc_raw->i >= mrb->backtrace.n_allocated) { + int new_n_allocated; + if (mrb->backtrace.n_allocated == 0) { + new_n_allocated = 8; + } + else { + new_n_allocated = mrb->backtrace.n_allocated * 2; + } + mrb->backtrace.entries = + mrb_realloc(mrb, + mrb->backtrace.entries, + sizeof(mrb_backtrace_entry) * new_n_allocated); + mrb->backtrace.n_allocated = new_n_allocated; + } + + entry = &mrb->backtrace.entries[mrb->backtrace.n]; + entry->filename = loc_raw->filename; + entry->lineno = loc_raw->lineno; + entry->klass = loc_raw->klass; + entry->sep = loc_raw->sep; + entry->method_id = loc_raw->method_id; + + mrb->backtrace.n++; +} + +void +mrb_save_backtrace(mrb_state *mrb) +{ + mrb_value lastpc; + mrb_code *code; + mrb_int ciidx; + + mrb->backtrace.n = 0; + mrb->backtrace.exc = 0; + + if (!mrb->exc) + return; + + mrb->backtrace.exc = mrb->exc; + + lastpc = mrb_obj_iv_get(mrb, mrb->exc, mrb_intern_lit(mrb, "lastpc")); + if (mrb_nil_p(lastpc)) { + code = NULL; + } + else { + code = (mrb_code*)mrb_cptr(lastpc); + } + + ciidx = mrb_fixnum(mrb_obj_iv_get(mrb, mrb->exc, mrb_intern_lit(mrb, "ciidx"))); + + each_backtrace(mrb, ciidx, code, save_backtrace_i, NULL); +} + +mrb_value +mrb_restore_backtrace(mrb_state *mrb) +{ + int i; + mrb_value backtrace; + + backtrace = mrb_ary_new(mrb); + for (i = 0; i < mrb->backtrace.n; i++) { + int ai; + mrb_backtrace_entry *entry; + mrb_value mrb_entry; + + ai = mrb_gc_arena_save(mrb); + entry = &(mrb->backtrace.entries[i]); + + mrb_entry = mrb_str_new_cstr(mrb, entry->filename); + mrb_str_cat_lit(mrb, mrb_entry, ":"); + mrb_str_concat(mrb, mrb_entry, + mrb_fixnum_to_str(mrb, + mrb_fixnum_value(entry->lineno), + 10)); + if (entry->method_id != 0) { + mrb_str_cat_lit(mrb, mrb_entry, ":in "); + + if (entry->klass) { + mrb_str_cat_cstr(mrb, mrb_entry, mrb_class_name(mrb, entry->klass)); + mrb_str_cat_cstr(mrb, mrb_entry, entry->sep); + } + + mrb_str_cat_cstr(mrb, mrb_entry, mrb_sym2name(mrb, entry->method_id)); + } + + mrb_ary_push(mrb, backtrace, mrb_entry); + + mrb_gc_arena_restore(mrb, ai); + } + + return backtrace; +} diff --git a/src/error.c b/src/error.c index 15a969d93..14e4ab4d3 100644 --- a/src/error.c +++ b/src/error.c @@ -174,6 +174,42 @@ exc_inspect(mrb_state *mrb, mrb_value exc) return str; } +void mrb_save_backtrace(mrb_state *mrb); +mrb_value mrb_restore_backtrace(mrb_state *mrb); + +static mrb_value +exc_get_backtrace(mrb_state *mrb, mrb_value exc) +{ + mrb_sym attr_name; + mrb_value backtrace; + + attr_name = mrb_intern_lit(mrb, "backtrace"); + backtrace = mrb_iv_get(mrb, exc, attr_name); + if (mrb_nil_p(backtrace)) { + if (mrb_obj_ptr(exc) == mrb->backtrace.exc && mrb->backtrace.n > 0) { + backtrace = mrb_restore_backtrace(mrb); + mrb->backtrace.n = 0; + mrb->backtrace.exc = 0; + } + else { + backtrace = mrb_exc_backtrace(mrb, exc); + } + mrb_iv_set(mrb, exc, attr_name, backtrace); + } + + return backtrace; +} + +static mrb_value +exc_set_backtrace(mrb_state *mrb, mrb_value exc) +{ + mrb_value backtrace; + + mrb_get_args(mrb, "o", &backtrace); + mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "backtrace"), backtrace); + + return backtrace; +} static void exc_debug_info(mrb_state *mrb, struct RObject *exc) @@ -202,12 +238,52 @@ exc_debug_info(mrb_state *mrb, struct RObject *exc) } } +static void +set_backtrace(mrb_state *mrb, mrb_value info, mrb_value bt) +{ + mrb_funcall(mrb, info, "set_backtrace", 1, bt); +} + +static mrb_bool +have_backtrace(mrb_state *mrb, struct RObject *exc) +{ + return !mrb_nil_p(mrb_obj_iv_get(mrb, exc, mrb_intern_lit(mrb, "backtrace"))); +} + +void +mrb_exc_set(mrb_state *mrb, mrb_value exc) +{ + if (!mrb->gc.out_of_memory && mrb->backtrace.n > 0) { + mrb_value target_exc = mrb_nil_value(); + if ((mrb->exc && !have_backtrace(mrb, mrb->exc))) { + target_exc = mrb_obj_value(mrb->exc); + } + else if (!mrb_nil_p(exc) && mrb_obj_ptr(exc) == mrb->backtrace.exc) { + target_exc = exc; + } + if (!mrb_nil_p(target_exc)) { + mrb_value backtrace; + backtrace = mrb_restore_backtrace(mrb); + set_backtrace(mrb, target_exc, backtrace); + } + } + + mrb->backtrace.n = 0; + if (mrb_nil_p(exc)) { + mrb->exc = 0; + } + else { + mrb->exc = mrb_obj_ptr(exc); + } +} + MRB_API mrb_noreturn void mrb_exc_raise(mrb_state *mrb, mrb_value exc) { - mrb->exc = mrb_obj_ptr(exc); + mrb_exc_set(mrb, exc); if (!mrb->gc.out_of_memory) { exc_debug_info(mrb, mrb->exc); + mrb_save_backtrace(mrb); } if (!mrb->jmp) { mrb_p(mrb, exc); @@ -337,12 +413,6 @@ mrb_bug(mrb_state *mrb, const char *fmt, ...) exit(EXIT_FAILURE); } -static void -set_backtrace(mrb_state *mrb, mrb_value info, mrb_value bt) -{ - mrb_funcall(mrb, info, "set_backtrace", 1, bt); -} - static mrb_value make_exception(mrb_state *mrb, int argc, const mrb_value *argv, mrb_bool isstr) { @@ -449,7 +519,8 @@ mrb_init_exception(mrb_state *mrb) mrb_define_method(mrb, exception, "to_s", exc_to_s, MRB_ARGS_NONE()); mrb_define_method(mrb, exception, "message", exc_message, MRB_ARGS_NONE()); mrb_define_method(mrb, exception, "inspect", exc_inspect, MRB_ARGS_NONE()); - mrb_define_method(mrb, exception, "backtrace", mrb_exc_backtrace, MRB_ARGS_NONE()); + mrb_define_method(mrb, exception, "backtrace", exc_get_backtrace, MRB_ARGS_NONE()); + mrb_define_method(mrb, exception, "set_backtrace", exc_set_backtrace, MRB_ARGS_REQ(1)); mrb->eStandardError_class = mrb_define_class(mrb, "StandardError", mrb->eException_class); /* 15.2.23 */ runtime_error = mrb_define_class(mrb, "RuntimeError", mrb->eStandardError_class); /* 15.2.28 */ @@ -613,14 +613,11 @@ gc_mark_children(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) case MRB_TT_ENV: { struct REnv *e = (struct REnv*)obj; + mrb_int i, len; - if (!MRB_ENV_STACK_SHARED_P(e)) { - mrb_int i, len; - - len = MRB_ENV_STACK_LEN(e); - for (i=0; i<len; i++) { - mrb_gc_mark_value(mrb, e->stack[i]); - } + len = MRB_ENV_STACK_LEN(e); + for (i=0; i<len; i++) { + mrb_gc_mark_value(mrb, e->stack[i]); } } break; @@ -725,9 +722,18 @@ obj_free(mrb_state *mrb, struct RBasic *obj) case MRB_TT_FIBER: { struct mrb_context *c = ((struct RFiber*)obj)->cxt; - - if (c != mrb->root_c) - mrb_free_context(mrb, c); + if (c && c != mrb->root_c) { + mrb_callinfo *ci = c->ci; + mrb_callinfo *ce = c->cibase; + + while (ce <= ci) { + struct REnv *e = ci->env; + if (e && !is_dead(&mrb->gc, e) && MRB_ENV_STACK_SHARED_P(e)) { + mrb_env_unshare(mrb, e); + } + ci--; + } + } } break; diff --git a/src/hash.c b/src/hash.c index ac7256987..7712cd467 100644 --- a/src/hash.c +++ b/src/hash.c @@ -91,12 +91,6 @@ mrb_hash_ht_hash_equal(mrb_state *mrb, mrb_value a, mrb_value b) } } -typedef struct { - mrb_value v; - mrb_int n; -} mrb_hash_value; - -KHASH_DECLARE(ht, mrb_value, mrb_hash_value, TRUE) KHASH_DEFINE (ht, mrb_value, mrb_hash_value, TRUE, mrb_hash_ht_hash_func, mrb_hash_ht_hash_equal) static void mrb_hash_modify(mrb_state *mrb, mrb_value hash); diff --git a/src/load.c b/src/load.c index da88f0d3a..d3bb281ae 100644 --- a/src/load.c +++ b/src/load.c @@ -614,10 +614,12 @@ mrb_read_irep(mrb_state *mrb, const uint8_t *bin) return read_irep(mrb, bin, flags); } +void mrb_exc_set(mrb_state *mrb, mrb_value exc); + static void irep_error(mrb_state *mrb) { - mrb->exc = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, E_SCRIPT_ERROR, "irep load error")); + mrb_exc_set(mrb, mrb_exc_new_str_lit(mrb, E_SCRIPT_ERROR, "irep load error")); } MRB_API mrb_value @@ -633,7 +635,7 @@ mrb_load_irep_cxt(mrb_state *mrb, const uint8_t *bin, mrbc_context *c) proc = mrb_proc_new(mrb, irep); mrb_irep_decref(mrb, irep); if (c && c->no_exec) return mrb_obj_value(proc); - return mrb_toplevel_run(mrb, proc); + return mrb_top_run(mrb, proc, mrb_top_self(mrb), 0); } MRB_API mrb_value @@ -695,7 +697,7 @@ mrb_load_irep_file_cxt(mrb_state *mrb, FILE* fp, mrbc_context *c) mrb_irep_decref(mrb, irep); if (c && c->dump_result) mrb_codedump_all(mrb, proc); if (c && c->no_exec) return mrb_obj_value(proc); - val = mrb_toplevel_run(mrb, proc); + val = mrb_top_run(mrb, proc, mrb_top_self(mrb), 0); return val; } diff --git a/src/state.c b/src/state.c index c8c0658e4..898e77336 100644 --- a/src/state.c +++ b/src/state.c @@ -215,6 +215,8 @@ mrb_str_pool(mrb_state *mrb, mrb_value str) return mrb_obj_value(ns); } +void mrb_free_backtrace(mrb_state *mrb); + MRB_API void mrb_free_context(mrb_state *mrb, struct mrb_context *c) { @@ -242,6 +244,7 @@ mrb_close(mrb_state *mrb) /* free */ mrb_gc_free_gv(mrb); + mrb_free_backtrace(mrb); mrb_free_context(mrb, mrb->root_c); mrb_free_symtbl(mrb); mrb_alloca_free(mrb); diff --git a/src/string.c b/src/string.c index 6664eabd6..a3f337d4b 100644 --- a/src/string.c +++ b/src/string.c @@ -306,17 +306,20 @@ bytes2chars(char *p, mrb_int bi) mrb_int i, b, n; for (b=i=0; b<bi; i++) { - n = utf8len(p, p+bi); + n = utf8len_codepage[(unsigned char)*p]; b += n; p += n; } + if (b != bi) return -1; return i; } +#define BYTES_ALIGN_CHECK(pos) if (pos < 0) return mrb_nil_value(); #else #define RSTRING_CHAR_LEN(s) RSTRING_LEN(s) #define chars2bytes(p, off, ci) (ci) #define bytes2chars(p, bi) (bi) +#define BYTES_ALIGN_CHECK(pos) #endif static inline mrb_int @@ -352,12 +355,12 @@ mrb_memsearch(const void *x0, mrb_int m, const void *y0, mrb_int n) return 0; } else if (m == 1) { - const unsigned char *ys = y, *ye = ys + n; - for (; y < ye; ++y) { - if (*x == *y) - return y - ys; - } - return -1; + const unsigned char *ys = memchr(y, *x, n); + + if (ys) + return ys - y; + else + return -1; } return mrb_memsearch_qs((const unsigned char *)x0, m, (const unsigned char *)y0, n); } @@ -1608,6 +1611,7 @@ mrb_str_index(mrb_state *mrb, mrb_value str) if (pos == -1) return mrb_nil_value(); pos = bytes2chars(RSTRING_PTR(str), pos); + BYTES_ALIGN_CHECK(pos); return mrb_fixnum_value(pos); } @@ -1877,6 +1881,7 @@ mrb_str_rindex(mrb_state *mrb, mrb_value str) pos = str_rindex(mrb, str, sub, pos); if (pos >= 0) { pos = bytes2chars(RSTRING_PTR(str), pos); + BYTES_ALIGN_CHECK(pos); return mrb_fixnum_value(pos); } break; diff --git a/src/symbol.c b/src/symbol.c index c39e88012..25ae132e1 100644 --- a/src/symbol.c +++ b/src/symbol.c @@ -354,7 +354,9 @@ symname_p(const char *name) if (*++m == '*') ++m; break; case '!': - if (*++m == '=') ++m; + switch (*++m) { + case '=': case '~': ++m; + } break; case '+': case '-': if (*++m == '@') ++m; @@ -209,6 +209,7 @@ top_env(mrb_state *mrb, struct RProc *proc) #define CI_ACC_SKIP -1 #define CI_ACC_DIRECT -2 +#define CI_ACC_RESUMED -3 static mrb_callinfo* cipush(mrb_state *mrb) @@ -237,27 +238,34 @@ cipush(mrb_state *mrb) return ci; } +MRB_API void +mrb_env_unshare(mrb_state *mrb, struct REnv *e) +{ + 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); +} + 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); + mrb_env_unshare(mrb, c->ci->env); } c->ci--; } +void mrb_exc_set(mrb_state *mrb, mrb_value exc); + static void ecall(mrb_state *mrb, int i) { @@ -496,21 +504,32 @@ eval_under(mrb_state *mrb, mrb_value self, mrb_value blk, struct RClass *c) { struct RProc *p; mrb_callinfo *ci; + mrb_int max = 3; 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); + return mrb_yield_with_class(mrb, blk, 1, &self, self, c); } ci->target_class = c; p = mrb_proc_ptr(blk); ci->proc = p; + ci->argc = 1; if (MRB_PROC_CFUNC_P(p)) { + stack_extend(mrb, 3, 0); + mrb->c->stack[0] = self; + mrb->c->stack[1] = self; + mrb->c->stack[2] = mrb_nil_value(); return p->body.func(mrb, self); } ci->nregs = p->body.irep->nregs; + if (max < ci->nregs) max = ci->nregs; + stack_extend(mrb, max, 0); + mrb->c->stack[0] = self; + mrb->c->stack[1] = self; + mrb->c->stack[2] = mrb_nil_value(); ci = cipush(mrb); ci->nregs = 0; ci->target_class = 0; @@ -669,7 +688,7 @@ localjump_error(mrb_state *mrb, localjump_error_kind kind) 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 = mrb_obj_ptr(exc); + mrb_exc_set(mrb, exc); } static void @@ -688,7 +707,7 @@ argnum_error(mrb_state *mrb, mrb_int num) mrb_fixnum_value(mrb->c->ci->argc), mrb_fixnum_value(num)); } exc = mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str); - mrb->exc = mrb_obj_ptr(exc); + mrb_exc_set(mrb, exc); } #define ERR_PC_SET(mrb, pc) mrb->c->ci->err = pc; @@ -727,11 +746,23 @@ argnum_error(mrb_state *mrb, mrb_int num) 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_vm_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int stack_keep) +{ + mrb_irep *irep = proc->body.irep; + + if (!mrb->c->stack) { + stack_init(mrb); + } + stack_extend(mrb, irep->nregs, stack_keep); + mrb->c->stack[0] = self; + return mrb_vm_exec(mrb, proc, irep->iseq); +} + +MRB_API mrb_value +mrb_vm_exec(mrb_state *mrb, struct RProc *proc, mrb_code *pc) { /* 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; @@ -775,14 +806,9 @@ RETRY_TRY_BLOCK: 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) { @@ -1007,7 +1033,7 @@ RETRY_TRY_BLOCK: CASE(OP_RAISE) { /* A raise(R(A)) */ - mrb->exc = mrb_obj_ptr(regs[GETARG_A(i)]); + mrb_exc_set(mrb, regs[GETARG_A(i)]); goto L_RAISE; } @@ -1124,19 +1150,24 @@ RETRY_TRY_BLOCK: 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)) { + if (ci->acc == CI_ACC_RESUMED) { + mrb->jmp = prev_jmp; + return result; + } + else { + mrb_assert(!MRB_PROC_CFUNC_P(ci[-1].proc)); proc = ci[-1].proc; irep = proc->body.irep; pool = irep->pool; syms = irep->syms; } } + mrb->c->stack[0] = result; regs = mrb->c->stack = ci->stackent; pc = ci->pc; cipop(mrb); @@ -1241,7 +1272,7 @@ RETRY_TRY_BLOCK: mrb_value exc; exc = mrb_exc_new_str_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method"); - mrb->exc = mrb_obj_ptr(exc); + mrb_exc_set(mrb, exc); goto L_RAISE; } recv = regs[0]; @@ -1331,7 +1362,7 @@ RETRY_TRY_BLOCK: mrb_value exc; exc = mrb_exc_new_str_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method"); - mrb->exc = mrb_obj_ptr(exc); + mrb_exc_set(mrb, exc); goto L_RAISE; } stack = e->stack + 1; @@ -1561,7 +1592,7 @@ RETRY_TRY_BLOCK: } 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 = mrb_obj_ptr(exc); + mrb_exc_set(mrb, exc); goto L_RAISE; } /* automatic yield at the end */ @@ -1601,14 +1632,19 @@ RETRY_TRY_BLOCK: while (eidx > mrb->c->ci[-1].eidx) { ecall(mrb, --eidx); } + if (mrb->c->vmexec && !mrb->c->ci->target_class) { + mrb->c->vmexec = FALSE; + mrb->jmp = prev_jmp; + return v; + } 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; } + pc = ci->pc; DEBUG(printf("from :%s\n", mrb_sym2name(mrb, ci->mid))); proc = mrb->c->ci->proc; irep = proc->body.irep; @@ -2323,7 +2359,7 @@ RETRY_TRY_BLOCK: /* 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 = mrb_obj_ptr(exc); + mrb_exc_set(mrb, exc); goto L_RAISE; } regs[GETARG_A(i)] = mrb_obj_value(mrb->c->ci->target_class); @@ -2381,7 +2417,7 @@ RETRY_TRY_BLOCK: else { exc = mrb_exc_new_str(mrb, E_LOCALJUMP_ERROR, msg); } - mrb->exc = mrb_obj_ptr(exc); + mrb_exc_set(mrb, exc); goto L_RAISE; } } @@ -2398,30 +2434,24 @@ RETRY_TRY_BLOCK: 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) */ + return mrb_vm_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_top_run(mrb_state *mrb, struct RProc *proc, mrb_value self, 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); + return mrb_vm_run(mrb, proc, self, 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); + v = mrb_vm_run(mrb, proc, self, 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); -} diff --git a/test/assert.rb b/test/assert.rb index 6fad58fa8..e8368c64c 100644 --- a/test/assert.rb +++ b/test/assert.rb @@ -10,11 +10,7 @@ def t_print(*args) len = args.size while i < len str = args[i].to_s - begin - __printstr__ str - rescue NoMethodError - __t_printstr__ str rescue print str - end + __t_printstr__ str rescue print str i += 1 end end diff --git a/test/t/exception.rb b/test/t/exception.rb index d27813028..742f4a044 100644 --- a/test/t/exception.rb +++ b/test/t/exception.rb @@ -373,12 +373,47 @@ assert('Raise in ensure') do end end -assert('Raise in rescue') do - assert_raise(ArgumentError) do - begin - raise "" # RuntimeError - rescue - raise ArgumentError +def backtrace_avaialble? + begin + raise "XXX" + rescue => exception + not exception.backtrace.empty? + end +end + +assert('GC in rescue') do + skip "backtrace isn't avaialble" unless backtrace_avaialble? + + line = nil + begin + [1].each do + [2].each do + [3].each do + line = __LINE__; raise "XXX" + end + end + end + rescue => exception + GC.start + assert_equal("#{__FILE__}:#{line}:in Object.call", + exception.backtrace.first) + end +end + +assert('Method call in rescue') do + skip "backtrace isn't avaialble" unless backtrace_avaialble? + + line = nil + begin + [1].each do + [2].each do + line = __LINE__; raise "XXX" + end + end + rescue => exception + [3].each do end + assert_equal("#{__FILE__}:#{line}:in Object.call", + exception.backtrace.first) end end |
