File Coverage

blib/lib/Class/Prototyped.pm
Criterion Covered Total %
statement 681 807 84.3
branch 270 386 69.9
condition 77 119 64.7
subroutine 83 94 88.3
pod 7 7 100.0
total 1118 1413 79.1


line stmt bran cond sub pod time code
1             #############################################################################
2             #
3             # Class::Prototyped - Fast prototype-based OO programming in Perl
4             #
5             # Author: Ned Konz and Toby Ovod-Everett
6             #############################################################################
7             # Copyright 2001-2004 Ned Konz and Toby Ovod-Everett. All rights reserved.
8             #
9             # This program is free software; you can redistribute it and/or modify it
10             # under the same terms as Perl itself.
11             #
12             # For comments, questions, bugs or general interest, feel free to
13             # contact Toby Ovod-Everett at toby@ovod-everett.org
14             #############################################################################
15              
16             # Class::Prototyped - Fast prototype-based OO programming in Perl
17              
18             package Class::Prototyped::Mirror;
19             sub PREFIX() { 'PKG0x' }
20             sub PREFIX_LENGTH() { 5 }
21              
22              
23             package Class::Prototyped;
24 13     13   17673 use strict;
  13         31  
  13         483  
25 13     13   68 use Carp();
  13         22  
  13         2378  
26              
27             $Class::Prototyped::VERSION = '1.13';
28              
29             sub import {
30 13     13   183 while (my $symbol = shift) {
31 30 100       319 if ($symbol eq ':OVERLOAD') {
    100          
    100          
    50          
    100          
32 2 50       14 unless (scalar keys %Class::Prototyped::overloadable_symbols) {
33 2     2   180 eval "use overload";
  2         4047  
  2         2562  
  2         12  
34 2         65 @Class::Prototyped::overloadable_symbols{ map { split }
  30         187  
35             values %overload::ops } = undef;
36             }
37             }
38             elsif ($symbol eq ':REFLECT') {
39             *UNIVERSAL::reflect =
40 5     7   149 sub { Class::Prototyped::Mirror->new($_[0]) };
  7         39  
41             }
42             elsif ($symbol eq ':EZACCESS') {
43 13     13   68 no strict 'refs';
  13         26  
  13         10977  
44              
45 5         15 foreach my $call (
46             qw(addSlot addSlots deleteSlot deleteSlots getSlot getSlots super)
47             ) {
48 35         196 *{$call} = sub {
49 116     116   1304 my $obj = shift->reflect;
50 116         448 UNIVERSAL::can($obj, $call)->($obj, @_);
51 35         117 };
52             }
53             }
54             elsif ($symbol eq ':SUPER_FAST') {
55 0         0 *Class::Prototyped::Mirror::super =
56             \&Class::Prototyped::Mirror::super_fast;
57             }
58             elsif ($symbol eq ':NEW_MAIN') {
59 4     11   182 *main::new = sub { Class::Prototyped->new(@_) };
  11         1283  
60             }
61             }
62             }
63              
64             # Constructor. Pass in field definitions.
65             sub new {
66 93     93 1 5055 my $class = shift;
67              
68 93 50       316 Carp::croak("odd number of slot parameters to new\n") if scalar(@_) % 2;
69              
70 93         426 $class->newCore('new', undef, @_);
71             }
72              
73             sub newPackage {
74 1     1 1 2 my $class = shift;
75 1         4 my $package = shift;
76              
77 1 50       5 Carp::croak("odd number of slot parameters to newPackage\n") if scalar(@_) % 2;
78              
79 1         4 $class->newCore('newPackage', $package, @_);
80             }
81              
82             # Creates a copy of an object
83             sub clone {
84 15     15 1 13194 my $original = shift;
85              
86 15 50       68 Carp::croak("odd number of slot parameters to clone\n") if scalar(@_) % 2;
87              
88 15         100 $original->newCore('clone', undef, @_);
89             }
90              
91             sub clonePackage {
92 1     1 1 265 my $original = shift;
93 1         1 my $package = shift;
94              
95 1 50       5 Carp::croak("odd number of slot parameters to clonePackage\n") if scalar(@_) % 2;
96              
97 1         4 $original->newCore('clonePackage', $package, @_);
98             }
99              
100              
101             sub newCore {
102 110     110 1 182 my $class = shift;
103 110         171 my $caller = shift;
104 110         155 my $package = shift;
105              
106 110         661 my $isPackage = substr($caller, -7) eq 'Package';
107 110         344 my $isNew = substr($caller, 0, 3) eq 'new';
108 110         198 my $isClone = substr($caller, 0, 5) eq 'clone';
109              
110 110 50       416 Carp::croak("odd number of slot parameters to $caller\n") if scalar(@_) % 2;
111              
112 110         302 my $class_mirror = $class->reflect;
113              
114 110         163 my($self, $tied);
115             {
116 13     13   74 no strict 'refs';
  13         23  
  13         7543  
  110         136  
117              
118 110 100       259 if ( $isPackage ) {
119 2 50       4 if (scalar(keys %{"$package\::"})) {
  2         15  
120 0         0 Carp::croak(
121             "attempt to use newPackage with already existing package\n"
122             . "package: $package");
123             }
124 2         3 my %self;
125 2         11 tie %self, $class_mirror->tiedInterfacePackage();
126 2         5 $tied = tied %self;
127 2         5 $Class::Prototyped::Mirror::objects{$package} = $self = \%self;
128             }
129             else {
130 108         182 $self = {};
131 108         394 $package = Class::Prototyped::Mirror::PREFIX . substr("$self", 7, -1); # HASH($package)
132              
133 108         276 tie %$self, $class->reflect->tiedInterfacePackage();
134              
135 108         260 $tied = tied %$self;
136 108         192 *{"$package\::DESTROY"} = \&Class::Prototyped::DESTROY;
  108         1156  
137             }
138             }
139              
140 110         308 $tied->package($package);
141 110         142 @{ $tied->{isa} } = qw(Class::Prototyped);
  110         1777  
142 110         239 $tied->{vivified_parents} = 1;
143 110         181 $tied->{vivified_methods} = 1;
144 110         318 $tied->{defaults} = $class_mirror->_defaults;
145              
146 110         241 bless $self, $package; # in my own package
147              
148 110 100 100     842 my $parsedSlots = scalar @_ || $isClone ?
    100          
149             $self->reflect->addSlotsParser( ( $isClone ? $class->reflect->getSlots() : () ), @_ ) :
150             [];
151              
152 110 100       388 if ( $isNew ) {
153 94 100 100     381 my $classstar = ( ref($class) &&
154             substr(ref($class), 0, Class::Prototyped::Mirror::PREFIX_LENGTH) ne
155             Class::Prototyped::Mirror::PREFIX
156             ) ? ref($class) : $class; # allow object for named class to provide a class name
157              
158 94 100 66     266 if (!(grep { $_->[0] eq 'class*' } @$parsedSlots) && $classstar ne 'Class::Prototyped')
  163         753  
159             {
160 38         53 unshift(@$parsedSlots, @{$self->reflect->addSlotsParser('class*' => $classstar)});
  38         110  
161             }
162             }
163              
164 110 100       409 $self->reflect->addParsedSlots($parsedSlots) if scalar(@$parsedSlots);
165              
166 110         484 return $self;
167             }
168              
169             sub reflect {
170 1115   66 1115 1 39653 return $Class::Prototyped::Mirror::mirrors{ $_[0] } || Class::Prototyped::Mirror->new($_[0]);
171             }
172              
173             sub destroy {
174 105     105 1 140 my $self = shift;
175 105         404 my $mirror = $self->reflect;
176 105         169 my(@otherOrder) = @{$mirror->_otherOrder};
  105         271  
177 105         381 $mirror->deleteSlots(@otherOrder);
178             }
179              
180             # Remove my symbol table
181             sub DESTROY {
182 109     109   9340 my $self = shift;
183 109         189 my $package = ref($self);
184 109 100 66     663 if ((substr($package, 0, Class::Prototyped::Mirror::PREFIX_LENGTH) eq
185             Class::Prototyped::Mirror::PREFIX)
186             && ($package ne 'Class::Prototyped'))
187             {
188 13     13   106 no strict 'refs';
  13         38  
  13         12626  
189              
190 108 50       2404 my $tied = tied(%$self) or return;
191 108         214 my $parentOrder = $tied->{parentOrder};
192 108         178 my $isa = $tied->{isa};
193 108         148 my $slots = $tied->{slots};
194              
195 108         173 my (@deadIndices);
196 108         298 foreach my $i (0 .. $#$parentOrder) {
197 91         186 my $parent = $slots->{ $parentOrder->[$i] };
198 91   66     328 my $parentPackage = ref($parent) || $parent;
199 91         401 push (@deadIndices, $i)
200 91 50       109 unless scalar(keys %{"$parentPackage\::"});
201             }
202              
203 108         231 foreach my $i (@deadIndices) {
204 0         0 delete($slots->{ $parentOrder->[$i] });
205 0         0 splice(@$parentOrder, $i, 1);
206 0         0 splice(@$isa, $i, 1);
207             }
208              
209             # this is required to re-cache @ISA
210 108         124 delete ${"$package\::"}{'::ISA::CACHE::'};
  108         290  
211 108         2782 @$isa=@$isa;
212              
213 108         266 my $parent_DESTROY;
214 108         165 my (@isa_queue) = @{"$package\::ISA"};
  108         476  
215 108         149 my (%isa_cache);
216 108         388 while (my $pkg = shift @isa_queue) {
217 233 100       650 exists $isa_cache{$pkg} and next;
218 172         193 my $code = *{"$pkg\::DESTROY"}{CODE};
  172         679  
219 172 100 100     933 if (defined $code && $code != \&Class::Prototyped::DESTROY) {
220 26         31 $parent_DESTROY = $code;
221 26         42 last;
222             }
223 146         195 unshift (@isa_queue, @{"$pkg\::ISA"});
  146         471  
224 146         946 $isa_cache{$pkg} = undef;
225             }
226              
227 108         661 $self->destroy; # call the user destroy function
228              
229 108 100       593 $parent_DESTROY->($self) if defined $parent_DESTROY;
230              
231 108         393 $self->reflect->deleteSlots($self->reflect->slotNames('PARENT'));
232              
233 108         193 foreach my $key (keys %{"$package\::"}) {
  108         464  
234 836         922 delete ${"$package\::"}{$key};
  836         2978  
235             }
236              
237             # this only works because we're not a multi-level package:
238 108         881 delete($main::{"$package\::"});
239              
240 108         3273 delete($Class::Prototyped::Mirror::parents{$package});
241             }
242             }
243              
244             $Class::Prototyped::Mirror::ending = 0;
245 13     13   0 sub END { $Class::Prototyped::Mirror::ending = 1 }
246              
247             package Class::Prototyped::Tied;
248             $Class::Prototyped::Tied::VERSION = '1.13';
249             @Class::Prototyped::Tied::DONT_LIE_FOR = qw(Data::Dumper);
250              
251             sub TIEHASH {
252 139   100 139   2165 bless $_[1] || {
253             package => undef,
254             isa => undef,
255             parentOrder => [],
256             otherOrder => [],
257             slots => {},
258             types => {},
259             attribs => {},
260             defaults => undef,
261             vivified_parents => 0,
262             vivified_methods => 0,
263             },
264             $_[0];
265             }
266              
267             sub FIRSTKEY {
268 20     20   3434 $_[0]->{dont_lie} = 0;
269 20         118 my $caller = (caller(0))[0];
270 20         55 foreach my $i (@Class::Prototyped::Tied::DONT_LIE_FOR) {
271 20 100       89 $_[0]->{dont_lie} = $caller eq $i and last;
272             }
273 20         39 $_[0]->{iter} = 1;
274 20         24 $_[0]->{cachedOrder} = [@{ $_[0]->{parentOrder} }, @{ $_[0]->{otherOrder} }];
  20         40  
  20         98  
275              
276 20 100       88 unless ($_[0]->{dont_lie}) {
277 2         5 my $slots = $_[0]->{slots};
278 2         7 @{ $_[0]->{cachedOrder} } =
  5         19  
279 2         6 grep { !UNIVERSAL::isa($slots->{$_}, 'CODE') }
280 2         3 @{ $_[0]->{cachedOrder} };
281             }
282 20         102 return $_[0]->{cachedOrder}->[0];
283             }
284              
285             sub NEXTKEY {
286 255     255   1067 return $_[0]->{cachedOrder}->[ $_[0]->{iter}++ ];
287             }
288              
289             sub EXISTS {
290 0 0   0   0 exists $_[0]->{slots}->{ $_[1] } or return 0;
291 0 0       0 UNIVERSAL::isa($_[0]->{slots}->{ $_[1] }, 'CODE') or return 1;
292 0         0 my $dont_lie = 0;
293 0         0 my $caller = (caller(0))[0];
294 0         0 foreach my $i (@Class::Prototyped::Tied::DONT_LIE_FOR) {
295 0 0       0 $dont_lie = $caller eq $i and last;
296             }
297 0 0       0 return $dont_lie ? 1 : 0;
298             }
299              
300             sub CLEAR {
301 1     1   532 Carp::croak("attempt to call CLEAR on the hash interface"
302             . " of a Class::Prototyped object\n");
303             }
304              
305             sub package {
306 139 100   139   402 return $_[0]->{package} unless @_ > 1;
307 13     13   78 no strict 'refs';
  13         24  
  13         10573  
308 137         179 $_[0]->{isa} = \@{"$_[1]\::ISA"};
  137         835  
309 137         353 $_[0]->{package} = $_[1];
310             }
311              
312             #### Default Tied implementation
313             package Class::Prototyped::Tied::Default;
314             $Class::Prototyped::Tied::Default::VERSION = '1.13';
315             @Class::Prototyped::Tied::Default::ISA = qw(Class::Prototyped::Tied);
316              
317             sub STORE {
318 10     10   851 my $slots = $_[0]->{slots};
319              
320 10 100       196 Carp::croak(
321             "attempt to access non-existent slot through tied hash object interface"
322             )
323             unless exists $slots->{ $_[1] };
324              
325 9 100       162 Carp::croak(
326             "attempt to access METHOD slot through tied hash object interface")
327             if UNIVERSAL::isa($slots->{ $_[1] }, 'CODE');
328              
329 8 50       23 Carp::croak(
330             "attempt to modify parent slot through the tied hash object interface")
331             if substr($_[1], -1) eq '*';
332              
333 8         40 $slots->{ $_[1] } = $_[2];
334             }
335              
336             sub FETCH {
337 283     283   1387 my $slots = $_[0]->{slots};
338              
339 283 100       1337 Carp::croak(
340             "attempt to access non-existent slot through tied hash object interface:\n"
341             . "$_[1]")
342             unless exists $slots->{ $_[1] };
343              
344 278 100       1128 if (UNIVERSAL::isa($slots->{ $_[1] }, 'CODE')) {
345 127         162 my $dont_lie = 0;
346 127         566 my $caller = (caller(0))[0];
347 127         296 foreach my $i (@Class::Prototyped::Tied::DONT_LIE_FOR) {
348 127 100       391 $dont_lie = $caller eq $i and last;
349             }
350             Carp::croak(
351 127 100       480 "attempt to access METHOD slot through tied hash object interface")
352             unless $dont_lie;
353             }
354              
355 277         4934 $slots->{ $_[1] };
356             }
357              
358             sub DELETE {
359 0     0   0 Carp::croak "attempt to delete a slot through tied hash object interface";
360             }
361              
362             #### AutoVivifying Tied implementation
363             package Class::Prototyped::Tied::AutoVivify;
364             $Class::Prototyped::Tied::AutoVivify::VERSION = '1.13';
365             @Class::Prototyped::Tied::AutoVivify::ISA = qw(Class::Prototyped::Tied);
366              
367             sub STORE {
368 3     3   8 my $slots = $_[0]->{slots};
369              
370 3 50       12 Carp::croak(
371             "attempt to modify parent slot through the tied hash object interface")
372             if substr($_[1], -1) eq '*';
373              
374 3 100       12 if (exists $slots->{ $_[1] }) {
375 1 50       5 Carp::croak(
376             "attempt to access METHOD slot through tied hash object interface")
377             if UNIVERSAL::isa($slots->{ $_[1] }, 'CODE');
378             }
379             else {
380 2         4 my $slot = $_[1];
381 2         14 $slots->{ $_[1] } = $_[2];
382             my $implementation = bless sub {
383 1 50   1   8 @_ > 1 ? $slots->{$slot} = $_[1] : $slots->{$slot};
384 2         24 }, 'Class::Prototyped::FieldAccessor';
385 13     13   80 no strict 'refs';
  13         33  
  13         5290  
386 2         8 local $^W = 0; # suppress redefining messages.
387 2         4 *{ $_[0]->package . "::$slot" } = $implementation;
  2         10  
388 2         4 push (@{ $_[0]->{otherOrder} }, $slot);
  2         6  
389 2         8 $_[0]->{types}->{$slot} = 'FIELD';
390             }
391              
392 3 50       10 Carp::croak(
393             "attempt to access non-existent slot through tied hash object interface"
394             )
395             unless exists $slots->{ $_[1] };
396              
397 3         14 $slots->{ $_[1] } = $_[2];
398             }
399              
400             sub FETCH {
401 9     9   102 my $slots = $_[0]->{slots};
402              
403 9 100 100     55 if (exists $slots->{ $_[1] }
404             and UNIVERSAL::isa($slots->{ $_[1] }, 'CODE'))
405             {
406 1         3 my $dont_lie = 0;
407 1         17 my $caller = (caller(0))[0];
408 1         4 foreach my $i (@Class::Prototyped::Tied::DONT_LIE_FOR) {
409 1 50       7 $dont_lie = $caller eq $i and last;
410             }
411             Carp::croak(
412 1 50       201 "attempt to access METHOD slot through tied hash object interface")
413             unless $dont_lie;
414             }
415              
416 8         30 $slots->{ $_[1] };
417             }
418              
419             sub EXISTS {
420 0     0   0 exists $_[0]->{slots}->{ $_[1] };
421             }
422              
423             sub DELETE {
424 0     0   0 my $slots = $_[0]->{slots};
425              
426 0 0 0     0 if (UNIVERSAL::isa($slots->{ $_[1] }, 'CODE')
427             && (caller(0))[0] ne 'Data::Dumper')
428             {
429 0         0 Carp::croak
430             "attempt to delete METHOD slot through tied hash object interface";
431             }
432              
433 0         0 my $package = $_[0]->package;
434 0         0 my $slot = $_[1];
435             {
436 13     13   80 no strict 'refs';
  13         26  
  13         5062  
  0         0  
437 0         0 my $name = "$package\::$slot";
438              
439             # save the glob...
440 0         0 local *old = *{$name};
  0         0  
441              
442             # and restore everything else
443 0         0 local *new;
444 0         0 foreach my $type (qw(HASH IO FORMAT SCALAR ARRAY)) {
445 0         0 my $elem = *old{$type};
446 0 0       0 next if !defined($elem);
447 0         0 *new = $elem;
448             }
449 0         0 *{$name} = *new;
  0         0  
450             }
451 0         0 my $otherOrder = $_[0]->{otherOrder};
452 0         0 @$otherOrder = grep { $_ ne $slot } @$otherOrder;
  0         0  
453 0         0 delete $slots->{$slot}; # and delete the data/sub ref
454 0         0 delete $_[0]->{types}->{$slot};
455             }
456              
457             # Everything that deals with modifying or inspecting the form
458             # of an object is done through a reflector.
459              
460             package Class::Prototyped::Mirror;
461             $Class::Prototyped::Mirror::VERSION = '1.13';
462             $Class::Prototyped::Mirror::PROFILE::VERSION = '1.13';
463             $Class::Prototyped::Mirror::SUPER::VERSION = '1.13';
464              
465             sub new {
466 951     951   1462 my $class = shift;
467 951         1279 my($entity) = @_;
468              
469 951 100       2472 if ( ref($entity) ) {
470 914 100       3527 if (substr(ref($entity), 0, Class::Prototyped::Mirror::PREFIX_LENGTH) eq
    100          
471             Class::Prototyped::Mirror::PREFIX)
472             {
473 909         6838 return bless \$entity, 'Class::Prototyped::Mirror';
474             }
475             elsif ($Class::Prototyped::Mirror::objects{ ref($entity) } == $entity) {
476 3   50     33 return $Class::Prototyped::Mirror::mirrors{ $entity } ||= bless \$entity, 'Class::Prototyped::Mirror';
477             }
478             else {
479 2         15 return Class::Prototyped::Mirror::Normal->new($entity);
480             }
481             }
482              
483 37         62 my $object;
484 37 100       280 unless ($object = $Class::Prototyped::Mirror::objects{ $entity }) {
485 27         39 my (%self);
486             my $tiepkg;
487 27 100       101 if ($entity eq 'Class::Prototyped') {
488 13         34 $tiepkg = 'Class::Prototyped::Tied::Default';
489             }
490             else {
491 13     13   77 no strict 'refs';
  13         29  
  13         1718  
492 14         20 $tiepkg = eval { ${"$entity\::ISA"}[0]->reflect->tiedInterfacePackage() };
  14         22  
  14         126  
493 14 100       53 $tiepkg = Class::Prototyped->reflect->tiedInterfacePackage() if $@;
494             }
495 27         222 tie %self, $tiepkg;
496 27         95 $object = $Class::Prototyped::Mirror::objects{ $entity } = \%self;
497 27         139 tied(%self)->package($entity);
498              
499 27         61 my $defaults;
500 27 100       81 if ($entity eq 'Class::Prototyped') {
501 13         84 $defaults = {FIELD => undef, METHOD => undef, PARENT => undef};
502             }
503             else {
504 13     13   225 no strict 'refs';
  13         24  
  13         4967  
505 14         21 $defaults = eval { ${"$entity\::ISA"}[0]->reflect->_defaults() };
  14         25  
  14         74  
506 14 100       70 $defaults = Class::Prototyped->reflect->_defaults() if $@;
507             }
508              
509 27         57 tied(%self)->{defaults} = $defaults;
510              
511 27         79 bless $object, $entity;
512             }
513 37   100     351 return $Class::Prototyped::Mirror::mirrors{ $entity } ||= bless \$object, 'Class::Prototyped::Mirror';
514             }
515              
516              
517             #This code exists to support calling ->reflect->super on a "normal" object that
518             #is blessed into a C::P class.
519              
520             package Class::Prototyped::Mirror::Normal;
521             $Class::Prototyped::Mirror::Normal::VERSION = '1.13';
522             @Class::Prototyped::Mirror::Normal::ISA = qw(Class::Prototyped::Mirror);
523              
524             sub new {
525 2     2   3 my $class = shift;
526 2         5 my($entity) = @_;
527              
528 2         10 my $temp = Class::Prototyped::Mirror->new(ref($entity));
529              
530 2         5 my $self = bless \(my $o = ${$temp}), $class;
  2         8  
531 2         13 $Class::Prototyped::Mirror::Normal::superselfs->{$self} = $entity;
532 2         22 return $self;
533             }
534              
535             sub super {
536 1     1   2 my $mirror = shift;
537 1         7 (bless \$Class::Prototyped::Mirror::Normal::superselfs->{$mirror}, 'Class::Prototyped::Mirror')->super(@_);
538             }
539              
540             sub DESTROY {
541 2     2   664 delete $Class::Prototyped::Mirror::Normal::superselfs->{$_[0]};
542             }
543              
544             package Class::Prototyped::Mirror;
545              
546             #### Interface to tied object
547              
548             sub autoloadCall {
549 0     0   0 my $mirror = shift;
550              
551 0         0 my $package = $mirror->package();
552 13     13   100 no strict 'refs';
  13         27  
  13         20502  
553 0         0 my $call = ${"$package\::AUTOLOAD"};
  0         0  
554 0         0 $call =~ s/.*:://;
555 0         0 return $call;
556             }
557              
558             sub package {
559 1     1   1 ref(${ $_[0] });
  1         3  
560             }
561              
562             sub tiedInterfacePackage {
563 132     132   192 my $self = shift;
564              
565 132 100       324 if ($_[0]) {
566 2   33     15 my $package = {
567             'default' => 'Class::Prototyped::Tied::Default',
568             'autovivify' => 'Class::Prototyped::Tied::AutoVivify',
569             }->{$_[0]} || $_[0];
570              
571 2 50 33     11 if ($package eq $_[0] && scalar(keys %{"$package\::"}) == 0) {
  0         0  
572 0         0 eval "use $package";
573 0 0       0 Carp::croak "attempt to import package for :TIED_INTERFACE failed:\n$package"
574             if $@;
575             }
576              
577 2         3 tie %{ ${ $self } }, $package, tied(%{ ${ $self } });
  2         2  
  2         9  
  2         3  
  2         14  
578 2         6 return $package;
579             }
580             else {
581 130         173 return ref(tied(%{ ${ $self } }));
  130         180  
  130         1105  
582             }
583             }
584              
585             sub defaultAttributes {
586 4     4   5 my $mirror = shift;
587              
588 4 100       20 tied(%{ ${ $mirror } })->{defaults} = $_[0] if scalar(@_);
  1         2  
  1         3  
589 4         9 my $defaults = $mirror->_defaults;
590              
591 4         9 my $retval = {};
592 4 50       15 $retval->{FIELD} = defined $defaults->{FIELD} ? {%{$defaults->{FIELD}}} : undef;
  0         0  
593 4 100       12 $retval->{METHOD} = defined $defaults->{METHOD} ? {%{$defaults->{METHOD}}} : undef;
  1         6  
594 4 50       11 $retval->{PARENT} = defined $defaults->{PARENT} ? {%{$defaults->{PARENT}}} : undef;
  0         0  
595 4         18 return $retval;
596             }
597              
598             sub _isa {
599 0     0   0 tied(%{ ${ $_[0] } })->isa;
  0         0  
  0         0  
600             }
601              
602             sub _parentOrder {
603 0     0   0 my $tied = tied(%{ ${ $_[0] } });
  0         0  
  0         0  
604 0 0       0 $_[0]->_autovivify_parents unless $tied->{vivified_parents};
605 0         0 $tied->{parentOrder};
606             }
607              
608             sub _otherOrder {
609 105     105   114 my $tied = tied(%{ ${ $_[0] } });
  105         125  
  105         260  
610 105 50       315 $_[0]->_autovivify_methods unless $tied->{vivified_methods};
611 105         394 $tied->{otherOrder};
612             }
613              
614             sub _slotOrder {
615 0     0   0 my $tied = tied(%{ ${ $_[0] } });
  0         0  
  0         0  
616 0 0       0 $_[0]->_autovivify_parents unless $tied->{vivified_parents};
617 0 0       0 $_[0]->_autovivify_methods unless $tied->{vivified_methods};
618 0         0 [@{ $tied->{parentOrder} }, @{ $tied->{otherOrder} }];
  0         0  
  0         0  
619             }
620              
621             sub _slots {
622 53     53   82 my $tied = tied(%{ ${ $_[0] } });
  53         62  
  53         118  
623 53 50       145 $_[0]->_autovivify_parents unless $tied->{vivified_parents};
624 53 50       117 $_[0]->_autovivify_methods unless $tied->{vivified_methods};
625 53         159 $tied->{slots};
626             }
627              
628             sub _types {
629 3     3   6 tied(%{ ${ $_[0] } })->{types};
  3         5  
  3         21  
630             }
631              
632             sub _attribs {
633 9     9   13 tied(%{ ${ $_[0] } })->{attribs};
  9         12  
  9         34  
634             }
635              
636             sub _defaults {
637 138     138   304 tied(%{ ${ $_[0] } })->{defaults};
  138         204  
  138         520  
638             }
639              
640             sub _vivified_parents {
641 0         0 @_ > 1 ? tied(%{ ${ $_[0] } })->{vivified_parents} = $_[1] :
  0         0  
  0         0  
642 0 0   0   0 tied(%{ ${ $_[0] } })->{vivified_parents};
  0         0  
643             }
644              
645             sub _vivified_methods {
646 1         2 @_ > 1 ? tied(%{ ${ $_[0] } })->{vivified_methods} = $_[1] :
  1         8  
  0         0  
647 1 50   1   5 tied(%{ ${ $_[0] } })->{vivified_methods};
  0         0  
648             }
649              
650             #The following returns package, _isa, _parentOrder, _otherOrder,
651             #_slots, _types, _attribs, and _defaults;
652             sub _everything {
653 1064     1064   1246 my $tied = tied(%{ ${ $_[0] } });
  1064         1143  
  1064         3047  
654 1064 100       12141 $_[0]->_autovivify_parents unless $tied->{vivified_parents};
655 1064 100       2341 $_[0]->_autovivify_methods unless $tied->{vivified_methods};
656              
657             return (
658 1064         1180 ref(${ $_[0] }),
  1064         5300  
659             $tied->{isa},
660             $tied->{parentOrder},
661             $tied->{otherOrder},
662             $tied->{slots},
663             $tied->{types},
664             $tied->{attribs},
665             $tied->{defaults},
666             );
667             }
668              
669             #### Autovivifivation support
670              
671             sub _autovivify_parents {
672 75     75   87 my $tied = tied(%{ ${ $_[0] } });
  75         85  
  75         295  
673 75 100       255 return if $tied->{vivified_parents};
674              
675 6         9 my $mirror = shift;
676 6         11 $tied->{vivified_parents} = 1;
677              
678 6         30 my($package, $isa, $parentOrder, $otherOrder, $slots, $types, $attribs, undef) =
679             $mirror->_everything;
680              
681 6 50 66     26 if (scalar(grep { UNIVERSAL::isa($_, 'Class::Prototyped') } @$isa)
  10         74  
682             && $isa->[-1] ne 'Class::Prototyped')
683             {
684 0         0 push (@$isa, 'Class::Prototyped');
685 13     13   91 no strict 'refs';
  13         24  
  13         4494  
686 0         0 delete ${"$package\::"}{'::ISA::CACHE::'}; # re-cache @ISA
  0         0  
687 0         0 @$isa=@$isa;
688             }
689              
690 6 50       7 if (@{$parentOrder}) {
  6         24  
691 0         0 Carp::croak("attempt to autovivify in the "
692             . "presence of an existing parentOrder\n" . "package: $package");
693             }
694 6         29 my @isa = @$isa;
695 6 100 100     45 pop (@isa) if scalar(@isa) && $isa[-1] eq 'Class::Prototyped';
696              
697 6         18 foreach my $parentPackage (@isa) {
698 9         11 my $count = '';
699 9         14 my $slot = "$parentPackage$count*";
700 9   66     33 while (exists $slots->{$slot} || $slot eq 'self*') {
701 3         11 $slot = $parentPackage . (++$count) . '*';
702             }
703 9         16 push (@$parentOrder, $slot);
704 9         14 $slots->{$slot} = $parentPackage;
705 9         22 $types->{$slot} = 'PARENT';
706             }
707             }
708              
709             sub _autovivify_methods {
710 76     76   103 my $tied = tied(%{ ${ $_[0] } });
  76         83  
  76         225  
711 76 100       237 return if $tied->{vivified_methods};
712              
713 7         19 my $mirror = shift;
714 7         13 $tied->{vivified_methods} = 1;
715              
716 7         26 my($package, $isa, $parentOrder, $otherOrder, $slots, $types, $attribs, undef) =
717             $mirror->_everything;
718              
719 13     13   83 no strict 'refs';
  13         20  
  13         50932  
720 7         15 foreach my $slot (grep { $_ ne 'DESTROY' } keys %{"$package\::"}) {
  425         640  
  7         151  
721 424 100       453 my $code = *{"$package\::$slot"}{CODE} or next;
  424         1658  
722 356 100       704 ref($code) =~ /^Class::Prototyped::FieldAccessor/ and next;
723 355 50       614 Carp::croak("the slot self* is inviolable") if $slot eq 'self*';
724              
725 355 100       591 if (exists $slots->{$slot}) {
726 1 50 33     12 Carp::croak("you overwrote a slot via an include $slot")
727             if !UNIVERSAL::isa($slots->{$slot}, 'CODE')
728             || $slots->{$slot} != $code;
729             }
730             else {
731 354         502 push (@$otherOrder, $slot);
732 354         580 $slots->{$slot} = $code;
733 354         1141 $types->{$slot} = 'METHOD';
734             }
735             }
736             }
737              
738             sub object {
739 69     69   187 $_[0]->_autovivify_parents;
740 69         279 $_[0]->_autovivify_methods;
741 69         77 ${ $_[0] };
  69         149  
742             }
743              
744             sub class {
745 3     3   10 return $_[0]->_slots->{'class*'};
746             }
747              
748             sub dump {
749 0 0   0   0 eval "package main; use Data::Dumper;"
750             unless (scalar keys(%Data::Dumper::));
751              
752 0         0 Data::Dumper->Dump([ $_[0]->object ], [ $_[0]->package ]);
753             }
754              
755              
756             sub slotStruct_name () {0};
757             sub slotStruct_value () {1};
758             sub slotStruct_type () {2};
759             sub slotStruct_attribs () {3};
760             sub slotStruct_implementor () {4};
761             sub slotStruct_filters () {5};
762             sub slotStruct_advisories () {6};
763              
764              
765             #### The support for attribute rationalization is not very fancy
766             $Class::Prototyped::Mirror::attributes = {
767             FIELD => {
768             constant => {
769             type => 'implementor',
770             code => sub {
771             my($mirror, $slotName, $slotValue, $slotAttribs, $implementation, $slots) = @_;
772              
773             $slotAttribs->{constant} = 1;
774             return bless sub {
775             $slots->{$slotName};
776             }, 'Class::Prototyped::FieldAccessor::Constant';
777             }
778             },
779              
780             autoload => {
781             type => 'filter',
782             rank => 50,
783             code => sub {
784             my($mirror, $slotName, $slotValue, $slotAttribs, $implementation, $slots) = @_;
785              
786             if ($slotAttribs->{autoload} = $slotAttribs->{autoload} ? 1 : undef) {
787             my $self = $mirror->object;
788             $implementation = bless sub {
789             my $retval = &$slotValue;
790             my $attribs = $self->reflect->_attribs->{$slotName};
791             delete($attribs->{autoload});
792             $self->reflect->addSlot([$slotName, %$attribs] => $retval);
793             return $retval;
794             }, 'Class::Prototyped::FieldAccessor::Autoload';
795             }
796             return $implementation;
797             }
798             },
799              
800             profile => {
801             type => 'filter',
802             rank => 80,
803             code => sub {
804             my($mirror, $slotName, $slotValue, $slotAttribs, $implementation, $slots) = @_;
805              
806             my $profileLevel = $slotAttribs->{profile};
807             if ($profileLevel) {
808             package Class::Prototyped::Mirror::PROFILE;
809             my $old_implementation = $implementation;
810             my $package = ref( ${ $mirror } );
811             $implementation = sub {
812             my $caller = '';
813             if ($profileLevel == 2) {
814             my($pack, $file, $line) = caller;
815             $caller = "$file ($line)";
816             $Class::Prototyped::Mirror::PROFILE::counts->{$package}->{$slotName}->{$caller}++;
817             } else {
818             $Class::Prototyped::Mirror::PROFILE::counts->{$package}->{$slotName}++;
819             }
820             goto &$old_implementation;
821             };
822             }
823             return $implementation;
824             },
825             },
826              
827             'wantarray' => {
828             type => 'filter',
829             rank => 90,
830             code => sub {
831             my($mirror, $slotName, $slotValue, $slotAttribs, $implementation, $slots) = @_;
832              
833             if ($slotAttribs->{'wantarray'} = $slotAttribs->{'wantarray'} ? 1 : undef) {
834             my $old_implementation = $implementation;
835             $implementation = bless sub {
836             my $retval = &$old_implementation;
837             if (ref($retval) eq 'ARRAY' && wantarray) {
838             return (@$retval);
839             }
840             else {
841             return $retval;
842             }
843             }, 'Class::Prototyped::FieldAccessor::Wantarray';
844             }
845             return $implementation;
846             }
847             },
848              
849             description => {
850             type => 'advisory',
851             },
852             },
853              
854             METHOD => {
855             superable => {
856             type => 'filter',
857             rank => 10,
858             code => sub {
859             my($mirror, $slotName, $slotValue, $slotAttribs, $implementation, $slots) = @_;
860              
861             if ($slotAttribs->{superable} = $slotAttribs->{superable} ? 1 : undef) {
862             package Class::Prototyped::Mirror::SUPER;
863             my $old_implementation = $implementation;
864             my $package = ref( ${ $mirror } );
865             $implementation = sub {
866             local $Class::Prototyped::Mirror::SUPER::package =
867             $package;
868             &$old_implementation;
869             };
870             package Class::Prototyped::Mirror;
871             }
872             return $implementation;
873             }
874             },
875              
876             profile => {
877             type => 'filter',
878             rank => 90,
879             code => sub {
880             my($mirror, $slotName, $slotValue, $slotAttribs, $implementation, $slots) = @_;
881              
882             my $profileLevel = $slotAttribs->{profile};
883             if ($profileLevel) {
884             package Class::Prototyped::Mirror::PROFILE;
885             my $old_implementation = $implementation;
886             my $package = ref( ${ $mirror } );
887             $implementation = sub {
888             my $caller = '';
889             if ($profileLevel == 2) {
890             my($pack, $file, $line) = caller;
891             $caller = "$file ($line)";
892             $Class::Prototyped::Mirror::PROFILE::counts->{$package}->{$slotName}->{$caller}++;
893             } else {
894             $Class::Prototyped::Mirror::PROFILE::counts->{$package}->{$slotName}++;
895             }
896             goto &$old_implementation;
897             };
898             }
899             return $implementation;
900             },
901             },
902              
903             overload => {
904             type => 'advisory',
905             },
906              
907             description => {
908             type => 'advisory',
909             },
910             },
911              
912             PARENT => {
913             description => {
914             type => 'advisory',
915             },
916              
917             promote => {
918             type => 'advisory',
919             },
920             },
921             };
922              
923             sub addSlotsParser {
924 268     268   4026 my $mirror = shift;
925              
926 268 50       730 Carp::croak("odd number of arguments to addSlotsParser\n")
927             if scalar(@_) % 2;
928              
929 219         460 my($package, undef, undef, undef, $slots, undef, undef, $defaults) =
930             $mirror->_everything();
931              
932 219         456 my(@retvals);
933              
934 219         856 while (my($slotThing, $slotValue) = splice(@_, 0, 2)) {
935 411         473 my($slotName, $slotType, $slotAttribs, $slotImplementor, $slotFilters, $slotAdvisories);
936 411         1484 my $isCode = UNIVERSAL::isa($slotValue, 'CODE');
937              
938 411 100       857 if (ref($slotThing) eq 'ARRAY') {
939 163         319 $slotName = $slotThing->[0];
940              
941 163   100     390 my $temp = $slotThing->[1] || '';
942 163 100 100     916 if ($temp eq 'METHOD' || $temp eq 'FIELD' || $temp eq 'PARENT') {
      100        
943 123         149 $slotType = $temp;
944 123         192 $temp = 2;
945             }
946             else {
947 40 100       153 $slotType = $isCode ? 'METHOD' :
    100          
948             (substr($slotName, -1) eq '*' ? 'PARENT' : 'FIELD');
949 40         84 $temp = 1;
950             }
951              
952 163 100       181 if ($#{$slotThing} >= $temp) {
  163 50       527  
953 79 100       92 if ($#{$slotThing} == $temp) {
  79         200  
954 0         0 $slotAttribs = defined $defaults->{$slotType}
955 31 50       392 ? { %{$defaults->{$slotType}}, $slotThing->[$temp] => 1 }
956             : { $slotThing->[$temp] => 1 };
957             }
958             else {
959 0         0 $slotAttribs = defined $defaults->{$slotType}
960 0         0 ? { %{$defaults->{$slotType}}, @{$slotThing}[$temp..$#{$slotThing}] }
  0         0  
  48         155  
961 48 50       129 : { @{$slotThing}[$temp..$#{$slotThing}] };
  48         87  
962             }
963             }
964             elsif (defined $defaults->{$slotType}) {
965 0         0 $slotAttribs = { %{$defaults->{$slotType}} };
  0         0  
966             }
967              
968 163 100       502 if ($slotType eq 'METHOD') {
    100          
969 67 50       188 Carp::croak("it is not permitted to use '!' notation in conjunction with slot attributes")
970             if substr($slotName, -1) eq '!';
971              
972 67 100       281 Carp::croak("method slots have to have CODE refs as values")
973             if !$isCode;
974             }
975             elsif ($slotType eq 'PARENT') {
976 34 50       107 Carp::croak("it is not permitted to use '**' notation in conjunction with slot attributes")
977             if substr($slotName, -2, 1) eq '*';
978             }
979             }
980             else {
981 248         319 $slotName = $slotThing;
982 248 100       632 $slotType = $isCode ? 'METHOD' :
    100          
983             (substr($slotName, -1) eq '*' ? 'PARENT' : 'FIELD');
984              
985 248 100       757 if (defined $defaults->{$slotType}) {
986 1         2 $slotAttribs = { %{$defaults->{$slotType}} };
  1         4  
987             }
988              
989             # Slots that end in '!' mean that the method is superable
990 248 100 100     1002 if ($slotType eq 'METHOD' && substr($slotName, -1) eq '!') {
991 35         52 $slotName = substr($slotName, 0, -1);
992 35         90 $slotAttribs->{superable} = 1;
993             }
994              
995             # Temporary support for &
996 248 50 66     977 if ($slotType eq 'FIELD' && substr($slotName, -1) eq '&') {
997 0         0 $slotName = substr($slotName, 0, -1);
998 0         0 $slotAttribs->{constant} = 1;
999             }
1000              
1001             # Slots that end in '**' mean to push the slot
1002             # to the front of the parents list.
1003 248 100 100     808 if ($slotType eq 'PARENT' && substr($slotName, -2) eq '**') {
1004 4         7 $slotName = substr($slotName, 0, -1); # xyz** => xyz*
1005 4         10 $slotAttribs->{promote} = 1;
1006             }
1007             }
1008              
1009 410 50 66     1394 if ($slotType eq 'METHOD' && exists($Class::Prototyped::overloadable_symbols{$slotName})) {
1010 0         0 $slotAttribs->{overload} = 1;
1011             }
1012             else {
1013 410 50 33     1157 Carp::croak("can't use slot attribute overload for slots that aren't overloadable")
1014             if ($slotAttribs->{overload} && !exists($Class::Prototyped::overloadable_symbols{$slotName}));
1015             }
1016              
1017 410 100 66     1788 Carp::croak("slots should end in * if and only if the type is parent")
1018             if ( (substr($slotName, -1) eq '*') != ($slotType eq 'PARENT') && !$slotAttribs->{overload} );
1019              
1020 407 100       939 if ($slotName eq '*') {
1021 10   66     37 $slotName = (ref($slotValue) || $slotValue) . $slotName;
1022             }
1023              
1024 407 100       546 if(scalar(keys(%{$slotAttribs}))) {
  407         1597  
1025 119         2009 my $attributes = $Class::Prototyped::Mirror::attributes->{$slotType};
1026              
1027 119         182 foreach my $attrib (keys %{$slotAttribs}) {
  119         307  
1028 121 100       1076 Carp::croak("$slotType slots cannot have the '$attrib' attribute.")
1029             unless exists $attributes->{$attrib};
1030              
1031 115         231 my $atype = $attributes->{$attrib}->{type};
1032 115 100       406 if ($atype eq 'filter') {
    100          
    50          
1033 77         82 push(@{$slotFilters}, $attrib);
  77         309  
1034             }
1035             elsif ($atype eq 'advisory') {
1036 22         27 push(@{$slotAdvisories}, $attrib);
  22         97  
1037             }
1038             elsif ($atype eq 'implementor') {
1039 16 50       33 Carp::croak("slots cannot have more than one implementor.")
1040             if defined($slotImplementor);
1041 16 50       86 $slotImplementor = $attributes->{$attrib}->{code} if $slotAttribs->{$attrib};
1042             }
1043             else {
1044 0         0 Carp::croak("unknown attribute type '$atype' for '$attrib'.");
1045             }
1046             }
1047              
1048 113 100       337 if (defined $slotFilters) {
1049 76 50       166 @{$slotFilters} = map { $attributes->{$_}->{code} } sort {
  77         211  
  1         8  
1050 76         178 $attributes->{$a}->{rank} <=> $attributes->{$b}->{rank} || $a cmp $b
1051 76         96 } @{$slotFilters};
1052             }
1053              
1054 113 100       308 if (defined $slotAdvisories) {
1055 22         25 @{$slotAdvisories} = grep {defined} map { $attributes->{$_}->{code} } sort @{ $slotAdvisories };
  22         49  
  22         49  
  22         60  
  22         50  
1056             }
1057             }
1058              
1059 401 50       1112 Carp::croak("the slot self* is inviolable") if $slotName eq 'self*';
1060              
1061 401 50 33     1011 Carp::croak("Can only use operator names for method slots\nslot: $slotName")
1062             if ( exists($Class::Prototyped::overloadable_symbols{$slotName}) &&
1063             $slotType ne 'METHOD' );
1064              
1065 401 100       1282 if ($slotType eq 'PARENT') {
    100          
1066 96 50       284 Carp::croak("parent slots cannot be code blocks") if ($isCode);
1067              
1068 96 50 33     417 unless (UNIVERSAL::isa($slotValue, 'Class::Prototyped')
      66        
1069             || (ref(\$slotValue) eq 'SCALAR' && defined $slotValue))
1070             {
1071 0         0 Carp::croak("attempt to add parent that isn't a "
1072             . "Class::Prototyped or package name\n"
1073             . "package: $package slot: $slotName parent: $slotValue");
1074             }
1075              
1076 96 50       473 if (UNIVERSAL::isa($slotValue, $package)) {
1077 0         0 Carp::croak("attempt at recursive inheritance\n"
1078             . "parent $slotValue is a package $package");
1079             }
1080             }
1081             elsif ($slotType eq 'METHOD') {
1082 169 50 33     405 Carp::croak("cannot replace DESTROY method for unnamed objects")
1083             if ($slotName eq 'DESTROY' && substr($package, 0, PREFIX_LENGTH) eq PREFIX);
1084             }
1085              
1086 401         2618 push(@retvals, [$slotName, $slotValue, $slotType, $slotAttribs, $slotImplementor, $slotFilters, $slotAdvisories]);
1087             }
1088 209         693 return \@retvals;
1089             }
1090              
1091             sub addParsedSlots {
1092 178     178   252 my $mirror = shift;
1093              
1094 178         371 my($package, $isa, $parentOrder, $otherOrder, $slots, $types, $attribs, undef) =
1095             $mirror->_everything();
1096              
1097 178         314 while (@{$_[0]}) {
  579         1459  
1098 401         540 my($slotName, $slotValue, $slotType, $slotAttribs, $slotImplementor, $slotFilters, $slotAdvisories) = @{ shift @{$_[0]} };
  401         504  
  401         1218  
1099              
1100 401 100       7667 &deleteSlots($mirror, $slotName) if exists($slots->{$slotName});
1101              
1102 401         826 $slots->{$slotName} = $slotValue; #everything goes into the slots!!!!!
1103              
1104 401 100       799 if ($slotType eq 'PARENT') {
1105 96   66     278 my $parentPackage = ref($slotValue) || $slotValue;
1106              
1107 96 100       271 if (substr($parentPackage, 0, PREFIX_LENGTH) eq PREFIX) {
1108 58         178 $Class::Prototyped::Mirror::parents{$package}->{$slotName} = $slotValue;
1109             }
1110             else {
1111 38 50       267 Carp::carp(
1112             "it is recommended to use ->reflect->include for mixing in named files."
1113             )
1114             if $parentPackage =~ /\.p[lm]$/i;
1115              
1116 13     13   155 no strict 'refs';
  13         60  
  13         1331  
1117 38 50 33     95 if (!ref($slotValue)
  38         150  
1118             && !(scalar keys(%{"$parentPackage\::"})))
1119             {
1120 0         0 $mirror->include($parentPackage);
1121             }
1122             }
1123              
1124 96 100       221 my $splice_point = $slotAttribs->{promote} ? 0 : @$parentOrder;
1125 96         157 delete $slotAttribs->{promote};
1126 96         1004 splice(@$isa, $splice_point, 0, $parentPackage);
1127             {
1128             #Defends against ISA caching problems
1129 13     13   67 no strict 'refs';
  13         25  
  13         3733  
  96         210  
1130 96         111 delete ${"$package\::"}{'::ISA::CACHE::'};
  96         320  
1131 96         1645 @$isa = @$isa;
1132             }
1133 96         347 splice(@$parentOrder, $splice_point, 0, $slotName);
1134             }
1135             else {
1136             my $implementation = defined $slotImplementor
1137             ? $slotImplementor->($mirror, $slotName, $slotValue, $slotAttribs, undef, $slots)
1138             : ( $slotType eq 'METHOD'
1139             ? $slotValue
1140             : bless sub {
1141 132 100   132   2007776 @_ > 1 ? $slots->{$slotName} = $_[1] : $slots->{$slotName};
1142 305 100       1498 }, 'Class::Prototyped::FieldAccessor'
    100          
1143             );
1144              
1145 305 100       669 if (defined $slotFilters) {
1146 76         88 foreach my $filter (@{ $slotFilters }) {
  76         135  
1147 77         271 $implementation = $filter->($mirror, $slotName, $slotValue, $slotAttribs, $implementation, $slots);
1148             }
1149             }
1150              
1151 305 100       624 if (defined $slotAdvisories) {
1152 4         5 foreach my $advisory (@{ $slotAdvisories }) {
  4         9  
1153 0         0 $advisory->($mirror, $slotName, $slotValue, $slotAttribs, $implementation, $slots);
1154             }
1155             }
1156              
1157 305 50       694 if ($slotAttribs->{overload}) {
1158 0         0 eval "package $package;
1159             use overload '$slotName' => \$implementation, fallback => 1;
1160             bless \$object, \$package;";
1161 0 0       0 Carp::croak("Eval failed while defining overload\n"
1162             . "operation: \"$slotName\" error: $@")
1163             if $@;
1164             }
1165             else {
1166 13     13   73 no strict 'refs';
  13         25  
  13         4685  
1167 305         939 local $^W = 0; # suppress redefining messages.
1168 305         348 *{"$package\::$slotName"} = $implementation;
  305         1831  
1169             }
1170 305         715 push (@$otherOrder, $slotName);
1171             }
1172 401         901 $attribs->{$slotName} = $slotAttribs;
1173 401         1303 $types->{$slotName} = $slotType;
1174             }
1175              
1176 178         487 return $mirror;
1177             }
1178              
1179             sub addSlots {
1180 92     92   158 my $mirror = shift;
1181 92         315 $mirror->addParsedSlots( $mirror->addSlotsParser(@_) );
1182             }
1183              
1184             *addSlot = \&addSlots; # alias addSlot to addSlots
1185              
1186             # $obj->reflect->deleteSlots( name [, name [...]] );
1187             sub deleteSlots {
1188 239     239   320 my $mirror = shift;
1189 239         535 my (@deleteSlots) = @_;
1190              
1191 239         500 my($package, $isa, $parentOrder, $otherOrder, $slots, $types, $attribs, undef) =
1192             $mirror->_everything;
1193              
1194 239         574 foreach my $slot (@deleteSlots) {
1195 390 50       1019 $slot = substr($slot, 0, -1) if substr($slot, -2) eq '**';
1196 390 50       842 $slot = substr($slot, 0, -1) if substr($slot, -1) eq '!';
1197              
1198 390 50       1010 next if !exists($slots->{$slot});
1199              
1200 390         631 my $value = $slots->{$slot};
1201              
1202 390 100       816 if (substr($slot, -1) eq '*') { # parent slot
1203 94         126 my $index = 0;
1204 94   33     285 1 while ($parentOrder->[$index] ne $slot
1205             and $index++ < @$parentOrder);
1206              
1207 94 50       188 if ($index < @$parentOrder) {
1208 94         155 splice(@$parentOrder, $index, 1);
1209 94         856 splice(@$isa, $index, 1);
1210             {
1211             #Defends against ISA caching problems
1212 13     13   73 no strict 'refs';
  13         27  
  13         4057  
  94         259  
1213 94         188 delete ${"$package\::"}{'::ISA::CACHE::'};
  94         306  
1214 94         1526 @$isa=@$isa;
1215             }
1216             }
1217             else { # not found
1218              
1219 0 0       0 if (!$Class::Prototyped::Mirror::ending) {
1220 0         0 Carp::cluck "couldn't find $slot in $package\n";
1221 0         0 $DB::single = 1;
1222             }
1223             }
1224              
1225 94 50       276 if (defined($value)) {
1226 94         150 my $parentPackage = ref($value);
1227 94 100       407 if (substr($parentPackage, 0, PREFIX_LENGTH) eq PREFIX) {
1228             delete
1229 58         159 ($Class::Prototyped::Mirror::parents{$package}->{$slot}
1230             );
1231             }
1232             }
1233             else {
1234              
1235 0 0       0 if (!$Class::Prototyped::Mirror::ending) {
1236 0         0 Carp::cluck "slot undef for $slot in $package\n";
1237 0         0 $DB::single = 1;
1238             }
1239             }
1240             }
1241             else {
1242              
1243 296 50       2988 if (exists($Class::Prototyped::overloadable_symbols{$slot})) {
1244 0         0 Carp::croak(
1245             "Perl segfaults when the last overload is removed. Boom!\n")
1246             if (1 == grep {
1247 0 0       0 exists($Class::Prototyped::overloadable_symbols{$_});
1248             } keys(%$slots));
1249              
1250 0         0 eval "package $package;
1251             no overload '$slot';
1252             bless {}, \$package;"
1253             ; # dummy bless so that overloading works.
1254 0 0       0 Carp::croak("Eval failed while removing overload\n"
1255             . "operation: \"$slot\" error: $@")
1256             if $@;
1257             }
1258             else { # we have a method by that name; delete it
1259 13     13   74 no strict 'refs';
  13         28  
  13         11182  
1260 296         632 my $name = "$package\::$slot";
1261              
1262             # save the glob...
1263 296         330 local *old = *{$name};
  296         1455  
1264              
1265             # and restore everything else
1266 296         1229 local *new;
1267 296         585 foreach my $type (qw(HASH IO FORMAT SCALAR ARRAY)) {
1268 1480         1821 my $elem = *old{$type};
1269 1480 100       3236 next if !defined($elem);
1270 296         503 *new = $elem;
1271             }
1272 296         406 *{$name} = *new;
  296         2527  
1273             }
1274 296         658 @$otherOrder = grep { $_ ne $slot } @$otherOrder;
  958         2340  
1275             }
1276 390         731 delete $slots->{$slot}; # and delete the data/sub ref
1277 390         578 delete $types->{$slot};
1278 390         1361 delete $attribs->{$slot};
1279             }
1280              
1281 239         812 return $mirror;
1282             }
1283              
1284             *deleteSlot = \&deleteSlots; # alias deleteSlot to deleteSlots
1285              
1286             sub super_slow {
1287 104 100   104   629 return shift->super_fast(@_)
1288             if ((caller(1))[0] eq 'Class::Prototyped::Mirror::SUPER');
1289 90 50       587 return shift->super_fast(@_)
1290             if ((caller(2))[0] eq 'Class::Prototyped::Mirror::SUPER');
1291 0         0 Carp::croak(
1292             "attempt to call super on a method that was defined without !\n"
1293             . "method: " . $_[1]);
1294             }
1295              
1296             *super = \&super_slow unless defined(*super{CODE});
1297              
1298             sub super_fast {
1299 104     104   133 my $mirror = shift;
1300 104         120 my $message = shift;
1301              
1302 104 50       202 $message or Carp::croak("you have to pass the method name to super");
1303              
1304 104         106 my $object = ${ $mirror };
  104         159  
1305              
1306 104         116 my (@isa);
1307             {
1308 13     13   79 no strict 'refs';
  13         27  
  13         15068  
  104         107  
1309 104         102 @isa = @{ $Class::Prototyped::Mirror::SUPER::package . '::ISA' };
  104         411  
1310             }
1311 104         109 my $method;
1312              
1313 104         155 foreach my $parentPackage (@isa) {
1314 104         329 $method = UNIVERSAL::can($parentPackage, $message);
1315 104 50       239 last if $method;
1316             }
1317             $method
1318 104 50       184 or Carp::croak("could not find super in parents\nmessage: $message");
1319 104         207 $method->($object, @_);
1320             }
1321              
1322             sub slotNames {
1323 210     210   4798 my $mirror = shift;
1324 210         314 my $type = shift;
1325              
1326 210         493 my($package, $isa, $parentOrder, $otherOrder, $slots, $types, $attribs, undef) =
1327             $mirror->_everything;
1328              
1329 210         749 my @slotNames = (@$parentOrder, @$otherOrder);
1330 210 100       513 if ($type) {
1331 152         277 @slotNames = grep { $types->{$_} eq $type } @slotNames;
  346         1055  
1332             }
1333 210 50       1285 return wantarray ? @slotNames : \@slotNames;
1334             }
1335              
1336             sub slotType {
1337 3     3   7 my $mirror = shift;
1338 3         6 my $slotName = shift;
1339              
1340 3         9 my $types = $mirror->_types;
1341 3 50       15 Carp::croak(
1342             "attempt to determine slotType for unknown slot\nslot: $slotName")
1343             unless exists $types->{$slotName};
1344 3         16 return $types->{$slotName};
1345             }
1346              
1347             # may return dups
1348             sub allSlotNames {
1349 4     4   8 my $mirror = shift;
1350 4         8 my $type = shift;
1351              
1352 4         8 my @slotNames;
1353 4         14 foreach my $parent ($mirror->withAllParents()) {
1354 6         30 my $mirror = Class::Prototyped::Mirror->new($parent);
1355 6         34 push (@slotNames, $mirror->slotNames($type));
1356             }
1357 4 50       30 return wantarray ? @slotNames : \@slotNames;
1358             }
1359              
1360             sub parents {
1361 28     28   41 my $mirror = shift;
1362              
1363 28         56 my $object = $mirror->object;
1364 28         67 my $slots = $mirror->_slots;
1365 28         70 return map { $slots->{$_} } $mirror->slotNames('PARENT');
  16         61  
1366             }
1367              
1368             sub allParents {
1369 24     24   38 my $mirror = shift;
1370 24   100     73 my $retval = shift || [];
1371 24   100     58 my $seen = shift || {};
1372              
1373 24         58 foreach my $parent ($mirror->parents) {
1374 12 50       352 next if $seen->{$parent}++;
1375 12         23 push @$retval, $parent;
1376 12         31 my $mirror = Class::Prototyped::Mirror->new($parent);
1377 12         37 $mirror->allParents($retval, $seen);
1378             }
1379 24 100       142 return wantarray ? @$retval : $retval;
1380             }
1381              
1382             sub withAllParents {
1383 8     8   14 my $mirror = shift;
1384              
1385 8         24 my $object = $mirror->object;
1386 8         24 my $retval = [$object];
1387 8         29 my $seen = { $object => 1 };
1388 8         61 $mirror->allParents($retval, $seen);
1389             }
1390              
1391             # getSlot returns both the slotName and the slot in array context
1392             # so that it can append !'s to superable methods, so that getSlots does the
1393             # right thing, so that clone does the right thing.
1394             # However, in scalar context, it just returns the value.
1395              
1396             sub getSlot {
1397 183     183   1177 my $mirror = shift;
1398 183         220 my $slot = shift;
1399 183         201 my $format = shift;
1400              
1401 183         336 my($package, $isa, $parentOrder, $otherOrder, $slots, $types, $attribs, undef) =
1402             $mirror->_everything;
1403              
1404 183 50       641 my $value = ($slot ne 'self*') ? $slots->{$slot} : $mirror->object;
1405              
1406 183 100       700 return $value unless wantarray;
1407              
1408 150 100       201 $slot = [$slot, $types->{$slot}, %{$attribs->{$slot} || {}}];
  150         589  
1409              
1410 150 50 33     1078 if (!defined $format || $format eq 'default') {
    100          
    50          
1411 0         0 return ($slot, $value);
1412             }
1413             elsif ($format eq 'simple') {
1414 2         10 return ($slot->[0], $value);
1415             }
1416             elsif ($format eq 'rotated') {
1417 148         1199 return ($slot->[0], {
1418 148         240 attribs => { @{$slot}[2..$#{$slot}] },
  148         242  
1419             type => $slot->[1],
1420             value => $value
1421             }
1422             );
1423             }
1424             }
1425              
1426             sub getSlots {
1427 46     46   1014 my $mirror = shift;
1428 46         64 my $type = shift;
1429 46         71 my $format = shift;
1430              
1431 46         62 my @retval;
1432 46 100 66     256 if (defined $type || defined $format) {
1433 24         86 @retval = map { $mirror->getSlot($_, $format) } $mirror->slotNames($type);
  150         338  
1434             }
1435             else {
1436 22         71 my($package, $isa, $parentOrder, $otherOrder, $slots, $types, $attribs, undef) =
1437             $mirror->_everything;
1438 102 100       569 @retval = map {
1439 22         67 ([$_, $types->{$_}, %{$attribs->{$_} || {}}] => $slots->{$_})
  102         154  
1440             } (@$parentOrder, @$otherOrder);
1441             }
1442              
1443 46 50       473 return wantarray ? @retval : \@retval;
1444             }
1445              
1446             sub promoteParents {
1447 0     0   0 my $mirror = shift;
1448 0         0 my (@newOrder) = @_;
1449              
1450 0         0 my($package, $isa, $parentOrder, $otherOrder, $slots, $types, $attribs, undef) =
1451             $mirror->_everything;
1452              
1453 0         0 my %seen;
1454 0         0 foreach my $slot (@newOrder) {
1455 0         0 $seen{$slot}++;
1456 0 0 0     0 if ($seen{$slot} > 1 || !exists($slots->{$slot})) {
1457 0         0 Carp::croak("promoteParents called with bad order list\nlist: @_");
1458             }
1459             else {
1460 0         0 @{$parentOrder} = grep { $_ ne $slot } @{$parentOrder};
  0         0  
  0         0  
  0         0  
1461             }
1462             }
1463              
1464 0         0 @{$parentOrder} = (@newOrder, @{$parentOrder});
  0         0  
  0         0  
1465              
1466 0 0       0 @$isa =
1467 0         0 ((map { ref($slots->{$_}) ? ref($slots->{$_}) : $slots->{$_} }
1468 0         0 @{$parentOrder}), 'Class::Prototype');
1469              
1470             # this is required to re-cache @ISA
1471 13     13   123 no strict 'refs';
  13         27  
  13         14324  
1472 0         0 delete ${"$package\::"}{'::ISA::CACHE::'};
  0         0  
1473 0         0 @$isa=@$isa;
1474             }
1475              
1476             sub wrap {
1477 3     3   5 my $mirror = shift;
1478 3   50     8 my $class = $mirror->class || 'Class::Prototyped';
1479 3         10 my $wrapped = $class->new;
1480 3         21 my $wrappedMirror = $wrapped->reflect;
1481              
1482             # add all the slots from the original object
1483 3         8 $wrappedMirror->addSlots($mirror->getSlots);
1484              
1485             # delete all my original slots
1486             # so that the wrapped gets called
1487 3         13 $mirror->deleteSlots($mirror->slotNames);
1488 3         10 $mirror->addSlots(@_, [qw(wrapped* promote)] => $wrapped);
1489 3         14 $mirror;
1490             }
1491              
1492             sub unwrap {
1493 2     2   11 my $mirror = shift;
1494 2 50       5 my $wrapped = $mirror->getSlot('wrapped*')
1495             or Carp::croak "unwrapping without a wrapped\n";
1496 2         7 my $wrappedMirror = $wrapped->reflect;
1497 2         4 $mirror->deleteSlots($mirror->slotNames);
1498 2         5 $mirror->addSlots($wrappedMirror->getSlots);
1499              
1500             # $wrappedMirror->deleteSlots( $wrappedMirror->slotNames );
1501 2         11 $mirror;
1502             }
1503              
1504             sub delegate {
1505 4     4   7 my $mirror = shift;
1506              
1507 4         124 while (my ($name, $value) = splice(@_, 0, 2)) {
1508 10 50       58 my @names = (UNIVERSAL::isa($name, 'ARRAY') ? @$name : $name);
1509 10         12 my @conflicts;
1510              
1511 10         16 foreach my $slotName (@names) {
1512 10         23 push (@conflicts, grep { $_ eq $slotName } $mirror->slotNames);
  73         229  
1513             }
1514             Carp::croak(
1515 10 100       133 "delegate would cause conflict with existing slots\n" . "pattern: "
1516             . join ('|', @names) . " , conflicting slots: "
1517             . join (', ', @conflicts))
1518             if @conflicts;
1519              
1520 9         12 my $delegateMethod;
1521 9 100       35 if (UNIVERSAL::isa($value, 'ARRAY')) {
1522 5         10 $delegateMethod = $value->[1];
1523 5         10 $value = $value->[0];
1524             }
1525 9   66     22 my $delegate = $mirror->getSlot($value) || $value;
1526 9 100       272 Carp::croak("Can't delegate to a subroutine\nslot: $name")
1527             if (UNIVERSAL::isa($delegate, 'CODE'));
1528              
1529 8         14 foreach my $slotName (@names) {
1530 8 100       25 my $method = defined($delegateMethod) ? $delegateMethod : $slotName;
1531             $mirror->addSlot(
1532             $slotName => sub {
1533 7     7   11 shift; # discard original recipient
1534 7         26 $delegate->$method(@_);
1535             }
1536 8         114 );
1537             }
1538             }
1539             }
1540              
1541             sub findImplementation {
1542 22     22   72 my $mirror = shift;
1543 22         40 my $slotName = shift;
1544              
1545 22         48 my $object = $mirror->object;
1546 22 50       124 UNIVERSAL::can($object, $slotName) or return;
1547              
1548 22         49 my $slots = $mirror->_slots;
1549 22 100       119 exists $slots->{$slotName} and return wantarray ? 'self*' : $object;
    100          
1550              
1551 8         32 foreach my $parentName ($mirror->slotNames('PARENT')) {
1552 8         26 my $mirror =
1553             Class::Prototyped::Mirror->new(
1554             scalar($mirror->getSlot($parentName)));
1555 8 100       21 if (wantarray) {
1556 4         18 my (@retval) = $mirror->findImplementation($slotName);
1557 4 50       34 scalar(@retval) and return ($parentName, @retval);
1558             }
1559             else {
1560 4         12 my $retval = $mirror->findImplementation($slotName);
1561 4 50       85 $retval and return $retval;
1562             }
1563             }
1564 0         0 Carp::croak("fatal error in findImplementation");
1565             }
1566              
1567             # load the given file or package in the receiver's namespace
1568             # Note that no import is done.
1569             # Croaks on an eval error
1570             #
1571             # $mirror->include('Package');
1572             # $mirror->include('File.pl');
1573             #
1574             # $mirror->include('File.pl', 'thisObject');
1575             # makes thisObject() return the object into which the include
1576             # is happening (as long as you don't change packages in the
1577             # included code)
1578             sub include {
1579 1     1   3 my $mirror = shift;
1580 1         2 my $name = shift;
1581 1         2 my $accessorName = shift;
1582              
1583 1 50       9 $name = "'$name'" if $name =~ /\.p[lm]$/i;
1584              
1585 1         5 my $object = $mirror->object;
1586 1         6 my $package = $mirror->package;
1587 1         3 my $text = "package $package;\n";
1588 1 50       6 $text .= "*$package\::$accessorName = sub { \$object };\n"
1589             if defined($accessorName);
1590              
1591             # $text .= "sub $accessorName { \$object };\n" if defined($accessorName);
1592 1         3 $text .= "require $name;\n";
1593 1         115 my $retval = eval $text;
1594 1 50       5 Carp::croak("include failed\npackage: $package include: $name error: $@")
1595             if $@;
1596              
1597 1 50       4 if (substr($name, -1) eq "'") {
1598 1         5 $mirror->_vivified_methods(0);
1599 1         4 $mirror->_autovivify_methods;
1600             }
1601              
1602 1 50       7 $mirror->deleteSlots($accessorName) if defined($accessorName);
1603             }
1604              
1605             1;
1606             __END__