File Coverage

blib/lib/Object/InsideOut.pm
Criterion Covered Total %
statement 1366 1707 80.0
branch 659 944 69.8
condition 164 291 56.3
subroutine 95 102 93.1
pod 9 16 56.2
total 2293 3060 74.9


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