Fix all debugger issues from perl 5.8.1 on FC++ (RT#69862)
[p5sagit/namespace-clean.git] / lib / namespace / clean.pm
CommitLineData
40aef9d6 1package namespace::clean;
04312494 2# ABSTRACT: Keep imports and functions out of your namespace
40aef9d6 3
4use warnings;
5use strict;
6
8177960a 7use vars qw( $STORAGE_VAR );
9887772b 8use Package::Stash;
40aef9d6 9
8620b198 10our $VERSION = '0.21';
fa84e425 11
8177960a 12$STORAGE_VAR = '__NAMESPACE_CLEAN_STORAGE';
40aef9d6 13
9887772b 14BEGIN {
1cd61d2e 15
7b026c56 16 use warnings;
17 use strict;
18
1cd61d2e 19 # when changing also change in Makefile.PL
20 my $b_h_eos_req = '0.07';
21
9887772b 22 if (eval {
23 require B::Hooks::EndOfScope;
1cd61d2e 24 B::Hooks::EndOfScope->VERSION($b_h_eos_req);
9887772b 25 1
26 } ) {
27 B::Hooks::EndOfScope->import('on_scope_end');
28 }
29 else {
30 eval <<'PP' or die $@;
31
1cd61d2e 32 use Tie::Hash ();
33
34 {
35 package namespace::clean::_TieHintHash;
36
7b026c56 37 use warnings;
38 use strict;
39
1cd61d2e 40 use base 'Tie::ExtraHash';
41 }
42
9887772b 43 {
44 package namespace::clean::_ScopeGuard;
45
7b026c56 46 use warnings;
47 use strict;
48
9887772b 49 sub arm { bless [ $_[1] ] }
50
51 sub DESTROY { $_[0]->[0]->() }
52 }
53
9887772b 54
55 sub on_scope_end (&) {
56 $^H |= 0x020000;
57
58 if( my $stack = tied( %^H ) ) {
1cd61d2e 59 if ( (my $c = ref $stack) ne 'namespace::clean::_TieHintHash') {
60 die <<EOE;
61========================================================================
62 !!! F A T A L E R R O R !!!
63
64 foreign tie() of %^H detected
65========================================================================
66
67namespace::clean is currently operating in pure-perl fallback mode, because
68your system is lacking the necessary dependency B::Hooks::EndOfScope $b_h_eos_req.
69In this mode namespace::clean expects to be able to tie() the hinthash %^H,
70however it is apparently already tied by means unknown to the tie-class
71$c
72
73Since this is a no-win situation execution will abort here and now. Please
74try to find out which other module is relying on hinthash tie() ability,
75and file a bug for both the perpetrator and namespace::clean, so that the
76authors can figure out an acceptable way of moving forward.
77
78EOE
79 }
9887772b 80 push @$stack, namespace::clean::_ScopeGuard->arm(shift);
81 }
82 else {
1cd61d2e 83 tie( %^H, 'namespace::clean::_TieHintHash', namespace::clean::_ScopeGuard->arm(shift) );
9887772b 84 }
85 }
86
87 1;
88
89PP
90
91 }
92}
93
09e4e594 94=head1 NAME
95
96namespace::clean - Keep imports and functions out of your namespace
97
40aef9d6 98=head1 SYNOPSIS
99
100 package Foo;
101 use warnings;
102 use strict;
103
6c0ece9b 104 use Carp qw(croak); # 'croak' will be removed
40aef9d6 105
6c0ece9b 106 sub bar { 23 } # 'bar' will be removed
40aef9d6 107
6c0ece9b 108 # remove all previously defined functions
40aef9d6 109 use namespace::clean;
110
6c0ece9b 111 sub baz { bar() } # 'baz' still defined, 'bar' still bound
40aef9d6 112
6c0ece9b 113 # begin to collection function names from here again
9b680ffe 114 no namespace::clean;
115
6c0ece9b 116 sub quux { baz() } # 'quux' will be removed
9b680ffe 117
6c0ece9b 118 # remove all functions defined after the 'no' unimport
9b680ffe 119 use namespace::clean;
120
6c0ece9b 121 # Will print: 'No', 'No', 'Yes' and 'No'
40aef9d6 122 print +(__PACKAGE__->can('croak') ? 'Yes' : 'No'), "\n";
123 print +(__PACKAGE__->can('bar') ? 'Yes' : 'No'), "\n";
124 print +(__PACKAGE__->can('baz') ? 'Yes' : 'No'), "\n";
9b680ffe 125 print +(__PACKAGE__->can('quux') ? 'Yes' : 'No'), "\n";
40aef9d6 126
127 1;
128
129=head1 DESCRIPTION
130
de673bbf 131=head2 Keeping packages clean
132
40aef9d6 133When you define a function, or import one, into a Perl package, it will
134naturally also be available as a method. This does not per se cause
135problems, but it can complicate subclassing and, for example, plugin
04312494 136classes that are included via multiple inheritance by loading them as
6c0ece9b 137base classes.
40aef9d6 138
139The C<namespace::clean> pragma will remove all previously declared or
140imported symbols at the end of the current package's compile cycle.
6c0ece9b 141Functions called in the package itself will still be bound by their
142name, but they won't show up as methods on your class or instances.
143
144By unimporting via C<no> you can tell C<namespace::clean> to start
145collecting functions for the next C<use namespace::clean;> specification.
40aef9d6 146
53e92ec5 147You can use the C<-except> flag to tell C<namespace::clean> that you
472d4b1e 148don't want it to remove a certain function or method. A common use would
149be a module exporting an C<import> method along with some functions:
53e92ec5 150
151 use ModuleExportingImport;
152 use namespace::clean -except => [qw( import )];
153
472d4b1e 154If you just want to C<-except> a single sub, you can pass it directly.
155For more than one value you have to use an array reference.
156
271df965 157=head2 Explicitly removing functions when your scope is compiled
de673bbf 158
271df965 159It is also possible to explicitly tell C<namespace::clean> what packages
de673bbf 160to remove when the surrounding scope has finished compiling. Here is an
161example:
162
163 package Foo;
164 use strict;
165
166 # blessed NOT available
167
168 sub my_class {
169 use Scalar::Util qw( blessed );
170 use namespace::clean qw( blessed );
171
172 # blessed available
173 return blessed shift;
174 }
175
176 # blessed NOT available
177
1a1be5dc 178=head2 Moose
179
180When using C<namespace::clean> together with L<Moose> you want to keep
181the installed C<meta> method. So your classes should look like:
182
183 package Foo;
184 use Moose;
185 use namespace::clean -except => 'meta';
186 ...
187
188Same goes for L<Moose::Role>.
189
fcfe7810 190=head2 Cleaning other packages
191
192You can tell C<namespace::clean> that you want to clean up another package
193instead of the one importing. To do this you have to pass in the C<-cleanee>
194option like this:
195
196 package My::MooseX::namespace::clean;
197 use strict;
198
199 use namespace::clean (); # no cleanup, just load
200
201 sub import {
202 namespace::clean->import(
203 -cleanee => scalar(caller),
204 -except => 'meta',
205 );
206 }
207
208If you don't care about C<namespace::clean>s discover-and-C<-except> logic, and
209just want to remove subroutines, try L</clean_subroutines>.
210
fa84e425 211=head1 METHODS
212
213=head2 clean_subroutines
40aef9d6 214
fcfe7810 215This exposes the actual subroutine-removal logic.
216
217 namespace::clean->clean_subroutines($cleanee, qw( subA subB ));
218
219will remove C<subA> and C<subB> from C<$cleanee>. Note that this will remove the
220subroutines B<immediately> and not wait for scope end. If you want to have this
221effect at a specific time (e.g. C<namespace::clean> acts on scope compile end)
222it is your responsibility to make sure it runs at that time.
40aef9d6 223
224=cut
225
80e4e267 226# Constant to optimise away the unused code branches
227use constant RENAME_SUB => $] > 5.008_008_9 && $] < 5.013_006_1;
228{ no strict; delete ${__PACKAGE__."::"}{RENAME_SUB} }
229
230# In perl 5.8.9-5.12, it assumes that sub_fullname($sub) can
231# always be used to find the CV again.
232# In perl 5.8.8 and 5.14, it assumes that the name of the glob
233# passed to entersub can be used to find the CV.
234# since we are deleting the glob where the subroutine was originally
235# defined, those assumptions no longer hold.
236#
237# So in 5.8.9-5.12 we need to move it elsewhere and point the
238# CV's name to the new glob.
239#
240# In 5.8.8 and 5.14 we move it elsewhere and rename the
241# original glob by assigning the new glob back to it.
017bd598 242my $sub_utils_loaded;
80e4e267 243my $DebuggerFixup = sub {
017bd598 244 my ($f, $sub, $cleanee_stash, $deleted_stash) = @_;
245
80e4e267 246 if (RENAME_SUB) {
247 if (! defined $sub_utils_loaded ) {
248 $sub_utils_loaded = do {
249 my $sn_ver = 0.04;
250 eval { require Sub::Name; Sub::Name->VERSION($sn_ver) }
251 or die "Sub::Name $sn_ver required when running under -d or equivalent: $@";
017bd598 252
80e4e267 253 my $si_ver = 0.04;
254 eval { require Sub::Identify; Sub::Identify->VERSION($si_ver) }
255 or die "Sub::Identify $si_ver required when running under -d or equivalent: $@";
fcfe7810 256
80e4e267 257 1;
258 } ? 1 : 0;
259 }
017bd598 260
80e4e267 261 if ( Sub::Identify::sub_fullname($sub) eq ($cleanee_stash->name . "::$f") ) {
262 my $new_fq = $deleted_stash->name . "::$f";
263 Sub::Name::subname($new_fq, $sub);
264 $deleted_stash->add_symbol("&$f", $sub);
265 }
266 }
267 else {
017bd598 268 $deleted_stash->add_symbol("&$f", $sub);
269 }
270};
271
272my $RemoveSubs = sub {
de673bbf 273 my $cleanee = shift;
274 my $store = shift;
16d8eca5 275 my $cleanee_stash = Package::Stash->new($cleanee);
017bd598 276 my $deleted_stash;
277
de673bbf 278 SYMBOL:
279 for my $f (@_) {
017bd598 280
de673bbf 281 # ignore already removed symbols
282 next SYMBOL if $store->{exclude}{ $f };
de673bbf 283
017bd598 284 my $sub = $cleanee_stash->get_symbol("&$f")
285 or next SYMBOL;
226432f6 286
80e4e267 287 my $need_debugger_fixup =
288 $^P
289 &&
290 ref(my $globref = \$cleanee_stash->namespace->{$f}) eq 'GLOB'
291 ;
292
293 if ($need_debugger_fixup) {
294 # convince the Perl debugger to work
295 # see the comment on top of $DebuggerFixup
296 $DebuggerFixup->(
297 $f,
298 $sub,
299 $cleanee_stash,
300 $deleted_stash ||= Package::Stash->new("namespace::clean::deleted::$cleanee"),
301 );
de673bbf 302 }
d6aecfbf 303
d64ad6f8 304 my @symbols = map {
305 my $name = $_ . $f;
306 my $def = $cleanee_stash->get_symbol($name);
307 defined($def) ? [$name, $def] : ()
ad4b1a60 308 } '$', '@', '%', '';
d64ad6f8 309
c86d6ae2 310 $cleanee_stash->remove_glob($f);
d64ad6f8 311
80e4e267 312 # if this perl needs no renaming trick we need to
313 # rename the original glob after the fact
314 # (see commend of $DebuggerFixup
315 if (!RENAME_SUB && $need_debugger_fixup) {
316 *$globref = $deleted_stash->namespace->{$f};
317 }
318
d64ad6f8 319 $cleanee_stash->add_symbol(@$_) for @symbols;
de673bbf 320 }
321};
53e92ec5 322
fcfe7810 323sub clean_subroutines {
324 my ($nc, $cleanee, @subs) = @_;
325 $RemoveSubs->($cleanee, {}, @subs);
326}
327
fa84e425 328=head2 import
fcfe7810 329
330Makes a snapshot of the current defined functions and installs a
331L<B::Hooks::EndOfScope> hook in the current scope to invoke the cleanups.
332
333=cut
334
de673bbf 335sub import {
336 my ($pragma, @args) = @_;
53e92ec5 337
de673bbf 338 my (%args, $is_explicit);
fcfe7810 339
340 ARG:
341 while (@args) {
342
343 if ($args[0] =~ /^\-/) {
344 my $key = shift @args;
345 my $value = shift @args;
346 $args{ $key } = $value;
347 }
348 else {
349 $is_explicit++;
350 last ARG;
351 }
9b680ffe 352 }
353
fcfe7810 354 my $cleanee = exists $args{ -cleanee } ? $args{ -cleanee } : scalar caller;
de673bbf 355 if ($is_explicit) {
aa2aafae 356 on_scope_end {
de673bbf 357 $RemoveSubs->($cleanee, {}, @args);
aa2aafae 358 };
9b680ffe 359 }
de673bbf 360 else {
361
362 # calling class, all current functions and our storage
363 my $functions = $pragma->get_functions($cleanee);
364 my $store = $pragma->get_class_store($cleanee);
16d8eca5 365 my $stash = Package::Stash->new($cleanee);
de673bbf 366
367 # except parameter can be array ref or single value
368 my %except = map {( $_ => 1 )} (
369 $args{ -except }
370 ? ( ref $args{ -except } eq 'ARRAY' ? @{ $args{ -except } } : $args{ -except } )
371 : ()
372 );
373
374 # register symbols for removal, if they have a CODE entry
375 for my $f (keys %$functions) {
376 next if $except{ $f };
c86d6ae2 377 next unless $stash->has_symbol("&$f");
de673bbf 378 $store->{remove}{ $f } = 1;
379 }
380
381 # register EOF handler on first call to import
382 unless ($store->{handler_is_installed}) {
aa2aafae 383 on_scope_end {
de673bbf 384 $RemoveSubs->($cleanee, $store, keys %{ $store->{remove} });
aa2aafae 385 };
de673bbf 386 $store->{handler_is_installed} = 1;
387 }
388
389 return 1;
390 }
9b680ffe 391}
392
fa84e425 393=head2 unimport
9b680ffe 394
395This method will be called when you do a
396
397 no namespace::clean;
398
399It will start a new section of code that defines functions to clean up.
400
401=cut
402
403sub unimport {
fcfe7810 404 my ($pragma, %args) = @_;
9b680ffe 405
6c0ece9b 406 # the calling class, the current functions and our storage
fcfe7810 407 my $cleanee = exists $args{ -cleanee } ? $args{ -cleanee } : scalar caller;
9b680ffe 408 my $functions = $pragma->get_functions($cleanee);
409 my $store = $pragma->get_class_store($cleanee);
410
6c0ece9b 411 # register all unknown previous functions as excluded
9b680ffe 412 for my $f (keys %$functions) {
413 next if $store->{remove}{ $f }
414 or $store->{exclude}{ $f };
415 $store->{exclude}{ $f } = 1;
416 }
417
418 return 1;
419}
420
fa84e425 421=head2 get_class_store
9b680ffe 422
04312494 423This returns a reference to a hash in a passed package containing
6c0ece9b 424information about function names included and excluded from removal.
9b680ffe 425
426=cut
427
428sub get_class_store {
429 my ($pragma, $class) = @_;
16d8eca5 430 my $stash = Package::Stash->new($class);
5460fcfb 431 my $var = "%$STORAGE_VAR";
c86d6ae2 432 $stash->add_symbol($var, {})
433 unless $stash->has_symbol($var);
434 return $stash->get_symbol($var);
40aef9d6 435}
436
fa84e425 437=head2 get_functions
40aef9d6 438
439Takes a class as argument and returns all currently defined functions
440in it as a hash reference with the function name as key and a typeglob
441reference to the symbol as value.
442
443=cut
444
445sub get_functions {
446 my ($pragma, $class) = @_;
447
16d8eca5 448 my $stash = Package::Stash->new($class);
40aef9d6 449 return {
c86d6ae2 450 map { $_ => $stash->get_symbol("&$_") }
451 $stash->list_all_symbols('CODE')
40aef9d6 452 };
453}
454
6c0ece9b 455=head1 IMPLEMENTATION DETAILS
456
04312494 457This module works through the effect that a
6c0ece9b 458
459 delete $SomePackage::{foo};
460
461will remove the C<foo> symbol from C<$SomePackage> for run time lookups
462(e.g., method calls) but will leave the entry alive to be called by
472d4b1e 463already resolved names in the package itself. C<namespace::clean> will
464restore and therefor in effect keep all glob slots that aren't C<CODE>.
6c0ece9b 465
466A test file has been added to the perl core to ensure that this behaviour
467will be stable in future releases.
468
469Just for completeness sake, if you want to remove the symbol completely,
470use C<undef> instead.
471
9887772b 472=head1 CAVEATS
473
474This module is fully functional in a pure-perl environment, where
c8e3fb7e 475L<B::Hooks::EndOfScope> (with the XS dependency L<Variable::Magic>), may
476not be available. However in this case this module falls back to a
9887772b 477L<tie()|perlfunc/tie> of L<%^H|perlvar/%^H> which may or may not interfere
478with some crack you may be doing independently of namespace::clean.
479
c8e3fb7e 480If you want to ensure that your codebase is protected from this unlikely
481clash, you need to explicitly depend on L<B::Hooks::EndOfScope>.
482
40aef9d6 483=head1 SEE ALSO
484
705fe1b1 485L<B::Hooks::EndOfScope>
40aef9d6 486
04312494 487=head1 THANKS
40aef9d6 488
04312494 489Many thanks to Matt S Trout for the inspiration on the whole idea.
40aef9d6 490
fa84e425 491=head1 AUTHORS
492
493=over
494
495=item *
496
497Robert 'phaylon' Sedlacek <rs@474.at>
498
499=item *
500
501Florian Ragwitz <rafl@debian.org>
502
503=item *
504
505Jesse Luehrs <doy@tozt.net>
506
507=item *
508
509Peter Rabbitson <ribasushi@cpan.org>
510
511=back
512
513=head1 COPYRIGHT AND LICENSE
514
515This software is copyright (c) 2011 by Robert 'phaylon' Sedlacek.
516
517This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
518
40aef9d6 519=cut
520
de673bbf 521no warnings;
522'Danger! Laws of Thermodynamics may not apply.'