From: Nicholas Clark Date: Sat, 22 Sep 2007 15:46:44 +0000 (+0000) Subject: Fix bug 45607 - for the corner case *{"BONK"} = \&{"BONK"} the order X-Git-Tag: accepted/trunk/20130322.191538~14545 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=53a42478dbff55e7fc2f0fe0876cb4ceeaba894c;p=platform%2Fupstream%2Fperl.git 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 --- diff --git a/pp_hot.c b/pp_hot.c index 62eddad..6fb53d4 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); diff --git a/t/op/gv.t b/t/op/gv.t index bca84e7..5b04f87 100755 --- a/t/op/gv.t +++ b/t/op/gv.t @@ -12,7 +12,7 @@ BEGIN { use warnings; require './test.pl'; -plan( tests => 160 ); +plan( tests => 161 ); # type coersion on assignment $foo = 'foo'; @@ -485,6 +485,15 @@ foreach my $value ([1,2,3], {1=>2}, *STDOUT{IO}, \&ok, *STDOUT{FORMAT}) { eval "`` if 0"; is($@, '', "Can't trip up readpipe overloading"); } + +{ + die if exists $::{BONK}; + $::{BONK} = \"powie"; + *{"BONK"} = \&{"BONK"}; + eval 'is(BONK(), "powie", + "Assigment works when glob created midway (bug 45607)"); 1' + or die $@; +} __END__ Perl Rules