/* --- uplevel() data tokens and global storage ---------------------------- */
+#define SU_UPLEVEL_HIJACKS_RUNOPS SU_HAS_PERL(5, 8, 0)
+
typedef struct {
void *next;
COP *old_curcop;
+#if SU_UPLEVEL_HIJACKS_RUNOPS
runops_proc_t old_runops;
+#endif
bool old_catch;
OP *old_op;
} su_uplevel_ud;
return 0;
}
+#if SU_UPLEVEL_HIJACKS_RUNOPS
+
STATIC int su_uplevel_goto_runops(pTHX) {
#define su_uplevel_goto_runops() su_uplevel_goto_runops(aTHX)
register OP *op;
return 0;
}
+#endif /* SU_UPLEVEL_HIJACKS_RUNOPS */
+
#define su_at_underscore(C) AvARRAY(AvARRAY(CvPADLIST(C))[CvDEPTH(C)])[0]
STATIC void su_uplevel_restore(pTHX_ void *sus_) {
PERL_SI *cur = sud->old_curstackinfo;
PERL_SI *si = sud->si;
+#if SU_UPLEVEL_HIJACKS_RUNOPS
if (PL_runops == su_uplevel_goto_runops)
PL_runops = sud->old_runops;
+#endif
if (sud->callback) {
PERL_CONTEXT *cx = cxstack + sud->cxix;
SU_UPLEVEL_SAVE(op, (OP *) &sub_op);
+#if SU_UPLEVEL_HIJACKS_RUNOPS
sud->old_runops = PL_runops;
+#endif
sud->old_catch = CATCH_GET;
CATCH_SET(TRUE);
}
if (su_uplevel_goto_static(CvROOT(renamed))) {
- if (PL_runops != Perl_runops_standard) {
- if (PL_runops == Perl_runops_debug) {
+#if SU_UPLEVEL_HIJACKS_RUNOPS
+ if (PL_runops != PL_runops_std) {
+ if (PL_runops == PL_runops_dbg) {
if (PL_debug)
croak("uplevel() can't execute code that calls goto when debugging flags are set");
} else if (PL_runops != su_uplevel_goto_runops)
}
PL_runops = su_uplevel_goto_runops;
+#else /* SU_UPLEVEL_HIJACKS_RUNOPS */
+ croak("uplevel() can't execute code that calls goto before perl 5.8");
+#endif /* !SU_UPLEVEL_HIJACKS_RUNOPS */
}
CALLRUNOPS(aTHX);
Some rare oddities may still happen when running inside the debugger.
It may help to use a perl higher than 5.8.9 or 5.10.0, as they contain some context-related fixes.
-Calling C<goto> to replace an L</uplevel>'d code frame does not work when a custom runloop is used or when debugging flags are set with C<perl -D>.
-In those two cases, L</uplevel> will look for a C<goto &sub> statement in its callback and, if there is one, throw an exception before executing the code.
+Calling C<goto> to replace an L</uplevel>'d code frame does not work :
+
+=over 4
+
+=item *
+
+for a C<perl> older than the 5.8 series ;
+
+=item *
+
+for a C<DEBUGGING> C<perl> run with debugging flags set (as in C<perl -D ...>) ;
+
+=item *
+
+when the runloop callback is replaced by another module.
+
+=back
+
+In those three cases, L</uplevel> will look for a C<goto &sub> statement in its callback and, if there is one, throw an exception before executing the code.
Moreover, in order to handle C<goto> statements properly, L</uplevel> currently has to suffer a run-time overhead proportional to the size of the the callback in every case (with a small ratio), and proportional to the size of B<all> the code executed as the result of the L</uplevel> call (including subroutine calls inside the callback) when a C<goto> statement is found in the L</uplevel> callback.
Despite this shortcoming, this XS version of L</uplevel> should still run way faster than the pure-Perl version from L<Sub::Uplevel>.
# goto
-{
+SKIP: {
+ if ("$]" < 5.008) {
+ my $cb = sub { fail 'should not be executed' };
+ local $@;
+ eval { sub { uplevel { goto $cb } HERE }->() };
+ like $@, qr/^uplevel\(\) can't execute code that calls goto before perl 5\.8/,
+ 'goto croaks';
+ skip "goto to an uplevel'd stack frame does not work on perl 5\.6"
+ => ((5 * 4 * 4) * 3 + 1) - 1;
+ }
+
my @args = (
[ [ ], [ 'm' ] ],
[ [ 'a' ], [ ] ],
# goto XS
-{
+SKIP: {
+ skip "goto to an uplevel'd stack frame does not work on perl 5\.6" => 5
+ if "$]" < 5.008;
+
my $desc = 'uplevel() calling goto &uplevel';
local $@;
eval {