diff options
| author | dearblue <[email protected]> | 2021-02-22 23:32:43 +0900 |
|---|---|---|
| committer | dearblue <[email protected]> | 2021-02-22 23:32:43 +0900 |
| commit | 927615e1f072d8fff3d9b84660cdce15a239e36c (patch) | |
| tree | 14e14aa860b778176435be8d6d666917d891a9d8 /src/vm.c | |
| parent | 792f6ac6700469ddf9be8f87ca3376082f9af7f3 (diff) | |
| download | mruby-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/vm.c')
| -rw-r--r-- | src/vm.c | 79 |
1 files changed, 72 insertions, 7 deletions
@@ -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* |
