mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	introduce RCLASS_CLONED flag for inline cache.
Methods on duplicated class/module refer same constant inline cache (IC). Constant access lookup should be done for cloned class/modules but inline cache doesn't check it. To check it, this patch introduce new RCLASS_CLONED flag which are set when if class/module is cloned (both orig and dst). [Bug #15877]
This commit is contained in:
		
							parent
							
								
									c7acb37248
								
							
						
					
					
						commit
						71efad1ed3
					
				
					 5 changed files with 60 additions and 14 deletions
				
			
		
							
								
								
									
										8
									
								
								class.c
									
										
									
									
									
								
							
							
						
						
									
										8
									
								
								class.c
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -308,11 +308,17 @@ class_init_copy_check(VALUE clone, VALUE orig)
 | 
			
		|||
	rb_raise(rb_eTypeError, "can't copy singleton class");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#include "gc.h"
 | 
			
		||||
/* :nodoc: */
 | 
			
		||||
VALUE
 | 
			
		||||
rb_mod_init_copy(VALUE clone, VALUE orig)
 | 
			
		||||
{
 | 
			
		||||
    /* cloned flag is refer at constant inline cache
 | 
			
		||||
     * see vm_get_const_key_cref() in vm_insnhelper.c
 | 
			
		||||
     */
 | 
			
		||||
    FL_SET(clone, RCLASS_CLONED);
 | 
			
		||||
    FL_SET(orig , RCLASS_CLONED);
 | 
			
		||||
 | 
			
		||||
    if (RB_TYPE_P(clone, T_CLASS)) {
 | 
			
		||||
	class_init_copy_check(clone, orig);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1056,6 +1056,7 @@ int rb_singleton_class_internal_p(VALUE sklass);
 | 
			
		|||
#define RCLASS_REFINED_CLASS(c) (RCLASS_EXT(c)->refined_class)
 | 
			
		||||
#define RCLASS_SERIAL(c) (RCLASS_EXT(c)->class_serial)
 | 
			
		||||
 | 
			
		||||
#define RCLASS_CLONED     FL_USER6
 | 
			
		||||
#define RICLASS_IS_ORIGIN FL_USER5
 | 
			
		||||
 | 
			
		||||
static inline void
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -329,18 +329,22 @@ class TestClass < Test::Unit::TestCase
 | 
			
		|||
    end;
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  module M
 | 
			
		||||
    C = 1
 | 
			
		||||
 | 
			
		||||
    def self.m
 | 
			
		||||
      C
 | 
			
		||||
    end
 | 
			
		||||
  class CloneTest
 | 
			
		||||
    def foo; TEST; end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def test_constant_access_from_method_in_cloned_module # [ruby-core:47834]
 | 
			
		||||
    m = M.dup
 | 
			
		||||
    assert_equal 1, m::C
 | 
			
		||||
    assert_equal 1, m.m
 | 
			
		||||
  CloneTest1 = CloneTest.clone
 | 
			
		||||
  CloneTest2 = CloneTest.clone
 | 
			
		||||
  class CloneTest1
 | 
			
		||||
    TEST = :C1
 | 
			
		||||
  end
 | 
			
		||||
  class CloneTest2
 | 
			
		||||
    TEST = :C2
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def test_constant_access_from_method_in_cloned_class
 | 
			
		||||
    assert_equal :C1, CloneTest1.new.foo, '[Bug #15877]'
 | 
			
		||||
    assert_equal :C2, CloneTest2.new.foo, '[Bug #15877]'
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def test_invalid_superclass
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2418,6 +2418,39 @@ class TestModule < Test::Unit::TestCase
 | 
			
		|||
    }
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  module CloneTestM_simple
 | 
			
		||||
    C = 1
 | 
			
		||||
    def self.m; C; end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  module CloneTestM0
 | 
			
		||||
    def foo; TEST; end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  CloneTestM1 = CloneTestM0.clone
 | 
			
		||||
  CloneTestM2 = CloneTestM0.clone
 | 
			
		||||
  module CloneTestM1
 | 
			
		||||
    TEST = :M1
 | 
			
		||||
  end
 | 
			
		||||
  module CloneTestM2
 | 
			
		||||
    TEST = :M2
 | 
			
		||||
  end
 | 
			
		||||
  class CloneTestC1
 | 
			
		||||
    include CloneTestM1
 | 
			
		||||
  end
 | 
			
		||||
  class CloneTestC2
 | 
			
		||||
    include CloneTestM2
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def test_constant_access_from_method_in_cloned_module
 | 
			
		||||
    m = CloneTestM_simple.dup
 | 
			
		||||
    assert_equal 1, m::C, '[ruby-core:47834]'
 | 
			
		||||
    assert_equal 1, m.m, '[ruby-core:47834]'
 | 
			
		||||
 | 
			
		||||
    assert_equal :M1, CloneTestC1.new.foo, '[Bug #15877]'
 | 
			
		||||
    assert_equal :M2, CloneTestC2.new.foo, '[Bug #15877]'
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def assert_top_method_is_private(method)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -785,8 +785,9 @@ vm_get_const_key_cref(const VALUE *ep)
 | 
			
		|||
    const rb_cref_t *key_cref = cref;
 | 
			
		||||
 | 
			
		||||
    while (cref) {
 | 
			
		||||
	if (FL_TEST(CREF_CLASS(cref), FL_SINGLETON)) {
 | 
			
		||||
	    return key_cref;
 | 
			
		||||
	if (FL_TEST(CREF_CLASS(cref), FL_SINGLETON) ||
 | 
			
		||||
            FL_TEST(CREF_CLASS(cref), RCLASS_CLONED)) {
 | 
			
		||||
            return key_cref;
 | 
			
		||||
	}
 | 
			
		||||
	cref = CREF_NEXT(cref);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -3722,7 +3723,8 @@ static int
 | 
			
		|||
vm_ic_hit_p(IC ic, const VALUE *reg_ep)
 | 
			
		||||
{
 | 
			
		||||
    if (ic->ic_serial == GET_GLOBAL_CONSTANT_STATE()) {
 | 
			
		||||
        return (ic->ic_cref == NULL || ic->ic_cref == vm_get_cref(reg_ep));
 | 
			
		||||
        return (ic->ic_cref == NULL || // no need to check CREF
 | 
			
		||||
                ic->ic_cref == vm_get_cref(reg_ep));
 | 
			
		||||
    }
 | 
			
		||||
    return FALSE;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue