This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
PerlIO::scalar: stringify refs
authorDavid Mitchell <davem@iabyn.com>
Thu, 17 Oct 2013 14:35:14 +0000 (15:35 +0100)
committerDavid Mitchell <davem@iabyn.com>
Thu, 17 Oct 2013 14:46:05 +0000 (15:46 +0100)
If $s in
    open my $fh, "<",  \$s
or similar is a ref, then
stringify that ref: i.e. convert it from a ref into the string
"SCALAR(0x....)" or whatever.

This fixes

    RT #119529 Filehandle opened from ref to ref hangs on reading

which in this case was looping forever, since it kept thinking that
the var was a string ("SCALAR.."), but whose length was 0.

I haven't gone for a complete "always force var into a string" approach,
since PerlIO::scalar has quite a tolerance for "bad" vars; e.g.
it won't warn if $var is undef or a read-only constant number etc;
and it already normalises under some circumstances and not others.
So I've just increased the cases somewhat where it normalises.

Also, I didn't look to closely at the code that was looping (or to put it
another way, I looked at it but didn't understand it), so it could
conceivably still behave badly on some other strange type of variable that
manages to avoid getting normalised.

ext/PerlIO-scalar/scalar.pm
ext/PerlIO-scalar/scalar.xs
ext/PerlIO-scalar/t/scalar.t

index 64ecc22..e38dd54 100644 (file)
@@ -1,5 +1,5 @@
 package PerlIO::scalar;
 package PerlIO::scalar;
-our $VERSION = '0.16';
+our $VERSION = '0.17';
 require XSLoader;
 XSLoader::load();
 1;
 require XSLoader;
 XSLoader::load();
 1;
index e7e8330..95668e7 100644 (file)
@@ -48,7 +48,12 @@ PerlIOScalar_pushed(pTHX_ PerlIO * f, const char *mode, SV * arg,
     else {
        s->var = newSVpvn("", 0);
     }
     else {
        s->var = newSVpvn("", 0);
     }
-    SvUPGRADE(s->var, SVt_PV);
+    if (SvROK(s->var))
+        /* force refs, overload etc to be plain strings */
+        (void)SvPV_force_nomg_nolen(s->var);
+    else
+        SvUPGRADE(s->var, SVt_PV);
+
     code = PerlIOBase_pushed(aTHX_ f, mode, Nullsv, tab);
     if (!SvOK(s->var) || (PerlIOBase(f)->flags) & PERLIO_F_TRUNCATE)
     {
     code = PerlIOBase_pushed(aTHX_ f, mode, Nullsv, tab);
     if (!SvOK(s->var) || (PerlIOBase(f)->flags) & PERLIO_F_TRUNCATE)
     {
index 5a91071..f5ee47e 100644 (file)
@@ -16,7 +16,7 @@ use Fcntl qw(SEEK_SET SEEK_CUR SEEK_END); # Not 0, 1, 2 everywhere.
 
 $| = 1;
 
 
 $| = 1;
 
-use Test::More tests => 108;
+use Test::More tests => 109;
 
 my $fh;
 my $var = "aaa\n";
 
 my $fh;
 my $var = "aaa\n";
@@ -462,3 +462,12 @@ my $byte_warning = "Strings with code points over 0xFF may not be mapped into in
     ok(!(print $fh "B"), "write to an non-downgradable SV (and warn)");
     is_deeply(\@warnings, [ $byte_warning ], "check warning");
 }
     ok(!(print $fh "B"), "write to an non-downgradable SV (and warn)");
     is_deeply(\@warnings, [ $byte_warning ], "check warning");
 }
+
+#  RT #119529: non-string should be forced into a string
+
+{
+    my $x = \42;
+    open my $fh, "<", \$x;
+    my $got = <$fh>; # this used to loop
+    like($got, qr/^SCALAR\(0x[0-9a-f]+\)$/, "ref to a ref");
+}