#define PUSHSUB(cx) \
PUSHSUB_BASE(cx) \
cx->blk_u16 = PL_op->op_private & \
- (OPpLVAL_INTRO|OPpENTERSUB_INARGS);
+ (OPpLVAL_INTRO|OPpENTERSUB_INARGS|OPpENTERSUB_DEREF);
/* variant for use by OP_DBSTATE, where op_private holds hint bits */
#define PUSHSUB_DB(cx) \
@{$priv{$_}}{32,64,96} = ("DREFAV", "DREFHV", "DREFSV")
for (qw(rv2gv rv2sv padsv aelem helem));
$priv{$_}{16} = "STATE" for ("padav", "padhv", "padsv");
-@{$priv{"entersub"}}{4,16,32,64} = ("INARGS","DBG","TARG","NOMOD");
+@{$priv{"entersub"}}{1,4,16,32,64} = qw( DREF INARGS DBG TARG NOMOD );
@{$priv{rv2cv}}{1,8,128} = ("CONST","AMPER","NO()");
$priv{"gv"}{32} = "EARLYCV";
$priv{"aelem"}{16} = $priv{"helem"}{16} = "LVDEFER";
# b <0> pushmark s
# c <#> gvsv[*_] s
# d <#> gv[*getkey] s/EARLYCV
-# e <1> entersub[t5] lKS/TARG,1
+# e <1> entersub[t5] lKS/TARG
# f <#> gvsv[*_] s
# g <@> list lK
# h <@> leave lKP
# k <0> pushmark s
# l <#> gvsv[*_] s
# m <#> gv[*getkey] s/EARLYCV
-# n <1> entersub[t10] sKS/TARG,1
+# n <1> entersub[t10] sKS/TARG
# o <2> helem sKRM*/2
# p <2> sassign vKS/2
# q <0> unstack s
# b <0> pushmark s
# c <#> gvsv[*_] s
# d <#> gv[*getkey] s/EARLYCV
-# e <1> entersub[t5] lKS/TARG,1
+# e <1> entersub[t5] lKS/TARG
# f <#> gvsv[*_] s
# g <@> list lK
# h <@> leave lKP
# b <0> pushmark s
# c <$> gvsv(*_) s
# d <$> gv(*getkey) s/EARLYCV
-# e <1> entersub[t2] lKS/TARG,1
+# e <1> entersub[t2] lKS/TARG
# f <$> gvsv(*_) s
# g <@> list lK
# h <@> leave lKP
# i <0> pushmark s
# j <#> gvsv[*_] s
# k <#> gv[*getkey] s/EARLYCV
-# l <1> entersub[t10] sKS/TARG,1
+# l <1> entersub[t10] sKS/TARG
# m <2> helem sKRM*/2
# n <2> sassign vKS/2
# o <0> unstack s
# i <0> pushmark s
# j <$> gvsv(*_) s
# k <$> gv(*getkey) s/EARLYCV
-# l <1> entersub[t4] sKS/TARG,1
+# l <1> entersub[t4] sKS/TARG
# m <2> helem sKRM*/2
# n <2> sassign vKS/2
# o <0> unstack s
if ((type == OP_UNDEF || type == OP_REFGEN) &&
!(o->op_flags & OPf_STACKED)) {
o->op_type = OP_RV2CV; /* entersub => rv2cv */
- /* The default is to set op_private to the number of children,
- which for a UNOP such as RV2CV is always 1. And w're using
- the bit for a flag in RV2CV, so we need it clear. */
+ /* Both ENTERSUB and RV2CV use this bit, but for different pur-
+ poses, so we need it clear. */
o->op_private &= ~1;
o->op_ppaddr = PL_ppaddr[OP_RV2CV];
assert(cUNOPo->op_first->op_type == OP_NULL);
o->op_flags |= OPf_SPECIAL;
o->op_private &= ~1;
}
+ else if (type == OP_RV2SV || type == OP_RV2AV || type == OP_RV2HV){
+ o->op_private |= OPpENTERSUB_DEREF;
+ o->op_flags |= OPf_MOD;
+ }
+
break;
case OP_COND_EXPR:
cv = rv2cv_op_cv(cvop, RV2CVOPCV_MARK_EARLY);
namegv = cv ? (GV*)rv2cv_op_cv(cvop, RV2CVOPCV_RETURN_NAME_GV) : NULL;
+ o->op_private &= ~1;
o->op_private |= OPpENTERSUB_HASTARG;
o->op_private |= (PL_hints & HINT_STRICT_REFS);
if (PERLDB_SUB && PL_curstash != PL_debstash)
case OP_RV2SV:
case OP_RV2AV:
case OP_RV2HV:
- if (oldop
- && ( oldop->op_type == OP_AELEM
+ if (oldop &&
+ (
+ (
+ ( oldop->op_type == OP_AELEM
|| oldop->op_type == OP_PADSV
|| oldop->op_type == OP_RV2SV
|| oldop->op_type == OP_RV2GV
|| oldop->op_type == OP_HELEM
)
&& (oldop->op_private & OPpDEREF)
+ )
+ || ( oldop->op_type == OP_ENTERSUB
+ && oldop->op_private & OPpENTERSUB_DEREF )
+ )
) {
o->op_private |= OPpDEREFed;
}
#define OPpENTERSUB_HASTARG 32 /* Called from OP tree. */
#define OPpENTERSUB_NOMOD 64 /* Immune to op_lvalue() for :attrlist. */
#define OPpENTERSUB_INARGS 4 /* Lval used as arg to a sub. */
+#define OPpENTERSUB_DEREF 1 /* Lval call that autovivifies. */
/* OP_RV2CV only */
#define OPpENTERSUB_AMPER 8 /* Used & form to call. */
#define OPpENTERSUB_NOPAREN 128 /* bare sub call (without parens) */
}
else
*++newsp = &PL_sv_undef;
+ if (CxLVAL(cx) & OPpENTERSUB_DEREF) {
+ SvGETMAGIC(TOPs);
+ if (!SvOK(TOPs)) {
+ U8 deref_type;
+ if (cx->blk_sub.retop->op_type == OP_RV2SV)
+ deref_type = OPpDEREF_SV;
+ else if (cx->blk_sub.retop->op_type == OP_RV2AV)
+ deref_type = OPpDEREF_AV;
+ else {
+ assert(cx->blk_sub.retop->op_type == OP_RV2HV);
+ deref_type = OPpDEREF_HV;
+ }
+ vivify_ref(TOPs, deref_type);
+ }
+ }
}
else if (gimme == G_ARRAY) {
+ assert (!(CxLVAL(cx) & OPpENTERSUB_DEREF));
while (++MARK <= SP) {
*++newsp = *MARK;
TAINT_NOT; /* Each item is independent */
bool popsub2 = FALSE;
bool clear_errsv = FALSE;
bool lval = FALSE;
+ bool gmagic = FALSE;
I32 gimme;
SV **newsp;
PMOP *newpm;
popsub2 = TRUE;
lval = !!CvLVALUE(cx->blk_sub.cv);
retop = cx->blk_sub.retop;
+ gmagic = CxLVAL(cx) & OPpENTERSUB_DEREF;
cxstack_ix++; /* preserve cx entry on stack for use by POPSUB */
break;
case CXt_EVAL:
FREETMPS;
*++newsp = sv_mortalcopy(sv);
SvREFCNT_dec(sv);
+ if (gmagic) SvGETMAGIC(sv);
}
}
+ else if (SvTEMP(*SP)) {
+ *++newsp = *SP;
+ if (gmagic) SvGETMAGIC(*SP);
+ }
else
- *++newsp =
- SvTEMP(*SP) ? *SP : sv_mortalcopy(*SP);
+ *++newsp = sv_mortalcopy(*SP);
}
else
*++newsp = sv_mortalcopy(*SP);
I32 gimme;
register PERL_CONTEXT *cx;
SV *sv;
+ bool gmagic;
if (CxMULTICALL(&cxstack[cxstack_ix]))
return 0;
POPBLOCK(cx,newpm);
cxstack_ix++; /* temporarily protect top context */
+ gmagic = CxLVAL(cx) & OPpENTERSUB_DEREF;
TAINT_NOT;
if (gimme == G_SCALAR) {
*MARK = SvREFCNT_inc(TOPs);
FREETMPS;
sv_2mortal(*MARK);
+ if (gmagic) SvGETMAGIC(*MARK);
}
else {
sv = SvREFCNT_inc(TOPs); /* FREETMPS could clobber it */
SvREFCNT_dec(sv);
}
}
+ else if (SvTEMP(TOPs)) {
+ *MARK = TOPs;
+ if (gmagic) SvGETMAGIC(TOPs);
+ }
else
- *MARK = SvTEMP(TOPs) ? TOPs : sv_mortalcopy(TOPs);
+ *MARK = sv_mortalcopy(TOPs);
}
else {
MEXTEND(MARK, 0);
SP = MARK;
}
}
+
+ if (CxLVAL(cx) & OPpENTERSUB_DEREF) {
+ assert(gimme == G_SCALAR);
+ SvGETMAGIC(TOPs);
+ if (!SvOK(TOPs)) {
+ U8 deref_type;
+ if (cx->blk_sub.retop->op_type == OP_RV2SV)
+ deref_type = OPpDEREF_SV;
+ else if (cx->blk_sub.retop->op_type == OP_RV2AV)
+ deref_type = OPpDEREF_AV;
+ else {
+ assert(cx->blk_sub.retop->op_type == OP_RV2HV);
+ deref_type = OPpDEREF_HV;
+ }
+ vivify_ref(TOPs, deref_type);
+ }
+ }
+
rvalue_array:
PUTBACK;
ok($rgot == 0, 'a plain *foo causes no get-magic');
ok($wgot == 0, 'a plain *foo causes no set-magic');
+# get-magic when exiting a non-lvalue sub in potentially autovivify-
+# ing context
+$tied_to = tie $_{elem}, "Tie::Monitor";
+eval { () = sub { delete $_{elem} }->()->[3] };
+ok +($tied_to->init)[0],
+ 'get-magic is called on mortal magic var on sub exit in autoviv context';
+$tied_to = tie $_{elem}, "Tie::Monitor";
+eval { () = sub { return delete $_{elem} }->()->[3] };
+ok +($tied_to->init)[0],
+ 'get-magic is called on mortal magic var on return in autoviv context';
+
done_testing();
# adapted from Tie::Counter by Abigail
@INC = '../lib';
require './test.pl';
}
-plan tests=>124;
+plan tests=>134;
sub a : lvalue { my $a = 34; ${\(bless \$a)} } # Return a temporary
sub b : lvalue { ${\shift} }
is $_, '44', '(lvalue)[0]'.$suffix;
}
continue { $suffix = ' (explicit return)' }
+
+# autovivification
+for my $sub (sub :lvalue {$_}, sub :lvalue {return $_}) {
+ undef $_;
+ &$sub()->[3] = 4;
+ is $_->[3], 4, 'func->[...] autovivification'.$suffix;
+ undef $_;
+ &$sub()->{3} = 4;
+ is $_->{3}, 4, 'func->{...} autovivification'.$suffix;
+ undef $_;
+ ${&$sub()} = 4;
+ is $$_, 4, '${func()} autovivification' .$suffix;
+ undef $_;
+ @{&$sub()} = 4;
+ is "@$_", 4, '@{func()} autovivification' .$suffix;
+ undef $_;
+ %{&$sub()} = (4,5);
+ is join('-',%$_), '4-5', '%{func()} autovivification'.$suffix;
+}
+continue { $suffix = ' (explicit return)' }