In perl.git, the branch blead has been updated

<http://perl5.git.perl.org/perl.git/commitdiff/1921e03146ca6022defa6af5267c4dd20c0ca699?hp=cc4d3128555c2fbf5af7fc75854461cd87502812>

- Log -----------------------------------------------------------------
commit 1921e03146ca6022defa6af5267c4dd20c0ca699
Author: David Mitchell <[email protected]>
Date:   Sun Mar 20 17:12:13 2016 +0000

    stop lc() etc accidentally modifying in-place.
    
    As an optimisation, [ul]c() and [ul]cfirst() sometimes modify their
    argument in-place rather than returning a modified copy.
    
    This should only be done when there is no possibility that the arg is
    going to be reused. However, this fails:
    
        use List::Util qw{ first };
        my %hash = ( ASD => 1, ZXC => 2, QWE => 3, TYU => 4);
        print first { lc $_ eq 'qwe' } keys %hash;
    
    which prints "qwe" rather than "QWE".
    
    Bascally everything in perl that sets $_ or $a/$b and calls a code block
    or function, such as map, grep, for and, sort, either copies any PADTMPs,
    turns off SvTEMP, and/or bumps the reference count.
    
    List::Util doesn't do this, and it is likely that other CPAN modules
    which do "set $_ and call a block" don't either.
    
    This has been failing since 5.20.0: perl has been in-placing if the arg is
    (SvTEMP && RC==1 && !mg) (due to v5.19.7-112-g5cd5e2d).
    
    Make the optimisation critera stricter by always copying SvTEMPs.
    It still allows the optimisation if the arg is a PADTMP - I don't know
    whether this is unsafe too.
    
    Perhaps we can think of something better after 5.24?
-----------------------------------------------------------------------

Summary of changes:
 pp.c      | 14 +++-----------
 t/op/lc.t | 26 +++++++++++++++++++++++++-
 2 files changed, 28 insertions(+), 12 deletions(-)

diff --git a/pp.c b/pp.c
index 4fcc577..4a2cde0 100644
--- a/pp.c
+++ b/pp.c
@@ -3875,10 +3875,7 @@ PP(pp_ucfirst)
     /* We may be able to get away with changing only the first character, in
      * place, but not if read-only, etc.  Later we may discover more reasons to
      * not convert in-place. */
-    inplace = !SvREADONLY(source)
-          && (  SvPADTMP(source)
-             || (  SvTEMP(source) && !SvSMAGICAL(source)
-                && SvREFCNT(source) == 1));
+    inplace = !SvREADONLY(source) && SvPADTMP(source);
 
     /* First calculate what the changed first character should be.  This 
affects
      * whether we can just swap it out, leaving the rest of the string 
unchanged,
@@ -4118,9 +4115,7 @@ PP(pp_uc)
 
     SvGETMAGIC(source);
 
-    if ((SvPADTMP(source)
-        ||
-       (SvTEMP(source) && !SvSMAGICAL(source) && SvREFCNT(source) == 1))
+    if (   SvPADTMP(source)
        && !SvREADONLY(source) && SvPOK(source)
        && !DO_UTF8(source)
        && (
@@ -4377,10 +4372,7 @@ PP(pp_lc)
 
     SvGETMAGIC(source);
 
-    if (   (  SvPADTMP(source)
-          || (  SvTEMP(source) && !SvSMAGICAL(source)
-             && SvREFCNT(source) == 1  )
-          )
+    if (   SvPADTMP(source)
        && !SvREADONLY(source) && SvPOK(source)
        && !DO_UTF8(source)) {
 
diff --git a/t/op/lc.t b/t/op/lc.t
index a390c63..2ce65ac 100644
--- a/t/op/lc.t
+++ b/t/op/lc.t
@@ -1,6 +1,8 @@
 #!./perl
 
 # This file is intentionally encoded in latin-1.
+#
+# Test uc(), lc(), fc(), ucfirst(), lcfirst(), quotemeta() etc
 
 BEGIN {
     chdir 't' if -d 't';
@@ -14,7 +16,7 @@ BEGIN {
 
 use feature qw( fc );
 
-plan tests => 134 + 4 * 256;
+plan tests => 139 + 4 * 256;
 
 is(lc(undef),     "", "lc(undef) is ''");
 is(lcfirst(undef), "", "lcfirst(undef) is ''");
@@ -317,6 +319,28 @@ $h{k} = bless[], "\x{130}bcde"; # U+0130 grows with lc()
 like lc delete $h{k}, qr "^i\x{307}bcde=array\(.*\)",
     'lc(TEMP ref) does not produce a corrupt string';
 
+# List::Util::first() etc sets $_ to an SvTEMP without raising its
+# refcount.  This was causing lc() etc to unsafely modify in-place.
+# see http://nntp.perl.org/group/perl.perl5.porters/228213
+
+SKIP: {
+    skip "no List::Util on miniperl", 5, if is_miniperl;
+    require List::Util;
+    my %hl = qw(a 1 b 2 c 3);
+    my %hu = qw(A 1 B 2 C 3);
+    my $x;
+    $x = List::Util::first(sub { uc      $_ eq 'A' }, keys %hl);
+    is($x, "a", "first { uc }");
+    $x = List::Util::first(sub { ucfirst $_ eq 'A' }, keys %hl);
+    is($x, "a", "first { ucfirst }");
+    $x = List::Util::first(sub { lc      $_ eq 'a' }, keys %hu);
+    is($x, "A", "first { lc }");
+    $x = List::Util::first(sub { lcfirst $_ eq 'a' }, keys %hu);
+    is($x, "A", "first { lcfirst }");
+    $x = List::Util::first(sub { fc      $_ eq 'a' }, keys %hu);
+    is($x, "A", "first { fc }");
+}
+
 
 my $utf8_locale = find_utf8_ctype_locale();
 

--
Perl5 Master Repository

Reply via email to