diff --git a/method.h b/method.h index 6b60a49a3aac4d..fbbcad075fe4d9 100644 --- a/method.h +++ b/method.h @@ -101,8 +101,9 @@ static inline void METHOD_ENTRY_FLAGS_COPY(rb_method_entry_t *dst, const rb_method_entry_t *src) { dst->flags = - (dst->flags & ~(IMEMO_FL_USER0|IMEMO_FL_USER1|IMEMO_FL_USER2)) | - (src->flags & (IMEMO_FL_USER0|IMEMO_FL_USER1|IMEMO_FL_USER2)); + (dst->flags & ~(IMEMO_FL_USER0|IMEMO_FL_USER1|IMEMO_FL_USER2 + |IMEMO_FL_USER3)) | + (src->flags & (IMEMO_FL_USER0|IMEMO_FL_USER1|IMEMO_FL_USER2|IMEMO_FL_USER3)); } typedef enum { diff --git a/test/ruby/test_module.rb b/test/ruby/test_module.rb index 81c5345a5f541e..cc22e2a77c3f85 100644 --- a/test/ruby/test_module.rb +++ b/test/ruby/test_module.rb @@ -3292,6 +3292,35 @@ def test_iclass_memory_leak CODE end + def test_complemented_method_entry_memory_leak + # [Bug #19894] + assert_no_memory_leak([], <<~PREP, <<~CODE, rss: true) + code = proc do + $c = Class.new do + def foo; end + end + + $m = Module.new do + refine $c do + def foo; end + end + end + + Class.new do + using $m + + def initialize + o = $c.new + o.method(:foo).unbind + end + end.new + end + 1_000.times(&code) + PREP + 100_000.times(&code) + CODE + end + private def assert_top_method_is_private(method) diff --git a/vm_method.c b/vm_method.c index cd5543f0424b3a..cf4cfd2e566042 100644 --- a/vm_method.c +++ b/vm_method.c @@ -693,11 +693,13 @@ rb_method_entry_create(ID called_id, VALUE klass, rb_method_visibility_t visi, c const rb_method_entry_t * rb_method_entry_clone(const rb_method_entry_t *src_me) { - rb_method_entry_t *me = rb_method_entry_alloc(src_me->called_id, src_me->owner, src_me->defined_class, - method_definition_addref(src_me->def)); + rb_method_entry_t *me = rb_method_entry_alloc(src_me->called_id, src_me->owner, src_me->defined_class, src_me->def); if (METHOD_ENTRY_COMPLEMENTED(src_me)) { method_definition_addref_complement(src_me->def); } + else { + method_definition_addref(src_me->def); + } METHOD_ENTRY_FLAGS_COPY(me, src_me); return me; @@ -724,7 +726,7 @@ rb_method_entry_complement_defined_class(const rb_method_entry_t *src_me, ID cal def = NULL; } else { - def = method_definition_addref_complement(def); + method_definition_addref_complement(def); } me = rb_method_entry_alloc(called_id, src_me->owner, defined_class, def); METHOD_ENTRY_FLAGS_COPY(me, src_me);