2 package Class::C3::PurePerl;
10 Class::C3::PurePerl - The default pure-Perl implementation of Class::C3
14 This is the plain pure-Perl implementation of Class::C3. The main Class::C3 package will
15 first attempt to load L<Class::C3::XS>, and then failing that, will fall back to this. Do
16 not use this package directly, use L<Class::C3> instead.
20 Stevan Little, E<lt>stevan@iinteractive.comE<gt>
22 Brandon L. Black, E<lt>blblack@gmail.comE<gt>
24 =head1 COPYRIGHT AND LICENSE
26 Copyright 2005, 2006 by Infinity Interactive, Inc.
28 L<http://www.iinteractive.com>
30 This library is free software; you can redistribute it and/or modify
31 it under the same terms as Perl itself.
35 package # hide me from PAUSE
41 use Scalar::Util 'blessed';
43 our $VERSION = '0.15';
49 eval "require Algorithm::C3";
50 die "Could not load 'mro' or 'Algorithm::C3'!" if $@;
57 # this is our global stash of both
58 # MRO's and method dispatch tables
59 # the structure basically looks like
63 # MRO => [ <class precendence list> ],
65 # orig => <original location of method>,
66 # code => \&<ref to original method>
68 # has_overload_fallback => (1 | 0)
73 # use these for debugging ...
74 sub _dump_MRO_table { %MRO }
77 # state tracking for initialize()/uninitialize()
78 our $_initialized = 0;
82 # skip if the caller is main::
83 # since that is clearly not relevant
84 return if $class eq 'main';
86 return if $TURN_OFF_C3;
87 mro::set_mro_c3($class) if $C3_IN_CORE;
89 # make a note to calculate $class
91 $MRO{$class} = undef unless exists $MRO{$class};
97 %next::METHOD_CACHE = ();
98 # why bother if we don't have anything ...
99 return unless keys %MRO;
101 mro::set_mro_c3($_) for keys %MRO;
106 $MRO{$_} = undef foreach keys %MRO;
108 _calculate_method_dispatch_tables();
109 _apply_method_dispatch_tables();
115 # why bother if we don't have anything ...
116 %next::METHOD_CACHE = ();
117 return unless keys %MRO;
119 mro::set_mro_dfs($_) for keys %MRO;
122 _remove_method_dispatch_tables();
127 sub reinitialize { goto &initialize }
129 ## functions for applying C3 to classes
131 sub _calculate_method_dispatch_tables {
132 return if $C3_IN_CORE;
134 foreach my $class (keys %MRO) {
135 _calculate_method_dispatch_table($class, \%merge_cache);
139 sub _calculate_method_dispatch_table {
140 return if $C3_IN_CORE;
141 my ($class, $merge_cache) = @_;
143 my @MRO = calculateMRO($class, $merge_cache);
144 $MRO{$class} = { MRO => \@MRO };
145 my $has_overload_fallback = 0;
148 # we do @MRO[1 .. $#MRO] here because it
149 # makes no sense to interogate the class
150 # which you are calculating for.
151 foreach my $local (@MRO[1 .. $#MRO]) {
152 # if overload has tagged this module to
153 # have use "fallback", then we want to
155 $has_overload_fallback = ${"${local}::()"}
156 if defined ${"${local}::()"};
157 foreach my $method (grep { defined &{"${local}::$_"} } keys %{"${local}::"}) {
158 # skip if already overriden in local class
159 next unless !defined *{"${class}::$method"}{CODE};
160 $methods{$method} = {
161 orig => "${local}::$method",
162 code => \&{"${local}::$method"}
163 } unless exists $methods{$method};
166 # now stash them in our %MRO table
167 $MRO{$class}->{methods} = \%methods;
168 $MRO{$class}->{has_overload_fallback} = $has_overload_fallback;
171 sub _apply_method_dispatch_tables {
172 return if $C3_IN_CORE;
173 foreach my $class (keys %MRO) {
174 _apply_method_dispatch_table($class);
178 sub _apply_method_dispatch_table {
179 return if $C3_IN_CORE;
182 ${"${class}::()"} = $MRO{$class}->{has_overload_fallback}
183 if $MRO{$class}->{has_overload_fallback};
184 foreach my $method (keys %{$MRO{$class}->{methods}}) {
185 *{"${class}::$method"} = $MRO{$class}->{methods}->{$method}->{code};
189 sub _remove_method_dispatch_tables {
190 return if $C3_IN_CORE;
191 foreach my $class (keys %MRO) {
192 _remove_method_dispatch_table($class);
196 sub _remove_method_dispatch_table {
197 return if $C3_IN_CORE;
200 delete ${"${class}::"}{"()"} if $MRO{$class}->{has_overload_fallback};
201 foreach my $method (keys %{$MRO{$class}->{methods}}) {
202 delete ${"${class}::"}{$method}
203 if defined *{"${class}::${method}"}{CODE} &&
204 (*{"${class}::${method}"}{CODE} eq $MRO{$class}->{methods}->{$method}->{code});
208 ## functions for calculating C3 MRO
211 my ($class, $merge_cache) = @_;
213 return @{mro::get_mro_linear_c3($class)} if $C3_IN_CORE;
215 return Algorithm::C3::merge($class, sub {
221 package # hide me from PAUSE
227 use Scalar::Util 'blessed';
229 our $VERSION = '0.06';
235 my $class = blessed($self) || $self;
236 my $indirect = caller() =~ /^(?:next|maybe::next)$/;
237 my $level = $indirect ? 2 : 1;
239 my ($method_caller, $label, @label);
240 while ($method_caller = (caller($level++))[3]) {
241 @label = (split '::', $method_caller);
244 $label eq '(eval)' ||
245 $label eq '__ANON__';
250 my $caller = join '::' => @label;
252 $method = $METHOD_CACHE{"$class|$caller|$label"} ||= do {
254 my @MRO = Class::C3::calculateMRO($class);
257 while ($current = shift @MRO) {
258 last if $caller eq $current;
263 foreach my $class (@MRO) {
264 next if (defined $Class::C3::MRO{$class} &&
265 defined $Class::C3::MRO{$class}{methods}{$label});
266 last if (defined ($found = *{$class . '::' . $label}{CODE}));
272 return $method if $indirect;
274 die "No next::method '$label' found for $self" if !$method;
279 sub can { method($_[0]) }
281 package # hide me from PAUSE
287 our $VERSION = '0.02';
289 sub method { (next::method($_[0]) || return)->(@_) }