Unverified Commit d63c0df6 authored by Yukihiro "Matz" Matsumoto's avatar Yukihiro "Matz" Matsumoto Committed by GitHub

Merge pull request #5493 from dearblue/binding.2

Fixed finding variables from `proc` in `binding.eval` failed
parents e462b42f c182903e
......@@ -24,16 +24,16 @@ binding_extract_pc(mrb_state *mrb, mrb_value binding)
}
}
static const struct RProc *
binding_extract_proc(mrb_state *mrb, mrb_value binding)
const struct RProc *
mrb_binding_extract_proc(mrb_state *mrb, mrb_value binding)
{
mrb_value obj = mrb_iv_get(mrb, binding, MRB_SYM(proc));
mrb_check_type(mrb, obj, MRB_TT_PROC);
return mrb_proc_ptr(obj);
}
static struct REnv *
binding_extract_env(mrb_state *mrb, mrb_value binding)
struct REnv *
mrb_binding_extract_env(mrb_state *mrb, mrb_value binding)
{
mrb_value obj = mrb_iv_get(mrb, binding, MRB_SYM(env));
if (mrb_nil_p(obj)) {
......@@ -108,8 +108,8 @@ binding_local_variable_defined_p(mrb_state *mrb, mrb_value self)
mrb_sym varname;
mrb_get_args(mrb, "n", &varname);
const struct RProc *proc = binding_extract_proc(mrb, self);
struct REnv *env = binding_extract_env(mrb, self);
const struct RProc *proc = mrb_binding_extract_proc(mrb, self);
struct REnv *env = mrb_binding_extract_env(mrb, self);
mrb_value *e = binding_local_variable_search(mrb, proc, env, varname);
if (e) {
return mrb_true_value();
......@@ -129,8 +129,8 @@ binding_local_variable_get(mrb_state *mrb, mrb_value self)
mrb_sym varname;
mrb_get_args(mrb, "n", &varname);
const struct RProc *proc = binding_extract_proc(mrb, self);
struct REnv *env = binding_extract_env(mrb, self);
const struct RProc *proc = mrb_binding_extract_proc(mrb, self);
struct REnv *env = mrb_binding_extract_env(mrb, self);
mrb_value *e = binding_local_variable_search(mrb, proc, env, varname);
if (!e) {
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_get_args(mrb, "no", &varname, &obj);
const struct RProc *proc = binding_extract_proc(mrb, self);
struct REnv *env = binding_extract_env(mrb, self);
const struct RProc *proc = mrb_binding_extract_proc(mrb, self);
struct REnv *env = mrb_binding_extract_env(mrb, self);
mrb_value *e = binding_local_variable_search(mrb, proc, env, varname);
if (e) {
*e = obj;
......@@ -184,7 +184,7 @@ binding_source_location(mrb_state *mrb, mrb_value self)
}
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) ||
!proc->upper || MRB_PROC_CFUNC_P(proc->upper)) {
srcloc = mrb_nil_value();
......
class Binding
def eval(expr, *args)
Kernel.eval(expr, self, *args)
end
end
#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
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
}
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
}
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>
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_mod_module_eval(mrb_state*, mrb_value);
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
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() */
mrb->c->ci->argc = 0;
/* clear block */
mrb->c->ci->stack[1] = mrb_nil_value();
return mrb_exec_irep(mrb, self, proc, posthook);
}
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];
return mrb_exec_irep(mrb, self, proc);
}
static mrb_value
......@@ -178,19 +147,15 @@ f_eval(mrb_state *mrb, mrb_value self)
const char *file = NULL;
mrb_int line = 1;
struct RProc *proc;
mrb_func_t posthook = NULL;
mrb_get_args(mrb, "s|ozi", &s, &len, &binding, &file, &line);
proc = create_proc_from_string(mrb, s, len, binding, file, line);
if (!mrb_nil_p(binding)) {
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));
return exec_irep(mrb, self, proc, posthook);
return exec_irep(mrb, self, proc);
}
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_assert(!MRB_PROC_CFUNC_P(proc));
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 {
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_assert(!MRB_PROC_CFUNC_P(proc));
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 {
mrb_get_args(mrb, "");
......
......@@ -7,7 +7,7 @@
#include "mruby/presym.h"
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
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->u.target_class = tc;
return mrb_exec_irep(mrb, recv, proc, NULL);
return mrb_exec_irep(mrb, recv, proc);
}
static mrb_value
......
......@@ -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_value *destst = env->stack + irep->nlocals;
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->nregs = 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());
}
#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
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;
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;
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 + extra);
mrb_stack_extend(mrb, keep);
}
else {
mrb_stack_extend(mrb, nregs + extra);
stack_clear(ci->stack+keep, nregs-keep + extra);
}
if (posthook) {
exec_irep_prepare_posthook(mrb, ci, (nregs < keep ? keep : nregs), posthook);
mrb_stack_extend(mrb, nregs);
stack_clear(ci->stack+keep, nregs-keep);
}
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_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;
if (ci->acc >= 0) {
return exec_irep(mrb, self, p, posthook);
return exec_irep(mrb, self, p);
}
else {
mrb_value ret;
......@@ -706,7 +641,7 @@ mrb_f_send(mrb_state *mrb, mrb_value 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
......@@ -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[2] = mrb_nil_value();
ci->argc = -1;
return exec_irep(mrb, self, p, NULL);
return exec_irep(mrb, self, p);
}
static struct RBreak*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment