X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FClass%2FC3.pm;h=793c1824f71aa712bafe302b8fb0295941351d4c;hb=ff5d5837313d7af9df1f3892e1340fe094beced5;hp=c5a720cbcc8cfb5283a52054908dec84171f127f;hpb=ff168601b6fb63af7716f6da5c21e34053660f8a;p=gitmo%2FClass-C3.git diff --git a/lib/Class/C3.pm b/lib/Class/C3.pm index c5a720c..793c182 100644 --- a/lib/Class/C3.pm +++ b/lib/Class/C3.pm @@ -4,10 +4,29 @@ package Class::C3; 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 @@ -28,6 +47,8 @@ our %MRO; # use these for debugging ... sub _dump_MRO_table { %MRO } our $TURN_OFF_C3 = 0; + +# state tracking for initialize()/uninitialize() our $_initialized = 0; sub import { @@ -35,7 +56,10 @@ sub import { # 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}; @@ -44,24 +68,34 @@ sub import { ## 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 } @@ -69,6 +103,7 @@ 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); @@ -76,6 +111,7 @@ sub _calculate_method_dispatch_tables { } sub _calculate_method_dispatch_table { + return if $C3_IN_CORE; my ($class, $merge_cache) = @_; no strict 'refs'; my @MRO = calculateMRO($class, $merge_cache); @@ -107,28 +143,36 @@ sub _calculate_method_dispatch_table { } 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}; @@ -139,82 +183,25 @@ sub _remove_method_dispatch_table { } } -## 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; - -use strict; -use warnings; - -use Scalar::Util 'blessed'; - -our $VERSION = '0.05'; +sub _core_calculateMRO { @{mro::get_linear_isa($_[0])} } -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; @@ -266,6 +253,23 @@ Class::C3 - A pragma to use the C3 method resolution order algortihm 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 @@ -484,6 +488,16 @@ limitation of this module. =back +=head1 COMPATIBILITY + +If your software requires Perl 5.9.5 or higher, you do not need L, you can simple C, and not worry about C, avoid some of the above caveats, and get the best possible performance. See L for more details. + +If your software is meant to work on earlier Perls, use L as documented here. L will detect Perl 5.9.5+ and take advantage of the core support when available. + +=head1 Class::C3::XS + +This module will load L 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). + =head1 CODE COVERAGE I use B to test the code coverage of my tests, below is the B report on this @@ -562,6 +576,8 @@ C. Stevan Little, Estevan@iinteractive.comE +Brandon L. Black, Eblblack@gmail.comE + =head1 COPYRIGHT AND LICENSE Copyright 2005, 2006 by Infinity Interactive, Inc.