File Coverage

blib/lib/Aion.pm
Criterion Covered Total %
statement 681 842 80.8
branch 166 304 54.6
condition 43 117 36.7
subroutine 120 121 99.1
pod 9 27 33.3
total 1019 1411 72.2


line stmt bran cond sub pod time code
1             package Aion;
2              
3 4     4   208587 use common::sense;
  4         8  
  4         831  
4              
5             our $VERSION = "2.1";
6              
7 4     4   1892 use Aion::Types qw//;
  4         13  
  4         155  
8 4     4   1907 use Aion::Meta::RequiresAnyFunction;
  4         11  
  4         132  
9 4     4   1544 use Aion::Meta::Feature;
  4         8  
  4         142  
10 4     4   2115 use Aion::Meta::RequiresFeature;
  4         11  
  4         157  
11 4     4   1257 use Aion::Meta::Subroutine;
  4         8  
  4         155  
12              
13             # Когда осуществлять проверки:
14             # ro - только при выдаче
15             # wo - только при установке
16             # rw - при выдаче и уcтановке
17             # no - никогда не проверять
18 4     4   1655 use Aion::Env AION_ISA => (default => 'rw');
  4         11  
  4         27  
19              
20             sub export($@);
21              
22             # Классы в которых подключён Aion с метаинформацией
23             our %META;
24              
25             # Вызывается из другого пакета, для импорта данного
26             sub import {
27 57     57   8444 my (undef, $attr) = @_;
28 57         156 my $pkg = caller;
29              
30 57 50       771 *{"$pkg\::DOES"} = \&does if \&does != $pkg->can('DOES');
  57         240  
31              
32 57 100       214 if($attr ne '-role') { # Класс
33 49         180 export $pkg, qw/extends/;
34 49         102 *{"${pkg}::new"} = \&initialize;
  49         192  
35             } else { # Роль
36 8         20 export $pkg, qw/requires req/;
37             }
38              
39 57         138 export $pkg, qw/with has aspect does exactly/;
40              
41             # Метаинформация
42 57         1001 $META{$pkg} = {
43             order => scalar keys %META,
44             require => {},
45             feature => {},
46             subroutine => {},
47             aspect => {
48             is => \&is_aspect,
49             isa => \&isa_aspect,
50             coerce => \&coerce_aspect,
51             lazy => \&lazy_aspect,
52             default => \&default_aspect,
53             trigger => \&trigger_aspect,
54             release => \&release_aspect,
55             init_arg => \&init_arg_aspect,
56             accessor => \&accessor_aspect,
57             writer => \&writer_aspect,
58             reader => \&reader_aspect,
59             predicate => \&predicate_aspect,
60             clearer => \&clearer_aspect,
61             cleaner => \&cleaner_aspect,
62             eon => \&eon_aspect,
63             }
64             };
65              
66 4 50   4   29 eval "package $pkg; use Aion::Types; 1" or die;
  4     3   7  
  4     3   1621  
  3     1   23  
  3     1   6  
  3     1   1527  
  3     1   18  
  3     1   4  
  3     1   1135  
  1     1   9  
  1     1   2  
  1     1   631  
  1     1   9  
  1     1   2  
  1     1   655  
  1     1   9  
  1     1   2  
  1     1   515  
  1     1   6  
  1     1   3  
  1     1   306  
  1     1   5  
  1     1   1  
  1     1   348  
  1     1   5  
  1     1   2  
  1     1   297  
  1     1   4  
  1     1   2  
  1     1   294  
  1     1   7  
  1     1   1  
  1     1   306  
  1     1   5  
  1     1   2  
  1     1   334  
  1     1   8  
  1     1   2  
  1     1   451  
  1     1   9  
  1     1   2  
  1     1   516  
  1     1   6  
  1     1   2  
  1     1   414  
  1     1   7  
  1     1   1  
  1     1   395  
  1     1   5  
  1     1   2  
  1         338  
  1         6  
  1         2  
  1         286  
  1         6  
  1         1  
  1         399  
  1         6  
  1         2  
  1         289  
  1         6  
  1         1  
  1         328  
  1         6  
  1         2  
  1         385  
  1         5  
  1         2  
  1         310  
  1         6  
  1         1  
  1         323  
  1         12  
  1         3  
  1         323  
  1         5  
  1         2  
  1         349  
  1         8  
  1         1  
  1         312  
  1         6  
  1         3  
  1         370  
  1         7  
  1         2  
  1         475  
  1         8  
  1         3  
  1         567  
  1         10  
  1         2  
  1         506  
  1         9  
  1         3  
  1         559  
  1         9  
  1         2  
  1         538  
  1         9  
  1         3  
  1         670  
  1         11  
  1         2  
  1         586  
  1         10  
  1         2  
  1         632  
  1         8  
  1         2  
  1         587  
  1         9  
  1         3  
  1         554  
  1         8  
  1         3  
  1         496  
  1         8  
  1         2  
  1         532  
  1         8  
  1         11  
  1         587  
  1         9  
  1         2  
  1         507  
  1         9  
  1         2  
  1         599  
  1         10  
  1         3  
  1         547  
  1         7  
  1         2  
  1         573  
  1         9  
  1         2  
  1         526  
  1         9  
  1         2  
  1         549  
  1         5  
  1         1  
  1         402  
  1         5  
  1         1  
  1         432  
  1         5  
  1         1  
  1         325  
  57         7306  
67             }
68              
69             # Удаляет добавленные символы
70             sub unimport {
71 0     0   0 my $pkg = caller;
72            
73 0         0 undef &{"${pkg}::$_"} for qw/extends with aspect requires req/;
  0         0  
74            
75 0 0       0 eval "package $pkg; no Aion::Types; 1" or die;
76             }
77              
78             # Экспортирует функции в пакет, если их там ещё нет
79             sub export($@) {
80 114     114 0 176 my $pkg = shift;
81 114         209 for my $sub (@_) {
82 350         1262 my $can = $pkg->can($sub);
83 350 50 33     644 die "$pkg can $sub!" if $can && $can != \&$sub;
84 350 50       744 *{"${pkg}::$sub"} = \&$sub unless $can;
  350         807  
85             }
86             }
87              
88             # Проверяет, что этот пакет инициализирован Aion
89             sub is_aion($) {
90 118     118 0 196 my $pkg = shift;
91 118 50       431 die "$pkg is'nt class of Aion!" if !exists $META{$pkg};
92             }
93              
94             #@category Aspects
95              
96             # ro, rw, + и -, *
97             sub is_aspect {
98 36     36 0 80 my ($is, $feature) = @_;
99 36 50       325 die "Use is => '{ro|rw|wo|no} {+|-} {*} {?} {!}'" if $is !~ /^(?ro|rw|wo|no)?(?[+-])?(?\*)?(?\??)(?!?)\z/;
100              
101 36         120 my ($construct, $name) = @$feature{qw/construct name/};
102              
103 36 100       511 $construct->getter("die 'Feature $name cannot be get!';") if $+{access} ~~ [qw/wo no/];
104              
105 36 100       794 $construct->setter("die 'Feature $name cannot be set!';") if $+{access} ~~ [qw/ro no/];
106              
107 36 100       197 $construct->add_trigger("%(weaken)s") if $+{weak};
108              
109 36 100       310 $feature->{required} = 1, $construct->not_specified(' else { die "%(init_arg)s required!" }') if $+{require} eq '+';
110            
111 36 100       230 $feature->{excessive} = 1, $construct->initer('die "%(init_arg)s excessive!"') if $+{require} eq '-';
112              
113 36 100       162 $feature->{make_predicate} = 1 if $+{has};
114 36 100       243 $feature->{make_clearer} = 1 if $+{clear};
115             }
116              
117             # isa => Type
118             sub isa_aspect {
119 26     26 0 59 my ($isa, $feature) = @_;
120 26         81 my ($construct, $name) = @$feature{qw/construct name/};
121              
122 26         111 $feature->{isa} = Aion::Types::External[$isa];
123              
124 26 50       179 $construct->add_release("${\$feature->meta}\{isa}->validate(\$val, 'Get feature $name');") if AION_ISA =~ /ro|rw/;
  26         95  
125              
126 26 50       220 $construct->add_preset("${\$feature->meta}\{isa}->validate(\$val, 'Set feature $name');") if AION_ISA =~ /wo|rw/;
  26         79  
127             }
128              
129             # coerce => 1
130             sub coerce_aspect {
131 4     4 0 8 my ($coerce, $feature) = @_;
132              
133 4 50       11 return unless $coerce;
134              
135 4 50       21 die "coerce: isa not present!" unless $feature->{isa};
136              
137 4 50       22 $feature->{construct}->add_preset("\$val = ${\$feature->meta}\{isa}->coerce(\$val);", 1) if AION_ISA =~ /wo|rw/;
  4         12  
138             }
139              
140             my $pleroma;
141              
142             sub pleroma {
143 1     1   4567 require Aion::Pleroma;
144 1         10 $pleroma = Aion::Pleroma->new;
145 1     1   5 *pleroma = sub { $pleroma };
  1         729  
146 1         8 $pleroma
147             }
148              
149             # eon => $key
150             sub eon_aspect {
151 3     3 1 8 my ($key, $feature) = @_;
152              
153 3 50       9 die "eon is not compatible with default!" if $feature->{opt}{default};
154              
155 3 100       10 if($key eq 1) {
    100          
156 1         2 my $isa = $feature->{isa};
157 1 50 33     4 $key = $isa && $isa->{name} eq "Object" && $isa->{args}[0]
158             or die "use: has $feature->{name} => (isa => Object[...], eon => 1)";
159             }
160             elsif($key eq 2) {
161 1         2 my $isa = $feature->{isa};
162 1   50     3 $key = ($isa && $isa->{name} eq "Object" && $isa->{args}[0]
163             or die "use: has $feature->{name} => (isa => Object[...], eon => 2)")
164             . "#$feature->{name}";
165            
166             }
167              
168 3     3   17 default_aspect(sub { Aion->pleroma->resolve($key) }, $feature);
  3         13  
169             }
170              
171             # lazy => 1|0
172             sub lazy_aspect {
173 4     4 0 13 my ($lazy, $feature) = @_;
174              
175 4         14 $feature->{lazy} = $lazy;
176             }
177              
178             # default => value
179             sub default_aspect {
180 17     17 0 40 my ($default, $feature) = @_;
181              
182 17         403 my $name = $feature->name;
183 17         43 my $default_is_code = ref $default eq "CODE";
184              
185 17 100       36 if($default_is_code) {
186 9         22 $feature->{builder} = $default;
187             } else {
188 8         27 $feature->{default} = $default;
189 8 100       38 $feature->{isa}->validate($default, $name) if $feature->{isa};
190             }
191              
192 17 100 100     78 if($feature->{opt}{lazy} // $default_is_code) {
193 7         20 $feature->{lazy} = 1;
194              
195 7 100       14 if ($default_is_code) {
196 6         87 $feature->construct->add_access("unless(%(has)s) {
197 6         16 my \$val = ${\$feature->meta}\{builder}->(\$self);
198             %(write)s
199             }");
200             } else {
201 1         27 $feature->construct->add_access("unless(%(has)s) {
202 1         7 my \$val = ${\$feature->meta}\{default};
203             %(write)s
204             }");
205             }
206             } else {
207 10 100       22 if($default_is_code) {
208 3         8 $feature->{construct}->not_specified(" else {
209 3         10 my \$val = ${\$feature->meta}\{builder}->(\$self);
210             %(write)s
211             }");
212             } else {
213 7         15 $feature->{construct}->not_specified(" else {
214 7         45 my \$val = ${\$feature->meta}\{default};
215             %(write)s
216             }");
217             }
218            
219             }
220             }
221              
222             # trigger => $sub
223             sub trigger_aspect {
224 1     1 0 5 my ($trigger, $feature) = @_;
225              
226 1         4 $feature->{trigger} = $trigger;
227              
228 1         3 my $construct = $feature->{construct};
229              
230 1         6 $construct->add_preset("my \@old = %(has)s? %(get)s: ();");
231 1         2 $construct->add_trigger("${\$feature->meta}\{trigger}->(\$self, \@old);");
  1         3  
232             }
233              
234             # release => $sub
235             sub release_aspect {
236 1     1 0 3 my ($release, $feature) = @_;
237              
238 1         4 $feature->{release} = $release;
239              
240 1         2 $feature->{construct}->add_release("${\$feature->meta}\{release}->(\$self, \$val);");
  1         3  
241             }
242              
243             # init_arg => $name
244             sub init_arg_aspect {
245 1     1 0 3 my ($init_arg, $feature) = @_;
246              
247 1         15 $feature->construct->init_arg($init_arg);
248             }
249              
250             # accessor => $name
251             sub accessor_aspect {
252 1     1 0 4 my ($accessor, $feature) = @_;
253              
254 1         31 $feature->construct->accessor_name($accessor);
255             }
256              
257             # writer => $name
258             sub writer_aspect {
259 1     1 0 2 my ($writer, $feature) = @_;
260              
261 1         2 $feature->{make_writer} = 1;
262 1         15 $feature->construct->writer_name($writer);
263             }
264              
265             # reader => $name
266             sub reader_aspect {
267 1     1 0 3 my ($reader, $feature) = @_;
268              
269 1         25 $feature->{make_reader} = 1;
270 1         18 $feature->construct->reader_name($reader);
271             }
272              
273             # predicate => $name
274             sub predicate_aspect {
275 1     1 0 4 my ($predicate, $feature) = @_;
276              
277 1         3 $feature->{make_predicate} = 1;
278 1         14 $feature->construct->predicate_name($predicate);
279             }
280              
281             # clearer => $name
282             sub clearer_aspect {
283 1     1 0 3 my ($clearer, $feature) = @_;
284              
285 1         3 $feature->{make_clearer} = 1;
286 1         18 $feature->construct->clearer_name($clearer);
287             }
288              
289             # cleaner => $sub
290             sub cleaner_aspect {
291 1     1 0 6 my ($cleaner, $feature) = @_;
292              
293 1         4 my ($cls, $construct) = @$feature{qw/pkg construct/};
294            
295 1         4 $feature->{cleaner} = $cleaner;
296              
297 1         3 $construct->add_cleaner("${\$feature->meta}\{cleaner}->(\$self);");
  1         7  
298             }
299              
300             # Расширяет класс или роль
301             sub inherits($$@) {
302 14     14 0 32 my $pkg = shift; my $is_with = shift;
  14         21  
303              
304 14         36 is_aion $pkg;
305              
306 14         58 my $FEATURE = $Aion::META{$pkg}{feature};
307 14         25 my $ASPECT = $Aion::META{$pkg}{aspect};
308 14   50     48 my $REQUIRE = $Aion::META{$pkg}{require} //= {};
309              
310             # Добавляем наследуемые свойства и атрибуты
311 14         33 for my $module (@_) {
312 16 100 50     323 eval "require $module" or die unless $module->can('with') || $module->can('new');
      66        
313              
314 16 50       65 if(my $meta = $Aion::META{$module}) {
315 16         32 %$FEATURE = (%$FEATURE, %{$meta->{feature}}) ;
  16         44  
316 16         95 %$ASPECT = (%$ASPECT, %{$meta->{aspect}});
  16         328  
317 16         67 %$REQUIRE = (%$REQUIRE, %{$meta->{require}});
  16         70  
318             }
319             }
320              
321 14 100       38 my $import_name = $is_with? 'import_with': 'import_extends';
322 14         28 for my $module (@_) {
323 16         103 my $import = $module->can($import_name);
324 16 100       44 $import->($module, $pkg) if $import;
325             }
326              
327 14         872 return;
328             }
329              
330             # Наследование классов
331             sub extends(@) {
332 3     3 1 5894 my $pkg = caller;
333              
334 3         9 is_aion $pkg;
335              
336 3         4 push @{"${pkg}::ISA"}, @_;
  3         67  
337 3         5 push @{$Aion::META{$pkg}{extends}}, @_;
  3         15  
338              
339 3         7 unshift @_, $pkg, 0;
340 3         10 goto &inherits;
341             }
342              
343             # Расширение ролями
344             sub with(@) {
345 11     11 1 10097 my $pkg = caller;
346              
347 11         58 is_aion $pkg;
348              
349 11         13 push @{"${pkg}::ISA"}, @_;
  11         199  
350 11         31 push @{$Aion::META{$pkg}{with}}, @_;
  11         68  
351              
352 11         33 unshift @_, $pkg, 1;
353 11         48 goto &inherits;
354             }
355              
356             sub requires(@) {
357 1     1 1 4253 my $pkg = caller;
358              
359 1         7 is_aion $pkg;
360              
361             #TODO: добавить проверку на существование
362 1         17 $Aion::META{$pkg}{require}{$_} = Aion::Meta::RequiresAnyFunction->new(pkg => $pkg, name => $_) for @_;
363             }
364              
365             # Требуется свойство
366             sub req(@) {
367 1     1 1 5 my ($name) = @_;
368 1         5 my $pkg = caller;
369              
370 1         5 is_aion $pkg;
371              
372 1         3 my $meta = $Aion::META{$pkg};
373              
374             #TODO: добавить проверку на существование по модулю и сравнить, что не одинаковы, если модули не совпадают
375             # die "Feature `$name` already required!" if exists $meta->{require}{$name};
376              
377 1         15 $meta->{require}{$name} = Aion::Meta::RequiresFeature->new($pkg, @_);
378 1         4 return;
379             }
380              
381             # Добавляется аспект
382             sub aspect($$) {
383 2     2 1 5090 my ($name, $sub) = @_;
384 2         6 my $pkg = caller;
385              
386 2         10 is_aion $pkg;
387              
388 2         6 my $ASPECT = $Aion::META{$pkg}{aspect};
389 2 50       7 die "Aspect `$name` exists!" if exists $ASPECT->{$name};
390 2         4 $ASPECT->{$name} = $sub;
391 2         6 return;
392             }
393              
394             # Ищет именно классы, а не роли
395             sub exactly {
396 7     7 1 3748 my ($self, $class) = @_;
397 7 100       233 return '' if Aion::Types::ClassName->exclude($class);
398 6         35 goto &UNIVERSAL::isa;
399             }
400              
401              
402             # Определяет - подключена ли роль
403             sub does {
404 6     6 1 3020 my ($self, $role) = @_;
405 6 100       193 return '' if Aion::Types::ClassName->include($role);
406 5         35 goto &UNIVERSAL::isa;
407             }
408              
409             # Создаёт свойство
410             sub has(@) {
411 47     47 1 46488 my $property = shift;
412              
413 47         129 my $pkg = caller;
414 47         186 is_aion $pkg;
415              
416 47         226 my %opt = @_;
417 47         105 my $meta = $Aion::META{$pkg};
418              
419             # создаём фичи
420 47 100       145 for my $name (ref $property? @$property: $property) {
421              
422             die "has: the method $name is already in the package $pkg"
423 48 50 33     637 if $pkg->can($name) && !exists $meta->{feature}{$name};
424              
425 48         353 my $feature = Aion::Meta::Feature->new($pkg, $name, @_);
426              
427 48         159 my $require = delete $meta->{require}{$name};
428 48 100       128 $require->compare($feature) if $require;
429              
430 48         122 my $overload = $meta->{feature}{$name};
431 48 50       129 $overload->compare($feature) if $overload;
432            
433 48         211 $feature->mk_property;
434 48         189 $meta->{feature}{$name} = $feature;
435             }
436 47         204 return;
437             }
438              
439             # Инициализатор: закрывает класс и заменяется на конструктор
440             sub initialize {
441 39     39 0 209052 my ($cls) = @_;
442              
443 39   33     225 $cls = ref $cls || $cls;
444 39         159 is_aion $cls;
445              
446 39         89 my $REQUIRE = $Aion::META{$cls}{require};
447 39         78 my $FEATURE = $Aion::META{$cls}{feature};
448 39         75 my $SUBROUTINE = $Aion::META{$cls}{subroutine};
449 39         120 for my $key (keys %$REQUIRE) {
450 6         17 my $require = $REQUIRE->{$key};
451            
452 6 100       82 if ($require->isa('Aion::Meta::RequiresAnyFunction')) {
    100          
453 2         20 $require->compare($cls->can($key));
454             } elsif ($require->isa('Aion::Meta::RequiresFeature')) {
455 1         45 $require->compare($FEATURE->{$require->name});
456             } else {
457 3         128 $require->compare($SUBROUTINE->{$require->subname});
458             }
459             }
460              
461 36         91 %$REQUIRE = ();
462              
463             # TODO: очищать класс от вспомогательных функций
464             #eval "package $cls; Aion->unimport; 1" or die;
465              
466 36         101 my $new = << 'END';
467             package %(cls)s {
468             sub new {
469             my ($cls, %value) = @_;
470             $cls = ref $cls || $cls;
471             my $self = bless {}, $cls;
472            
473             %(initializers)s
474            
475             if(scalar keys %value) {
476             my @fakekeys = sort keys %value;
477             die "@fakekeys is'nt feature!" if @fakekeys == 1;
478             local $" = ", ";
479             die "@fakekeys is'nt features!"
480             }
481              
482             return $self;
483             }
484             }
485             END
486              
487 36         78 my @destroyers;
488             my $initializers = join "", map {
489 47 100       129 push @destroyers, $_->{construct}->destroyer if $_->{cleaner};
490             $_->{construct}->initializer
491 36         158 } sort { $a->{order} <=> $b->{order} } values %$FEATURE;
  47         211  
  19         79  
492            
493 36         168 my %var = (
494             cls => $cls,
495             initializers => $initializers,
496             );
497            
498 36         236 $new =~ s/%\((\w+)\)s/$var{$1}/ge;
  72         322  
499              
500 36 100 33 5   13379 eval $new;
  5 0 33 10   14  
  5 50 33 8   21  
  5 100 33 3   8  
  5 50 33 2   13  
  3 50 33 2   6  
  3 100 33 1   9  
  3 100 33 1   13  
  3 100 33 1   25  
  5 0 33 1   10  
  0 50 33 1   0  
  0 100 33 1   0  
  0 0 33 1   0  
  0 50 33 2   0  
  5 100 33 2   75  
  10 100 33 1   17831  
  10 50 33 1   44  
  10 0 33 9   22  
  10 50 33 1   21  
  3 100 33 1   6  
  3 0 33 1   16  
  2 50 33 1   9  
  10 50 33 1   33  
  9 0 33 1   38  
  7 50 33 6   20  
  7 50 33 1   28  
  0 50 33 1   0  
  1 50 33 1   16  
  10 0 33 1   3984  
  15 50 33 1   48  
  8 50 33 1   16  
  8 0       21  
  5 50       10  
  12 50       47  
  11 0       28  
  11 50       23  
  8 50       16  
  1 0       6  
  0 50       0  
  6 50       44  
  1 0       3  
  8 50       49  
  1 50       42  
  1 0       6  
  3 50       11  
  0 50       0  
  0 0       0  
  0 50       0  
  0 50       0  
  3 0       69  
  3 50       810  
  3 0       44  
  3 50       9  
  3 50       10  
  1 0       3  
  1 50       9  
  1 100       7  
  1 100       4  
  3 100       9  
  0 0       0  
  0 50       0  
  0 50       0  
  0 0       0  
  3 50       34  
  2 50       3336  
  2 50       12  
  2 0       4  
  2 50       7  
  1 0       4  
  1 50       10  
  1 50       7  
  1 0       3  
  1 50       8  
  1 50       5  
  2 50       9  
  1 50       3  
  1 0       8  
  1 50       4  
  2 50       5  
  0 0       0  
  0 50       0  
  0 100       0  
  2 100       11  
  2 100       7  
  2 100       5  
  2 100       6  
  0 50       0  
  0 0       0  
  0 50       0  
  0 50       0  
  2 0       6  
  2 50       519  
  2 50       11  
  2 0       4  
  2 50       5  
  1 0       3  
  1 50       2  
  1 50       3  
  1 50       5  
  2 0       4  
  0 50       0  
  0 0       0  
  0 50       0  
  0         0  
  2         31  
  1         2  
  1         5  
  1         2  
  1         3  
  0         0  
  0         0  
  1         4  
  0         0  
  0         0  
  0         0  
  0         0  
  1         16  
  1         4  
  1         5  
  1         2  
  1         3  
  1         2  
  1         6  
  1         2  
  1         4  
  1         4  
  0         0  
  0         0  
  1         2  
  0         0  
  0         0  
  0         0  
  0         0  
  1         4  
  1         4  
  1         6  
  1         2  
  1         3  
  0         0  
  0         0  
  0         0  
  1         2  
  0         0  
  0         0  
  0         0  
  0         0  
  1         6  
  1         3  
  1         4  
  1         2  
  1         3  
  0         0  
  0         0  
  1         3  
  0         0  
  0         0  
  0         0  
  0         0  
  1         3  
  1         3  
  1         7  
  1         2  
  1         3  
  1         2  
  1         4  
  1         3  
  0         0  
  0         0  
  0         0  
  0         0  
  1         16  
  1         3  
  1         4  
  1         3  
  1         3  
  0         0  
  0         0  
  1         3  
  0         0  
  0         0  
  0         0  
  0         0  
  1         4  
  1         6  
  1         9  
  1         4  
  1         4  
  0         0  
  0         0  
  1         4  
  0         0  
  0         0  
  0         0  
  0         0  
  1         4  
  2         763  
  2         18  
  2         7  
  2         7  
  2         5  
  2         9  
  2         9  
  0         0  
  0         0  
  0         0  
  0         0  
  2         32  
  2         599  
  2         9  
  2         4  
  2         5  
  2         4  
  2         11  
  2         8  
  2         6  
  2         5  
  0         0  
  0         0  
  0         0  
  0         0  
  2         26  
  1         6  
  1         7  
  1         4  
  1         4  
  0         0  
  0         0  
  0         0  
  0         0  
  1         9  
  1         2  
  1         6  
  1         2  
  1         3  
  0         0  
  0         0  
  1         2  
  0         0  
  0         0  
  0         0  
  0         0  
  1         15  
  9         9329  
  9         80  
  9         30  
  9         24  
  2         7  
  2         7  
  9         28  
  8         17  
  8         25  
  1         22  
  8         25  
  1         13  
  7         18  
  0         0  
  0         0  
  0         0  
  0         0  
  7         231  
  1         3  
  1         5  
  1         3  
  1         3  
  0         0  
  0         0  
  1         3  
  0         0  
  0         0  
  0         0  
  0         0  
  1         2  
  1         8  
  1         8  
  1         4  
  1         4  
  1         2  
  1         10  
  1         9  
  0         0  
  1         4  
  0         0  
  1         4  
  1         5  
  1         5  
  1         4  
  0         0  
  0         0  
  0         0  
  0         0  
  1         5  
  1         5  
  1         8  
  1         3  
  1         4  
  0         0  
  0         0  
  0         0  
  0         0  
  1         5  
  1         5  
  1         6  
  1         2  
  1         4  
  1         2  
  1         4  
  1         3  
  0         0  
  0         0  
  0         0  
  0         0  
  1         3  
  1         5  
  1         5  
  1         2  
  1         3  
  1         2  
  1         5  
  1         5  
  0         0  
  1         3  
  1         3  
  1         4  
  1         2  
  0         0  
  1         3  
  0         0  
  0         0  
  0         0  
  1         2  
  1         3  
  1         2  
  1         3  
  0         0  
  0         0  
  0         0  
  0         0  
  1         5  
  1         3  
  1         6  
  1         3  
  1         2  
  0         0  
  0         0  
  1         4  
  0         0  
  0         0  
  0         0  
  0         0  
  1         4  
  6         5952  
  6         34  
  6         21  
  6         24  
  1         2  
  1         10  
  1         5  
  6         17  
  5         12  
  5         34  
  5         22  
  1         20  
  5         17  
  1         11  
  4         13  
  2         10  
  2         17  
  1         3  
  1         13  
  2         11  
  1         3  
  1         5  
  1         3  
  1         4  
  0         0  
  0         0  
  1         4  
  0         0  
  0         0  
  0         0  
  0         0  
  1         20  
  1         6  
  1         21  
  1         4  
  1         5  
  0         0  
  0         0  
  0         0  
  1         5  
  1         8  
  1         11  
  1         7  
  0         0  
  0         0  
  0         0  
  0         0  
  1         50  
  1         4  
  1         5  
  1         3  
  1         2  
  0         0  
  0         0  
  0         0  
  1         3  
  0         0  
  0         0  
  0         0  
  0         0  
  1         39  
  1         2  
  1         6  
  1         3  
  1         4  
  0         0  
  0         0  
  0         0  
  0         0  
  1         7  
  1         5  
  1         6  
  1         3  
  1         3  
  1         1  
  1         7  
  1         12  
  1         3  
  1         2  
  1         4  
  1         2  
  1         3  
  0         0  
  0         0  
  0         0  
  0         0  
  1         4  
  1         4  
  1         6  
  1         5  
  1         4  
  0         0  
  0         0  
  0         0  
  0         0  
  1         6  
501 36 50       281 die if $@;
502              
503 36 100       106 if (@destroyers) {
504 1         4 my $destroyer = << 'END';
505             package %(cls)s {
506             sub DESTROY {
507             my ($self) = @_;
508              
509             warn "${\ref $self}#${\Scalar::Util::id $self} destroy in global phase!" if ${^GLOBAL_PHASE} eq 'DESTRUCT';
510              
511             %(destroyers)s
512             }
513             }
514             END
515              
516 1         8 my %var = (
517             cls => $cls,
518             destroyers => join "", @destroyers,
519             );
520            
521 1         13 $destroyer =~ s/%\((\w+)\)s/$var{$1}/ge;
  2         12  
522              
523 1 50   2   373 eval $destroyer;
  2 50       756  
  2 100       10  
  0         0  
  0         0  
  2         7  
  1         2  
  1         7  
  1         7  
524 1 50       12 die $@ if $@;
525             }
526            
527 36         56 goto &{"${cls}::new"};
  36         934  
528             }
529              
530             1;
531              
532             __END__