File Coverage

blib/lib/namespace/clean.pm
Criterion Covered Total %
statement 60 60 100.0
branch 20 22 90.9
condition 2 3 66.6
subroutine 11 11 100.0
pod 3 3 100.0
total 96 99 96.9


line stmt bran cond sub pod time code
1             package namespace::clean;
2              
3 11     11   135036 use warnings;
  11         26  
  11         348  
4 11     11   56 use strict;
  11         20  
  11         526  
5              
6             our $VERSION = '0.25_01';
7             our $STORAGE_VAR = '__NAMESPACE_CLEAN_STORAGE';
8              
9 11     11   8539 use B::Hooks::EndOfScope 'on_scope_end';
  11         129674  
  11         78  
10 11     11   6711 use namespace::clean::_Util qw( stash_for DEBUGGER_NEEDS_CV_RENAME DEBUGGER_NEEDS_CV_PIVOT );
  11         29  
  11         11594  
11              
12             # Built-in debugger CV-retrieval fixups necessary before perl 5.15.5:
13             # since we are deleting the glob where the subroutine was originally
14             # defined, the assumptions below no longer hold.
15             #
16             # In 5.8.9 ~ 5.13.5 (inclusive) the debugger assumes that a CV can
17             # always be found under sub_fullname($sub)
18             # Workaround: use sub naming to properly name the sub hidden in the package's
19             # deleted-stash
20             #
21             # In the rest of the range ( ... ~ 5.8.8 and 5.13.6 ~ 5.15.4 ) the debugger
22             # assumes the name of the glob passed to entersub can be used to find the CV
23             # Workaround: realias the original glob to the deleted-stash slot
24             #
25             # Can not tie constants to the current value of $^P directly,
26             # as the debugger can be enabled during runtime (kinda dubious)
27             #
28              
29             my $RemoveSubs = sub {
30             my $cleanee = shift;
31             my $store = shift;
32             my $cleanee_stash = stash_for($cleanee);
33             my $deleted_stash;
34              
35             SYMBOL:
36             for my $f (@_) {
37              
38             # ignore already removed symbols
39             next SYMBOL if $store->{exclude}{ $f };
40              
41             my $sub = $cleanee_stash->get_symbol("&$f")
42             or next SYMBOL;
43              
44             my $need_debugger_fixup =
45             ( DEBUGGER_NEEDS_CV_RENAME or DEBUGGER_NEEDS_CV_PIVOT )
46             &&
47             $^P
48             &&
49             ref(my $globref = \$cleanee_stash->namespace->{$f}) eq 'GLOB'
50             &&
51             ( $deleted_stash ||= stash_for("namespace::clean::deleted::$cleanee") )
52             ;
53              
54             # convince the Perl debugger to work
55             # see the comment on top
56             if ( DEBUGGER_NEEDS_CV_RENAME and $need_debugger_fixup ) {
57             namespace::clean::_Util::get_subname( $sub ) eq ( $cleanee_stash->name . "::$f" )
58             and
59             $deleted_stash->add_symbol(
60             "&$f",
61             namespace::clean::_Util::set_subname( $deleted_stash->name . "::$f", $sub ),
62             );
63             }
64             elsif ( DEBUGGER_NEEDS_CV_PIVOT and $need_debugger_fixup ) {
65             $deleted_stash->add_symbol("&$f", $sub);
66             }
67              
68             my @symbols = map {
69             my $name = $_ . $f;
70             my $def = $cleanee_stash->get_symbol($name);
71             defined($def) ? [$name, $def] : ()
72             } '$', '@', '%', '';
73              
74             $cleanee_stash->remove_glob($f);
75              
76             # if this perl needs no renaming trick we need to
77             # rename the original glob after the fact
78             DEBUGGER_NEEDS_CV_PIVOT
79             and
80             $need_debugger_fixup
81             and
82             *$globref = $deleted_stash->namespace->{$f};
83              
84             $cleanee_stash->add_symbol(@$_) for @symbols;
85             }
86             };
87              
88             sub clean_subroutines {
89 1     1 1 501 my ($nc, $cleanee, @subs) = @_;
90 1         3 $RemoveSubs->($cleanee, {}, @subs);
91             }
92              
93             sub import {
94 1016     1016   859765 my ($pragma, @args) = @_;
95              
96 1016         1179 my (%args, $is_explicit);
97              
98             ARG:
99 1016         2337 while (@args) {
100              
101 2007 100       5462 if ($args[0] =~ /^\-/) {
102 1006         1434 my $key = shift @args;
103 1006         1320 my $value = shift @args;
104 1006         3300 $args{ $key } = $value;
105             }
106             else {
107 1001         1209 $is_explicit++;
108 1001         1446 last ARG;
109             }
110             }
111              
112 1016 100       2820 my $cleanee = exists $args{ -cleanee } ? $args{ -cleanee } : scalar caller;
113 1016 100       1591 if ($is_explicit) {
114             on_scope_end {
115 1001     1001   33856 $RemoveSubs->($cleanee, {}, @args);
116 1001         4778 };
117             }
118             else {
119              
120             # calling class, all current functions and our storage
121 15         133 my $functions = $pragma->get_functions($cleanee);
122 15         70 my $store = $pragma->get_class_store($cleanee);
123 15         426 my $stash = stash_for($cleanee);
124              
125             # except parameter can be array ref or single value
126 5         17 my %except = map {( $_ => 1 )} (
127             $args{ -except }
128 2         5 ? ( ref $args{ -except } eq 'ARRAY' ? @{ $args{ -except } } : $args{ -except } )
129 15 100       127 : ()
    100          
130             );
131              
132             # register symbols for removal, if they have a CODE entry
133 15         77 for my $f (keys %$functions) {
134 77 100       164 next if $except{ $f };
135 72 50       421 next unless $stash->has_symbol("&$f");
136 72         166 $store->{remove}{ $f } = 1;
137             }
138              
139             # register EOF handler on first call to import
140 15 100       51 unless ($store->{handler_is_installed}) {
141             on_scope_end {
142 14     14   5999 $RemoveSubs->($cleanee, $store, keys %{ $store->{remove} });
  14         74  
143 14         93 };
144 14         201 $store->{handler_is_installed} = 1;
145             }
146              
147 15         712 return 1;
148             }
149             }
150              
151             sub unimport {
152 1     1   7 my ($pragma, %args) = @_;
153              
154             # the calling class, the current functions and our storage
155 1 50       3 my $cleanee = exists $args{ -cleanee } ? $args{ -cleanee } : scalar caller;
156 1         4 my $functions = $pragma->get_functions($cleanee);
157 1         4 my $store = $pragma->get_class_store($cleanee);
158              
159             # register all unknown previous functions as excluded
160 1         4 for my $f (keys %$functions) {
161             next if $store->{remove}{ $f }
162 2 100 66     9 or $store->{exclude}{ $f };
163 1         3 $store->{exclude}{ $f } = 1;
164             }
165              
166 1         34 return 1;
167             }
168              
169             sub get_class_store {
170 16     16 1 32 my ($pragma, $class) = @_;
171 16         440 my $stash = stash_for($class);
172 16         75 my $var = "%$STORAGE_VAR";
173 16 100       222 $stash->add_symbol($var, {})
174             unless $stash->has_symbol($var);
175 16         115 return $stash->get_symbol($var);
176             }
177              
178             sub get_functions {
179 16     16 1 33 my ($pragma, $class) = @_;
180              
181 16         522 my $stash = stash_for($class);
182             return {
183 16         348 map { $_ => $stash->get_symbol("&$_") }
  79         601  
184             $stash->list_all_symbols('CODE')
185             };
186             }
187              
188             'Danger! Laws of Thermodynamics may not apply.'
189              
190             __END__