summaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authordearblue <[email protected]>2021-02-22 23:32:43 +0900
committerdearblue <[email protected]>2021-02-22 23:32:43 +0900
commit927615e1f072d8fff3d9b84660cdce15a239e36c (patch)
tree14e14aa860b778176435be8d6d666917d891a9d8 /src
parent792f6ac6700469ddf9be8f87ca3376082f9af7f3 (diff)
downloadmruby-927615e1f072d8fff3d9b84660cdce15a239e36c.tar.gz
mruby-927615e1f072d8fff3d9b84660cdce15a239e36c.zip
Added other methods for `Binding`
- Added to `mruby-binding-core` - `Binding#local_variable_defined?` - `Binding#local_variable_get` - `Binding#local_variable_set` - `Binding#local_variables` - `Binding#receiver` - `Binding#source_location` - `Binding#inspect` - Added to `mruby-proc-binding` - `Proc#binding` The reason for separating `Proc#binding` is that core-mrbgems has a method that returns a closure object to minimize possible problems with being able to manipulate internal variables. By separating it as different mrbgem, each user can judge this problem and incorporate it arbitrarily.
Diffstat (limited to 'src')
-rw-r--r--src/proc.c38
-rw-r--r--src/vm.c79
2 files changed, 110 insertions, 7 deletions
diff --git a/src/proc.c b/src/proc.c
index 89fcd3d28..cfaf37af6 100644
--- a/src/proc.c
+++ b/src/proc.c
@@ -372,6 +372,44 @@ mrb_proc_get_caller(mrb_state *mrb, struct REnv **envp)
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/vm.c b/src/vm.c
index 0e7eb65e5..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
@@ -760,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*