summaryrefslogtreecommitdiffhomepage
path: root/mrbgems
diff options
context:
space:
mode:
authordearblue <[email protected]>2021-02-22 23:32:43 +0900
committerdearblue <[email protected]>2021-02-22 23:32:43 +0900
commit927615e1f072d8fff3d9b84660cdce15a239e36c (patch)
tree14e14aa860b778176435be8d6d666917d891a9d8 /mrbgems
parent792f6ac6700469ddf9be8f87ca3376082f9af7f3 (diff)
downloadmruby-927615e1f072d8fff3d9b84660cdce15a239e36c.tar.gz
mruby-927615e1f072d8fff3d9b84660cdce15a239e36c.zip
Added other methods for `Binding`
- Added to `mruby-binding-core` - `Binding#local_variable_defined?` - `Binding#local_variable_get` - `Binding#local_variable_set` - `Binding#local_variables` - `Binding#receiver` - `Binding#source_location` - `Binding#inspect` - Added to `mruby-proc-binding` - `Proc#binding` The reason for separating `Proc#binding` is that core-mrbgems has a method that returns a closure object to minimize possible problems with being able to manipulate internal variables. By separating it as different mrbgem, each user can judge this problem and incorporate it arbitrarily.
Diffstat (limited to 'mrbgems')
-rw-r--r--mrbgems/mruby-binding-core/mrbgem.rake4
-rw-r--r--mrbgems/mruby-binding-core/src/binding-core.c266
-rw-r--r--mrbgems/mruby-binding-core/test/binding-core.rb29
-rw-r--r--mrbgems/mruby-binding/mrbgem.rake3
-rw-r--r--mrbgems/mruby-binding/test/binding.c13
-rw-r--r--mrbgems/mruby-binding/test/binding.rb65
-rw-r--r--mrbgems/mruby-eval/src/eval.c47
-rw-r--r--mrbgems/mruby-metaprog/src/metaprog.c4
-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
13 files changed, 524 insertions, 18 deletions
diff --git a/mrbgems/mruby-binding-core/mrbgem.rake b/mrbgems/mruby-binding-core/mrbgem.rake
index f6b34982f..c0ba48207 100644
--- a/mrbgems/mruby-binding-core/mrbgem.rake
+++ b/mrbgems/mruby-binding-core/mrbgem.rake
@@ -1,5 +1,7 @@
MRuby::Gem::Specification.new('mruby-binding-core') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
- spec.summary = 'Binding class'
+ 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
index 0113fc030..8086185ea 100644
--- a/mrbgems/mruby-binding-core/src/binding-core.c
+++ b/mrbgems/mruby-binding-core/src/binding-core.c
@@ -4,8 +4,160 @@
#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)
@@ -14,19 +166,119 @@ binding_local_variables(mrb_state *mrb, mrb_value self)
return mrb_proc_local_variables(mrb, proc);
}
-const struct RProc *mrb_proc_get_caller(mrb_state *mrb, struct REnv **env);
+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)
{
- struct RObject *obj;
mrb_value binding;
struct RProc *proc;
struct REnv *env;
- obj = (struct RObject*)mrb_obj_alloc(mrb, MRB_TT_OBJECT, mrb_class_get(mrb, "Binding"));
- binding = mrb_obj_value(obj);
+ 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));
@@ -42,7 +294,13 @@ mrb_mruby_binding_core_gem_init(mrb_state *mrb)
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
diff --git a/mrbgems/mruby-binding-core/test/binding-core.rb b/mrbgems/mruby-binding-core/test/binding-core.rb
index d93f43619..066e79b18 100644
--- a/mrbgems/mruby-binding-core/test/binding-core.rb
+++ b/mrbgems/mruby-binding-core/test/binding-core.rb
@@ -9,3 +9,32 @@ assert("Binding#local_variables") do
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
index 2701c506f..4ad5638ea 100644
--- a/mrbgems/mruby-binding/mrbgem.rake
+++ b/mrbgems/mruby-binding/mrbgem.rake
@@ -5,4 +5,7 @@ MRuby::Gem::Specification.new('mruby-binding') do |spec|
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/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
index 73742400f..7dd3fd1dd 100644
--- a/mrbgems/mruby-binding/test/binding.rb
+++ b/mrbgems/mruby-binding/test/binding.rb
@@ -2,4 +2,69 @@ 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 8cc37904a..d85c829d7 100644
--- a/mrbgems/mruby-eval/src/eval.c
+++ b/mrbgems/mruby-eval/src/eval.c
@@ -9,10 +9,10 @@
#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)
@@ -127,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;
@@ -142,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
@@ -154,15 +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
@@ -187,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-metaprog/src/metaprog.c b/mrbgems/mruby-metaprog/src/metaprog.c
index 99a1b46da..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)
{
@@ -117,8 +119,6 @@ mrb_obj_ivar_set(mrb_state *mrb, mrb_value self)
return val;
}
-mrb_value mrb_proc_local_variables(mrb_state *mrb, const struct RProc *proc);
-
/* 15.3.1.2.7 */
/* 15.3.1.3.28 */
/*
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());