/* Pushy stuff. */
+/* This is also called directly by pp_lvavref. */
PP(pp_padav)
{
dSP; dTARGET;
const char * const name = CopSTASHPV(PL_curcop);
gv = newGVgen_flags(name,
HvNAMEUTF8(CopSTASH(PL_curcop)) ? SVf_UTF8 : 0 );
+ SvREFCNT_inc_simple_void_NN(gv);
}
prepare_SV_for_RV(sv);
SvRV_set(sv, MUTABLE_SV(gv));
AV * const av = MUTABLE_AV(TOPs);
const I32 lvalue = PL_op->op_flags & OPf_MOD || LVRET;
if (lvalue) {
- SV ** const sv = Perl_av_arylen_p(aTHX_ MUTABLE_AV(av));
- if (!*sv) {
- *sv = newSV_type(SVt_PVMG);
- sv_magic(*sv, MUTABLE_SV(av), PERL_MAGIC_arylen, NULL, 0);
+ SV ** const svp = Perl_av_arylen_p(aTHX_ MUTABLE_AV(av));
+ if (!*svp) {
+ *svp = newSV_type(SVt_PVMG);
+ sv_magic(*svp, MUTABLE_SV(av), PERL_MAGIC_arylen, NULL, 0);
}
- SETs(*sv);
+ SETs(*svp);
} else {
SETs(sv_2mortal(newSViv(AvFILL(MUTABLE_AV(av)))));
}
CV *cv = sv_2cv(TOPs, &stash_unused, &gv, flags);
if (cv) NOOP;
else if ((flags == (GV_ADD|GV_NOEXPAND)) && gv && SvROK(gv)) {
- cv = MUTABLE_CV(gv);
+ cv = SvTYPE(SvRV(gv)) == SVt_PVCV
+ ? MUTABLE_CV(SvRV(gv))
+ : MUTABLE_CV(gv);
}
else
cv = MUTABLE_CV(&PL_sv_undef);
SvREFCNT_inc_void_NN(sv);
}
else if (SvPADTMP(sv)) {
- assert(!IS_PADGV(sv));
sv = newSVsv(sv);
}
else {
RETPUSHYES;
}
+
+/* also used for: pp_transr() */
+
PP(pp_trans)
{
dSP; dTARG;
if (PL_op->op_flags & OPf_STACKED)
sv = POPs;
- else if (PL_op->op_private & OPpTARGET_MY)
+ else if (ARGTARG)
sv = GETTARGET;
else {
sv = DEFSV;
/* Lvalue operators. */
-static void
+static size_t
S_do_chomp(pTHX_ SV *retval, SV *sv, bool chomping)
{
STRLEN len;
char *s;
+ size_t count = 0;
PERL_ARGS_ASSERT_DO_CHOMP;
if (chomping && (RsSNARF(PL_rs) || RsRECORD(PL_rs)))
- return;
+ return 0;
if (SvTYPE(sv) == SVt_PVAV) {
I32 i;
AV *const av = MUTABLE_AV(sv);
for (i = 0; i <= max; i++) {
sv = MUTABLE_SV(av_fetch(av, i, FALSE));
if (sv && ((sv = *(SV**)sv), sv != &PL_sv_undef))
- do_chomp(retval, sv, chomping);
+ count += do_chomp(retval, sv, chomping);
}
- return;
+ return count;
}
else if (SvTYPE(sv) == SVt_PVHV) {
HV* const hv = MUTABLE_HV(sv);
HE* entry;
(void)hv_iterinit(hv);
while ((entry = hv_iternext(hv)))
- do_chomp(retval, hv_iterval(hv,entry), chomping);
- return;
+ count += do_chomp(retval, hv_iterval(hv,entry), chomping);
+ return count;
}
else if (SvREADONLY(sv)) {
Perl_croak_no_modify();
}
- else if (SvIsCOW(sv)) {
- sv_force_normal_flags(sv, 0);
- }
- if (PL_encoding) {
+ if (IN_ENCODING) {
if (!SvUTF8(sv)) {
/* XXX, here sv is utf8-ized as a side-effect!
If encoding.pm is used properly, almost string-generating
operations, including literal strings, chr(), input data, etc.
should have been utf8-ized already, right?
*/
- sv_recode_to_utf8(sv, PL_encoding);
+ sv_recode_to_utf8(sv, _get_encoding());
}
}
if (RsPARA(PL_rs)) {
if (*s != '\n')
goto nope;
- ++SvIVX(retval);
+ ++count;
while (len && s[-1] == '\n') {
--len;
--s;
- ++SvIVX(retval);
+ ++count;
}
}
else {
}
rsptr = temp_buffer;
}
- else if (PL_encoding) {
+ else if (IN_ENCODING) {
/* RS is 8 bit, encoding.pm is used.
* Do not recode PL_rs as a side-effect. */
svrecode = newSVpvn(rsptr, rslen);
- sv_recode_to_utf8(svrecode, PL_encoding);
+ sv_recode_to_utf8(svrecode, _get_encoding());
rsptr = SvPV_const(svrecode, rslen);
rs_charlen = sv_len_utf8(svrecode);
}
if (rslen == 1) {
if (*s != *rsptr)
goto nope;
- ++SvIVX(retval);
+ ++count;
}
else {
if (len < rslen - 1)
s -= rslen - 1;
if (memNE(s, rsptr, rslen))
goto nope;
- SvIVX(retval) += rs_charlen;
+ count += rs_charlen;
}
}
- s = SvPV_force_nomg_nolen(sv);
+ SvPV_force_nomg_nolen(sv);
SvCUR_set(sv, len);
*SvEND(sv) = '\0';
SvNIOK_off(sv);
Safefree(temp_buffer);
} else {
- if (len && !SvPOK(sv))
+ if (len && (!SvPOK(sv) || SvIsCOW(sv)))
s = SvPV_force_nomg(sv, len);
if (DO_UTF8(sv)) {
if (s && len) {
sv_setpvs(retval, "");
SvSETMAGIC(sv);
}
+ return count;
}
+
+/* also used for: pp_schomp() */
+
PP(pp_schop)
{
dSP; dTARGET;
const bool chomping = PL_op->op_type == OP_SCHOMP;
+ const size_t count = do_chomp(TARG, TOPs, chomping);
if (chomping)
- sv_setiv(TARG, 0);
- do_chomp(TARG, TOPs, chomping);
+ sv_setiv(TARG, count);
SETTARG;
RETURN;
}
+
+/* also used for: pp_chomp() */
+
PP(pp_chop)
{
dSP; dMARK; dTARGET; dORIGMARK;
const bool chomping = PL_op->op_type == OP_CHOMP;
+ size_t count = 0;
- if (chomping)
- sv_setiv(TARG, 0);
while (MARK < SP)
- do_chomp(TARG, *++MARK, chomping);
+ count += do_chomp(TARG, *++MARK, chomping);
+ if (chomping)
+ sv_setiv(TARG, count);
SP = ORIGMARK;
XPUSHTARG;
RETURN;
if (!sv)
RETPUSHUNDEF;
- SV_CHECK_THINKFIRST_COW_DROP(sv);
+ if (SvTHINKFIRST(sv))
+ sv_force_normal_flags(sv, SV_COW_DROP_PV|SV_IMMEDIATE_UNREF);
switch (SvTYPE(sv)) {
case SVt_NULL:
));
/* FALLTHROUGH */
case SVt_PVFM:
- {
/* let user-undef'd sub keep its identity */
- GV* const gv = CvGV((const CV *)sv);
- HEK * const hek = CvNAME_HEK((CV *)sv);
- if (hek) share_hek_hek(hek);
- cv_undef(MUTABLE_CV(sv));
- if (gv) CvGV_set(MUTABLE_CV(sv), gv);
- else if (hek) {
- SvANY((CV *)sv)->xcv_gv_u.xcv_hek = hek;
- CvNAMED_on(sv);
- }
- }
+ cv_undef_flags(MUTABLE_CV(sv), CV_UNDEF_KEEP_NAME);
break;
case SVt_PVGV:
assert(isGV_with_GP(sv));
RETPUSHUNDEF;
}
+
+/* also used for: pp_i_postdec() pp_i_postinc() pp_postdec() */
+
PP(pp_postinc)
{
dSP; dTARGET;
/* special case for undef: see thread at 2003-03/msg00536.html in archive */
if (inc && !SvOK(TARG))
sv_setiv(TARG, 0);
- SETs(TARG);
+ SETTARG;
return NORMAL;
}
SvGETMAGIC(sv);
}
else {
+ if (UNLIKELY(PL_op->op_private & OPpREPEAT_DOLIST)) {
+ /* The parser saw this as a list repeat, and there
+ are probably several items on the stack. But we're
+ in scalar/void context, and there's no pp_list to save us
+ now. So drop the rest of the items -- robin@kitsite.com
+ */
+ dMARK;
+ if (MARK + 1 < SP) {
+ MARK[1] = TOPm1s;
+ MARK[2] = TOPs;
+ }
+ else {
+ dTOPss;
+ ASSUME(MARK + 1 == SP);
+ XPUSHs(sv);
+ MARK[1] = &PL_sv_undef;
+ }
+ SP = MARK + 2;
+ }
tryAMAGICbin_MG(repeat_amg, AMGf_assign);
sv = POPs;
}
MEXTEND(MARK, max);
if (count > 1) {
while (SP > MARK) {
-#if 0
- /* This code was intended to fix 20010809.028:
-
- $x = 'abcd';
- for (($x =~ /./g) x 2) {
- print chop; # "abcdabcd" expected as output.
- }
-
- * but that change (#11635) broke this code:
-
- $x = [("foo")x2]; # only one "foo" ended up in the anonlist.
-
- * I can't think of a better fix that doesn't introduce
- * an efficiency hit by copying the SVs. The stack isn't
- * refcounted, and mortalisation obviously doesn't
- * Do The Right Thing when the stack has more than
- * one pointer to the same mortal value.
- * .robin.
- */
- if (*SP) {
- *SP = sv_2mortal(newSVsv(*SP));
- SvREADONLY_on(*SP);
- }
-#else
if (*SP) {
if (mod && SvPADTMP(*SP)) {
- assert(!IS_PADGV(*SP));
*SP = sv_mortalcopy(*SP);
}
SvTEMP_off((*SP));
}
-#endif
SP--;
}
MARK++;
else
(void)SvPOK_only(TARG);
- if (PL_op->op_private & OPpREPEAT_DOLIST) {
- /* The parser saw this as a list repeat, and there
- are probably several items on the stack. But we're
- in scalar context, and there's no pp_list to save us
- now. So drop the rest of the items -- robin@kitsite.com
- */
- dMARK;
- SP = MARK;
- }
PUSHTARG;
}
RETURN;
return (leftuv > (UV)rightiv) - (leftuv < (UV)rightiv);
}
}
- assert(0); /* NOTREACHED */
+ NOT_REACHED; /* NOTREACHED */
}
#endif
{
RETURN;
}
+
+/* also used for: pp_sge() pp_sgt() pp_slt() */
+
PP(pp_sle)
{
dSP;
}
}
+
+/* also used for: pp_bit_xor() */
+
PP(pp_bit_or)
{
dSP; dATARGET;
}
}
+
+/* also used for: pp_cos() pp_exp() pp_log() pp_sqrt() */
+
PP(pp_sin)
{
dSP; dTARGET;
- int amg_type = sin_amg;
+ int amg_type = fallback_amg;
const char *neg_report = NULL;
- NV (*func)(NV) = Perl_sin;
const int op_type = PL_op->op_type;
switch (op_type) {
- case OP_COS:
- amg_type = cos_amg;
- func = Perl_cos;
- break;
- case OP_EXP:
- amg_type = exp_amg;
- func = Perl_exp;
- break;
- case OP_LOG:
- amg_type = log_amg;
- func = Perl_log;
- neg_report = "log";
- break;
- case OP_SQRT:
- amg_type = sqrt_amg;
- func = Perl_sqrt;
- neg_report = "sqrt";
- break;
+ case OP_SIN: amg_type = sin_amg; break;
+ case OP_COS: amg_type = cos_amg; break;
+ case OP_EXP: amg_type = exp_amg; break;
+ case OP_LOG: amg_type = log_amg; neg_report = "log"; break;
+ case OP_SQRT: amg_type = sqrt_amg; neg_report = "sqrt"; break;
}
+ assert(amg_type != fallback_amg);
tryAMAGICun_MG(amg_type, 0);
{
SV * const arg = POPs;
const NV value = SvNV_nomg(arg);
- if (neg_report) {
- if (op_type == OP_LOG ? (value <= 0.0) : (value < 0.0)) {
+ NV result = NV_NAN;
+ if (neg_report) { /* log or sqrt */
+ if (
+#if defined(NAN_COMPARE_BROKEN) && defined(Perl_isnan)
+ ! Perl_isnan(value) &&
+#endif
+ (op_type == OP_LOG ? (value <= 0.0) : (value < 0.0))) {
SET_NUMERIC_STANDARD();
/* diag_listed_as: Can't take log of %g */
DIE(aTHX_ "Can't take %s of %"NVgf, neg_report, value);
}
}
- XPUSHn(func(value));
+ switch (op_type) {
+ default:
+ case OP_SIN: result = Perl_sin(value); break;
+ case OP_COS: result = Perl_cos(value); break;
+ case OP_EXP: result = Perl_exp(value); break;
+ case OP_LOG: result = Perl_log(value); break;
+ case OP_SQRT: result = Perl_sqrt(value); break;
+ }
+ XPUSHn(result);
RETURN;
}
}
value = SvNV(sv);
}
/* 1 of 2 things can be carried through SvNV, SP or TARG, SP was carried */
+#if defined(NAN_COMPARE_BROKEN) && defined(Perl_isnan)
+ if (! Perl_isnan(value) && value == 0.0)
+#else
if (value == 0.0)
+#endif
value = 1.0;
{
dTARGET;
}
else {
const NV value = SvNV_nomg(sv);
- if (value >= 0.0) {
+ if (SvNOK(sv) && UNLIKELY(Perl_isinfnan(SvNV(sv))))
+ SETn(SvNV(sv));
+ else if (value >= 0.0) {
if (value < (NV)UV_MAX + 0.5) {
SETu(U_V(value));
} else {
RETURN;
}
+
+/* also used for: pp_hex() */
+
PP(pp_oct)
{
dSP; dTARGET;
tmps++, len--;
if (*tmps == '0')
tmps++, len--;
- if (*tmps == 'x' || *tmps == 'X') {
+ if (isALPHA_FOLD_EQ(*tmps, 'x')) {
hex:
result_uv = grok_hex (tmps, &len, &flags, &result_nv);
}
- else if (*tmps == 'b' || *tmps == 'B')
+ else if (isALPHA_FOLD_EQ(*tmps, 'b'))
result_uv = grok_bin (tmps, &len, &flags, &result_nv);
else
result_uv = grok_oct (tmps, &len, &flags, &result_nv);
dSP; dTARGET;
SV * const sv = TOPs;
- SvGETMAGIC(sv);
+ U32 in_bytes = IN_BYTES;
+ /* simplest case shortcut */
+ /* turn off SVf_UTF8 in tmp flags if HINT_BYTES on*/
+ U32 svflags = (SvFLAGS(sv) ^ (in_bytes << 26)) & (SVf_POK|SVs_GMG|SVf_UTF8);
+ STATIC_ASSERT_STMT(HINT_BYTES == 0x00000008 && SVf_UTF8 == 0x20000000 && (SVf_UTF8 == HINT_BYTES << 26));
+ SETs(TARG);
+
+ if(LIKELY(svflags == SVf_POK))
+ goto simple_pv;
+ if(svflags & SVs_GMG)
+ mg_get(sv);
if (SvOK(sv)) {
- if (!IN_BYTES)
- SETi(sv_len_utf8_nomg(sv));
+ if (!IN_BYTES) /* reread to avoid using an C auto/register */
+ sv_setiv(TARG, (IV)sv_len_utf8_nomg(sv));
else
{
STRLEN len;
- (void)SvPV_nomg_const(sv,len);
- SETi(len);
+ /* unrolled SvPV_nomg_const(sv,len) */
+ if(SvPOK_nog(sv)){
+ simple_pv:
+ len = SvCUR(sv);
+ } else {
+ (void)sv_2pv_flags(sv, &len, 0|SV_CONST_RETURN);
+ }
+ sv_setiv(TARG, (IV)(len));
}
} else {
if (!SvPADTMP(TARG)) {
sv_setsv_nomg(TARG, &PL_sv_undef);
- SETTARG;
- }
- SETs(&PL_sv_undef);
+ } else { /* TARG is on stack at this point and is overwriten by SETs.
+ This branch is the odd one out, so put TARG by default on
+ stack earlier to let local SP go out of liveness sooner */
+ SETs(&PL_sv_undef);
+ goto no_set_magic;
+ }
}
- RETURN;
+ SvSETMAGIC(TARG);
+ no_set_magic:
+ return NORMAL; /* no putback, SP didn't move in this opcode */
}
/* Returns false if substring is completely outside original string.
}
}
SPAGAIN;
- if (rvalue) {
+ if (PL_op->op_private & OPpSUBSTR_REPL_FIRST)
+ SP++;
+ else if (rvalue) {
SvSETMAGIC(TARG);
PUSHs(TARG);
}
}
sv_setuv(ret, do_vecget(src, offset, size));
+ if (!lvalue)
+ SvSETMAGIC(ret);
PUSHs(ret);
RETURN;
}
+
+/* also used for: pp_rindex() */
+
PP(pp_index)
{
dSP; dTARGET;
little_utf8 = DO_UTF8(little);
if (big_utf8 ^ little_utf8) {
/* One needs to be upgraded. */
- if (little_utf8 && !PL_encoding) {
+ if (little_utf8 && !IN_ENCODING) {
/* Well, maybe instead we might be able to downgrade the small
string? */
char * const pv = (char*)bytes_from_utf8((U8 *)little_p, &llen,
temp = little_utf8
? newSVpvn(big_p, biglen) : newSVpvn(little_p, llen);
- if (PL_encoding) {
- sv_recode_to_utf8(temp, PL_encoding);
+ if (IN_ENCODING) {
+ sv_recode_to_utf8(temp, _get_encoding());
} else {
sv_utf8_upgrade(temp);
}
STRLEN len;
const U8 *s = (U8*)SvPV_const(argsv, len);
- if (PL_encoding && SvPOK(argsv) && !DO_UTF8(argsv)) {
+ if (IN_ENCODING && SvPOK(argsv) && !DO_UTF8(argsv)) {
SV * const tmpsv = sv_2mortal(newSVsv(argsv));
- s = (U8*)sv_recode_to_utf8(tmpsv, PL_encoding);
+ s = (U8*)sv_recode_to_utf8(tmpsv, _get_encoding());
len = UTF8SKIP(s); /* Should be well-formed; so this is its length */
argsv = tmpsv;
}
SV *top = POPs;
SvGETMAGIC(top);
- if (!IN_BYTES /* under bytes, chr(-1) eq chr(0xff), etc. */
- && ((SvIOKp(top) && !SvIsUV(top) && SvIV_nomg(top) < 0)
- ||
- ((SvNOKp(top) || (SvOK(top) && !SvIsUV(top)))
- && SvNV_nomg(top) < 0.0))) {
+ if (UNLIKELY(isinfnansv(top)))
+ Perl_croak(aTHX_ "Cannot chr %"NVgf, SvNV(top));
+ else {
+ if (!IN_BYTES /* under bytes, chr(-1) eq chr(0xff), etc. */
+ && ((SvIOKp(top) && !SvIsUV(top) && SvIV_nomg(top) < 0)
+ ||
+ ((SvNOKp(top) || (SvOK(top) && !SvIsUV(top)))
+ && SvNV_nomg(top) < 0.0))) {
if (ckWARN(WARN_UTF8)) {
if (SvGMAGICAL(top)) {
SV *top2 = sv_newmortal();
sv_setsv_nomg(top2, top);
top = top2;
}
- Perl_warner(aTHX_ packWARN(WARN_UTF8),
- "Invalid negative number (%"SVf") in chr", SVfARG(top));
- }
- value = UNICODE_REPLACEMENT;
- } else {
- value = SvUV_nomg(top);
+ Perl_warner(aTHX_ packWARN(WARN_UTF8),
+ "Invalid negative number (%"SVf") in chr", SVfARG(top));
+ }
+ value = UNICODE_REPLACEMENT;
+ } else {
+ value = SvUV_nomg(top);
+ }
}
SvUPGRADE(TARG,SVt_PV);
*tmps = '\0';
(void)SvPOK_only(TARG);
SvUTF8_on(TARG);
- XPUSHs(TARG);
+ XPUSHTARG;
RETURN;
}
*tmps = '\0';
(void)SvPOK_only(TARG);
- if (PL_encoding && !IN_BYTES) {
- sv_recode_to_utf8(TARG, PL_encoding);
+ if (IN_ENCODING && !IN_BYTES) {
+ sv_recode_to_utf8(TARG, _get_encoding());
tmps = SvPVX(TARG);
if (SvCUR(TARG) == 0
|| ! is_utf8_string((U8*)tmps, SvCUR(TARG))
}
}
- XPUSHs(TARG);
+ XPUSHTARG;
RETURN;
}
/* If Unicode, try to downgrade.
* If not possible, croak.
* Yes, we made this up. */
- SV* const tsv = sv_2mortal(newSVsv(left));
+ SV* const tsv = newSVpvn_flags(tmps, len, SVf_UTF8|SVs_TEMP);
- SvUTF8_on(tsv);
sv_utf8_downgrade(tsv, FALSE);
tmps = SvPV_const(tsv, len);
}
# else
sv_setpv(TARG, PerlProc_crypt(tmps, SvPV_nolen_const(right)));
# endif
+ SvUTF8_off(TARG);
SETTARG;
RETURN;
#else
/* Generally UTF-8 and UTF-EBCDIC are indistinguishable at this level. So
* most comments below say UTF-8, when in fact they mean UTF-EBCDIC as well */
+
+/* also used for: pp_lcfirst() */
+
PP(pp_ucfirst)
{
/* Actually is both lcfirst() and ucfirst(). Only the first character
}
}
else if (UTF8_IS_DOWNGRADEABLE_START(*s)) {
+ if (
#ifdef USE_LOCALE_CTYPE
/* In locale, we quote all non-ASCII Latin1 chars.
* Otherwise use the quoting rules */
- if (IN_LC_RUNTIME(LC_CTYPE)
- || _isQUOTEMETA(TWO_BYTE_UTF8_TO_NATIVE(*s, *(s + 1))))
+
+ IN_LC_RUNTIME(LC_CTYPE)
+ ||
+#endif
+ _isQUOTEMETA(TWO_BYTE_UTF8_TO_NATIVE(*s, *(s + 1))))
{
to_quote = TRUE;
}
-#endif
}
else if (is_QUOTEMETA_high(s)) {
to_quote = TRUE;
RETURN;
}
+
/* Smart dereferencing for keys, values and each */
+
+/* also used for: pp_reach() pp_rvalues() */
+
PP(pp_rkeys)
{
dSP;
RETURN;
}
+/* also used for: pp_avalues()*/
PP(pp_akeys)
{
dSP;
if (!(*lelem = firstrelem[ix]))
*lelem = &PL_sv_undef;
else if (mod && SvPADTMP(*lelem)) {
- assert(!IS_PADGV(*lelem));
*lelem = firstrelem[ix] = sv_mortalcopy(*lelem);
}
}
RETURN;
}
+/* also used for: pp_pop()*/
PP(pp_shift)
{
dSP;
PP(pp_split)
{
dSP; dTARG;
- AV *ary;
+ AV *ary = PL_op->op_flags & OPf_STACKED ? (AV *)POPs : NULL;
IV limit = POPi; /* note, negative is forever */
SV * const sv = POPs;
STRLEN len;
#else
pm = (PMOP*)POPs;
#endif
- if (!pm || !s)
+ if (!pm)
DIE(aTHX_ "panic: pp_split, pm=%p, s=%p", pm, s);
rx = PM_GETRE(pm);
ary = GvAVn(pm->op_pmreplrootu.op_pmtargetgv);
}
#endif
- else
- ary = NULL;
+ else if (pm->op_targ)
+ ary = (AV *)PAD_SVl(pm->op_targ);
if (ary) {
realarray = 1;
PUTBACK;
av_extend(ary,0);
+ (void)sv_2mortal(SvREFCNT_inc_simple_NN(sv));
av_clear(ary);
SPAGAIN;
if ((mg = SvTIED_mg((const SV *)ary, PERL_MAGIC_tied))) {
}
+/* used for: pp_padany(), pp_mapstart(), pp_custom(); plus any system ops
+ * that aren't implemented on a particular platform */
+
PP(unimplemented_op)
{
const Optype op_type = PL_op->op_type;
RETURN;
}
+static void
+S_localise_aelem_lval(pTHX_ AV * const av, SV * const keysv,
+ const bool can_preserve)
+{
+ const SSize_t ix = SvIV(keysv);
+ if (can_preserve ? av_exists(av, ix) : TRUE) {
+ SV ** const svp = av_fetch(av, ix, 1);
+ if (!svp || !*svp)
+ Perl_croak(aTHX_ PL_no_aelem, ix);
+ save_aelem(av, ix, svp);
+ }
+ else
+ SAVEADELETE(av, ix);
+}
+
+static void
+S_localise_helem_lval(pTHX_ HV * const hv, SV * const keysv,
+ const bool can_preserve)
+{
+ if (can_preserve ? hv_exists_ent(hv, keysv, 0) : TRUE) {
+ HE * const he = hv_fetch_ent(hv, keysv, 1, 0);
+ SV ** const svp = he ? &HeVAL(he) : NULL;
+ if (!svp || !*svp)
+ Perl_croak(aTHX_ PL_no_helem_sv, SVfARG(keysv));
+ save_helem_flags(hv, keysv, svp, 0);
+ }
+ else
+ SAVEHDELETE(hv, keysv);
+}
+
+static void
+S_localise_gv_slot(pTHX_ GV *gv, U8 type)
+{
+ if (type == OPpLVREF_SV) {
+ save_pushptrptr(gv, SvREFCNT_inc_simple(GvSV(gv)), SAVEt_GVSV);
+ GvSV(gv) = 0;
+ }
+ else if (type == OPpLVREF_AV)
+ /* XXX Inefficient, as it creates a new AV, which we are
+ about to clobber. */
+ save_ary(gv);
+ else {
+ assert(type == OPpLVREF_HV);
+ /* XXX Likewise inefficient. */
+ save_hash(gv);
+ }
+}
+
+
+PP(pp_refassign)
+{
+ dSP;
+ SV * const key = PL_op->op_private & OPpLVREF_ELEM ? POPs : NULL;
+ SV * const left = PL_op->op_flags & OPf_STACKED ? POPs : NULL;
+ dTOPss;
+ const char *bad = NULL;
+ const U8 type = PL_op->op_private & OPpLVREF_TYPE;
+ if (!SvROK(sv)) DIE(aTHX_ "Assigned value is not a reference");
+ switch (type) {
+ case OPpLVREF_SV:
+ if (SvTYPE(SvRV(sv)) > SVt_PVLV)
+ bad = " SCALAR";
+ break;
+ case OPpLVREF_AV:
+ if (SvTYPE(SvRV(sv)) != SVt_PVAV)
+ bad = "n ARRAY";
+ break;
+ case OPpLVREF_HV:
+ if (SvTYPE(SvRV(sv)) != SVt_PVHV)
+ bad = " HASH";
+ break;
+ case OPpLVREF_CV:
+ if (SvTYPE(SvRV(sv)) != SVt_PVCV)
+ bad = " CODE";
+ }
+ if (bad)
+ /* diag_listed_as: Assigned value is not %s reference */
+ DIE(aTHX_ "Assigned value is not a%s reference", bad);
+ {
+ MAGIC *mg;
+ HV *stash;
+ switch (left ? SvTYPE(left) : 0) {
+ case 0:
+ {
+ SV * const old = PAD_SV(ARGTARG);
+ PAD_SETSV(ARGTARG, SvREFCNT_inc_NN(SvRV(sv)));
+ SvREFCNT_dec(old);
+ if ((PL_op->op_private & (OPpLVAL_INTRO|OPpPAD_STATE))
+ == OPpLVAL_INTRO)
+ SAVECLEARSV(PAD_SVl(ARGTARG));
+ break;
+ }
+ case SVt_PVGV:
+ if (PL_op->op_private & OPpLVAL_INTRO) {
+ S_localise_gv_slot(aTHX_ (GV *)left, type);
+ }
+ gv_setref(left, sv);
+ SvSETMAGIC(left);
+ break;
+ case SVt_PVAV:
+ if (UNLIKELY(PL_op->op_private & OPpLVAL_INTRO)) {
+ S_localise_aelem_lval(aTHX_ (AV *)left, key,
+ SvCANEXISTDELETE(left));
+ }
+ av_store((AV *)left, SvIV(key), SvREFCNT_inc_simple_NN(SvRV(sv)));
+ break;
+ case SVt_PVHV:
+ if (UNLIKELY(PL_op->op_private & OPpLVAL_INTRO))
+ S_localise_helem_lval(aTHX_ (HV *)left, key,
+ SvCANEXISTDELETE(left));
+ (void)hv_store_ent((HV *)left, key, SvREFCNT_inc_simple_NN(SvRV(sv)), 0);
+ }
+ if (PL_op->op_flags & OPf_MOD)
+ SETs(sv_2mortal(newSVsv(sv)));
+ /* XXX else can weak references go stale before they are read, e.g.,
+ in leavesub? */
+ RETURN;
+ }
+}
+
+PP(pp_lvref)
+{
+ dSP;
+ SV * const ret = sv_2mortal(newSV_type(SVt_PVMG));
+ SV * const elem = PL_op->op_private & OPpLVREF_ELEM ? POPs : NULL;
+ SV * const arg = PL_op->op_flags & OPf_STACKED ? POPs : NULL;
+ MAGIC * const mg = sv_magicext(ret, arg, PERL_MAGIC_lvref,
+ &PL_vtbl_lvref, (char *)elem,
+ elem ? HEf_SVKEY : (I32)ARGTARG);
+ mg->mg_private = PL_op->op_private;
+ if (PL_op->op_private & OPpLVREF_ITER)
+ mg->mg_flags |= MGf_PERSIST;
+ if (UNLIKELY(PL_op->op_private & OPpLVAL_INTRO)) {
+ if (elem) {
+ MAGIC *mg;
+ HV *stash;
+ const bool can_preserve = SvCANEXISTDELETE(arg);
+ if (SvTYPE(arg) == SVt_PVAV)
+ S_localise_aelem_lval(aTHX_ (AV *)arg, elem, can_preserve);
+ else
+ S_localise_helem_lval(aTHX_ (HV *)arg, elem, can_preserve);
+ }
+ else if (arg) {
+ S_localise_gv_slot(aTHX_ (GV *)arg,
+ PL_op->op_private & OPpLVREF_TYPE);
+ }
+ else if (!(PL_op->op_private & OPpPAD_STATE))
+ SAVECLEARSV(PAD_SVl(ARGTARG));
+ }
+ XPUSHs(ret);
+ RETURN;
+}
+
+PP(pp_lvrefslice)
+{
+ dSP; dMARK;
+ AV * const av = (AV *)POPs;
+ const bool localizing = PL_op->op_private & OPpLVAL_INTRO;
+ bool can_preserve = FALSE;
+
+ if (UNLIKELY(localizing)) {
+ MAGIC *mg;
+ HV *stash;
+ SV **svp;
+
+ can_preserve = SvCANEXISTDELETE(av);
+
+ if (SvTYPE(av) == SVt_PVAV) {
+ SSize_t max = -1;
+
+ for (svp = MARK + 1; svp <= SP; svp++) {
+ const SSize_t elem = SvIV(*svp);
+ if (elem > max)
+ max = elem;
+ }
+ if (max > AvMAX(av))
+ av_extend(av, max);
+ }
+ }
+
+ while (++MARK <= SP) {
+ SV * const elemsv = *MARK;
+ if (SvTYPE(av) == SVt_PVAV)
+ S_localise_aelem_lval(aTHX_ av, elemsv, can_preserve);
+ else
+ S_localise_helem_lval(aTHX_ (HV *)av, elemsv, can_preserve);
+ *MARK = sv_2mortal(newSV_type(SVt_PVMG));
+ sv_magic(*MARK,(SV *)av,PERL_MAGIC_lvref,(char *)elemsv,HEf_SVKEY);
+ }
+ RETURN;
+}
+
+PP(pp_lvavref)
+{
+ if (PL_op->op_flags & OPf_STACKED)
+ Perl_pp_rv2av(aTHX);
+ else
+ Perl_pp_padav(aTHX);
+ {
+ dSP;
+ dTOPss;
+ SETs(0); /* special alias marker that aassign recognises */
+ XPUSHs(sv);
+ RETURN;
+ }
+}
/*
* Local variables: