1 package B::Hooks::EndOfScope::WithFallback;
8 # note - the tie() fallback will probably work on 5.6 as well,
9 # if you need to go that low - patches passing tests will be accepted
12 my ($v_m_req, $bheos_ver);
14 # Adjust the Makefile.PL if changing this minimum version
17 # FIXME - remove if merged with B::H::EOS
23 require Variable::Magic;
24 Variable::Magic->VERSION($v_m_req);
25 } ? sub () { 1 } : sub () { 0 };
28 # FIXME - remove if merged with B::H::EOS
30 *__HAS_BHEOS = __HAS_VM && eval {
31 require B::Hooks::EndOfScope;
32 B::Hooks::EndOfScope->VERSION ($bheos_ver)
33 } ? sub () { 1 } : sub () { 0 };
36 use Sub::Exporter -setup => {
37 exports => ['on_scope_end'],
38 groups => { default => ['on_scope_end'] },
43 B::Hooks::EndOfScope::WithFallback - B::Hooks::EndOfScope without an XS dependency
51 Just like its twin L<B::Hooks::EndOfScope> this module allows you to execute
52 code when perl finished compiling the surrounding scope. The only difference
53 is that this module will function even without the presence of the XS
54 dependency L<Variable::Magic>. The behavior and API is identical to that of
55 L<B::Hooks::EndOfScope> with the exception of one caveat as listed below.
57 =head1 WHY ANOTHER MODULE
59 While the design of the non-XS implementation is sound and passes every test
60 of the original L<B::Hooks::EndOfScope> distribution, the authors of
61 L<B::Hooks::EndOfScope> are currently not interested in integrating it into
62 their distribution on philosophical grounds.
66 Handling exceptions in scope-end callbacks is tricky business. While
67 L<Variable::Magic> has access to some very dark sorcery to make it possible to
68 throw an exception from within a callback, the pure-perl impleentation does
69 not have access to these hacks. Therefore, what would have been a compile-time
70 exception is instead emulated with output on C<STDERR> and an immediate exit
71 via L<POSIX/_exit>. This can potentially have an impact on your code, since
72 no C<END> blocks, nor C<DESTROY> callbacks will execute.
82 Registers C<$code> to be executed after the surrounding scope has been
85 This is exported by default. See L<Sub::Exporter> on how to customize it.
89 # FIXME - remove if merged with B::H::EOS
90 # already loaded - might as well
92 *on_scope_end = \&B::Hooks::EndOfScope::on_scope_end;
95 # we have V::M - just replicate everything here, do not
96 # even try to load B::H::EOS (may or may not be installed)
97 # the amount of code is minimal anyway, and we save on
98 # the syscalls to find/load B/H/EOS.pm
101 my $wiz = Variable::Magic::wizard (
102 data => sub { [$_[1]] },
103 free => sub { $_->() for @{ $_[1] }; () }
106 # str-eval so it is subnamed correctly
107 eval <<'EOS' or die $@;
109 sub on_scope_end (&) {
114 if (my $stack = Variable::Magic::getdata %^H, $wiz) {
115 push @{ $stack }, $cb;
118 Variable::Magic::cast %^H, $wiz, $cb;
122 1; # for the above `or die()`
127 # str eval for the above reason and so we do not burn cycles defining
128 # packages we will not use
129 eval <<'EOS' or die $@;
134 package B::Hooks::EndOfScope::WithFallback::_TieHintHash;
139 our @ISA = 'Tie::ExtraHash';
142 # Instead of relying on specific destruction order like the V::M
143 # implementation does, use an explicit array with its own destructor
144 # ordering. This is a potential FIXME for the V::M-using code (it may
145 # very well start unwinding the *other* way), but leaving it as-is
146 # for now to match what B::H::EOS does.
148 package B::Hooks::EndOfScope::WithFallback::_ScopeGuardArray;
153 sub new { bless [], ref $_[0] || $_[0] }
157 # keep unwinding the stack until something decides to throw
158 while (@{$_[0]} and $@ eq '') {
159 eval { $_[0]->[0]{code}->(); shift @{$_[0]} };
162 if ( (my $err = $@) ne '') {
163 # argh argh argh - why did you have to throw in a scope-end?!
164 # we can not properly throw during a BEGIN from within pure-perl
165 # (V::M does some very weird XS magic to be able to). However - we
166 # are still compiling - so we can very well just exit() with a long
167 # explanation. Exitting with a normal exit() however won't work, as
168 # it causes perl to segfault (even 5.14), so doing the POSIX thing
170 my $exit_code = $ENV{B_HOOKS_EOS_PP_ON_DIE_EXITCODE};
171 $exit_code = 1 if (! defined $exit_code or ! length $exit_code);
173 print STDERR <<EOE; require POSIX; POSIX::_exit($exit_code);
175 ========================================================================
176 !!! F A T A L E R R O R !!!
178 Exception thrown by scope-end callback
179 ========================================================================
181 B::Hooks::EndOfScope::WithFallback is currently operating in pure-perl
182 fallback mode, because your system is lacking the necessary dependency
183 Variable::Magic $v_m_req
184 In this mode B::Hooks::EndOfScope::WithFallback is unable to accomodate
185 callbacks throwing exception, due to the design of perl itself. Your
186 entire application will terminate immediately using POSIX::_exit (this
187 means nothing else beyond this point will execute, including any END
188 blocks you may have defined. The callback originally defined around
189 $_[0]->[0]{caller} terminated with the following error:
197 sub on_scope_end (&) {
201 if(my $t = tied( %^H ) ) {
202 if ( (my $c = ref $t) ne 'B::Hooks::EndOfScope::WithFallback::_TieHintHash') {
205 ========================================================================
206 !!! F A T A L E R R O R !!!
208 foreign tie() of %^H detected
209 ========================================================================
211 B::Hooks::EndOfScope::WithFallback is currently operating in pure-perl
212 fallback mode, because your system is lacking the necessary dependency
213 Variable::Magic $v_m_req
214 In this mode B::Hooks::EndOfScope::WithFallback expects to be able to tie()
215 the hinthash %^H, however it is apparently already tied by means unknown to
218 Since this is a no-win situation execution will abort here and now. Please
219 try to find out which other module is relying on hinthash tie() ability,
220 and file a bug for both the perpetrator and B::Hooks::EndOfScope::WithFallback
221 so that the authors can figure out an acceptable way of moving forward.
230 'B::Hooks::EndOfScope::WithFallback::_TieHintHash',
231 ($stack = B::Hooks::EndOfScope::WithFallback::_ScopeGuardArray->new),
236 do { @callsite = caller(++$f) }
237 while (@callsite and $callsite[1] =~ /\(eval.+\)/);
241 caller => sprintf '%s line %s', @callsite[1,2]
245 1; # for the above `or die()`
252 L<B::Hooks::EndOfScope>
256 ribasushi: Peter Rabbitson <ribasushi@cpan.org>
264 Copyright (c) 2011 the B::Hooks::EndOfScope::WithFalback L</AUTHOR> and L</CONTRIBUTORS>
269 This library is free software and may be distributed under the same terms