summaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/array.c9
-rw-r--r--src/dump.c102
-rw-r--r--src/gc.c3
-rw-r--r--src/hash.c107
-rw-r--r--src/load.c3
-rw-r--r--src/object.c4
-rw-r--r--src/print.c32
-rw-r--r--src/proc.c109
-rw-r--r--src/state.c6
-rw-r--r--src/vm.c93
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);
diff --git a/src/gc.c b/src/gc.c
index 98a2237d5..07ca61e52 100644
--- a/src/gc.c
+++ b/src/gc.c
@@ -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++;
}
diff --git a/src/vm.c b/src/vm.c
index f8b74034e..2607ba308 100644
--- a/src/vm.c
+++ b/src/vm.c
@@ -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