line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Module::Patch; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
our $DATE = '2019-01-06'; # DATE |
4
|
|
|
|
|
|
|
our $VERSION = '0.275'; # VERSION |
5
|
|
|
|
|
|
|
|
6
|
1
|
|
|
1
|
|
2022
|
use 5.010001; |
|
1
|
|
|
|
|
3
|
|
7
|
1
|
|
|
1
|
|
4
|
use strict 'subs', 'vars'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
27
|
|
8
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
21
|
|
9
|
1
|
|
|
1
|
|
1257
|
use Log::ger; |
|
1
|
|
|
|
|
41
|
|
|
1
|
|
|
|
|
4
|
|
10
|
|
|
|
|
|
|
|
11
|
1
|
|
|
1
|
|
601
|
use Monkey::Patch::Action qw(); |
|
1
|
|
|
|
|
3051
|
|
|
1
|
|
|
|
|
19
|
|
12
|
1
|
|
|
1
|
|
354
|
use Package::Stash; |
|
1
|
|
|
|
|
6800
|
|
|
1
|
|
|
|
|
36
|
|
13
|
1
|
|
|
1
|
|
371
|
use Package::Util::Lite qw(package_exists); |
|
1
|
|
|
|
|
364
|
|
|
1
|
|
|
|
|
591
|
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
our @EXPORT_OK = qw(patch_package); |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
sub is_loaded { |
18
|
6
|
|
|
6
|
0
|
11
|
my $mod = shift; |
19
|
|
|
|
|
|
|
|
20
|
6
|
|
|
|
|
18
|
(my $mod_pm = "$mod.pm") =~ s!::!/!g; |
21
|
6
|
100
|
|
|
|
27
|
exists($INC{$mod_pm}) && $INC{$mod_pm}; |
22
|
|
|
|
|
|
|
} |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
my%loaded_by_us; |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
sub import { |
27
|
6
|
|
|
6
|
|
10158
|
my $self = shift; |
28
|
|
|
|
|
|
|
|
29
|
6
|
|
|
|
|
17
|
my $caller = caller; |
30
|
|
|
|
|
|
|
|
31
|
6
|
50
|
|
|
|
180
|
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"} = \&{$_}; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
37
|
|
|
|
|
|
|
} |
38
|
|
|
|
|
|
|
} else { |
39
|
|
|
|
|
|
|
# we are subclassed, patch caller with patch_data() |
40
|
6
|
|
|
|
|
14
|
my %opts = @_; |
41
|
|
|
|
|
|
|
|
42
|
6
|
|
|
|
|
7
|
my $load; |
43
|
6
|
50
|
|
|
|
16
|
if (exists $opts{-load_target}) { |
44
|
0
|
|
|
|
|
0
|
$load = $opts{-load_target}; |
45
|
0
|
|
|
|
|
0
|
delete $opts{-load_target}; |
46
|
|
|
|
|
|
|
} |
47
|
6
|
|
50
|
|
|
26
|
$load //= 1; |
48
|
6
|
|
|
|
|
8
|
my $force; |
49
|
6
|
100
|
|
|
|
37
|
if (exists $opts{-force}) { |
50
|
1
|
|
|
|
|
3
|
$force = $opts{-force}; |
51
|
1
|
|
|
|
|
2
|
delete $opts{-force}; |
52
|
|
|
|
|
|
|
} |
53
|
6
|
|
100
|
|
|
19
|
$force //= 0; |
54
|
6
|
|
|
|
|
7
|
my $warn; |
55
|
6
|
50
|
|
|
|
13
|
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
|
|
|
18
|
$warn //= 1; |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
# patch already applied, ignore |
62
|
6
|
50
|
|
|
|
6
|
return if ${"$self\::handles"}; |
|
6
|
|
|
|
|
27
|
|
63
|
|
|
|
|
|
|
|
64
|
6
|
100
|
|
|
|
8
|
unless (${"$self\::patch_data_cached"}) { |
|
6
|
|
|
|
|
25
|
|
65
|
3
|
|
|
|
|
10
|
${"$self\::patch_data_cached"} = $self->patch_data; |
|
3
|
|
|
|
|
34
|
|
66
|
|
|
|
|
|
|
} |
67
|
|
|
|
|
|
|
|
68
|
6
|
50
|
|
|
|
6
|
my $pdata = ${"$self\::patch_data_cached"} or |
|
6
|
|
|
|
|
17
|
|
69
|
|
|
|
|
|
|
die "BUG: $self: No patch data supplied"; |
70
|
6
|
|
50
|
|
|
21
|
my $v = $pdata->{v} // 1; |
71
|
6
|
|
|
|
|
7
|
my $curv = 3; |
72
|
6
|
50
|
33
|
|
|
21
|
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
|
|
|
|
|
8
|
my $target = $self; |
92
|
6
|
50
|
|
|
|
39
|
$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
|
|
|
|
13
|
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
|
|
346
|
eval "package $caller; use $target"; |
|
1
|
|
|
|
|
29
|
|
|
1
|
|
|
|
|
16
|
|
|
1
|
|
|
|
|
54
|
|
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
|
|
7
|
no warnings 'once'; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
1093
|
|
121
|
6
|
|
100
|
|
|
19
|
my $pcdata = $pdata->{config} // {}; |
122
|
6
|
|
|
|
|
8
|
my $config = \%{"$self\::config"}; |
|
6
|
|
|
|
|
16
|
|
123
|
6
|
|
|
|
|
22
|
while (my ($k, $v) = each %$pcdata) { |
124
|
6
|
50
|
|
|
|
18
|
die "Invalid configuration defined by $self\::patch_data(): ". |
125
|
|
|
|
|
|
|
"$k: must start with dash" unless $k =~ /\A-/; |
126
|
6
|
|
|
|
|
10
|
$config->{$k} = $v->{default}; |
127
|
6
|
100
|
|
|
|
17
|
if (exists $opts{$k}) { |
128
|
1
|
|
|
|
|
3
|
$config->{$k} = $opts{$k}; |
129
|
1
|
|
|
|
|
3
|
delete $opts{$k}; |
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
|
133
|
6
|
100
|
|
|
|
16
|
if (keys %opts) { |
134
|
1
|
|
|
|
|
12
|
die "$self: Unknown option(s): ".join(", ", keys %opts); |
135
|
|
|
|
|
|
|
} |
136
|
|
|
|
|
|
|
|
137
|
5
|
50
|
|
|
|
8
|
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
|
|
|
|
|
21
|
log_trace "Module::Patch: patching $target with $self ..."; |
146
|
4
|
|
|
|
|
13
|
${"$self\::handles"} = patch_package( |
147
|
|
|
|
|
|
|
$target, $pdata->{patches}, |
148
|
5
|
|
33
|
|
|
29
|
{force=>$force, patch_module=>ref($self) || $self}); |
149
|
|
|
|
|
|
|
|
150
|
4
|
50
|
|
|
|
24
|
if ($pdata->{after_patch}) { |
151
|
0
|
|
|
|
|
0
|
$pdata->{after_patch}->(); |
152
|
|
|
|
|
|
|
} |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
} |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
sub unimport { |
158
|
4
|
|
|
4
|
|
7733
|
my $self = shift; |
159
|
|
|
|
|
|
|
|
160
|
4
|
50
|
|
|
|
19
|
if ($self eq __PACKAGE__) { |
161
|
|
|
|
|
|
|
# we are not subclassed, do nothing |
162
|
|
|
|
|
|
|
} else { |
163
|
4
|
50
|
|
|
|
6
|
my $pdata = ${"$self\::patch_data_cached"} or |
|
4
|
|
|
|
|
18
|
|
164
|
|
|
|
|
|
|
die "BUG: $self: No patch data supplied"; |
165
|
|
|
|
|
|
|
|
166
|
4
|
50
|
|
|
|
8
|
if ($pdata->{before_unpatch}) { |
167
|
0
|
|
|
|
|
0
|
$pdata->{before_unpatch}->(); |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
|
170
|
4
|
|
|
|
|
5
|
my $handles = ${"$self\::handles"}; |
|
4
|
|
|
|
|
10
|
|
171
|
4
|
|
|
|
|
17
|
log_trace "Module::Patch: Unpatching $self ..."; |
172
|
4
|
|
|
|
|
9
|
undef ${"$self\::handles"}; |
|
4
|
|
|
|
|
9
|
|
173
|
|
|
|
|
|
|
# do we need to undef ${"$self\::config"}?, i'm thinking not really |
174
|
|
|
|
|
|
|
|
175
|
4
|
50
|
|
|
|
17
|
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
|
11
|
my ($package0, $patches_spec, $opts) = @_; |
188
|
5
|
|
50
|
|
|
12
|
$opts //= {}; |
189
|
|
|
|
|
|
|
|
190
|
5
|
|
|
|
|
8
|
my $handles = {}; |
191
|
5
|
50
|
|
|
|
13
|
for my $target (ref($package0) eq 'ARRAY' ? @$package0 : ($package0)) { |
192
|
5
|
50
|
|
|
|
13
|
die "FATAL: Target module '$target' not loaded" |
193
|
|
|
|
|
|
|
unless package_exists($target); |
194
|
5
|
|
|
|
|
61
|
my $target_version = ${"$target\::VERSION"}; |
|
5
|
|
|
|
|
11
|
|
195
|
5
|
|
|
|
|
6
|
my $target_subs; |
196
|
|
|
|
|
|
|
|
197
|
5
|
|
|
|
|
5
|
my $i = 0; |
198
|
|
|
|
|
|
|
PATCH: |
199
|
5
|
|
|
|
|
10
|
for my $pspec (@$patches_spec) { |
200
|
7
|
|
|
|
|
11
|
my $act = $pspec->{action}; |
201
|
7
|
50
|
|
|
|
20
|
my $errp = ($opts->{patch_module} ? "$opts->{patch_module}: ":""). |
202
|
|
|
|
|
|
|
"patch[$i]"; # error prefix |
203
|
7
|
50
|
|
|
|
14
|
$act or die "BUG: $errp: no action supplied"; |
204
|
7
|
50
|
|
|
|
25
|
$act =~ /\A(wrap|add|replace|add_or_replace|delete)\z/ or die |
205
|
|
|
|
|
|
|
"BUG: $errp: action '$pspec->{action}' unknown"; |
206
|
7
|
50
|
|
|
|
14
|
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
|
|
|
|
11
|
$pspec->{code} or die "BUG: $errp: code not supplied"; |
211
|
|
|
|
|
|
|
} |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
my $sub_names = ref($pspec->{sub_name}) eq 'ARRAY' ? |
214
|
7
|
50
|
|
|
|
17
|
[@{ $pspec->{sub_name} }] : [$pspec->{sub_name}]; |
|
0
|
|
|
|
|
0
|
|
215
|
7
|
|
|
|
|
12
|
for (@$sub_names) { |
216
|
7
|
50
|
|
|
|
325
|
$_ = qr/.*/ if $_ eq ':all'; |
217
|
7
|
50
|
|
|
|
12
|
$_ = qr/^_/ if $_ eq ':private'; |
218
|
7
|
50
|
|
|
|
10
|
$_ = qr/^[^_]/ if $_ eq ':public'; |
219
|
7
|
50
|
|
|
|
15
|
die "BUG: $errp: unknown tag in sub_name $_" if /^:/; |
220
|
|
|
|
|
|
|
} |
221
|
|
|
|
|
|
|
|
222
|
7
|
|
|
|
|
7
|
my @s; |
223
|
7
|
|
|
|
|
10
|
for my $sub_name (@$sub_names) { |
224
|
7
|
50
|
|
|
|
11
|
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
|
|
|
|
|
24
|
push @s, $sub_name; |
233
|
|
|
|
|
|
|
} |
234
|
|
|
|
|
|
|
} |
235
|
|
|
|
|
|
|
|
236
|
7
|
100
|
66
|
|
|
35
|
unless (!defined($pspec->{mod_version}) || |
237
|
|
|
|
|
|
|
$pspec->{mod_version} eq ':all') { |
238
|
5
|
50
|
33
|
|
|
18
|
defined($target_version) && length($target_version) |
239
|
|
|
|
|
|
|
or die "FATAL: Target package '$target' does not have ". |
240
|
|
|
|
|
|
|
"\$VERSION"; |
241
|
5
|
|
|
|
|
6
|
my $mod_versions = $pspec->{mod_version}; |
242
|
5
|
100
|
|
|
|
13
|
$mod_versions = ref($mod_versions) eq 'ARRAY' ? |
243
|
|
|
|
|
|
|
[@$mod_versions] : [$mod_versions]; |
244
|
5
|
|
|
|
|
9
|
for (@$mod_versions) { |
245
|
7
|
50
|
|
|
|
11
|
$_ = qr/.*/ if $_ eq ':all'; |
246
|
7
|
50
|
|
|
|
13
|
die "BUG: $errp: unknown tag in mod_version $_" |
247
|
|
|
|
|
|
|
if /^:/; |
248
|
|
|
|
|
|
|
} |
249
|
|
|
|
|
|
|
|
250
|
5
|
100
|
|
|
|
11
|
unless (grep { |
251
|
7
|
50
|
|
|
|
22
|
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
|
|
|
|
58
|
"patching anyway (force)":"skipped"). "."; |
257
|
2
|
100
|
|
|
|
15
|
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
|
|
|
|
|
23
|
$target, $s, $act, $pspec->{code}); |
266
|
|
|
|
|
|
|
} |
267
|
|
|
|
|
|
|
|
268
|
5
|
|
|
|
|
405
|
$i++; |
269
|
|
|
|
|
|
|
} # for $pspec |
270
|
|
|
|
|
|
|
} # for $target |
271
|
4
|
|
|
|
|
6
|
$handles; |
272
|
|
|
|
|
|
|
} |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
1; |
275
|
|
|
|
|
|
|
# ABSTRACT: Patch package with a set of patches |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
__END__ |