1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

Make a cyclic prepend not modify ancestors for the receiver

Check for cyclic prepend before making any changes.  This requires
scanning the module ancestor chain twice, but in general modules
do not have large numbers of ancestors.
This commit is contained in:
Jeremy Evans 2021-02-09 15:39:34 -08:00
parent b83ad02997
commit 2d877327e1
Notes: git 2021-02-12 00:46:21 +09:00
2 changed files with 19 additions and 2 deletions

14
class.c
View file

@ -1024,6 +1024,18 @@ include_modules_at(const VALUE klass, VALUE c, VALUE module, int search_super)
struct rb_id_table *const klass_m_tbl = RCLASS_M_TBL(klass_origin);
VALUE original_klass = klass;
if (klass_m_tbl) {
VALUE original_module = module;
while (module) {
if (klass_m_tbl == RCLASS_M_TBL(module))
return -1;
module = RCLASS_SUPER(module);
}
module = original_module;
}
while (module) {
int c_seen = FALSE;
int superclass_seen = FALSE;
@ -1032,8 +1044,6 @@ include_modules_at(const VALUE klass, VALUE c, VALUE module, int search_super)
if (klass == c) {
c_seen = TRUE;
}
if (klass_m_tbl && klass_m_tbl == RCLASS_M_TBL(module))
return -1;
if (klass_origin != c || search_super) {
/* ignore if the module included already in superclasses for include,
* ignore if the module included before origin class for prepend

View file

@ -478,6 +478,13 @@ class TestModule < Test::Unit::TestCase
assert_raise(ArgumentError) { Module.new { include } }
end
def test_prepend_self
m = Module.new
assert_equal([m], m.ancestors)
m.prepend(m) rescue nil
assert_equal([m], m.ancestors)
end
def test_prepend_works_with_duped_classes
m = Module.new
a = Class.new do