diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/array.c | 9 | ||||
| -rw-r--r-- | src/dump.c | 102 | ||||
| -rw-r--r-- | src/gc.c | 3 | ||||
| -rw-r--r-- | src/hash.c | 107 | ||||
| -rw-r--r-- | src/load.c | 3 | ||||
| -rw-r--r-- | src/object.c | 4 | ||||
| -rw-r--r-- | src/print.c | 32 | ||||
| -rw-r--r-- | src/proc.c | 109 | ||||
| -rw-r--r-- | src/state.c | 6 | ||||
| -rw-r--r-- | src/vm.c | 93 |
10 files changed, 363 insertions, 105 deletions
diff --git a/src/array.c b/src/array.c index a66ff8183..8ab30bd8e 100644 --- a/src/array.c +++ b/src/array.c @@ -56,18 +56,17 @@ mrb_ary_new(mrb_state *mrb) } /* - * to copy array, use this instead of memcpy because of portability + * To copy array, use this instead of memcpy because of portability * * gcc on ARM may fail optimization of memcpy - * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka3934.html + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56620 * * gcc on MIPS also fail - * http://gcc.gnu.org/bugzilla/show_bug.cgi?id=39755 + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39755 * * memcpy doesn't exist on freestanding environment * * If you optimize for binary size, use memcpy instead of this at your own risk * of above portability issue. * - * see also http://togetter.com/li/462898 - * + * See also https://togetter.com/li/462898 (Japanese) */ static inline void array_copy(mrb_value *dst, const mrb_value *src, mrb_int size) diff --git a/src/dump.c b/src/dump.c index 5173b88e5..51e8dca4e 100644 --- a/src/dump.c +++ b/src/dump.c @@ -23,45 +23,6 @@ static size_t get_irep_record_size_1(mrb_state *mrb, const mrb_irep *irep); # error This code cannot be built on your environment. #endif -#define OPERATOR_SYMBOL(sym_name, name) {name, sym_name, sizeof(sym_name)-1} -struct operator_symbol { - const char *name; - const char *sym_name; - uint16_t sym_name_len; -}; -static const struct operator_symbol operator_table[] = { - OPERATOR_SYMBOL("!", "not"), - OPERATOR_SYMBOL("%", "mod"), - OPERATOR_SYMBOL("&", "and"), - OPERATOR_SYMBOL("*", "mul"), - OPERATOR_SYMBOL("+", "add"), - OPERATOR_SYMBOL("-", "sub"), - OPERATOR_SYMBOL("/", "div"), - OPERATOR_SYMBOL("<", "lt"), - OPERATOR_SYMBOL(">", "gt"), - OPERATOR_SYMBOL("^", "xor"), - OPERATOR_SYMBOL("`", "tick"), - OPERATOR_SYMBOL("|", "or"), - OPERATOR_SYMBOL("~", "neg"), - OPERATOR_SYMBOL("!=", "neq"), - OPERATOR_SYMBOL("!~", "nmatch"), - OPERATOR_SYMBOL("&&", "andand"), - OPERATOR_SYMBOL("**", "pow"), - OPERATOR_SYMBOL("+@", "plus"), - OPERATOR_SYMBOL("-@", "minus"), - OPERATOR_SYMBOL("<<", "lshift"), - OPERATOR_SYMBOL("<=", "le"), - OPERATOR_SYMBOL("==", "eq"), - OPERATOR_SYMBOL("=~", "match"), - OPERATOR_SYMBOL(">=", "ge"), - OPERATOR_SYMBOL(">>", "rshift"), - OPERATOR_SYMBOL("[]", "aref"), - OPERATOR_SYMBOL("||", "oror"), - OPERATOR_SYMBOL("<=>", "cmp"), - OPERATOR_SYMBOL("===", "eqq"), - OPERATOR_SYMBOL("[]=", "aset"), -}; - static size_t get_irep_header_size(mrb_state *mrb) { @@ -806,7 +767,7 @@ dump_irep(mrb_state *mrb, const mrb_irep *irep, uint8_t flags, uint8_t **bin, si section_irep_size += get_irep_record_size(mrb, irep); /* DEBUG section size */ - if (flags & DUMP_DEBUG_INFO) { + if (flags & MRB_DUMP_DEBUG_INFO) { if (debug_info_defined) { section_lineno_size += sizeof(struct rite_section_debug_header); /* filename table */ @@ -842,7 +803,7 @@ dump_irep(mrb_state *mrb, const mrb_irep *irep, uint8_t flags, uint8_t **bin, si sizeof(struct rite_binary_footer); /* write DEBUG section */ - if (flags & DUMP_DEBUG_INFO) { + if (flags & MRB_DUMP_DEBUG_INFO) { if (debug_info_defined) { result = write_section_debug(mrb, irep, cur, filenames, filenames_len); if (result != MRB_DUMP_OK) { @@ -920,11 +881,13 @@ mrb_dump_irep_cfunc(mrb_state *mrb, const mrb_irep *irep, uint8_t flags, FILE *f return MRB_DUMP_WRITE_FAULT; } if (fprintf(fp, - "#ifdef __cplusplus\n" - "extern const uint8_t %s[];\n" - "#endif\n" + "%s\n" "const uint8_t %s[] = {", - initname, initname) < 0) { + (flags & MRB_DUMP_STATIC) ? "static" + : "#ifdef __cplusplus\n" + "extern\n" + "#endif", + initname) < 0) { mrb_free(mrb, bin); return MRB_DUMP_WRITE_FAULT; } @@ -1033,6 +996,45 @@ sym_name_cvar_p(const char *name, mrb_int len) return len >= 3 && name[0] == '@' && sym_name_ivar_p(name+1, len-1); } +#define OPERATOR_SYMBOL(sym_name, name) {name, sym_name, sizeof(sym_name)-1} +struct operator_symbol { + const char *name; + const char *sym_name; + uint16_t sym_name_len; +}; +static const struct operator_symbol operator_table[] = { + OPERATOR_SYMBOL("!", "not"), + OPERATOR_SYMBOL("%", "mod"), + OPERATOR_SYMBOL("&", "and"), + OPERATOR_SYMBOL("*", "mul"), + OPERATOR_SYMBOL("+", "add"), + OPERATOR_SYMBOL("-", "sub"), + OPERATOR_SYMBOL("/", "div"), + OPERATOR_SYMBOL("<", "lt"), + OPERATOR_SYMBOL(">", "gt"), + OPERATOR_SYMBOL("^", "xor"), + OPERATOR_SYMBOL("`", "tick"), + OPERATOR_SYMBOL("|", "or"), + OPERATOR_SYMBOL("~", "neg"), + OPERATOR_SYMBOL("!=", "neq"), + OPERATOR_SYMBOL("!~", "nmatch"), + OPERATOR_SYMBOL("&&", "andand"), + OPERATOR_SYMBOL("**", "pow"), + OPERATOR_SYMBOL("+@", "plus"), + OPERATOR_SYMBOL("-@", "minus"), + OPERATOR_SYMBOL("<<", "lshift"), + OPERATOR_SYMBOL("<=", "le"), + OPERATOR_SYMBOL("==", "eq"), + OPERATOR_SYMBOL("=~", "match"), + OPERATOR_SYMBOL(">=", "ge"), + OPERATOR_SYMBOL(">>", "rshift"), + OPERATOR_SYMBOL("[]", "aref"), + OPERATOR_SYMBOL("||", "oror"), + OPERATOR_SYMBOL("<=>", "cmp"), + OPERATOR_SYMBOL("===", "eqq"), + OPERATOR_SYMBOL("[]=", "aset"), +}; + static const char* sym_operator_name(const char *sym_name, mrb_int len) { @@ -1232,8 +1234,14 @@ mrb_dump_irep_cstruct(mrb_state *mrb, const mrb_irep *irep, uint8_t flags, FILE int max = 1; int n = dump_irep_struct(mrb, irep, flags, fp, initname, 0, init_syms_code, &max); if (n != MRB_DUMP_OK) return n; - fprintf(fp, "#ifdef __cplusplus\nextern const struct RProc %s[];\n#endif\n", initname); - fprintf(fp, "const struct RProc %s[] = {{\n", initname); + fprintf(fp, + "%s\n" + "const struct RProc %s[] = {{\n", + (flags & MRB_DUMP_STATIC) ? "static" + : "#ifdef __cplusplus\n" + "extern\n" + "#endif", + initname); fprintf(fp, "NULL,NULL,MRB_TT_PROC,7,0,{&%s_irep_0},NULL,{NULL},\n}};\n", initname); fputs("static void\n", fp); fprintf(fp, "%s_init_syms(mrb_state *mrb)\n", initname); @@ -559,6 +559,9 @@ mrb_obj_alloc(mrb_state *mrb, enum mrb_vtype ttype, struct RClass *cls) mrb_raisef(mrb, E_TYPE_ERROR, "allocation failure of %C", cls); } } + if (ttype <= MRB_TT_FREE) { + mrb_raisef(mrb, E_TYPE_ERROR, "allocation failure of %C (type %d)", cls, (int)ttype); + } #ifdef MRB_GC_STRESS mrb_full_gc(mrb); diff --git a/src/hash.c b/src/hash.c index 289f02a91..c30a8dec4 100644 --- a/src/hash.c +++ b/src/hash.c @@ -151,20 +151,20 @@ DEFINE_ACCESSOR(ht, ea_n_used, uint32_t, ea_n_used) #else DEFINE_FLAG_ACCESSOR(ar, ea_capa, uint32_t, AR_EA_CAPA) DEFINE_FLAG_ACCESSOR(ar, ea_n_used, uint32_t, AR_EA_N_USED) -DEFINE_ACCESSOR(ht, ea_capa, uint32_t, ht->ea_capa) -DEFINE_ACCESSOR(ht, ea_n_used, uint32_t, ht->ea_n_used) +DEFINE_ACCESSOR(ht, ea_capa, uint32_t, hsh.ht->ea_capa) +DEFINE_ACCESSOR(ht, ea_n_used, uint32_t, hsh.ht->ea_n_used) #endif DEFINE_FLAG_ACCESSOR(ib, bit, uint32_t, IB_BIT) DEFINE_ACCESSOR(ar, size, uint32_t, size) -DEFINE_ACCESSOR(ar, ea, hash_entry*, ea) +DEFINE_ACCESSOR(ar, ea, hash_entry*, hsh.ea) DEFINE_DECREMENTER(ar, size) DEFINE_ACCESSOR(ht, size, uint32_t, size) -DEFINE_ACCESSOR(ht, ea, hash_entry*, ht->ea) -DEFINE_GETTER(ht, ib, uint32_t*, ht->ib) +DEFINE_ACCESSOR(ht, ea, hash_entry*, hsh.ht->ea) +DEFINE_GETTER(ht, ib, uint32_t*, hsh.ht->ib) DEFINE_INCREMENTER(ht, size) DEFINE_DECREMENTER(ht, size) DEFINE_GETTER(h, size, uint32_t, size) -DEFINE_ACCESSOR(h, ht, hash_table*, ht) +DEFINE_ACCESSOR(h, ht, hash_table*, hsh.ht) DEFINE_SWITCHER(ht, HT) #define ea_each_used(ea, n_used, entry_var, code) do { \ @@ -215,40 +215,70 @@ DEFINE_SWITCHER(ht, HT) } while (0) /* - * `h_check_modified` raises an exception when a dangerous modification is - * made to `h` by executing `code`. - * - * This macro is not called if `h->ht` (`h->ea`) is `NULL` (`Hash` size is - * zero). And because the `hash_entry` is rather large, `h->ht->ea` and - * `h->ht->ea_capa` are able to be safely accessed even in AR. This nature - * is used to eliminate branch of AR or HT. - * - * `HT_ASSERT_SAFE_READ` checks if members can be accessed according to its - * assumptions. + * In `h_check_modified()`, in the case of `MRB_NO_BOXING`, `ht_ea()` or + * `ht_ea_capa()` for AR may read uninitialized area (#5332). Therefore, do + * not use those macros for AR in `MRB_NO_BOXING` (but in the case of + * `MRB_64BIT`, `ht_ea_capa()` is the same as `ar_ea_capa()`, so use it). */ -#define HT_ASSERT_SAFE_READ(attr_name) \ +#ifdef MRB_NO_BOXING +# define H_CHECK_MODIFIED_USE_HT_EA_FOR_AR FALSE +# ifdef MRB_64BIT +# define H_CHECK_MODIFIED_USE_HT_EA_CAPA_FOR_AR TRUE +# else +# define H_CHECK_MODIFIED_USE_HT_EA_CAPA_FOR_AR FALSE +# endif /* MRB_64BIT */ +#else +# define H_CHECK_MODIFIED_USE_HT_EA_FOR_AR TRUE +# define H_CHECK_MODIFIED_USE_HT_EA_CAPA_FOR_AR TRUE + /* + * `h_check_modified` raises an exception when a dangerous modification is + * made to `h` by executing `code`. + * + * `h_check_modified` macro is not called if `h->hsh.ht` (`h->hsh.ea`) is `NULL` + * (`Hash` size is zero). And because the `hash_entry` is rather large, + * `h->hsh.ht->ea` and `h->hsh.ht->ea_capa` are able to be safely accessed even for + * AR. This nature is used to eliminate branch of AR or HT. + * + * `HT_ASSERT_SAFE_READ` checks if members can be accessed according to its + * assumptions. + */ +# define HT_ASSERT_SAFE_READ(attr_name) \ mrb_static_assert1( \ offsetof(hash_table, attr_name) + sizeof(((hash_table*)0)->attr_name) <= \ sizeof(hash_entry)) HT_ASSERT_SAFE_READ(ea); -#ifdef MRB_32BIT +# ifdef MRB_32BIT HT_ASSERT_SAFE_READ(ea_capa); -#endif -#undef HT_ASSERT_SAFE_READ -#define h_check_modified(mrb, h, code) do { \ - struct RHash *h__ = h; \ - uint32_t mask = MRB_HASH_HT|MRB_HASH_IB_BIT_MASK|MRB_HASH_AR_EA_CAPA_MASK; \ - uint32_t flags = h__->flags & mask; \ - void* tbl__ = (mrb_assert(h__->ht), h__->ht); \ - uint32_t ht_ea_capa__ = ht_ea_capa(h__); \ - hash_entry *ht_ea__ = ht_ea(h__); \ - code; \ - if (flags != (h__->flags & mask) || \ - tbl__ != h__->ht || \ - ht_ea_capa__ != ht_ea_capa(h__) || \ - ht_ea__ != ht_ea(h__)) { \ - mrb_raise(mrb, E_RUNTIME_ERROR, "hash modified"); \ - } \ +# endif +# undef HT_ASSERT_SAFE_READ +#endif /* MRB_NO_BOXING */ + +/* + * `h_check_modified` raises an exception when a dangerous modification is + * made to `h` by executing `code`. + */ +#define h_check_modified(mrb, h, code) do { \ + struct RHash *h__ = h; \ + uint32_t mask__ = MRB_HASH_HT|MRB_HASH_IB_BIT_MASK|MRB_HASH_AR_EA_CAPA_MASK; \ + uint32_t flags__ = h__->flags & mask__; \ + void* tbl__ = (mrb_assert(h__->hsh.ht), h__->hsh.ht); \ + uint32_t ht_ea_capa__ = 0; \ + hash_entry *ht_ea__ = NULL; \ + if (H_CHECK_MODIFIED_USE_HT_EA_CAPA_FOR_AR || h_ht_p(h__)) { \ + ht_ea_capa__ = ht_ea_capa(h__); \ + } \ + if (H_CHECK_MODIFIED_USE_HT_EA_FOR_AR || h_ht_p(h__)) { \ + ht_ea__ = ht_ea(h__); \ + } \ + code; \ + if (flags__ != (h__->flags & mask__) || \ + tbl__ != h__->hsh.ht || \ + ((H_CHECK_MODIFIED_USE_HT_EA_CAPA_FOR_AR || h_ht_p(h__)) && \ + ht_ea_capa__ != ht_ea_capa(h__)) || \ + ((H_CHECK_MODIFIED_USE_HT_EA_FOR_AR || h_ht_p(h__)) && \ + ht_ea__ != ht_ea(h__))) { \ + mrb_raise(mrb, E_RUNTIME_ERROR, "hash modified"); \ + } \ } while (0) #define U32(v) ((uint32_t)(v)) @@ -718,6 +748,7 @@ ib_bit_for(uint32_t size) static uint32_t ib_byte_size_for(uint32_t ib_bit) { + mrb_assert(IB_INIT_BIT <= ib_bit); uint32_t ary_size = IB_INIT_BIT == 4 ? ib_bit_to_capa(ib_bit) * 2 / IB_TYPE_BIT * ib_bit / 2 : ib_bit_to_capa(ib_bit) / IB_TYPE_BIT * ib_bit; @@ -892,7 +923,13 @@ static void ht_rehash(mrb_state *mrb, struct RHash *h) { /* see comments in `h_rehash` */ - uint32_t size = ht_size(h), w_size = 0, ea_capa = ht_ea_capa(h); + uint32_t size = ht_size(h); + if (size <= AR_MAX_SIZE) { + ht_to_ar(mrb, h); + ar_rehash(mrb, h); + return; + } + uint32_t w_size = 0, ea_capa = ht_ea_capa(h); hash_entry *ea = ht_ea(h); ht_init(mrb, h, 0, ea, ea_capa, h_ht(h), ib_bit_for(size)); ht_set_size(h, size); diff --git a/src/load.c b/src/load.c index 93c16a5e1..68be23546 100644 --- a/src/load.c +++ b/src/load.c @@ -153,6 +153,7 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flag i64 <<= 32; i64 |= bin_to_uint32(src); src += sizeof(uint32_t); + pool[i].tt = tt; pool[i].u.i64 = (int64_t)i64; } break; @@ -163,7 +164,7 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flag case IREP_TT_FLOAT: #ifndef MRB_NO_FLOAT pool[i].tt = tt; - pool[i].u.f = str_to_double(mrb, (const char*)src); + pool[i].u.f = str_to_double(mrb, (const char*)src); src += sizeof(double); break; #else diff --git a/src/object.c b/src/object.c index d6474ae97..0c6b86630 100644 --- a/src/object.c +++ b/src/object.c @@ -288,7 +288,7 @@ mrb_init_object(mrb_state *mrb) struct RClass *f; mrb->nil_class = n = mrb_define_class(mrb, "NilClass", mrb->object_class); - MRB_SET_INSTANCE_TT(n, MRB_TT_TRUE); + MRB_SET_INSTANCE_TT(n, MRB_TT_FALSE); mrb_undef_class_method(mrb, n, "new"); mrb_define_method(mrb, n, "&", false_and, MRB_ARGS_REQ(1)); /* 15.2.4.3.1 */ mrb_define_method(mrb, n, "^", false_xor, MRB_ARGS_REQ(1)); /* 15.2.4.3.2 */ @@ -307,7 +307,7 @@ mrb_init_object(mrb_state *mrb) mrb_define_method(mrb, t, "inspect", true_to_s, MRB_ARGS_NONE()); mrb->false_class = f = mrb_define_class(mrb, "FalseClass", mrb->object_class); - MRB_SET_INSTANCE_TT(f, MRB_TT_TRUE); + MRB_SET_INSTANCE_TT(f, MRB_TT_FALSE); mrb_undef_class_method(mrb, f, "new"); mrb_define_method(mrb, f, "&", false_and, MRB_ARGS_REQ(1)); /* 15.2.6.3.1 */ mrb_define_method(mrb, f, "^", false_xor, MRB_ARGS_REQ(1)); /* 15.2.6.3.2 */ diff --git a/src/print.c b/src/print.c index 607eb9d1f..4af871b43 100644 --- a/src/print.c +++ b/src/print.c @@ -28,10 +28,6 @@ printstr(mrb_value obj, FILE *stream) printcstr(RSTRING_PTR(obj), RSTRING_LEN(obj), stream); } } -#else -# define printcstr(str, len, stream) (void)0 -# define printstr(obj, stream) (void)0 -#endif void mrb_core_init_printabort(void) @@ -52,6 +48,7 @@ mrb_p(mrb_state *mrb, mrb_value obj) } } + MRB_API void mrb_print_error(mrb_state *mrb) { @@ -69,3 +66,30 @@ mrb_show_copyright(mrb_state *mrb) { printstr(mrb_const_get(mrb, mrb_obj_value(mrb->object_class), MRB_SYM(MRUBY_COPYRIGHT)), stdout); } + +#else +void +mrb_core_init_printabort(void) +{ +} + +MRB_API void +mrb_p(mrb_state *mrb, mrb_value obj) +{ +} + +MRB_API void +mrb_print_error(mrb_state *mrb) +{ +} + +MRB_API void +mrb_show_version(mrb_state *mrb) +{ +} + +MRB_API void +mrb_show_copyright(mrb_state *mrb) +{ +} +#endif diff --git a/src/proc.c b/src/proc.c index 870d5ea16..4a202525c 100644 --- a/src/proc.c +++ b/src/proc.c @@ -10,6 +10,8 @@ #include <mruby/opcode.h> #include <mruby/data.h> #include <mruby/presym.h> +#include <mruby/array.h> +#include <mruby/hash.h> static const mrb_code call_iseq[] = { OP_CALL, @@ -169,11 +171,11 @@ mrb_proc_cfunc_env_get(mrb_state *mrb, mrb_int idx) struct REnv *e; if (!p || !MRB_PROC_CFUNC_P(p)) { - mrb_raise(mrb, E_TYPE_ERROR, "Can't get cfunc env from non-cfunc proc."); + mrb_raise(mrb, E_TYPE_ERROR, "Can't get cfunc env from non-cfunc proc"); } e = MRB_PROC_ENV(p); if (!e) { - mrb_raise(mrb, E_TYPE_ERROR, "Can't get cfunc env from cfunc Proc without REnv."); + mrb_raise(mrb, E_TYPE_ERROR, "Can't get cfunc env from cfunc Proc without REnv"); } if (idx < 0 || MRB_ENV_LEN(e) <= idx) { mrb_raisef(mrb, E_INDEX_ERROR, "Env index out of range: %i (expected: 0 <= index < %i)", @@ -305,6 +307,109 @@ mrb_proc_arity(const struct RProc *p) return arity; } +mrb_value +mrb_proc_local_variables(mrb_state *mrb, const struct RProc *proc) +{ + const mrb_irep *irep; + mrb_value vars; + size_t i; + + if (proc == NULL || MRB_PROC_CFUNC_P(proc)) { + return mrb_ary_new(mrb); + } + vars = mrb_hash_new(mrb); + while (proc) { + if (MRB_PROC_CFUNC_P(proc)) break; + irep = proc->body.irep; + if (irep->lv) { + for (i = 0; i + 1 < irep->nlocals; ++i) { + if (irep->lv[i]) { + mrb_sym sym = irep->lv[i]; + const char *name = mrb_sym_name(mrb, sym); + switch (name[0]) { + case '*': case '&': + break; + default: + mrb_hash_set(mrb, vars, mrb_symbol_value(sym), mrb_true_value()); + break; + } + } + } + } + if (MRB_PROC_SCOPE_P(proc)) break; + proc = proc->upper; + } + + return mrb_hash_keys(mrb, vars); +} + +const struct RProc * +mrb_proc_get_caller(mrb_state *mrb, struct REnv **envp) +{ + struct mrb_context *c = mrb->c; + mrb_callinfo *ci = (c->ci > c->cibase) ? c->ci - 1 : c->cibase; + const struct RProc *proc = ci->proc; + + if (!proc || MRB_PROC_CFUNC_P(proc)) { + if (envp) *envp = NULL; + } + else { + struct RClass *tc = MRB_PROC_TARGET_CLASS(proc); + struct REnv *e = mrb_vm_ci_env(ci); + + if (e == NULL) { + int nstacks = proc->body.irep->nlocals; + e = mrb_env_new(mrb, c, ci, nstacks, ci->stack, tc); + ci->u.env = e; + } + else if (tc) { + e->c = tc; + mrb_field_write_barrier(mrb, (struct RBasic*)e, (struct RBasic*)tc); + } + if (envp) *envp = e; + } + + return proc; +} + +#define IREP_LVAR_MERGE_DEFAULT 50 +#define IREP_LVAR_MERGE_MINIMUM 8 +#define IREP_LVAR_MERGE_MAXIMUM 240 + +#ifdef MRB_IREP_LVAR_MERGE_LIMIT +# define IREP_LVAR_MERGE_LIMIT \ + ((MRB_IREP_LVAR_MERGE_LIMIT) < IREP_LVAR_MERGE_MINIMUM ? IREP_LVAR_MERGE_MINIMUM : \ + (MRB_IREP_LVAR_MERGE_LIMIT) > IREP_LVAR_MERGE_MAXIMUM ? IREP_LVAR_MERGE_MAXIMUM : \ + (MRB_IREP_LVAR_MERGE_LIMIT)) +#else +# define IREP_LVAR_MERGE_LIMIT IREP_LVAR_MERGE_DEFAULT +#endif + +void +mrb_proc_merge_lvar(mrb_state *mrb, mrb_irep *irep, struct REnv *env, int num, const mrb_sym *lv, const mrb_value *stack) +{ + mrb_assert(!(irep->flags & MRB_IREP_NO_FREE)); + + if ((irep->nlocals + num) > IREP_LVAR_MERGE_LIMIT) { + mrb_raise(mrb, E_RUNTIME_ERROR, "too many local variables for binding (mruby limitation)"); + } + + if (!lv) { + mrb_raise(mrb, E_RUNTIME_ERROR, "unavailable local variable names"); + } + + irep->lv = (mrb_sym*)mrb_realloc(mrb, (mrb_sym*)irep->lv, sizeof(mrb_sym) * (irep->nlocals + num)); + env->stack = (mrb_value*)mrb_realloc(mrb, env->stack, sizeof(mrb_value) * (irep->nlocals + 1 /* self */ + num)); + + mrb_sym *destlv = (mrb_sym*)irep->lv + irep->nlocals - 1 /* self */; + mrb_value *destst = env->stack + irep->nlocals; + memmove(destlv, lv, sizeof(mrb_sym) * num); + memmove(destst, stack, sizeof(mrb_value) * num); + irep->nlocals += num; + irep->nregs = irep->nlocals; + MRB_ENV_SET_LEN(env, irep->nlocals); +} + void mrb_init_proc(mrb_state *mrb) { diff --git a/src/state.c b/src/state.c index 83cbf1629..0ba6db6ce 100644 --- a/src/state.c +++ b/src/state.c @@ -110,6 +110,12 @@ void mrb_irep_incref(mrb_state *mrb, mrb_irep *irep) { if (irep->flags & MRB_IREP_NO_FREE) return; + if (irep->refcnt == UINT16_MAX) { + mrb_garbage_collect(mrb); + if (irep->refcnt == UINT16_MAX) { + mrb_raise(mrb, E_RUNTIME_ERROR, "too many irep references"); + } + } irep->refcnt++; } @@ -483,13 +483,73 @@ 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()); } +#define DECOMPOSE32(n) (((n) >> 24) & 0xff), (((n) >> 16) & 0xff), (((n) >> 8) & 0xff), (((n) >> 0) & 0xff) +#define CATCH_HANDLER_MAKE_BYTECODE(t, b, e, j) t, DECOMPOSE32(b), DECOMPOSE32(e), DECOMPOSE32(j) +#define CATCH_HANDLER_NUM_TO_BYTE(n) ((n) * sizeof(struct mrb_irep_catch_handler)) + +static void +mrb_exec_irep_prepare_posthook(mrb_state *mrb, mrb_callinfo *ci, int nregs, mrb_func_t posthook) +{ + /* + * stack: [proc, errinfo, return value by called proc] + * + * begin + * OP_NOP # A dummy instruction built in to make the catch handler react. + * ensure + * OP_EXCEPT R1 # Save the exception object. + * OP_CALL # Call a C function for the hook. + * # The stack is kept as it is in the called proc. + * # The exception will be rethrown within the hook function. + * end + */ + static const mrb_code hook_iseq[] = { + OP_NOP, + OP_EXCEPT, 1, + OP_CALL, + CATCH_HANDLER_MAKE_BYTECODE(MRB_CATCH_ENSURE, 0, 1, 1), + }; + static const mrb_irep hook_irep = { + 1, 3, 1, MRB_IREP_STATIC, hook_iseq, + NULL, NULL, NULL, NULL, NULL, + sizeof(hook_iseq) / sizeof(hook_iseq[0]) - CATCH_HANDLER_NUM_TO_BYTE(1), + 0, 0, 0, 0 + }; + static const struct RProc hook_caller = { + NULL, NULL, MRB_TT_PROC, 7 /* GC_RED */, MRB_FL_OBJ_IS_FROZEN, { &hook_irep }, NULL, { NULL } + }; + + struct RProc *hook = mrb_proc_new_cfunc(mrb, posthook); + int acc = 2; + memmove(ci->stack + acc, ci->stack, sizeof(mrb_value) * nregs); + ci->stack[0] = mrb_obj_value(hook); + ci->stack[1] = mrb_nil_value(); + mrb_callinfo hook_ci = { 0, 0, ci->acc, &hook_caller, ci->stack, &hook_iseq[1], { NULL } }; + ci = cipush(mrb, acc, acc, NULL, ci[0].proc, ci[0].mid, ci[0].argc); + ci->u.env = ci[-1].u.env; + ci[-1] = hook_ci; +} + +/* + * If `posthook` is given, `posthook` will be called even if an + * exception or global jump occurs in `p`. Exception or global jump objects + * are stored in `mrb->c->stack[1]` and should be rethrown in `posthook`. + * + * if (!mrb_nil_p(mrb->c->stack[1])) { + * mrb_exc_raise(mrb, mrb->c->stack[1]); + * } + * + * If you want to return the return value by `proc` as it is, please do + * `return mrb->c->stack[2]`. + * + * However, if `proc` is a C function, it will be ignored. + */ mrb_value -mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p) +mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p, mrb_func_t posthook) { mrb_callinfo *ci = mrb->c->ci; mrb_int keep, nregs; - mrb->c->ci->stack[0] = self; + ci->stack[0] = self; mrb_vm_ci_proc_set(ci, p); if (MRB_PROC_CFUNC_P(p)) { return MRB_PROC_CFUNC(p)(mrb, self); @@ -497,12 +557,17 @@ mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p) nregs = p->body.irep->nregs; if (ci->argc < 0) keep = 3; else keep = ci->argc + 2; + int extra = posthook ? (2 /* hook proc + errinfo */) : 0; if (nregs < keep) { - mrb_stack_extend(mrb, keep); + mrb_stack_extend(mrb, keep + extra); } else { - mrb_stack_extend(mrb, nregs); - stack_clear(mrb->c->ci->stack+keep, nregs-keep); + mrb_stack_extend(mrb, nregs + extra); + stack_clear(ci->stack+keep, nregs-keep + extra); + } + + if (posthook) { + mrb_exec_irep_prepare_posthook(mrb, ci, (nregs < keep ? keep : nregs), posthook); } cipush(mrb, 0, 0, NULL, NULL, 0, 0); @@ -573,7 +638,7 @@ mrb_f_send(mrb_state *mrb, mrb_value self) } return MRB_METHOD_CFUNC(m)(mrb, self); } - return mrb_exec_irep(mrb, self, MRB_METHOD_PROC(m)); + return mrb_exec_irep(mrb, self, MRB_METHOD_PROC(m), NULL); } static mrb_value @@ -659,11 +724,21 @@ mrb_value mrb_obj_instance_eval(mrb_state *mrb, mrb_value self) { mrb_value a, b; + struct RClass *c; if (mrb_get_args(mrb, "|S&", &a, &b) == 1) { mrb_raise(mrb, E_NOTIMP_ERROR, "instance_eval with string not implemented"); } - return eval_under(mrb, self, b, mrb_singleton_class_ptr(mrb, self)); + switch (mrb_type(self)) { + case MRB_TT_MODULE: + case MRB_TT_CLASS: + case MRB_TT_ICLASS: + c = mrb_class_ptr(self); + break; + default: + c = mrb_singleton_class_ptr(mrb, self); + } + return eval_under(mrb, self, b, c); } MRB_API mrb_value @@ -750,7 +825,7 @@ mrb_yield_cont(mrb_state *mrb, mrb_value b, mrb_value self, mrb_int argc, const mrb->c->ci->stack[1] = mrb_ary_new_from_values(mrb, argc, argv); mrb->c->ci->stack[2] = mrb_nil_value(); ci->argc = -1; - return mrb_exec_irep(mrb, self, p); + return mrb_exec_irep(mrb, self, p, NULL); } static struct RBreak* @@ -1012,7 +1087,7 @@ mrb_vm_exec(mrb_state *mrb, const struct RProc *proc, const mrb_code *pc) const struct mrb_irep_catch_handler *ch; #ifdef DIRECT_THREADED - static void *optable[] = { + static const void * const optable[] = { #define OPCODE(x,_) &&L_OP_ ## x, #include "mruby/ops.h" #undef OPCODE |
