summaryrefslogtreecommitdiff
path: root/pp_hot.c
diff options
context:
space:
mode:
authorNicholas Clark <nick@ccl4.org>2007-09-22 15:46:44 +0000
committerNicholas Clark <nick@ccl4.org>2007-09-22 15:46:44 +0000
commit53a42478dbff55e7fc2f0fe0876cb4ceeaba894c (patch)
tree1cf9becd3813514075ef380eaae6b7e88b2c1e0a /pp_hot.c
parente47d7ad1097fe8bf25456f76aaa863c85118bca8 (diff)
downloadperl-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.c46
1 files changed, 36 insertions, 10 deletions
diff --git a/pp_hot.c b/pp_hot.c
index 62eddadfa6..6fb53d49a4 100644
--- a/pp_hot.c
+++ b/pp_hot.c
@@ -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);