summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorYukihiro "Matz" Matsumoto <[email protected]>2021-02-25 21:15:24 +0900
committerGitHub <[email protected]>2021-02-25 21:15:24 +0900
commit192f3df9a1fca043801e42febcd4b105fa1d5733 (patch)
tree2e83f49a75fb581e522a7d388c5cc86f978cd1a7
parentf1c2096f8ed966cf055d1a32271748b5fad4ffe0 (diff)
parent927615e1f072d8fff3d9b84660cdce15a239e36c (diff)
downloadmruby-192f3df9a1fca043801e42febcd4b105fa1d5733.tar.gz
mruby-192f3df9a1fca043801e42febcd4b105fa1d5733.zip
Merge pull request #5362 from dearblue/binding
Binding
-rw-r--r--mrbgems/mruby-binding-core/mrbgem.rake7
-rw-r--r--mrbgems/mruby-binding-core/src/binding-core.c309
-rw-r--r--mrbgems/mruby-binding-core/test/binding-core.rb40
-rw-r--r--mrbgems/mruby-binding/mrbgem.rake11
-rw-r--r--mrbgems/mruby-binding/mrblib/binding.rb5
-rw-r--r--mrbgems/mruby-binding/test/binding.c13
-rw-r--r--mrbgems/mruby-binding/test/binding.rb70
-rw-r--r--mrbgems/mruby-eval/src/eval.c115
-rw-r--r--mrbgems/mruby-eval/test/eval.rb2
-rw-r--r--mrbgems/mruby-metaprog/src/metaprog.c37
-rw-r--r--mrbgems/mruby-proc-binding/mrbgem.rake9
-rw-r--r--mrbgems/mruby-proc-binding/src/proc-binding.c52
-rw-r--r--mrbgems/mruby-proc-binding/test/proc-binding.c14
-rw-r--r--mrbgems/mruby-proc-binding/test/proc-binding.rb22
-rw-r--r--mrbgems/mruby-proc-ext/src/proc.c14
-rw-r--r--src/proc.c105
-rw-r--r--src/vm.c79
17 files changed, 831 insertions, 73 deletions
diff --git a/mrbgems/mruby-binding-core/mrbgem.rake b/mrbgems/mruby-binding-core/mrbgem.rake
new file mode 100644
index 000000000..c0ba48207
--- /dev/null
+++ b/mrbgems/mruby-binding-core/mrbgem.rake
@@ -0,0 +1,7 @@
+MRuby::Gem::Specification.new('mruby-binding-core') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Binding class (core features only)'
+
+ spec.add_test_dependency('mruby-proc-ext', :core => 'mruby-proc-ext')
+end
diff --git a/mrbgems/mruby-binding-core/src/binding-core.c b/mrbgems/mruby-binding-core/src/binding-core.c
new file mode 100644
index 000000000..8086185ea
--- /dev/null
+++ b/mrbgems/mruby-binding-core/src/binding-core.c
@@ -0,0 +1,309 @@
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/hash.h>
+#include <mruby/proc.h>
+#include <mruby/variable.h>
+#include <mruby/presym.h>
+#include <mruby/opcode.h>
+#include <mruby/debug.h>
+
+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_proc_local_variables(mrb_state *mrb, const struct RProc *proc);
+const struct RProc *mrb_proc_get_caller(mrb_state *mrb, struct REnv **env);
+
+static mrb_int
+binding_extract_pc(mrb_state *mrb, mrb_value binding)
+{
+ mrb_value obj = mrb_iv_get(mrb, binding, MRB_SYM(pc));
+ if (mrb_nil_p(obj)) {
+ return -1;
+ }
+ else {
+ mrb_check_type(mrb, obj, MRB_TT_INTEGER);
+ return mrb_int(mrb, obj);
+ }
+}
+
+static const struct RProc *
+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)
+{
+ mrb_value obj = mrb_iv_get(mrb, binding, MRB_SYM(env));
+ if (mrb_nil_p(obj)) {
+ return NULL;
+ }
+ else {
+ mrb_check_type(mrb, obj, MRB_TT_ENV);
+ return (struct REnv *)mrb_obj_ptr(obj);
+ }
+}
+
+static void
+binding_local_variable_name_check(mrb_state *mrb, mrb_sym id)
+{
+ if (id == 0) {
+ badname:
+ mrb_raisef(mrb, E_NAME_ERROR, "wrong local variable name %!n for binding", id);
+ }
+
+ mrb_int len;
+ const char *name = mrb_sym_name_len(mrb, id, &len);
+ if (len == 0) {
+ goto badname;
+ }
+
+ if (ISASCII(*name) && !(*name == '_' || ISLOWER(*name))) {
+ goto badname;
+ }
+ len--;
+ name++;
+
+ for (; len > 0; len--, name++) {
+ if (ISASCII(*name) && !(*name == '_' || ISALNUM(*name))) {
+ goto badname;
+ }
+ }
+}
+
+static mrb_value *
+binding_local_variable_search(mrb_state *mrb, const struct RProc *proc, struct REnv *env, mrb_sym varname)
+{
+ binding_local_variable_name_check(mrb, varname);
+
+ while (proc) {
+ if (MRB_PROC_CFUNC_P(proc)) break;
+
+ const mrb_irep *irep = proc->body.irep;
+ const mrb_sym *lv;
+ if (irep && (lv = irep->lv)) {
+ for (int i = 0; i + 1 < irep->nlocals; i++, lv++) {
+ if (varname == *lv) {
+ return (env && MRB_ENV_LEN(env) > i) ? &env->stack[i + 1] : NULL;
+ }
+ }
+ }
+
+ if (MRB_PROC_SCOPE_P(proc)) break;
+ env = MRB_PROC_ENV(proc);
+ proc = proc->upper;
+ }
+
+ return NULL;
+}
+
+/*
+ * call-seq:
+ * local_variable_defined?(symbol) -> bool
+ */
+static mrb_value
+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);
+ mrb_value *e = binding_local_variable_search(mrb, proc, env, varname);
+ if (e) {
+ return mrb_true_value();
+ }
+ else {
+ return mrb_false_value();
+ }
+}
+
+/*
+ * call-seq:
+ * local_variable_get(symbol) -> object
+ */
+static mrb_value
+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);
+ 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);
+ }
+
+ return *e;
+}
+
+static mrb_value
+binding_local_variable_set(mrb_state *mrb, mrb_value self)
+{
+ mrb_sym varname;
+ 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);
+ mrb_value *e = binding_local_variable_search(mrb, proc, env, varname);
+ if (e) {
+ *e = obj;
+ }
+ else {
+ mrb_proc_merge_lvar(mrb, (mrb_irep*)proc->body.irep, env, 1, &varname, &obj);
+ }
+
+ return obj;
+}
+
+static mrb_value
+binding_local_variables(mrb_state *mrb, mrb_value self)
+{
+ const struct RProc *proc = mrb_proc_ptr(mrb_iv_get(mrb, self, MRB_SYM(proc)));
+ return mrb_proc_local_variables(mrb, proc);
+}
+
+static mrb_value
+binding_receiver(mrb_state *mrb, mrb_value self)
+{
+ return mrb_iv_get(mrb, self, MRB_SYM(recv));
+}
+
+/*
+ * call-seq:
+ * source_location -> [String, Integer]
+ */
+static mrb_value
+binding_source_location(mrb_state *mrb, mrb_value self)
+{
+ if (mrb_iv_defined(mrb, self, MRB_SYM(source_location))) {
+ return mrb_iv_get(mrb, self, MRB_SYM(source_location));
+ }
+
+ mrb_value srcloc;
+ const struct RProc *proc = binding_extract_proc(mrb, self);
+ if (!proc || MRB_PROC_CFUNC_P(proc) ||
+ !proc->upper || MRB_PROC_CFUNC_P(proc->upper)) {
+ srcloc = mrb_nil_value();
+ goto cache_source_location;
+ }
+
+ {
+ const mrb_irep *irep = proc->upper->body.irep;
+ mrb_int pc = binding_extract_pc(mrb, self);
+ if (pc < 0) {
+ srcloc = mrb_nil_value();
+ }
+ else {
+ const char *fname = mrb_debug_get_filename(mrb, irep, pc);
+ mrb_int fline = mrb_debug_get_line(mrb, irep, pc);
+
+ if (fname && fline >= 0) {
+ srcloc = mrb_assoc_new(mrb, mrb_str_new_cstr(mrb, fname), mrb_fixnum_value(fline));
+ }
+ else {
+ srcloc = mrb_nil_value();
+ }
+ }
+ }
+
+cache_source_location:
+ if (!mrb_frozen_p(mrb_obj_ptr(self))) {
+ mrb_iv_set(mrb, self, MRB_SYM(source_location), srcloc);
+ }
+ return srcloc;
+}
+
+mrb_value
+mrb_binding_alloc(mrb_state *mrb)
+{
+ struct RObject *obj = (struct RObject*)mrb_obj_alloc(mrb, MRB_TT_OBJECT, mrb_class_get(mrb, "Binding"));
+ return mrb_obj_value(obj);
+}
+
+struct RProc*
+mrb_binding_wrap_lvspace(mrb_state *mrb, const struct RProc *proc, struct REnv **envp)
+{
+ /*
+ * local variable space: It is a space to hold the top-level variable of
+ * binding.eval and binding.local_variable_set.
+ */
+
+ static const mrb_code iseq_dummy[] = { OP_RETURN, 0 };
+
+ struct RProc *lvspace = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class);
+ mrb_irep *irep = mrb_add_irep(mrb);
+ irep->flags = MRB_ISEQ_NO_FREE;
+ irep->iseq = iseq_dummy;
+ irep->ilen = sizeof(iseq_dummy) / sizeof(iseq_dummy[0]);
+ irep->lv = (mrb_sym*)mrb_calloc(mrb, 1, sizeof(mrb_sym)); /* initial allocation for dummy */
+ irep->nlocals = 1;
+ irep->nregs = 1;
+ lvspace->body.irep = irep;
+ lvspace->upper = proc;
+ if (*envp) {
+ lvspace->e.env = *envp;
+ lvspace->flags |= MRB_PROC_ENVSET;
+ }
+
+ *envp = (struct REnv*)mrb_obj_alloc(mrb, MRB_TT_ENV, NULL);
+ (*envp)->stack = (mrb_value*)mrb_calloc(mrb, 1, sizeof(mrb_value));
+ (*envp)->stack[0] = lvspace->e.env ? lvspace->e.env->stack[0] : mrb_nil_value();
+ (*envp)->cxt = lvspace->e.env ? lvspace->e.env->cxt : mrb->c;
+ (*envp)->mid = 0;
+ (*envp)->flags = MRB_ENV_CLOSED | MRB_ENV_HEAPED;
+ MRB_ENV_SET_LEN(*envp, 1);
+
+ return lvspace;
+}
+
+static mrb_value
+mrb_f_binding(mrb_state *mrb, mrb_value self)
+{
+ mrb_value binding;
+ struct RProc *proc;
+ struct REnv *env;
+
+ binding = mrb_binding_alloc(mrb);
+ proc = (struct RProc*)mrb_proc_get_caller(mrb, &env);
+ if (!env || MRB_PROC_CFUNC_P(proc)) {
+ proc = NULL;
+ env = NULL;
+ }
+
+ if (proc && !MRB_PROC_CFUNC_P(proc)) {
+ const mrb_irep *irep = proc->body.irep;
+ mrb_iv_set(mrb, binding, MRB_SYM(pc), mrb_fixnum_value(mrb->c->ci[-1].pc - irep->iseq - 1 /* step back */));
+ }
+ proc = mrb_binding_wrap_lvspace(mrb, proc, &env);
+ mrb_iv_set(mrb, binding, MRB_SYM(proc), mrb_obj_value(proc));
+ mrb_iv_set(mrb, binding, MRB_SYM(recv), self);
+ mrb_iv_set(mrb, binding, MRB_SYM(env), mrb_obj_value(env));
+ return binding;
+}
+
+void
+mrb_mruby_binding_core_gem_init(mrb_state *mrb)
+{
+ struct RClass *binding = mrb_define_class(mrb, "Binding", mrb->object_class);
+ mrb_undef_class_method(mrb, binding, "new");
+ mrb_undef_class_method(mrb, binding, "allocate");
+
+ mrb_define_method(mrb, mrb->kernel_module, "binding", mrb_f_binding, MRB_ARGS_NONE());
+
+ mrb_define_method(mrb, binding, "local_variable_defined?", binding_local_variable_defined_p, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, binding, "local_variable_get", binding_local_variable_get, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, binding, "local_variable_set", binding_local_variable_set, MRB_ARGS_REQ(2));
+ mrb_define_method(mrb, binding, "local_variables", binding_local_variables, MRB_ARGS_NONE());
+ mrb_define_method(mrb, binding, "receiver", binding_receiver, MRB_ARGS_NONE());
+ mrb_define_method(mrb, binding, "source_location", binding_source_location, MRB_ARGS_NONE());
+ mrb_define_method(mrb, binding, "inspect", mrb_any_to_s, MRB_ARGS_NONE());
+}
+
+void
+mrb_mruby_binding_core_gem_final(mrb_state *mrb)
+{
+}
diff --git a/mrbgems/mruby-binding-core/test/binding-core.rb b/mrbgems/mruby-binding-core/test/binding-core.rb
new file mode 100644
index 000000000..066e79b18
--- /dev/null
+++ b/mrbgems/mruby-binding-core/test/binding-core.rb
@@ -0,0 +1,40 @@
+assert("Kernel.#binding") do
+ assert_kind_of Binding, binding
+end
+
+assert("Binding#local_variables") do
+ block = Proc.new do |a|
+ b = 1
+ binding
+ end
+ assert_equal [:a, :b, :block], block.call(0).local_variables.sort
+end
+
+assert("Binding#local_variable_set") do
+ bind = binding
+ 1.times {
+ assert_equal(9, bind.local_variable_set(:x, 9))
+ assert_raise(NameError) { x }
+ assert_equal([:bind, :x], bind.local_variables.sort)
+ }
+end
+
+assert("Binding#local_variable_get") do
+ bind = binding
+ x = 1
+ 1.times {
+ y = 2
+ assert_equal(1, bind.local_variable_get(:x))
+ x = 10
+ assert_equal(10, bind.local_variable_get(:x))
+ assert_raise(NameError) { bind.local_variable_get(:y) }
+ assert_equal([:bind, :x], bind.local_variables.sort)
+ }
+end
+
+assert("Binding#source_location") do
+ skip unless -> {}.source_location
+
+ bind, source_location = binding, [__FILE__, __LINE__]
+ assert_equal source_location, bind.source_location
+end
diff --git a/mrbgems/mruby-binding/mrbgem.rake b/mrbgems/mruby-binding/mrbgem.rake
new file mode 100644
index 000000000..4ad5638ea
--- /dev/null
+++ b/mrbgems/mruby-binding/mrbgem.rake
@@ -0,0 +1,11 @@
+MRuby::Gem::Specification.new('mruby-binding') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Binding class'
+
+ spec.add_dependency('mruby-binding-core', :core => 'mruby-binding-core')
+ spec.add_dependency('mruby-eval', :core => 'mruby-eval')
+ spec.add_test_dependency('mruby-metaprog', :core => 'mruby-metaprog')
+ spec.add_test_dependency('mruby-method', :core => 'mruby-method')
+ spec.add_test_dependency('mruby-proc-ext', :core => 'mruby-proc-ext')
+end
diff --git a/mrbgems/mruby-binding/mrblib/binding.rb b/mrbgems/mruby-binding/mrblib/binding.rb
new file mode 100644
index 000000000..b07480db1
--- /dev/null
+++ b/mrbgems/mruby-binding/mrblib/binding.rb
@@ -0,0 +1,5 @@
+class Binding
+ def eval(expr, *args)
+ Kernel.eval(expr, self, *args)
+ end
+end
diff --git a/mrbgems/mruby-binding/test/binding.c b/mrbgems/mruby-binding/test/binding.c
new file mode 100644
index 000000000..5a37ca043
--- /dev/null
+++ b/mrbgems/mruby-binding/test/binding.c
@@ -0,0 +1,13 @@
+#include <mruby.h>
+
+static mrb_value
+binding_in_c(mrb_state *mrb, mrb_value self)
+{
+ return mrb_funcall_argv(mrb, mrb_obj_value(mrb->object_class), mrb_intern_lit(mrb, "binding"), 0, NULL);
+}
+
+void
+mrb_mruby_binding_gem_test(mrb_state *mrb)
+{
+ mrb_define_method(mrb, mrb->object_class, "binding_in_c", binding_in_c, MRB_ARGS_NONE());
+}
diff --git a/mrbgems/mruby-binding/test/binding.rb b/mrbgems/mruby-binding/test/binding.rb
new file mode 100644
index 000000000..7dd3fd1dd
--- /dev/null
+++ b/mrbgems/mruby-binding/test/binding.rb
@@ -0,0 +1,70 @@
+assert("Binding#eval") do
+ b = nil
+ 1.times { x, y, z = 1, 2, 3; [x,y,z]; b = binding }
+ assert_equal([1, 2, 3], b.eval("[x, y, z]"))
+ here = self
+ assert_equal(here, b.eval("self"))
+end
+
+assert("Binding#local_variables") do
+ block = Proc.new do |a|
+ b = 1
+ binding
+ end
+ bind = block.call(0)
+ assert_equal [:a, :b, :bind, :block], bind.local_variables.sort
+ bind.eval("x = 2")
+ assert_equal [:a, :b, :bind, :block, :x], bind.local_variables.sort
+end
+
+assert("Binding#local_variable_set") do
+ bind = binding
+ 1.times {
+ assert_equal(9, bind.local_variable_set(:x, 9))
+ assert_equal(9, bind.eval("x"))
+ assert_equal([:bind, :x], bind.eval("local_variables.sort"))
+ }
+end
+
+assert("Binding#local_variable_get") do
+ bind = binding
+ x = 1
+ 1.times {
+ y = 2
+ assert_equal(1, bind.local_variable_get(:x))
+ x = 10
+ assert_equal(10, bind.local_variable_get(:x))
+ assert_raise(NameError) { bind.local_variable_get(:y) }
+ bind.eval("z = 3")
+ assert_equal(3, bind.local_variable_get(:z))
+ bind.eval("y = 5")
+ assert_equal(5, bind.local_variable_get(:y))
+ assert_equal(2, y)
+ }
+end
+
+assert("Binding#source_location") do
+ skip unless -> {}.source_location
+
+ bind, source_location = binding, [__FILE__, __LINE__]
+ assert_equal source_location, bind.source_location
+end
+
+assert "Kernel#binding and .eval from C" do
+ bind = binding_in_c
+ assert_equal 5, bind.eval("2 + 3")
+ assert_nothing_raised { bind.eval("self") }
+end
+
+assert "Binding#eval with Binding.new via UnboundMethod" do
+ assert_raise(NoMethodError) { Class.instance_method(:new).bind_call(Binding) }
+end
+
+assert "Binding#eval with Binding.new via Method" do
+ # The following test is OK if SIGSEGV does not occur
+ cx = Class.new(Binding)
+ cx.define_singleton_method(:allocate, &Object.method(:allocate))
+ Class.instance_method(:new).bind_call(cx).eval("")
+
+ assert_true true
+end
diff --git a/mrbgems/mruby-eval/src/eval.c b/mrbgems/mruby-eval/src/eval.c
index 08ef8d937..d85c829d7 100644
--- a/mrbgems/mruby-eval/src/eval.c
+++ b/mrbgems/mruby-eval/src/eval.c
@@ -6,12 +6,13 @@
#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_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p, mrb_func_t posthook);
mrb_value mrb_obj_instance_eval(mrb_state *mrb, mrb_value self);
-
void mrb_codedump_all(mrb_state*, struct RProc*);
+void mrb_proc_merge_lvar(mrb_state *mrb, mrb_irep *irep, struct REnv *env, int num, const mrb_sym *lv, const mrb_value *stack);
static struct RProc*
create_proc_from_string(mrb_state *mrb, const char *s, mrb_int len, mrb_value binding, const char *file, mrb_int line)
@@ -19,12 +20,36 @@ create_proc_from_string(mrb_state *mrb, const char *s, mrb_int len, mrb_value bi
mrbc_context *cxt;
struct mrb_parser_state *p;
struct RProc *proc;
+ const struct RProc *scope;
struct REnv *e;
mrb_callinfo *ci; /* callinfo of eval caller */
struct RClass *target_class = NULL;
+ struct mrb_context *c = mrb->c;
if (!mrb_nil_p(binding)) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "Binding of eval must be nil.");
+ mrb_value scope_obj;
+ if (!mrb_class_defined(mrb, "Binding")
+ || !mrb_obj_is_kind_of(mrb, binding, mrb_class_get(mrb, "Binding"))) {
+ mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %C (expected binding)",
+ mrb_obj_class(mrb, binding));
+ }
+ scope_obj = mrb_iv_get(mrb, binding, MRB_SYM(proc));
+ mrb_assert(mrb_proc_p(scope_obj));
+ scope = mrb_proc_ptr(scope_obj);
+ if (MRB_PROC_CFUNC_P(scope)) {
+ e = NULL;
+ }
+ else {
+ mrb_value env = mrb_iv_get(mrb, binding, MRB_SYM(env));
+ mrb_assert(mrb_env_p(env));
+ e = (struct REnv *)mrb_obj_ptr(env);
+ mrb_assert(e != NULL);
+ }
+ }
+ else {
+ ci = (c->ci > c->cibase) ? c->ci - 1 : c->cibase;
+ scope = ci->proc;
+ e = NULL;
}
cxt = mrbc_context_new(mrb);
@@ -33,8 +58,7 @@ create_proc_from_string(mrb_state *mrb, const char *s, mrb_int len, mrb_value bi
mrbc_filename(mrb, cxt, file ? file : "(eval)");
cxt->capture_errors = TRUE;
cxt->no_optimize = TRUE;
- ci = (mrb->c->ci > mrb->c->cibase) ? mrb->c->ci - 1 : mrb->c->cibase;
- cxt->upper = ci->proc && MRB_PROC_CFUNC_P(ci->proc) ? NULL : ci->proc;
+ cxt->upper = scope && MRB_PROC_CFUNC_P(scope) ? NULL : scope;
p = mrb_parse_nstring(mrb, s, len, cxt);
@@ -70,28 +94,29 @@ create_proc_from_string(mrb_state *mrb, const char *s, mrb_int len, mrb_value bi
mrbc_context_free(mrb, cxt);
mrb_raise(mrb, E_SCRIPT_ERROR, "codegen error");
}
- if (mrb->c->ci > mrb->c->cibase) {
- ci = &mrb->c->ci[-1];
+ if (c->ci > c->cibase) {
+ ci = &c->ci[-1];
}
else {
- ci = mrb->c->cibase;
- }
- if (ci->proc) {
- target_class = MRB_PROC_TARGET_CLASS(ci->proc);
+ ci = c->cibase;
}
- if (ci->proc && !MRB_PROC_CFUNC_P(ci->proc)) {
- if ((e = mrb_vm_ci_env(ci)) != NULL) {
- /* do nothing, because e is assigned already */
- }
- else {
- e = mrb_env_new(mrb, mrb->c, ci, ci->proc->body.irep->nlocals, ci->stack, target_class);
- ci->u.env = e;
+ if (scope) {
+ target_class = MRB_PROC_TARGET_CLASS(scope);
+ if (!MRB_PROC_CFUNC_P(scope)) {
+ if (e == NULL) {
+ /* when `binding` is nil */
+ e = mrb_vm_ci_env(ci);
+ if (e == NULL) {
+ e = mrb_env_new(mrb, c, ci, ci->proc->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->e.env = e;
- proc->flags |= MRB_PROC_ENVSET;
- mrb_field_write_barrier(mrb, (struct RBasic*)proc, (struct RBasic*)e);
}
- proc->upper = ci->proc;
+ proc->upper = scope;
mrb_vm_ci_target_class_set(mrb->c->ci, target_class);
/* mrb_codedump_all(mrb, proc); */
@@ -102,7 +127,7 @@ 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)
+exec_irep(mrb_state *mrb, mrb_value self, struct RProc *proc, mrb_func_t posthook)
{
/* no argument passed from eval() */
mrb->c->ci->argc = 0;
@@ -117,7 +142,38 @@ exec_irep(mrb_state *mrb, mrb_value self, struct RProc *proc)
}
/* clear block */
mrb->c->ci->stack[1] = mrb_nil_value();
- return mrb_exec_irep(mrb, self, proc);
+ 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];
}
static mrb_value
@@ -129,12 +185,19 @@ 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);
+ return exec_irep(mrb, self, proc, posthook);
}
static mrb_value
@@ -159,7 +222,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);
+ return exec_irep(mrb, self, proc, NULL);
}
else {
mrb_get_args(mrb, "&", &b);
diff --git a/mrbgems/mruby-eval/test/eval.rb b/mrbgems/mruby-eval/test/eval.rb
index 639ed68f9..e95171223 100644
--- a/mrbgems/mruby-eval/test/eval.rb
+++ b/mrbgems/mruby-eval/test/eval.rb
@@ -44,7 +44,7 @@ assert('Kernel#eval', '15.3.1.3.12') do
end
assert('rest arguments of eval') do
- assert_raise(ArgumentError) { Kernel.eval('0', 0, 'test', 0) }
+ assert_raise(TypeError) { Kernel.eval('0', 0, 'test', 0) }
assert_equal ['test', 'test.rb', 10] do
Kernel.eval('[\'test\', __FILE__, __LINE__]', nil, 'test.rb', 10)
end
diff --git a/mrbgems/mruby-metaprog/src/metaprog.c b/mrbgems/mruby-metaprog/src/metaprog.c
index f2f2e7dda..4bf31cbeb 100644
--- a/mrbgems/mruby-metaprog/src/metaprog.c
+++ b/mrbgems/mruby-metaprog/src/metaprog.c
@@ -21,6 +21,8 @@ typedef enum {
NOEX_RESPONDS = 0x80
} mrb_method_flag_t;
+mrb_value mrb_proc_local_variables(mrb_state *mrb, const struct RProc *proc);
+
static mrb_value
mrb_f_nil(mrb_state *mrb, mrb_value cv)
{
@@ -133,40 +135,7 @@ mrb_obj_ivar_set(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_local_variables(mrb_state *mrb, mrb_value self)
{
- const struct RProc *proc;
- const mrb_irep *irep;
- mrb_value vars;
- size_t i;
-
- proc = mrb->c->ci[-1].proc;
-
- if (proc == NULL || MRB_PROC_CFUNC_P(proc)) {
- return mrb_ary_new(mrb);
- }
- vars = mrb_hash_new(mrb);
- while (proc) {
- if (MRB_PROC_CFUNC_P(proc)) break;
- irep = proc->body.irep;
- if (irep->lv) {
- for (i = 0; i + 1 < irep->nlocals; ++i) {
- if (irep->lv[i]) {
- mrb_sym sym = irep->lv[i];
- const char *name = mrb_sym_name(mrb, sym);
- switch (name[0]) {
- case '*': case '&':
- break;
- default:
- mrb_hash_set(mrb, vars, mrb_symbol_value(sym), mrb_true_value());
- break;
- }
- }
- }
- }
- if (MRB_PROC_SCOPE_P(proc)) break;
- proc = proc->upper;
- }
-
- return mrb_hash_keys(mrb, vars);
+ return mrb_proc_local_variables(mrb, mrb->c->ci[-1].proc);
}
KHASH_DECLARE(st, mrb_sym, char, FALSE)
diff --git a/mrbgems/mruby-proc-binding/mrbgem.rake b/mrbgems/mruby-proc-binding/mrbgem.rake
new file mode 100644
index 000000000..425aac847
--- /dev/null
+++ b/mrbgems/mruby-proc-binding/mrbgem.rake
@@ -0,0 +1,9 @@
+MRuby::Gem::Specification.new('mruby-proc-binding') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Proc#binding method'
+
+ spec.add_dependency('mruby-binding-core', :core => 'mruby-binding-core')
+ spec.add_test_dependency('mruby-binding', :core => 'mruby-binding')
+ spec.add_test_dependency('mruby-compiler', :core => 'mruby-compiler')
+end
diff --git a/mrbgems/mruby-proc-binding/src/proc-binding.c b/mrbgems/mruby-proc-binding/src/proc-binding.c
new file mode 100644
index 000000000..82d9d1d51
--- /dev/null
+++ b/mrbgems/mruby-proc-binding/src/proc-binding.c
@@ -0,0 +1,52 @@
+#include <mruby.h>
+#include <mruby/presym.h>
+#include <mruby/proc.h>
+#include <mruby/variable.h>
+
+void mrb_proc_merge_lvar(mrb_state *mrb, mrb_irep *irep, struct REnv *env, int num, const mrb_sym *lv, const mrb_value *stack);
+
+/* provided by mruby-proc-ext */
+mrb_value mrb_proc_source_location(mrb_state *mrb, struct RProc *p);
+
+/* provided by mruby-binding-core */
+mrb_value mrb_binding_alloc(mrb_state *mrb);
+struct RProc *mrb_binding_wrap_lvspace(mrb_state *mrb, const struct RProc *proc, struct REnv **envp);
+
+static mrb_value
+mrb_proc_binding(mrb_state *mrb, mrb_value procval)
+{
+ mrb_value binding = mrb_binding_alloc(mrb);
+ const struct RProc *proc = mrb_proc_ptr(procval);
+ struct REnv *env;
+
+ mrb_value receiver;
+ if (!proc || MRB_PROC_CFUNC_P(proc) || !proc->upper || MRB_PROC_CFUNC_P(proc->upper)) {
+ env = NULL;
+ proc = NULL;
+ receiver = mrb_nil_value();
+ }
+ else {
+ env = MRB_PROC_ENV(proc);
+ mrb_assert(env);
+ proc = proc->upper;
+ receiver = MRB_ENV_LEN(env) > 0 ? env->stack[0] : mrb_nil_value();
+ }
+
+ proc = mrb_binding_wrap_lvspace(mrb, proc, &env);
+ mrb_iv_set(mrb, binding, MRB_SYM(proc), mrb_obj_value((void *)proc));
+ mrb_iv_set(mrb, binding, MRB_SYM(recv), receiver);
+ mrb_iv_set(mrb, binding, MRB_SYM(env), mrb_obj_value(env));
+ mrb_iv_set(mrb, binding, MRB_SYM(source_location), mrb_proc_source_location(mrb, mrb_proc_ptr(procval)));
+ return binding;
+}
+
+void
+mrb_mruby_proc_binding_gem_init(mrb_state *mrb)
+{
+ mrb_define_method(mrb, mrb->proc_class, "binding", mrb_proc_binding, MRB_ARGS_NONE());
+}
+
+void
+mrb_mruby_proc_binding_gem_final(mrb_state *mrb)
+{
+}
diff --git a/mrbgems/mruby-proc-binding/test/proc-binding.c b/mrbgems/mruby-proc-binding/test/proc-binding.c
new file mode 100644
index 000000000..ec071b920
--- /dev/null
+++ b/mrbgems/mruby-proc-binding/test/proc-binding.c
@@ -0,0 +1,14 @@
+#include <mruby.h>
+#include <mruby/compile.h>
+
+static mrb_value
+proc_in_c(mrb_state *mrb, mrb_value self)
+{
+ return mrb_load_string(mrb, "proc { |a, b| a + b }");
+}
+
+void
+mrb_mruby_proc_binding_gem_test(mrb_state *mrb)
+{
+ mrb_define_method(mrb, mrb->object_class, "proc_in_c", proc_in_c, MRB_ARGS_NONE());
+}
diff --git a/mrbgems/mruby-proc-binding/test/proc-binding.rb b/mrbgems/mruby-proc-binding/test/proc-binding.rb
new file mode 100644
index 000000000..b28d8b1dd
--- /dev/null
+++ b/mrbgems/mruby-proc-binding/test/proc-binding.rb
@@ -0,0 +1,22 @@
+assert "Proc#binding" do
+ block = ->(i) {}
+ a, b, c = 1, 2, 3
+ bind = block.binding
+ assert_equal([:a, :b, :bind, :block, :c], bind.local_variables.sort)
+ assert_equal(1, bind.local_variable_get(:a))
+ assert_equal(5, bind.eval("b + c"))
+ bind.local_variable_set(:x, 9)
+ assert_equal(9, bind.local_variable_get(:x))
+end
+
+assert("Binding#source_location after Proc#binding") do
+ skip unless -> {}.source_location
+
+ block, source_location = -> {}, [__FILE__, __LINE__]
+ assert_equal source_location, block.binding.source_location
+end
+
+assert "Proc#binding and .eval from C" do
+ bind = proc_in_c.binding
+ assert_nothing_raised { bind.eval("self") }
+end
diff --git a/mrbgems/mruby-proc-ext/src/proc.c b/mrbgems/mruby-proc-ext/src/proc.c
index 6117c4d40..9d8a7b037 100644
--- a/mrbgems/mruby-proc-ext/src/proc.c
+++ b/mrbgems/mruby-proc-ext/src/proc.c
@@ -13,11 +13,9 @@ mrb_proc_lambda_p(mrb_state *mrb, mrb_value self)
return mrb_bool_value(MRB_PROC_STRICT_P(p));
}
-static mrb_value
-mrb_proc_source_location(mrb_state *mrb, mrb_value self)
+mrb_value
+mrb_proc_source_location(mrb_state *mrb, struct RProc *p)
{
- struct RProc *p = mrb_proc_ptr(self);
-
if (MRB_PROC_CFUNC_P(p)) {
return mrb_nil_value();
}
@@ -35,6 +33,12 @@ mrb_proc_source_location(mrb_state *mrb, mrb_value self)
}
static mrb_value
+mrb_proc_source_location_m(mrb_state *mrb, mrb_value self)
+{
+ return mrb_proc_source_location(mrb, mrb_proc_ptr(self));
+}
+
+static mrb_value
mrb_proc_inspect(mrb_state *mrb, mrb_value self)
{
struct RProc *p = mrb_proc_ptr(self);
@@ -183,7 +187,7 @@ mrb_mruby_proc_ext_gem_init(mrb_state* mrb)
{
struct RClass *p = mrb->proc_class;
mrb_define_method(mrb, p, "lambda?", mrb_proc_lambda_p, MRB_ARGS_NONE());
- mrb_define_method(mrb, p, "source_location", mrb_proc_source_location, MRB_ARGS_NONE());
+ mrb_define_method(mrb, p, "source_location", mrb_proc_source_location_m, MRB_ARGS_NONE());
mrb_define_method(mrb, p, "to_s", mrb_proc_inspect, MRB_ARGS_NONE());
mrb_define_method(mrb, p, "inspect", mrb_proc_inspect, MRB_ARGS_NONE());
mrb_define_method(mrb, p, "parameters", mrb_proc_parameters, MRB_ARGS_NONE());
diff --git a/src/proc.c b/src/proc.c
index 870d5ea16..cfaf37af6 100644
--- a/src/proc.c
+++ b/src/proc.c
@@ -10,6 +10,8 @@
#include <mruby/opcode.h>
#include <mruby/data.h>
#include <mruby/presym.h>
+#include <mruby/array.h>
+#include <mruby/hash.h>
static const mrb_code call_iseq[] = {
OP_CALL,
@@ -305,6 +307,109 @@ mrb_proc_arity(const struct RProc *p)
return arity;
}
+mrb_value
+mrb_proc_local_variables(mrb_state *mrb, const struct RProc *proc)
+{
+ const mrb_irep *irep;
+ mrb_value vars;
+ size_t i;
+
+ if (proc == NULL || MRB_PROC_CFUNC_P(proc)) {
+ return mrb_ary_new(mrb);
+ }
+ vars = mrb_hash_new(mrb);
+ while (proc) {
+ if (MRB_PROC_CFUNC_P(proc)) break;
+ irep = proc->body.irep;
+ if (irep->lv) {
+ for (i = 0; i + 1 < irep->nlocals; ++i) {
+ if (irep->lv[i]) {
+ mrb_sym sym = irep->lv[i];
+ const char *name = mrb_sym_name(mrb, sym);
+ switch (name[0]) {
+ case '*': case '&':
+ break;
+ default:
+ mrb_hash_set(mrb, vars, mrb_symbol_value(sym), mrb_true_value());
+ break;
+ }
+ }
+ }
+ }
+ if (MRB_PROC_SCOPE_P(proc)) break;
+ proc = proc->upper;
+ }
+
+ return mrb_hash_keys(mrb, vars);
+}
+
+const struct RProc *
+mrb_proc_get_caller(mrb_state *mrb, struct REnv **envp)
+{
+ struct mrb_context *c = mrb->c;
+ mrb_callinfo *ci = (c->ci > c->cibase) ? c->ci - 1 : c->cibase;
+ const struct RProc *proc = ci->proc;
+
+ if (!proc || MRB_PROC_CFUNC_P(proc)) {
+ if (envp) *envp = NULL;
+ }
+ else {
+ struct RClass *tc = MRB_PROC_TARGET_CLASS(proc);
+ struct REnv *e = mrb_vm_ci_env(ci);
+
+ if (e == NULL) {
+ int nstacks = proc->body.irep->nlocals;
+ e = mrb_env_new(mrb, c, ci, nstacks, ci->stack, tc);
+ ci->u.env = e;
+ }
+ else if (tc) {
+ e->c = tc;
+ mrb_field_write_barrier(mrb, (struct RBasic*)e, (struct RBasic*)tc);
+ }
+ if (envp) *envp = e;
+ }
+
+ 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)
{
diff --git a/src/vm.c b/src/vm.c
index 0e7eb65e5..2607ba308 100644
--- a/src/vm.c
+++ b/src/vm.c
@@ -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*