File Coverage

blib/lib/Object/InsideOut.pm
Criterion Covered Total %
statement 1366 1704 80.1
branch 659 944 69.8
condition 160 282 56.7
subroutine 94 101 93.0
pod 9 16 56.2
total 2288 3047 75.0


line stmt bran cond sub pod time code
1             package Object::InsideOut; {
2              
3             require 5.006;
4              
5 66     66   729899 use strict;
  66         114  
  63         1391  
6 63     63   259 use warnings;
  62         176  
  62         2392  
7              
8             our $VERSION = '4.03';
9             $VERSION = eval $VERSION;
10              
11 56     62   18711 use Object::InsideOut::Exception 4.03;
  56         912  
  56         1861  
12 56     56   24579 use Object::InsideOut::Util 4.03 qw(create_object hash_re is_it make_shared);
  56         824  
  56         218  
13 56     56   262 use Object::InsideOut::Metadata 4.03;
  56         631  
  56         243  
14              
15             require B;
16              
17 56     56   204 use Scalar::Util 1.10;
  56         806  
  56         14128  
18             if (! Scalar::Util->can('weaken')) {
19             OIO->Trace(0);
20             OIO::Code->die(
21             'message' => q/Cannot use 'pure perl' version of Scalar::Util - 'weaken' missing/,
22             'Info' => 'Upgrade/reinstall your version of Scalar::Util');
23             }
24              
25              
26             ### Global Data ###
27              
28             my %GBL;
29             if (! exists($GBL{'GBL_SET'})) {
30             %GBL = (
31             'GBL_SET' => 1, # Control flag for initializing this hash
32              
33             %GBL, # Contains 'perm', 'merge', 'attr', 'meta'
34             # from compilation phase
35              
36             init => 1, # Initialization flag
37             # term # Termination flag
38              
39             export => [ # Exported subroutines (i.e., @EXPORT)
40             qw(new clone meta set DESTROY)
41             ],
42              
43             tree => { # Class trees
44             td => {}, # Top down
45             bu => {}, # Bottom up
46             },
47              
48             asi => {}, # Reverse 'isa'
49              
50             id => {
51             obj => {}, # Object IDs
52             reuse => {}, # Reclaimed obj IDs
53             },
54              
55             fld => {
56             ref => {}, # :Field
57             # new
58             type => {}, # :Type
59             weak => {}, # :Weak
60             deep => {}, # :Deep
61             def => {}, # :Default
62              
63             regen => { # Fix field keys during CLONE
64             type => [],
65             weak => [],
66             deep => [],
67             },
68             },
69             hash_only => {}, # :Hash_Only
70              
71             args => {}, # :InitArgs
72              
73             sub => {
74             id => {}, # :ID
75             init => {}, # :Init
76             pre => {}, # :PreInit
77             repl => {}, # :Replicate
78             dest => {}, # :Destroy
79             auto => {}, # :Automethod
80             # cumu # :Cumulative
81             # chain # :Chained
82             # ol # :*ify (overload)
83             },
84              
85             dump => {
86             dumper => {}, # :Dumper
87             pumper => {}, # :Pumper
88             fld => {}, # Field info
89             args => [], # InitArgs info
90             },
91              
92             heritage => {}, # Foreign class inheritance data
93              
94             # Currently executing thread
95             tid => (($threads::threads) ? threads->tid() : 0),
96             # pids # Pseudo-forks
97              
98             obj => {}, # Object registry for thread cloning
99              
100             share => { # Object sharing between threads
101             cl => {},
102             ok => $threads::shared::threads_shared,
103             # obj # Tracks TIDs for shared objects
104             },
105              
106             # cache # Object initialization activity cache
107             );
108              
109             # Add metadata
110             $GBL{'meta'}{'add'}{'Object::InsideOut'} = {
111             'import' => {'hidden' => 1},
112             'MODIFY_CODE_ATTRIBUTES' => {'hidden' => 1},
113             'inherit' => {'restricted' => 1},
114             'disinherit' => {'restricted' => 1},
115             'heritage' => {'restricted' => 1},
116             };
117             }
118              
119              
120             ### Import ###
121              
122             # Doesn't export anything - just builds class trees and handles module flags
123             sub import
124             {
125 194     194   24638 my $self = shift; # Ourself (i.e., 'Object::InsideOut')
126 194 50       560 if (Scalar::Util::blessed($self)) {
127 0         0 OIO::Method->die('message' => q/'import' called as an object method/);
128             }
129              
130             # Invoked via inheritance - ignore
131 194 50       401 if ($self ne 'Object::InsideOut') {
132 0 0       0 if (Exporter->can('import')) {
133 0         0 my $lvl = $Exporter::ExportLevel;
134 0 0       0 $Exporter::ExportLevel = (caller() eq 'Object::InsideOut') ? 3 : 1;
135 0         0 $self->Exporter::import(@_);
136 0         0 $Exporter::ExportLevel = $lvl;
137             }
138 0         0 return;
139             }
140              
141 194         250 my $class = caller(); # The class that is using us
142 194 50 33     823 if (! $class || $class eq 'main') {
143 0         0 OIO::Code->die(
144             'message' => q/'import' invoked from 'main'/,
145             'Info' => "Can't use 'use Object::InsideOut;' or 'Object::InsideOut->import();' inside application code");
146             }
147              
148 56     56   247 no strict 'refs';
  56         70  
  54         3399  
149              
150             # Check for class's global sharing flag
151             # (normally set in the app's main code)
152 194 50       175 if (defined(${$class.'::shared'})) {
  194         1084  
153 0         0 set_sharing($class, ${$class.'::shared'}, (caller())[1..2]);
  0         0  
154             }
155              
156             # Check for class's global 'storable' flag
157             # (normally set in the app's main code)
158             {
159 54     56   212 no warnings 'once';
  54         66  
  54         118925  
  194         173  
160 194 100       157 if (${$class.'::storable'}) {
  194         691  
161 1         3 push(@_, 'Storable');
162             }
163             }
164              
165             # Import packages and handle :SHARED flag
166 194         181 my @packages;
167 194         402 while (my $pkg = shift) {
168 141 50       251 next if (! $pkg); # Ignore empty strings and such
169              
170             # Handle thread object sharing flag
171 141 50       319 if ($pkg =~ /^:(NOT?_?|!)?SHAR/i) {
172 0 0       0 my $sharing = (defined($1)) ? 0 : 1;
173 0         0 set_sharing($class, $sharing, (caller())[1..2]);
174 0         0 next;
175             }
176              
177             # Handle hash fields only flag
178 141 100       300 if ($pkg =~ /^:HASH/i) {
179 3         11 $GBL{'hash_only'}{$class} = [ $class, (caller())[1,2] ];
180 3         8 next;
181             }
182              
183             # Restricted class
184 138 100       251 if ($pkg =~ /^:RESTRICT(?:ED)?(?:\((.*)\))?/i) {
185 2         8 *{$class.'::new'}
186             = wrap_RESTRICTED($class, 'new',
187 3     3   6 sub { goto &Object::InsideOut::new },
188 2   100     19 [ grep {$_} split(/[,'\s]+/, $1 || '') ]);
  1         5  
189 2         9 $GBL{'meta'}{'add'}{$class}{'new'} = { 'kind' => 'constructor',
190             'merge_args' => 1,
191             'restricted' => 1 };
192 2         6 next;
193             }
194              
195             # Private class
196 136 100       304 if ($pkg =~ /^:PRIV(?:ATE)?(?:\((.*)\))?/i) {
197 1         8 *{$class.'::new'}
198             = wrap_PRIVATE($class, 'new',
199 2     2   3 sub { goto &Object::InsideOut::new },
200 1   50     10 [ $class, grep {$_} split(/[,'\s]+/, $1 || '') ]);
  2         5  
201 1         5 $GBL{'meta'}{'add'}{$class}{'new'} = { 'kind' => 'constructor',
202             'merge_args' => 1,
203             'private' => 1 };
204 1         5 next;
205             }
206              
207             # Public class
208 135 100       281 if ($pkg =~ /^:PUB/i) {
209 2     2   7 *{$class.'::new'} = sub { goto &Object::InsideOut::new };
  2         8  
  2         266  
210 2         8 $GBL{'meta'}{'add'}{$class}{'new'} = { 'kind' => 'constructor',
211             'merge_args' => 1 };
212 2         5 next;
213             }
214              
215             # Handle secure flag
216 133 100       227 if ($pkg =~ /^:SECUR/i) {
217 1         2 $pkg = 'Object::InsideOut::Secure';
218             }
219              
220             # Load the package, if needed
221 133 50       809 if (! $class->isa($pkg)) {
222             # If no package symbols, then load it
223 133 100       110 if (! grep { $_ !~ /::$/ } keys(%{$pkg.'::'})) {
  943         1080  
  133         413  
224 10         484 eval "require $pkg";
225 10 100       108 if ($@) {
226 5         33 OIO::Code->die(
227             'message' => "Failure loading package '$pkg'",
228             'Error' => $@);
229             }
230             # Empty packages make no sense
231 5 100       6 if (! grep { $_ !~ /::$/ } keys(%{$pkg.'::'})) {
  24         68  
  5         19  
232 2         11 OIO::Code->die('message' => "Package '$pkg' is empty");
233             }
234             }
235              
236             # Add to package list
237 126         170 push(@packages, $pkg);
238             }
239              
240              
241             # Import the package, if needed
242 126 50       358 if (ref($_[0])) {
243 0         0 my $imports = shift;
244 0 0       0 if (ref($imports) ne 'ARRAY') {
245 0         0 OIO::Code->die('message' => "Arguments to '$pkg' must be contained within an array reference: $imports");
246             }
247 0         0 eval { $pkg->import(@{$imports}); };
  0         0  
  0         0  
248 0 0       0 if ($@) {
249 0         0 OIO::Code->die(
250             'message' => "Failure running 'import' on package '$pkg'",
251             'Error' => $@);
252             }
253             }
254             }
255              
256             # Create class tree
257 187         160 my @tree;
258             my %seen; # Used to prevent duplicate entries in @tree
259 187         178 my $need_oio = 1;
260 187         279 foreach my $parent (@packages) {
261 126 100       249 if (exists($GBL{'tree'}{'td'}{$parent})) {
262             # Inherit from Object::InsideOut class
263 119         104 foreach my $ancestor (@{$GBL{'tree'}{'td'}{$parent}}) {
  119         234  
264 208 100       312 if (! exists($seen{$ancestor})) {
265 172         177 push(@tree, $ancestor);
266 172         247 $GBL{'asi'}{$ancestor}{$class} = undef;
267 172         207 $seen{$ancestor} = undef;
268             }
269             }
270 119         98 push(@{$class.'::ISA'}, $parent);
  119         907  
271 119         223 $need_oio = 0;
272              
273             } else { # Inherit from foreign class
274             # Get inheritance 'classes' hash
275 7 50       16 if (! exists($GBL{'heritage'}{$class})) {
276 7         16 create_heritage($class);
277             }
278             # Add parent to inherited classes
279 7         176 $GBL{'heritage'}{$class}{'cl'}{$parent} = undef;
280             }
281             }
282              
283             # Add Object::InsideOut to class's @ISA array, if needed
284 187 100       326 if ($need_oio) {
285 104         90 push(@{$class.'::ISA'}, 'Object::InsideOut');
  104         849  
286             }
287              
288             # Add calling class to tree
289 187 50       411 if (! exists($seen{$class})) {
290 187         233 push(@tree, $class);
291             }
292              
293             # Save the trees
294 187         298 $GBL{'tree'}{'td'}{$class} = \@tree;
295 187         215 @{$GBL{'tree'}{'bu'}{$class}} = reverse(@tree);
  187         446  
296              
297 187         32850 $GBL{'init'} = 1; # Need to initialize
298             }
299              
300              
301             ### Attribute Handling ###
302              
303             # Handles subroutine attributes supported by this package.
304             # See 'perldoc attributes' for details.
305             sub MODIFY_CODE_ATTRIBUTES
306             {
307 2600     2600   69239 my ($pkg, $code, @attrs) = @_;
308              
309             # Call attribute handlers in the class tree
310 2600 50       5953 if (exists($GBL{'attr'}{'MOD'}{'CODE'})) {
311 0         0 @attrs = CHECK_ATTRS('CODE', $pkg, $code, @attrs);
312 0 0       0 return if (! @attrs);
313             }
314              
315             # Save caller info with code ref for error reporting purposes
316 2600         18989 my %info = (
317             pkg => $pkg,
318             code => $code,
319             wrap => $code,
320             loc => [ $pkg, (caller(2))[1,2] ],
321             );
322              
323             # Special handling for :Restricted :Cumulative/:Chained methods
324 2600 100 100     9638 if ((my ($restrict) = grep(/^RESTRICT(?:ED)?$/i, @attrs)) &&
325 17 100       90 (grep { ($_ =~ /^CUM(?:ULATIVE)?$/i) ||
326             ($_ =~ /^CHAIN(?:ED)?$/i) } @attrs))
327             {
328 3         4 @attrs = grep { $_ !~ /^RESTRICT(?:ED)?$/i } @attrs;
  6         11  
329 3         6 ($info{'exempt'}) = $restrict =~ /^RESTRICT(?:ED)?\((.*)\)/;
330             }
331              
332 2600         1869 my @unused_attrs; # List of any unhandled attributes
333              
334             # Save the code refs in the appropriate hashes
335 2600         4552 while (my $attribute = shift(@attrs)) {
336 4104         15349 my ($attr, $arg) = $attribute =~ /(\w+)(?:[(]\s*(.*)\s*[)])?/;
337 4104         4362 $attr = uc($attr);
338              
339 4104 100 66     30217 if ($attr eq 'ID') {
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    50          
    100          
    50          
340 5         11 $GBL{'sub'}{'id'}{$pkg} = \%info;
341 5   100     27 push(@attrs, $arg || 'HIDDEN');
342 5         14 $GBL{'init'} = 1;
343              
344             } elsif ($attr eq 'PREINIT') {
345 1         2 $GBL{'sub'}{'pre'}{$pkg} = $code;
346 1   50     7 push(@attrs, $arg || 'HIDDEN');
347              
348             } elsif ($attr eq 'INIT') {
349 21         54 $GBL{'sub'}{'init'}{$pkg} = $code;
350 21   100     148 push(@attrs, $arg || 'HIDDEN');
351              
352             } elsif ($attr =~ /^REPL(?:ICATE)?$/) {
353 1         2 $GBL{'sub'}{'repl'}{$pkg} = $code;
354 1   50     6 push(@attrs, $arg || 'HIDDEN');
355              
356             } elsif ($attr =~ /^DEST(?:ROY)?$/) {
357 2         4 $GBL{'sub'}{'dest'}{$pkg} = $code;
358 2   50     15 push(@attrs, $arg || 'HIDDEN');
359              
360             } elsif ($attr =~ /^AUTO(?:METHOD)?$/) {
361 13         23 $GBL{'sub'}{'auto'}{$pkg} = $code;
362 13   50     54 push(@attrs, $arg || 'HIDDEN');
363 13         30 $GBL{'init'} = 1;
364              
365             } elsif ($attr =~ /^CUM(?:ULATIVE)?$/) {
366 41 100 100     44 push(@{$GBL{'sub'}{'cumu'}{'new'}{($arg && $arg =~ /BOTTOM/i) ? 'bu' : 'td'}}, \%info);
  41         195  
367 41         112 $GBL{'init'} = 1;
368              
369             } elsif ($attr =~ /^CHAIN(?:ED)?$/) {
370 29 100 66     24 push(@{$GBL{'sub'}{'chain'}{'new'}{($arg && $arg =~ /BOTTOM/i) ? 'bu' : 'td'}}, \%info);
  29         138  
371 29         75 $GBL{'init'} = 1;
372              
373             } elsif ($attr =~ /^DUMP(?:ER)?$/) {
374 2         7 $GBL{'dump'}{'dumper'}{$pkg} = $code;
375 2   50     14 push(@attrs, $arg || 'HIDDEN');
376              
377             } elsif ($attr =~ /^PUMP(?:ER)?$/) {
378 2         6 $GBL{'dump'}{'pumper'}{$pkg} = $code;
379 2   50     14 push(@attrs, $arg || 'HIDDEN');
380              
381             } elsif ($attr =~ /^RESTRICT(?:ED)?$/) {
382 13         18 $info{'exempt'} = $arg;
383 13         17 push(@{$GBL{'perm'}{'restr'}}, \%info);
  13         32  
384 13         31 $GBL{'init'} = 1;
385              
386             } elsif ($attr =~ /^PRIV(?:ATE)?$/) {
387 1431         1762 $info{'exempt'} = $arg;
388 1431         997 push(@{$GBL{'perm'}{'priv'}}, \%info);
  1431         2178  
389 1431         3219 $GBL{'init'} = 1;
390              
391             } elsif ($attr =~ /^HIDD?EN?$/) {
392 48         52 push(@{$GBL{'perm'}{'hide'}}, \%info);
  48         123  
393 48         133 $GBL{'init'} = 1;
394              
395             } elsif ($attr =~ /^SUB/) {
396 1850         1360 push(@{$GBL{'meta'}{'subr'}}, \%info);
  1850         3683  
397 1850 100       2732 if ($arg) {
398 1419         1507 push(@attrs, $arg);
399             }
400 1850         4368 $GBL{'init'} = 1;
401              
402             } elsif ($attr =~ /^METHOD/ && $attribute ne 'method') {
403 483 100       763 if ($arg) {
404 479         894 $info{'kind'} = lc($arg);
405 479         364 push(@{$GBL{'meta'}{'method'}}, \%info);
  479         969  
406 479         1216 $GBL{'init'} = 1;
407             }
408              
409             } elsif ($attr =~ /^MERGE/) {
410 82         108 push(@{$GBL{'merge'}}, \%info);
  82         219  
411 82 100       215 if ($arg) {
412 1         2 push(@attrs, $arg);
413             }
414 82         252 $GBL{'init'} = 1;
415              
416             } elsif ($attr =~ /^MOD(?:IFY)?_(ARRAY|CODE|HASH|SCALAR)_ATTR/) {
417 3         7 install_ATTRIBUTES(\%GBL);
418 3         9 $GBL{'attr'}{'MOD'}{$1}{$pkg} = $code;
419 3   50     17 push(@attrs, $arg || 'HIDDEN');
420              
421             } elsif ($attr =~ /^FETCH_(ARRAY|CODE|HASH|SCALAR)_ATTR/) {
422 1         4 install_ATTRIBUTES(\%GBL);
423 1         2 push(@{$GBL{'attr'}{'FETCH'}{$1}}, $code);
  1         4  
424 1   50     54 push(@attrs, $arg || 'HIDDEN');
425              
426             } elsif ($attr eq 'SCALARIFY') {
427 0         0 OIO::Attribute->die(
428             'message' => q/:SCALARIFY not allowed/,
429             'Info' => q/The scalar of an object is its object ID, and can't be redefined/,
430             'ignore_package' => 'attributes');
431              
432 532         570 } elsif (my ($ify) = grep { $_ eq $attr } (qw(STRINGIFY
433             NUMERIFY
434             BOOLIFY
435             ARRAYIFY
436             HASHIFY
437             GLOBIFY
438             CODIFY)))
439             {
440             # Overload (-ify) attributes
441 75         99 $info{'ify'} = $ify;
442 75         49 push(@{$GBL{'sub'}{'ol'}}, \%info);
  75         145  
443 75         198 $GBL{'init'} = 1;
444              
445             } elsif ($attr !~ /^PUB(LIC)?$/) { # PUBLIC is ignored
446             # Not handled
447 0         0 push(@unused_attrs, $attribute);
448             }
449             }
450              
451             # If using Attribute::Handlers, send it any unused attributes
452 2600 50 33     4243 if (@unused_attrs &&
453             Attribute::Handlers::UNIVERSAL->can('MODIFY_CODE_ATTRIBUTES'))
454             {
455 0         0 return (Attribute::Handlers::UNIVERSAL::MODIFY_CODE_ATTRIBUTES($pkg, $code, @unused_attrs));
456             }
457              
458             # Return any unused attributes
459 2600         5957 return (@unused_attrs);
460             }
461              
462             my $BALANCED_PARENS; # Must declare before assigning (so var in scope for regex)
463             $BALANCED_PARENS = qr{(?>(?:(?>[^()]+)|[(](??{$BALANCED_PARENS})[)])*)};
464              
465             # Handles hash field and :InitArgs attributes.
466             sub MODIFY_HASH_ATTRIBUTES :Sub
467             {
468 72     71   6189 my ($pkg, $hash, @attrs) = @_;
469              
470             # Call attribute handlers in the class tree
471 72 50       214 if (exists($GBL{'attr'}{'MOD'}{'HASH'})) {
472 1         2 @attrs = CHECK_ATTRS('HASH', $pkg, $hash, @attrs);
473 1 0       12 return if (! @attrs);
474             }
475              
476 72         133 my @unused_attrs; # List of any unhandled attributes
477              
478             # Process attributes
479 72         128 foreach my $attr (@attrs) {
480             # Declaration for object field hash
481 83 100       2212 if ($attr =~ /^(?:Field|[GS]et|Acc|Com|Mut|St(?:an)?d|LV(alue)?|All|R(?:ead)?O(?:nly)?|Arg|Type|Hand)/i) {
    50          
    50          
    100          
    50          
    100          
    50          
    0          
482             # Save hash ref and attribute
483             # Accessors will be built during initialization
484 47 100       140 if ($attr =~ /^(?:Field|Type)/i) {
485 39         29 unshift(@{$GBL{'fld'}{'new'}{$pkg}}, [ $hash, $attr ]);
  39         181  
486             } else {
487 9         11 push(@{$GBL{'fld'}{'new'}{$pkg}}, [ $hash, $attr ]);
  9         32  
488             }
489 47         86 $GBL{'init'} = 1; # Flag that initialization is required
490             }
491              
492             # Weak field
493             elsif ($attr =~ /^Weak$/i) {
494 1         11 $GBL{'fld'}{'weak'}{$hash} = 1;
495 1         2 push(@{$GBL{'fld'}{'regen'}{'weak'}}, $hash);
  1         11  
496             }
497              
498             # Deep cloning field
499             elsif ($attr =~ /^Deep$/i) {
500 1         2 $GBL{'fld'}{'deep'}{$hash} = 1;
501 1         11 push(@{$GBL{'fld'}{'regen'}{'deep'}}, $hash);
  1         2  
502             }
503              
504             # Defaults
505             elsif ($attr =~ /^Def(?:ault)?[(]($BALANCED_PARENS)[)]$/i) {
506 2         4 my $val;
507 2         122 eval "package $pkg; use $]; \$val = sub { my \$self = \$_[0]; $1 }";
  11         30  
  26         2797  
508 2 50       6 if ($@) {
509 0         0 OIO::Attribute->die(
510             'location' => [ $pkg, (caller(2))[1,2] ],
511             'message' => "Bad ':Default' attribute in package '$pkg'",
512             'Attribute' => $attr,
513             'Error' => $@);
514             }
515 2         4 push(@{$GBL{'fld'}{'def'}{$pkg}}, [ $hash, $val ]);
  2         10  
516             }
517              
518             # Sequentials
519             elsif ($attr =~ /^Seq(?:uence)?(?:From)?[(]($BALANCED_PARENS)[)]$/i) {
520 0         0 my $val = $1;
521 0         0 eval qq{
522             package $pkg;
523             my \$next = $val;
524             \$val = eval{ \$next->can('next') }
525             ? sub { \$next->next() }
526             : sub { \$next++ };
527             };
528 0 0       0 if ($@) {
529 0         0 OIO::Attribute->die(
530             'location' => [ $pkg, (caller(2))[1,2] ],
531             'message' => "Bad ':SequenceFrom' attribute in package '$pkg'",
532             'Attribute' => $attr,
533             'Error' => $@);
534             }
535 0         0 push(@{$GBL{'fld'}{'def'}{$pkg}}, [ $hash, $val ]);
  0         0  
536             }
537              
538             # Field name for dump
539             elsif ($attr =~ /^Name\s*[(]\s*'?([^)'\s]+)'?\s*[)]/i) {
540 1         9 $GBL{'dump'}{'fld'}{$pkg}{$1} = { fld => $hash, src => 'Name' };
541             }
542              
543             # Declaration for object initializer hash
544             elsif ($attr =~ /^InitArgs?$/i) {
545 33         89 $GBL{'args'}{$pkg} = $hash;
546 33         38 push(@{$GBL{'dump'}{'args'}}, $pkg);
  33         158  
547             }
548              
549             # Unhandled
550             # (Must filter out ':shared' attribute due to Perl bug)
551             elsif ($attr ne 'shared') {
552 0         0 push(@unused_attrs, $attr);
553             }
554             }
555              
556             # If using Attribute::Handlers, send it any unused attributes
557 71 50 33     207 if (@unused_attrs &&
558             Attribute::Handlers::UNIVERSAL->can('MODIFY_HASH_ATTRIBUTES'))
559             {
560 0         0 return (Attribute::Handlers::UNIVERSAL::MODIFY_HASH_ATTRIBUTES($pkg, $hash, @unused_attrs));
561             }
562              
563             # Return any unused attributes
564 71         178 return (@unused_attrs);
565 54     56   27279 }
  54         48833  
  54         244  
566              
567              
568             # Handles array field attributes.
569             sub MODIFY_ARRAY_ATTRIBUTES :Sub
570             {
571 195     195   11697 my ($pkg, $array, @attrs) = @_;
572              
573             # Call attribute handlers in the class tree
574 195 100       525 if (exists($GBL{'attr'}{'MOD'}{'ARRAY'})) {
575 2         8 @attrs = CHECK_ATTRS('ARRAY', $pkg, $array, @attrs);
576 2 50       5 return if (! @attrs);
577             }
578              
579 195         1181 my @unused_attrs; # List of any unhandled attributes
580              
581             # Process attributes
582 195         265 foreach my $attr (@attrs) {
583             # Declaration for object field array
584 352 100       1897 if ($attr =~ /^(?:Field|[GS]et|Acc|Com|Mut|St(?:an)?d|LV(alue)?|All|R(?:ead)?O(?:nly)?|Arg|Type|Hand)/i) {
    100          
    100          
    100          
    100          
    50          
    0          
585             # Save array ref and attribute
586             # Accessors will be built during initialization
587 326 100       692 if ($attr =~ /^(?:Field|Type)/i) {
588 221         192 unshift(@{$GBL{'fld'}{'new'}{$pkg}}, [ $array, $attr ]);
  221         630  
589             } else {
590 105         88 push(@{$GBL{'fld'}{'new'}{$pkg}}, [ $array, $attr ]);
  105         208  
591             }
592 326         462 $GBL{'init'} = 1; # Flag that initialization is required
593             }
594              
595             # Weak field
596             elsif ($attr =~ /^Weak$/i) {
597 1         3 $GBL{'fld'}{'weak'}{$array} = 1;
598 1         1 push(@{$GBL{'fld'}{'regen'}{'weak'}}, $array);
  1         4  
599             }
600              
601             # Deep cloning field
602             elsif ($attr =~ /^Deep$/i) {
603 1         3 $GBL{'fld'}{'deep'}{$array} = 1;
604 1         2 push(@{$GBL{'fld'}{'regen'}{'deep'}}, $array);
  1         5  
605             }
606              
607             # Defaults
608             elsif ($attr =~ /^Def(?:ault)?[(]($BALANCED_PARENS)[)]$/i) {
609 17         18 my $val;
610 17         968 eval "package $pkg; use $]; \$val = sub { my \$self = \$_[0]; $1 }";
  12         27  
  16         525  
  23         58  
  24         1622  
  25         2199  
  30         948  
  21         45  
  13         332  
  13         25  
  24         1785  
  13         27  
  17         26  
  16         64  
  9         16  
  6         15  
  9         31  
  8         50  
  5         60  
  5         12  
  7         18  
  2         2  
  5         9  
  4         149  
  1         1  
611 17 50       58 if ($@) {
612 0         0 OIO::Attribute->die(
613             'location' => [ $pkg, (caller(2))[1,2] ],
614             'message' => "Bad ':Default' attribute in package '$pkg'",
615             'Attribute' => $attr,
616             'Error' => $@);
617             }
618 17         15 push(@{$GBL{'fld'}{'def'}{$pkg}}, [ $array, $val ]);
  17         83  
619             }
620              
621             # Sequentials
622             elsif ($attr =~ /^Seq(?:uence)?(?:From)?[(]($BALANCED_PARENS)[)]$/i) {
623 3         6 my $val = $1;
624 3         285 eval qq{
625             package $pkg;
626             my \$next = $val;
627             \$val = eval{ \$next->can('next') }
628             ? sub { \$next->next() }
629             : sub { \$next++ };
630             };
631 3 50       8 if ($@) {
632 0         0 OIO::Attribute->die(
633             'location' => [ $pkg, (caller(2))[1,2] ],
634             'message' => "Bad ':SequenceFrom' attribute in package '$pkg'",
635             'Attribute' => $attr,
636             'Error' => $@);
637             }
638 3         2 push(@{$GBL{'fld'}{'def'}{$pkg}}, [ $array, $val ]);
  3         16  
639             }
640              
641             # Field name for dump
642             elsif ($attr =~ /^Name\s*[(]\s*'?([^)'\s]+)'?\s*[)]/i) {
643 4         35 $GBL{'dump'}{'fld'}{$pkg}{$1} = { fld => $array, src => 'Name' };
644             }
645              
646             # Unhandled
647             # (Must filter out ':shared' attribute due to Perl bug)
648             elsif ($attr ne 'shared') {
649 0         0 push(@unused_attrs, $attr);
650             }
651             }
652              
653             # If using Attribute::Handlers, send it any unused attributes
654 195 50 33     474 if (@unused_attrs &&
655             Attribute::Handlers::UNIVERSAL->can('MODIFY_ARRAY_ATTRIBUTES'))
656             {
657 0         0 return (Attribute::Handlers::UNIVERSAL::MODIFY_ARRAY_ATTRIBUTES($pkg, $array, @unused_attrs));
658             }
659              
660             # Return any unused attributes
661 195         431 return (@unused_attrs);
662 53     56   32104 }
  53         69  
  53         163  
663              
664              
665             ### Array-based Object Support ###
666              
667             # Supplies an ID for an object being created in a class tree
668             # and reclaims IDs from destroyed objects
669             sub _ID :Sub
670             {
671 436 50   436   741 return if $GBL{'term'}; # Ignore during global cleanup
672              
673 436         495 my ($class, $id) = @_; # The object's class and id
674 436         488 my $tree = $GBL{'sub'}{'id'}{$class}{'pkg'};
675              
676              
677             # If class is sharing, then all ID tracking is done as though in thread 0,
678             # else tracking is done per thread
679 436         640 my $sharing = is_sharing($class);
680 436 50       676 my $thread_id = ($sharing) ? 0 : $GBL{'tid'};
681              
682             # Save deleted IDs for later reuse
683 436         457 my $reuse = $GBL{'id'}{'reuse'};
684 436 100       689 if ($id) {
685 215 100       395 if (! exists($$reuse{$tree})) {
686 74 50       235 $$reuse{$tree} = ($sharing) ? make_shared([]) : [];
687             }
688 215 50       457 lock($$reuse{$tree}) if $sharing;
689 215         201 my $r_tree = $$reuse{$tree};
690 215 100       351 if (! defined($$r_tree[$thread_id])) {
691 74 50       244 $$r_tree[$thread_id] = ($sharing) ? make_shared([]) : [];
692             } else {
693 141         128 foreach (@{$$r_tree[$thread_id]}) {
  141         252  
694 101 50       212 if ($_ == $id) {
695 0         0 warn("ERROR: Duplicate reclaimed object ID ($id) in class tree for $tree in thread $thread_id\n");
696 0         0 return;
697             }
698             }
699             }
700 215         198 push(@{$$r_tree[$thread_id]}, $id);
  215         315  
701 215         289 return;
702             }
703              
704             # Use a reclaimed ID if available
705 221 100       412 if (exists($$reuse{$tree})) {
706 85 50       140 lock($$reuse{$tree}) if $sharing;
707 85 50       189 if (defined($$reuse{$tree}[$thread_id])) {
708 85         76 my $id = pop(@{$$reuse{$tree}[$thread_id]});
  85         143  
709 85 100       156 if (defined($id)) {
710 83         298 return $id;
711             }
712             }
713             }
714              
715             # Return the next ID
716 138         166 my $g_id = $GBL{'id'}{'obj'};
717 138 100       310 if (exists($$g_id{$tree})) {
718 61 50       121 lock($$g_id{$tree}) if $sharing;
719 61         237 return (++$$g_id{$tree}[$thread_id]);
720             }
721 77 50       173 if ($sharing) {
722 0         0 $$g_id{$tree} = make_shared([]);
723 0         0 lock($$g_id{$tree});
724 0         0 return (++$$g_id{$tree}[$thread_id]);
725             }
726 77         156 $$g_id{$tree} = [];
727 77         379 return (++$$g_id{$tree}[$thread_id]);
728 53     54   17869 }
  53         73  
  53         157  
729              
730              
731             ### Initialization Handling ###
732              
733             # Finds a subroutine's name from its code ref
734             sub sub_name :Sub(Private)
735             {
736 2594         2085 my ($ref, $attr, $location) = @_;
737              
738 2594         1561 my $name;
739 2594         1687 eval { $name = B::svref_2object($ref)->GV()->NAME(); };
  2594         6746  
740 2594 50       5630 if ($@) {
    50          
741 0         0 OIO::Attribute->die(
742             'location' => $location,
743             'message' => "Failure finding name for subroutine with $attr attribute",
744             'Error' => $@);
745              
746             } elsif ($name eq '__ANON__') {
747 0         0 OIO::Attribute->die(
748             'location' => $location,
749             'message' => q/Subroutine name not found/,
750             'Info' => "Can't use anonymous subroutine for $attr attribute");
751             }
752              
753 2594         4779 return ($name); # Found
754 53     54   10060 }
  53         66  
  53         159  
755              
756              
757             # Perform much of the 'magic' for this module
758             sub initialize :Sub(Private)
759             {
760 340 100       865 return if (! delete($GBL{'init'}));
761              
762 173         327 my $trees = $GBL{'tree'}{'td'};
763 173         280 my $id_subs = $GBL{'sub'}{'id'};
764 173         252 my $obj_ids = $GBL{'id'}{'obj'};
765              
766 53     54   5782 no warnings 'redefine';
  53         65  
  53         1827  
767 53     54   204 no strict 'refs';
  53         59  
  53         87460  
768              
769             # Determine classes that need ID subs
770             # Purge existing references to the default ID sub (i.e., _ID)
771             # if no objects exist in that hierarchy
772 173         184 my %need_id_sub;
773 173         176 foreach my $class (keys(%{$trees})) {
  173         494  
774 419 100 100     1597 if (! exists($$id_subs{$class})) {
    100          
775 183         263 $need_id_sub{$class} = undef;
776             } elsif (($$id_subs{$class}{'code'} == \&_ID) &&
777             ! exists($$obj_ids{$$id_subs{$class}{'pkg'}}))
778             {
779 165         284 delete($$id_subs{$class});
780 165         273 $need_id_sub{$class} = undef;
781             }
782             }
783              
784             # Get ID subs to propagate
785 173         209 my %to_propagate;
786 173         182 foreach my $class (keys(%{$id_subs})) {
  173         324  
787 71         105 $to_propagate{$$id_subs{$class}{'pkg'}} = undef;
788             }
789              
790             # Propagate ID subs to classes
791 173         425 while (%need_id_sub) {
792             # Get ID sub package
793 203         185 my $pkg;
794 203 100       617 if (%to_propagate) {
795 24         54 ($pkg) = keys(%to_propagate);
796 24         41 delete($to_propagate{$pkg});
797             } else {
798 179         324 (my $class) = keys(%need_id_sub);
799 179         286 $pkg = $$trees{$class}[0];
800 179         211 delete($need_id_sub{$pkg});
801 179 50       373 if (! defined($pkg)) {
802             # bug
803 0         0 OIO::Internal->die(
804             'message' => "Class '$class' has empty tree",
805             );
806             }
807 179 50       341 if (exists($$id_subs{$pkg})) {
808             # bug
809 0         0 OIO::Internal->die(
810             'message' => "ID sub for '$pkg' exists but was not propagated properly",
811             );
812             }
813 179         741 $$id_subs{$pkg} = {
814             pkg => $pkg,
815             code => \&_ID,
816             loc => [ '', 'Default :ID sub', 0 ],
817             };
818             }
819              
820             # Add ID sub to classes using package
821 203 100       594 next if (! exists($GBL{'asi'}{$pkg}));
822 81         97 my @propagate_to = keys(%{$GBL{'asi'}{$pkg}});
  81         279  
823 81         144 my %seen = map { $_ => undef } @propagate_to;
  175         330  
824 81         251 while (my $class = pop(@propagate_to)) {
825 831 100       900 if (exists($$id_subs{$class})) {
826             # Verify it's the same ID sub
827 662 50 33     2748 if (($$id_subs{$class}{'code'} != $$id_subs{$pkg}{'code'}) ||
828             ($$id_subs{$class}{'pkg'} ne $$id_subs{$pkg}{'pkg'}))
829             {
830             # Runtime merging of hierarchies with existing objects
831 0 0 0     0 if (($$id_subs{$class}{'code'} == \&_ID) ||
832             ($$id_subs{$pkg}{'code'} == \&_ID))
833             {
834             OIO::Runtime->die(
835             'message' => "Possible extant objects prevent runtime creation of hierarchy for class '$class'",
836             'Info' => "Runtime loading of classes needs to be performed before any objects are created within their hierarchies",
837             ((($$id_subs{$class}{'code'} == \&_ID) && ($$id_subs{$pkg}{'code'} == \&_ID))
838             ? ()
839             : ('Class1' => "The hierarchy for '$$id_subs{$class}{'pkg'}' is using object IDs generated by " .
840             (($$id_subs{$class}{'code'} == \&_ID) ? 'Object::InsideOut' : 'a custom :ID subroutine'),
841             'Class2' => "The hierarchy for '$$id_subs{$pkg}{'pkg'}' is using object IDs generated by " .
842 0 0 0     0 (($$id_subs{$pkg}{'code'} == \&_ID) ? 'Object::InsideOut' : 'a custom :ID subroutine'))));
    0          
    0          
843             }
844             # Multiple :ID subs in hierarchy
845 0         0 my (undef, $file, $line) = @{$$id_subs{$class}{'loc'}};
  0         0  
846 0         0 my (undef, $file2, $line2) = @{$$id_subs{$pkg}{'loc'}};
  0         0  
847 0         0 OIO::Attribute->die(
848             'message' => "Multiple :ID subs defined within hierarchy for class '$class'",
849             'Info' => ":ID subs in class '$$id_subs{$class}{'pkg'}' (file '$file', line $line), and class '$$id_subs{$pkg}{'pkg'}' (file '$file2', line $line2)");
850             }
851             } else {
852             # Add ID sub to class
853 169         196 $$id_subs{$class} = $$id_subs{$pkg};
854 169         163 delete($need_id_sub{$class});
855             # Propagate to classes in this class's tree
856 169         133 foreach my $add (@{$$trees{$class}}) {
  169         341  
857 487 50       673 if (! defined($seen{$add})) {
858 487         399 push(@propagate_to, $add);
859 487         465 $seen{$add} = undef;
860             }
861             }
862             # Propagate to classes that use this one
863 169 100       399 if (exists($GBL{'asi'}{$class})) {
864 92         74 foreach my $add (keys(%{$GBL{'asi'}{$class}})) {
  92         218  
865 169 50       254 if (! defined($seen{$add})) {
866 169         200 push(@propagate_to, $add);
867 169         263 $seen{$add} = undef;
868             }
869             }
870             }
871             }
872             }
873             }
874              
875 173 50       548 if ($GBL{'share'}{'ok'}) {
876             # If needed, process any thread object sharing flags
877 0         0 my $sh_cl = $GBL{'share'}{'cl'};
878 0         0 foreach my $flag_class (keys(%{$sh_cl})) {
  0         0  
879             # Find the class in any class tree
880 0         0 foreach my $tree (values(%{$trees})) {
  0         0  
881 0 0       0 if (grep(/^$flag_class$/, @$tree)) {
882             # Check each class in the tree
883 0         0 foreach my $class (@$tree) {
884 0 0       0 if (exists($$sh_cl{$class})) {
885             # Check for sharing conflicts
886 0 0       0 if ($$sh_cl{$class}{'share'}
887             != $$sh_cl{$flag_class}{'share'})
888             {
889             my ($pkg1, $pkg2)
890 0 0       0 = ($$sh_cl{$flag_class}{'share'})
891             ? ($flag_class, $class)
892             : ($class, $flag_class);
893             my @loc = ($pkg1,
894             $$sh_cl{$pkg1}{'file'},
895 0         0 $$sh_cl{$pkg1}{'line'});
896 0         0 OIO::Code->die(
897             'location' => \@loc,
898             'message' => "Can't combine thread-sharing classes ($pkg1) with non-sharing classes ($pkg2) in the same class tree",
899             'Info' => "Class '$pkg1' was declared as sharing (file '$loc[1]' line $loc[2]), but class '$pkg2' was declared as non-sharing (file '$$sh_cl{$pkg2}{'file'}' line $$sh_cl{$pkg2}{'line'})");
900             }
901             } else {
902             # Add the sharing flag to this class
903 0         0 $$sh_cl{$class} = $$sh_cl{$flag_class};
904             }
905             }
906             }
907             }
908             # Set up for obj ID sequences, and obj ID reuse
909             # for shared classes using _ID
910 0 0       0 if ($$sh_cl{$flag_class}{'share'}) {
911 0         0 my $reuse = $GBL{'id'}{'reuse'};
912 0 0 0     0 if (exists($$id_subs{$flag_class}) &&
913             ($$id_subs{$flag_class}{'code'} == \&_ID))
914             {
915 0         0 my $share_tree = $$id_subs{$flag_class}{'pkg'};
916 0 0       0 if (! exists($$obj_ids{$share_tree})) {
917 0         0 $$obj_ids{$share_tree} = make_shared([]);
918 0         0 $$obj_ids{$share_tree}[0] = 0;
919             }
920 0 0       0 if (! exists($$reuse{$share_tree})) {
921 0         0 $$reuse{$share_tree} = make_shared([]);
922             }
923 0         0 my $r_tree = $$reuse{$share_tree};
924 0 0       0 if (! defined($$r_tree[0])) {
925 0         0 $$r_tree[0] = make_shared([]);
926             }
927             }
928             }
929             }
930              
931             # Set up for shared object tracking
932 0 0 0     0 if (! exists($GBL{'share'}{'obj'}) &&
      0        
933             (($] < 5.008009) || ($threads::shared::VERSION lt '1.15')))
934             {
935 0         0 $GBL{'share'}{'obj'} = make_shared({});
936             }
937             }
938              
939             # Process field attributes
940 173         381 process_fields();
941              
942             # Implement ->isa()/->can() with :AutoMethods
943 173 100       206 if (%{$GBL{'sub'}{'auto'}}) {
  173         470  
944 15         43 install_UNIVERSAL();
945             }
946              
947             # Implement overload (-ify) operators
948 173 100       421 if (exists($GBL{'sub'}{'ol'})) {
949 12         40 generate_OVERLOAD(\%GBL);
950             }
951              
952             # Add metadata for methods
953 173         269 my $meta = $GBL{'meta'}{'add'};
954 173 100       453 if (my $meta_m = delete($GBL{'meta'}{'method'})) {
955 54         68 while (my $info = shift(@{$meta_m})) {
  533         981  
956 479   33     1096 $$info{'name'} ||= sub_name($$info{'code'}, ':METHOD', $$info{'loc'});
957 479         1279 $$meta{$$info{'pkg'}}{$$info{'name'}}{'kind'} = $$info{'kind'};
958             }
959             }
960              
961             # Add metadata for subroutines
962 173 100       504 if (my $meta_s = delete($GBL{'meta'}{'subr'})) {
963 71         92 while (my $info = shift(@{$meta_s})) {
  1919         2856  
964 1848   33     3578 $$info{'name'} ||= sub_name($$info{'code'}, ':SUB', $$info{'loc'});
965 1848         3568 $$meta{$$info{'pkg'}}{$$info{'name'}}{'hidden'} = 1;
966             }
967             }
968              
969             # Implement merged argument methods
970 173 100       458 if (my $merge = delete($GBL{'merge'})) {
971 58         78 while (my $info = shift(@{$merge})) {
  140         310  
972 82   33     293 $$info{'name'} ||= sub_name($$info{'code'}, ':MergeArgs', $$info{'loc'});
973 82         107 my $pkg = $$info{'pkg'};
974 82         96 my $name = $$info{'name'};
975              
976 82         156 my $new_wrap = wrap_MERGE_ARGS($$info{'wrap'});
977 82         91 *{$pkg.'::'.$name} = $new_wrap;
  82         352  
978 82         109 $$info{'wrap'} = $new_wrap;
979              
980 82         225 $$meta{$pkg}{$name}{'merge_args'} = 1;
981             }
982             }
983              
984             # Implement restricted methods - only callable within hierarchy
985 173 100       452 if (my $restr = delete($GBL{'perm'}{'restr'})) {
986 6         9 while (my $info = shift(@{$restr})) {
  19         49  
987 13   66     42 $$info{'name'} ||= sub_name($$info{'code'}, ':RESTRICTED', $$info{'loc'});
988 13         16 my $pkg = $$info{'pkg'};
989 13         13 my $name = $$info{'name'};
990              
991 13   100     59 my $exempt = [ grep {$_} split(/[,'\s]+/, $$info{'exempt'} || '') ];
  6         11  
992              
993 13         114 my $new_wrap = wrap_RESTRICTED($pkg, $name, $$info{'wrap'}, $exempt);
994 13         12 *{$pkg.'::'.$name} = $new_wrap;
  13         42  
995 13         16 $$info{'wrap'} = $new_wrap;
996              
997 13         33 $$meta{$pkg}{$name}{'restricted'} = 1;
998             }
999             }
1000              
1001             # Implement private methods - only callable from class itself
1002 173 100       419 if (my $priv = delete($GBL{'perm'}{'priv'})) {
1003 73         84 while (my $info = shift(@{$priv})) {
  1502         2332  
1004 1429   66     1914 $$info{'name'} ||= sub_name($$info{'code'}, ':PRIVATE', $$info{'loc'});
1005 1429         1068 my $pkg = $$info{'pkg'};
1006 1429         961 my $name = $$info{'name'};
1007              
1008 1429   100     3877 my $exempt = [ $pkg, grep {$_} split(/[,'\s]+/, $$info{'exempt'} || '') ];
  1         75  
1009              
1010 1429         1698 my $new_wrap = wrap_PRIVATE($pkg, $name, $$info{'wrap'}, $exempt);
1011 1429         972 *{$pkg.'::'.$name} = $new_wrap;
  1429         2909  
1012 1429         1094 $$info{'wrap'} = $new_wrap;
1013              
1014 1429         2631 $$meta{$pkg}{$name}{'private'} = 1;
1015             }
1016             }
1017              
1018             # Implement hidden methods - no longer callable by name
1019 173 100       427 if (my $hide = delete($GBL{'perm'}{'hide'})) {
1020 26         36 while (my $info = shift(@{$hide})) {
  74         182  
1021 48   33     194 $$info{'name'} ||= sub_name($$info{'code'}, ':HIDDEN', $$info{'loc'});
1022 48         54 my $pkg = $$info{'pkg'};
1023 48         54 my $name = $$info{'name'};
1024              
1025 48         167 *{$pkg.'::'.$name} = wrap_HIDDEN($pkg, $name);
  48         161  
1026              
1027 48         245 $$meta{$pkg}{$name}{'hidden'} = 1;
1028             }
1029             }
1030              
1031             # Implement cumulative methods
1032 173 100       424 if (exists($GBL{'sub'}{'cumu'}{'new'})) {
1033 8         19 generate_CUMULATIVE(\%GBL);
1034             }
1035              
1036             # Implement chained methods
1037 173 100       714 if (exists($GBL{'sub'}{'chain'}{'new'})) {
1038 5         14 generate_CHAINED(\%GBL);
1039             }
1040              
1041             # Export methods
1042 173         188 my @export = @{$GBL{'export'}};
  173         569  
1043 173         241 my $trees_bu = $GBL{'tree'}{'bu'};
1044 173         183 foreach my $pkg (keys(%{$trees})) {
  173         392  
1045             EXPORT:
1046 428 100       1523 foreach my $sym (@export, ($pkg->isa('Storable'))
1047             ? (qw(STORABLE_freeze STORABLE_thaw))
1048             : ())
1049             {
1050 2321         2169 my $full_sym = $pkg.'::'.$sym;
1051             # Only export if method doesn't already exist,
1052             # and not overridden in a parent class
1053 2321 100       1372 if (! *{$full_sym}{CODE}) {
  2321         5624  
1054 1022         644 foreach my $class (@{$$trees_bu{$pkg}}) {
  1022         1273  
1055 1928         1678 my $class_sym = $class.'::'.$sym;
1056 1928 50 66     1107 if (*{$class_sym}{CODE} &&
  1928         4374  
1057 436         457 (*{$class_sym}{CODE} != \&{$sym}))
  436         1264  
1058             {
1059 0         0 next EXPORT;
1060             }
1061             }
1062 1022         670 *{$full_sym} = \&{$sym};
  1022         1590  
  1022         1096  
1063              
1064             # Add metadata
1065 1022 100 100     5816 if ($sym eq 'new') {
    100 100        
    50 100        
    100          
    100          
1066 182         663 $$meta{$pkg}{'new'} = { 'kind' => 'constructor',
1067             'merge_args' => 1 };
1068              
1069             } elsif ($sym eq 'clone' || $sym eq 'dump') {
1070 200         420 $$meta{$pkg}{$sym}{'kind'} = 'object';
1071              
1072             } elsif ($sym eq 'create_field') {
1073 0         0 $$meta{$pkg}{$sym}{'kind'} = 'class';
1074              
1075             } elsif ($sym =~ /^STORABLE_/ || ($sym eq 'AUTOLOAD')) {
1076 40         121 $$meta{$pkg}{$sym}{'hidden'} = 1;
1077              
1078             } elsif ($sym =~ /herit/ || $sym eq 'set') {
1079 226         599 $$meta{$pkg}{$sym} = { 'kind' => 'object',
1080             'restricted' => 1 };
1081             }
1082             }
1083             }
1084             }
1085              
1086             # Add accumulated metadata
1087 173         616 add_meta($meta);
1088 173         32258 $GBL{'meta'}{'add'} = {};
1089 53     54   283 }
  53         54  
  53         183  
1090              
1091              
1092             # Process attributes for field hashes/arrays including generating accessors
1093             sub process_fields :Sub(Private)
1094             {
1095 177         296 my $new = delete($GBL{'fld'}{'new'});
1096 177 100       412 return if (! $new);
1097              
1098             # 'Want' module loaded?
1099 55   66     197 my $use_want = (defined($Want::VERSION) && ($Want::VERSION >= 0.12));
1100              
1101 55         107 my $trees = $GBL{'tree'}{'td'};
1102 55         93 my $fld_refs = $GBL{'fld'}{'ref'};
1103 55         102 my $g_ho = $GBL{'hash_only'};
1104 55         65 my $do_ho = %{$g_ho};
  55         115  
1105              
1106             # Process field attributes
1107 55         84 foreach my $pkg (keys(%{$new})) {
  55         159  
1108 90         111 while (my $item = shift(@{$$new{$pkg}})) {
  458         1083  
1109 370         260 my ($fld, $attr) = @{$item};
  370         477  
1110              
1111             # Verify not a 'hash field only' class
1112 370 100 100     1301 if ((ref($fld) eq 'ARRAY') && $do_ho) {
1113 2         2 foreach my $ho (keys(%{$g_ho})) {
  2         6  
1114 2         3 foreach my $class (@{$$trees{$pkg}}) {
  2         11  
1115 2 50       6 if ($class eq $ho) {
1116             my $loc = ((caller())[1] =~ /Dynamic/)
1117             ? [ (caller(2))[0..2] ]
1118 2 50       17 : $$g_ho{$ho};
1119 2         30 OIO::Code->die(
1120             'location' => $loc,
1121             'message' => "Can't combine 'hash only' classes ($ho) with array-based classes ($class) in the same class tree",
1122             'Info' => "Class '$ho' was declared as ':hash_only', but class '$class' has array-based fields");
1123             }
1124             }
1125             }
1126             }
1127              
1128             # Share the field, if applicable
1129 368 50 33     610 if (is_sharing($pkg) && !threads::shared::is_shared($fld)) {
1130             # Preserve any contents
1131 0         0 my $contents = Object::InsideOut::Util::clone_shared($fld);
1132              
1133             # Share the field
1134 0         0 threads::shared::share($fld);
1135              
1136             # Restore contents
1137 0 0       0 if ($contents) {
1138 0 0       0 if (ref($fld) eq 'HASH') {
1139 0         0 %{$fld} = %{$contents};
  0         0  
  0         0  
1140             } else {
1141 0         0 @{$fld} = @{$contents};
  0         0  
  0         0  
1142             }
1143             }
1144             }
1145              
1146             # Process any accessor declarations
1147 368 50       529 if ($attr) {
1148 368         553 create_accessors($pkg, $fld, $attr, $use_want);
1149             }
1150              
1151             # Save field ref
1152 368 100       327 if (! grep { $_ == $fld } @{$$fld_refs{$pkg}}) {
  1659         1964  
  368         743  
1153 231         238 push(@{$$fld_refs{$pkg}}, $fld);
  231         497  
1154             }
1155             }
1156             }
1157 53     54   20597 }
  53         64  
  53         176  
1158              
1159              
1160             # Normalize the :InitArgs hash
1161             sub normalize :Sub
1162             {
1163 95     95 0 184 my $hash = $_[$#_];
1164 95 50       250 if (ref($hash) ne 'HASH') {
1165 0         0 OIO::Args->die(
1166             'message' => 'Argument is not a hash ref',
1167             'Usage' => q/Object::InsideOut::normalize($hash)/);
1168             }
1169              
1170 95         108 foreach my $arg (keys(%{$hash})) {
  95         235  
1171 182         207 my $spec = $$hash{$arg};
1172 182 100       367 next if (ref($spec) ne 'HASH');
1173 124         104 foreach my $opt (keys(%{$spec})) {
  124         276  
1174 260 100       2606 if ($opt =~ qr/^DEF(?:AULTs?)?$/i) {
    100          
    100          
    100          
    100          
    100          
1175 32         80 $$spec{'_D'} = $$spec{$opt};
1176             } elsif ($opt =~ qr/^FIELD$/i) {
1177 62         210 $$spec{'_F'} = $$spec{$opt};
1178             } elsif ($opt =~ qr/^(?:MAND|REQ)/i) {
1179 4         14 $$spec{'_M'} = $$spec{$opt};
1180             } elsif ($opt =~ qr/^PRE/i) {
1181 3         8 $$spec{'_P'} = $$spec{$opt};
1182             } elsif ($opt =~ qr/^RE(?:GEXp?)?$/i) {
1183             # Turn into an actual 'Regexp', if needed
1184             $$spec{'_R'} = (ref($$spec{$opt}) eq 'Regexp')
1185 15 50       77 ? $$spec{$opt}
1186             : qr/^$$spec{$opt}$/;
1187             } elsif ($opt =~ qr/^TYPE$/i) {
1188 14         54 $$spec{'_T'} = $$spec{$opt};
1189             }
1190             }
1191             }
1192 95         164 $$hash{' '} = undef;
1193              
1194 95         148 return ($hash);
1195 53     54   17617 }
  53         64  
  53         175  
1196              
1197              
1198             ### Thread-Shared Object Support ###
1199              
1200             # Set a class as thread-sharing
1201             sub set_sharing :Sub(Private)
1202             {
1203 0         0 my ($class, $sharing, $file, $line) = @_;
1204 0 0       0 $sharing = ($sharing) ? 1 : 0;
1205              
1206 0         0 my $sh_cl = $GBL{'share'}{'cl'};
1207 0 0       0 if (exists($$sh_cl{$class})) {
1208 0 0       0 if ($$sh_cl{$class}{'share'} != $sharing) {
1209 0         0 my (@loc, $nfile, $nline);
1210 0 0       0 if ($sharing) {
1211 0         0 @loc = ($class, $file, $line);
1212 0         0 $nfile = $$sh_cl{$class}{'file'};
1213 0         0 $nline = $$sh_cl{$class}{'line'};
1214             } else {
1215             @loc = ($class,
1216             $$sh_cl{$class}{'file'},
1217 0         0 $$sh_cl{$class}{'line'});
1218 0         0 ($nfile, $nline) = ($file, $line);
1219             }
1220 0         0 OIO::Code->die(
1221             'location' => \@loc,
1222             'message' => "Can't combine thread-sharing and non-sharing instances of a class in the same application",
1223             'Info' => "Class '$class' was declared as sharing in '$file' line $line, but was declared as non-sharing in '$nfile' line $nline");
1224             }
1225             } else {
1226 0         0 $$sh_cl{$class} = {
1227             share => $sharing,
1228             file => $file,
1229             line => $line,
1230             };
1231             # Set up equality via overload
1232 0 0 0     0 if ($sharing && $threads::shared::threads_shared
      0        
1233             && $threads::shared::VERSION ge '0.95')
1234             {
1235 0         0 push(@{$GBL{'sub'}{'ol'}}, { 'pkg' => $class, 'ify' => 'EQUATE' });
  0         0  
1236             }
1237             }
1238 53     54   12693 }
  53         62  
  53         155  
1239              
1240              
1241             # Determines if a class's objects are shared between threads
1242             sub is_sharing :Sub(Private)
1243             {
1244 1514 50       3917 return if ! $GBL{'share'}{'ok'};
1245 0         0 my $class = $_[0];
1246 0         0 my $sh_cl = $GBL{'share'}{'cl'};
1247 0   0     0 return (exists($$sh_cl{$class}) && $$sh_cl{$class}{'share'});
1248 53     54   6567 }
  53         67  
  53         154  
1249              
1250              
1251             ### Thread Cloning Support ###
1252              
1253             sub CLONE
1254             {
1255             # Don't execute when called for sub-classes
1256 0 0   0   0 return if ($_[0] ne 'Object::InsideOut');
1257              
1258             # Don't execute twice for same thread
1259 0         0 my $tid;
1260 0 0       0 if ($threads::threads) {
1261 0         0 $tid = threads->tid();
1262 0 0       0 return if ($GBL{'tid'} == $tid);
1263 0         0 $GBL{'tid'} = $tid;
1264             } else {
1265             # Pseudo-fork
1266 0 0       0 return if (exists($GBL{'pids'}{$$}));
1267 0         0 $GBL{'pids'}{$$} = undef;
1268 0         0 $tid = $GBL{'tid'};
1269             }
1270              
1271             # Check for delayed threads::shared usage
1272 0 0 0     0 if ($threads::shared::threads_shared && ! $GBL{'share'}{'ok'}) {
1273 0         0 OIO::Code->die(
1274             'message' => q/'threads::shared' imported after Object::InsideOut initialized/,
1275             'Info' => q/Add 'use threads::shared;' to the start of your application code/);
1276             }
1277              
1278             # Process thread-shared objects
1279 0 0       0 if (exists($GBL{'share'}{'obj'})) {
1280 0         0 my $sh_obj = $GBL{'share'}{'obj'};
1281 0         0 lock($sh_obj);
1282              
1283             # Add thread ID to every object in the thread tracking registry
1284 0         0 foreach my $class (keys(%{$sh_obj})) {
  0         0  
1285 0         0 foreach my $oid (keys(%{$$sh_obj{$class}})) {
  0         0  
1286 0         0 push(@{$$sh_obj{$class}{$oid}}, $tid);
  0         0  
1287             }
1288             }
1289             }
1290              
1291             # Fix field references
1292 0         0 my $g_fld = $GBL{'fld'};
1293 0         0 my $regen = $$g_fld{'regen'};
1294 0         0 $$g_fld{'type'} = { map { $_->[0] => $_->[1] } @{$$regen{'type'}} };
  0         0  
  0         0  
1295 0         0 $$g_fld{'weak'} = { map { $_ => 1 } @{$$regen{'weak'}} };
  0         0  
  0         0  
1296 0         0 $$g_fld{'deep'} = { map { $_ => 1 } @{$$regen{'deep'}} };
  0         0  
  0         0  
1297              
1298             # Process non-thread-shared objects
1299 0         0 my $g_obj = $GBL{'obj'};
1300 0         0 my $trees = $GBL{'tree'}{'td'};
1301 0         0 my $id_subs = $GBL{'sub'}{'id'};
1302 0         0 my $fld_ref = $$g_fld{'ref'};
1303 0         0 my $weak = $$g_fld{'weak'};
1304 0         0 my $repl_subs = $GBL{'sub'}{'repl'};
1305 0         0 my $do_repl = keys(%{$repl_subs});
  0         0  
1306 0         0 foreach my $class (keys(%{$g_obj})) {
  0         0  
1307 0         0 my $obj_cl = $$g_obj{$class};
1308              
1309             # Get class tree
1310 0         0 my @tree = @{$$trees{$class}};
  0         0  
1311              
1312             # Get the ID sub for this class, if any
1313 0         0 my $id_sub = $$id_subs{$class}{'code'};
1314              
1315             # Get any replication handlers
1316 0         0 my @repl;
1317 0 0       0 if ($do_repl) {
1318 0         0 @repl = grep { $_ } map { $$repl_subs{$_} } @tree;
  0         0  
  0         0  
1319             }
1320              
1321             # Process each object in the class
1322 0         0 foreach my $old_id (keys(%{$obj_cl})) {
  0         0  
1323 0         0 my $obj;
1324 0 0       0 if ($id_sub == \&_ID) {
1325             # Objects using internal ID sub keep their same ID
1326 0         0 $obj = $$obj_cl{$old_id};
1327              
1328             # Set 'next object ID'
1329 0         0 my $pkg = $GBL{'sub'}{'id'}{$class}{'pkg'};
1330 0         0 my $g_id = $GBL{'id'}{'obj'}{$pkg};
1331 0 0 0     0 if (! $$g_id[$tid] || ($$g_id[$tid] < $$obj)) {
1332 0         0 $$g_id[$tid] = $$obj;
1333             }
1334              
1335             } else {
1336             # Get cloned object associated with old ID
1337 0         0 $obj = delete($$obj_cl{$old_id});
1338              
1339             # Unlock the object
1340 0 0       0 Internals::SvREADONLY($$obj, 0) if ($] >= 5.008003);
1341              
1342             # Replace the old object ID with a new one
1343 0         0 local $SIG{'__DIE__'} = 'OIO::trap';
1344 0         0 $$obj = $id_sub->($class);
1345              
1346             # Lock the object again
1347 0 0       0 Internals::SvREADONLY($$obj, 1) if ($] >= 5.008003);
1348              
1349             # Update the keys of the field arrays/hashes
1350             # with the new object ID
1351 0         0 foreach my $pkg (@tree) {
1352 0         0 foreach my $fld (@{$$fld_ref{$pkg}}) {
  0         0  
1353 0 0       0 if (ref($fld) eq 'HASH') {
1354 0         0 $$fld{$$obj} = delete($$fld{$old_id});
1355 0 0       0 if ($$weak{'weak'}{$fld}) {
1356 0         0 Scalar::Util::weaken($$fld{$$obj});
1357             }
1358             } else {
1359 0         0 $$fld[$$obj] = $$fld[$old_id];
1360 0         0 undef($$fld[$old_id]);
1361 0 0       0 if ($$weak{$fld}) {
1362 0         0 Scalar::Util::weaken($$fld[$$obj]);
1363             }
1364             }
1365             }
1366             }
1367              
1368             # Resave weakened reference to object
1369 0         0 Scalar::Util::weaken($$obj_cl{$$obj} = $obj);
1370             }
1371              
1372             # Dispatch any special replication handling
1373 0 0       0 if (@repl) {
1374 0         0 my $pseudo_object = \do{ my $scalar = $old_id; };
  0         0  
1375 0         0 foreach my $repl (@repl) {
1376 0         0 local $SIG{'__DIE__'} = 'OIO::trap';
1377 0         0 $repl->($pseudo_object, $obj, 'CLONE');
1378             }
1379             }
1380             }
1381             }
1382             }
1383              
1384              
1385             ### Object Methods ###
1386              
1387             # Helper subroutine to create a new 'bare' object
1388             sub _obj :Sub(Private)
1389             {
1390 233         248 my $class = shift;
1391              
1392             # Create a new 'bare' object
1393 233         973 my $self = create_object($class, $GBL{'sub'}{'id'}{$class}{'code'});
1394              
1395             # Thread support
1396 233 50       364 if (is_sharing($class)) {
    50          
1397 0         0 threads::shared::share($self);
1398              
1399             # Add thread tracking list for this thread-shared object
1400 0 0       0 if (exists($GBL{'share'}{'obj'})) {
1401 0         0 my $sh_obj = $GBL{'share'}{'obj'};
1402 0         0 lock($sh_obj);
1403 0 0       0 if (exists($$sh_obj{$class})) {
1404 0         0 $$sh_obj{$class}{$$self} = make_shared([ $GBL{'tid'} ]);
1405             } else {
1406 0         0 $$sh_obj{$class} = make_shared({ $$self => [ $GBL{'tid'} ] });
1407             }
1408             }
1409              
1410             } elsif ($threads::threads) {
1411             # Add non-thread-shared object to thread cloning list
1412 0         0 Scalar::Util::weaken($GBL{'obj'}{$class}{$$self} = $self);
1413             }
1414              
1415 233         324 return ($self);
1416 53     54   39202 }
  53         65  
  53         172  
1417              
1418              
1419             # Extracts specified args from those given
1420             sub _args :Sub(Private)
1421             {
1422 145         229 my ($class,
1423             $self, # Object being initialized with args
1424             $spec, # Hash ref of arg specifiers
1425             $args, # Hash ref of args
1426             $used) # Hash ref of used args
1427             = @_;
1428              
1429             # Ensure :InitArgs hash is normalized
1430 145 100       339 if (! exists($$spec{' '})) {
1431 56         179 normalize($spec);
1432             }
1433              
1434             # Extract arg-matching regexs from the specifiers
1435 145         134 my %regex;
1436 145         145 while (my ($key, $val) = each(%{$spec})) {
  685         1252  
1437 540 100       788 next if ($key eq ' ');
1438 395 100       803 $regex{$key} = (ref($val) eq 'HASH') ? $$val{'_R'} : $val;
1439             }
1440              
1441             # Search for specified args
1442 145         203 my %found = ();
1443 145         132 my $add_used = $used;
1444             EXTRACT: {
1445             # Find arguments using regex's
1446 145         132 foreach my $key (keys(%regex)) {
  167         290  
1447 480         385 my $regex = $regex{$key};
1448 480 100       822 my ($value, $arg) = ($regex) ? hash_re($args, $regex) : ($$args{$key}, $key);
1449 480 100       603 if (defined($found{$key})) {
1450 43 100       65 if (defined($value)) {
1451 20         22 $found{$key} = $value;
1452             }
1453             } else {
1454 437         442 $found{$key} = $value;
1455             }
1456 480 100       643 if (defined($arg)) {
1457 451         560 $$add_used{$arg} = undef;
1458             }
1459             }
1460              
1461             # Check for class-specific argument hash ref
1462 167 100       338 if (exists($$args{$class})) {
1463 22         26 $args = $$args{$class};
1464 22 50       51 if (ref($args) ne 'HASH') {
1465 0         0 OIO::Args->die(
1466             'message' => "Bad class initializer for '$class'",
1467             'Usage' => q/Class initializers must be a hash ref/);
1468             }
1469 22         26 $$add_used{$class} = {};
1470 22         25 $add_used = $$add_used{$class};
1471             # Loop back to process class-specific arguments
1472 22         24 redo EXTRACT;
1473             }
1474             }
1475              
1476             # Check on what we've found
1477             CHECKIT:
1478 145         129 foreach my $key (keys(%{$spec})) {
  145         273  
1479 514         441 my $spec_item = $$spec{$key};
1480             # No specs to check
1481 514 100       806 if (ref($spec_item) ne 'HASH') {
1482             # The specifier entry was just 'key => regex'. If 'key' is not in
1483             # the args, the we need to remove the 'undef' entry in the found
1484             # args hash.
1485 200 100       344 if (! defined($found{$key})) {
1486 148         137 delete($found{$key});
1487             }
1488 200         249 next CHECKIT;
1489             }
1490              
1491             # Preprocess the argument
1492 314 100       487 if (my $pre = $$spec_item{'_P'}) {
1493 3 50       14 if (ref($pre) ne 'CODE') {
1494 0         0 OIO::Code->die(
1495             'message' => q/Can't handle argument/,
1496             'Info' => "'Preprocess' is not a code ref for initializer '$key' for class '$class'");
1497             }
1498              
1499 3         3 my (@errs);
1500 3         17 local $SIG{'__WARN__'} = sub { push(@errs, @_); };
  0         0  
1501 3         3 eval {
1502 3         5 local $SIG{'__DIE__'};
1503 3         7 $found{$key} = $pre->($class, $key, $spec_item, $self, $found{$key})
1504             };
1505 3 50 33     1437 if ($@ || @errs) {
1506 0   0     0 my ($err) = split(/ at /, $@ || join(" | ", @errs));
1507 0         0 OIO::Code->die(
1508             'message' => "Problem with preprocess routine for initializer '$key' for class '$class",
1509             'Error' => $err);
1510             }
1511             }
1512              
1513             # Handle args not found
1514 314 100       466 if (! defined($found{$key})) {
1515             # Complain if mandatory
1516 171 100       262 if ($$spec_item{'_M'}) {
1517 2         36 OIO::Args->die(
1518             'message' => "Missing mandatory initializer '$key' for class '$class'");
1519             }
1520              
1521             # Assign default value
1522 169 100       269 if (exists($$spec_item{'_D'})) {
1523 77 100       121 if (ref($$spec_item{'_D'}) eq 'CODE') {
1524 37         518 $found{$key} = $$spec_item{'_D'}->($self);
1525             } else {
1526 40         129 $found{$key} = Object::InsideOut::Util::clone($$spec_item{'_D'});
1527             }
1528             }
1529              
1530             # If no default, then remove it from the found args hash
1531 169 100       303 if (! defined($found{$key})) {
1532 92         92 delete($found{$key});
1533 92         112 next CHECKIT;
1534             }
1535             }
1536              
1537             # Check for correct type
1538 220 100       365 if (my $type = $$spec_item{'_T'}) {
1539 32         33 my $subtype;
1540              
1541             # Custom type checking
1542 32 100       150 if (ref($type)) {
    100          
    100          
    100          
1543 16 50       28 if (ref($type) ne 'CODE') {
1544 0         0 OIO::Code->die(
1545             'message' => q/Can't validate argument/,
1546             'Info' => "'Type' is not a code ref or string for initializer '$key' for class '$class'");
1547             }
1548              
1549 16         12 my ($ok, @errs);
1550 16         88 local $SIG{'__WARN__'} = sub { push(@errs, @_); };
  2         48  
1551 16         20 eval {
1552 16         24 local $SIG{'__DIE__'};
1553 16         45 $ok = $type->($found{$key})
1554             };
1555 16 100 66     136 if ($@ || @errs) {
1556 2   33     17 my ($err) = split(/ at /, $@ || join(" | ", @errs));
1557 2         14 OIO::Code->die(
1558             'message' => "Problem with type check routine for initializer '$key' for class '$class",
1559             'Error' => $err);
1560             }
1561 14 100       47 if (! $ok) {
1562 8         54 OIO::Args->die(
1563             'message' => "Initializer '$key' for class '$class' failed type check: $found{$key}");
1564             }
1565             }
1566              
1567             # Is it supposed to be a scalar
1568             elsif ($type =~ /^scalar$/i) {
1569 2 100       5 if (ref($found{$key})) {
1570 1         9 OIO::Args->die(
1571             'message' => "Bad value for initializer '$key': $found{$key}",
1572             'Usage' => "Initializer '$key' for class '$class' must be a scalar");
1573             }
1574             }
1575              
1576             # Is it supposed to be a number
1577             elsif ($type =~ /^num(?:ber|eric)?$/i) {
1578 4 100       13 if (! Scalar::Util::looks_like_number($found{$key})) {
1579 2         26 OIO::Args->die(
1580             'message' => "Bad value for initializer '$key': $found{$key}",
1581             'Usage' => "Initializer '$key' for class '$class' must be a number");
1582             }
1583             }
1584              
1585             # For 'LIST', turn anything not an array ref into an array ref
1586             elsif ($type =~ /^(?:list|array)\s*(?:\(\s*(\S+)\s*\))*$/i) {
1587 6 50       20 if (defined($1)) {
1588 0         0 $subtype = $1;
1589             }
1590 6 100       15 if (ref($found{$key}) ne 'ARRAY') {
1591 3         9 $found{$key} = [ $found{$key} ];
1592             }
1593             }
1594              
1595             # Otherwise, check for a specific class or ref type
1596             # Exact spelling and case required
1597             else {
1598 4 50       30 if ($type =~ /^(array|hash|scalar)(?:_?ref)?\s*(?:\(\s*(\S+)\s*\))*$/i) {
1599 4         14 $type = uc($1);
1600 4 100       11 if (defined($2)) {
1601 2         4 $subtype = $2;
1602             }
1603             }
1604 4 50       17 if (! is_it($found{$key}, $type)) {
1605 0         0 OIO::Args->die(
1606             'message' => "Bad value for initializer '$key': $found{$key}",
1607             'Usage' => "Initializer '$key' for class '$class' must be an object or ref of type '$type'");
1608             }
1609             }
1610              
1611             # Check type of each element in array
1612 19 100       44 if (defined($subtype)) {
1613 2 50       14 if ($subtype =~ /^scalar$/i) {
    100          
1614             # Scalar elements
1615 0         0 foreach my $elem (@{$found{$key}}) {
  0         0  
1616 0 0       0 if (ref($elem)) {
1617 0         0 OIO::Args->die(
1618             'message' => "Bad value for initializer '$key': $elem",
1619             'Usage' => "Values making up initializer '$key' for class '$class' must be scalars");
1620             }
1621             }
1622             } elsif ($subtype =~ /^num(?:ber|eric)?$/i) {
1623             # Numeric elements
1624 1         2 foreach my $elem (@{$found{$key}}) {
  1         4  
1625 3 50       9 if (! Scalar::Util::looks_like_number($elem)) {
1626 0         0 OIO::Args->die(
1627             'message' => "Bad value for initializer '$key': $elem",
1628             'Usage' => "Values making up initializer '$key' for class '$class' must be numeric");
1629             }
1630             }
1631             } else {
1632 1         2 foreach my $elem (@{$found{$key}}) {
  1         3  
1633 2 50       3 if (! is_it($elem, $subtype)) {
1634 0         0 OIO::Args->die(
1635             'message' => "Bad value for initializer '$key': $elem",
1636             'Usage' => "Values making up Initializer '$key' for class '$class' must be objects or refs of type '$subtype'");
1637             }
1638             }
1639             }
1640             }
1641             }
1642              
1643             # If the destination field is specified, then put it in, and remove it
1644             # from the found args hash.
1645 207 100       384 if (my $field = $$spec_item{'_F'}) {
1646 193         415 $self->set($field, delete($found{$key}));
1647             }
1648             }
1649              
1650             # Done - return remaining found args
1651 130         409 return (\%found);
1652 53     54   60163 }
  53         68  
  53         188  
1653              
1654              
1655             # Object Constructor
1656             sub new :MergeArgs
1657             {
1658 219         262 my ($thing, $all_args) = @_;
1659 219   33     778 my $class = ref($thing) || $thing;
1660              
1661             # Can't call ->new() on this package
1662 219 50       441 if ($class eq 'Object::InsideOut') {
1663 0         0 OIO::Method->die('message' => q/'new' called on non-class 'Object::InsideOut'/);
1664             }
1665              
1666             # Perform package initialization, if required
1667 219         406 initialize();
1668              
1669             # Create a new 'bare' object
1670 219         440 my $self = _obj($class);
1671              
1672             # Object initialization activity caching
1673 219         327 my $have_cache = exists($GBL{'cache'}{$class});
1674 219 100       571 my %cache = ($have_cache) ? %{$GBL{'cache'}{$class}}
  109         365  
1675             : ( 'pre' => 0, 'def' => 0 );
1676              
1677             # Execute pre-initialization subroutines
1678 219 100 100     965 if ($cache{'pre'} || ! $have_cache) {
1679 112         167 my $preinit_subs = $GBL{'sub'}{'pre'};
1680 112 100       113 if (%{$preinit_subs}) {
  112         301  
1681 4         4 foreach my $pkg (@{$GBL{'tree'}{'bu'}{$class}}) {
  4         9  
1682 8 100       15 if (my $preinit = $$preinit_subs{$pkg}) {
1683 4         8 local $SIG{'__DIE__'} = 'OIO::trap';
1684 4         18 $self->$preinit($all_args);
1685 4 100       20 if ($have_cache) {
1686 2 50       6 last if (! (--$cache{'pre'}));
1687             } else {
1688 2         5 $cache{'pre'}++;
1689             }
1690             }
1691             }
1692             }
1693             }
1694              
1695 219         347 my $tree = $GBL{'tree'}{'td'}{$class};
1696              
1697             # Set any defaults
1698 219 100 100     840 if ($cache{'def'} || ! $have_cache) {
1699 132         130 foreach my $pkg (@{$tree}) {
  132         249  
1700 223 100       623 if (my $def = $GBL{'fld'}{'def'}{$pkg}) {
1701             $self->set($_->[0], $_->[1]->($self))
1702 28         24 foreach (@{$def});
  28         640  
1703 28 100       46 if ($have_cache) {
1704 22 50       52 last if (! (--$cache{'def'}));
1705             } else {
1706 6         11 $cache{'def'}++;
1707             }
1708             }
1709             }
1710             }
1711              
1712             # Process :InitArgs
1713 219         218 my %pkg_args;
1714 219         235 my $used_args = {};
1715 219         242 my $g_args = $GBL{'args'};
1716 219         172 foreach my $pkg (@{$tree}) {
  219         495  
1717 327 100       604 if (my $spec = $$g_args{$pkg}) {
1718 145         379 $pkg_args{$pkg} = _args($pkg, $self, $spec, $all_args, $used_args);
1719             }
1720             }
1721              
1722             # Call :Init subs
1723 204         269 my $init_subs = $GBL{'sub'}{'init'};
1724 204         175 foreach my $pkg (@{$tree}) {
  204         268  
1725 312 100       2836 if (my $init = $$init_subs{$pkg}) {
    100          
    100          
1726 53         164 local $SIG{'__DIE__'} = 'OIO::trap';
1727 53 100       98 if (exists($pkg_args{$pkg})) {
1728 49         545 $self->$init($pkg_args{$pkg});
1729             } else {
1730 4         10 $self->$init($all_args);
1731 4         427 undef($used_args);
1732             }
1733              
1734             } elsif (exists($pkg_args{$pkg})) {
1735 81 100       80 if (%{$pkg_args{$pkg}}) {
  81         205  
1736             # It's an error if there are unhandled args, but no :Init sub
1737             OIO::Args::Unhandled->die(
1738 2         4 'message' => "Unhandled parameter for class '$class': " . join(', ', keys(%{$pkg_args{$pkg}})),
  2         10  
1739             'Usage' => q/Add appropriate 'Field =>' designators to the :InitArgs hash/);
1740             }
1741              
1742             } elsif (exists($$all_args{$pkg})) {
1743             # It's an error if there are unhandled class-specific args
1744 1 50       4 if (ref($$all_args{$pkg}) ne 'HASH') {
1745 0         0 OIO::Args->die(
1746             'message' => "Bad class initializer for '$class'",
1747             'Usage' => q/Class initializers must be a hash ref/);
1748             }
1749             OIO::Args::Unhandled->die(
1750 1         3 'message' => "Unhandled parameter for class '$class': " . join(', ', keys(%{$$all_args{$pkg}})),
  1         5  
1751             'Usage' => q/Add :Init subroutine or :InitArgs hash/);
1752             }
1753             }
1754              
1755             # Any unused args?
1756 199 100       3452 if ($used_args) {
1757 195         176 my %pkgs;
1758 195         204 @pkgs{@{$tree}} = undef;
  195         344  
1759 195         466 foreach my $key (keys(%$all_args)) {
1760 172 100       248 if (exists($pkgs{$key})) {
1761 19         18 foreach my $subkey (keys(%{$$all_args{$key}})) {
  19         45  
1762 31 100       68 if (! exists($$used_args{$key}{$subkey})) {
1763 2         9 OIO::Args::Unhandled->die('message' => "Unhandled parameter for class '$key': $subkey");
1764             }
1765             }
1766             } else {
1767 153 100       302 if (! exists($$used_args{$key})) {
1768 3         21 OIO::Args::Unhandled->die('message' => "Unhandled parameter: $key");
1769             }
1770             }
1771             }
1772             }
1773              
1774             # Remember object initialization activity caching
1775 194 100       381 if (! $have_cache) {
1776 106         190 $GBL{'cache'}{$class} = \%cache;
1777             }
1778              
1779             # Done - return object
1780 194         1633 return ($self);
1781 53     54   30747 }
  53         72  
  53         185  
1782              
1783              
1784             # Creates a copy of an object
1785             sub clone
1786             {
1787 5     5 0 249 my ($parent, $is_deep) = @_; # Parent object and deep cloning flag
1788 5 100       12 $is_deep = ($is_deep) ? 'deep' : ''; # Deep clone the object?
1789              
1790             # Must call ->clone() as an object method
1791 5         15 my $class = Scalar::Util::blessed($parent);
1792 5 50       13 if (! $class) {
1793 0         0 OIO::Method->die('message' => q/'clone' called as a class method/);
1794             }
1795              
1796             # Create a new 'bare' object
1797 5         19 my $clone = _obj($class);
1798              
1799             # Flag for shared class
1800 5         8 my $am_sharing = is_sharing($class);
1801              
1802             # Clone the object
1803 5         8 my $fld_ref = $GBL{'fld'}{'ref'};
1804 5         10 my $weak = $GBL{'fld'}{'weak'};
1805 5         7 my $deep = $GBL{'fld'}{'deep'};
1806 5         6 my $repl = $GBL{'sub'}{'repl'};
1807 5         5 foreach my $pkg (@{$GBL{'tree'}{'td'}{$class}}) {
  5         14  
1808             # Clone field data from the parent
1809 7         6 foreach my $fld (@{$$fld_ref{$pkg}}) {
  7         11  
1810 7   66     18 my $fdeep = $is_deep || $$deep{$fld}; # Deep clone the field?
1811 7 50       11 lock($fld) if ($am_sharing);
1812 7 50       14 if (ref($fld) eq 'HASH') {
1813             $$fld{$$clone} = (! $fdeep) ? $$fld{$$parent}
1814             : ($am_sharing)
1815             ? Object::InsideOut::Util::clone_shared($$fld{$$parent})
1816 0 0       0 : Object::InsideOut::Util::clone($$fld{$$parent});
    0          
1817 0 0       0 if ($$weak{$fld}) {
1818 0         0 Scalar::Util::weaken($$fld{$$clone});
1819             }
1820             } else {
1821 7 50       26 $$fld[$$clone] = (! $fdeep) ? $$fld[$$parent]
    100          
1822             : ($am_sharing)
1823             ? Object::InsideOut::Util::clone_shared($$fld[$$parent])
1824             : Object::InsideOut::Util::clone($$fld[$$parent]);
1825 7 100       20 if ($$weak{$fld}) {
1826 1         4 Scalar::Util::weaken($$fld[$$clone]);
1827             }
1828             }
1829             }
1830              
1831             # Dispatch any special replication handling
1832 7 50       19 if (my $replicate = $$repl{$pkg}) {
1833 0         0 local $SIG{'__DIE__'} = 'OIO::trap';
1834 0         0 $parent->$replicate($clone, $is_deep);
1835             }
1836             }
1837              
1838             # Done - return clone
1839 5         10 return ($clone);
1840             }
1841              
1842              
1843             # Get a metadata object
1844             sub meta
1845             {
1846 19   66 19 1 1560 my $class = ref($_[0]) || $_[0];
1847              
1848             # No metadata for OIO
1849 19 100       37 if ($class eq 'Object::InsideOut') {
1850 1         13 OIO::Method->die('message' => q/'meta' called on non-class 'Object::InsideOut'/);
1851             }
1852              
1853 18         30 initialize(); # Perform package initialization, if required
1854              
1855 18         61 return (Object::InsideOut::Metadata->new('GBL' => \%GBL,
1856             'CLASS' => $class));
1857             }
1858              
1859              
1860             # Put data in a field, making sure that sharing is supported
1861             sub set
1862             {
1863 335     335 0 6299 my ($self, $field, $data) = @_;
1864              
1865             # Must call ->set() as an object method
1866 335 50       819 if (! Scalar::Util::blessed($self)) {
1867 0         0 OIO::Method->die('message' => q/'set' called as a class method/);
1868             }
1869              
1870             # Restrict usage to inside class hierarchy
1871 335 50       559 if (! $self->isa('Object::InsideOut')) {
1872 0         0 my $caller = caller();
1873 0         0 OIO::Method->die('message' => "Can't call restricted method 'inherit' from class '$caller'");
1874             }
1875              
1876             # Check usage
1877 335 50       505 if (! defined($field)) {
1878 0         0 OIO::Args->die(
1879             'message' => 'Missing field argument',
1880             'Usage' => '$obj->set($field_ref, $data)');
1881             }
1882 335         336 my $fld_type = ref($field);
1883 335 50 66     1127 if (! $fld_type || ($fld_type ne 'ARRAY' && $fld_type ne 'HASH')) {
      33        
1884 0         0 OIO::Args->die(
1885             'message' => 'Invalid field argument',
1886             'Usage' => '$obj->set($field_ref, $data)');
1887             }
1888              
1889             # Check data
1890 335         452 my $weak = $GBL{'fld'}{'weak'}{$field};
1891 335 50 66     574 if ($weak && ! ref($data)) {
1892 0         0 OIO::Args->die(
1893             'message' => "Bad argument: $data",
1894             'Usage' => q/Argument to specified field must be a reference/);
1895             }
1896              
1897             # Handle sharing
1898 335 50 33     679 if ($GBL{'share'}{'ok'} && threads::shared::is_shared($field)) {
1899 0         0 lock($field);
1900 0 0       0 if ($fld_type eq 'HASH') {
1901 0         0 $$field{$$self} = make_shared($data);
1902             } else {
1903 0         0 $$field[$$self] = make_shared($data);
1904             }
1905              
1906             } else {
1907             # No sharing - just store the data
1908 335 100       423 if ($fld_type eq 'HASH') {
1909 66         114 $$field{$$self} = $data;
1910             } else {
1911 269         369 $$field[$$self] = $data;
1912             }
1913             }
1914              
1915             # Weaken data, if required
1916 335 100       1851 if ($weak) {
1917 3 50       3 if ($fld_type eq 'HASH') {
1918 0         0 Scalar::Util::weaken($$field{$$self});
1919             } else {
1920 3         11 Scalar::Util::weaken($$field[$$self]);
1921             }
1922             }
1923             }
1924              
1925              
1926             # Object Destructor
1927             sub DESTROY
1928             {
1929 290     290   38582 my $self = shift;
1930 290         378 my $class = ref($self);
1931              
1932 290 100       1276 return if (! $$self);
1933              
1934             # Grab any error coming into this routine
1935 225         229 my $err = $@;
1936              
1937             # Preserve other error variables
1938 225         1081 local($!, $^E, $?);
1939              
1940             # Workaround for Perl's "in cleanup" bug
1941 225 50 33     554 if ($threads::shared::threads_shared && ! $GBL{'term'}) {
1942 0         0 eval {
1943 0         0 my $bug = keys(%{$GBL{'id'}{'obj'}})
1944 0         0 + keys(%{$GBL{'id'}{'reuse'}})
1945             + ((exists($GBL{'share'}{'obj'}))
1946 0 0       0 ? keys(%{$GBL{'share'}{'obj'}})
  0         0  
1947             : 0);
1948             };
1949 0 0       0 if ($@) {
1950 0         0 $GBL{'term'} = 1;
1951             }
1952             }
1953              
1954 225         207 eval {
1955 225         378 my $is_sharing = is_sharing($class);
1956 225 50       575 if ($is_sharing) {
    50          
1957             # Thread-shared object
1958 0         0 my $tid = $GBL{'tid'};
1959              
1960 0 0       0 if ($GBL{'term'}) {
    0          
1961 0 0       0 return if ($tid); # Continue only if main thread
1962              
1963             } elsif (exists($GBL{'share'}{'obj'})) {
1964 0         0 my $so_cl = $GBL{'share'}{'obj'}{$class};
1965 0 0       0 if (! exists($$so_cl{$$self})) {
1966             # This can happen when a non-shared object
1967             # is returned from a thread
1968 0         0 warn("ERROR: Attempt to DESTROY object ID $$self of class $class in thread ID $tid twice\n");
1969 0         0 return;
1970             }
1971              
1972             # Remove thread ID from this object's thread tracking list
1973             # NOTE: The threads->object() test was added for the case
1974             # where OIO objects are passed via Thead::Queue. I don't
1975             # know if this will cause problems with detached threads as
1976             # threads->object() returns undef for them. Also, the main
1977             # thread (0) is always a valid thread.
1978 0         0 lock($so_cl);
1979 0 0 0     0 if (@{$$so_cl{$$self}} = grep { ($_ != $tid) &&
  0 0       0  
  0         0  
1980             (($_ == 0) || threads->object($_)) }
1981 0         0 @{$$so_cl{$$self}}) {
1982 0         0 return;
1983             }
1984              
1985             # Delete the object from the thread tracking registry
1986 0         0 delete($$so_cl{$$self});
1987             }
1988              
1989             } elsif ($threads::threads) {
1990 0         0 my $obj_cl = $GBL{'obj'}{$class};
1991 0 0       0 if (! exists($$obj_cl{$$self})) {
1992 0         0 warn("ERROR: Attempt to DESTROY object ID $$self of class $class twice\n");
1993 0         0 return;
1994             }
1995              
1996             # Delete this non-thread-shared object from the thread cloning
1997             # registry
1998 0         0 delete($$obj_cl{$$self});
1999             }
2000              
2001             # Dispatch any special destruction handling
2002 225         181 my $dest_err;
2003 225         317 my $dest_subs = $GBL{'sub'}{'dest'};
2004 225         406 my $fld_refs = $GBL{'fld'}{'ref'};
2005 225         182 foreach my $pkg (@{$GBL{'tree'}{'bu'}{$class}}) {
  225         538  
2006 345 100       689 if (my $destroy = $$dest_subs{$pkg}) {
2007 6         5 eval {
2008 6         15 local $SIG{'__DIE__'} = 'OIO::trap';
2009 6         14 $self->$destroy();
2010             };
2011 6         75 $dest_err = OIO::combine($dest_err, $@);
2012             }
2013             }
2014              
2015             # Delete object field data
2016 225         207 foreach my $pkg (@{$GBL{'tree'}{'bu'}{$class}}) {
  225         345  
2017 345         252 foreach my $fld (@{$$fld_refs{$pkg}}) {
  345         459  
2018             # If sharing, then must lock object field
2019 669 50       752 lock($fld) if ($is_sharing);
2020 669 100       775 if (ref($fld) eq 'HASH') {
2021 91 50       122 if ($is_sharing) {
2022             # Workaround for Perl's "in cleanup" bug
2023 0 0       0 next if ! defined($$fld{$$self});
2024             }
2025 91         160 delete($$fld{$$self});
2026             } else {
2027 578 50       640 if ($is_sharing) {
2028             # Workaround for Perl's "in cleanup" bug
2029 0 0       0 next if ! defined($$fld[$$self]);
2030             }
2031 578         751 undef($$fld[$$self]);
2032             }
2033             }
2034             }
2035              
2036             # Unlock the object
2037 225 50       678 Internals::SvREADONLY($$self, 0) if ($] >= 5.008003);
2038              
2039             # Reclaim the object ID if applicable
2040 225 100       629 if ($GBL{'sub'}{'id'}{$class}{'code'} == \&_ID) {
2041 215         356 _ID($class, $$self);
2042             }
2043              
2044             # Erase the object ID - just in case
2045 225         245 $$self = undef;
2046              
2047             # Propagate any errors
2048 225 100       434 if ($dest_err) {
2049 3         12 die($dest_err);
2050             }
2051             };
2052              
2053             # Propagate any errors
2054 225 100 66     1990 if ($err || $@) {
2055 54         413 $@ = OIO::combine($err, $@);
2056 54 100       174 die("$@") if (! $err);
2057             }
2058             }
2059              
2060              
2061             # OIO specific ->can()
2062             sub can :Method(Object)
2063             {
2064 128     128 1 7263 my ($thing, $method) = @_;
2065              
2066 128 50       233 return if (! defined($thing));
2067              
2068             # Metadata call for methods
2069 128 50       205 if (@_ == 1) {
2070 0         0 my $meths = Object::InsideOut::meta($thing)->get_methods();
2071 0 0       0 return (wantarray()) ? (keys(%$meths)) : [ keys(%$meths) ];
2072             }
2073              
2074 128 50       175 return if (! defined($method));
2075              
2076             # Try UNIVERSAL::can()
2077 128         84 eval { $thing->Object::InsideOut::SUPER::can($method) };
  128         502  
2078 53     54   58321 }
  53         72  
  53         205  
2079              
2080              
2081             # OIO specific ->isa()
2082             sub isa :Method(Object)
2083             {
2084 909     909 1 26233 my ($thing, $type) = @_;
2085              
2086 909 50       1363 return ('') if (! defined($thing));
2087              
2088             # Metadata call for classes
2089 909 50       1403 if (@_ == 1) {
2090 0         0 return Object::InsideOut::meta($thing)->get_classes();
2091             }
2092              
2093             # Workaround for Perl bug #47233
2094 909 50       1311 return ('') if (! defined($type));
2095              
2096             # Try UNIVERSAL::isa()
2097 909         809 eval { $thing->Object::InsideOut::SUPER::isa($type); }
  909         4471  
2098 53     53   7765 }
  53         74  
  53         184  
2099              
2100              
2101             ### Serialization Support Using Storable ###
2102              
2103             sub STORABLE_freeze :Sub
2104             {
2105 5     5 0 75 my ($self, $cloning) = @_;
2106 5         16 return ('', $self->dump());
2107 53     53   5613 }
  53         66  
  53         182  
2108              
2109             sub STORABLE_thaw :Sub
2110             {
2111 5     5 0 89 my ($obj, $cloning, $data);
2112 5 50       8 if (@_ == 4) {
2113 5         10 ($obj, $cloning, undef, $data) = @_;
2114             } else {
2115             # Backward compatibility
2116 0         0 ($obj, $cloning, $data) = @_;
2117             }
2118              
2119             # Recreate the object
2120 5         4 my $self;
2121 5         6 eval {
2122 5         14 $self = Object::InsideOut->pump($data);
2123             };
2124 5 100       14 if ($@) {
2125 1         7 die($@->as_string()); # Storable doesn't like exception objects
2126             }
2127              
2128             # Transfer the ID to Storable's object
2129 4         5 $$obj = $$self;
2130             # Make object shared, if applicable
2131 4 50       6 if (is_sharing(ref($obj))) {
2132 0         0 threads::shared::share($obj);
2133             }
2134             # Make object readonly
2135 4 50       9 if ($] >= 5.008003) {
2136 4         6 Internals::SvREADONLY($$obj, 1);
2137 4         6 Internals::SvREADONLY($$self, 0);
2138             }
2139             # Prevent object destruction
2140 4         10 undef($$self);
2141 53     53   9856 }
  53         64  
  53         152  
2142              
2143              
2144             ### Accessor Generator ###
2145              
2146             # Names a field for dumping
2147             sub add_dump_field :Sub(Private)
2148             {
2149 218         319 my ($src, $name, $fld, $dump) = @_;
2150              
2151             # Name already in use for different field
2152 218 50 66     546 if (exists($$dump{$name}) && ($fld != $$dump{$name}{'fld'})) {
2153 0         0 return ('conflict');
2154             }
2155              
2156             # Entry already exists for field
2157 218 100       510 if (my ($old_name) = grep { $$dump{$_}{'fld'} == $fld } keys(%$dump)) {
  705         978  
2158 21         30 my $old_src = $$dump{$old_name}{'src'};
2159 21 100       60 if ($old_src eq 'Name') {
    100          
    50          
    100          
    50          
    50          
    0          
2160 7         28 return ('named');
2161             } elsif ($src eq 'Name') {
2162 11         17 delete($$dump{$old_name});
2163             } elsif ($old_src eq 'InitArgs') {
2164 0         0 return ('named');
2165             } elsif ($src eq 'InitArgs') {
2166 2         6 delete($$dump{$old_name});
2167             } elsif ($old_src eq 'Get') {
2168 0         0 return ('named');
2169             } elsif ($src eq 'Get') {
2170 1         2 delete($$dump{$old_name});
2171             } elsif ($old_src eq 'Set') {
2172 0         0 return ('named');
2173             } else {
2174 0         0 delete($$dump{$old_name}); # Shouldn't get here
2175             }
2176             }
2177              
2178 211         531 $$dump{$name} = { fld => $fld, src => $src };
2179 211         536 return ('okay');
2180 53     53   11516 }
  53         65  
  53         177  
2181              
2182              
2183             # Utility sub to infer class API from symbol table...
2184             # (replaces ->meta->get_methods for non-OIO classes)
2185             sub get_symtab_methods_for :Sub(Private)
2186             {
2187 1         2 my ($class_delegated_to) = @_;
2188              
2189 1         1 my %methods; #...collects the methods that are found
2190              
2191             # Walk the class's inheritance tree...
2192 1         2 my @hierarchy = ($class_delegated_to);
2193 1         11 while (my $classname = shift @hierarchy) {
2194 53     53   5709 no strict 'refs'; #...because symbols are inherently symbolic
  53         75  
  53         4678  
2195              
2196             # Accumulate ancestors for subsequent investigation...
2197 4         3 push(@hierarchy, @{$classname.'::ISA'});
  4         13  
2198              
2199             # Grab and remember all subs from this class's symbol table...
2200 4         1 for my $symname (keys(%{$classname.'::'})) {
  4         8  
2201             # Only want symbols that define subroutines...
2202 18 100       8 next if !*{$classname.'::'.$symname}{CODE};
  18         40  
2203             # Save the necessary info...
2204 7         14 $methods{$symname}{'class'} = $class_delegated_to;
2205             }
2206             }
2207              
2208 1         3 return \%methods
2209 53     53   207 }
  53         72  
  53         184  
2210              
2211              
2212             # Utility sub to handle :Handles(Class::*) feature...
2213             sub get_class_methods :Sub(Private)
2214             {
2215 8         8 my ($class_delegated_from, $class_delegated_to) = @_;
2216              
2217             # Not expandable...
2218 8 100       25 return $class_delegated_to if $class_delegated_to !~ /::/;
2219              
2220             # Clean up any trailing ::...
2221 3         9 $class_delegated_to =~ s/::+$//;
2222              
2223             # Grab all known method names of specified class...
2224 3 100       31 my $methods = $class_delegated_to->can('meta')
2225             ? $class_delegated_to->meta()->get_methods()
2226             : get_symtab_methods_for($class_delegated_to);
2227              
2228             # Select the "real" ones...
2229 53     53   7384 no strict 'refs';
  53         75  
  53         5500  
2230             return grep {
2231             # Ignore "infrastructure" methods...
2232             !/^(?:new|clone|meta|set)$/
2233              
2234             # Ignore Object::InsideOut internal methods...
2235             && $methods->{$_}{class} eq $class_delegated_to
2236              
2237             # Ignore methods already installed...
2238 13         71 && !*{"${class_delegated_from}::$_"}{CODE}
2239              
2240 3 100 100     6 } keys %{$methods};
  40         151  
  3         10  
2241 53     53   204 }
  53         58  
  53         168  
2242              
2243              
2244             # Creates object data accessors for classes
2245             sub create_accessors :Sub(Private)
2246             {
2247 368         463 my ($pkg, $field_ref, $attr, $use_want) = @_;
2248              
2249             # Extract info from attribute
2250 368         953 my ($kind) = $attr =~ /^(\w+)/;
2251 368         1016 my ($name) = $attr =~ /^\w+\s*\(\s*'?([\w:()]*)'?\s*\)$/;
2252 368         666 my ($decl) = $attr =~ /^\w+\s*\(\s*(.*)\s*\)/;
2253 368         262 my $type_code;
2254              
2255 368 100 100     1184 if ($name) {
    100          
    100          
    100          
    100          
2256 119         255 $decl = "{'$kind'=>'$name'}";
2257 119         129 undef($name);
2258             } elsif (! $decl) {
2259 133 50       399 return if ($kind =~ /^Field/i);
2260 0         0 OIO::Attribute->die(
2261             'message' => "Missing declarations for attribute in package '$pkg'",
2262             'Attribute' => $attr);
2263             } elsif (($kind =~ /^Type/i) && ($decl =~ /^(?:sub|\\&)/)) {
2264 5         7 $type_code = $decl;
2265 5         16 $decl = "{'$kind'=>$decl}";
2266             } elsif ($kind =~ /^Hand/i) {
2267 2         9 $decl =~ s/['",]/ /g;
2268 2         5 $decl = "{'$kind'=>'$decl'}";
2269             } elsif ($kind !~ /^Field/i) {
2270 12 50       89 if (! ($decl =~ s/'?name'?\s*=>/'$kind'=>/i)) {
2271 0         0 OIO::Attribute->die(
2272             'message' => "Missing 'Name' parameter for attribute in package '$pkg'",
2273             'Attribute' => $attr);
2274             }
2275             }
2276              
2277             # Parse the accessor declaration
2278 235         200 my $acc_spec;
2279             {
2280             # Ensure the attribute declaration is a hash
2281 235 100       182 if ($decl !~ /^{/) {
  235         517  
2282 93         207 $decl = "{ $decl }";
2283             }
2284              
2285 235         196 my @errs;
2286 235         1140 local $SIG{'__WARN__'} = sub { push(@errs, @_); };
  0         0  
2287              
2288 235     71   13332 eval "package $pkg; use $]; \$acc_spec = $decl";
  82     36   1405  
  134     22   8327  
  89     17   1597  
  97     12   1089  
  71     11   484  
  84     11   1945  
  49     11   1336  
  64     11   2051  
  52     10   277  
  53         1057  
  35         1343  
  52         931  
  50         640  
  49         217  
  40         216  
  37         104  
  59         6683  
  54         1458  
  42         828  
  68         502  
  35         1302  
  21         803  
  22         526  
  22         297  
  21         46  
  18         368  
  37         2462  
  31         171  
  22         542  
  8         341  
  20         183  
  19         465  
  11         96  
  23         2973  
  11         57  
  12         24  
  7         15  
  37         307  
  12         36  
  32         157  
  34         1187  
  5         11  
  13         21  
  16         35  
  23         2958  
  19         80  
  2         151  
  1         6  
  0         0  
  1         2  
  1         3  
  3         4  
  0         0  
  1         3  
  1         2  
  1         4  
  0         0  
  0         0  
  1         6  
  1         8  
  20         2443  
  20         53  
  2         5  
  18         12  
  18         29  
  2         226  
  2         3  
  18         1214  
  8         9  
  8         11  
  8         15  
  6         11  
2289              
2290 235 50 33     1828 if ($@ || @errs) {
2291 0   0     0 my ($err) = split(/ at /, $@ || join(" | ", @errs));
2292 0         0 OIO::Attribute->die(
2293             'message' => "Malformed attribute in package '$pkg'",
2294             'Error' => $err,
2295             'Attribute' => $attr);
2296             }
2297             }
2298              
2299 235         479 my $fld_type = $GBL{'fld'}{'type'};
2300              
2301             # Get info for accessors/delegators
2302 235         221 my ($get, $set, $return, $private, $restricted, $lvalue, $arg, $pre, $delegate);
2303 235         231 my $accessor_type = 'accessor';
2304 235 100       727 if ($kind !~ /^arg$/i) {
2305 197         160 foreach my $key (keys(%{$acc_spec})) {
  197         528  
2306 284         349 my $key_uc = uc($key);
2307 284         279 my $val = $$acc_spec{$key};
2308              
2309             # :InitArgs
2310 284 100       927 if ($key_uc =~ /ALL/) {
    100          
    100          
2311 16         19 $arg = $val;
2312 16 50       86 if ($key_uc eq 'ALL') {
2313 16         22 $key_uc = 'ACC';
2314             }
2315             } elsif ($key_uc =~ /R(?:EAD)?O(?:NLY)?/) {
2316 4         5 $arg = $val;
2317 4 100       10 if ($key_uc =~ /^R(?:EAD)?O(?:NLY)?$/) {
2318 3         4 $key_uc = 'GET';
2319             }
2320             } elsif ($key_uc =~ /ARG/) {
2321 2         4 $arg = $val;
2322 2         2 $key_uc = 'IGNORE';
2323             }
2324              
2325             # Standard accessors
2326 284 100 33     1683 if ($key_uc =~ /^ST.*D.*R(?:EAD)?O(?:NLY)?/) {
    100          
    100          
    50          
    50          
    50          
    100          
    100          
    100          
    100          
    100          
    50          
    100          
    50          
2327 1         2 $get = 'get_' . $val;
2328             }
2329             elsif ($key_uc =~ /^ST.*D/) {
2330 19         27 $get = 'get_' . $val;
2331 19         26 $set = 'set_' . $val;
2332             }
2333             # Get and/or set accessors
2334             elsif ($key_uc =~ /^ACC|^COM|^MUT|[GS]ET/) {
2335             # Get accessor
2336 144 100       586 if ($key_uc =~ /ACC|COM|MUT|GET/) {
2337 133         148 $get = $val;
2338             }
2339             # Set accessor
2340 144 100       411 if ($key_uc =~ /ACC|COM|MUT|SET/) {
2341 90         98 $set = $val;
2342             }
2343             }
2344             # Deep clone the field
2345             elsif ($key_uc eq 'COPY' || $key_uc eq 'CLONE') {
2346 0 0       0 if (uc($val) eq 'DEEP') {
2347 0         0 $GBL{'fld'}{'deep'}{$field_ref} = 1;
2348             }
2349 0         0 next;
2350             } elsif ($key_uc eq 'DEEP') {
2351 0 0       0 if ($val) {
2352 0         0 $GBL{'fld'}{'deep'}{$field_ref} = 1;
2353             }
2354 0         0 next;
2355             }
2356             # Store weakened refs
2357             elsif ($key_uc =~ /^WEAK/) {
2358 0 0       0 if ($val) {
2359 0         0 $GBL{'fld'}{'weak'}{$field_ref} = 1;
2360             }
2361 0         0 next;
2362             }
2363             # Field type checking for set accessor
2364             elsif ($key_uc eq 'TYPE') {
2365             # Check type-checking setting and set default
2366 62 50 66     263 if (!$val || (ref($val) && (ref($val) ne 'CODE'))) {
      33        
2367 0         0 OIO::Attribute->die(
2368             'message' => "Can't create accessor method for package '$pkg'",
2369             'Info' => q/Bad 'Type' specifier: Must be a 'string' or code ref/,
2370             'Attribute' => $attr);
2371             }
2372             # Normalize type declaration
2373 62 100       103 if (! ref($val)) {
2374 52         85 $val =~ s/\s//g;
2375 52         45 my $subtype;
2376 52 100       111 if ($val =~ /^(.*)\((.+)\)$/i) {
2377 6         16 $val = $1;
2378 6         15 $subtype = $2;
2379 6 100       35 if ($subtype =~ /^num(?:ber|eric)?$/i) {
    50          
2380 3         7 $subtype = 'numeric';
2381             } elsif ($subtype =~ /^scalar$/i) {
2382 0         0 $subtype = 'scalar';
2383             }
2384             }
2385 52 100       293 if ($val =~ /^num(?:ber|eric)?$/i) {
    100          
    100          
    100          
    100          
2386 15         24 $val = 'numeric';
2387             } elsif ($val =~ /^scalar$/i) {
2388 1         2 $val = 'scalar';
2389             } elsif ($val =~ /^(?:list|array)$/i) {
2390 11         19 $val = 'list';
2391             } elsif (uc($val) eq 'HASH') {
2392 4         7 $val = 'HASH';
2393             } elsif ($val =~ /^(hash|array|scalar)_?ref$/i) {
2394 9         22 $val = uc($1) . '_ref';
2395             }
2396 52 100       95 if ($subtype) {
2397 6         18 $val .= "($subtype)";
2398             }
2399             }
2400 62         131 my $type = {
2401             type => $val,
2402             code => $type_code,
2403             };
2404 62         137 $$fld_type{$field_ref} = $type;
2405 62         62 push(@{$GBL{'fld'}{'regen'}{'type'}}, [ $field_ref, $type ]);
  62         163  
2406 62         121 next;
2407             }
2408             # Field name for ->dump()
2409             elsif ($key_uc eq 'NAME') {
2410 3         6 $name = $val;
2411             }
2412             # Set accessor return type
2413             elsif ($key_uc =~ /^RET(?:URN)?$/) {
2414 28         34 $return = uc($val);
2415             }
2416             # Set accessor permission
2417             elsif ($key_uc =~ /^PERM|^PRIV|^REST/) {
2418 4 50       10 if ($key_uc =~ /^PERM/) {
    0          
    0          
2419 4 100       18 if ($val =~ /^PRIV/i) {
    50          
2420 1         4 my @exempt = split(/[(),\s]+/, $val);
2421 1         2 @exempt = grep { $_ } @exempt;
  1         3  
2422 1         2 shift(@exempt);
2423 1         2 unshift(@exempt, $pkg);
2424 1         3 $private = "'" . join("','", @exempt) . "'";
2425             } elsif ($val =~ /^REST/i) {
2426 3         12 my @exempt = split(/[(),\s]+/, $val);
2427 3         6 @exempt = grep { $_ } @exempt;
  4         9  
2428 3         4 shift(@exempt);
2429 3         9 $restricted = "'" . join("','", @exempt) . "'";
2430             }
2431             } elsif ($key_uc =~ /^PRIV/) {
2432 0 0       0 if ($val) {
2433 0         0 $private = "'$pkg'";
2434             }
2435             } elsif ($key_uc =~ /^REST/) {
2436 0 0       0 if ($val) {
2437 0         0 $restricted = '';
2438             }
2439             }
2440             }
2441             # :lvalue accessor
2442             elsif ($key_uc =~ /^LV/) {
2443 14 100 66     57 if ($val && !Scalar::Util::looks_like_number($val)) {
2444 9         9 $get = $val;
2445 9         9 $set = $val;
2446 9         8 $lvalue = 1;
2447             } else {
2448 5         6 $lvalue = $val;
2449             }
2450             }
2451             # Preprocessor
2452             elsif ($key_uc =~ /^PRE/) {
2453 0         0 $pre = $val;
2454 0 0       0 if (ref($pre) ne 'CODE') {
2455 0         0 OIO::Attribute->die(
2456             'message' => "Can't create accessor method for package '$pkg'",
2457             'Info' => q/Bad 'Preprocessor' specifier: Must be a code ref/,
2458             'Attribute' => $attr);
2459             }
2460             }
2461             # Delegator
2462             elsif ($key_uc =~ /^HAND/) {
2463 7         8 $delegate = $val;
2464 7         21 $accessor_type = 'delegator';
2465             }
2466             # Unknown parameter
2467             elsif ($key_uc ne 'IGNORE') {
2468 0         0 OIO::Attribute->die(
2469             'message' => "Can't create accessor method for package '$pkg'",
2470             'Info' => "Unknown accessor specifier: $key");
2471             }
2472              
2473             # $val must have a usable value
2474 222 50 33     932 if (! defined($val) || $val eq '') {
2475 0         0 OIO::Attribute->die(
2476             'message' => "Invalid '$key' entry in attribute",
2477             'Attribute' => $attr);
2478             }
2479             }
2480             }
2481              
2482             # :InitArgs
2483 235 100 100     963 if ($arg || ($kind =~ /^ARG$/i)) {
2484 60         80 my $g_args = $GBL{'args'};
2485 60 100       137 if (! exists($$g_args{$pkg})) {
2486 28         51 $$g_args{$pkg} = {};
2487             }
2488 60         70 $g_args = $$g_args{$pkg};
2489 60 100       114 if (!$arg) {
2490 38         187 $arg = hash_re($acc_spec, qr/^ARG$/i);
2491 38         114 $$g_args{$arg} = normalize($acc_spec);
2492             }
2493 60 50       111 if (!defined($name)) {
2494 60         72 $name = $arg;
2495             }
2496 60         98 $$g_args{$arg}{'_F'} = $field_ref;
2497             # Add type to :InitArgs
2498 60 100 66     230 if ($$fld_type{$field_ref} && ! exists($$g_args{$arg}{'_T'})) {
2499 14         24 $$g_args{$arg}{'_T'} = $$fld_type{$field_ref}{'type'};
2500             }
2501              
2502             # Add default to :InitArgs
2503 60 100       171 if (my $g_def = delete($GBL{'fld'}{'def'}{$pkg})) {
2504 23         13 my @defs;
2505 23         22 foreach my $item (@{$g_def}) {
  23         29  
2506 100 100       122 if ($field_ref == $$item[0]) {
2507 8         14 $$g_args{$arg}{'_D'} = $$item[1];
2508             } else {
2509 92         85 push(@defs, $item);
2510             }
2511             }
2512 23 100       33 if (@defs) {
2513 22         39 $GBL{'fld'}{'def'}{$pkg} = \@defs;
2514             }
2515             }
2516             }
2517              
2518             # Add field info for dump()
2519 235         327 my $dump = $GBL{'dump'}{'fld'};
2520 235   100     553 $$dump{$pkg} ||= {};
2521 235         216 $dump = $$dump{$pkg};
2522              
2523 235 100 66     592 if ($name) {
    100          
    100          
    100          
2524 63 50       137 if (add_dump_field('Name', $name, $field_ref, $dump) eq 'conflict') {
2525 0         0 OIO::Attribute->die(
2526             'message' => "Can't create accessor method for package '$pkg'",
2527             'Info' => "'$name' already specified for another field using '$$dump{$name}{'src'}'",
2528             'Attribute' => $attr);
2529             }
2530             # Done if only 'Name' present
2531 63 50 66     293 if (! $get && ! $set && ! $return && ! $lvalue) {
      33        
      33        
2532 39         82 return;
2533             }
2534             } elsif ($get) {
2535 138 50       266 if (add_dump_field('Get', $get, $field_ref, $dump) eq 'conflict') {
2536 0         0 OIO::Attribute->die(
2537             'message' => "Can't create accessor method for package '$pkg'",
2538             'Info' => "'$get' already specified for another field using '$$dump{$get}{'src'}'",
2539             'Attribute' => $attr);
2540             }
2541             } elsif ($set) {
2542 3 50       8 if (add_dump_field('Set', $set, $field_ref, $dump) eq 'conflict') {
2543 0         0 OIO::Attribute->die(
2544             'message' => "Can't create accessor method for package '$pkg'",
2545             'Info' => "'$set' already specified for another field using '$$dump{$set}{'src'}'",
2546             'Attribute' => $attr);
2547             }
2548             } elsif (! $return && ! $lvalue && ! $delegate) {
2549 27         50 return;
2550             }
2551              
2552             # If 'RETURN' or 'LVALUE', need 'SET', too
2553 169 50 100     698 if (($return || $lvalue) && ! $set) {
      66        
2554 0         0 OIO::Attribute->die(
2555             'message' => "Can't create accessor method for package '$pkg'",
2556             'Info' => "No set accessor specified to go with 'RETURN'/'LVALUE'",
2557             'Attribute' => $attr);
2558             }
2559              
2560             # Check for name conflict
2561 169         214 foreach my $method ($get, $set) {
2562 338 100       504 if ($method) {
2563 53     53   90289 no strict 'refs';
  53         78  
  53         41414  
2564             # Do not overwrite existing methods
2565 280 50       195 if (*{$pkg.'::'.$method}{CODE}) {
  280         1213  
2566 0         0 OIO::Attribute->die(
2567             'message' => q/Can't create accessor method/,
2568             'Info' => "Method '$method' already exists in class '$pkg'",
2569             'Attribute' => $attr);
2570             }
2571             }
2572             }
2573              
2574             # Check return type and set default
2575 169 100 100     582 if (! defined($return) || $return eq 'NEW') {
    100 100        
    50 66        
      66        
2576 150         172 $return = 'NEW';
2577             } elsif ($return eq 'OLD' || $return =~ /^PREV(?:IOUS)?$/ || $return eq 'PRIOR') {
2578 10         11 $return = 'OLD';
2579             } elsif ($return eq 'SELF' || $return =~ /^OBJ(?:ECT)?$/) {
2580 9         12 $return = 'SELF';
2581             } else {
2582 0         0 OIO::Attribute->die(
2583             'message' => q/Can't create accessor method/,
2584             'Info' => "Invalid setting for 'RETURN': $return",
2585             'Attribute' => $attr);
2586             }
2587              
2588             # Get type checking (if any)
2589 169         239 my ($type, $subtype, $is_ref) = ('NONE', '', 0);
2590 169 100       393 if ($$fld_type{$field_ref}) {
2591 63         91 $type = $$fld_type{$field_ref}{'type'};
2592 63 100       118 if (! ref($type)) {
2593 52 100       136 if ($type =~ /^(.*)\((.+)\)$/i) {
2594 5         15 $type = $1;
2595 5         12 $subtype = $2;
2596             }
2597 52 100       127 if ($type =~ /^(HASH|ARRAY|SCALAR)_ref$/) {
2598 9         19 $type = $1;
2599 9         11 $is_ref = 1;
2600             }
2601             }
2602             }
2603 169 50 66     290 if ($subtype && ($type ne 'list' && $type ne 'ARRAY')) {
      66        
2604 0         0 OIO::Attribute->die(
2605             'message' => "Invalid type specification for package '$pkg'",
2606             'Info' => "Type '$type' cannot have subtypes",
2607             'Attribute' => $attr);
2608             }
2609              
2610             # Metadata
2611 169         204 my %meta;
2612 169 100       268 if ($set) {
2613 118 100 100     560 $meta{$set}{'kind'} = ($get && ($get eq $set)) ? 'accessor' : 'set';
2614 118 100       217 if ($lvalue) {
2615 14         16 $meta{$set}{'lvalue'} = 1;
2616             }
2617 118         214 $meta{$set}{'return'} = lc($return);
2618             # Type
2619 118 100       341 if (ref($type)) {
    100          
2620 9         16 $meta{$set}{'type'} = $$fld_type{$field_ref}{'code'};
2621             } elsif ($type ne 'NONE') {
2622 48         68 $meta{$set}{'type'} = $type;
2623             }
2624 118 100       197 if ($subtype) {
2625 5         14 $meta{$set}{'type'} .= "($subtype)";
2626             }
2627             }
2628 169 100 100     707 if ($get && (!$set || ($get ne $set))) {
      66        
2629 74         143 $meta{$get}{'kind'} = 'get';
2630             }
2631 169         192 foreach my $meth ($get, $set) {
2632 338 100       457 next if (! $meth);
2633             # Permissions
2634 280 100       604 if (defined($private)) {
    100          
2635 2         3 $meta{$meth}{'hidden'} = 1;
2636             } elsif (defined($restricted)) {
2637 5         9 $meta{$meth}{'restricted'} = 1;
2638             }
2639             }
2640 169         511 add_meta($pkg, \%meta);
2641              
2642 169         275 my $weak = $GBL{'fld'}{'weak'}{$field_ref};
2643              
2644             # Code to be eval'ed into subroutines
2645 169         373 my $code = "package $pkg;\n";
2646              
2647             # Create an :lvalue accessor
2648 169 100       375 if ($lvalue) {
    100          
2649 14         30 $code .= create_lvalue_accessor($pkg, $set, $field_ref, $get,
2650             $type, $is_ref, $subtype,
2651             $name, $return, $private,
2652             $restricted, $weak, $pre);
2653             }
2654              
2655             # Create 'set' or combination accessor
2656             elsif ($set) {
2657             # Begin with subroutine declaration in the appropriate package
2658 104         179 $code .= "*${pkg}::$set = sub {\n";
2659              
2660 104         196 $code .= preamble_code($pkg, $set, $private, $restricted);
2661              
2662 104 100       216 my $fld_str = (ref($field_ref) eq 'HASH') ? "\$field->\{\${\$_[0]}}" : "\$field->\[\${\$_[0]}]";
2663              
2664             # Add GET portion for combination accessor
2665 104 100 100     377 if ($get && ($get eq $set)) {
2666 77         145 $code .= " return ($fld_str) if (\@_ == 1);\n";
2667             }
2668              
2669             # If set only, then must have at least one arg
2670             else {
2671 27         70 $code .= <<"_CHECK_ARGS_";
2672             if (\@_ < 2) {
2673             OIO::Args->die(
2674             'message' => q/Missing arg(s) to '$pkg->$set'/,
2675             'location' => [ caller() ]);
2676             }
2677             _CHECK_ARGS_
2678             }
2679              
2680             # Add preprocessing code block
2681 104 50       189 if ($pre) {
2682 0         0 $code .= <<"_PRE_";
2683             {
2684             my \@errs;
2685             local \$SIG{'__WARN__'} = sub { push(\@errs, \@_); };
2686             eval {
2687             my \$self = shift;
2688             \@_ = (\$self, \$preproc->(\$self, \$field, \@_));
2689             };
2690             if (\$@ || \@errs) {
2691             my (\$err) = split(/ at /, \$@ || join(" | ", \@errs));
2692             OIO::Code->die(
2693             'message' => q/Problem with preprocessing routine for '$pkg->$set'/,
2694             'Error' => \$err);
2695             }
2696             }
2697             _PRE_
2698             }
2699              
2700             # Add data type checking
2701 104         206 my ($type_code, $arg_str) = type_code($pkg, $set, $weak,
2702             $type, $is_ref, $subtype);
2703 104         130 $code .= $type_code;
2704              
2705             # Add field locking code if sharing
2706 104 50       205 if (is_sharing($pkg)) {
2707 0         0 $code .= " lock(\$field);\n"
2708             }
2709              
2710             # Grab 'OLD' value
2711 104 100       201 if ($return eq 'OLD') {
2712 6         11 $code .= " my \$ret = $fld_str;\n";
2713             }
2714              
2715             # Add actual 'set' code
2716 104 50       153 $code .= (is_sharing($pkg))
2717             ? " $fld_str = Object::InsideOut::Util::make_shared($arg_str);\n"
2718             : " $fld_str = $arg_str;\n";
2719 104 100       179 if ($weak) {
2720 1         3 $code .= " Scalar::Util::weaken($fld_str);\n";
2721             }
2722              
2723             # Add code for return value
2724 104 100       375 if ($return eq 'SELF') {
    100          
    100          
    100          
2725 5         5 $code .= " \$_[0];\n";
2726             } elsif ($return eq 'OLD') {
2727 6 100       12 if ($use_want) {
2728 4         5 $code .= " ((Want::wantref() eq 'OBJECT') && !Scalar::Util::blessed(\$ret)) ? \$_[0] : ";
2729             }
2730 6         7 $code .= "\$ret;\n";
2731             } elsif ($use_want) {
2732 4         7 $code .= " ((Want::wantref() eq 'OBJECT') && !Scalar::Util::blessed($fld_str)) ? \$_[0] : $fld_str;\n";
2733             } elsif ($weak) {
2734 1         2 $code .= " $fld_str;\n";
2735             }
2736              
2737             # Done
2738 104         142 $code .= "};\n";
2739             }
2740 169 100       320 undef($type) if (! ref($type));
2741              
2742             # Create 'get' accessor
2743 169 100 100     711 if ($get && (!$set || ($get ne $set))) {
      66        
2744 74 100       174 $code .= "*${pkg}::$get = sub {\n"
2745              
2746             . preamble_code($pkg, $get, $private, $restricted, 'readonly')
2747              
2748             . ((ref($field_ref) eq 'HASH')
2749             ? " \$field->{\${\$_[0]}};\n};\n"
2750             : " \$field->[\${\$_[0]}];\n};\n");
2751             }
2752              
2753             # Create delegation accessor
2754 169 100       267 if ($delegate) {
2755 7         19 $delegate =~ s/\s*-->\s*/-->/g;
2756 7         26 my @methods = split(/[,\s]+/, $delegate);
2757 7         8 @methods = grep { $_ } @methods;
  10         17  
2758 7         9 @methods = map { get_class_methods($pkg, $_) } @methods;
  8         18  
2759 7         14 for my $method (@methods) {
2760 12         27 my ($from, $to) = split(/-->/, $method);
2761 12 100       26 if (! defined($to)) {
2762 9         9 $to = $from;
2763             }
2764 53     53   238 no strict 'refs';
  53         69  
  53         21547  
2765 12 50       9 if (*{$pkg.'::'.$from}{CODE}) {
  12         40  
2766 0         0 OIO::Attribute->die(
2767             'message' => q/Can't create delegator method/,
2768             'Info' => "Method '$from' already exists in class '$pkg'",
2769             'Attribute' => $attr);
2770             }
2771 12 50       26 $code .= "*${pkg}::$from = sub {\n"
2772              
2773             . preamble_code($pkg, $method, $private, $restricted)
2774              
2775             . " my \$self = shift;\n"
2776              
2777             . ((ref($field_ref) eq 'HASH')
2778             ? " \$field->{\${\$self}}->$to(\@_);\n};\n"
2779             : " \$field->[\${\$self}]->$to(\@_);\n};\n");
2780             }
2781             }
2782              
2783             # Compile the subroutine(s) in the smallest possible lexical scope
2784 169         142 my @errs;
2785 169         799 local $SIG{'__WARN__'} = sub { push(@errs, @_); };
  0         0  
2786             {
2787 169         186 my $field = $field_ref;
  169         148  
2788 169         144 my $type_check = $type;
2789 169         163 my $preproc = $pre;
2790 169 100       22333 eval $code;
  45 100       95  
  43 100       614  
  32 100       97  
  33 100       2721  
  21 100       65  
  17 100       115  
  18 100       40  
  23 100       3267  
  18         550  
  20         89  
  15         452  
  28         90  
  28         399  
  45         5473  
  37         1010  
  17         30  
  36         1137  
  32         70  
  15         305  
  15         38  
  38         4796  
  24         1150  
  11         433  
  13         23  
  19         33  
  17         35  
  18         542  
  25         69  
  23         84  
  41         5104  
  29         125  
  12         16  
  33         42  
  36         103  
  22         3437  
  20         50  
2791             }
2792 169 50 33     1762 if ($@ || @errs) {
2793 0   0     0 my ($err) = split(/ at /, $@ || join(" | ", @errs));
2794 0         0 OIO::Internal->die(
2795             'message' => "Failure creating accessor for class '$pkg'",
2796             'Error' => $err,
2797             'Declaration' => $attr,
2798             'Code' => $code,
2799             'self' => 1);
2800             }
2801 53     53   258 }
  53         67  
  53         225  
2802              
2803              
2804             # Generate code for start of accessor
2805             sub preamble_code :Sub(Private)
2806             {
2807 204         234 my ($pkg, $name, $private, $restricted, $readonly) = @_;
2808 204         182 my $code = '';
2809              
2810             # Argument checking code
2811 204 100       314 if (defined($readonly)) {
2812 74         132 $code = <<"_READONLY_";
2813             if (\@_ > 1) {
2814             OIO::Method->die('message' => "Can't call readonly accessor method '$pkg->$name' with an argument");
2815             }
2816             _READONLY_
2817             }
2818              
2819             # Permission checking code
2820 204 100       443 if (defined($private)) {
    100          
2821 2         4 $code = <<"_PRIVATE_";
2822             my \$caller = caller();
2823             if (! grep { \$_ eq \$caller } ($private)) {
2824             OIO::Method->die('message' => "Can't call private method '$pkg->$name' from class '\$caller'");
2825             }
2826             _PRIVATE_
2827             } elsif (defined($restricted)) {
2828 3         10 $code = <<"_RESTRICTED_";
2829             my \$caller = caller();
2830             if (! ((grep { \$_ eq \$caller } ($restricted)) ||
2831             \$caller->isa('$pkg') ||
2832             $pkg->isa(\$caller)))
2833             {
2834             OIO::Method->die('message' => "Can't call restricted method '$pkg->$name' from class '\$caller'");
2835             }
2836             _RESTRICTED_
2837             }
2838              
2839 204         468 return ($code);
2840 53     53   9460 }
  53         64  
  53         393  
2841              
2842              
2843             # Generate type checking code
2844             sub type_code :Sub(Private)
2845             {
2846 118         168 my ($pkg, $name, $weak, $type, $is_ref, $subtype) = @_;
2847 118         119 my $code = '';
2848 118         107 my $arg_str = '$_[1]';
2849              
2850             # Type checking code
2851 118 100 100     432 if (ref($type)) {
    100          
    100          
    100          
    100          
    100          
2852 9         38 $code = <<"_CODE_";
2853             {
2854             my (\$ok, \@errs);
2855             local \$SIG{'__WARN__'} = sub { push(\@errs, \@_); };
2856             eval { \$ok = \$type_check->($arg_str) };
2857             if (\$@ || \@errs) {
2858             my (\$err) = split(/ at /, \$@ || join(" | ", \@errs));
2859             OIO::Code->die(
2860             'message' => q/Problem with type check routine for '$pkg->$name'/,
2861             'Error' => \$err);
2862             }
2863             if (! \$ok) {
2864             OIO::Args->die(
2865             'message' => "Argument to '$pkg->$name' failed type check: $arg_str",
2866             'location' => [ caller() ]);
2867             }
2868             }
2869             _CODE_
2870              
2871             } elsif ($type eq 'NONE') {
2872             # For 'weak' fields, the data must be a ref
2873 61 100       132 if ($weak) {
2874 1         5 $code = <<"_WEAK_";
2875             if (! ref($arg_str)) {
2876             OIO::Args->die(
2877             'message' => "Bad argument: $arg_str",
2878             'Usage' => q/Argument to '$pkg->$name' must be a reference/,
2879             'location' => [ caller() ]);
2880             }
2881             _WEAK_
2882             }
2883              
2884             } elsif ($type eq 'scalar') {
2885             # One scalar argument
2886 1         5 $code = <<"_SCALAR_";
2887             if (ref($arg_str)) {
2888             OIO::Args->die(
2889             'message' => "Bad argument: $arg_str",
2890             'Usage' => q/Argument to '$pkg->$name' must be a scalar/,
2891             'location' => [ caller() ]);
2892             }
2893             _SCALAR_
2894              
2895             } elsif ($type eq 'numeric') {
2896             # One numeric argument
2897 14         53 $code = <<"_NUMERIC_";
2898             if (! Scalar::Util::looks_like_number($arg_str)) {
2899             OIO::Args->die(
2900             'message' => "Bad argument: $arg_str",
2901             'Usage' => q/Argument to '$pkg->$name' must be a number/,
2902             'location' => [ caller() ]);
2903             }
2904             _NUMERIC_
2905              
2906             } elsif ($type eq 'list') {
2907             # List/array - 1+ args or array ref
2908 9         17 $code = <<'_ARRAY_';
2909             my $arg;
2910             if (@_ == 2 && ref($_[1]) eq 'ARRAY') {
2911             $arg = $_[1];
2912             } else {
2913             my @args = @_;
2914             shift(@args);
2915             $arg = \@args;
2916             }
2917             _ARRAY_
2918 9         36 $arg_str = '$arg';
2919              
2920             } elsif ($type eq 'HASH' && !$is_ref) {
2921             # Hash - pairs of args or hash ref
2922 3         15 $code = <<"_HASH_";
2923             my \$arg;
2924             if (\@_ == 2 && ref(\$_[1]) eq 'HASH') {
2925             \$arg = \$_[1];
2926             } elsif (\@_ % 2 == 0) {
2927             OIO::Args->die(
2928             'message' => q/Odd number of arguments: Can't create hash ref/,
2929             'Usage' => q/'$pkg->$name' requires a hash ref or an even number of args (to make a hash ref)/,
2930             'location' => [ caller() ]);
2931             } else {
2932             my \@args = \@_;
2933             shift(\@args);
2934             my \%args = \@args;
2935             \$arg = \\\%args;
2936             }
2937             _HASH_
2938 3         5 $arg_str = '$arg';
2939              
2940             } else {
2941             # One object or ref arg - exact spelling and case required
2942 21         56 $code = <<"_REF_";
2943             if (! Object::InsideOut::Util::is_it($arg_str, '$type')) {
2944             OIO::Args->die(
2945             'message' => q/Bad argument: Wrong type/,
2946             'Usage' => q/Argument to '$pkg->$name' must be of type '$type'/,
2947             'location' => [ caller() ]);
2948             }
2949             _REF_
2950             }
2951              
2952             # Subtype checking code
2953 118 100       200 if ($subtype) {
2954 5 50       40 if ($subtype =~ /^scalar$/i) {
    100          
2955 0         0 $code .= <<"_SCALAR_SUBTYPE_";
2956             foreach my \$elem (\@{$arg_str}) {
2957             if (ref(\$elem)) {
2958             OIO::Args->die(
2959             'message' => q/Bad argument: Wrong type/,
2960             'Usage' => q/Values to '$pkg->$name' must be scalars/,
2961             'location' => [ caller() ]);
2962             }
2963             }
2964             _SCALAR_SUBTYPE_
2965             } elsif ($subtype =~ /^num(?:ber|eric)?$/i) {
2966 3         12 $code .= <<"_NUM_SUBTYPE_";
2967             foreach my \$elem (\@{$arg_str}) {
2968             if (! Scalar::Util::looks_like_number(\$elem)) {
2969             OIO::Args->die(
2970             'message' => q/Bad argument: Wrong type/,
2971             'Usage' => q/Values to '$pkg->$name' must be numeric/,
2972             'location' => [ caller() ]);
2973             }
2974             }
2975             _NUM_SUBTYPE_
2976             } else {
2977 2         15 $code .= <<"_SUBTYPE_";
2978             foreach my \$elem (\@{$arg_str}) {
2979             if (! Object::InsideOut::Util::is_it(\$elem, '$subtype')) {
2980             OIO::Args->die(
2981             'message' => q/Bad argument: Wrong type/,
2982             'Usage' => q/Values to '$pkg->$name' must be of type '$subtype'/,
2983             'location' => [ caller() ]);
2984             }
2985             }
2986             _SUBTYPE_
2987             }
2988             }
2989              
2990 118         230 return ($code, $arg_str);
2991 53     53   19956 }
  53         68  
  53         172  
2992              
2993              
2994             ### Wrappers ###
2995              
2996             # Returns a 'wrapper' closure back to initialize() that adds merged argument
2997             # support for a method.
2998             sub wrap_MERGE_ARGS :Sub(Private)
2999             {
3000 82         91 my $code = shift;
3001             return sub {
3002 255     325   25792 my $self = shift;
3003              
3004             # Gather arguments into a single hash ref
3005 255         317 my $args = {};
3006 255         636 while (my $arg = shift) {
3007 284 100       683 if (ref($arg) eq 'HASH') {
    50          
    50          
3008             # Add args from a hash ref
3009 78         55 @{$args}{keys(%{$arg})} = values(%{$arg});
  78         188  
  78         75  
  78         99  
3010             } elsif (ref($arg)) {
3011 0         0 OIO::Args->die(
3012 0         0 'message' => "Bad initializer: @{[ref($arg)]} ref not allowed",
3013             'Usage' => q/Args must be 'key=>val' pair(s) and\/or hash ref(s)/);
3014             } elsif (! @_) {
3015 0         0 OIO::Args->die(
3016             'message' => "Bad initializer: Missing value for key '$arg'",
3017             'Usage' => q/Args must be 'key=>val' pair(s) and\/or hash ref(s)/);
3018             } else {
3019             # Add 'key => value' pair
3020 206         501 $$args{$arg} = shift;
3021             }
3022             }
3023              
3024 255         496 @_ = ($self, $args);
3025 255         614 goto $code;
3026 82         326 };
3027 53     53   12707 }
  53         109  
  53         181  
3028              
3029              
3030             # Returns a 'wrapper' closure back to initialize() that restricts a method
3031             # to being only callable from within its class hierarchy
3032             sub wrap_RESTRICTED :Sub(Private)
3033             {
3034 15         22 my ($pkg, $method, $code, $exempt) = @_;
3035             return sub {
3036             # Caller must be in class hierarchy, or be specified as an exemption
3037 39         4752 my $caller = caller();
3038 39 100 100     117 if (! ((grep { $_ eq $caller } @$exempt) ||
  11   100     67  
3039             $caller->isa($pkg) ||
3040             $pkg->isa($caller)))
3041             {
3042 4         27 OIO::Method->die('message' => "Can't call restricted method '$pkg->$method' from class '$caller'");
3043             }
3044 35         74 goto $code;
3045 15         51 };
3046 53     53   8769 }
  53         65  
  53         156  
3047              
3048              
3049             # Returns a 'wrapper' closure back to initialize() that makes a method
3050             # private (i.e., only callable from within its own class).
3051             sub wrap_PRIVATE :Sub(Private)
3052             {
3053 1430         1568 my ($pkg, $method, $code, $exempt) = @_;
3054             return sub {
3055             # Caller must be in the package, or be specified as an exemption
3056 4283     4283   8963 my $caller = caller();
3057 4283 100       4066 if (! grep { $_ eq $caller } @$exempt) {
  4287         9181  
3058 4         32 OIO::Method->die('message' => "Can't call private method '$pkg->$method' from class '$caller'");
3059             }
3060 4279         5992 goto $code;
3061 1430         3181 };
3062 53     53   7888 }
  53         65  
  53         171  
3063              
3064              
3065             # Returns a 'wrapper' closure back to initialize() that makes a subroutine
3066             # uncallable - with the original code ref stored elsewhere, of course.
3067             sub wrap_HIDDEN :Sub(Private)
3068             {
3069 48         56 my ($pkg, $method) = @_;
3070             return sub {
3071 0         0 OIO::Method->die('message' => "Can't call hidden method '$pkg->$method'");
3072             }
3073 53     53   6193 }
  53         60  
  53         170  
  48         138  
3074              
3075              
3076             ### Delayed Loading ###
3077              
3078             # Loads sub-modules
3079             sub load :Sub(Private)
3080             {
3081 59         90 my $mod = shift;
3082 59         139 my $file = "Object/InsideOut/$mod.pm";
3083              
3084 59 50       186 if (! exists($INC{$file})) {
3085             # Load the file
3086 59         168874 my $rc = do($file);
3087              
3088             # Check for errors
3089 59 50       384 if ($@) {
    50          
    50          
3090 0         0 OIO::Internal->die(
3091             'message' => "Failure compiling file '$file'",
3092             'Error' => $@,
3093             'self' => 1);
3094             } elsif (! defined($rc)) {
3095 0         0 OIO::Internal->die(
3096             'message' => "Failure reading file '$file'",
3097             'Error' => $!,
3098             'self' => 1);
3099             } elsif (! $rc) {
3100 0         0 OIO::Internal->die(
3101             'message' => "Failure processing file '$file'",
3102             'Error' => $rc,
3103             'self' => 1);
3104             }
3105             }
3106 53     53   9223 }
  53         65  
  53         155  
3107              
3108             sub generate_CUMULATIVE :Sub(Private)
3109             {
3110 8         62 load('Cumulative');
3111 8         18 goto &generate_CUMULATIVE;
3112 53     53   4897 }
  53         64  
  53         154  
3113              
3114             sub create_CUMULATIVE :Sub(Private)
3115             {
3116 1         3 load('Cumulative');
3117 1         3 goto &create_CUMULATIVE;
3118 53     53   5095 }
  53         58  
  53         165  
3119              
3120             sub generate_CHAINED :Sub(Private)
3121             {
3122 5         12 load('Chained');
3123 5         13 goto &generate_CHAINED;
3124 53     53   4678 }
  53         70  
  53         161  
3125              
3126             sub create_CHAINED :Sub(Private)
3127             {
3128 1         3 load('Chained');
3129 1         2 goto &create_CHAINED;
3130 53     53   4772 }
  53         63  
  53         158  
3131              
3132             sub generate_OVERLOAD :Sub(Private)
3133             {
3134 11         34 load('Overload');
3135 11         32 goto &generate_OVERLOAD;
3136 53     53   4737 }
  53         66  
  53         157  
3137              
3138             sub install_UNIVERSAL :Sub(Private)
3139             {
3140 8         20 load('Universal');
3141 8         20 @_ = (\%GBL);
3142 8         21 goto &install_UNIVERSAL;
3143 53     53   5138 }
  53         66  
  53         146  
3144              
3145             sub install_ATTRIBUTES :Sub
3146             {
3147 1     0 0 2 load('attributes');
3148 1         4 goto &install_ATTRIBUTES;
3149 53     53   4498 }
  53         61  
  53         142  
3150              
3151             sub dump :Method(Object)
3152             {
3153 6     6 1 60 load('Dump');
3154 6         24 @_ = (\%GBL, 'dump', @_);
3155 6         20 goto &dump;
3156 53     53   5645 }
  53         70  
  53         152  
3157              
3158             sub pump :Method(Class)
3159             {
3160 0     0 1 0 load('Dump');
3161 0         0 @_ = (\%GBL, 'pump', @_);
3162 0         0 goto &dump;
3163 53     53   5365 }
  53         70  
  53         168  
3164              
3165             sub inherit :Method(Object)
3166             {
3167 0     0 1 0 load('Foreign');
3168 0         0 @_ = (\%GBL, 'inherit', @_);
3169 0         0 goto &inherit;
3170 53     53   5348 }
  53         56  
  53         156  
3171              
3172             sub heritage :Method(Object)
3173             {
3174 0     0 1 0 load('Foreign');
3175 0         0 @_ = (\%GBL, 'heritage', @_);
3176 0         0 goto &inherit;
3177 53     53   5386 }
  53         70  
  53         149  
3178              
3179             sub disinherit :Method(Object)
3180             {
3181 0     0 1 0 load('Foreign');
3182 0         0 @_ = (\%GBL, 'disinherit', @_);
3183 0         0 goto &inherit;
3184 53     53   5504 }
  53         81  
  53         150  
3185              
3186             sub create_heritage :Sub(Private)
3187             {
3188 4         11 load('Foreign');
3189 4         16 @_ = (\%GBL, 'create_heritage', @_);
3190 4         18 goto &inherit;
3191 53     53   5087 }
  53         70  
  53         154  
3192              
3193             sub create_field :Method(Class)
3194             {
3195 3     3 0 27 load('Dynamic');
3196 3         31 @_ = (\%GBL, 'create_field', @_);
3197 3         12 goto &create_field;
3198 53     53   5010 }
  53         59  
  53         152  
3199              
3200             sub add_class :Method(Class)
3201             {
3202 1     1 1 3 load('Dynamic');
3203 1         4 @_ = (\%GBL, 'add_class', @_);
3204 1         3 goto &create_field;
3205 53     53   5497 }
  53         68  
  53         163  
3206              
3207             sub AUTOLOAD :Sub
3208             {
3209 9     9   706 load('Autoload');
3210 9         32 @_ = (\%GBL, @_);
3211 9         31 goto &Object::InsideOut::AUTOLOAD;
3212 53     53   5083 }
  53         66  
  53         146  
3213              
3214             sub create_lvalue_accessor :Sub(Private)
3215             {
3216 1         2 load('lvalue');
3217 1         2 goto &create_lvalue_accessor;
3218 53     53   5131 }
  53         62  
  53         156  
3219              
3220              
3221             ### Initialization and Termination ###
3222              
3223             # Initialize the package after loading
3224             initialize();
3225              
3226             {
3227             # Initialize as part of the CHECK phase
3228 53     53   3833 no warnings 'void';
  53         61  
  53         8077  
3229             CHECK {
3230 50     50   28802 initialize();
3231             }
3232             }
3233              
3234             # Initialize just before cloning a thread
3235             sub CLONE_SKIP
3236             {
3237 70 100   0   5253 if ($_[0] eq 'Object::InsideOut') {
3238 36         118 initialize();
3239             }
3240 38         73 return 0;
3241             }
3242              
3243             # Workaround for Perl's "in cleanup" bug
3244             END {
3245 53     53   7486 $GBL{'term'} = 1;
3246             }
3247              
3248             } # End of package's lexical scope
3249              
3250             1;
3251             # EOF