Merge 'trunk' into 'Class-C3-PurePerl'
Brandon L Black [Mon, 30 Apr 2007 23:38:24 +0000 (23:38 +0000)]
r30755@brandon-blacks-computer (orig r2234):  blblack | 2007-04-30 18:35:11 -0500
new overload fallback fixes, matches the behavior of normal overload and overload+c3 in blead

1  2 
ChangeLog
lib/Class/C3.pm

diff --combined ChangeLog
+++ b/ChangeLog
@@@ -1,20 -1,8 +1,22 @@@
  Revision history for Perl extension Class::C3.
  
++    - Fixed overload fallback edge cases.
++
 +0.15_05 Thurs, Apr 19, 2007
 +    - Patch is in the latest perl-current now,
 +      and this dev release assumes 5.9.5 has the patch
 +
 +0.15_03 Tue, Apr 17, 2007
 +    - New c3.patch, improves threads compat and
 +      mem mgmt.
 +
 +0.15_02 Sun, Apr 15, 2007
      - Fix for overloading to method name string,
         from Ittetsu Miyazaki.
 -    - Fixed overload fallback edge cases.
 +    - Supports Class::C3::XS
 +
 +0.15_01 Fri, Apr 13, 2007
 +    - Supports bleadperl + c3 patches (experimental)
  
  0.14 Tues, Sep 19, 2006
      - Fix for rt.cpan.org #21558
diff --combined lib/Class/C3.pm
@@@ -4,27 -4,10 +4,27 @@@ package Class::C3
  use strict;
  use warnings;
  
 -use Scalar::Util 'blessed';
 -use Algorithm::C3;
 -
 -our $VERSION = '0.14';
 +our $VERSION = '0.15_05';
 +
 +our $C3_IN_CORE;
 +our $C3_XS;
 +
 +BEGIN {
 +    if($^V < 5.009005) {
 +        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
@@@ -54,10 -37,7 +54,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};
  ## 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);
      $MRO{$class} = { MRO => \@MRO };
-     my $has_overload_fallback = 0;
+     my $has_overload_fallback;
      my %methods;
      # NOTE: 
      # we do @MRO[1 .. $#MRO] here because it
          # have use "fallback", then we want to
          # grab that value 
          $has_overload_fallback = ${"${local}::()"} 
-             if defined ${"${local}::()"};
+             if !defined $has_overload_fallback && defined ${"${local}::()"};
          foreach my $method (grep { defined &{"${local}::$_"} } keys %{"${local}::"}) {
              # skip if already overriden in local class
              next unless !defined *{"${class}::$method"}{CODE};
  }
  
  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};
+         if !defined &{"${class}::()"}
+            && defined $MRO{$class}->{has_overload_fallback};
      foreach my $method (keys %{$MRO{$class}->{methods}}) {
          if ( $method =~ /^\(/ ) {
              my $orig = $MRO{$class}->{methods}->{$method}->{orig};
  }
  
  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; 
 -
 -use strict;
 -use warnings;
 -
 -use Scalar::Util 'blessed';
 +sub _core_calculateMRO { @{mro::get_linear_isa($_[0])} }
  
 -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;
  
@@@ -251,17 -273,6 +252,17 @@@ Class::C3 - A pragma to use the C3 meth
      D->can('hello')->();          # can() also works correctly
      UNIVERSAL::can('D', 'hello'); # as does UNIVERSAL::can()
  
 +=head1 SPECIAL NOTE FOR 0.15_05
 +
 +To try this with the new perl core c3 support,
 +download the most recent copy perl-current:
 +
 +http://mirrors.develooper.com/perl/APC/perl-current-snap/
 +
 +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_05.
 +
  =head1 DESCRIPTION
  
  This is pragma to change Perl 5's standard method resolution order from depth-first left-to-right 
@@@ -480,16 -491,6 +481,16 @@@ limitation of this module
  
  =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