File Coverage

blib/lib/Object/InsideOut.pm
Criterion Covered Total %
statement 1370 1704 80.4
branch 660 944 69.9
condition 160 282 56.7
subroutine 94 101 93.0
pod 9 16 56.2
total 2293 3047 75.2


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