StackOverflow on using method_added in a mixin #4839
In https://github.com/dry-rb/dry-validation/ we have some dynamic behavior involving using the
class A end class B < A end module C def method_added(name) super end end B.extend(C) A.extend(C) # ^Note the order in which classes are extended class B def foo end end
MRI successfully executes this piece of code whereas jruby fails with the following error:
The text was updated successfully, but these errors were encountered:
Ok, so I looked into this a bit.
The problem here is how we resolve the
This is a bit of an unusual situation because normally you would not have two of the same module in a class hierarchy; includes against a hierarchy that already has a module will not re-add that module. The order here forces the problem.
Our class hierarchies match MRI, with the "A" hierarchy having a single "C" module and the "B" hierarchy having two "C" modules. And in MRI, the custom method_added does indeed get called twice.
In JRuby, however, the super here is compiled as "unresolved" which means we inspect the class hierarchy to figure out where we're "supering" from. The logic for this currently searches up the hierarchy for the method's "implementation class" (which would be the "C" module here), and unsurprisingly that always resets the
Trouble is, I'm not sure the best way to fix it. In MRI, they resolve this by having method caches also keep the pseudo-location within the hierarchy, so the super always knows where to start. We do not have that information in our caches, so of course none of our call site logic is set up to use it.
Since you say the error is minor, I'm inclined to bump this to a major release rather than start mucking about with method caches on our maintenance line. But it should be fixed.
Workaround for anyone following along: make sure you're never including (or extending) a module into a descendant of a hierarchy before you include or extend it in a an ancestor.
@aamarill Yes, it is! Since we have a fairly simple reproduction, I'd recommend looking at the overflowed stack trace and starting from there. The problem is likely that super is finding the wrong location for the next level of class, and so it just keeps going back to the bottom of the hierarchy.