ruby-changes:60217
From: Jeremy <ko1@a...>
Date: Fri, 28 Feb 2020 04:03:29 +0900 (JST)
Subject: [ruby-changes:60217] 3556a834a2 (master): Make Module#include affect the iclasses of the module
https://git.ruby-lang.org/ruby.git/commit/?id=3556a834a2 From 3556a834a2847e52162d1d3302d4c64390df1694 Mon Sep 17 00:00:00 2001 From: Jeremy Evans <code@j...> Date: Mon, 6 Jan 2020 16:41:03 -0800 Subject: Make Module#include affect the iclasses of the module When calling Module#include, if the receiver is a module, walk the subclasses list and include the argument module in each iclass. This does not affect Module#prepend, as fixing that is significantly more involved. Fixes [Bug #9573] diff --git a/class.c b/class.c index 98b2a1d..ae1a9c0 100644 --- a/class.c +++ b/class.c @@ -883,6 +883,26 @@ rb_include_module(VALUE klass, VALUE module) https://github.com/ruby/ruby/blob/trunk/class.c#L883 changed = include_modules_at(klass, RCLASS_ORIGIN(klass), module, TRUE); if (changed < 0) rb_raise(rb_eArgError, "cyclic include detected"); + + if (RB_TYPE_P(klass, T_MODULE)) { + rb_subclass_entry_t *iclass = RCLASS_EXT(klass)->subclasses; + int do_include = 1; + while (iclass) { + VALUE check_class = iclass->klass; + while (check_class) { + if (RB_TYPE_P(check_class, T_ICLASS) && + (RBASIC(check_class)->klass == module)) { + do_include = 0; + } + check_class = RCLASS_SUPER(check_class); + } + + if (do_include) { + include_modules_at(iclass->klass, RCLASS_ORIGIN(iclass->klass), module, TRUE); + } + iclass = iclass->next; + } + } } static enum rb_id_table_iterator_result diff --git a/test/ruby/test_module.rb b/test/ruby/test_module.rb index 5194a56..cafb0cd 100644 --- a/test/ruby/test_module.rb +++ b/test/ruby/test_module.rb @@ -473,6 +473,57 @@ class TestModule < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_module.rb#L473 assert_raise(ArgumentError) { Module.new { include } } end + def test_include_into_module_already_included + c = Class.new{def foo; [:c] end} + modules = lambda do || + sub = Class.new(c){def foo; [:sc] + super end} + [ + Module.new{def foo; [:m1] + super end}, + Module.new{def foo; [:m2] + super end}, + Module.new{def foo; [:m3] + super end}, + sub, + sub.new + ] + end + + m1, m2, m3, sc, o = modules.call + assert_equal([:sc, :c], o.foo) + sc.include m1 + assert_equal([:sc, :m1, :c], o.foo) + m1.include m2 + assert_equal([:sc, :m1, :m2, :c], o.foo) + m2.include m3 + assert_equal([:sc, :m1, :m2, :m3, :c], o.foo) + + m1, m2, m3, sc, o = modules.call + sc.prepend m1 + assert_equal([:m1, :sc, :c], o.foo) + m1.include m2 + assert_equal([:m1, :m2, :sc, :c], o.foo) + m2.include m3 + assert_equal([:m1, :m2, :m3, :sc, :c], o.foo) + + m1, m2, m3, sc, o = modules.call + sc.include m2 + assert_equal([:sc, :m2, :c], o.foo) + sc.prepend m1 + assert_equal([:m1, :sc, :m2, :c], o.foo) + m1.include m2 + assert_equal([:m1, :sc, :m2, :c], o.foo) + m1.include m3 + assert_equal([:m1, :m3, :sc, :m2, :c], o.foo) + + m1, m2, m3, sc, o = modules.call + sc.include m3 + sc.include m2 + assert_equal([:sc, :m2, :m3, :c], o.foo) + sc.prepend m1 + assert_equal([:m1, :sc, :m2, :m3, :c], o.foo) + m1.include m2 + m1.include m3 + assert_equal([:m1, :sc, :m2, :m3, :c], o.foo) + end + def test_included_modules assert_equal([], Mixin.included_modules) assert_equal([Mixin], User.included_modules) -- cgit v0.10.2 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/