mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
Merge pull request #5493 from dearblue/binding.2
Fixed finding variables from `proc` in `binding.eval` failed
This commit is contained in:
@@ -24,16 +24,16 @@ binding_extract_pc(mrb_state *mrb, mrb_value binding)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct RProc *
|
const struct RProc *
|
||||||
binding_extract_proc(mrb_state *mrb, mrb_value binding)
|
mrb_binding_extract_proc(mrb_state *mrb, mrb_value binding)
|
||||||
{
|
{
|
||||||
mrb_value obj = mrb_iv_get(mrb, binding, MRB_SYM(proc));
|
mrb_value obj = mrb_iv_get(mrb, binding, MRB_SYM(proc));
|
||||||
mrb_check_type(mrb, obj, MRB_TT_PROC);
|
mrb_check_type(mrb, obj, MRB_TT_PROC);
|
||||||
return mrb_proc_ptr(obj);
|
return mrb_proc_ptr(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct REnv *
|
struct REnv *
|
||||||
binding_extract_env(mrb_state *mrb, mrb_value binding)
|
mrb_binding_extract_env(mrb_state *mrb, mrb_value binding)
|
||||||
{
|
{
|
||||||
mrb_value obj = mrb_iv_get(mrb, binding, MRB_SYM(env));
|
mrb_value obj = mrb_iv_get(mrb, binding, MRB_SYM(env));
|
||||||
if (mrb_nil_p(obj)) {
|
if (mrb_nil_p(obj)) {
|
||||||
@@ -108,8 +108,8 @@ binding_local_variable_defined_p(mrb_state *mrb, mrb_value self)
|
|||||||
mrb_sym varname;
|
mrb_sym varname;
|
||||||
mrb_get_args(mrb, "n", &varname);
|
mrb_get_args(mrb, "n", &varname);
|
||||||
|
|
||||||
const struct RProc *proc = binding_extract_proc(mrb, self);
|
const struct RProc *proc = mrb_binding_extract_proc(mrb, self);
|
||||||
struct REnv *env = binding_extract_env(mrb, self);
|
struct REnv *env = mrb_binding_extract_env(mrb, self);
|
||||||
mrb_value *e = binding_local_variable_search(mrb, proc, env, varname);
|
mrb_value *e = binding_local_variable_search(mrb, proc, env, varname);
|
||||||
if (e) {
|
if (e) {
|
||||||
return mrb_true_value();
|
return mrb_true_value();
|
||||||
@@ -129,8 +129,8 @@ binding_local_variable_get(mrb_state *mrb, mrb_value self)
|
|||||||
mrb_sym varname;
|
mrb_sym varname;
|
||||||
mrb_get_args(mrb, "n", &varname);
|
mrb_get_args(mrb, "n", &varname);
|
||||||
|
|
||||||
const struct RProc *proc = binding_extract_proc(mrb, self);
|
const struct RProc *proc = mrb_binding_extract_proc(mrb, self);
|
||||||
struct REnv *env = binding_extract_env(mrb, self);
|
struct REnv *env = mrb_binding_extract_env(mrb, self);
|
||||||
mrb_value *e = binding_local_variable_search(mrb, proc, env, varname);
|
mrb_value *e = binding_local_variable_search(mrb, proc, env, varname);
|
||||||
if (!e) {
|
if (!e) {
|
||||||
mrb_raisef(mrb, E_NAME_ERROR, "local variable %!n is not defined", varname);
|
mrb_raisef(mrb, E_NAME_ERROR, "local variable %!n is not defined", varname);
|
||||||
@@ -146,8 +146,8 @@ binding_local_variable_set(mrb_state *mrb, mrb_value self)
|
|||||||
mrb_value obj;
|
mrb_value obj;
|
||||||
mrb_get_args(mrb, "no", &varname, &obj);
|
mrb_get_args(mrb, "no", &varname, &obj);
|
||||||
|
|
||||||
const struct RProc *proc = binding_extract_proc(mrb, self);
|
const struct RProc *proc = mrb_binding_extract_proc(mrb, self);
|
||||||
struct REnv *env = binding_extract_env(mrb, self);
|
struct REnv *env = mrb_binding_extract_env(mrb, self);
|
||||||
mrb_value *e = binding_local_variable_search(mrb, proc, env, varname);
|
mrb_value *e = binding_local_variable_search(mrb, proc, env, varname);
|
||||||
if (e) {
|
if (e) {
|
||||||
*e = obj;
|
*e = obj;
|
||||||
@@ -184,7 +184,7 @@ binding_source_location(mrb_state *mrb, mrb_value self)
|
|||||||
}
|
}
|
||||||
|
|
||||||
mrb_value srcloc;
|
mrb_value srcloc;
|
||||||
const struct RProc *proc = binding_extract_proc(mrb, self);
|
const struct RProc *proc = mrb_binding_extract_proc(mrb, self);
|
||||||
if (!proc || MRB_PROC_CFUNC_P(proc) ||
|
if (!proc || MRB_PROC_CFUNC_P(proc) ||
|
||||||
!proc->upper || MRB_PROC_CFUNC_P(proc->upper)) {
|
!proc->upper || MRB_PROC_CFUNC_P(proc->upper)) {
|
||||||
srcloc = mrb_nil_value();
|
srcloc = mrb_nil_value();
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
class Binding
|
|
||||||
def eval(expr, *args)
|
|
||||||
Kernel.eval(expr, self, *args)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
#include <mruby.h>
|
||||||
|
#include <mruby/array.h>
|
||||||
|
#include <mruby/class.h>
|
||||||
|
#include <mruby/compile.h>
|
||||||
|
#include <mruby/error.h>
|
||||||
|
#include <mruby/proc.h>
|
||||||
|
#include <mruby/presym.h>
|
||||||
|
#include <mruby/string.h>
|
||||||
|
|
||||||
|
mrb_noreturn void mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args);
|
||||||
|
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_value mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p);
|
||||||
|
const struct RProc *mrb_binding_extract_proc(mrb_state *mrb, mrb_value binding);
|
||||||
|
struct REnv *mrb_binding_extract_env(mrb_state *mrb, mrb_value binding);
|
||||||
|
typedef mrb_bool mrb_parser_foreach_top_variable_func(mrb_state *mrb, mrb_sym sym, void *user);
|
||||||
|
void mrb_parser_foreach_top_variable(mrb_state *mrb, struct mrb_parser_state *p, mrb_parser_foreach_top_variable_func *func, void *user);
|
||||||
|
|
||||||
|
static void
|
||||||
|
insert_args(mrb_state *mrb, size_t offset, mrb_value obj)
|
||||||
|
{
|
||||||
|
mrb_callinfo *ci = mrb->c->ci;
|
||||||
|
mrb_value *argp = ci->stack + 1 /* recv */;
|
||||||
|
|
||||||
|
if (ci->argc < 0) {
|
||||||
|
mrb_ary_splice(mrb, *argp, offset, 0, obj);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
argp += offset;
|
||||||
|
mrb_stack_extend(mrb, ci->argc + offset + 2 /* recv + block */);
|
||||||
|
memmove(argp + 1 /* obj */, argp, sizeof(mrb_value) * (ci->argc - offset + 1 /* block */));
|
||||||
|
*argp = obj;
|
||||||
|
ci->argc++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
binding_eval_error_check(mrb_state *mrb, struct mrb_parser_state *p, const char *file)
|
||||||
|
{
|
||||||
|
if (!p) {
|
||||||
|
mrb_raise(mrb, E_RUNTIME_ERROR, "Failed to create parser state (out of memory)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 < p->nerr) {
|
||||||
|
mrb_value str;
|
||||||
|
|
||||||
|
if (file) {
|
||||||
|
str = mrb_format(mrb, "file %s line %d: %s",
|
||||||
|
file,
|
||||||
|
p->error_buffer[0].lineno,
|
||||||
|
p->error_buffer[0].message);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
str = mrb_format(mrb, "line %d: %s",
|
||||||
|
p->error_buffer[0].lineno,
|
||||||
|
p->error_buffer[0].message);
|
||||||
|
}
|
||||||
|
mrb_exc_raise(mrb, mrb_exc_new_str(mrb, E_SYNTAX_ERROR, str));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LV_BUFFERS 8
|
||||||
|
|
||||||
|
struct expand_lvspace {
|
||||||
|
mrb_irep *irep;
|
||||||
|
struct REnv *env;
|
||||||
|
size_t numvar;
|
||||||
|
mrb_sym syms[LV_BUFFERS];
|
||||||
|
};
|
||||||
|
|
||||||
|
static mrb_bool
|
||||||
|
expand_lvspace(mrb_state *mrb, mrb_sym sym, void *user)
|
||||||
|
{
|
||||||
|
struct expand_lvspace *p = (struct expand_lvspace*)user;
|
||||||
|
mrb_int symlen;
|
||||||
|
const char *symname = mrb_sym_name_len(mrb, sym, &symlen);
|
||||||
|
|
||||||
|
if (symname && symlen > 0) {
|
||||||
|
if (symname[0] != '&' && symname[0] != '*') {
|
||||||
|
p->syms[p->numvar++] = sym;
|
||||||
|
if (p->numvar >= LV_BUFFERS) {
|
||||||
|
mrb_proc_merge_lvar(mrb, p->irep, p->env, p->numvar, p->syms, NULL);
|
||||||
|
p->numvar = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct binding_eval_prepare_body {
|
||||||
|
mrb_value binding;
|
||||||
|
const char *file;
|
||||||
|
const char *expr;
|
||||||
|
mrb_int exprlen;
|
||||||
|
mrbc_context *mrbc;
|
||||||
|
struct mrb_parser_state *pstate;
|
||||||
|
};
|
||||||
|
|
||||||
|
static mrb_value
|
||||||
|
binding_eval_prepare_body(mrb_state *mrb, void *opaque)
|
||||||
|
{
|
||||||
|
struct binding_eval_prepare_body *p = (struct binding_eval_prepare_body*)opaque;
|
||||||
|
|
||||||
|
const struct RProc *proc = mrb_binding_extract_proc(mrb, p->binding);
|
||||||
|
mrb_assert(!MRB_PROC_CFUNC_P(proc));
|
||||||
|
|
||||||
|
p->mrbc = mrbc_context_new(mrb);
|
||||||
|
mrbc_filename(mrb, p->mrbc, p->file ? p->file : "(eval)");
|
||||||
|
p->mrbc->upper = proc;
|
||||||
|
p->mrbc->capture_errors = TRUE;
|
||||||
|
p->pstate = mrb_parse_nstring(mrb, p->expr, p->exprlen, p->mrbc);
|
||||||
|
binding_eval_error_check(mrb, p->pstate, p->file);
|
||||||
|
|
||||||
|
struct expand_lvspace args = {
|
||||||
|
(mrb_irep*)proc->body.irep,
|
||||||
|
mrb_binding_extract_env(mrb, p->binding),
|
||||||
|
0,
|
||||||
|
{ 0 }
|
||||||
|
};
|
||||||
|
mrb_parser_foreach_top_variable(mrb, p->pstate, expand_lvspace, &args);
|
||||||
|
if (args.numvar > 0) {
|
||||||
|
mrb_proc_merge_lvar(mrb, args.irep, args.env, args.numvar, args.syms, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mrb_nil_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
binding_eval_prepare(mrb_state *mrb, mrb_value binding)
|
||||||
|
{
|
||||||
|
struct binding_eval_prepare_body d = { binding, NULL, NULL, 0, NULL, NULL };
|
||||||
|
mrb_int argc;
|
||||||
|
mrb_value *argv;
|
||||||
|
mrb_get_args(mrb, "s|z*!", &d.expr, &d.exprlen, &d.file, &argv, &argc);
|
||||||
|
|
||||||
|
mrb_bool error;
|
||||||
|
mrb_value ret = mrb_protect_error(mrb, binding_eval_prepare_body, &d, &error);
|
||||||
|
if (d.pstate) mrb_parser_free(d.pstate);
|
||||||
|
if (d.mrbc) mrbc_context_free(mrb, d.mrbc);
|
||||||
|
if (error) mrb_exc_raise(mrb, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
static mrb_value
|
||||||
|
mrb_binding_eval(mrb_state *mrb, mrb_value binding)
|
||||||
|
{
|
||||||
|
binding_eval_prepare(mrb, binding);
|
||||||
|
|
||||||
|
struct RClass *c = mrb->kernel_module;
|
||||||
|
mrb_method_t m = mrb_method_search_vm(mrb, &c, MRB_SYM(eval));
|
||||||
|
if (MRB_METHOD_UNDEF_P(m)) {
|
||||||
|
int argc = mrb->c->ci->argc;
|
||||||
|
mrb_value *argv = mrb->c->ci->stack + 1;
|
||||||
|
mrb_value args = (argc < 0) ? argv[0] : mrb_ary_new_from_values(mrb, argc, argv);
|
||||||
|
mrb_method_missing(mrb, MRB_SYM(eval), binding, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
insert_args(mrb, 1, binding);
|
||||||
|
struct RProc *proc = MRB_METHOD_PROC_P(m) ? MRB_METHOD_PROC(m) : mrb_proc_new_cfunc(mrb, MRB_METHOD_FUNC(m));
|
||||||
|
mrb->c->ci->u.target_class = c;
|
||||||
|
return mrb_exec_irep(mrb, binding, proc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
mrb_mruby_binding_gem_init(mrb_state *mrb)
|
||||||
|
{
|
||||||
|
struct RClass *binding = mrb_class_get_id(mrb, MRB_SYM(Binding));
|
||||||
|
mrb_define_method(mrb, binding, "eval", mrb_binding_eval, MRB_ARGS_ANY());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
mrb_mruby_binding_gem_final(mrb_state *mrb)
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -68,3 +68,10 @@ assert "Binding#eval with Binding.new via Method" do
|
|||||||
|
|
||||||
assert_true true
|
assert_true true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
assert "access local variables into procs" do
|
||||||
|
bx = binding
|
||||||
|
block = bx.eval("a = 1; proc { a }")
|
||||||
|
bx.eval("a = 2")
|
||||||
|
assert_equal 2, block.call
|
||||||
|
end
|
||||||
|
|||||||
@@ -7759,3 +7759,19 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef mrb_bool mrb_parser_foreach_top_variable_func(mrb_state *mrb, mrb_sym sym, void *user);
|
||||||
|
void mrb_parser_foreach_top_variable(mrb_state *mrb, struct mrb_parser_state *p, mrb_parser_foreach_top_variable_func *func, void *user);
|
||||||
|
|
||||||
|
void
|
||||||
|
mrb_parser_foreach_top_variable(mrb_state *mrb, struct mrb_parser_state *p, mrb_parser_foreach_top_variable_func *func, void *user)
|
||||||
|
{
|
||||||
|
const mrb_ast_node *n = p->tree;
|
||||||
|
if ((intptr_t)n->car == NODE_SCOPE) {
|
||||||
|
n = n->cdr->car;
|
||||||
|
for (; n; n = n->cdr) {
|
||||||
|
mrb_sym sym = (intptr_t)n->car;
|
||||||
|
if (sym && !func(mrb, sym, user)) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -13951,3 +13951,19 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef mrb_bool mrb_parser_foreach_top_variable_func(mrb_state *mrb, mrb_sym sym, void *user);
|
||||||
|
void mrb_parser_foreach_top_variable(mrb_state *mrb, struct mrb_parser_state *p, mrb_parser_foreach_top_variable_func *func, void *user);
|
||||||
|
|
||||||
|
void
|
||||||
|
mrb_parser_foreach_top_variable(mrb_state *mrb, struct mrb_parser_state *p, mrb_parser_foreach_top_variable_func *func, void *user)
|
||||||
|
{
|
||||||
|
const mrb_ast_node *n = p->tree;
|
||||||
|
if ((intptr_t)n->car == NODE_SCOPE) {
|
||||||
|
n = n->cdr->car;
|
||||||
|
for (; n; n = n->cdr) {
|
||||||
|
mrb_sym sym = (intptr_t)n->car;
|
||||||
|
if (sym && !func(mrb, sym, user)) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
#include <mruby/variable.h>
|
#include <mruby/variable.h>
|
||||||
|
|
||||||
struct REnv *mrb_env_new(mrb_state *mrb, struct mrb_context *c, mrb_callinfo *ci, int nstacks, mrb_value *stack, struct RClass *tc);
|
struct REnv *mrb_env_new(mrb_state *mrb, struct mrb_context *c, mrb_callinfo *ci, int nstacks, mrb_value *stack, struct RClass *tc);
|
||||||
mrb_value mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p, mrb_func_t posthook);
|
mrb_value mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p);
|
||||||
mrb_value mrb_obj_instance_eval(mrb_state *mrb, mrb_value self);
|
mrb_value mrb_obj_instance_eval(mrb_state *mrb, mrb_value self);
|
||||||
mrb_value mrb_mod_module_eval(mrb_state*, mrb_value);
|
mrb_value mrb_mod_module_eval(mrb_state*, mrb_value);
|
||||||
void mrb_codedump_all(mrb_state*, struct RProc*);
|
void mrb_codedump_all(mrb_state*, struct RProc*);
|
||||||
@@ -129,44 +129,13 @@ create_proc_from_string(mrb_state *mrb, const char *s, mrb_int len, mrb_value bi
|
|||||||
}
|
}
|
||||||
|
|
||||||
static mrb_value
|
static mrb_value
|
||||||
exec_irep(mrb_state *mrb, mrb_value self, struct RProc *proc, mrb_func_t posthook)
|
exec_irep(mrb_state *mrb, mrb_value self, struct RProc *proc)
|
||||||
{
|
{
|
||||||
/* no argument passed from eval() */
|
/* no argument passed from eval() */
|
||||||
mrb->c->ci->argc = 0;
|
mrb->c->ci->argc = 0;
|
||||||
/* clear block */
|
/* clear block */
|
||||||
mrb->c->ci->stack[1] = mrb_nil_value();
|
mrb->c->ci->stack[1] = mrb_nil_value();
|
||||||
return mrb_exec_irep(mrb, self, proc, posthook);
|
return mrb_exec_irep(mrb, self, proc);
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
eval_merge_lvar(mrb_state *mrb, mrb_irep *irep, struct REnv *env, int num, const mrb_sym *lv, const mrb_value *stack)
|
|
||||||
{
|
|
||||||
mrb_assert(mrb->c->stend >= stack + num);
|
|
||||||
mrb_proc_merge_lvar(mrb, irep, env, num, lv, stack);
|
|
||||||
}
|
|
||||||
|
|
||||||
static mrb_value
|
|
||||||
eval_merge_lvar_hook(mrb_state *mrb, mrb_value dummy_self)
|
|
||||||
{
|
|
||||||
const mrb_callinfo *orig_ci = &mrb->c->ci[1];
|
|
||||||
const struct RProc *orig_proc = orig_ci->proc;
|
|
||||||
const mrb_irep *orig_irep = orig_proc->body.irep;
|
|
||||||
int orig_nlocals = orig_irep->nlocals;
|
|
||||||
|
|
||||||
if (orig_nlocals > 1) {
|
|
||||||
struct RProc *proc = (struct RProc *)orig_proc->upper;
|
|
||||||
struct REnv *env = MRB_PROC_ENV(orig_proc);
|
|
||||||
eval_merge_lvar(mrb, (mrb_irep *)proc->body.irep, env,
|
|
||||||
orig_nlocals - 1, orig_irep->lv,
|
|
||||||
mrb->c->ci->stack + 3 /* hook proc + exc + ret val */);
|
|
||||||
}
|
|
||||||
|
|
||||||
mrb_value exc = mrb->c->ci->stack[1];
|
|
||||||
if (!mrb_nil_p(exc)) {
|
|
||||||
mrb_exc_raise(mrb, exc);
|
|
||||||
}
|
|
||||||
|
|
||||||
return mrb->c->ci->stack[2];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static mrb_value
|
static mrb_value
|
||||||
@@ -178,19 +147,15 @@ f_eval(mrb_state *mrb, mrb_value self)
|
|||||||
const char *file = NULL;
|
const char *file = NULL;
|
||||||
mrb_int line = 1;
|
mrb_int line = 1;
|
||||||
struct RProc *proc;
|
struct RProc *proc;
|
||||||
mrb_func_t posthook = NULL;
|
|
||||||
|
|
||||||
mrb_get_args(mrb, "s|ozi", &s, &len, &binding, &file, &line);
|
mrb_get_args(mrb, "s|ozi", &s, &len, &binding, &file, &line);
|
||||||
|
|
||||||
proc = create_proc_from_string(mrb, s, len, binding, file, line);
|
proc = create_proc_from_string(mrb, s, len, binding, file, line);
|
||||||
if (!mrb_nil_p(binding)) {
|
if (!mrb_nil_p(binding)) {
|
||||||
self = mrb_iv_get(mrb, binding, MRB_SYM(recv));
|
self = mrb_iv_get(mrb, binding, MRB_SYM(recv));
|
||||||
if (mrb_env_p(mrb_iv_get(mrb, binding, MRB_SYM(env)))) {
|
|
||||||
posthook = eval_merge_lvar_hook;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
mrb_assert(!MRB_PROC_CFUNC_P(proc));
|
mrb_assert(!MRB_PROC_CFUNC_P(proc));
|
||||||
return exec_irep(mrb, self, proc, posthook);
|
return exec_irep(mrb, self, proc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static mrb_value
|
static mrb_value
|
||||||
@@ -210,7 +175,7 @@ f_instance_eval(mrb_state *mrb, mrb_value self)
|
|||||||
MRB_PROC_SET_TARGET_CLASS(proc, mrb_class_ptr(cv));
|
MRB_PROC_SET_TARGET_CLASS(proc, mrb_class_ptr(cv));
|
||||||
mrb_assert(!MRB_PROC_CFUNC_P(proc));
|
mrb_assert(!MRB_PROC_CFUNC_P(proc));
|
||||||
mrb_vm_ci_target_class_set(mrb->c->ci, mrb_class_ptr(cv));
|
mrb_vm_ci_target_class_set(mrb->c->ci, mrb_class_ptr(cv));
|
||||||
return exec_irep(mrb, self, proc, NULL);
|
return exec_irep(mrb, self, proc);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
mrb_get_args(mrb, "");
|
mrb_get_args(mrb, "");
|
||||||
@@ -233,7 +198,7 @@ f_class_eval(mrb_state *mrb, mrb_value self)
|
|||||||
MRB_PROC_SET_TARGET_CLASS(proc, mrb_class_ptr(self));
|
MRB_PROC_SET_TARGET_CLASS(proc, mrb_class_ptr(self));
|
||||||
mrb_assert(!MRB_PROC_CFUNC_P(proc));
|
mrb_assert(!MRB_PROC_CFUNC_P(proc));
|
||||||
mrb_vm_ci_target_class_set(mrb->c->ci, mrb_class_ptr(self));
|
mrb_vm_ci_target_class_set(mrb->c->ci, mrb_class_ptr(self));
|
||||||
return exec_irep(mrb, self, proc, NULL);
|
return exec_irep(mrb, self, proc);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
mrb_get_args(mrb, "");
|
mrb_get_args(mrb, "");
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
#include "mruby/presym.h"
|
#include "mruby/presym.h"
|
||||||
|
|
||||||
mrb_noreturn void mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args);
|
mrb_noreturn void mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args);
|
||||||
mrb_value mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p, mrb_func_t posthook);
|
mrb_value mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p);
|
||||||
|
|
||||||
static mrb_value
|
static mrb_value
|
||||||
args_shift(mrb_state *mrb)
|
args_shift(mrb_state *mrb)
|
||||||
@@ -242,7 +242,7 @@ mcall(mrb_state *mrb, mrb_value self, mrb_value recv)
|
|||||||
mrb->c->ci->mid = mid;
|
mrb->c->ci->mid = mid;
|
||||||
mrb->c->ci->u.target_class = tc;
|
mrb->c->ci->u.target_class = tc;
|
||||||
|
|
||||||
return mrb_exec_irep(mrb, recv, proc, NULL);
|
return mrb_exec_irep(mrb, recv, proc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static mrb_value
|
static mrb_value
|
||||||
|
|||||||
+8
-1
@@ -417,7 +417,14 @@ mrb_proc_merge_lvar(mrb_state *mrb, mrb_irep *irep, struct REnv *env, int num, c
|
|||||||
mrb_sym *destlv = (mrb_sym*)irep->lv + irep->nlocals - 1 /* self */;
|
mrb_sym *destlv = (mrb_sym*)irep->lv + irep->nlocals - 1 /* self */;
|
||||||
mrb_value *destst = env->stack + irep->nlocals;
|
mrb_value *destst = env->stack + irep->nlocals;
|
||||||
memmove(destlv, lv, sizeof(mrb_sym) * num);
|
memmove(destlv, lv, sizeof(mrb_sym) * num);
|
||||||
memmove(destst, stack, sizeof(mrb_value) * num);
|
if (stack) {
|
||||||
|
memmove(destst, stack, sizeof(mrb_value) * num);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (int i = num; i > 0; i--, destst++) {
|
||||||
|
*destst = mrb_nil_value();
|
||||||
|
}
|
||||||
|
}
|
||||||
irep->nlocals += num;
|
irep->nlocals += num;
|
||||||
irep->nregs = irep->nlocals;
|
irep->nregs = irep->nlocals;
|
||||||
MRB_ENV_SET_LEN(env, irep->nlocals);
|
MRB_ENV_SET_LEN(env, irep->nlocals);
|
||||||
|
|||||||
@@ -526,68 +526,8 @@ 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());
|
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
|
|
||||||
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, MRB_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.
|
|
||||||
*/
|
|
||||||
static mrb_value
|
static mrb_value
|
||||||
exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p, mrb_func_t posthook)
|
exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p)
|
||||||
{
|
{
|
||||||
mrb_callinfo *ci = mrb->c->ci;
|
mrb_callinfo *ci = mrb->c->ci;
|
||||||
int keep, nregs;
|
int keep, nregs;
|
||||||
@@ -600,17 +540,12 @@ exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p, mrb_func_t posthook)
|
|||||||
nregs = p->body.irep->nregs;
|
nregs = p->body.irep->nregs;
|
||||||
if (ci->argc < 0) keep = 3;
|
if (ci->argc < 0) keep = 3;
|
||||||
else keep = ci->argc + 2;
|
else keep = ci->argc + 2;
|
||||||
int extra = posthook ? (2 /* hook proc + errinfo */) : 0;
|
|
||||||
if (nregs < keep) {
|
if (nregs < keep) {
|
||||||
mrb_stack_extend(mrb, keep + extra);
|
mrb_stack_extend(mrb, keep);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
mrb_stack_extend(mrb, nregs + extra);
|
mrb_stack_extend(mrb, nregs);
|
||||||
stack_clear(ci->stack+keep, nregs-keep + extra);
|
stack_clear(ci->stack+keep, nregs-keep);
|
||||||
}
|
|
||||||
|
|
||||||
if (posthook) {
|
|
||||||
exec_irep_prepare_posthook(mrb, ci, (nregs < keep ? keep : nregs), posthook);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cipush(mrb, 0, 0, NULL, NULL, 0, 0);
|
cipush(mrb, 0, 0, NULL, NULL, 0, 0);
|
||||||
@@ -619,11 +554,11 @@ exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p, mrb_func_t posthook)
|
|||||||
}
|
}
|
||||||
|
|
||||||
mrb_value
|
mrb_value
|
||||||
mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p, mrb_func_t posthook)
|
mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p)
|
||||||
{
|
{
|
||||||
mrb_callinfo *ci = mrb->c->ci;
|
mrb_callinfo *ci = mrb->c->ci;
|
||||||
if (ci->acc >= 0) {
|
if (ci->acc >= 0) {
|
||||||
return exec_irep(mrb, self, p, posthook);
|
return exec_irep(mrb, self, p);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
mrb_value ret;
|
mrb_value ret;
|
||||||
@@ -706,7 +641,7 @@ mrb_f_send(mrb_state *mrb, mrb_value self)
|
|||||||
}
|
}
|
||||||
return MRB_METHOD_CFUNC(m)(mrb, self);
|
return MRB_METHOD_CFUNC(m)(mrb, self);
|
||||||
}
|
}
|
||||||
return exec_irep(mrb, self, MRB_METHOD_PROC(m), NULL);
|
return exec_irep(mrb, self, MRB_METHOD_PROC(m));
|
||||||
}
|
}
|
||||||
|
|
||||||
static mrb_value
|
static mrb_value
|
||||||
@@ -884,7 +819,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[1] = mrb_ary_new_from_values(mrb, argc, argv);
|
||||||
mrb->c->ci->stack[2] = mrb_nil_value();
|
mrb->c->ci->stack[2] = mrb_nil_value();
|
||||||
ci->argc = -1;
|
ci->argc = -1;
|
||||||
return exec_irep(mrb, self, p, NULL);
|
return exec_irep(mrb, self, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct RBreak*
|
static struct RBreak*
|
||||||
|
|||||||
Reference in New Issue
Block a user