line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Fukurama::Class::Extends; |
2
|
5
|
|
|
5
|
|
25243
|
use Fukurama::Class::Version(0.01); |
|
5
|
|
|
|
|
11
|
|
|
5
|
|
|
|
|
35
|
|
3
|
5
|
|
|
5
|
|
30
|
use Fukurama::Class::Rigid; |
|
5
|
|
|
|
|
9
|
|
|
5
|
|
|
|
|
41
|
|
4
|
5
|
|
|
5
|
|
30
|
use Fukurama::Class::Carp; |
|
5
|
|
|
|
|
18
|
|
|
5
|
|
|
|
|
77
|
|
5
|
5
|
|
|
5
|
|
2385
|
use Fukurama::Class::Tree(); |
|
5
|
|
|
|
|
14
|
|
|
5
|
|
|
|
|
3513
|
|
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
our $LEVEL_DISABLE = 0; |
8
|
|
|
|
|
|
|
our $LEVEL_CHECK_NONE = 1; |
9
|
|
|
|
|
|
|
our $LEVEL_CHECK_REGISTERED = 2; |
10
|
|
|
|
|
|
|
our $LEVEL_CHECK_CHILDS = 3; |
11
|
|
|
|
|
|
|
our $LEVEL_CHECK_ALL = 4; |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
our $CHECK_LEVEL = $LEVEL_CHECK_CHILDS; |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
my $REGISTER = {}; |
16
|
|
|
|
|
|
|
my $ERRORS = {}; |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
=head1 NAME |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
Fukurama::Class::Extends - Pragma to extend class inheritation |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
=head1 VERSION |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
Version 0.01 (beta) |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
=head1 SYNOPSIS |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
package MyClass; |
29
|
|
|
|
|
|
|
use Fukurama::Class::Extends('MyParent'); |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
=head1 DESCRIPTION |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
This pragma-like module provides some extra check features for inheritation at compiletime. |
34
|
|
|
|
|
|
|
It would check that your parent Module is loaded and that in multi-inheritation there is no |
35
|
|
|
|
|
|
|
subroutine-conflict. Use Fukurama::Class instead, to get all the features for OO. |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
=head1 CONFIG |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
You can define the check-level which describes how the module will check inheritations. |
40
|
|
|
|
|
|
|
The following levels are allowed: |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
=over 4 |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
=item $Fukurama::Class::Extends::CHECK_LEVEL = $Fukurama::Class::Extends::LEVEL_DISABLE |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
There is no check. If you use this level, it's like you use B |
47
|
|
|
|
|
|
|
This level is recommended for production. |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
=item $Fukurama::Class::Extends::CHECK_LEVEL = $Fukurama::Class::Extends::LEVEL_CHECK_NONE |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
All registration processes are executed, but there would be no check. |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
=item $Fukurama::Class::Extends::CHECK_LEVEL = $Fukurama::Class::Extends::LEVEL_CHECK_REGISTERED |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
All classes, which use this module would checked for Multi-inheritation-conflicts. |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
=item $Fukurama::Class::Extends::CHECK_LEVEL = $Fukurama::Class::Extends::LEVEL_CHECK_CHILDS |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
All classes, which use this module AND all childs of these classes would checked for Multi-inheritation-conflicts. |
60
|
|
|
|
|
|
|
This is the default behavior when you does'n change the check-level. |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
=item $Fukurama::Class::Extends::CHECK_LEVEL = $Fukurama::Class::Extends::LEVEL_CHECK_ALL |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
All classes would checked for Multi-inheritation-conflicts. This means really ALL classes. Even all perl-internals. |
65
|
|
|
|
|
|
|
This level is only for the sake of completeness. |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
=back |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
=head1 EXPORT |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
- |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=head1 METHODS |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
=over 4 |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
=item extends( child_class:STRING, childs_parent_class:STRING ) return:VOID |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
Helper-method, which would executed by every pragma usage. |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
=item run_check() return:VOID |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
Helper method for static perl (see Fukurama::Class > BUGS) |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
=item register_class_tree() return:VOID |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
Helper method to register needed handler in Fukurama::Class::Tree |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
=back |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
=head1 AUTHOR, BUGS, SUPPORT, ACKNOWLEDGEMENTS, COPYRIGHT & LICENSE |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
see perldoc of L |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
=cut |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
# void |
98
|
|
|
|
|
|
|
my $BUILD_HANDLER = sub { |
99
|
|
|
|
|
|
|
my $classname = $_[0]; |
100
|
|
|
|
|
|
|
my $classdef = $_[1]; |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
my $inheritation_paths = Fukurama::Class::Tree->get_inheritation_path($classname); |
103
|
|
|
|
|
|
|
$classdef->{'extends'} = $inheritation_paths if(scalar(@$inheritation_paths)); |
104
|
|
|
|
|
|
|
return; |
105
|
|
|
|
|
|
|
}; |
106
|
|
|
|
|
|
|
# void |
107
|
|
|
|
|
|
|
my $CHECK_HANDLER = sub { |
108
|
|
|
|
|
|
|
my $classname = $_[0]; |
109
|
|
|
|
|
|
|
my $classdef = $_[1]; |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
my $paths = $classdef->{'extends'}; |
112
|
|
|
|
|
|
|
return if(ref($paths) ne 'ARRAY' || !__PACKAGE__->_check_this_class($classname, $paths)); |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
my $parent_path_subs = []; |
115
|
|
|
|
|
|
|
foreach my $path (@$paths) { |
116
|
|
|
|
|
|
|
my $path_subs = __PACKAGE__->_get_all_subs_for_classpath($path); |
117
|
|
|
|
|
|
|
push(@$parent_path_subs, { |
118
|
|
|
|
|
|
|
subs => $path_subs, |
119
|
|
|
|
|
|
|
path => $path, |
120
|
|
|
|
|
|
|
}); |
121
|
|
|
|
|
|
|
} |
122
|
|
|
|
|
|
|
my $all_subs = {}; |
123
|
|
|
|
|
|
|
foreach my $entry (@$parent_path_subs) { |
124
|
|
|
|
|
|
|
foreach my $subname (keys(%{$entry->{'subs'}})) { |
125
|
|
|
|
|
|
|
if($all_subs->{$subname} && !__PACKAGE__->_is_same_sub($subname, $entry->{'subs'}->{$subname}, $all_subs->{$subname}->{'subs'}->{$subname})) { |
126
|
|
|
|
|
|
|
my $other_entry = $all_subs->{$subname}; |
127
|
|
|
|
|
|
|
my $ident = "$classname\::$entry->{'subs'}->{$subname}"; |
128
|
|
|
|
|
|
|
next if($ERRORS->{$ident}); |
129
|
|
|
|
|
|
|
_carp("Multi-inheritation-warning for class '$classname':\n" . |
130
|
|
|
|
|
|
|
" > sub '$subname' is defined twice in parent-classes\n" . |
131
|
|
|
|
|
|
|
" > '$entry->{'subs'}->{$subname}' and '$other_entry->{'subs'}->{$subname}'\n" . |
132
|
|
|
|
|
|
|
" > inheritation-path for '$entry->{'subs'}->{$subname}':\n" . |
133
|
|
|
|
|
|
|
" $classname > " . join(' > ', @{$entry->{'path'}}) . "\n" . |
134
|
|
|
|
|
|
|
" > inheritation-path for '$other_entry->{'subs'}->{$subname}':\n" . |
135
|
|
|
|
|
|
|
" $classname > " . join(' > ', @{$other_entry->{'path'}}) . "\n", 1); |
136
|
|
|
|
|
|
|
$ERRORS->{$ident} = 1; |
137
|
|
|
|
|
|
|
} |
138
|
|
|
|
|
|
|
$all_subs->{$subname} = $entry; |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
} |
141
|
|
|
|
|
|
|
return; |
142
|
|
|
|
|
|
|
}; |
143
|
|
|
|
|
|
|
# AUTOMAGIC void |
144
|
|
|
|
|
|
|
sub import { |
145
|
6
|
|
|
6
|
|
7730
|
my $class = $_[0]; |
146
|
6
|
|
|
|
|
10
|
my $parent = $_[1]; |
147
|
|
|
|
|
|
|
|
148
|
6
|
|
|
|
|
39
|
my ($child) = caller(0); |
149
|
6
|
|
|
|
|
16
|
local $Carp::CarpLevel = $Carp::CarpLevel + 1; |
150
|
6
|
|
|
|
|
20
|
$class->extends($child, $parent); |
151
|
5
|
|
|
|
|
914
|
return undef; |
152
|
|
|
|
|
|
|
} |
153
|
|
|
|
|
|
|
# void |
154
|
|
|
|
|
|
|
sub extends { |
155
|
16
|
|
|
16
|
1
|
34
|
my $class = $_[0]; |
156
|
16
|
|
|
|
|
24
|
my $child = $_[1]; |
157
|
16
|
|
|
|
|
18
|
my $parent = $_[2]; |
158
|
16
|
|
50
|
|
|
79
|
my $import_depth = $_[3] || 0; |
159
|
|
|
|
|
|
|
|
160
|
16
|
|
|
2
|
|
122
|
local $SIG{'__DIE__'} = sub {}; |
|
2
|
|
|
|
|
30
|
|
161
|
|
|
|
|
|
|
|
162
|
5
|
|
|
5
|
|
33
|
no strict 'refs'; |
|
5
|
|
|
|
|
9
|
|
|
5
|
|
|
|
|
2160
|
|
163
|
|
|
|
|
|
|
|
164
|
16
|
50
|
|
|
|
51
|
if($CHECK_LEVEL > $LEVEL_DISABLE) { |
165
|
16
|
50
|
33
|
|
|
28
|
if(!%{"$child\::"} && eval("use $parent();return 1")) { |
|
16
|
|
|
|
|
77
|
|
166
|
0
|
|
|
|
|
0
|
_croak($@, $import_depth); |
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
} |
169
|
16
|
100
|
66
|
2
|
|
1343
|
if(!eval("package $child;use base qw($parent);return 1") || $@) { |
|
2
|
|
|
2
|
|
15
|
|
|
2
|
|
|
2
|
|
4
|
|
|
2
|
|
|
2
|
|
494
|
|
|
2
|
|
|
2
|
|
13
|
|
|
2
|
|
|
2
|
|
6
|
|
|
2
|
|
|
1
|
|
444
|
|
|
2
|
|
|
1
|
|
13
|
|
|
2
|
|
|
1
|
|
4
|
|
|
2
|
|
|
|
|
392
|
|
|
2
|
|
|
|
|
13
|
|
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
503
|
|
|
2
|
|
|
|
|
15
|
|
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
396
|
|
|
2
|
|
|
|
|
16
|
|
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
829
|
|
|
1
|
|
|
|
|
9
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
16
|
|
|
1
|
|
|
|
|
7
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
19
|
|
|
1
|
|
|
|
|
9
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
20
|
|
|
1
|
|
|
|
|
9
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
49
|
|
170
|
1
|
|
|
|
|
12
|
_croak("Can't extend class '$parent' in child class '$child':\n$@", $import_depth); |
171
|
|
|
|
|
|
|
} |
172
|
|
|
|
|
|
|
|
173
|
15
|
50
|
|
|
|
41
|
return if($CHECK_LEVEL == $LEVEL_DISABLE); |
174
|
15
|
|
|
|
|
36
|
$REGISTER->{$child} = 1; |
175
|
15
|
|
|
|
|
50
|
$class->register_class_tree(); |
176
|
15
|
|
|
|
|
91
|
return; |
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
# STATIC void |
179
|
|
|
|
|
|
|
sub register_class_tree { |
180
|
18
|
|
|
18
|
1
|
36
|
my $class = $_[0]; |
181
|
|
|
|
|
|
|
|
182
|
18
|
|
|
|
|
110
|
Fukurama::Class::Tree->register_build_handler($BUILD_HANDLER); |
183
|
18
|
|
|
|
|
64
|
Fukurama::Class::Tree->register_check_handler($CHECK_HANDLER); |
184
|
18
|
|
|
|
|
31
|
return; |
185
|
|
|
|
|
|
|
} |
186
|
|
|
|
|
|
|
# STATIC boolean |
187
|
|
|
|
|
|
|
sub _check_this_class { |
188
|
677
|
|
|
677
|
|
779
|
my $class = $_[0]; |
189
|
677
|
|
|
|
|
685
|
my $classname = $_[1]; |
190
|
677
|
|
|
|
|
668
|
my $paths = $_[2]; |
191
|
|
|
|
|
|
|
|
192
|
677
|
50
|
|
|
|
1280
|
return 1 if($CHECK_LEVEL == $LEVEL_CHECK_ALL); |
193
|
677
|
50
|
|
|
|
1259
|
return 0 if($CHECK_LEVEL == $LEVEL_CHECK_NONE); |
194
|
|
|
|
|
|
|
|
195
|
677
|
100
|
|
|
|
1394
|
return 1 if($REGISTER->{$classname}); |
196
|
659
|
50
|
|
|
|
1206
|
return 0 if($CHECK_LEVEL == $LEVEL_CHECK_REGISTERED); |
197
|
|
|
|
|
|
|
|
198
|
659
|
50
|
|
|
|
1151
|
if($CHECK_LEVEL == $LEVEL_CHECK_CHILDS) { |
199
|
659
|
|
|
|
|
1039
|
foreach my $path (@$paths) { |
200
|
905
|
|
|
|
|
1285
|
foreach my $path_class (@$path) { |
201
|
2849
|
100
|
|
|
|
11469
|
return 1 if($REGISTER->{$path_class}); |
202
|
|
|
|
|
|
|
} |
203
|
|
|
|
|
|
|
} |
204
|
|
|
|
|
|
|
} |
205
|
656
|
|
|
|
|
4082
|
return 0; |
206
|
|
|
|
|
|
|
} |
207
|
|
|
|
|
|
|
# boolean |
208
|
|
|
|
|
|
|
sub _is_same_sub { |
209
|
2
|
|
|
2
|
|
4
|
my $class = $_[0]; |
210
|
2
|
|
|
|
|
4
|
my $subname = $_[1]; |
211
|
2
|
|
|
|
|
3
|
my $first_class = $_[2]; |
212
|
2
|
|
|
|
|
5
|
my $second_class = $_[3]; |
213
|
|
|
|
|
|
|
|
214
|
5
|
|
|
5
|
|
30
|
no strict 'refs'; |
|
5
|
|
|
|
|
8
|
|
|
5
|
|
|
|
|
1460
|
|
215
|
|
|
|
|
|
|
|
216
|
2
|
50
|
|
|
|
3
|
return 1 if(*{$first_class . '::' . $subname}{'CODE'} == *{$second_class . '::' . $subname}{'CODE'}); |
|
2
|
|
|
|
|
8
|
|
|
2
|
|
|
|
|
11
|
|
217
|
2
|
|
|
|
|
10
|
return 0; |
218
|
|
|
|
|
|
|
} |
219
|
|
|
|
|
|
|
# hashref |
220
|
|
|
|
|
|
|
sub _get_all_subs_for_classpath { |
221
|
23
|
|
|
23
|
|
33
|
my $class = $_[0]; |
222
|
23
|
|
|
|
|
36
|
my $path = $_[1]; |
223
|
|
|
|
|
|
|
|
224
|
23
|
|
|
|
|
32
|
my $path_subs = {}; |
225
|
23
|
|
|
|
|
37
|
foreach my $parent (@$path) { |
226
|
26
|
|
|
|
|
96
|
foreach my $subname (Fukurama::Class::Tree->get_class_subs($parent)) { |
227
|
147
|
|
66
|
|
|
560
|
$path_subs->{$subname} ||= $parent; |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
} |
230
|
23
|
|
|
|
|
54
|
return $path_subs; |
231
|
|
|
|
|
|
|
} |
232
|
|
|
|
|
|
|
# void |
233
|
|
|
|
|
|
|
sub run_check { |
234
|
12
|
|
|
12
|
1
|
32
|
my $class = $_[0]; |
235
|
12
|
|
|
|
|
25
|
my $type = $_[1]; |
236
|
|
|
|
|
|
|
|
237
|
12
|
100
|
|
|
|
42
|
$type = 'MANUAL' if(!defined($type)); |
238
|
12
|
50
|
|
|
|
91
|
Fukurama::Class::Tree->run_check('CHECK') if($CHECK_LEVEL > $LEVEL_DISABLE); |
239
|
12
|
|
|
|
|
68
|
return; |
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
|
242
|
5
|
|
|
5
|
|
32
|
no warnings 'void'; # avoid 'Too late to run CHECK/INIT block' |
|
5
|
|
|
|
|
9
|
|
|
5
|
|
|
|
|
488
|
|
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
# AUTOMAGIC void |
245
|
|
|
|
|
|
|
CHECK { |
246
|
5
|
|
|
5
|
|
34
|
__PACKAGE__->run_check('CHECK'); |
247
|
|
|
|
|
|
|
} |
248
|
|
|
|
|
|
|
# AUTOMAGIC void |
249
|
|
|
|
|
|
|
END { |
250
|
5
|
|
|
5
|
|
9507
|
__PACKAGE__->run_check('END'); |
251
|
|
|
|
|
|
|
} |
252
|
|
|
|
|
|
|
1; |