Fixing Kernel#singleton_method bug in Ruby.

Few days ago when I was browsing through StackOverflow I came across this question about weird behaviour of Kernel#singleton_method such as if you call Kernel#singleton_methods method on ActiveSupport::Deprecation class you would get you list of singleton methods (no surprise here) but when you try to get that singleton method with Kernel#singleton_method Ruby would throw NameError error:

At first, I thought it’s ActiveSupport doing something fishy. And while I was looking through AS codebase this answer appeared. @michaelj discovered that it's possible to reproduce this bug without ActiveSupport at all. All you needed to do is to prepend any module to a singleton class:

So it was something wrong with Module#prepend.

I started looking for some bugs with Module#prepend on Ruby's bugtracker. All I could find related was this bug with Object#methods and Module#prepend. So what I needed to do is to check how they fixed it and do something similar with Kernel#singleton_method.

DISCLAIMER: I’m not that good with Ruby internals so next part of the post might have some incorrect statements.

Main thing I learned from Object#methods fix was RCLASS_ORIGIN macro. This macro is used to get origin class of the passed class/module. And as I discovered Module#prepend makes a copy of a target class internally so if you need to access original one you can use that macro. Honestly, I don't really understand why you can't access singleton method but I got the idea.

That’s how rb_obj_singleton_method looked before the fix:

as you can see the klass was retrieved by rb_singleton_class_get function which returns class's copy if it was already prepended with some module. That means all I had to do is to apply RCLASS_ORIGIN on that class. And it did the trick. You can find the whole patch in this issue.

And may Ruby be with you.