#if A_WORKAROUND_REQUIRE_PROPAGATION
-typedef struct {
- UV bits;
- I32 requires;
-} a_hint_t;
+#define A_ENCODE_UV(B, U) \
+ len = 0; \
+ while (len < sizeof(UV)) { \
+ (B)[len++] = (U) & 0xFF; \
+ (U) >>= 8; \
+ }
+
+#define A_DECODE_UV(U, B) \
+ len = sizeof(UV); \
+ while (len > 0) \
+ (U) = ((U) << 8) | (B)[--len];
STATIC SV *a_tag(pTHX_ UV bits) {
#define a_tag(B) a_tag(aTHX_ (B))
- SV *tag;
- a_hint_t h;
-
- h.bits = bits;
+ SV *hint;
+ const PERL_SI *si;
+ UV requires = 0;
+ unsigned char buf[sizeof(UV) * 2];
+ STRLEN len;
- {
- const PERL_SI *si;
- I32 requires = 0;
+ for (si = PL_curstackinfo; si; si = si->si_prev) {
+ I32 cxix;
- for (si = PL_curstackinfo; si; si = si->si_prev) {
- I32 cxix;
+ for (cxix = si->si_cxix; cxix >= 0; --cxix) {
+ const PERL_CONTEXT *cx = si->si_cxstack + cxix;
- for (cxix = si->si_cxix; cxix >= 0; --cxix) {
- const PERL_CONTEXT *cx = si->si_cxstack + cxix;
-
- if (CxTYPE(cx) == CXt_EVAL && cx->blk_eval.old_op_type == OP_REQUIRE)
- ++requires;
- }
+ if (CxTYPE(cx) == CXt_EVAL && cx->blk_eval.old_op_type == OP_REQUIRE)
+ ++requires;
}
-
- h.requires = requires;
}
- return newSVpvn((const char *) &h, sizeof h);
+ A_ENCODE_UV(buf, requires);
+ A_ENCODE_UV(buf + sizeof(UV), bits);
+ hint = newSVpvn(buf, sizeof buf);
+ SvREADONLY_on(hint);
+
+ return hint;
}
STATIC UV a_detag(pTHX_ const SV *hint) {
#define a_detag(H) a_detag(aTHX_ (H))
- const a_hint_t *h;
+ const PERL_SI *si;
+ UV requires = 0, requires_max = 0, bits = 0;
+ unsigned char *buf;
+ STRLEN len;
if (!(hint && SvOK(hint)))
return 0;
- h = (const a_hint_t *) SvPVX(hint);
-
- {
- const PERL_SI *si;
- I32 requires = 0;
+ buf = SvPVX(hint);
+ A_DECODE_UV(requires_max, buf);
- for (si = PL_curstackinfo; si; si = si->si_prev) {
- I32 cxix;
+ for (si = PL_curstackinfo; si; si = si->si_prev) {
+ I32 cxix;
- for (cxix = si->si_cxix; cxix >= 0; --cxix) {
- const PERL_CONTEXT *cx = si->si_cxstack + cxix;
+ for (cxix = si->si_cxix; cxix >= 0; --cxix) {
+ const PERL_CONTEXT *cx = si->si_cxstack + cxix;
- if (CxTYPE(cx) == CXt_EVAL && cx->blk_eval.old_op_type == OP_REQUIRE
- && ++requires > h->requires)
- return 0;
- }
+ if (CxTYPE(cx) == CXt_EVAL && cx->blk_eval.old_op_type == OP_REQUIRE
+ && ++requires > requires_max)
+ return 0;
}
}
- return h->bits;
+ A_DECODE_UV(bits, buf + sizeof(UV));
+
+ return bits;
}
#else /* A_WORKAROUND_REQUIRE_PROPAGATION */
STATIC UV a_hint(pTHX) {
#define a_hint() a_hint(aTHX)
- const SV *hint;
+ SV *hint;
#if A_HAS_PERL(5, 9, 5)
hint = Perl_refcounted_he_fetch(aTHX_ PL_curcop->cop_hints_hash,
NULL,
#endif
val = ptable_fetch(a_op_map, o);
- if (val) {
- *oi = *val;
- val = oi;
- } else
- oi->old_pp = 0;
+ *oi = *val;
#ifdef USE_ITHREADS
MUTEX_UNLOCK(&a_op_map_mutex);
UV hint;
dSP;
- if (!SvOK(TOPs)) {
+ a_map_fetch(PL_op, &oi);
+
+ if (PL_op == oi.root) { /* This means "@$arrayref" */
+ PL_op->op_ppaddr = oi.old_pp;
+ } else if (!SvOK(TOPs)) {
/* We always need to push an empty array to fool the pp_aelem() that comes
* later. */
SV *av;
RETURN;
}
- a_map_fetch(PL_op, &oi);
-
return CALL_FPTR(oi.old_pp)(aTHX);
}
a_map_fetch(PL_op, &oi);
- if (!SvOK(TOPs)) {
- if (oi.root->op_flags & OPf_MOD) {
- SV *hv;
- POPs;
- hv = sv_2mortal((SV *) newHV());
- PUSHs(hv);
- }
+ if (PL_op == oi.root) { /* This means "%$hashref" */
+ PL_op->op_ppaddr = oi.old_pp;
+ } else if (!SvOK(TOPs)) {
RETURN;
}
/* ... pp_deref (aelem,helem,rv2sv,padsv) .................................. */
-STATIC const char a_msg_forbidden[] = "Reference vivification forbidden";
-STATIC const char a_msg_impossible[] = "Can't vivify reference";
-
STATIC OP *a_pp_deref(pTHX) {
a_op_info oi;
UV flags;
SPAGAIN;
if (!SvOK(TOPs)) {
if (flags & A_HINT_STRICT)
- croak(a_msg_forbidden);
+ croak("Reference vivification forbidden");
else if (flags & A_HINT_WARN)
- warn(a_msg_forbidden);
+ warn("Reference was vivified");
else /* A_HINT_STORE */
- croak(a_msg_impossible);
+ croak("Can't vivify reference");
}
}
} else if (flags && (PL_op->op_private & OPpDEREF || PL_op == oi.root)) {
oi.flags = flags & A_HINT_NOTIFY;
- if ((oi.root->op_flags & (OPf_MOD|OPf_REF)) != (OPf_MOD|OPf_REF)) {
- if (flags & A_HINT_FETCH)
- oi.flags |= (A_HINT_FETCH|A_HINT_DEREF);
- } else if (flags & A_HINT_STORE)
+ if (oi.root->op_flags & OPf_MOD) {
+ if (flags & A_HINT_STORE)
oi.flags |= (A_HINT_STORE|A_HINT_DEREF);
+ } else if (flags & A_HINT_FETCH)
+ oi.flags |= (A_HINT_FETCH|A_HINT_DEREF);
if (PL_op == oi.root)
oi.flags &= ~A_HINT_DEREF;
return CALL_FPTR(oi.old_pp)(aTHX);
}
-/* ... pp_root (exists,delete) ............................................. */
+/* ... pp_root (exists,delete,keys,values) ................................. */
-STATIC OP *a_pp_root(pTHX) {
+STATIC OP *a_pp_root_unop(pTHX) {
+ a_op_info oi;
+ dSP;
+
+ if (!a_defined(TOPs)) {
+ POPs;
+ /* Can only be reached by keys or values */
+ if (GIMME_V == G_SCALAR) {
+ dTARGET;
+ PUSHi(0);
+ }
+ RETURN;
+ }
+
+ a_map_fetch(PL_op, &oi);
+
+ return CALL_FPTR(oi.old_pp)(aTHX);
+}
+
+STATIC OP *a_pp_root_binop(pTHX) {
a_op_info oi;
dSP;
}
o = CALL_FPTR(old_ck)(aTHX_ o);
+ if (cUNOPo->op_first->op_type == OP_GV)
+ return o;
+
hint = a_hint();
if (hint & A_HINT_DO) {
if (!(hint & A_HINT_STRICT)) {
return o;
}
-/* ... ck_root (exists,delete) ............................................. */
+/* ... ck_root (exists,delete,keys,values) ................................. */
STATIC OP *(*a_old_ck_exists)(pTHX_ OP *) = 0;
STATIC OP *(*a_old_ck_delete)(pTHX_ OP *) = 0;
+STATIC OP *(*a_old_ck_keys) (pTHX_ OP *) = 0;
+STATIC OP *(*a_old_ck_values)(pTHX_ OP *) = 0;
STATIC OP *a_ck_root(pTHX_ OP *o) {
OP * (*old_ck)(pTHX_ OP *o) = 0;
+ OP * (*new_pp)(pTHX) = 0;
bool enabled = FALSE;
UV hint = a_hint();
switch (o->op_type) {
case OP_EXISTS:
old_ck = a_old_ck_exists;
+ new_pp = a_pp_root_binop;
enabled = hint & A_HINT_EXISTS;
break;
case OP_DELETE:
old_ck = a_old_ck_delete;
+ new_pp = a_pp_root_binop;
enabled = hint & A_HINT_DELETE;
break;
+ case OP_KEYS:
+ old_ck = a_old_ck_keys;
+ new_pp = a_pp_root_unop;
+ enabled = hint & A_HINT_FETCH;
+ break;
+ case OP_VALUES:
+ old_ck = a_old_ck_values;
+ new_pp = a_pp_root_unop;
+ enabled = hint & A_HINT_FETCH;
+ break;
}
o = CALL_FPTR(old_ck)(aTHX_ o);
- if (enabled) {
- a_map_set_root(o, hint | A_HINT_DEREF);
- a_map_store(o, o->op_ppaddr, hint);
- o->op_ppaddr = a_pp_root;
- } else {
- a_map_set_root(o, 0);
- }
+ if (hint & A_HINT_DO) {
+ if (enabled) {
+ a_map_set_root(o, hint | A_HINT_DEREF);
+ a_map_store(o, o->op_ppaddr, hint);
+ o->op_ppaddr = new_pp;
+ } else {
+ a_map_set_root(o, 0);
+ }
+ } else
+ a_map_delete(o);
return o;
}
PL_check[OP_PADANY] = MEMBER_TO_FPTR(a_ck_padany);
a_old_ck_padsv = PL_check[OP_PADSV];
PL_check[OP_PADSV] = MEMBER_TO_FPTR(a_ck_padsv);
+
a_old_ck_aelem = PL_check[OP_AELEM];
PL_check[OP_AELEM] = MEMBER_TO_FPTR(a_ck_deref);
a_old_ck_helem = PL_check[OP_HELEM];
PL_check[OP_HELEM] = MEMBER_TO_FPTR(a_ck_deref);
a_old_ck_rv2sv = PL_check[OP_RV2SV];
PL_check[OP_RV2SV] = MEMBER_TO_FPTR(a_ck_deref);
+
a_old_ck_rv2av = PL_check[OP_RV2AV];
PL_check[OP_RV2AV] = MEMBER_TO_FPTR(a_ck_rv2xv);
a_old_ck_rv2hv = PL_check[OP_RV2HV];
PL_check[OP_RV2HV] = MEMBER_TO_FPTR(a_ck_rv2xv);
+
a_old_ck_exists = PL_check[OP_EXISTS];
PL_check[OP_EXISTS] = MEMBER_TO_FPTR(a_ck_root);
a_old_ck_delete = PL_check[OP_DELETE];
PL_check[OP_DELETE] = MEMBER_TO_FPTR(a_ck_root);
+ a_old_ck_keys = PL_check[OP_KEYS];
+ PL_check[OP_KEYS] = MEMBER_TO_FPTR(a_ck_root);
+ a_old_ck_values = PL_check[OP_VALUES];
+ PL_check[OP_VALUES] = MEMBER_TO_FPTR(a_ck_root);
stash = gv_stashpvn(__PACKAGE__, __PACKAGE_LEN__, 1);
newCONSTSUB(stash, "A_HINT_STRICT", newSVuv(A_HINT_STRICT));