diff options
author | Nicholas Clark <nick@ccl4.org> | 2007-09-22 15:46:44 +0000 |
---|---|---|
committer | Nicholas Clark <nick@ccl4.org> | 2007-09-22 15:46:44 +0000 |
commit | 53a42478dbff55e7fc2f0fe0876cb4ceeaba894c (patch) | |
tree | 1cf9becd3813514075ef380eaae6b7e88b2c1e0a /pp_hot.c | |
parent | e47d7ad1097fe8bf25456f76aaa863c85118bca8 (diff) | |
download | perl-53a42478dbff55e7fc2f0fe0876cb4ceeaba894c.tar.gz |
Fix bug 45607 - for the corner case *{"BONK"} = \&{"BONK"} the order
of op evaluation means that what had been a reference to a constant
can turn into a typeglob before the sassign gets to run.
p4raw-id: //depot/perl@31940
Diffstat (limited to 'pp_hot.c')
-rw-r--r-- | pp_hot.c | 46 |
1 files changed, 36 insertions, 10 deletions
@@ -168,18 +168,44 @@ PP(pp_sassign) if (!got_coderef) { /* We've been returned a constant rather than a full subroutine, but they expect a subroutine reference to apply. */ - ENTER; - SvREFCNT_inc_void(SvRV(cv)); - /* newCONSTSUB takes a reference count on the passed in SV - from us. We set the name to NULL, otherwise we get into - all sorts of fun as the reference to our new sub is - donated to the GV that we're about to assign to. - */ - SvRV_set(left, (SV *)newCONSTSUB(GvSTASH(right), NULL, + if (SvROK(cv)) { + ENTER; + SvREFCNT_inc_void(SvRV(cv)); + /* newCONSTSUB takes a reference count on the passed in SV + from us. We set the name to NULL, otherwise we get into + all sorts of fun as the reference to our new sub is + donated to the GV that we're about to assign to. + */ + SvRV_set(left, (SV *)newCONSTSUB(GvSTASH(right), NULL, SvRV(cv))); - SvREFCNT_dec(cv); - LEAVE; + SvREFCNT_dec(cv); + LEAVE; + } else { + /* What can happen for the corner case *{"BONK"} = \&{"BONK"}; + is that + First: ops for \&{"BONK"}; return us the constant in the + symbol table + Second: ops for *{"BONK"} cause that symbol table entry + (and our reference to it) to be upgraded from RV + to typeblob) + Thirdly: We get here. cv is actually PVGV now, and its + GvCV() is actually the subroutine we're looking for + + So change the reference so that it points to the subroutine + of that typeglob, as that's what they were after all along. + */ + GV *const upgraded = (GV *) cv; + CV *const source = GvCV(upgraded); + + assert(source); + assert(CvFLAGS(source) & CVf_CONST); + + SvREFCNT_inc_void(source); + SvREFCNT_dec(upgraded); + SvRV_set(left, (SV *)source); + } } + } SvSetMagicSV(right, left); SETs(right); |