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)
============================================================*/

Reply via email to