diff options
author | David Mitchell <davem@iabyn.com> | 2014-09-19 16:07:08 +0100 |
---|---|---|
committer | David Mitchell <davem@iabyn.com> | 2014-09-19 16:33:08 +0100 |
commit | 3a23d767cf63fb2e8de3d79eadee49bbc33eec53 (patch) | |
tree | 10ae7ccb7acf9c4cd2d3e0c16bb610a3555c2446 | |
parent | 1da7b04307a09e4931d85cc5a89e85f653429783 (diff) | |
download | perl-3a23d767cf63fb2e8de3d79eadee49bbc33eec53.tar.gz |
fix B::PADOP->sv and ->gv
PADOP structs, which are only used on threaded builds, have an op_padix
field rather than an op_sv or op_gv.
The B::PADOP sv and gv methods would do PL_curpad[o->op_padix] to
look up the value, That is completely wrong. PL_curpad is the pad of the
caller of B::PADOP::sv/gv, not the pad of the sub containing the PADOP op.
This fault appears to to go back to 1999, when PADOP was first added.
It's probably never been spotted because:
a) PADOP only ever used for creating GV ops on threaded builds (so
->sv is probably never called),
b) it has a check where if the thing it retrieved from the pad isn't a GV,
it returns NULL instead.
Fix this by always returning NULL. This is comparable with B::SVOP_>sv,
which always returns op_sv, which on threaded builds always happens to be
NULL. Note that B::SVOP->sv expects the caller to retrieve op_targ and do
the pad lookup.
NB just to avoid confusion (I was certainly confused), these ops are
implemented with the types shown:
unthreaded:
const: B::SVOP
gvsv: B::SVOP
threaded:
const: B::SVOP
gvsv: B::PADOP
-rw-r--r-- | ext/B/B.xs | 23 | ||||
-rw-r--r-- | ext/B/t/b.t | 38 |
2 files changed, 47 insertions, 14 deletions
diff --git a/ext/B/B.xs b/ext/B/B.xs index a130ad3cb4..b048f809bd 100644 --- a/ext/B/B.xs +++ b/ext/B/B.xs @@ -1159,20 +1159,15 @@ next(o) } } break; - case 39: /* sv */ - case 40: /* gv */ - /* It happens that the output typemaps for B::SV and B::GV - * are identical. The "smarts" are in make_sv_object(), - * which determines which class to use based on SvTYPE(), - * rather than anything baked in at compile time. */ - if (cPADOPo->op_padix) { - ret = PAD_SVl(cPADOPo->op_padix); - if (ix == 40 && SvTYPE(ret) != SVt_PVGV) - ret = NULL; - } else { - ret = NULL; - } - ret = make_sv_object(aTHX_ ret); + case 39: /* B::PADOP::sv */ + case 40: /* B::PADOP::gv */ + /* PADOPs should only be created on threaded builds. + * They don't have an sv or gv field, just an op_padix + * field. Leave it to the caller to retrieve padix + * and look up th value in the pad. Don't do it here, + * becuase PL_curpad is the pad of the caller, not the + * pad of the sub the op is part of */ + ret = make_sv_object(aTHX_ NULL); break; case 41: /* pv */ /* OP_TRANS uses op_pv to point to a table of 256 or >=258 diff --git a/ext/B/t/b.t b/ext/B/t/b.t index 9933978f43..271eb371ea 100644 --- a/ext/B/t/b.t +++ b/ext/B/t/b.t @@ -443,4 +443,42 @@ SKIP: { } +# make sure ->sv, -gv methods do the right thing on threaded builds +{ + + # for some reason B::walkoptree only likes a sub name, not a code ref + my ($gv, $sv); + sub gvsv_const { + # make the early pad slots something unlike a threaded const or + # gvsv + my ($dummy1, $dummy2, $dummy3, $dummy4) = qw(foo1 foo2 foo3 foo4); + my $self = shift; + if ($self->name eq 'gvsv') { + $gv = $self->gv; + } + elsif ($self->name eq 'const') { + $sv = $self->sv; + } + }; + + B::walkoptree(B::svref_2object(sub {our $x = 1})->ROOT, "::gvsv_const"); + ok(defined $gv, "gvsv->gv seen"); + ok(defined $sv, "const->sv seen"); + if ($Config::Config{useithreads}) { + # should get NULLs + is(ref($gv), "B::SPECIAL", "gvsv->gv is special"); + is(ref($sv), "B::SPECIAL", "const->sv is special"); + is($$gv, 0, "gvsv->gv special is 0 (NULL)"); + is($$sv, 0, "const->sv special is 0 (NULL)"); + } + else { + is(ref($gv), "B::GV", "gvsv->gv is GV"); + is(ref($sv), "B::IV", "const->sv is IV"); + pass(); + pass(); + } + +} + + done_testing(); |