File Coverage

blib/lib/Class/Std/Fast.pm
Criterion Covered Total %
statement 214 241 88.8
branch 65 90 72.2
condition 31 47 65.9
subroutine 39 48 81.2
pod 5 5 100.0
total 354 431 82.1


line stmt bran cond sub pod time code
1             package Class::Std::Fast;
2              
3 16     16   243874 use version; $VERSION = qv('0.0.8');
  16         38044  
  16         104  
4 16     16   1263 use strict;
  16         53  
  16         515  
5 16     16   82 use warnings;
  16         33  
  16         483  
6 16     16   102 use Carp;
  16         31  
  16         2246  
7              
8             BEGIN {
9             # warn if we cannot save aray UNIVERSAL::Can (because Class::Std has
10             # already overwritten it...)
11 16 50   16   90 exists $INC{'Class/Std.pm'}
12             && warn 'Class::Std::Fast loaded too late - put
13             >use Class::Std::Fast< somewhere at the top of your application
14             ';
15              
16             # save away UNIVERSAL::can
17 16         46 *real_can = \&UNIVERSAL::can;
18              
19 16         20846 require Class::Std;
20 16     16   81 no strict qw(refs);
  16         37  
  16         1005  
21 16         240423 for my $sub ( qw(MODIFY_CODE_ATTRIBUTES AUTOLOAD _mislabelled initialize) ) {
22 64         94 *{$sub} = \&{'Class::Std::' . $sub};
  64         13824  
  64         194  
23             }
24             }
25              
26             my %object_cache_of = ();
27             my %do_cache_class_of = ();
28             my %destroy_isa_unsorted_of = ();
29              
30             my %attribute;
31              
32              
33             my %optimization_level_of = ();
34             my $instance_counter = 1;
35              
36             # use () prototype to indicate to perl that it does not need to prepare an
37             # argument stack
38 0     0 1 0 sub OBJECT_CACHE_REF () { return \%object_cache_of };
39 0     0 1 0 sub ID_GENERATOR_REF () { return \$instance_counter };
40              
41             my @exported_subs = qw(
42             ident
43             DESTROY
44             _DUMP
45             AUTOLOAD
46             );
47             my @exported_extension_subs = qw(
48             MODIFY_CODE_ATTRIBUTES
49             MODIFY_HASH_ATTRIBUTES
50             );
51              
52             sub _cache_class_ref () {
53 0 0   0   0 croak q{you can't call this method in your namespace}
54             if 0 != index caller, 'Class::Std::';
55 0         0 return \%do_cache_class_of;
56             }
57              
58             sub _attribute_ref () {
59 0 0   0   0 croak q{you can't call this method in your namespace}
60             if 0 != index caller, 'Class::Std::';
61 0         0 return \%attribute;
62             }
63              
64             sub _get_internal_attributes {
65 7 50   7   22 croak q{you can't call this method in your namespace}
66             if 0 != index caller, 'Class::Std::';
67 7         25 return $attribute{$_[-1]};
68             }
69              
70             sub _set_optimization_level {
71 0   0 0   0 $optimization_level_of{$_[0]} = $_[1] || 1;
72             }
73              
74             # Prototype allows perl to inline ID
75             sub ID() {
76 18     18 1 70 return $instance_counter++;
77             }
78              
79             sub ident ($) {
80 123     123 1 33538 return ${$_[0]};
  123         794  
81             }
82              
83             sub _init_class_cache {
84 1     1   2 $do_cache_class_of{ $_[0] } = 1;
85 1   50     8 $object_cache_of{ $_[0] } ||= [];
86             }
87              
88             sub _init_import {
89 37     37   102 my ($caller_package, %flags) = @_;
90 37 50       107 $destroy_isa_unsorted_of{ $caller_package } = undef
91             if ($flags{isa_unsorted});
92              
93 37 100       109 _init_class_cache( $caller_package )
94             if ($flags{cache});
95              
96 16     16   151 no strict qw(refs);
  16         148  
  16         4610  
97              
98 37 100 33     118 if ($flags{constructor} eq 'normal') {
    50 33        
    50          
    0          
99 36         59 *{ $caller_package . '::new' } = \&new;
  36         251  
100             }
101             elsif ($flags{constructor} eq 'basic' && $flags{cache}) {
102 0         0 *{ $caller_package . '::new' } = \&_new_basic_cache;
  0         0  
103             }
104             elsif ($flags{constructor} eq 'basic' && ! $flags{cache}) {
105 1         2 *{ $caller_package . '::new' } = \&_new_basic;
  1         7  
106             }
107             elsif ($flags{constructor} eq 'none' ) {
108             # nothing to do
109             }
110             else {
111 0         0 croak "Illegal import flags constructor => '$flags{constructor}', cache => '$flags{cache}'";
112             }
113             }
114              
115             sub import {
116 32     32   22276 my $caller_package = caller;
117              
118 32 50 33     283 my %flags = (@_>=3)
    100          
119             ? @_[1..$#_]
120             : (@_==2) && $_[1] >=2
121             ? ( constructor => 'basic', cache => 0 )
122             : ( constructor => 'normal', cache => 0);
123 32 100       136 $flags{cache} = 0 if not defined $flags{cache};
124 32 100       90 $flags{constructor} = 'normal' if not defined $flags{constructor};
125              
126 32         118 _init_import($caller_package, %flags);
127              
128 16     16   93 no strict qw(refs);
  16         29  
  16         2689  
129 32         71 for my $sub ( @exported_subs ) {
130 128         159 *{ $caller_package . '::' . $sub } = \&{$sub};
  128         621  
  128         256  
131             }
132 32         67 for my $sub ( @exported_extension_subs ) {
133 64         135 my $target = $caller_package . '::' . $sub;
134 64   100 0   81 my $real_sub = *{ $target }{CODE} || sub { return @_[2..$#_] };
  0         0  
135 16     16   92 no warnings qw(redefine);
  16         29  
  16         2865  
136 64         4232 *{ $target } = sub {
137 96     96   1671 my ($package, $referent, @unhandled) = @_;
138 96         175 for my $handler ($sub, $real_sub) {
139 192 100       1393 next if ! @unhandled;
140 97         313 @unhandled = $handler->($package, $referent, @unhandled);
141             }
142 96         289 return @unhandled;
143 64         398 };
144             }
145             }
146              
147             sub __create_getter {
148 29     29   53 my ($package, $referent, $getter) = @_;
149 16     16   84 no strict 'refs';
  16         48  
  16         1628  
150 29         212 *{$package.'::get_'.$getter} = sub {
151 59     59   5507 return $referent->{${$_[0]}};
  59         249  
152             }
153 29         122 }
154              
155             sub __create_setter {
156 14     14   29 my ($package, $referent, $setter) = @_;
157 16     16   79 no strict 'refs';
  16         27  
  16         10274  
158 14         95 *{$package.'::set_'.$setter} = sub {
159 45     45   2177 $referent->{${$_[0]}} = $_[1];
  45         89  
160 45         71 return $_[0];
161             }
162 14         56 }
163              
164             sub MODIFY_HASH_ATTRIBUTES {
165 44     44   92 my ($package, $referent, @attrs) = @_;
166 44         73 for my $attr (@attrs) {
167 45 100       251 next if $attr !~ m/\A ATTRS? \s* (?: \( (.*) \) )? \z/xms;
168 44         64 my ($default, $init_arg, $getter, $setter, $name);
169 44 100       188 if (my $config = $1) {
170 37         106 $default = Class::Std::_extract_default($config);
171 37         599 $name = Class::Std::_extract_name($config);
172 37   100     559 $init_arg = Class::Std::_extract_init_arg($config) || $name;
173 37 100 100     676 if ($getter = Class::Std::_extract_get($config) || $name) {
174 29         474 __create_getter($package, $referent, $getter, $name);
175             }
176 37 100 100     232 if ($setter = Class::Std::_extract_set($config) || $name) {
177 14         253 __create_setter($package, $referent, $setter, $name);
178             }
179             }
180 44         505 undef $attr;
181 44   100     55 push @{$attribute{$package}}, {
  44         585  
182             ref => $referent,
183             default => $default,
184             init_arg => $init_arg,
185             name => $name || $init_arg || $getter || $setter || '????',
186             };
187             }
188 44         230 return grep { defined } @attrs;
  45         175  
189             }
190              
191             sub _DUMP {
192 1     1   3 my ($self) = @_;
193 1         2 my $id = ${$self};
  1         3  
194              
195 1         2 my %dump;
196 1         3 for my $package (keys %attribute) {
197 2         4 my $attr_list_ref = $attribute{$package};
198 2         2 for my $attr_ref ( @{$attr_list_ref} ) {
  2         5  
199 4 50       12 next if !exists $attr_ref->{ref}{$id};
200 4         15 $dump{$package}{$attr_ref->{name}} = $attr_ref->{ref}{$id};
201             }
202             }
203              
204 1         1273 require Data::Dumper;
205 1         7347 my $dump = Data::Dumper::Dumper(\%dump);
206 1         125 $dump =~ s/^.{8}//gxms;
207 1         6 return $dump;
208             }
209              
210             sub _new_basic {
211 1     1   4 return bless \(my $anon_scalar = $instance_counter++), $_[0];
212             }
213              
214             sub _new_basic_cache {
215 0   0 0   0 return pop @{ $object_cache_of{ $_[0] }}
216             || bless \(my $anon_scalar = $instance_counter++), $_[0];
217             }
218              
219             sub new {
220 16     16   98 no strict 'refs';
  16         27  
  16         3644  
221              
222             # Symbol Class:: must exist...
223 43 50   43 1 10765 croak "Can't find class $_[0]" if ! keys %{ $_[0] . '::' };
  43         207  
224              
225 43         165 Class::Std::initialize(); # Ensure run-time (and mod_perl) setup is done
226              
227             # extra safety only required if we actually care of arguments ...
228 43 50 66     646 croak "Argument to $_[0]\->new() must be hash reference"
229             if ($#_) && ref $_[1] ne 'HASH';
230              
231             # try cache first if caching is enabled for this class
232             my $new_obj = exists($do_cache_class_of{ $_[0] })
233 43   100     1039 && pop @{ $object_cache_of{ $_[0] } }
234             || bless \(my $another_anon_scalar = $instance_counter++), $_[0];
235              
236 43         241 my (@missing_inits, @suss_keys, @start_methods);
237 43   100     195 $_[1] ||= {};
238 43         59 my %arg_set;
239 43         167 BUILD: for my $base_class (Class::Std::_reverse_hierarchy_of($_[0])) {
240 76         175 my $arg_set = $arg_set{$base_class}
241 76 100       1250 = { %{$_[1]}, %{$_[1]->{$base_class}||{}} };
  76         478  
242              
243             # Apply BUILD() methods ...
244             {
245 16     16   101 no warnings 'once';
  16         27  
  16         8121  
  76         148  
246 76 100       90 if (my $build_ref = *{$base_class.'::BUILD'}{CODE}) {
  76         416  
247 18         31 $build_ref->($new_obj, ${$new_obj}, $arg_set);
  18         147  
248             }
249 76 100       4953 if (my $init_ref = *{$base_class.'::START'}{CODE}) {
  76         513  
250             push @start_methods, sub {
251 4     4   12 $init_ref->($new_obj, ${$new_obj}, $arg_set);
  4         12  
252 4         19 };
253             }
254             }
255              
256             # Apply init_arg and default for attributes still undefined ...
257 76         94 my $init_arg;
258 76         206 INIT:
259 76         100 for my $attr_ref ( @{$attribute{$base_class}} ) {
260 147 100       230 defined $attr_ref->{ref}{${$new_obj}} and next INIT;
  147         350  
261             # Get arg from initializer list...
262 133 100 100     604 if (defined $attr_ref->{init_arg} && exists $arg_set->{$attr_ref->{init_arg}}) {
    100          
263 50         106 $attr_ref->{ref}{${$new_obj}} = $arg_set->{$attr_ref->{init_arg}};
  50         107  
264 50         111 next INIT;
265             }
266             elsif (defined $attr_ref->{default}) {
267             # Or use default value specified...
268 16         987 $attr_ref->{ref}{${$new_obj}} = eval $attr_ref->{default};
  16         40  
269              
270 16 100       69 $@ and $attr_ref->{ref}{${$new_obj}} = $attr_ref->{default};
  8         17  
271 16         51 next INIT;
272             }
273 67 50       187 if (defined $attr_ref->{init_arg}) {
274             # Record missing init_arg ...
275 0         0 push @missing_inits,
276             "Missing initializer label for $base_class: "
277             . "'$attr_ref->{init_arg}'.\n";
278 0         0 push @suss_keys, keys %{$arg_set};
  0         0  
279             }
280             }
281             }
282              
283 43 50       129 croak @missing_inits, _mislabelled(@suss_keys),
284             'Fatal error in constructor call'
285             if @missing_inits;
286              
287 43         277 $_->() for @start_methods;
288              
289 43         1163 return $new_obj;
290             }
291              
292              
293             # Copied form Class::Std for performance
294             my %_hierarchy_of;
295              
296             sub _hierarchy_of {
297 70     70   168 my ($class) = @_;
298              
299 70 50       265 return @{$_hierarchy_of{$class}} if exists $_hierarchy_of{$class};
  0         0  
300              
301 16     16   107 no strict 'refs';
  16         29  
  16         9902  
302              
303 70         341 my @hierarchy = $class;
304 70         114 my @parents = @{$class.'::ISA'};
  70         961  
305              
306 70         273 while (defined (my $parent = shift @parents)) {
307 101         174 push @hierarchy, $parent;
308 101         141 push @parents, @{$parent.'::ISA'};
  101         596  
309             }
310              
311             # only sort if sorting is of any interest
312             # BIG speedup for classes with a long linear inheritance tree -
313             # may cause trouble with diamond inheritance.
314             # Sorting must be disabled by user
315 70 50       404 if (! exists $destroy_isa_unsorted_of{$class}) {
316 70         115 my %seen;
317             # maybe applying the Schwarzian transform could help?
318             # ... and sort {} grep {} @list runs through the list twice...
319 70 100       461 return @{$_hierarchy_of{$class}} =
  131 100       1047  
320 171 100       1272 sort { $a->isa($b) ? -1
321             : $b->isa($a) ? +1
322             : 0
323             }
324 70         176 grep { ! exists $seen{$_} and $seen{$_} = 1 } @hierarchy;
325             }
326             else {
327 0         0 my %seen;
328 0 0       0 return @{$_hierarchy_of{$class}} = grep { ! exists $seen{$_} and $seen{$_} = 1 } @hierarchy;
  0         0  
  0         0  
329             }
330             }
331              
332             # DESTROY looks a bit cryptic, thus needs to be explained...
333             #
334             # It performs the following tasks:
335             # - traverse the @ISA hierarchy
336             # - for every base class
337             # - call DEMOLISH if there is such a method with $_[0], ${$_[0]} as
338             # arguments (read as: $self, $ident).
339             # - delete the element with key ${ $_[0] } (read as: $ident)from all :ATTR hashes
340             #
341             sub DESTROY {
342 60     60   41312 my $ident = ${$_[0]};
  60         134  
343 60         118 my $class = ref $_[0];
344 60         133 push @_, $ident;
345             # Shortcut: check @ISA - saves us a method call if 0...
346             # DEMOLISH: for my $base_class (scalar @{ "$class\::ISA" }
347             # ? Class::Std::_hierarchy_of($class)
348             # : ($class) ) {
349 16     16   100 no strict qw(refs);
  16         33  
  16         3882  
350 60 100       238 for my $base_class (exists $_hierarchy_of{$class} ? @{$_hierarchy_of{$class}} : _hierarchy_of($class)) {
  42         98  
351             # call by & to tell perl that it doesn't need to put up a new argument
352             # stack
353 12         119 &{"$base_class\::DEMOLISH"}
  104         381  
354 104 100       129 if ( exists(&{"$base_class\::DEMOLISH"}) );
355              
356 104         651 delete $_->{ref}->{ $ident }
357 104         4862 for (@{$attribute{$base_class}});
358             }
359             # call with @_ as arguments - dirty but fast...
360 60 100       1628 &Class::Std::Fast::_cache if exists($do_cache_class_of{ $class });
361             }
362              
363             # Maybe we could speed up DESTROY by putting specific DESTROY methods
364             # into Class::Std::Fast classes via symbol table
365              
366             sub _cache {
367 2     2   4 push @{ $object_cache_of{ ref $_[0] }}, bless $_[0], ref $_[0];
  2         130  
368             }
369              
370             # clean out cache method to prevent it being called in global destruction
371             sub END {
372 16     16   95 no warnings qw(redefine);
  16         45  
  16         1581  
373 16     16   3028 *Class::Std::Fast::_cache = sub {};
  0     0   0  
374             }
375              
376             # save away real can. We need can() [the real one] in
377             # Class::Std::Fast::Storable - implementing STORBALE_freeze_pre / post
378             # via AUTOMETHOD is a bad idea, anyway...
379              
380             sub real_can;
381             # *real_can = \&CORE::UNIVERSAL::can;
382              
383             # Override can to make it work with AUTOMETHODs
384             # Slows down can() for all objects
385             {
386             my $real_can = \&UNIVERSAL::can;
387 16     16   95 no warnings qw(redefine once);
  16         32  
  16         2350  
388             *UNIVERSAL::can = sub {
389 98334 50   98334   460101 defined $_[0] or return;
390 98334         134333 my ($invocant, $method_name) = @_;
391              
392 98334 100       245725 if (my $sub_ref = $real_can->(@_)) {
393 83439         2126766 return $sub_ref;
394             }
395              
396             # call to Class::Std::_hierarchy_of replaced by hash lookup
397 14894 100 66     392137 for my $parent_class ( exists $_hierarchy_of{ ref $invocant || $invocant }
  14842   33     47114  
      66        
398             ? @{ $_hierarchy_of{ ref $invocant || $invocant }}
399             : Class::Std::Fast::_hierarchy_of(ref $invocant || $invocant) ) {
400 16     16   91 no strict 'refs';
  16         32  
  16         2640  
401 44647 50       44071 if (my $automethod_ref = *{$parent_class.'::AUTOMETHOD'}{CODE}) {
  44647         151132  
402 0         0 local $CALLER::_ = $_;
403 0         0 local $_ = $method_name;
404 0 0       0 if (my $method_impl = $automethod_ref->(@_)) {
405 0     0   0 return sub { my $inv = shift; $inv->$method_name(@_) }
  0         0  
406 0         0 }
407             }
408             }
409              
410 14894         555687 return;
411             };
412             }
413              
414             1;
415              
416             __END__