summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFather Chrysostomos <sprout@cpan.org>2011-12-18 00:00:31 -0800
committerFather Chrysostomos <sprout@cpan.org>2011-12-18 00:00:31 -0800
commit423e8af5fd21022f9107100c8561c5f880121231 (patch)
tree0e115adfbb1f1bd6923853438c9d56e73bf2083e
parent3500db162fc97aafc7cfeaa8c61f6c681810e3b4 (diff)
downloadperl-423e8af5fd21022f9107100c8561c5f880121231.tar.gz
Stop seek($glob_copy...) from clearing PL_last_in_gv
seek had the same bug as tell. Here is the commit message from 8dc99089, which fixed tell: ---------------------------------------------------------------------- Stop tell($glob_copy) from clearing PL_last_in_gv This bug is a side effect of rv2gv’s starting to return an incoercible mortal copy of a coercible glob in 5.14: $ perl5.12.4 -le 'open FH, "t/test.pl"; $fh=*FH; tell $fh; print tell' 0 $ perl5.14.0 -le 'open FH, "t/test.pl"; $fh=*FH; tell $fh; print tell' -1 In the first case, tell without arguments is returning the position of the filehandle. In the second case, tell with an explicit argument that happens to be a coercible glob (tell has an implicit rv2gv, so tell $fh is actu- ally tell *$fh) sets PL_last_in_gv to a mortal copy thereof, which is freed at the end of the statement, setting PL_last_in_gv to null. So there is no ‘last used’ handle by the time we get to the tell without arguments. This commit adds a new rv2gv flag that tells it not to copy the glob. By doing it unconditionally on the kidop, this allows tell(*$fh) to work the same way. Let’s hope nobody does tell(*{*$fh}), which will unset PL_last_in_gv because the inner * returns a mortal copy. This whole area is really icky. PL_last_in_gv should be refcounted, but that would cause handles to leak out of scope, breaking programs that rely on the auto-closing ‘feature’.
-rw-r--r--op.c1
-rw-r--r--opcode.h2
-rw-r--r--regen/opcodes2
-rw-r--r--t/io/tell.t8
4 files changed, 10 insertions, 3 deletions
diff --git a/op.c b/op.c
index ad06161fb4..08e9790b80 100644
--- a/op.c
+++ b/op.c
@@ -9697,6 +9697,7 @@ Perl_ck_tell(pTHX_ OP *o)
o = ck_fun(o);
if (o->op_flags & OPf_KIDS) {
OP *kid = cLISTOPo->op_first;
+ if (kid->op_type == OP_NULL && kid->op_sibling) kid = kid->op_sibling;
if (kid->op_type == OP_RV2GV) kid->op_private |= OPpALLOW_FAKE;
}
return o;
diff --git a/opcode.h b/opcode.h
index 00d27f876c..709e92c431 100644
--- a/opcode.h
+++ b/opcode.h
@@ -1546,7 +1546,7 @@ EXT Perl_check_t PL_check[] /* or perlvars.h */
Perl_ck_fun, /* syswrite */
Perl_ck_eof, /* eof */
Perl_ck_tell, /* tell */
- Perl_ck_fun, /* seek */
+ Perl_ck_tell, /* seek */
Perl_ck_trunc, /* truncate */
Perl_ck_fun, /* fcntl */
Perl_ck_fun, /* ioctl */
diff --git a/regen/opcodes b/regen/opcodes
index e3c8767b6d..353bcc68d8 100644
--- a/regen/opcodes
+++ b/regen/opcodes
@@ -350,7 +350,7 @@ syswrite syswrite ck_fun imst@ F S S? S?
eof eof ck_eof is% F?
tell tell ck_tell st% F?
-seek seek ck_fun s@ F S S
+seek seek ck_tell s@ F S S
# truncate really behaves as if it had both "S S" and "F S"
truncate truncate ck_trunc is@ S S
diff --git a/t/io/tell.t b/t/io/tell.t
index 91fe31732e..1e577cb8be 100644
--- a/t/io/tell.t
+++ b/t/io/tell.t
@@ -6,7 +6,7 @@ BEGIN {
require './test.pl';
}
-print "1..33\n";
+print "1..35\n";
$TST = 'TST';
@@ -175,3 +175,9 @@ print "${not}ok 32 - argless tell after eof \$coercible\n";
eof *$fh;
$not = "not " x! (tell == 0);
print "${not}ok 33 - argless tell after eof *\$coercible\n";
+seek $fh,0,0;
+$not = "not " x! (tell == 0);
+print "${not}ok 34 - argless tell after seek \$coercible...\n";
+seek *$fh,0,0;
+$not = "not " x! (tell == 0);
+print "${not}ok 35 - argless tell after seek *\$coercible...\n";