This is a bit of a rathole, but here's the summary.
Recently CMOP introduced a bug where adding the same non-closure coderef
to >1 package did not quite work right. When asked for the method by name,
CMOP::Package does not return _anything_ for any package but the first.
Here's an example:
foreach my $ns ( qw( Foo2 Bar2 Baz2 ) ) {
my $meta = Class::MOP::Class->create($ns);
my $sub = sub { };
$meta->add_method( 'foo', $sub );
my $method = $meta->get_method('foo');
ok( $method, 'Got the foo method back' );
}
We'd expect this to work for each of the three namespaces. With the bug,
this only works for "Foo2".
Now the rathole ...
This happened because when CMOP::Package->add_method was called, it did
not create a CMOP::Method object when given a raw coderef. This was
delayed until the generation of the full method map instead. This is one
of Goro Fuji's recent optimizations, and my understanding is that this is
a pretty big win.
However, his code simply didn't store the method in the method map at all,
relying on being able to find it later by looking at the symbol table.
Because of the way Perl caches non-closure coderefs, CMOP::Package gets
confused.
In order to preserve this optimization, I've changed the code to add the
_raw_ coderef to the method map directly.
This had a few follow-on changes. In order to make sure we always return
CMOP::Method object, get_method now checks for the raw coderef case
explicitly, and if it finds one, replaces it with a CMOP::Method object.
It was already doing something like this anyway, since it sometimes had to
find things in the symbol table.
However, this also means that methods that return a list of methods (like
get_method_list) need to call get_method for every method they return.
This presumably will make it slower. Of course, with immutability, this is
a non-issue, as we're effectively paying the same costs as we did before
to generate the list for caching.
Because the method map now contains a mix of objects and raw coderefs,
I've made it private. Exposing this was always a code smell, since the
method map is an implementation detail. A quick scan of all the MX modules
I have checked out does not find any of them calling ->get_method_map
directly (yay).
All of this of course impacts Moose as well.
I took this as an opportunity to do some refactoring on Moose::Role. Since
it now inherits all the method stuff from CMOP::Package, it no longer
needs its own copy-pasted implementation of these methods.
However, it also had a hack to avoid including "meta" in a role's list of
methods. This was done so that role composition didn't lead to the
composing class/role acquiring the composed role's meta method.
All I was able to do here was _move_ the hack to a couple different
places. First, when delegating by a role name, I explicitly exclude a
'meta' method. Second, RoleSummation explicitly adds 'meta' to its list of
excluded methods. Finally, ToRole application simply ignores 'meta' when
looking at the consumed role's list of methods.
I'm not terribly happy about spreading the hack out like this, but I just
cannot figure out a way to encapsulate this in some better way.
Suggestions are welcome.
All tests pass in both branches, though obviously the Moose branch needs
the CMOP branch.
I'd appreciate some review on this, particularly the Moose hack situation
noted near the end.
-dave
/*============================================================
http://VegGuide.org http://blog.urth.org
Your guide to all that's veg House Absolute(ly Pointless)
============================================================*/