ruby-changes:38511
From: ko1 <ko1@a...>
Date: Thu, 21 May 2015 17:46:16 +0900 (JST)
Subject: [ruby-changes:38511] ko1:r50592 (trunk): * proc.c: fix issues caused by binding created from Method#to_proc.
ko1 2015-05-21 17:45:57 +0900 (Thu, 21 May 2015) New Revision: 50592 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=50592 Log: * proc.c: fix issues caused by binding created from Method#to_proc. [Bug #11163] * vm.c (vm_cref_new_toplevel): export as rb_vm_cref_new_toplevel(). * test/ruby/test_method.rb: add some assersions. Modified files: trunk/ChangeLog trunk/proc.c trunk/test/ruby/test_method.rb trunk/vm.c Index: ChangeLog =================================================================== --- ChangeLog (revision 50591) +++ ChangeLog (revision 50592) @@ -1,3 +1,12 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1 +Thu May 21 17:44:53 2015 Koichi Sasada <ko1@a...> + + * proc.c: fix issues caused by binding created from Method#to_proc. + [Bug #11163] + + * vm.c (vm_cref_new_toplevel): export as rb_vm_cref_new_toplevel(). + + * test/ruby/test_method.rb: add some assersions. + Thu May 21 17:29:26 2015 SHIBATA Hiroshi <hsbt@r...> * lib/matrix.rb: added documentation for Matrix#empty and Matrix#/ Index: proc.c =================================================================== --- proc.c (revision 50591) +++ proc.c (revision 50592) @@ -33,6 +33,7 @@ VALUE rb_cProc; https://github.com/ruby/ruby/blob/trunk/proc.c#L33 static VALUE bmcall(VALUE, VALUE, int, VALUE *, VALUE); static int method_arity(VALUE); static int method_min_max_arity(VALUE, int *max); + #define attached id__attached__ /* Proc */ @@ -369,14 +370,19 @@ get_local_variable_ptr(VALUE envval, ID https://github.com/ruby/ruby/blob/trunk/proc.c#L370 GetEnvPtr(envval, env); iseq = env->block.iseq; + if (RUBY_VM_NORMAL_ISEQ_P(iseq)) { for (i=0; i<iseq->local_table_size; i++) { if (iseq->local_table[i] == lid) { return &env->env[i]; } } + } + else { + return NULL; + } } while ((envval = env->prev_envval) != 0); - return 0; + return NULL; } /* @@ -2401,30 +2407,20 @@ static VALUE https://github.com/ruby/ruby/blob/trunk/proc.c#L2407 method_to_proc(VALUE method) { VALUE procval; - struct METHOD *meth; rb_proc_t *proc; - rb_env_t *env; /* * class Method * def to_proc - * proc{|*args| + * lambda{|*args| * self.call(*args) * } * end * end */ - TypedData_Get_Struct(method, struct METHOD, &method_data_type, meth); procval = rb_iterate(mlambda, 0, bmcall, method); GetProcPtr(procval, proc); proc->is_from_method = 1; - proc->block.self = meth->recv; - proc->block.klass = meth->defined_class; - GetEnvPtr(proc->envval, env); - env->block.self = meth->recv; - env->block.klass = meth->defined_class; - env->block.iseq = method_def_iseq(meth->me->def); - env->block.ep[-1] = (VALUE)method_cref(method); return procval; } @@ -2476,6 +2472,29 @@ localjump_reason(VALUE exc) https://github.com/ruby/ruby/blob/trunk/proc.c#L2472 return rb_iv_get(exc, "@reason"); } +rb_cref_t *rb_vm_cref_new_toplevel(void); /* vm.c */ + +static VALUE +env_clone(VALUE envval, VALUE receiver, const rb_cref_t *cref) +{ + VALUE newenvval = TypedData_Wrap_Struct(RBASIC_CLASS(envval), RTYPEDDATA_TYPE(envval), 0); + rb_env_t *env, *newenv; + int envsize; + + if (cref == NULL) { + cref = rb_vm_cref_new_toplevel(); + } + + GetEnvPtr(envval, env); + envsize = sizeof(rb_env_t) + (env->local_size + 1) * sizeof(VALUE); + newenv = xmalloc(envsize); + memcpy(newenv, env, envsize); + RTYPEDDATA_DATA(newenvval) = newenv; + newenv->block.self = receiver; + newenv->block.ep[-1] = (VALUE)cref; + return newenvval; +} + /* * call-seq: * prc.binding -> binding @@ -2503,34 +2522,31 @@ proc_binding(VALUE self) https://github.com/ruby/ruby/blob/trunk/proc.c#L2522 envval = proc->envval; iseq = proc->block.iseq; if (RUBY_VM_IFUNC_P(iseq)) { - rb_env_t *env; - if (!IS_METHOD_PROC_ISEQ(iseq)) { - rb_raise(rb_eArgError, "Can't create Binding from C level Proc"); - } - iseq = rb_method_iseq((VALUE)((struct vm_ifunc *)iseq)->data); - GetEnvPtr(envval, env); - if (iseq && env->local_size < iseq->local_size) { - int prev_local_size = env->local_size; - int local_size = iseq->local_size; - VALUE newenvval = TypedData_Wrap_Struct(RBASIC_CLASS(envval), RTYPEDDATA_TYPE(envval), 0); - rb_env_t *newenv = xmalloc(sizeof(rb_env_t) + ((local_size + 1) * sizeof(VALUE))); - RTYPEDDATA_DATA(newenvval) = newenv; - newenv->env_size = local_size + 2; - newenv->local_size = local_size; - newenv->prev_envval = env->prev_envval; - newenv->block = env->block; - MEMCPY(newenv->env, env->env, VALUE, prev_local_size + 1); - rb_mem_clear(newenv->env + prev_local_size + 1, local_size - prev_local_size); - newenv->env[local_size + 1] = newenvval; - envval = newenvval; + if (IS_METHOD_PROC_ISEQ(iseq)) { + VALUE method = (VALUE)((struct vm_ifunc *)iseq)->data; + envval = env_clone(envval, method_receiver(method), method_cref(method)); } + else { + rb_raise(rb_eArgError, "Can't create Binding from C level Proc"); + } } bindval = rb_binding_alloc(rb_cBinding); GetBindingPtr(bindval, bind); bind->env = envval; bind->blockprocval = proc->blockprocval; - if (RUBY_VM_NORMAL_ISEQ_P(iseq)) { + + if (!RUBY_VM_NORMAL_ISEQ_P(iseq)) { + if (RUBY_VM_IFUNC_P(iseq) && IS_METHOD_PROC_ISEQ(iseq)) { + VALUE method = (VALUE)((struct vm_ifunc *)iseq)->data; + iseq = rb_method_iseq(method); + } + else { + iseq = NULL; + } + } + + if (iseq) { bind->path = iseq->location.path; bind->first_lineno = FIX2INT(rb_iseq_first_lineno(iseq->self)); } @@ -2538,6 +2554,7 @@ proc_binding(VALUE self) https://github.com/ruby/ruby/blob/trunk/proc.c#L2554 bind->path = Qnil; bind->first_lineno = 0; } + return bindval; } Index: vm.c =================================================================== --- vm.c (revision 50591) +++ vm.c (revision 50592) @@ -98,6 +98,12 @@ vm_cref_new_toplevel(rb_thread_t *th) https://github.com/ruby/ruby/blob/trunk/vm.c#L98 return cref; } +rb_cref_t * +rb_vm_cref_new_toplevel(void) +{ + return vm_cref_new_toplevel(GET_THREAD()); +} + static void vm_cref_dump(const char *mesg, const rb_cref_t *cref) { @@ -772,8 +778,14 @@ rb_binding_add_dynavars(rb_binding_t *bi https://github.com/ruby/ruby/blob/trunk/vm.c#L778 MEMCPY(dyns + 1, dynvars, ID, dyncount); node = NEW_NODE(NODE_SCOPE, dyns, 0, 0); + if (base_iseq) { iseqval = rb_iseq_new(node, base_iseq->location.label, path, path, base_iseq->self, ISEQ_TYPE_EVAL); + } + else { + VALUE tempstr = rb_str_new2("<temp>"); + iseqval = rb_iseq_new_top(node, tempstr, tempstr, tempstr, Qfalse); + } node->u1.tbl = 0; /* reset table */ ALLOCV_END(idtmp); Index: test/ruby/test_method.rb =================================================================== --- test/ruby/test_method.rb (revision 50591) +++ test/ruby/test_method.rb (revision 50592) @@ -887,17 +887,25 @@ class TestMethod < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_method.rb#L887 end end + class C + D = "Const_D" + def foo + a = b = c = 12345 + end + end + def test_to_proc_binding bug11012 = '[ruby-core:68673] [Bug #11012]' - class << (obj = Object.new) - src = 1000.times.map {|i|"v#{i} = nil"}.join("\n") - eval("def foo()\n""#{src}\n""end") - end - b = obj.method(:foo).to_proc.binding - b.local_variables.each_with_index {|n, i| - b.local_variable_set(n, i) - } - assert_equal([998, 999], %w[v998 v999].map {|n| b.local_variable_get(n)}, bug11012) + b = C.new.method(:foo).to_proc.binding + assert_equal([], b.local_variables) + assert_equal("Const_D", b.eval("D")) # Check CREF + + assert_raise(NameError){ b.local_variable_get(:foo) } + assert_equal(123, b.local_variable_set(:foo, 123)) + assert_equal(123, b.local_variable_get(:foo)) + assert_equal(456, b.local_variable_set(:bar, 456)) + assert_equal(123, b.local_variable_get(:foo)) + assert_equal(456, b.local_variable_get(:bar)) end end -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/