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.
This commit is contained in:
dearblue
2021-02-22 23:32:43 +09:00
parent 792f6ac670
commit 927615e1f0
15 changed files with 634 additions and 25 deletions
+38
View File
@@ -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)
{
+72 -7
View File
@@ -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*