line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Attribute::Contract; |
2
|
|
|
|
|
|
|
|
3
|
4
|
|
|
4
|
|
127556
|
use strict; |
|
4
|
|
|
|
|
10
|
|
|
4
|
|
|
|
|
136
|
|
4
|
4
|
|
|
4
|
|
19
|
use warnings; |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
111
|
|
5
|
|
|
|
|
|
|
|
6
|
4
|
|
|
4
|
|
63
|
use 5.012; |
|
4
|
|
|
|
|
26
|
|
|
4
|
|
|
|
|
118
|
|
7
|
4
|
|
|
4
|
|
4170
|
use attributes; |
|
4
|
|
|
|
|
7207
|
|
|
4
|
|
|
|
|
24
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
our $VERSION = '0.05'; |
10
|
|
|
|
|
|
|
|
11
|
4
|
|
|
4
|
|
328
|
use constant NO_ATTRIBUTE_CONTRACT => $ENV{NO_ATTRIBUTE_CONTRACT}; |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
284
|
|
12
|
|
|
|
|
|
|
|
13
|
4
|
|
|
4
|
|
24
|
use Scalar::Util qw(refaddr); |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
497
|
|
14
|
|
|
|
|
|
|
|
15
|
4
|
|
|
4
|
|
2264
|
use Attribute::Contract::Modifier::Requires; |
|
4
|
|
|
|
|
11
|
|
|
4
|
|
|
|
|
104
|
|
16
|
4
|
|
|
4
|
|
2407
|
use Attribute::Contract::Modifier::Ensures; |
|
4
|
|
|
|
|
9
|
|
|
4
|
|
|
|
|
117
|
|
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
BEGIN { |
19
|
4
|
|
|
4
|
|
21
|
use Exporter (); |
|
4
|
|
|
|
|
4
|
|
|
4
|
|
|
|
|
190
|
|
20
|
4
|
|
|
4
|
|
8
|
our (@ISA, @EXPORT); |
21
|
|
|
|
|
|
|
|
22
|
4
|
|
|
|
|
58
|
@ISA = qw(Exporter); |
23
|
4
|
|
|
|
|
2369
|
@EXPORT = qw(&MODIFY_CODE_ATTRIBUTES &FETCH_CODE_ATTRIBUTES); |
24
|
|
|
|
|
|
|
} |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
our $CONTRACT_REQUIRES_ATTR_ALIAS = 'ContractRequires'; |
27
|
|
|
|
|
|
|
our $CONTRACT_ENSURES_ATTR_ALIAS = 'ContractEnsures'; |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
my %attrs; |
30
|
|
|
|
|
|
|
my %modifiers; |
31
|
|
|
|
|
|
|
my %symcache; |
32
|
|
|
|
|
|
|
my %todo; |
33
|
|
|
|
|
|
|
my %import; |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
sub contract_requires_name { |
36
|
24
|
100
|
|
24
|
0
|
243
|
$import{-names}->{requires} || $CONTRACT_REQUIRES_ATTR_ALIAS; |
37
|
|
|
|
|
|
|
} |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
sub contract_ensures_name { |
40
|
16
|
100
|
|
16
|
0
|
74
|
$import{-names}->{ensures} || $CONTRACT_ENSURES_ATTR_ALIAS; |
41
|
|
|
|
|
|
|
} |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
sub contract_attr_re { |
44
|
16
|
|
|
16
|
0
|
43
|
my $requires_name = contract_requires_name(); |
45
|
16
|
|
|
|
|
34
|
my $ensures_name = contract_ensures_name(); |
46
|
|
|
|
|
|
|
|
47
|
16
|
|
|
|
|
453
|
return qr/ |
48
|
|
|
|
|
|
|
^ |
49
|
|
|
|
|
|
|
($requires_name|$ensures_name) |
50
|
|
|
|
|
|
|
(?:\((.*?)\))? |
51
|
|
|
|
|
|
|
$ |
52
|
|
|
|
|
|
|
/x; |
53
|
|
|
|
|
|
|
} |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
sub import { |
56
|
6
|
|
|
6
|
|
1381
|
return if NO_ATTRIBUTE_CONTRACT; |
57
|
|
|
|
|
|
|
|
58
|
6
|
|
|
|
|
21
|
my ($package) = caller; |
59
|
6
|
|
|
|
|
17
|
$todo{$package}++; |
60
|
|
|
|
|
|
|
|
61
|
6
|
|
|
|
|
9
|
shift; |
62
|
6
|
|
|
|
|
23
|
%import = @_; |
63
|
|
|
|
|
|
|
|
64
|
6
|
|
|
|
|
2391
|
__PACKAGE__->export_to_level(1); |
65
|
|
|
|
|
|
|
} |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
sub CHECK { |
68
|
4
|
|
|
4
|
|
4033
|
return if NO_ATTRIBUTE_CONTRACT; |
69
|
|
|
|
|
|
|
|
70
|
4
|
|
|
|
|
30
|
foreach my $package (keys %todo) { |
71
|
6
|
|
|
|
|
110
|
foreach my $key (keys %modifiers) { |
72
|
9
|
|
|
|
|
238
|
my ($class, $method) = split /::/, $key; |
73
|
9
|
100
|
|
|
|
86
|
next unless $package->isa($class); |
74
|
|
|
|
|
|
|
|
75
|
7
|
50
|
|
|
|
57
|
next unless my $code_ref = $package->can($method); |
76
|
|
|
|
|
|
|
|
77
|
7
|
|
|
|
|
19
|
my $attrs = $modifiers{$key}; |
78
|
|
|
|
|
|
|
|
79
|
7
|
|
|
|
|
15
|
foreach my $attr (@$attrs) { |
80
|
8
|
50
|
|
|
|
116
|
next unless $attr =~ contract_attr_re(); |
81
|
|
|
|
|
|
|
|
82
|
8
|
|
|
|
|
42
|
attributes::->import($package, $code_ref, $attr); |
83
|
|
|
|
|
|
|
} |
84
|
|
|
|
|
|
|
} |
85
|
|
|
|
|
|
|
} |
86
|
|
|
|
|
|
|
} |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
sub FETCH_CODE_ATTRIBUTES { |
89
|
0
|
|
|
0
|
|
0
|
my ($package, $subref) = @_; |
90
|
|
|
|
|
|
|
|
91
|
0
|
|
|
|
|
0
|
my $attrs = $attrs{refaddr $subref }; |
92
|
|
|
|
|
|
|
|
93
|
0
|
|
|
|
|
0
|
return @$attrs; |
94
|
|
|
|
|
|
|
} |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
sub MODIFY_CODE_ATTRIBUTES { |
97
|
15
|
|
|
15
|
|
4489
|
my ($package, $code_ref, @attr) = @_; |
98
|
|
|
|
|
|
|
|
99
|
15
|
|
|
|
|
36
|
my $sym = findsym($package, $code_ref); |
100
|
15
|
|
|
|
|
44
|
my $name = *{$sym}{NAME}; |
|
15
|
|
|
|
|
35
|
|
101
|
|
|
|
|
|
|
|
102
|
15
|
50
|
|
|
|
83
|
return if exists $attrs{refaddr $code_ref }; |
103
|
15
|
100
|
|
|
|
74
|
return if exists $modifiers{"$package\::$name"}; |
104
|
|
|
|
|
|
|
|
105
|
8
|
|
|
|
|
32
|
$attrs{refaddr $code_ref } = \@attr; |
106
|
8
|
|
|
|
|
24
|
$modifiers{"$package\::$name"} = \@attr; |
107
|
|
|
|
|
|
|
|
108
|
8
|
50
|
|
|
|
21
|
if (@attr) { |
109
|
4
|
|
|
4
|
|
82
|
no strict; |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
286
|
|
110
|
8
|
|
|
|
|
15
|
my @isa = @{"$package\::ISA"}; |
|
8
|
|
|
|
|
34
|
|
111
|
4
|
|
|
4
|
|
20
|
use strict; |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
595
|
|
112
|
8
|
|
|
|
|
20
|
foreach my $isa (@isa) { |
113
|
2
|
|
|
|
|
5
|
my $key = "$isa\::$name"; |
114
|
2
|
50
|
|
|
|
7
|
if (exists $modifiers{$key}) { |
115
|
|
|
|
|
|
|
|
116
|
2
|
|
|
|
|
4
|
my $base_contract = $modifiers{$key}; |
117
|
2
|
|
|
|
|
5
|
my $contract = $modifiers{"$package\::$name"}; |
118
|
|
|
|
|
|
|
|
119
|
2
|
50
|
|
|
|
6
|
if (@$base_contract == @$contract) { |
120
|
|
|
|
|
|
|
next |
121
|
2
|
100
|
|
|
|
11
|
if join(',', sort @$base_contract) eq |
122
|
|
|
|
|
|
|
join(',', sort @$contract); |
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
|
125
|
1
|
|
|
|
|
264
|
Carp::croak(qq{Changing contract of method '$name'} |
126
|
|
|
|
|
|
|
. qq{ in $package is not allowed}); |
127
|
|
|
|
|
|
|
} |
128
|
|
|
|
|
|
|
} |
129
|
|
|
|
|
|
|
} |
130
|
|
|
|
|
|
|
|
131
|
4
|
|
|
4
|
|
35
|
no warnings 'redefine'; |
|
4
|
|
|
|
|
26
|
|
|
4
|
|
|
|
|
987
|
|
132
|
7
|
|
|
|
|
12
|
foreach my $attr (@attr) { |
133
|
8
|
50
|
|
|
|
24
|
next unless $attr =~ contract_attr_re(); |
134
|
|
|
|
|
|
|
|
135
|
8
|
|
|
|
|
31
|
my $type = $1; |
136
|
8
|
|
|
|
|
20
|
my $arguments = $2; |
137
|
|
|
|
|
|
|
|
138
|
8
|
100
|
|
|
|
18
|
my $modifier = $type eq contract_requires_name() ? 'Requires' : 'Ensures'; |
139
|
|
|
|
|
|
|
|
140
|
8
|
|
|
|
|
18
|
my $class = __PACKAGE__ . '::Modifier::' . $modifier; |
141
|
|
|
|
|
|
|
|
142
|
8
|
|
|
|
|
62
|
*{$sym} = $class->modify($package, $name, $code_ref, \%import, $arguments); |
|
8
|
|
|
|
|
59
|
|
143
|
|
|
|
|
|
|
} |
144
|
|
|
|
|
|
|
|
145
|
7
|
|
|
|
|
37
|
return (); |
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
# From Attribute::Handlers |
149
|
|
|
|
|
|
|
sub findsym { |
150
|
15
|
|
|
15
|
0
|
96
|
my ($package, $ref) = @_; |
151
|
|
|
|
|
|
|
|
152
|
15
|
100
|
|
|
|
83
|
return $symcache{$package, $ref} if $symcache{$package, $ref}; |
153
|
|
|
|
|
|
|
|
154
|
14
|
|
|
|
|
25
|
my $type = ref($ref); |
155
|
|
|
|
|
|
|
|
156
|
4
|
|
|
4
|
|
20
|
no strict 'refs'; |
|
4
|
|
|
|
|
11
|
|
|
4
|
|
|
|
|
167
|
|
157
|
14
|
|
|
|
|
20
|
foreach my $sym (values %{$package . "::"}) { |
|
14
|
|
|
|
|
50
|
|
158
|
4
|
|
|
4
|
|
18
|
use strict; |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
513
|
|
159
|
61
|
50
|
|
|
|
138
|
next unless ref(\$sym) eq 'GLOB'; |
160
|
|
|
|
|
|
|
|
161
|
61
|
|
|
|
|
258
|
return $symcache{$package, $ref} = \$sym |
162
|
61
|
100
|
100
|
|
|
62
|
if *{$sym}{$type} && *{$sym}{$type} == $ref; |
|
32
|
|
|
|
|
239
|
|
163
|
|
|
|
|
|
|
} |
164
|
|
|
|
|
|
|
|
165
|
0
|
|
|
|
|
|
return; |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
1; |
169
|
|
|
|
|
|
|
__END__ |