| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Module::Patch; |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
our $DATE = '2019-02-14'; # DATE |
|
4
|
|
|
|
|
|
|
our $VERSION = '0.276'; # VERSION |
|
5
|
|
|
|
|
|
|
|
|
6
|
1
|
|
|
1
|
|
2253
|
use 5.010001; |
|
|
1
|
|
|
|
|
4
|
|
|
7
|
1
|
|
|
1
|
|
4
|
use strict 'subs', 'vars'; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
29
|
|
|
8
|
1
|
|
|
1
|
|
4
|
use warnings; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
23
|
|
|
9
|
1
|
|
|
1
|
|
1513
|
use Log::ger; |
|
|
1
|
|
|
|
|
47
|
|
|
|
1
|
|
|
|
|
9
|
|
|
10
|
|
|
|
|
|
|
|
|
11
|
1
|
|
|
1
|
|
793
|
use Monkey::Patch::Action qw(); |
|
|
1
|
|
|
|
|
3720
|
|
|
|
1
|
|
|
|
|
22
|
|
|
12
|
1
|
|
|
1
|
|
476
|
use Package::Stash; |
|
|
1
|
|
|
|
|
8184
|
|
|
|
1
|
|
|
|
|
38
|
|
|
13
|
1
|
|
|
1
|
|
456
|
use Package::Util::Lite qw(package_exists); |
|
|
1
|
|
|
|
|
424
|
|
|
|
1
|
|
|
|
|
690
|
|
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
our @EXPORT_OK = qw(patch_package); |
|
16
|
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
sub is_loaded { |
|
18
|
6
|
|
|
6
|
0
|
12
|
my $mod = shift; |
|
19
|
|
|
|
|
|
|
|
|
20
|
6
|
|
|
|
|
21
|
(my $mod_pm = "$mod.pm") =~ s!::!/!g; |
|
21
|
6
|
100
|
|
|
|
29
|
exists($INC{$mod_pm}) && $INC{$mod_pm}; |
|
22
|
|
|
|
|
|
|
} |
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
my%loaded_by_us; |
|
25
|
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
sub import { |
|
27
|
6
|
|
|
6
|
|
11115
|
my $self = shift; |
|
28
|
|
|
|
|
|
|
|
|
29
|
6
|
|
|
|
|
21
|
my $caller = caller; |
|
30
|
|
|
|
|
|
|
|
|
31
|
6
|
50
|
|
|
|
203
|
if ($self eq __PACKAGE__) { |
|
32
|
|
|
|
|
|
|
# we are not subclassed, provide exports |
|
33
|
0
|
|
|
|
|
0
|
for my $exp (@_) { |
|
34
|
|
|
|
|
|
|
die "$exp is not exported by ".__PACKAGE__ |
|
35
|
0
|
0
|
|
|
|
0
|
unless grep { $_ eq $exp } @EXPORT_OK; |
|
|
0
|
|
|
|
|
0
|
|
|
36
|
0
|
|
|
|
|
0
|
*{"$caller\::$exp"} = \&{$exp}; |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
37
|
|
|
|
|
|
|
} |
|
38
|
|
|
|
|
|
|
} else { |
|
39
|
|
|
|
|
|
|
# we are subclassed, patch caller with patch_data() |
|
40
|
6
|
|
|
|
|
16
|
my %opts = @_; |
|
41
|
|
|
|
|
|
|
|
|
42
|
6
|
|
|
|
|
9
|
my $load; |
|
43
|
6
|
50
|
|
|
|
19
|
if (exists $opts{-load_target}) { |
|
44
|
0
|
|
|
|
|
0
|
$load = $opts{-load_target}; |
|
45
|
0
|
|
|
|
|
0
|
delete $opts{-load_target}; |
|
46
|
|
|
|
|
|
|
} |
|
47
|
6
|
|
50
|
|
|
29
|
$load //= 1; |
|
48
|
6
|
|
|
|
|
9
|
my $force; |
|
49
|
6
|
100
|
|
|
|
29
|
if (exists $opts{-force}) { |
|
50
|
1
|
|
|
|
|
3
|
$force = $opts{-force}; |
|
51
|
1
|
|
|
|
|
2
|
delete $opts{-force}; |
|
52
|
|
|
|
|
|
|
} |
|
53
|
6
|
|
100
|
|
|
23
|
$force //= 0; |
|
54
|
6
|
|
|
|
|
7
|
my $warn; |
|
55
|
6
|
50
|
|
|
|
12
|
if (exists $opts{-warn_target_loaded}) { |
|
56
|
0
|
|
|
|
|
0
|
$warn = $opts{-warn_target_loaded}; |
|
57
|
0
|
|
|
|
|
0
|
delete $opts{-warn_target_loaded}; |
|
58
|
|
|
|
|
|
|
} |
|
59
|
6
|
|
50
|
|
|
19
|
$warn //= 1; |
|
60
|
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
# patch already applied, ignore |
|
62
|
6
|
50
|
|
|
|
9
|
return if ${"$self\::handles"}; |
|
|
6
|
|
|
|
|
31
|
|
|
63
|
|
|
|
|
|
|
|
|
64
|
6
|
100
|
|
|
|
9
|
unless (${"$self\::patch_data_cached"}) { |
|
|
6
|
|
|
|
|
26
|
|
|
65
|
3
|
|
|
|
|
12
|
${"$self\::patch_data_cached"} = $self->patch_data; |
|
|
3
|
|
|
|
|
42
|
|
|
66
|
|
|
|
|
|
|
} |
|
67
|
|
|
|
|
|
|
|
|
68
|
6
|
50
|
|
|
|
9
|
my $pdata = ${"$self\::patch_data_cached"} or |
|
|
6
|
|
|
|
|
20
|
|
|
69
|
|
|
|
|
|
|
die "BUG: $self: No patch data supplied"; |
|
70
|
6
|
|
50
|
|
|
15
|
my $v = $pdata->{v} // 1; |
|
71
|
6
|
|
|
|
|
8
|
my $curv = 3; |
|
72
|
6
|
50
|
33
|
|
|
33
|
if ($v == 1 || $v == 2) { |
|
|
|
50
|
|
|
|
|
|
|
73
|
0
|
|
|
|
|
0
|
my $mpv; |
|
74
|
0
|
0
|
|
|
|
0
|
if ($v == 1) { |
|
|
|
0
|
|
|
|
|
|
|
75
|
0
|
|
|
|
|
0
|
$mpv = "0.06 or earlier"; |
|
76
|
|
|
|
|
|
|
} elsif ($v == 2) { |
|
77
|
0
|
|
|
|
|
0
|
$mpv = "0.07-0.09"; |
|
78
|
|
|
|
|
|
|
} |
|
79
|
0
|
|
0
|
|
|
0
|
die "$self ".( ${"$self\::VERSION" } // "?" ). |
|
|
0
|
|
0
|
|
|
0
|
|
|
80
|
|
|
|
|
|
|
" requires Module::Patch $mpv (patch_data format v=$v),". |
|
81
|
|
|
|
|
|
|
" this is Module::Patch ".($Module::Patch::VERSION // '?'). |
|
82
|
|
|
|
|
|
|
" (v=$curv), please install an older version of ". |
|
83
|
|
|
|
|
|
|
"Module::Patch or upgrade $self"; |
|
84
|
|
|
|
|
|
|
} elsif ($v == 3) { |
|
85
|
|
|
|
|
|
|
# ok, current version |
|
86
|
|
|
|
|
|
|
} else { |
|
87
|
0
|
|
|
|
|
0
|
die "BUG: $self: Unknown patch_data format version ($v), ". |
|
88
|
|
|
|
|
|
|
"only v=$curv supported by this version of Module::Patch"; |
|
89
|
|
|
|
|
|
|
} |
|
90
|
|
|
|
|
|
|
|
|
91
|
6
|
|
|
|
|
12
|
my $target = $self; |
|
92
|
6
|
50
|
|
|
|
47
|
$target =~ s/(?<=\w)::[Pp]atch::\w+$// |
|
93
|
|
|
|
|
|
|
or die "BUG: $self: Bad patch module name '$target', it should ". |
|
94
|
|
|
|
|
|
|
"end with '::Patch::YourCategory'"; |
|
95
|
|
|
|
|
|
|
|
|
96
|
6
|
100
|
|
|
|
13
|
if (is_loaded($target)) { |
|
97
|
5
|
50
|
|
|
|
12
|
if (!$loaded_by_us{$target}) { |
|
98
|
0
|
0
|
0
|
|
|
0
|
if ($load && $warn) { |
|
99
|
0
|
|
|
|
|
0
|
warn "$target is loaded before ".__PACKAGE__.", this is ". |
|
100
|
|
|
|
|
|
|
"not recommended since $target might export subs ". |
|
101
|
|
|
|
|
|
|
"before " . __PACKAGE__." gets the chance to patch " . |
|
102
|
|
|
|
|
|
|
"them"; |
|
103
|
|
|
|
|
|
|
} |
|
104
|
|
|
|
|
|
|
} |
|
105
|
|
|
|
|
|
|
} else { |
|
106
|
1
|
50
|
|
|
|
3
|
if ($load) { |
|
107
|
1
|
|
|
1
|
|
431
|
eval "package $caller; use $target"; |
|
|
1
|
|
|
|
|
36
|
|
|
|
1
|
|
|
|
|
19
|
|
|
|
1
|
|
|
|
|
66
|
|
|
108
|
1
|
50
|
|
|
|
5
|
die if $@; |
|
109
|
1
|
|
|
|
|
2
|
$loaded_by_us{$target}++; |
|
110
|
|
|
|
|
|
|
} else { |
|
111
|
0
|
0
|
|
|
|
0
|
if ($warn) { |
|
112
|
0
|
|
|
|
|
0
|
warn "$target does not exist and we are told not to load ". |
|
113
|
|
|
|
|
|
|
"it, skipped patching"; |
|
114
|
|
|
|
|
|
|
} |
|
115
|
0
|
|
|
|
|
0
|
return; |
|
116
|
|
|
|
|
|
|
} |
|
117
|
|
|
|
|
|
|
} |
|
118
|
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
# read patch module's configs |
|
120
|
1
|
|
|
1
|
|
8
|
no warnings 'once'; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
1301
|
|
|
121
|
6
|
|
100
|
|
|
24
|
my $pcdata = $pdata->{config} // {}; |
|
122
|
6
|
|
|
|
|
8
|
my $config = \%{"$self\::config"}; |
|
|
6
|
|
|
|
|
20
|
|
|
123
|
6
|
|
|
|
|
26
|
while (my ($k, $v) = each %$pcdata) { |
|
124
|
6
|
50
|
|
|
|
19
|
die "Invalid configuration defined by $self\::patch_data(): ". |
|
125
|
|
|
|
|
|
|
"$k: must start with dash" unless $k =~ /\A-/; |
|
126
|
6
|
|
|
|
|
12
|
$config->{$k} = $v->{default}; |
|
127
|
6
|
100
|
|
|
|
20
|
if (exists $opts{$k}) { |
|
128
|
1
|
|
|
|
|
2
|
$config->{$k} = $opts{$k}; |
|
129
|
1
|
|
|
|
|
4
|
delete $opts{$k}; |
|
130
|
|
|
|
|
|
|
} |
|
131
|
|
|
|
|
|
|
} |
|
132
|
|
|
|
|
|
|
|
|
133
|
6
|
100
|
|
|
|
17
|
if (keys %opts) { |
|
134
|
1
|
|
|
|
|
15
|
die "$self: Unknown option(s): ".join(", ", keys %opts); |
|
135
|
|
|
|
|
|
|
} |
|
136
|
|
|
|
|
|
|
|
|
137
|
5
|
50
|
|
|
|
12
|
if ($pdata->{after_read_config}) { |
|
138
|
0
|
|
|
|
|
0
|
$pdata->{after_read_config}->(); |
|
139
|
|
|
|
|
|
|
} |
|
140
|
|
|
|
|
|
|
|
|
141
|
5
|
50
|
|
|
|
10
|
if ($pdata->{before_patch}) { |
|
142
|
0
|
|
|
|
|
0
|
$pdata->{before_patch}->(); |
|
143
|
|
|
|
|
|
|
} |
|
144
|
|
|
|
|
|
|
|
|
145
|
5
|
|
|
|
|
23
|
log_trace "Module::Patch: patching $target with $self ..."; |
|
146
|
4
|
|
|
|
|
15
|
${"$self\::handles"} = patch_package( |
|
147
|
|
|
|
|
|
|
$target, $pdata->{patches}, |
|
148
|
5
|
|
33
|
|
|
36
|
{force=>$force, patch_module=>ref($self) || $self}); |
|
149
|
|
|
|
|
|
|
|
|
150
|
4
|
50
|
|
|
|
31
|
if ($pdata->{after_patch}) { |
|
151
|
0
|
|
|
|
|
0
|
$pdata->{after_patch}->(); |
|
152
|
|
|
|
|
|
|
} |
|
153
|
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
} |
|
155
|
|
|
|
|
|
|
} |
|
156
|
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
sub unimport { |
|
158
|
4
|
|
|
4
|
|
6595
|
my $self = shift; |
|
159
|
|
|
|
|
|
|
|
|
160
|
4
|
50
|
|
|
|
13
|
if ($self eq __PACKAGE__) { |
|
161
|
|
|
|
|
|
|
# we are not subclassed, do nothing |
|
162
|
|
|
|
|
|
|
} else { |
|
163
|
4
|
50
|
|
|
|
5
|
my $pdata = ${"$self\::patch_data_cached"} or |
|
|
4
|
|
|
|
|
21
|
|
|
164
|
|
|
|
|
|
|
die "BUG: $self: No patch data supplied"; |
|
165
|
|
|
|
|
|
|
|
|
166
|
4
|
50
|
|
|
|
10
|
if ($pdata->{before_unpatch}) { |
|
167
|
0
|
|
|
|
|
0
|
$pdata->{before_unpatch}->(); |
|
168
|
|
|
|
|
|
|
} |
|
169
|
|
|
|
|
|
|
|
|
170
|
4
|
|
|
|
|
7
|
my $handles = ${"$self\::handles"}; |
|
|
4
|
|
|
|
|
11
|
|
|
171
|
4
|
|
|
|
|
14
|
log_trace "Module::Patch: Unpatching $self ..."; |
|
172
|
4
|
|
|
|
|
12
|
undef ${"$self\::handles"}; |
|
|
4
|
|
|
|
|
10
|
|
|
173
|
|
|
|
|
|
|
# do we need to undef ${"$self\::config"}?, i'm thinking not really |
|
174
|
|
|
|
|
|
|
|
|
175
|
4
|
50
|
|
|
|
20
|
if ($pdata->{after_unpatch}) { |
|
176
|
0
|
|
|
|
|
0
|
$pdata->{after_unpatch}->(); |
|
177
|
|
|
|
|
|
|
} |
|
178
|
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
} |
|
180
|
|
|
|
|
|
|
} |
|
181
|
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
sub patch_data { |
|
183
|
0
|
|
|
0
|
0
|
0
|
die "BUG: patch_data() should be provided by subclass"; |
|
184
|
|
|
|
|
|
|
} |
|
185
|
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
sub patch_package { |
|
187
|
5
|
|
|
5
|
1
|
15
|
my ($package0, $patches_spec, $opts) = @_; |
|
188
|
5
|
|
50
|
|
|
11
|
$opts //= {}; |
|
189
|
|
|
|
|
|
|
|
|
190
|
5
|
|
|
|
|
8
|
my $handles = {}; |
|
191
|
5
|
50
|
|
|
|
17
|
for my $target (ref($package0) eq 'ARRAY' ? @$package0 : ($package0)) { |
|
192
|
5
|
50
|
|
|
|
12
|
die "FATAL: Target module '$target' not loaded" |
|
193
|
|
|
|
|
|
|
unless package_exists($target); |
|
194
|
5
|
|
|
|
|
86
|
my $target_version = ${"$target\::VERSION"}; |
|
|
5
|
|
|
|
|
12
|
|
|
195
|
5
|
|
|
|
|
6
|
my $target_subs; |
|
196
|
|
|
|
|
|
|
|
|
197
|
5
|
|
|
|
|
9
|
my $i = 0; |
|
198
|
|
|
|
|
|
|
PATCH: |
|
199
|
5
|
|
|
|
|
11
|
for my $pspec (@$patches_spec) { |
|
200
|
7
|
|
|
|
|
11
|
my $act = $pspec->{action}; |
|
201
|
7
|
50
|
|
|
|
26
|
my $errp = ($opts->{patch_module} ? "$opts->{patch_module}: ":""). |
|
202
|
|
|
|
|
|
|
"patch[$i]"; # error prefix |
|
203
|
7
|
50
|
|
|
|
15
|
$act or die "BUG: $errp: no action supplied"; |
|
204
|
7
|
50
|
|
|
|
31
|
$act =~ /\A(wrap|add|replace|add_or_replace|delete)\z/ or die |
|
205
|
|
|
|
|
|
|
"BUG: $errp: action '$pspec->{action}' unknown"; |
|
206
|
7
|
50
|
|
|
|
17
|
if ($act eq 'delete') { |
|
207
|
0
|
0
|
|
|
|
0
|
$pspec->{code} and die "BUG: $errp: for action 'delete', ". |
|
208
|
|
|
|
|
|
|
"code must not be supplied"; |
|
209
|
|
|
|
|
|
|
} else { |
|
210
|
7
|
50
|
|
|
|
15
|
$pspec->{code} or die "BUG: $errp: code not supplied"; |
|
211
|
|
|
|
|
|
|
} |
|
212
|
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
my $sub_names = ref($pspec->{sub_name}) eq 'ARRAY' ? |
|
214
|
7
|
50
|
|
|
|
19
|
[@{ $pspec->{sub_name} }] : [$pspec->{sub_name}]; |
|
|
0
|
|
|
|
|
0
|
|
|
215
|
7
|
|
|
|
|
15
|
for (@$sub_names) { |
|
216
|
7
|
50
|
|
|
|
13
|
$_ = qr/.*/ if $_ eq ':all'; |
|
217
|
7
|
50
|
|
|
|
15
|
$_ = qr/^_/ if $_ eq ':private'; |
|
218
|
7
|
50
|
|
|
|
12
|
$_ = qr/^[^_]/ if $_ eq ':public'; |
|
219
|
7
|
50
|
|
|
|
17
|
die "BUG: $errp: unknown tag in sub_name $_" if /^:/; |
|
220
|
|
|
|
|
|
|
} |
|
221
|
|
|
|
|
|
|
|
|
222
|
7
|
|
|
|
|
8
|
my @s; |
|
223
|
7
|
|
|
|
|
12
|
for my $sub_name (@$sub_names) { |
|
224
|
7
|
50
|
|
|
|
12
|
if (ref($sub_name) eq 'Regexp') { |
|
225
|
0
|
0
|
|
|
|
0
|
unless ($target_subs) { |
|
226
|
0
|
|
|
|
|
0
|
$target_subs = [Package::Stash->new($target)->list_all_symbols("CODE")]; |
|
227
|
|
|
|
|
|
|
} |
|
228
|
0
|
|
|
|
|
0
|
for (@$target_subs) { |
|
229
|
0
|
0
|
0
|
|
|
0
|
push @s, $_ if $_ !~~ @s && $_ =~ $sub_name; |
|
230
|
|
|
|
|
|
|
} |
|
231
|
|
|
|
|
|
|
} else { |
|
232
|
7
|
|
|
|
|
16
|
push @s, $sub_name; |
|
233
|
|
|
|
|
|
|
} |
|
234
|
|
|
|
|
|
|
} |
|
235
|
|
|
|
|
|
|
|
|
236
|
7
|
100
|
66
|
|
|
40
|
unless (!defined($pspec->{mod_version}) || |
|
237
|
|
|
|
|
|
|
$pspec->{mod_version} eq ':all') { |
|
238
|
5
|
50
|
33
|
|
|
20
|
defined($target_version) && length($target_version) |
|
239
|
|
|
|
|
|
|
or die "FATAL: Target package '$target' does not have ". |
|
240
|
|
|
|
|
|
|
"\$VERSION"; |
|
241
|
5
|
|
|
|
|
7
|
my $mod_versions = $pspec->{mod_version}; |
|
242
|
5
|
100
|
|
|
|
15
|
$mod_versions = ref($mod_versions) eq 'ARRAY' ? |
|
243
|
|
|
|
|
|
|
[@$mod_versions] : [$mod_versions]; |
|
244
|
5
|
|
|
|
|
10
|
for (@$mod_versions) { |
|
245
|
7
|
50
|
|
|
|
12
|
$_ = qr/.*/ if $_ eq ':all'; |
|
246
|
7
|
50
|
|
|
|
18
|
die "BUG: $errp: unknown tag in mod_version $_" |
|
247
|
|
|
|
|
|
|
if /^:/; |
|
248
|
|
|
|
|
|
|
} |
|
249
|
|
|
|
|
|
|
|
|
250
|
5
|
100
|
|
|
|
8
|
unless (grep { |
|
251
|
7
|
50
|
|
|
|
30
|
ref($_) eq 'Regexp' ? $target_version =~ $_ : $target_version eq $_ |
|
252
|
|
|
|
|
|
|
} @$mod_versions) { |
|
253
|
|
|
|
|
|
|
warn "$errp: Target module version $target_version ". |
|
254
|
|
|
|
|
|
|
"does not match [".join(", ", @$mod_versions)."], ". |
|
255
|
|
|
|
|
|
|
($opts->{force} ? |
|
256
|
2
|
100
|
|
|
|
106
|
"patching anyway (force)":"skipped"). "."; |
|
257
|
2
|
100
|
|
|
|
19
|
next PATCH unless $opts->{force}; |
|
258
|
|
|
|
|
|
|
} |
|
259
|
|
|
|
|
|
|
} |
|
260
|
|
|
|
|
|
|
|
|
261
|
6
|
|
|
|
|
12
|
for my $s (@s) { |
|
262
|
|
|
|
|
|
|
#log_trace("Patching %s ...", $s); |
|
263
|
|
|
|
|
|
|
$handles->{"$target\::$s"} = |
|
264
|
|
|
|
|
|
|
Monkey::Patch::Action::patch_package( |
|
265
|
6
|
|
|
|
|
21
|
$target, $s, $act, $pspec->{code}); |
|
266
|
|
|
|
|
|
|
} |
|
267
|
|
|
|
|
|
|
|
|
268
|
5
|
|
|
|
|
484
|
$i++; |
|
269
|
|
|
|
|
|
|
} # for $pspec |
|
270
|
|
|
|
|
|
|
} # for $target |
|
271
|
4
|
|
|
|
|
9
|
$handles; |
|
272
|
|
|
|
|
|
|
} |
|
273
|
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
1; |
|
275
|
|
|
|
|
|
|
# ABSTRACT: Patch package with a set of patches |
|
276
|
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
__END__ |