[前][次][番号順一覧][スレッド一覧]

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/

[前][次][番号順一覧][スレッド一覧]