354 lines
9.9 KiB
Perl
354 lines
9.9 KiB
Perl
# mro.pm
|
|
#
|
|
# Copyright (c) 2007 Brandon L Black
|
|
# Copyright (c) 2008,2009 Larry Wall and others
|
|
#
|
|
# You may distribute under the terms of either the GNU General Public
|
|
# License or the Artistic License, as specified in the README file.
|
|
#
|
|
package mro;
|
|
use strict;
|
|
use warnings;
|
|
|
|
# mro.pm versions < 1.00 reserved for MRO::Compat
|
|
# for partial back-compat to 5.[68].x
|
|
our $VERSION = '1.22';
|
|
|
|
require XSLoader;
|
|
XSLoader::load('mro');
|
|
|
|
sub import {
|
|
mro::set_mro(scalar(caller), $_[1]) if $_[1];
|
|
}
|
|
|
|
package # hide me from PAUSE
|
|
next;
|
|
|
|
sub can { mro::_nextcan($_[0], 0) }
|
|
|
|
sub method {
|
|
my $method = mro::_nextcan($_[0], 1);
|
|
goto &$method;
|
|
}
|
|
|
|
package # hide me from PAUSE
|
|
maybe::next;
|
|
|
|
sub method {
|
|
my $method = mro::_nextcan($_[0], 0);
|
|
goto &$method if defined $method;
|
|
return;
|
|
}
|
|
|
|
1;
|
|
|
|
__END__
|
|
|
|
=head1 NAME
|
|
|
|
mro - Method Resolution Order
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
use mro; # enables next::method and friends globally
|
|
|
|
use mro 'dfs'; # enable DFS MRO for this class (Perl default)
|
|
use mro 'c3'; # enable C3 MRO for this class
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
The "mro" namespace provides several utilities for dealing
|
|
with method resolution order and method caching in general.
|
|
|
|
These interfaces are only available in Perl 5.9.5 and higher.
|
|
See L<MRO::Compat> on CPAN for a mostly forwards compatible
|
|
implementation for older Perls.
|
|
|
|
=head1 OVERVIEW
|
|
|
|
It's possible to change the MRO of a given class either by using C<use
|
|
mro> as shown in the synopsis, or by using the L</mro::set_mro> function
|
|
below.
|
|
|
|
The special methods C<next::method>, C<next::can>, and
|
|
C<maybe::next::method> are not available until this C<mro> module
|
|
has been loaded via C<use> or C<require>.
|
|
|
|
=head1 The C3 MRO
|
|
|
|
In addition to the traditional Perl default MRO (depth first
|
|
search, called C<DFS> here), Perl now offers the C3 MRO as
|
|
well. Perl's support for C3 is based on the work done in
|
|
Stevan Little's module L<Class::C3>, and most of the C3-related
|
|
documentation here is ripped directly from there.
|
|
|
|
=head2 What is C3?
|
|
|
|
C3 is the name of an algorithm which aims to provide a sane method
|
|
resolution order under multiple inheritance. It was first introduced in
|
|
the language Dylan (see links in the L</"SEE ALSO"> section), and then
|
|
later adopted as the preferred MRO (Method Resolution Order) for the
|
|
new-style classes in Python 2.3. Most recently it has been adopted as the
|
|
"canonical" MRO for Perl 6 classes, and the default MRO for Parrot objects
|
|
as well.
|
|
|
|
=head2 How does C3 work
|
|
|
|
C3 works by always preserving local precedence ordering. This essentially
|
|
means that no class will appear before any of its subclasses. Take, for
|
|
instance, the classic diamond inheritance pattern:
|
|
|
|
<A>
|
|
/ \
|
|
<B> <C>
|
|
\ /
|
|
<D>
|
|
|
|
The standard Perl 5 MRO would be (D, B, A, C). The result being that B<A>
|
|
appears before B<C>, even though B<C> is the subclass of B<A>. The C3 MRO
|
|
algorithm however, produces the following order: (D, B, C, A), which does
|
|
not have this issue.
|
|
|
|
This example is fairly trivial; for more complex cases and a deeper
|
|
explanation, see the links in the L</"SEE ALSO"> section.
|
|
|
|
=head1 Functions
|
|
|
|
=head2 mro::get_linear_isa($classname[, $type])
|
|
|
|
Returns an arrayref which is the linearized MRO of the given class.
|
|
Uses whichever MRO is currently in effect for that class by default,
|
|
or the given MRO (either C<c3> or C<dfs> if specified as C<$type>).
|
|
|
|
The linearized MRO of a class is an ordered array of all of the
|
|
classes one would search when resolving a method on that class,
|
|
starting with the class itself.
|
|
|
|
If the requested class doesn't yet exist, this function will still
|
|
succeed, and return C<[ $classname ]>
|
|
|
|
Note that C<UNIVERSAL> (and any members of C<UNIVERSAL>'s MRO) are not
|
|
part of the MRO of a class, even though all classes implicitly inherit
|
|
methods from C<UNIVERSAL> and its parents.
|
|
|
|
=head2 mro::set_mro ($classname, $type)
|
|
|
|
Sets the MRO of the given class to the C<$type> argument (either
|
|
C<c3> or C<dfs>).
|
|
|
|
=head2 mro::get_mro($classname)
|
|
|
|
Returns the MRO of the given class (either C<c3> or C<dfs>).
|
|
|
|
=head2 mro::get_isarev($classname)
|
|
|
|
Gets the C<mro_isarev> for this class, returned as an
|
|
arrayref of class names. These are every class that "isa"
|
|
the given class name, even if the isa relationship is
|
|
indirect. This is used internally by the MRO code to
|
|
keep track of method/MRO cache invalidations.
|
|
|
|
As with C<mro::get_linear_isa> above, C<UNIVERSAL> is special.
|
|
C<UNIVERSAL> (and parents') isarev lists do not include
|
|
every class in existence, even though all classes are
|
|
effectively descendants for method inheritance purposes.
|
|
|
|
=head2 mro::is_universal($classname)
|
|
|
|
Returns a boolean status indicating whether or not
|
|
the given classname is either C<UNIVERSAL> itself,
|
|
or one of C<UNIVERSAL>'s parents by C<@ISA> inheritance.
|
|
|
|
Any class for which this function returns true is
|
|
"universal" in the sense that all classes potentially
|
|
inherit methods from it.
|
|
|
|
=head2 mro::invalidate_all_method_caches()
|
|
|
|
Increments C<PL_sub_generation>, which invalidates method
|
|
caching in all packages.
|
|
|
|
=head2 mro::method_changed_in($classname)
|
|
|
|
Invalidates the method cache of any classes dependent on the
|
|
given class. This is not normally necessary. The only
|
|
known case where pure perl code can confuse the method
|
|
cache is when you manually install a new constant
|
|
subroutine by using a readonly scalar value, like the
|
|
internals of L<constant> do. If you find another case,
|
|
please report it so we can either fix it or document
|
|
the exception here.
|
|
|
|
=head2 mro::get_pkg_gen($classname)
|
|
|
|
Returns an integer which is incremented every time a
|
|
real local method in the package C<$classname> changes,
|
|
or the local C<@ISA> of C<$classname> is modified.
|
|
|
|
This is intended for authors of modules which do lots
|
|
of class introspection, as it allows them to very quickly
|
|
check if anything important about the local properties
|
|
of a given class have changed since the last time they
|
|
looked. It does not increment on method/C<@ISA>
|
|
changes in superclasses.
|
|
|
|
It's still up to you to seek out the actual changes,
|
|
and there might not actually be any. Perhaps all
|
|
of the changes since you last checked cancelled each
|
|
other out and left the package in the state it was in
|
|
before.
|
|
|
|
This integer normally starts off at a value of C<1>
|
|
when a package stash is instantiated. Calling it
|
|
on packages whose stashes do not exist at all will
|
|
return C<0>. If a package stash is completely
|
|
deleted (not a normal occurrence, but it can happen
|
|
if someone does something like C<undef %PkgName::>),
|
|
the number will be reset to either C<0> or C<1>,
|
|
depending on how completely the package was wiped out.
|
|
|
|
=head2 next::method
|
|
|
|
This is somewhat like C<SUPER>, but it uses the C3 method
|
|
resolution order to get better consistency in multiple
|
|
inheritance situations. Note that while inheritance in
|
|
general follows whichever MRO is in effect for the
|
|
given class, C<next::method> only uses the C3 MRO.
|
|
|
|
One generally uses it like so:
|
|
|
|
sub some_method {
|
|
my $self = shift;
|
|
my $superclass_answer = $self->next::method(@_);
|
|
return $superclass_answer + 1;
|
|
}
|
|
|
|
Note that you don't (re-)specify the method name.
|
|
It forces you to always use the same method name
|
|
as the method you started in.
|
|
|
|
It can be called on an object or a class, of course.
|
|
|
|
The way it resolves which actual method to call is:
|
|
|
|
=over 4
|
|
|
|
=item 1
|
|
|
|
First, it determines the linearized C3 MRO of
|
|
the object or class it is being called on.
|
|
|
|
=item 2
|
|
|
|
Then, it determines the class and method name
|
|
of the context it was invoked from.
|
|
|
|
=item 3
|
|
|
|
Finally, it searches down the C3 MRO list until
|
|
it reaches the contextually enclosing class, then
|
|
searches further down the MRO list for the next
|
|
method with the same name as the contextually
|
|
enclosing method.
|
|
|
|
=back
|
|
|
|
Failure to find a next method will result in an
|
|
exception being thrown (see below for alternatives).
|
|
|
|
This is substantially different than the behavior
|
|
of C<SUPER> under complex multiple inheritance.
|
|
(This becomes obvious when one realizes that the
|
|
common superclasses in the C3 linearizations of
|
|
a given class and one of its parents will not
|
|
always be ordered the same for both.)
|
|
|
|
B<Caveat>: Calling C<next::method> from methods defined outside the class:
|
|
|
|
There is an edge case when using C<next::method> from within a subroutine
|
|
which was created in a different module than the one it is called from. It
|
|
sounds complicated, but it really isn't. Here is an example which will not
|
|
work correctly:
|
|
|
|
*Foo::foo = sub { (shift)->next::method(@_) };
|
|
|
|
The problem exists because the anonymous subroutine being assigned to the
|
|
C<*Foo::foo> glob will show up in the call stack as being called
|
|
C<__ANON__> and not C<foo> as you might expect. Since C<next::method> uses
|
|
C<caller> to find the name of the method it was called in, it will fail in
|
|
this case.
|
|
|
|
But fear not, there's a simple solution. The module C<Sub::Name> will
|
|
reach into the perl internals and assign a name to an anonymous subroutine
|
|
for you. Simply do this:
|
|
|
|
use Sub::Name 'subname';
|
|
*Foo::foo = subname 'Foo::foo' => sub { (shift)->next::method(@_) };
|
|
|
|
and things will Just Work.
|
|
|
|
=head2 next::can
|
|
|
|
This is similar to C<next::method>, but just returns either a code
|
|
reference or C<undef> to indicate that no further methods of this name
|
|
exist.
|
|
|
|
=head2 maybe::next::method
|
|
|
|
In simple cases, it is equivalent to:
|
|
|
|
$self->next::method(@_) if $self->next::can;
|
|
|
|
But there are some cases where only this solution
|
|
works (like C<goto &maybe::next::method>);
|
|
|
|
=head1 SEE ALSO
|
|
|
|
=head2 The original Dylan paper
|
|
|
|
=over 4
|
|
|
|
=item L<http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.19.3910&rep=rep1&type=pdf>
|
|
|
|
=back
|
|
|
|
=head2 Pugs
|
|
|
|
The Pugs prototype Perl 6 Object Model uses C3
|
|
|
|
=head2 Parrot
|
|
|
|
Parrot now uses C3
|
|
|
|
=over 4
|
|
|
|
=item L<http://use.perl.org/~autrijus/journal/25768>
|
|
|
|
=back
|
|
|
|
=head2 Python 2.3 MRO related links
|
|
|
|
=over 4
|
|
|
|
=item L<http://www.python.org/2.3/mro.html>
|
|
|
|
=item L<http://www.python.org/2.2.2/descrintro.html#mro>
|
|
|
|
=back
|
|
|
|
=head2 Class::C3
|
|
|
|
=over 4
|
|
|
|
=item L<Class::C3>
|
|
|
|
=back
|
|
|
|
=head1 AUTHOR
|
|
|
|
Brandon L. Black, E<lt>blblack@gmail.comE<gt>
|
|
|
|
Based on Stevan Little's L<Class::C3>
|
|
|
|
=cut
|