diff options
Diffstat (limited to 'mrbgems/mruby-eval')
| -rw-r--r-- | mrbgems/mruby-eval/src/eval.c | 247 | ||||
| -rw-r--r-- | mrbgems/mruby-eval/test/eval.rb | 58 |
2 files changed, 133 insertions, 172 deletions
diff --git a/mrbgems/mruby-eval/src/eval.c b/mrbgems/mruby-eval/src/eval.c index 7121ec377..89a2b3b10 100644 --- a/mrbgems/mruby-eval/src/eval.c +++ b/mrbgems/mruby-eval/src/eval.c @@ -4,150 +4,25 @@ #include <mruby/irep.h> #include <mruby/proc.h> #include <mruby/opcode.h> +#include <mruby/error.h> +#include <mruby/presym.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); 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); -static struct mrb_irep * -get_closure_irep(mrb_state *mrb, int level, struct RProc *scope) -{ - struct mrb_context *c = mrb->c; - struct REnv *e = scope->env; - struct RProc *proc; - if (level == 0) { - proc = scope; - if (MRB_PROC_CFUNC_P(proc)) { - return NULL; - } - return proc->body.irep; - } - - while (--level) { - e = (struct REnv*)e->c; - if (!e) return NULL; - } - - if (!e) return NULL; - if (!MRB_ENV_STACK_SHARED_P(e)) return NULL; - - proc = c->cibase[e->cioff].proc; - - if (!proc || MRB_PROC_CFUNC_P(proc)) { - return NULL; - } - return proc->body.irep; -} - -static inline mrb_code -search_variable(mrb_state *mrb, mrb_sym vsym, int bnest, struct RProc *scope) -{ - mrb_irep *virep; - int level; - int pos; - - for (level = 0; (virep = get_closure_irep(mrb, level, scope)); level++) { - if (!virep || virep->lv == NULL) { - continue; - } - for (pos = 0; pos < virep->nlocals - 1; pos++) { - if (vsym == virep->lv[pos].name) { - return (MKARG_B(pos + 1) | MKARG_C(level + bnest)); - } - } - } - - return 0; -} - -static mrb_bool -potential_upvar_p(struct mrb_locals *lv, uint16_t v, int argc, uint16_t nlocals) -{ - if (v >= nlocals) return FALSE; - /* skip arguments */ - if (v < argc+1) return FALSE; - return TRUE; -} - -static void -patch_irep(mrb_state *mrb, mrb_irep *irep, int bnest, struct RProc *scope) -{ - size_t i; - mrb_code c; - int argc = 0; - - for (i = 0; i < irep->ilen; i++) { - c = irep->iseq[i]; - switch(GET_OPCODE(c)){ - case OP_ENTER: - { - mrb_aspec ax = GETARG_Ax(c); - /* extra 1 means a slot for block */ - argc = MRB_ASPEC_REQ(ax)+MRB_ASPEC_OPT(ax)+MRB_ASPEC_REST(ax)+MRB_ASPEC_POST(ax)+1; - } - break; - - case OP_EPUSH: - patch_irep(mrb, irep->reps[GETARG_Bx(c)], bnest + 1, scope); - break; - - case OP_LAMBDA: - { - int arg_c = GETARG_c(c); - if (arg_c & OP_L_CAPTURE) { - patch_irep(mrb, irep->reps[GETARG_b(c)], bnest + 1, scope); - } - } - break; - - case OP_SEND: - if (GETARG_C(c) != 0) { - break; - } - { - mrb_code arg = search_variable(mrb, irep->syms[GETARG_B(c)], bnest, scope); - if (arg != 0) { - /* must replace */ - irep->iseq[i] = MKOPCODE(OP_GETUPVAR) | MKARG_A(GETARG_A(c)) | arg; - } - } - break; - - case OP_MOVE: - /* src part */ - if (potential_upvar_p(irep->lv, GETARG_B(c), argc, irep->nlocals)) { - mrb_code arg = search_variable(mrb, irep->lv[GETARG_B(c) - 1].name, bnest, scope); - if (arg != 0) { - /* must replace */ - irep->iseq[i] = MKOPCODE(OP_GETUPVAR) | MKARG_A(GETARG_A(c)) | arg; - } - } - /* dst part */ - if (potential_upvar_p(irep->lv, GETARG_A(c), argc, irep->nlocals)) { - mrb_code arg = search_variable(mrb, irep->lv[GETARG_A(c) - 1].name, bnest, scope); - if (arg != 0) { - /* must replace */ - irep->iseq[i] = MKOPCODE(OP_SETUPVAR) | MKARG_A(GETARG_B(c)) | arg; - } - } - break; - - case OP_STOP: - irep->iseq[i] = MKOP_AB(OP_RETURN, irep->nlocals, OP_R_NORMAL); - break; - } - } -} - void mrb_codedump_all(mrb_state*, struct RProc*); static struct RProc* -create_proc_from_string(mrb_state *mrb, char *s, int len, mrb_value binding, const char *file, mrb_int line) +create_proc_from_string(mrb_state *mrb, const char *s, mrb_int len, mrb_value binding, const char *file, mrb_int line) { mrbc_context *cxt; struct mrb_parser_state *p; struct RProc *proc, *scope; struct REnv *e; + mrb_callinfo *ci; /* callinfo of eval caller */ + struct RClass *target_class = NULL; struct mrb_context *c = mrb->c; mrb_bool is_binding = FALSE; @@ -165,30 +40,39 @@ create_proc_from_string(mrb_state *mrb, char *s, int len, mrb_value binding, con } cxt = mrbc_context_new(mrb); - cxt->lineno = line; + cxt->lineno = (uint16_t)line; - if (!file) { - file = "(eval)"; - } - mrbc_filename(mrb, cxt, file); + mrbc_filename(mrb, cxt, file ? file : "(eval)"); cxt->capture_errors = TRUE; cxt->no_optimize = TRUE; + ci = (c->ci > c->cibase) ? c->ci - 1 : c->cibase; + cxt->upper = ci->proc && MRB_PROC_CFUNC_P(ci->proc) ? NULL : ci->proc; p = mrb_parse_nstring(mrb, s, len, cxt); /* only occur when memory ran out */ if (!p) { - mrb_raise(mrb, E_RUNTIME_ERROR, "Failed to create parser state."); + mrb_raise(mrb, E_RUNTIME_ERROR, "Failed to create parser state (out of memory)"); } if (0 < p->nerr) { /* parse error */ - char buf[256]; - int n; - n = snprintf(buf, sizeof(buf), "line %d: %s\n", p->error_buffer[0].lineno, p->error_buffer[0].message); + 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_parser_free(p); mrbc_context_free(mrb, cxt); - mrb_exc_raise(mrb, mrb_exc_new(mrb, E_SYNTAX_ERROR, buf, n)); + mrb_exc_raise(mrb, mrb_exc_new_str(mrb, E_SYNTAX_ERROR, str)); } proc = mrb_generate_code(mrb, p); @@ -198,20 +82,30 @@ create_proc_from_string(mrb_state *mrb, char *s, int len, mrb_value binding, con mrbc_context_free(mrb, cxt); mrb_raise(mrb, E_SCRIPT_ERROR, "codegen error"); } - if (scope->target_class) { - proc->target_class = scope->target_class; + if (c->ci > c->cibase) { + ci = &c->ci[-1]; + } + else { + ci = c->cibase; + } + if (scope) { + target_class = MRB_PROC_TARGET_CLASS(scope); } - e = scope->env; - if (!e) e = c->ci[-1].env; - e = (struct REnv*)mrb_obj_alloc(mrb, MRB_TT_ENV, (struct RClass*)e); - e->mid = c->ci[-1].mid; - e->cioff = c->ci - c->cibase - 1; - e->stack = c->ci->stackent; - MRB_SET_ENV_STACK_LEN(e, scope->body.irep->nlocals); - c->ci->target_class = proc->target_class; - c->ci->env = 0; - proc->env = e; - patch_irep(mrb, proc->body.irep, 0, scope); + if (scope && !MRB_PROC_CFUNC_P(scope)) { + if ((e = mrb_vm_ci_env(ci)) != NULL) { + /* do nothing, because e is assigned already */ + } + else { + e = mrb_env_new(mrb, c, ci, scope->body.irep->nlocals, ci->stack, target_class); + ci->u.env = e; + } + proc->e.env = e; + proc->flags |= MRB_PROC_ENVSET; + mrb_field_write_barrier(mrb, (struct RBasic*)proc, (struct RBasic*)e); + } + proc->upper = scope; + mrb_vm_ci_target_class_set(mrb->c->ci, target_class); + /* mrb_codedump_all(mrb, proc); */ mrb_parser_free(p); mrbc_context_free(mrb, cxt); @@ -220,12 +114,31 @@ create_proc_from_string(mrb_state *mrb, char *s, int len, mrb_value binding, con } static mrb_value +exec_irep(mrb_state *mrb, mrb_value self, struct RProc *proc) +{ + /* no argument passed from eval() */ + mrb->c->ci->argc = 0; + if (mrb->c->ci->acc < 0) { + ptrdiff_t cioff = mrb->c->ci - mrb->c->cibase; + mrb_value ret = mrb_top_run(mrb, proc, self, 0); + if (mrb->exc) { + mrb_exc_raise(mrb, mrb_obj_value(mrb->exc)); + } + mrb->c->ci = mrb->c->cibase + cioff; + return ret; + } + /* clear block */ + mrb->c->ci->stack[1] = mrb_nil_value(); + return mrb_exec_irep(mrb, self, proc); +} + +static mrb_value f_eval(mrb_state *mrb, mrb_value self) { - char *s; + const char *s; mrb_int len; mrb_value binding = mrb_nil_value(); - char *file = NULL; + const char *file = NULL; mrb_int line = 1; struct RProc *proc; @@ -236,36 +149,32 @@ f_eval(mrb_state *mrb, mrb_value self) self = mrb_iv_get(mrb, binding, mrb_intern_lit(mrb, "recv")); } mrb_assert(!MRB_PROC_CFUNC_P(proc)); - return mrb_exec_irep(mrb, self, proc); + return exec_irep(mrb, self, proc); } -#define CI_ACC_SKIP -1 - static mrb_value f_instance_eval(mrb_state *mrb, mrb_value self) { - struct mrb_context *c = mrb->c; mrb_value b; - mrb_int argc; mrb_value *argv; + mrb_int argc; const mrb_value *argv; - mrb_get_args(mrb, "*&", &argv, &argc, &b); + mrb_get_args(mrb, "*!&", &argv, &argc, &b); if (mrb_nil_p(b)) { - char *s; + const char *s; mrb_int len; - char *file = NULL; + const char *file = NULL; mrb_int line = 1; mrb_value cv; struct RProc *proc; mrb_get_args(mrb, "s|zi", &s, &len, &file, &line); - c->ci->acc = CI_ACC_SKIP; cv = mrb_singleton_class(mrb, self); - c->ci->target_class = mrb_class_ptr(cv); proc = create_proc_from_string(mrb, s, len, mrb_nil_value(), file, line); - mrb->c->ci->env = NULL; + MRB_PROC_SET_TARGET_CLASS(proc, mrb_class_ptr(cv)); mrb_assert(!MRB_PROC_CFUNC_P(proc)); - return mrb_exec_irep(mrb, self, proc); + mrb_vm_ci_target_class_set(mrb->c->ci, mrb_class_ptr(cv)); + return exec_irep(mrb, self, proc); } else { mrb_get_args(mrb, "&", &b); @@ -277,7 +186,7 @@ void mrb_mruby_eval_gem_init(mrb_state* mrb) { mrb_define_module_function(mrb, mrb->kernel_module, "eval", f_eval, MRB_ARGS_ARG(1, 3)); - mrb_define_method(mrb, mrb->kernel_module, "instance_eval", f_instance_eval, MRB_ARGS_ARG(1, 2)); + mrb_define_method(mrb, mrb_class_get_id(mrb, MRB_SYM(BasicObject)), "instance_eval", f_instance_eval, MRB_ARGS_OPT(3)|MRB_ARGS_BLOCK()); } void diff --git a/mrbgems/mruby-eval/test/eval.rb b/mrbgems/mruby-eval/test/eval.rb index 4acf9fb41..e95171223 100644 --- a/mrbgems/mruby-eval/test/eval.rb +++ b/mrbgems/mruby-eval/test/eval.rb @@ -34,7 +34,7 @@ assert('Kernel.eval', '15.3.1.2.3') do } assert_equal(2) { a = 10 - Kernel.eval 'def f(a); b=a.send(:+, 1); end' + Kernel.eval 'def f(a); b=a+1; end' f(1) } end @@ -58,7 +58,7 @@ end assert('String instance_eval') do obj = Object.new - obj.instance_variable_set :@test, 'test' + obj.instance_eval{ @test = 'test' } assert_raise(ArgumentError) { obj.instance_eval(0) { } } assert_raise(ArgumentError) { obj.instance_eval('0', 'test', 0, 'test') } assert_equal(['test.rb', 10]) { obj.instance_eval('[__FILE__, __LINE__]', 'test.rb', 10)} @@ -80,7 +80,7 @@ assert('Kernel.#eval(string) context') do assert_equal('class') { obj.const_string } end -assert('Object#instance_eval with begin-rescue-ensure execution order') do +assert('BasicObject#instance_eval with begin-rescue-ensure execution order') do class HellRaiser def raise_hell order = [:enter_raise_hell] @@ -99,3 +99,55 @@ assert('Object#instance_eval with begin-rescue-ensure execution order') do hell_raiser = HellRaiser.new assert_equal([:enter_raise_hell, :begin, :rescue, :ensure], hell_raiser.raise_hell) end + +assert('BasicObject#instance_eval to define singleton methods Issue #3141') do + foo_class = Class.new do + def bar(x) + instance_eval "def baz; #{x}; end" + end + end + + f1 = foo_class.new + f2 = foo_class.new + f1.bar 1 + f2.bar 2 + assert_equal(1){f1.baz} + assert_equal(2){f2.baz} +end + +assert('Kernel.#eval(string) Issue #4021') do + assert_equal('FOO') { (eval <<'EOS').call } +foo = "FOO" +Proc.new { foo } +EOS + assert_equal('FOO') { + def do_eval(code) + eval(code) + end + do_eval(<<'EOS').call +foo = "FOO" +Proc.new { foo } +EOS + } +end + +assert('Calling the same method as the variable name') do + hoge = Object.new + def hoge.fuga + "Hit!" + end + assert_equal("Hit!") { fuga = "Miss!"; eval "hoge.fuga" } + assert_equal("Hit!") { fuga = "Miss!"; -> { eval "hoge.fuga" }.call } + assert_equal("Hit!") { -> { fuga = "Miss!"; eval "hoge.fuga" }.call } + assert_equal("Hit!") { fuga = "Miss!"; eval("-> { hoge.fuga }").call } +end + +assert('Access numbered parameter from eval') do + hoge = Object.new + def hoge.fuga(a, &b) + b.call(a) + end + assert_equal(6) { + hoge.fuga(3) { _1 + eval("_1") } + } +end |
