File Coverage

blib/lib/namespace/clean.pm
Criterion Covered Total %
statement 66 68 97.0
branch 22 26 84.6
condition 2 3 66.6
subroutine 13 13 100.0
pod 3 4 75.0
total 106 114 92.9


line stmt bran cond sub pod time code
1             package namespace::clean;
2              
3 11     11   132519 use warnings;
  11         26  
  11         359  
4 11     11   61 use strict;
  11         19  
  11         570  
5              
6             our $VERSION = '0.25_02';
7             our $STORAGE_VAR = '__NAMESPACE_CLEAN_STORAGE';
8              
9 11     11   8164 use B::Hooks::EndOfScope 'on_scope_end';
  11         157482  
  11         84  
10              
11             # FIXME This is a crock of shit, needs to go away
12             # currently here to work around https://rt.cpan.org/Ticket/Display.html?id=74151
13             # kill with fire when PS::XS is *finally* fixed
14             BEGIN {
15 11     11   1753 my $provider;
16              
17 11 50       56 if ( $] < 5.008007 ) {
18 0         0 require Package::Stash::PP;
19 0         0 $provider = 'Package::Stash::PP';
20             }
21             else {
22 11         7914 require Package::Stash;
23 11         22977 $provider = 'Package::Stash';
24             }
25 11 50   1063 0 941 eval <<"EOS" or die $@;
  1063         10688  
26              
27             sub stash_for (\$) {
28             $provider->new(\$_[0]);
29             }
30              
31             1;
32              
33             EOS
34             }
35              
36 11     11   6233 use namespace::clean::_Util qw( DEBUGGER_NEEDS_CV_RENAME DEBUGGER_NEEDS_CV_PIVOT );
  11         29  
  11         12344  
37              
38             # Built-in debugger CV-retrieval fixups necessary before perl 5.15.5:
39             # since we are deleting the glob where the subroutine was originally
40             # defined, the assumptions below no longer hold.
41             #
42             # In 5.8.9 ~ 5.13.5 (inclusive) the debugger assumes that a CV can
43             # always be found under sub_fullname($sub)
44             # Workaround: use sub naming to properly name the sub hidden in the package's
45             # deleted-stash
46             #
47             # In the rest of the range ( ... ~ 5.8.8 and 5.13.6 ~ 5.15.4 ) the debugger
48             # assumes the name of the glob passed to entersub can be used to find the CV
49             # Workaround: realias the original glob to the deleted-stash slot
50             #
51             # Can not tie constants to the current value of $^P directly,
52             # as the debugger can be enabled during runtime (kinda dubious)
53             #
54              
55             my $RemoveSubs = sub {
56             my $cleanee = shift;
57             my $store = shift;
58             my $cleanee_stash = stash_for($cleanee);
59             my $deleted_stash;
60              
61             SYMBOL:
62             for my $f (@_) {
63              
64             # ignore already removed symbols
65             next SYMBOL if $store->{exclude}{ $f };
66              
67             my $sub = $cleanee_stash->get_symbol("&$f")
68             or next SYMBOL;
69              
70             my $need_debugger_fixup =
71             ( DEBUGGER_NEEDS_CV_RENAME or DEBUGGER_NEEDS_CV_PIVOT )
72             &&
73             $^P
74             &&
75             ref(my $globref = \$cleanee_stash->namespace->{$f}) eq 'GLOB'
76             &&
77             ( $deleted_stash ||= stash_for("namespace::clean::deleted::$cleanee") )
78             ;
79              
80             # convince the Perl debugger to work
81             # see the comment on top
82             if ( DEBUGGER_NEEDS_CV_RENAME and $need_debugger_fixup ) {
83             namespace::clean::_Util::get_subname( $sub ) eq ( $cleanee_stash->name . "::$f" )
84             and
85             $deleted_stash->add_symbol(
86             "&$f",
87             namespace::clean::_Util::set_subname( $deleted_stash->name . "::$f", $sub ),
88             );
89             }
90             elsif ( DEBUGGER_NEEDS_CV_PIVOT and $need_debugger_fixup ) {
91             $deleted_stash->add_symbol("&$f", $sub);
92             }
93              
94             my @symbols = map {
95             my $name = $_ . $f;
96             my $def = $cleanee_stash->get_symbol($name);
97             defined($def) ? [$name, $def] : ()
98             } '$', '@', '%', '';
99              
100             $cleanee_stash->remove_glob($f);
101              
102             # if this perl needs no renaming trick we need to
103             # rename the original glob after the fact
104             DEBUGGER_NEEDS_CV_PIVOT
105             and
106             $need_debugger_fixup
107             and
108             *$globref = $deleted_stash->namespace->{$f};
109              
110             $cleanee_stash->add_symbol(@$_) for @symbols;
111             }
112             };
113              
114             sub clean_subroutines {
115 1     1 1 523 my ($nc, $cleanee, @subs) = @_;
116 1         4 $RemoveSubs->($cleanee, {}, @subs);
117             }
118              
119             sub import {
120 1016     1016   1163747 my ($pragma, @args) = @_;
121              
122 1016         1334 my (%args, $is_explicit);
123              
124             ARG:
125 1016         2749 while (@args) {
126              
127 2007 100       5942 if ($args[0] =~ /^\-/) {
128 1006         1308 my $key = shift @args;
129 1006         1510 my $value = shift @args;
130 1006         3610 $args{ $key } = $value;
131             }
132             else {
133 1001         1378 $is_explicit++;
134 1001         1562 last ARG;
135             }
136             }
137              
138 1016 100       3290 my $cleanee = exists $args{ -cleanee } ? $args{ -cleanee } : scalar caller;
139 1016 100       1807 if ($is_explicit) {
140             on_scope_end {
141 1001     1001   38451 $RemoveSubs->($cleanee, {}, @args);
142 1001         5377 };
143             }
144             else {
145              
146             # calling class, all current functions and our storage
147 15         48 my $functions = $pragma->get_functions($cleanee);
148 15         107 my $store = $pragma->get_class_store($cleanee);
149 15         373 my $stash = stash_for($cleanee);
150              
151             # except parameter can be array ref or single value
152 5         15 my %except = map {( $_ => 1 )} (
153             $args{ -except }
154 2         5 ? ( ref $args{ -except } eq 'ARRAY' ? @{ $args{ -except } } : $args{ -except } )
155 15 100       139 : ()
    100          
156             );
157              
158             # register symbols for removal, if they have a CODE entry
159 15         71 for my $f (keys %$functions) {
160 77 100       155 next if $except{ $f };
161 72 50       317 next unless $stash->has_symbol("&$f");
162 72         160 $store->{remove}{ $f } = 1;
163             }
164              
165             # register EOF handler on first call to import
166 15 100       56 unless ($store->{handler_is_installed}) {
167             on_scope_end {
168 14     14   6847 $RemoveSubs->($cleanee, $store, keys %{ $store->{remove} });
  14         76  
169 14         95 };
170 14         198 $store->{handler_is_installed} = 1;
171             }
172              
173 15         634 return 1;
174             }
175             }
176              
177             sub unimport {
178 1     1   6 my ($pragma, %args) = @_;
179              
180             # the calling class, the current functions and our storage
181 1 50       5 my $cleanee = exists $args{ -cleanee } ? $args{ -cleanee } : scalar caller;
182 1         2 my $functions = $pragma->get_functions($cleanee);
183 1         4 my $store = $pragma->get_class_store($cleanee);
184              
185             # register all unknown previous functions as excluded
186 1         4 for my $f (keys %$functions) {
187             next if $store->{remove}{ $f }
188 2 100 66     10 or $store->{exclude}{ $f };
189 1         3 $store->{exclude}{ $f } = 1;
190             }
191              
192 1         35 return 1;
193             }
194              
195             sub get_class_store {
196 16     16 1 36 my ($pragma, $class) = @_;
197 16         395 my $stash = stash_for($class);
198 16         76 my $var = "%$STORAGE_VAR";
199 16 100       212 $stash->add_symbol($var, {})
200             unless $stash->has_symbol($var);
201 16         113 return $stash->get_symbol($var);
202             }
203              
204             sub get_functions {
205 16     16 1 32 my ($pragma, $class) = @_;
206              
207 16         459 my $stash = stash_for($class);
208             return {
209 16         361 map { $_ => $stash->get_symbol("&$_") }
  79         489  
210             $stash->list_all_symbols('CODE')
211             };
212             }
213              
214             'Danger! Laws of Thermodynamics may not apply.'
215              
216             __END__