use strict;
use warnings;
-use Scalar::Util 'blessed';
-use Algorithm::C3;
-
-our $VERSION = '0.14';
+our $VERSION = '0.15_04';
+
+our $C3_IN_CORE;
+our $C3_XS;
+
+BEGIN {
+ eval "require mro"; # XXX in the future, this should be a version check
+ if($@) {
+ die $@ if $@ !~ /locate/;
+ eval "require Class::C3::XS";
+ if($@) {
+ die $@ if $@ !~ /locate/;
+ eval "require Algorithm::C3; require Class::C3::next";
+ die $@ if $@;
+ }
+ else {
+ $C3_XS = 1;
+ }
+ }
+ else {
+ $C3_IN_CORE = 1;
+ }
+}
# this is our global stash of both
# MRO's and method dispatch tables
# skip if the caller is main::
# since that is clearly not relevant
return if $class eq 'main';
+
return if $TURN_OFF_C3;
+ mro::set_mro($class, 'c3') if $C3_IN_CORE;
+
# make a note to calculate $class
# during INIT phase
$MRO{$class} = undef unless exists $MRO{$class};
## initializers
sub initialize {
+ %next::METHOD_CACHE = ();
# why bother if we don't have anything ...
return unless keys %MRO;
- if($_initialized) {
- uninitialize();
- $MRO{$_} = undef foreach keys %MRO;
+ if($C3_IN_CORE) {
+ mro::set_mro($_, 'c3') for keys %MRO;
+ }
+ else {
+ if($_initialized) {
+ uninitialize();
+ $MRO{$_} = undef foreach keys %MRO;
+ }
+ _calculate_method_dispatch_tables();
+ _apply_method_dispatch_tables();
+ $_initialized = 1;
}
- _calculate_method_dispatch_tables();
- _apply_method_dispatch_tables();
- %next::METHOD_CACHE = ();
- $_initialized = 1;
}
sub uninitialize {
# why bother if we don't have anything ...
- return unless keys %MRO;
- _remove_method_dispatch_tables();
%next::METHOD_CACHE = ();
- $_initialized = 0;
+ return unless keys %MRO;
+ if($C3_IN_CORE) {
+ mro::set_mro($_, 'dfs') for keys %MRO;
+ }
+ else {
+ _remove_method_dispatch_tables();
+ $_initialized = 0;
+ }
}
sub reinitialize { goto &initialize }
## functions for applying C3 to classes
sub _calculate_method_dispatch_tables {
+ return if $C3_IN_CORE;
my %merge_cache;
foreach my $class (keys %MRO) {
_calculate_method_dispatch_table($class, \%merge_cache);
}
sub _calculate_method_dispatch_table {
+ return if $C3_IN_CORE;
my ($class, $merge_cache) = @_;
no strict 'refs';
my @MRO = calculateMRO($class, $merge_cache);
}
sub _apply_method_dispatch_tables {
+ return if $C3_IN_CORE;
foreach my $class (keys %MRO) {
_apply_method_dispatch_table($class);
}
}
sub _apply_method_dispatch_table {
+ return if $C3_IN_CORE;
my $class = shift;
no strict 'refs';
${"${class}::()"} = $MRO{$class}->{has_overload_fallback}
if $MRO{$class}->{has_overload_fallback};
foreach my $method (keys %{$MRO{$class}->{methods}}) {
+ if ( $method =~ /^\(/ ) {
+ my $orig = $MRO{$class}->{methods}->{$method}->{orig};
+ ${"${class}::$method"} = $$orig if defined $$orig;
+ }
*{"${class}::$method"} = $MRO{$class}->{methods}->{$method}->{code};
}
}
sub _remove_method_dispatch_tables {
+ return if $C3_IN_CORE;
foreach my $class (keys %MRO) {
_remove_method_dispatch_table($class);
}
}
sub _remove_method_dispatch_table {
+ return if $C3_IN_CORE;
my $class = shift;
no strict 'refs';
delete ${"${class}::"}{"()"} if $MRO{$class}->{has_overload_fallback};
}
}
-## functions for calculating C3 MRO
-
sub calculateMRO {
my ($class, $merge_cache) = @_;
+
return Algorithm::C3::merge($class, sub {
no strict 'refs';
@{$_[0] . '::ISA'};
}, $merge_cache);
}
-package # hide me from PAUSE
- next;
+sub _core_calculateMRO { @{mro::get_linear_isa($_[0])} }
-use strict;
-use warnings;
-
-use Scalar::Util 'blessed';
-
-our $VERSION = '0.05';
-
-our %METHOD_CACHE;
-
-sub method {
- my $indirect = caller() =~ /^(?:next|maybe::next)$/;
- my $level = $indirect ? 2 : 1;
-
- my ($method_caller, $label, @label);
- while ($method_caller = (caller($level++))[3]) {
- @label = (split '::', $method_caller);
- $label = pop @label;
- last unless
- $label eq '(eval)' ||
- $label eq '__ANON__';
- }
- my $caller = join '::' => @label;
- my $self = $_[0];
- my $class = blessed($self) || $self;
-
- my $method = $METHOD_CACHE{"$class|$caller|$label"} ||= do {
-
- my @MRO = Class::C3::calculateMRO($class);
-
- my $current;
- while ($current = shift @MRO) {
- last if $caller eq $current;
- }
-
- no strict 'refs';
- my $found;
- foreach my $class (@MRO) {
- next if (defined $Class::C3::MRO{$class} &&
- defined $Class::C3::MRO{$class}{methods}{$label});
- last if (defined ($found = *{$class . '::' . $label}{CODE}));
- }
-
- $found;
- };
-
- return $method if $indirect;
-
- die "No next::method '$label' found for $self" if !$method;
-
- goto &{$method};
+if($C3_IN_CORE) {
+ no warnings 'redefine';
+ *Class::C3::calculateMRO = \&_core_calculateMRO;
+}
+elsif($C3_XS) {
+ no warnings 'redefine';
+ *Class::C3::calculateMRO = \&Class::C3::XS::calculateMRO;
}
-
-sub can { method($_[0]) }
-
-package # hide me from PAUSE
- maybe::next;
-
-use strict;
-use warnings;
-
-our $VERSION = '0.01';
-
-sub method { (next::method($_[0]) || return)->(@_) }
1;
D->can('hello')->(); # can() also works correctly
UNIVERSAL::can('D', 'hello'); # as does UNIVERSAL::can()
+=head1 SPECIAL NOTE FOR 0.15_04
+
+To try this with the experimental perl core c3 patch,
+download the most recent copy perl-current:
+
+http://mirrors.develooper.com/perl/APC/perl-current-snap/
+
+Apply the latest C3 patch from:
+
+http://www.dtmf.com/c3-subgen.patch
+
+Then:
+
+sh Configure -Dusedevel -Dprefix=/where/I/want/it -d -e && make && make test && make install
+
+then try your C3-using software against this perl + Class::C3 0.15_04.
+
=head1 DESCRIPTION
This is pragma to change Perl 5's standard method resolution order from depth-first left-to-right
=back
+=head1 COMPATIBILITY
+
+If your software requires Perl 5.9.5 or higher, you do not need L<Class::C3>, you can simple C<use mro 'c3'>, and not worry about C<initialize()>, avoid some of the above caveats, and get the best possible performance. See L<mro> for more details.
+
+If your software is meant to work on earlier Perls, use L<Class::C3> as documented here. L<Class::C3> will detect Perl 5.9.5+ and take advantage of the core support when available.
+
+=head1 Class::C3::XS
+
+This module will load L<Class::C3::XS> if it's installed and you are running on a Perl version older than 5.9.5. Installing this is recommended when possible, as it results in significant performance improvements (but unlike the 5.9.5+ core support, it still has all of the same caveats as L<Class::C3>).
+
=head1 CODE COVERAGE
I use B<Devel::Cover> to test the code coverage of my tests, below is the B<Devel::Cover> report on this