Skip to content

Commit 471c834

Browse files
committed
open: only treat literal undef as special
Passing undef as the filename to open should create a temporary file. Previously, this was implemented as a simple `if (arg == &PL_sv_undef)` check. However, other operations (such as accessing non-existent array/hash elements) also return `&PL_sv_undef` directly, which `open` would then silently interpret as directions to create a temp file. Solution: Check (at compile time) whether the filename argument is an `OP_UNDEF`, and if so, set the `OPf_SPECIAL` flag on `OP_OPEN`. At runtime, if `&PL_sv_undef` is passed but `OPf_SPECIAL` is not set, replace it by a new mortal SV, which is treated as a regular filename (that happens to be undef) by PerlIO. (For runtime calls via `&CORE::open`, we cannot distinguish between `undef` and `delete $hash{nosuchkey}` as arguments, so to keep the `undef` case working, we always set `OPf_SPECIAL` in the generated core sub for `open`.) This functionality probably should be a private op flag (something like `OPpUNDEF_MEANS_TEMPFILE`?), but as far as I can tell, all possible private bits are already taken in `OP_OPEN`. Fixes #22385.
1 parent df1adf3 commit 471c834

File tree

4 files changed

+58
-2
lines changed

4 files changed

+58
-2
lines changed

doio.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -849,6 +849,9 @@ Perl_do_open6(pTHX_ GV *gv, const char *oname, STRLEN len,
849849
}
850850
else {
851851
if (num_svs) {
852+
if (*svp == &PL_sv_undef && PL_op && !(PL_op->op_flags & OPf_SPECIAL)) {
853+
*svp = sv_newmortal();
854+
}
852855
fp = PerlIO_openn(aTHX_ type,mode,-1,0,0,NULL,num_svs,svp);
853856
}
854857
else {
@@ -884,6 +887,9 @@ Perl_do_open6(pTHX_ GV *gv, const char *oname, STRLEN len,
884887
}
885888
else {
886889
if (num_svs) {
890+
if (*svp == &PL_sv_undef && PL_op && !(PL_op->op_flags & OPf_SPECIAL)) {
891+
*svp = sv_newmortal();
892+
}
887893
fp = PerlIO_openn(aTHX_ type,mode,-1,0,0,NULL,num_svs,svp);
888894
}
889895
else {

op.c

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13491,6 +13491,19 @@ Perl_ck_open(pTHX_ OP *o)
1349113491
(last == OpSIBLING(oa))) /* The bareword. */
1349213492
last->op_private &= ~OPpCONST_STRICT;
1349313493
}
13494+
{
13495+
/* mark as special if filename is a literal undef */
13496+
const OP *arg = cLISTOPx(o)->op_first; /* pushmark */
13497+
if (
13498+
(arg = OpSIBLING(arg)) /* handle */
13499+
&& (arg = OpSIBLING(arg)) /* mode */
13500+
&& (arg = OpSIBLING(arg)) /* filename */
13501+
) {
13502+
if (arg->op_type == OP_UNDEF && !(arg->op_flags & OPf_KIDS)) {
13503+
o->op_flags |= OPf_SPECIAL;
13504+
}
13505+
}
13506+
}
1349413507
return ck_fun(o);
1349513508
}
1349613509

@@ -15679,7 +15692,19 @@ Perl_coresub_op(pTHX_ SV * const coreargssv, const int code,
1567915692
}
1568015693
return o;
1568115694
default:
15682-
o = op_convert_list(opnum,OPf_SPECIAL*(opnum == OP_GLOB),argop);
15695+
/* For open(), OPf_SPECIAL indicates we saw a literal undef as the
15696+
* filename argument and thus a &PL_sv_undef argument at runtime
15697+
* should trigger the creation of a temp file. This is to
15698+
* distinguish between open(..., ..., undef) and
15699+
* open(..., ..., delete $hash{key}), which also passes
15700+
* &PL_sv_undef if $hash{key} does not exist, but which should not
15701+
* create a temporary file.
15702+
* In case of a runtime call via &CORE::open(...) or
15703+
* my $f = \&CORE::open; $f->(...), we cannot distinguish between
15704+
* those cases. Therefore we always set the flag to interpret
15705+
* &PL_sv_undef as a temp file.
15706+
*/
15707+
o = op_convert_list(opnum,OPf_SPECIAL*(opnum == OP_GLOB || opnum == OP_OPEN),argop);
1568315708
if (is_handle_constructor(o, 2))
1568415709
argop->op_private |= OPpCOREARGS_DEREF2;
1568515710
if (opnum == OP_SUBSTR) {

op.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,8 @@ Deprecated. Use C<GIMME_V> instead.
164164
/* On OP_RETURN, module_true is in effect */
165165
/* On OP_NEXT/OP_LAST/OP_REDO, there is no
166166
* loop label */
167+
/* On OP_OPEN, create a temporary file if the
168+
* filename argument is &PL_sv_undef */
167169
/* There is no room in op_flags for this one, so it has its own bit-
168170
field member (op_folded) instead. The flag is only used to tell
169171
op_convert_list to set op_folded. */

t/io/perlio_open.t

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ BEGIN {
1111
use strict;
1212
use warnings;
1313

14-
plan tests => 10;
14+
plan tests => 16;
1515

1616
use Fcntl qw(:seek);
1717

@@ -43,4 +43,27 @@ SKIP:
4343
is($data, "abcxyz", "check the second write appended");
4444
}
4545

46+
{
47+
my $fn = \&CORE::open;
48+
ok($fn->(my $fh, "+>", undef), "(\\&CORE::open)->(my \$fh, '+>', undef)");
49+
print $fh "the right write stuff";
50+
ok(seek($fh, 0, SEEK_SET), "seek to zero");
51+
my $data = <$fh>;
52+
is($data, "the right write stuff", "found the right stuff");
53+
}
4654

55+
{
56+
# GH #22385
57+
my %hash;
58+
my $warnings = '';
59+
local $SIG{__WARN__} = sub { $warnings .= $_[0] };
60+
my $r = open my $fh, "+>", delete $hash{nosuchkey};
61+
my $enoent = $!{ENOENT};
62+
is $r, undef, "open(my \$fh, '+>', delete \$hash{nosuchkey}) fails";
63+
SKIP: {
64+
skip "This system doesn't understand ENOENT", 1
65+
unless exists $!{ENOENT};
66+
ok $enoent, "\$! is ENOENT";
67+
}
68+
like $warnings, qr/^Use of uninitialized value in open/, "it warns about undef";
69+
}

0 commit comments

Comments
 (0)