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

Build.PL
ChangeLog
MANIFEST
README
lib/Class/C3.pm
lib/Class/C3/next.pm [new file with mode: 0644]
t/00_load.t
t/10_Inconsistent_hierarchy.t
t/20_reinitialize.t

index da6c4f4..70672ce 100644 (file)
--- a/Build.PL
+++ b/Build.PL
@@ -6,13 +6,14 @@ my $build = Module::Build->new(
     module_name => 'Class::C3',
     license => 'perl',
     requires => {
-        'Scalar::Util'    => 1.10,
         'Algorithm::C3'   => 0.06,
+        'Scalar::Util'    => 1.10,
+    },
+    recommends => {
+        'Class::C3::XS'   => 0.02,
     },
-    optional => {},
     build_requires => {
         'Test::More' => '0.47',
-        'Test::Exception' => 0.15,
     },
     create_makefile_pl => 'traditional',
     recursive_test_files => 1,
index 5913011..888c1bb 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -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
index 002d31e..da820a6 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -1,11 +1,12 @@
 Build.PL
 ChangeLog
+lib/Class/C3.pm
+lib/Class/C3/next.pm
+Makefile.PL
 MANIFEST                       This list of files
 META.yml
-Makefile.PL
-README
-lib/Class/C3.pm
 opt/c3.pm
+README
 t/00_load.t
 t/01_MRO.t
 t/02_MRO.t
diff --git a/README b/README
index 9ba0f1b..86571da 100644 (file)
--- a/README
+++ b/README
@@ -1,4 +1,4 @@
-Class::C3 version 0.14
+Class::C3 version 0.15_01
 ===========================
 
 INSTALLATION
@@ -17,6 +17,22 @@ This module requires these other modules and libraries:
        Algorithm::C3 0.06
        Scalar::Util 1.10
 
+Additionally, this module will optionally take advantage of
+these if installed:
+
+       Class::C3::XS 0.01_06
+
+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.
+
 COPYRIGHT AND LICENCE
 
 Copyright (C) 2005, 2006 Infinity Interactive, Inc.
index 5b633d5..30e8736 100644 (file)
@@ -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
@@ -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};
@@ -46,24 +66,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 }
@@ -71,6 +101,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);
@@ -78,6 +109,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);
@@ -109,12 +141,14 @@ 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}
@@ -130,12 +164,14 @@ sub _apply_method_dispatch_table {
 }
 
 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};    
@@ -146,82 +182,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';
+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;
 
@@ -273,6 +252,17 @@ 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_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 
@@ -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 
diff --git a/lib/Class/C3/next.pm b/lib/Class/C3/next.pm
new file mode 100644 (file)
index 0000000..27dfaa2
--- /dev/null
@@ -0,0 +1,106 @@
+package  # hide me from PAUSE
+    next; 
+
+use strict;
+use warnings;
+no warnings 'redefine'; # for 00load.t w/ core support
+
+use Scalar::Util 'blessed';
+
+our $VERSION = '0.06';
+
+our %METHOD_CACHE;
+
+sub method {
+    my $self     = $_[0];
+    my $class    = blessed($self) || $self;
+    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 $method;
+
+    my $caller   = join '::' => @label;    
+    
+    $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};
+}
+
+sub can { method($_[0]) }
+
+package  # hide me from PAUSE
+    maybe::next; 
+
+use strict;
+use warnings;
+no warnings 'redefine'; # for 00load.t w/ core support
+
+our $VERSION = '0.02';
+
+sub method { (next::method($_[0]) || return)->(@_) }
+
+1;
+
+__END__
+
+=pod
+
+=head1 NAME
+
+Class::C3::next - Pure-perl next::method and friends
+
+=head1 DESCRIPTION
+
+This module is used internally by L<Class::C3> when
+neccesary, and shouldn't be used (or required in
+distribution dependencies) directly.  It
+defines C<next::method>, C<next::can>, and
+C<maybe::next::method> in pure perl.
+
+=head1 AUTHOR
+
+Stevan Little, E<lt>stevan@iinteractive.comE<gt>
+
+Brandon L. Black, E<lt>blblack@gmail.comE<gt>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright 2005, 2006 by Infinity Interactive, Inc.
+
+L<http://www.iinteractive.com>
+
+This library is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself. 
+
+=cut
index 9703116..125fbde 100644 (file)
@@ -3,8 +3,9 @@
 use strict;
 use warnings;
 
-use Test::More tests => 1;
+use Test::More tests => 2;
 
 BEGIN {
     use_ok('Class::C3');
-}
\ No newline at end of file
+    use_ok('Class::C3::next');
+}
index 2378ea3..d36e42d 100644 (file)
@@ -26,32 +26,32 @@ except TypeError:
 
 =cut
 
-{
-    package X;
-    use Class::C3;
-    
-    package Y;
-    use Class::C3;    
-    
-    package XY;
-    use Class::C3;
-    use base ('X', 'Y');
-    
-    package YX;
-    use Class::C3;
-    use base ('Y', 'X');
-    
-    package Z;
-    # use Class::C3; << Dont do this just yet ...
-    use base ('XY', 'YX');
-}
+eval q{ 
+    {
+        package X;
+        use Class::C3;
+
+        package Y;
+        use Class::C3;    
+
+        package XY;
+        use Class::C3;
+        use base ('X', 'Y');
+
+        package YX;
+        use Class::C3;
+        use base ('Y', 'X');
+
+        package Z;
+        eval 'use Class::C3' if $Class::C3::C3_IN_CORE;
+        use base ('XY', 'YX');
+    }
 
-Class::C3::initialize();
+    Class::C3::initialize();
 
-eval { 
     # now try to calculate the MRO
     # and watch it explode :)
-    Class::C3::calculateMRO('Z') 
+    Class::C3::calculateMRO('Z');
 };
 #diag $@;
-like($@, qr/^Inconsistent hierarchy/, '... got the right error with an inconsistent hierarchy');
+like($@, qr/Inconsistent hierarchy /, '... got the right error with an inconsistent hierarchy');
index 7dce5d4..0912cb4 100644 (file)
@@ -81,7 +81,13 @@ is_deeply(
     [ qw(Diamond_D Diamond_B Diamond_E Diamond_C Diamond_A) ],
     '... got the new MRO for Diamond_D');
 
-is(Diamond_D->hello, 'Diamond_C::hello', '... method still resolves with old MRO');
+# Doesn't work with core support, since reinit is not neccesary and the change
+#  takes effect immediately
+SKIP: {
+    skip "This test does not work with a c3-patched perl interpreter", 1
+        if $Class::C3::C3_IN_CORE;
+    is(Diamond_D->hello, 'Diamond_C::hello', '... method still resolves with old MRO');
+}
 
 Class::C3::reinitialize();