Perl_sv_add_backref(aTHX_ CvSTASH(proto), MUTABLE_SV(cv));
#endif
- OP_REFCNT_LOCK;
- CvROOT(cv) = OpREFCNT_inc(CvROOT(proto));
- OP_REFCNT_UNLOCK;
- CvSTART(cv) = CvSTART(proto);
+ if (CvISXSUB(proto)) {
+ CvXSUB(cv) = CvXSUB(proto);
+ CvXSUBANY(cv) = CvXSUBANY(proto);
+ } else {
+ OP_REFCNT_LOCK;
+ CvROOT(cv) = OpREFCNT_inc(CvROOT(proto));
+ OP_REFCNT_UNLOCK;
+ CvSTART(cv) = CvSTART(proto);
+ }
CvOUTSIDE(cv) = CvOUTSIDE(proto);
#ifdef CVf_WEAKOUTSIDE
if (!(CvFLAGS(proto) & CVf_WEAKOUTSIDE))
if ((PL_op = PL_ppaddr[OP_ENTERSUB](aTHX))) {
PERL_CONTEXT *sub_cx = cxstack + cxstack_ix;
+ /* If pp_entersub() returns a non-null OP, it means that the callback is not
+ * an XSUB. */
+
sud->callback = MUTABLE_CV(SvREFCNT_inc(callback));
CvDEPTH(callback)++;
SAVEDESTRUCTOR_X(su_uplevel_goto_handler, sud);
CALLRUNOPS(aTHX);
-
- ret = PL_stack_sp - (PL_stack_base + new_mark);
}
sud->died = 0;
- SPAGAIN;
-
+ ret = PL_stack_sp - (PL_stack_base + new_mark);
if (ret > 0) {
AV *old_stack = sud->old_curstackinfo->si_stack;
AvFILLp(old_stack) += ret;
}
- PUTBACK;
-
LEAVE;
return ret;
use strict;
use warnings;
-use Test::More tests => (1 * 3 + 2 * 4 + 3 * 5) * 2 + 7 + 5 + 6 + 5 + 6 + 5;
+use Test::More
+ tests => (1 * 3 + 2 * 4 + 3 * 5) * 2 + 7 + 7 + (5 + 6 + 5 + 6 + 5);
use Scope::Upper qw<uplevel HERE UP TOP>;
like $@, qr/^Can't uplevel to an eval frame/, "$desc: dies";
}
+# XS
+
+{
+ my $desc = 'uplevel to XS 1';
+ local $@;
+ eval {
+ sub {
+ my $cxt = HERE;
+ pass "$desc: before";
+ &uplevel(\&HERE => $cxt);
+ is HERE, $cxt, "$desc: after";
+ }->();
+ };
+ is $@, '', "$desc: no error";
+}
+
+{
+ my $desc = 'uplevel to XS 1';
+ local $@;
+ eval {
+ sub {
+ my $up = HERE;
+ sub {
+ is UP, $up, "$desc: before";
+ &uplevel(\&HERE => $up);
+ isnt HERE, $up, "$desc: after 1";
+ }->();
+ is HERE, $up, "$desc: after 2";
+ }->();
+ };
+ is $@, '', "$desc: no error";
+}
+
# Target destruction
{
use strict;
use warnings;
-use Test::More tests => 9 + 4 * 7 + 3 + ((5 * 4 * 4) * 3 + 1) + 2 + 6;
+use Test::More tests => 9 + 4 * 7 + 3 + ((5 * 4 * 4) * 3 + 1) + 3 + 2 + 6;
-use Scope::Upper qw<uplevel HERE>;
+use Scope::Upper qw<uplevel HERE UP>;
# Basic
}->('dummy');
}
+# uplevel() to uplevel()
+
+{
+ my $desc = '\&uplevel as the uplevel() callback';
+ local $@;
+ eval {
+ sub {
+ my $cxt = HERE;
+ sub {
+ sub {
+ # Note that an XS call does not need a context, so after the first uplevel
+ # call UP will point to the scope above the first target.
+ uplevel(\&uplevel => (sub {
+ is "@_", '1 2 3', "$desc: arguments inisde";
+ is HERE, $cxt, "$desc: context inside";
+ } => 1 .. 3 => UP) => UP);
+ }->(10 .. 19);
+ }->(sub { die 'wut' } => HERE);
+ }->('dummy');
+ };
+ is $@, '', "$desc: no error";
+}
+
# Magic
{
use strict;
use warnings;
-use Test::More tests => (13 + 5 + 4) * 2 + 1 + (3 + 3 + 1) + 2 + 11;
+use Test::More tests => (13 + 5 + 4) * 2 + 1 + (3 + 3 + 1) + 2 + 4 + 11;
use Scope::Upper qw<uplevel HERE UP>;
}
}
+# uplevel() to uplevel()
+
+{
+ my $desc = '\&uplevel as the uplevel() callback';
+ local $@;
+ eval {
+ my @ret = sub {
+ my $cxt = HERE;
+ my @ret = sub {
+ my @ret = sub {
+ # Note that an XS call does not need a context, so after the first uplevel
+ # call UP will point to the scope above the first target.
+ 'a', uplevel(\&uplevel => (sub {
+ return qw<x y z>;
+ } => UP) => UP), 'b';
+ }->();
+ is "@ret", 'a x y z b', "$desc: returned from uplevel";
+ return qw<u v w>;
+ }->();
+ is "@ret", 'u v w', "$desc: returned from the first target";
+ return qw<m n>;
+ }->();
+ is "@ret", 'm n', "$desc: returned from the second target";
+ };
+ is $@, '', "$desc: no error";
+}
+
# Magic
{
use strict;
use warnings;
-use Test::More tests => 3 + (3 + 4 + 4) + (3 + 4 + 4) + 5 + 3*3 + (4 + 7);
+use Test::More tests => 3 + (3 + 4 + 4) + (3 + 4 + 4) + 5 + 3*3 + (4 + 7) + 1;
use Scope::Upper qw<uplevel HERE SUB CALLER>;
is $@, '', "$desc: no exception outside";
check_depth \&clash, 0, "$desc: depth at the beginning";
}
+
+# XS
+
+{
+ my $desc = 'exception thrown from XS';
+ local $@;
+ eval {
+ sub {
+ &uplevel(\&uplevel => \1, HERE);
+ }->();
+ };
+ my $line = __LINE__-2; # The error happens at the target frame.
+ like $@,
+ qr/^First argument to uplevel must be a code reference at \Q$0\E line $line/,
+ "$desc: correct error";
+}