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

ruby-changes:63664

From: Jeremy <ko1@a...>
Date: Sat, 21 Nov 2020 08:27:11 +0900 (JST)
Subject: [ruby-changes:63664] 08686e71d5 (master): Do not allow Module#include to insert modules before the origin in the lookup chain

https://git.ruby-lang.org/ruby.git/commit/?id=08686e71d5

From 08686e71d5c48325ee1bd41c8d7ebcd6c37fa496 Mon Sep 17 00:00:00 2001
From: Jeremy Evans <code@j...>
Date: Thu, 8 Aug 2019 09:55:33 -0700
Subject: Do not allow Module#include to insert modules before the origin in
 the lookup chain

Module#include should only be able to insert modules after the origin,
otherwise it ends up working like Module#prepend.

This fixes the case where one of the modules in the included module
chain is included in a module that is already prepended to the receiver.

Fixes [Bug #7844]

diff --git a/class.c b/class.c
index 709d49c..1f56385 100644
--- a/class.c
+++ b/class.c
@@ -1013,17 +1013,22 @@ include_modules_at(const VALUE klass, VALUE c, VALUE module, int search_super) https://github.com/ruby/ruby/blob/trunk/class.c#L1013
     VALUE original_klass = klass;
 
     while (module) {
+        int origin_seen = FALSE;
 	int superclass_seen = FALSE;
 	struct rb_id_table *tbl;
 
+        if (klass == c)
+            origin_seen = TRUE;
 	if (klass_m_tbl && klass_m_tbl == RCLASS_M_TBL(module))
 	    return -1;
 	/* ignore if the module included already in superclasses */
         for (p = RCLASS_SUPER(klass); p; p = RCLASS_SUPER(p)) {
 	    int type = BUILTIN_TYPE(p);
+            if (c == p)
+                origin_seen = TRUE;
 	    if (type == T_ICLASS) {
 		if (RCLASS_M_TBL(p) == RCLASS_M_TBL(module)) {
-		    if (!superclass_seen) {
+                    if (!superclass_seen && origin_seen) {
 			c = p;  /* move insertion point */
 		    }
 		    goto skip;
diff --git a/test/ruby/test_module.rb b/test/ruby/test_module.rb
index 6c0fe6b..d63f31a 100644
--- a/test/ruby/test_module.rb
+++ b/test/ruby/test_module.rb
@@ -631,6 +631,16 @@ class TestModule < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_module.rb#L631
     assert_equal([m1, m2], m3.included_modules)
   end
 
+  def test_include_with_prepend
+    c = Class.new{def m; [:c] end}
+    p = Module.new{def m; [:p] + super end}
+    q = Module.new{def m; [:q] + super end; include p}
+    r = Module.new{def m; [:r] + super end; prepend q}
+    s = Module.new{def m; [:s] + super end; include r}
+    a = Class.new(c){def m; [:a] + super end; prepend p; include s}
+    assert_equal([:p, :a, :s, :q, :r, :c], a.new.m)
+  end
+
   def test_instance_methods
     assert_equal([:user, :user2], User.instance_methods(false).sort)
     assert_equal([:user, :user2, :mixin].sort, User.instance_methods(true).sort)
-- 
cgit v0.10.2


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

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