X-Git-Url: http://git.vpit.fr/?a=blobdiff_plain;f=autovivification.xs;h=554056c1c53a024050d703df2f08cb0346b2093f;hb=fb8ae185db37d75973080191fc17ec24f44cd070;hp=b9843fb38ebc08628975f29c20dc75685e595300;hpb=4e74ad4da8d7152cf6d477fd8de677ba94026e7c;p=perl%2Fmodules%2Fautovivification.git diff --git a/autovivification.xs b/autovivification.xs index b9843fb..554056c 100644 --- a/autovivification.xs +++ b/autovivification.xs @@ -6,413 +6,110 @@ #include "perl.h" #include "XSUB.h" -#define __PACKAGE__ "autovivification" -#define __PACKAGE_LEN__ (sizeof(__PACKAGE__)-1) +/* --- XS helpers ---------------------------------------------------------- */ -/* --- Compatibility wrappers ---------------------------------------------- */ - -#ifndef HvNAME_get -# define HvNAME_get(H) HvNAME(H) -#endif +#define XSH_PACKAGE "autovivification" -#ifndef HvNAMELEN_get -# define HvNAMELEN_get(H) strlen(HvNAME_get(H)) -#endif +#include "xsh/caps.h" +#include "xsh/util.h" +#include "xsh/ops.h" +#include "xsh/peep.h" -#define A_HAS_PERL(R, V, S) (PERL_REVISION > (R) || (PERL_REVISION == (R) && (PERL_VERSION > (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION >= (S)))))) +/* ... Lexical hints ....................................................... */ -#ifndef A_WORKAROUND_REQUIRE_PROPAGATION -# define A_WORKAROUND_REQUIRE_PROPAGATION !A_HAS_PERL(5, 10, 1) -#endif +/* Used both for hints and op flags */ +#define A_HINT_STRICT 1 +#define A_HINT_WARN 2 +#define A_HINT_FETCH 4 +#define A_HINT_STORE 8 +#define A_HINT_KEYS 16 +#define A_HINT_VALUES 32 +#define A_HINT_EXISTS 64 +#define A_HINT_DELETE 128 +#define A_HINT_NOTIFY (A_HINT_STRICT|A_HINT_WARN) +#define A_HINT_DO (A_HINT_FETCH|A_HINT_STORE|A_HINT_KEYS|A_HINT_VALUES|A_HINT_EXISTS|A_HINT_DELETE) +#define A_HINT_MASK (A_HINT_NOTIFY|A_HINT_DO) -#ifndef A_HAS_RPEEP -# define A_HAS_RPEEP A_HAS_PERL(5, 13, 5) -#endif +/* Only used in op flags */ +#define A_HINT_ROOT 256 +#define A_HINT_SECOND 512 +#define A_HINT_DEREF 1024 -#ifndef A_HAS_MULTIDEREF -# define A_HAS_MULTIDEREF A_HAS_PERL(5, 21, 7) -#endif +#define XSH_HINTS_TYPE_UV 1 -#ifndef OpSIBLING -# ifdef OP_SIBLING -# define OpSIBLING(O) OP_SIBLING(O) -# else -# define OpSIBLING(O) ((O)->op_sibling) -# endif -#endif +#include "xsh/hints.h" -/* ... Thread safety and multiplicity ...................................... */ +#define a_hint() xsh_hints_detag(xsh_hints_fetch()) -/* Always safe when the workaround isn't needed */ -#if !A_WORKAROUND_REQUIRE_PROPAGATION -# undef A_FORKSAFE -# define A_FORKSAFE 1 -/* Otherwise, safe unless Makefile.PL says it's Win32 */ -#elif !defined(A_FORKSAFE) -# define A_FORKSAFE 1 -#endif +/* ... Thread-local storage ................................................ */ -#ifndef A_MULTIPLICITY -# if defined(MULTIPLICITY) -# define A_MULTIPLICITY 1 -# else -# define A_MULTIPLICITY 0 -# endif -#endif -#if A_MULTIPLICITY -# ifndef PERL_IMPLICIT_CONTEXT -# error MULTIPLICITY builds must set PERL_IMPLICIT_CONTEXT -# endif -#endif +#define XSH_THREADS_COMPILE_TIME_PROTECTION 1 +#define XSH_THREADS_USER_CONTEXT 0 -#ifndef tTHX -# define tTHX PerlInterpreter* -#endif +#include "xsh/threads.h" -#if A_MULTIPLICITY && defined(USE_ITHREADS) && defined(dMY_CXT) && defined(MY_CXT) && defined(START_MY_CXT) && defined(MY_CXT_INIT) && (defined(MY_CXT_CLONE) || defined(dMY_CXT_SV)) -# define A_THREADSAFE 1 -# ifndef MY_CXT_CLONE -# define MY_CXT_CLONE \ - dMY_CXT_SV; \ - my_cxt_t *my_cxtp = (my_cxt_t*)SvPVX(newSV(sizeof(my_cxt_t)-1)); \ - Copy(INT2PTR(my_cxt_t*, SvUV(my_cxt_sv)), my_cxtp, 1, my_cxt_t); \ - sv_setuv(my_cxt_sv, PTR2UV(my_cxtp)) -# endif -#else -# define A_THREADSAFE 0 -# undef dMY_CXT -# define dMY_CXT dNOOP -# undef MY_CXT -# define MY_CXT a_globaldata -# undef START_MY_CXT -# define START_MY_CXT static my_cxt_t MY_CXT; -# undef MY_CXT_INIT -# define MY_CXT_INIT NOOP -# undef MY_CXT_CLONE -# define MY_CXT_CLONE NOOP -#endif +/* --- Compatibility wrappers ---------------------------------------------- */ -#if defined(OP_CHECK_MUTEX_LOCK) && defined(OP_CHECK_MUTEX_UNLOCK) -# define A_CHECK_MUTEX_LOCK OP_CHECK_MUTEX_LOCK -# define A_CHECK_MUTEX_UNLOCK OP_CHECK_MUTEX_UNLOCK -#else -# define A_CHECK_MUTEX_LOCK OP_REFCNT_LOCK -# define A_CHECK_MUTEX_UNLOCK OP_REFCNT_UNLOCK +#ifndef HvNAME_get +# define HvNAME_get(H) HvNAME(H) #endif -typedef OP *(*a_ck_t)(pTHX_ OP *); - -#ifdef wrap_op_checker - -# define a_ck_replace(T, NC, OCP) wrap_op_checker((T), (NC), (OCP)) - -#else - -static void a_ck_replace(pTHX_ OPCODE type, a_ck_t new_ck, a_ck_t *old_ck_p) { -#define a_ck_replace(T, NC, OCP) a_ck_replace(aTHX_ (T), (NC), (OCP)) - A_CHECK_MUTEX_LOCK; - if (!*old_ck_p) { - *old_ck_p = PL_check[type]; - PL_check[type] = new_ck; - } - A_CHECK_MUTEX_UNLOCK; -} - +#ifndef HvNAMELEN_get +# define HvNAMELEN_get(H) strlen(HvNAME_get(H)) #endif -static void a_ck_restore(pTHX_ OPCODE type, a_ck_t *old_ck_p) { -#define a_ck_restore(T, OCP) a_ck_restore(aTHX_ (T), (OCP)) - A_CHECK_MUTEX_LOCK; - if (*old_ck_p) { - PL_check[type] = *old_ck_p; - *old_ck_p = 0; - } - A_CHECK_MUTEX_UNLOCK; -} - -/* --- Helpers ------------------------------------------------------------- */ - -/* ... Thread-safe hints ................................................... */ - -#if A_WORKAROUND_REQUIRE_PROPAGATION - -typedef struct { - U32 bits; - IV require_tag; -} a_hint_t; - -#define A_HINT_FREE(H) PerlMemShared_free(H) - -#if A_THREADSAFE - -#define PTABLE_NAME ptable_hints -#define PTABLE_VAL_FREE(V) A_HINT_FREE(V) - -#define pPTBL pTHX -#define pPTBL_ pTHX_ -#define aPTBL aTHX -#define aPTBL_ aTHX_ - -#include "ptable.h" - -#define ptable_hints_store(T, K, V) ptable_hints_store(aTHX_ (T), (K), (V)) -#define ptable_hints_free(T) ptable_hints_free(aTHX_ (T)) - -#endif /* A_THREADSAFE */ - -#endif /* A_WORKAROUND_REQUIRE_PROPAGATION */ - -#define PTABLE_NAME ptable_seen -#define PTABLE_VAL_FREE(V) NOOP - -#include "ptable.h" - -/* PerlMemShared_free() needs the [ap]PTBLMS_? default values */ -#define ptable_seen_store(T, K, V) ptable_seen_store(aPTBLMS_ (T), (K), (V)) -#define ptable_seen_clear(T) ptable_seen_clear(aPTBLMS_ (T)) -#define ptable_seen_free(T) ptable_seen_free(aPTBLMS_ (T)) - -#define MY_CXT_KEY __PACKAGE__ "::_guts" XS_VERSION - -typedef struct { -#if A_THREADSAFE && A_WORKAROUND_REQUIRE_PROPAGATION - ptable *tbl; /* It really is a ptable_hints */ - tTHX owner; -#endif /* A_THREADSAFE && A_WORKAROUND_REQUIRE_PROPAGATION */ - ptable *seen; /* It really is a ptable_seen */ -} my_cxt_t; - -START_MY_CXT - -#if A_THREADSAFE - -#if A_WORKAROUND_REQUIRE_PROPAGATION - -typedef struct { - ptable *tbl; -#if A_HAS_PERL(5, 13, 2) - CLONE_PARAMS *params; -#else - CLONE_PARAMS params; -#endif -} a_ptable_clone_ud; - -#if A_HAS_PERL(5, 13, 2) -# define a_ptable_clone_ud_init(U, T, O) \ - (U).tbl = (T); \ - (U).params = Perl_clone_params_new((O), aTHX) -# define a_ptable_clone_ud_deinit(U) Perl_clone_params_del((U).params) -# define a_dup_inc(S, U) SvREFCNT_inc(sv_dup((S), (U)->params)) -#else -# define a_ptable_clone_ud_init(U, T, O) \ - (U).tbl = (T); \ - (U).params.stashes = newAV(); \ - (U).params.flags = 0; \ - (U).params.proto_perl = (O) -# define a_ptable_clone_ud_deinit(U) SvREFCNT_dec((U).params.stashes) -# define a_dup_inc(S, U) SvREFCNT_inc(sv_dup((S), &((U)->params))) +#ifndef A_HAS_MULTIDEREF +# define A_HAS_MULTIDEREF XSH_HAS_PERL(5, 21, 7) #endif -static void a_ptable_clone(pTHX_ ptable_ent *ent, void *ud_) { - a_ptable_clone_ud *ud = ud_; - a_hint_t *h1 = ent->val; - a_hint_t *h2; - - h2 = PerlMemShared_malloc(sizeof *h2); - h2->bits = h1->bits; - h2->require_tag = PTR2IV(a_dup_inc(INT2PTR(SV *, h1->require_tag), ud)); - - ptable_hints_store(ud->tbl, ent->key, h2); -} - -#endif /* A_WORKAROUND_REQUIRE_PROPAGATION */ - -static void a_thread_cleanup(pTHX_ void *ud) { - dMY_CXT; - -#if A_WORKAROUND_REQUIRE_PROPAGATION - ptable_hints_free(MY_CXT.tbl); - MY_CXT.tbl = NULL; -#endif /* A_WORKAROUND_REQUIRE_PROPAGATION */ - ptable_seen_free(MY_CXT.seen); - MY_CXT.seen = NULL; -} - -static int a_endav_free(pTHX_ SV *sv, MAGIC *mg) { - SAVEDESTRUCTOR_X(a_thread_cleanup, NULL); - - return 0; -} - -static MGVTBL a_endav_vtbl = { - 0, - 0, - 0, - 0, - a_endav_free -#if MGf_COPY - , 0 -#endif -#if MGf_DUP - , 0 +#ifndef A_HAS_SCALARKEYS_OPT +# define A_HAS_SCALARKEYS_OPT XSH_HAS_PERL(5, 27, 3) #endif -#if MGf_LOCAL - , 0 -#endif -}; - -#endif /* A_THREADSAFE */ -#if A_WORKAROUND_REQUIRE_PROPAGATION +/* ... Our vivify_ref() .................................................... */ -static IV a_require_tag(pTHX) { -#define a_require_tag() a_require_tag(aTHX) - const CV *cv, *outside; +/* Perl_vivify_ref() is not exported, so we have to reimplement it. */ - cv = PL_compcv; +#if A_HAS_MULTIDEREF - if (!cv) { - /* If for some reason the pragma is operational at run-time, try to discover - * the current cv in use. */ - const PERL_SI *si; +static SV *a_vivify_ref(pTHX_ SV *sv, int to_hash) { +#define a_vivify_ref(S, TH) a_vivify_ref(aTHX_ (S), (TH)) + SvGETMAGIC(sv); - for (si = PL_curstackinfo; si; si = si->si_prev) { - I32 cxix; + if (!SvOK(sv)) { + SV *val; - for (cxix = si->si_cxix; cxix >= 0; --cxix) { - const PERL_CONTEXT *cx = si->si_cxstack + cxix; + if (SvREADONLY(sv)) + Perl_croak_no_modify(); - switch (CxTYPE(cx)) { - case CXt_SUB: - case CXt_FORMAT: - /* The propagation workaround is only needed up to 5.10.0 and at that - * time format and sub contexts were still identical. And even later the - * cv members offsets should have been kept the same. */ - cv = cx->blk_sub.cv; - goto get_enclosing_cv; - case CXt_EVAL: - cv = cx->blk_eval.cv; - goto get_enclosing_cv; - default: - break; - } - } + /* Inlined prepare_SV_for_RV() */ + if (SvTYPE(sv) < SVt_PV && SvTYPE(sv) != SVt_IV) { + sv_upgrade(sv, SVt_IV); + } else if (SvTYPE(sv) >= SVt_PV) { + SvPV_free(sv); + SvLEN_set(sv, 0); + SvCUR_set(sv, 0); } - cv = PL_main_cv; + val = to_hash ? MUTABLE_SV(newHV()) : MUTABLE_SV(newAV()); + SvRV_set(sv, val); + SvROK_on(sv); + SvSETMAGIC(sv); + SvGETMAGIC(sv); } -get_enclosing_cv: - for (outside = CvOUTSIDE(cv); outside; outside = CvOUTSIDE(cv)) - cv = outside; - - return PTR2IV(cv); -} - -static SV *a_tag(pTHX_ UV bits) { -#define a_tag(B) a_tag(aTHX_ (B)) - a_hint_t *h; -#if A_THREADSAFE - dMY_CXT; - - if (!MY_CXT.tbl) - return newSViv(0); -#endif /* A_THREADSAFE */ - - h = PerlMemShared_malloc(sizeof *h); - h->bits = bits; - h->require_tag = a_require_tag(); - -#if A_THREADSAFE - /* We only need for the key to be an unique tag for looking up the value later - * Allocated memory provides convenient unique identifiers, so that's why we - * use the hint as the key itself. */ - ptable_hints_store(MY_CXT.tbl, h, h); -#endif /* A_THREADSAFE */ - - return newSViv(PTR2IV(h)); -} - -static UV a_detag(pTHX_ const SV *hint) { -#define a_detag(H) a_detag(aTHX_ (H)) - a_hint_t *h; -#if A_THREADSAFE - dMY_CXT; - - if (!MY_CXT.tbl) - return 0; -#endif /* A_THREADSAFE */ - - if (!(hint && SvIOK(hint))) - return 0; - - h = INT2PTR(a_hint_t *, SvIVX(hint)); -#if A_THREADSAFE - h = ptable_fetch(MY_CXT.tbl, h); -#endif /* A_THREADSAFE */ - - if (a_require_tag() != h->require_tag) - return 0; + if (SvGMAGICAL(sv)) { + SV *msv = sv_newmortal(); + sv_setsv_nomg(msv, sv); + return msv; + } - return h->bits; + return sv; } -#else /* A_WORKAROUND_REQUIRE_PROPAGATION */ - -#define a_tag(B) newSVuv(B) -/* PVs fetched from the hints chain have their SvLEN set to zero, so get the UV - * from a copy. */ -#define a_detag(H) \ - ((H) \ - ? (SvIOK(H) \ - ? SvUVX(H) \ - : (SvPOK(H) \ - ? sv_2uv(SvLEN(H) ? (H) : sv_mortalcopy(H)) \ - : 0 \ - ) \ - ) \ - : 0) - -#endif /* !A_WORKAROUND_REQUIRE_PROPAGATION */ - -/* Used both for hints and op flags */ -#define A_HINT_STRICT 1 -#define A_HINT_WARN 2 -#define A_HINT_FETCH 4 -#define A_HINT_STORE 8 -#define A_HINT_EXISTS 16 -#define A_HINT_DELETE 32 -#define A_HINT_NOTIFY (A_HINT_STRICT|A_HINT_WARN) -#define A_HINT_DO (A_HINT_FETCH|A_HINT_STORE|A_HINT_EXISTS|A_HINT_DELETE) -#define A_HINT_MASK (A_HINT_NOTIFY|A_HINT_DO) - -/* Only used in op flags */ -#define A_HINT_ROOT 64 -#define A_HINT_DEREF 128 - -static U32 a_hash = 0; - -static UV a_hint(pTHX) { -#define a_hint() a_hint(aTHX) - SV *hint; -#ifdef cop_hints_fetch_pvn - hint = cop_hints_fetch_pvn(PL_curcop, __PACKAGE__, __PACKAGE_LEN__, a_hash, 0); -#elif A_HAS_PERL(5, 9, 5) - hint = Perl_refcounted_he_fetch(aTHX_ PL_curcop->cop_hints_hash, - NULL, - __PACKAGE__, __PACKAGE_LEN__, - 0, - a_hash); -#else - SV **val = hv_fetch(GvHV(PL_hintgv), __PACKAGE__, __PACKAGE_LEN__, 0); - if (!val) - return 0; - hint = *val; -#endif - return a_detag(hint); -} +#endif /* A_HAS_MULTIDEREF */ -/* ... op => info map ...................................................... */ +/* --- op => info map ------------------------------------------------------ */ typedef struct { OP *(*old_pp)(pTHX); @@ -420,14 +117,17 @@ typedef struct { UV flags; } a_op_info; -#define PTABLE_NAME ptable_map -#define PTABLE_VAL_FREE(V) PerlMemShared_free(V) +#define PTABLE_NAME ptable_map +#define PTABLE_VAL_FREE(V) PerlMemShared_free(V) +#define PTABLE_VAL_NEED_CONTEXT 0 +#define PTABLE_NEED_DELETE 1 +#define PTABLE_NEED_WALK 0 -#include "ptable.h" +#include "xsh/ptable.h" -/* PerlMemShared_free() needs the [ap]PTBLMS_? default values */ -#define ptable_map_store(T, K, V) ptable_map_store(aPTBLMS_ (T), (K), (V)) -#define ptable_map_delete(T, K) ptable_map_delete(aPTBLMS_ (T), (K)) +#define ptable_map_store(T, K, V) ptable_map_store(aPMS_ (T), (K), (V)) +#define ptable_map_delete(T, K) ptable_map_delete(aPMS_ (T), (K)) +#define ptable_map_free(T) ptable_map_free(aPMS_ (T)) static ptable *a_op_map = NULL; @@ -437,13 +137,10 @@ static ptable *a_op_map = NULL; static perl_mutex a_op_map_mutex; -#define A_LOCK(M) MUTEX_LOCK(M) -#define A_UNLOCK(M) MUTEX_UNLOCK(M) - static const a_op_info *a_map_fetch(const OP *o, a_op_info *oi) { const a_op_info *val; - A_LOCK(&a_op_map_mutex); + XSH_LOCK(&a_op_map_mutex); val = ptable_fetch(a_op_map, o); if (val) { @@ -451,7 +148,7 @@ static const a_op_info *a_map_fetch(const OP *o, a_op_info *oi) { val = oi; } - A_UNLOCK(&a_op_map_mutex); + XSH_UNLOCK(&a_op_map_mutex); return val; } @@ -462,15 +159,12 @@ static const a_op_info *a_map_fetch(const OP *o, a_op_info *oi) { #define dA_MAP_THX dNOOP -#define A_LOCK(M) NOOP -#define A_UNLOCK(M) NOOP - #define a_map_fetch(O) ptable_fetch(a_op_map, (O)) #endif /* !USE_ITHREADS */ -static const a_op_info *a_map_store_locked(pPTBLMS_ const OP *o, OP *(*old_pp)(pTHX), void *next, UV flags) { -#define a_map_store_locked(O, PP, N, F) a_map_store_locked(aPTBLMS_ (O), (PP), (N), (F)) +static const a_op_info *a_map_store_locked(pPMS_ const OP *o, OP *(*old_pp)(pTHX), void *next, UV flags) { +#define a_map_store_locked(O, PP, N, F) a_map_store_locked(aPMS_ (O), (PP), (N), (F)) a_op_info *oi; if (!(oi = ptable_fetch(a_op_map, o))) { @@ -485,22 +179,22 @@ static const a_op_info *a_map_store_locked(pPTBLMS_ const OP *o, OP *(*old_pp)(p return oi; } -static void a_map_store(pPTBLMS_ const OP *o, OP *(*old_pp)(pTHX), void *next, UV flags) { -#define a_map_store(O, PP, N, F) a_map_store(aPTBLMS_ (O), (PP), (N), (F)) - A_LOCK(&a_op_map_mutex); +static void a_map_store(pTHX_ const OP *o, OP *(*old_pp)(pTHX), void *next, UV flags) { +#define a_map_store(O, PP, N, F) a_map_store(aTHX_ (O), (PP), (N), (F)) + XSH_LOCK(&a_op_map_mutex); a_map_store_locked(o, old_pp, next, flags); - A_UNLOCK(&a_op_map_mutex); + XSH_UNLOCK(&a_op_map_mutex); } static void a_map_delete(pTHX_ const OP *o) { #define a_map_delete(O) a_map_delete(aTHX_ (O)) - A_LOCK(&a_op_map_mutex); + XSH_LOCK(&a_op_map_mutex); ptable_map_delete(a_op_map, o); - A_UNLOCK(&a_op_map_mutex); + XSH_UNLOCK(&a_op_map_mutex); } static const OP *a_map_descend(const OP *o) { @@ -518,13 +212,13 @@ static const OP *a_map_descend(const OP *o) { return NULL; } -static void a_map_store_root(pPTBLMS_ const OP *root, OP *(*old_pp)(pTHX), UV flags) { -#define a_map_store_root(R, PP, F) a_map_store_root(aPTBLMS_ (R), (PP), (F)) +static void a_map_store_root(pTHX_ const OP *root, OP *(*old_pp)(pTHX), UV flags) { +#define a_map_store_root(R, PP, F) a_map_store_root(aTHX_ (R), (PP), (F)) const a_op_info *roi; a_op_info *oi; const OP *o = root; - A_LOCK(&a_op_map_mutex); + XSH_LOCK(&a_op_map_mutex); roi = a_map_store_locked(o, old_pp, (OP *) root, flags | A_HINT_ROOT); @@ -539,38 +233,37 @@ static void a_map_store_root(pPTBLMS_ const OP *root, OP *(*old_pp)(pTHX), UV fl } } - A_UNLOCK(&a_op_map_mutex); + XSH_UNLOCK(&a_op_map_mutex); return; } -static void a_map_update_flags_topdown(const OP *root, UV flags) { +static void a_map_update_flags_topdown(const OP *root, UV mask, UV flags) { a_op_info *oi; const OP *o = root; - A_LOCK(&a_op_map_mutex); + XSH_LOCK(&a_op_map_mutex); - flags &= ~A_HINT_ROOT; + mask |= A_HINT_ROOT; + flags &= ~mask; do { if ((oi = ptable_fetch(a_op_map, o))) - oi->flags = (oi->flags & A_HINT_ROOT) | flags; + oi->flags = (oi->flags & mask) | flags; if (!(o->op_flags & OPf_KIDS)) break; o = a_map_descend(o); } while (o); - A_UNLOCK(&a_op_map_mutex); + XSH_UNLOCK(&a_op_map_mutex); return; } -#define a_map_cancel(R) a_map_update_flags_topdown((R), 0) - static void a_map_update_flags_bottomup(const OP *o, UV flags, UV rflags) { a_op_info *oi; - A_LOCK(&a_op_map_mutex); + XSH_LOCK(&a_op_map_mutex); flags &= ~A_HINT_ROOT; rflags |= A_HINT_ROOT; @@ -582,7 +275,7 @@ static void a_map_update_flags_bottomup(const OP *o, UV flags, UV rflags) { } oi->flags = rflags; - A_UNLOCK(&a_op_map_mutex); + XSH_UNLOCK(&a_op_map_mutex); return; } @@ -607,8 +300,10 @@ static UV a_map_resolve(const OP *o, const a_op_info *oi) { if (root->op_flags & OPf_MOD) { if (rflags & A_HINT_STORE) flags = (A_HINT_STORE|A_HINT_DEREF); - } else if (rflags & A_HINT_FETCH) - flags = (A_HINT_FETCH|A_HINT_DEREF); + } else { + if (rflags & (A_HINT_FETCH|A_HINT_KEYS|A_HINT_VALUES)) + flags = (rflags|A_HINT_DEREF); + } if (!flags) { cancel: @@ -717,6 +412,29 @@ static OP *a_pp_rv2hv(pTHX) { return oi->old_pp(aTHX); } +#if A_HAS_SCALARKEYS_OPT + +static OP *a_pp_rv2hv_dokeys(pTHX) { + dA_MAP_THX; + const a_op_info *oi; + dSP; + + oi = a_map_fetch(PL_op); + + if (oi->flags & A_HINT_KEYS) { + if (a_undef(TOPs)) { + dTARGET; + (void) POPs; + PUSHi(0); + RETURN; + } + } + + return oi->old_pp(aTHX); +} + +#endif + /* ... pp_deref (aelem,helem,rv2sv,padsv) .................................. */ static void a_cannot_vivify(pTHX_ UV flags) { @@ -800,6 +518,40 @@ static OP *a_pp_root_binop(pTHX) { /* ... pp_multideref ....................................................... */ +/* This pp replacement is actually only called for topmost exists/delete ops, + * because we hijack the [ah]elem check functions and this disables the + * optimization for lvalue and rvalue dereferencing. In particular, the + * OPf_MOD branches should never be covered. In the future, the multideref + * optimization might also be disabled for custom exists/delete check functions, + * which will make this section unnecessary. However, the code tries to be as + * general as possible in case I think of a way to reenable the multideref + * optimization even when this module is in use. */ + +static UV a_do_multideref(const OP *o, UV flags) { + UV isexdel, other_flags; + + XSH_ASSERT(o->op_type == OP_MULTIDEREF); + + other_flags = flags & ~A_HINT_DO; + + isexdel = o->op_private & (OPpMULTIDEREF_EXISTS|OPpMULTIDEREF_DELETE); + if (isexdel) { + if (isexdel & OPpMULTIDEREF_EXISTS) { + flags &= A_HINT_EXISTS; + } else { + flags &= A_HINT_DELETE; + } + } else { + if (o->op_flags & OPf_MOD) { + flags &= A_HINT_STORE; + } else { + flags &= A_HINT_FETCH; + } + } + + return flags ? (flags | other_flags) : 0; +} + static SV *a_do_fake_pp(pTHX_ OP *op) { #define a_do_fake_pp(O) a_do_fake_pp(aTHX_ (O)) { @@ -841,7 +593,7 @@ static SV *a_do_fake_pp_unop_arg1(pTHX_ U32 type, U32 flags, SV *arg) { PUSHs(arg); PUTBACK; - return a_do_fake_pp(&unop); + return a_do_fake_pp((OP *) &unop); } static SV *a_do_fake_pp_unop_arg2(pTHX_ U32 type, U32 flags, SV *arg1, SV *arg2) { @@ -856,46 +608,40 @@ static SV *a_do_fake_pp_unop_arg2(pTHX_ U32 type, U32 flags, SV *arg1, SV *arg2) PUSHs(arg2); PUTBACK; - return a_do_fake_pp(&unop); + return a_do_fake_pp((OP *) &unop); } -#define a_do_pp_rv2av(R) a_do_fake_pp_unop_arg1(OP_RV2AV, OPf_REF, (R)) -#define a_do_pp_aexists(A, I) a_do_fake_pp_unop_arg2(OP_EXISTS, OPf_SPECIAL, (A), (I)) -#define a_do_pp_adelete(A, I) a_do_fake_pp_unop_arg2(OP_DELETE, OPf_SPECIAL, (A), (I)) -#define a_do_pp_aelem(A, I) a_do_fake_pp_unop_arg2(OP_AELEM, 0, (A), (I)) +#define a_do_pp_rv2av(R) a_do_fake_pp_unop_arg1(OP_RV2AV, OPf_REF, (R)) +#define a_do_pp_afetch(A, I) a_do_fake_pp_unop_arg2(OP_AELEM, 0, (A), (I)) +#define a_do_pp_afetch_lv(A, I) a_do_fake_pp_unop_arg2(OP_AELEM, OPf_MOD, (A), (I)) +#define a_do_pp_aexists(A, I) a_do_fake_pp_unop_arg2(OP_EXISTS, OPf_SPECIAL, (A), (I)) +#define a_do_pp_adelete(A, I) a_do_fake_pp_unop_arg2(OP_DELETE, OPf_SPECIAL, (A), (I)) -#define a_do_pp_rv2hv(R) a_do_fake_pp_unop_arg1(OP_RV2HV, OPf_REF, (R)) -#define a_do_pp_hexists(H, K) a_do_fake_pp_unop_arg2(OP_EXISTS, 0, (H), (K)) -#define a_do_pp_hdelete(H, K) a_do_fake_pp_unop_arg2(OP_DELETE, 0, (H), (K)) -#define a_do_pp_helem(H, K) a_do_fake_pp_unop_arg2(OP_HELEM, 0, (H), (K)) +#define a_do_pp_rv2hv(R) a_do_fake_pp_unop_arg1(OP_RV2HV, OPf_REF, (R)) +#define a_do_pp_hfetch(H, K) a_do_fake_pp_unop_arg2(OP_HELEM, 0, (H), (K)) +#define a_do_pp_hfetch_lv(H, K) a_do_fake_pp_unop_arg2(OP_HELEM, OPf_MOD, (H), (K)) +#define a_do_pp_hexists(H, K) a_do_fake_pp_unop_arg2(OP_EXISTS, 0, (H), (K)) +#define a_do_pp_hdelete(H, K) a_do_fake_pp_unop_arg2(OP_DELETE, 0, (H), (K)) static OP *a_pp_multideref(pTHX) { - UNOP_AUX_item *items = cUNOP_AUXx(PL_op)->op_aux; - UV actions = items->uv; - U8 isexdel = PL_op->op_private & (OPpMULTIDEREF_EXISTS|OPpMULTIDEREF_DELETE); - UV flags = 0; - SV *sv = NULL; + UNOP_AUX_item *items; + UV actions; + UV flags = 0; + SV *sv = NULL; dSP; { dA_MAP_THX; const a_op_info *oi = a_map_fetch(PL_op); - flags = oi->flags; - if (isexdel) { - if (isexdel & OPpMULTIDEREF_EXISTS) { - if (flags & A_HINT_EXISTS) - goto hijack; - } else if (flags & A_HINT_DELETE) { - assert(isexdel & OPpMULTIDEREF_DELETE); - goto hijack; - } - } else if (flags & A_HINT_FETCH) { - goto hijack; - } - return oi->old_pp(aTHX); + XSH_ASSERT(oi); + flags = a_do_multideref(PL_op, oi->flags); + if (!flags) + return oi->old_pp(aTHX); } -hijack: + items = cUNOP_AUXx(PL_op)->op_aux; + actions = items->uv; + PL_multideref_pc = items; while (1) { @@ -910,7 +656,7 @@ hijack: goto do_AV_aelem; case MDEREF_AV_gvav_aelem: /* $pkg[...] */ sv = UNOP_AUX_item_sv(++items); - assert(isGV_with_GP(sv)); + XSH_ASSERT(isGV_with_GP(sv)); sv = (SV *) GvAVn((GV *) sv); if (a_undef(sv)) goto ret_undef; @@ -922,25 +668,25 @@ hijack: goto do_AV_rv2av_aelem; case MDEREF_AV_gvsv_vivify_rv2av_aelem: /* $pkg->[...] */ sv = UNOP_AUX_item_sv(++items); - assert(isGV_with_GP(sv)); + XSH_ASSERT(isGV_with_GP(sv)); sv = GvSVn((GV *) sv); if (a_undef(sv)) goto ret_undef; goto do_AV_vivify_rv2av_aelem; case MDEREF_AV_padsv_vivify_rv2av_aelem: /* $lex->[...] */ sv = PAD_SVl((++items)->pad_offset); - if (a_undef(sv)) - goto ret_undef; /* FALLTHROUGH */ case MDEREF_AV_vivify_rv2av_aelem: /* vivify, ->[...] */ + if (a_undef(sv)) + goto ret_undef; do_AV_vivify_rv2av_aelem: - sv = Perl_vivify_ref(aTHX_ sv, OPpDEREF_AV); + sv = a_vivify_ref(sv, 0); do_AV_rv2av_aelem: sv = a_do_pp_rv2av(sv); do_AV_aelem: { SV *esv; - assert(SvTYPE(sv) == SVt_PVAV); + XSH_ASSERT(SvTYPE(sv) == SVt_PVAV); switch (actions & MDEREF_INDEX_MASK) { case MDEREF_INDEX_none: goto finish; @@ -952,7 +698,7 @@ do_AV_aelem: goto check_elem; case MDEREF_INDEX_gvsv: esv = UNOP_AUX_item_sv(++items); - assert(isGV_with_GP(esv)); + XSH_ASSERT(isGV_with_GP(esv)); esv = GvSVn((GV *) esv); check_elem: if (UNLIKELY(SvROK(esv) && !SvGAMAGIC(esv) && ckWARN(WARN_MISC))) @@ -963,18 +709,24 @@ check_elem: } PL_multideref_pc = items; if (actions & MDEREF_FLAG_last) { - if (isexdel) { - if (isexdel & OPpMULTIDEREF_EXISTS) + switch (flags & A_HINT_DO) { + case A_HINT_FETCH: + sv = a_do_pp_afetch(sv, esv); + break; + case A_HINT_STORE: + sv = a_do_pp_afetch_lv(sv, esv); + break; + case A_HINT_EXISTS: sv = a_do_pp_aexists(sv, esv); - else + break; + case A_HINT_DELETE: sv = a_do_pp_adelete(sv, esv); - } else { - sv = a_do_pp_aelem(sv, esv); + break; } goto finish; - } else { - sv = a_do_pp_aelem(sv, esv); } + sv = a_do_pp_afetch(sv, esv); + break; } case MDEREF_HV_padhv_helem: /* $lex{...} */ sv = PAD_SVl((++items)->pad_offset); @@ -983,7 +735,7 @@ check_elem: goto do_HV_helem; case MDEREF_HV_gvhv_helem: /* $pkg{...} */ sv = UNOP_AUX_item_sv(++items); - assert(isGV_with_GP(sv)); + XSH_ASSERT(isGV_with_GP(sv)); sv = (SV *) GvHVn((GV *) sv); if (a_undef(sv)) goto ret_undef; @@ -995,25 +747,25 @@ check_elem: goto do_HV_rv2hv_helem; case MDEREF_HV_gvsv_vivify_rv2hv_helem: /* $pkg->{...} */ sv = UNOP_AUX_item_sv(++items); - assert(isGV_with_GP(sv)); + XSH_ASSERT(isGV_with_GP(sv)); sv = GvSVn((GV *) sv); if (a_undef(sv)) goto ret_undef; goto do_HV_vivify_rv2hv_helem; case MDEREF_HV_padsv_vivify_rv2hv_helem: /* $lex->{...} */ sv = PAD_SVl((++items)->pad_offset); - if (a_undef(sv)) - goto ret_undef; /* FALLTHROUGH */ case MDEREF_HV_vivify_rv2hv_helem: /* vivify, ->{...} */ + if (a_undef(sv)) + goto ret_undef; do_HV_vivify_rv2hv_helem: - sv = Perl_vivify_ref(aTHX_ sv, OPpDEREF_HV); + sv = a_vivify_ref(sv, 1); do_HV_rv2hv_helem: sv = a_do_pp_rv2hv(sv); do_HV_helem: { SV *key; - assert(SvTYPE(sv) == SVt_PVHV); + XSH_ASSERT(SvTYPE(sv) == SVt_PVHV); switch (actions & MDEREF_INDEX_MASK) { case MDEREF_INDEX_none: goto finish; @@ -1025,24 +777,32 @@ do_HV_helem: break; case MDEREF_INDEX_gvsv: key = UNOP_AUX_item_sv(++items); - assert(isGV_with_GP(key)); + XSH_ASSERT(isGV_with_GP(key)); key = GvSVn((GV *) key); break; } PL_multideref_pc = items; if (actions & MDEREF_FLAG_last) { - if (isexdel) { - if (isexdel & OPpMULTIDEREF_EXISTS) + switch (flags & A_HINT_DO) { + case A_HINT_FETCH: + sv = a_do_pp_hfetch(sv, key); + break; + case A_HINT_STORE: + sv = a_do_pp_hfetch_lv(sv, key); + break; + case A_HINT_EXISTS: sv = a_do_pp_hexists(sv, key); - else + break; + case A_HINT_DELETE: sv = a_do_pp_hdelete(sv, key); - } else { - sv = a_do_pp_helem(sv, key); + break; + default: + break; } goto finish; - } else { - sv = a_do_pp_helem(sv, key); } + sv = a_do_pp_hfetch(sv, key); + break; } } @@ -1052,7 +812,7 @@ do_HV_helem: ret_undef: if (flags & (A_HINT_NOTIFY|A_HINT_STORE)) a_cannot_vivify(flags); - if (isexdel & OPpMULTIDEREF_EXISTS) + if (flags & A_HINT_EXISTS) sv = &PL_sv_no; else sv = &PL_sv_undef; @@ -1153,14 +913,19 @@ static OP *a_ck_deref(pTHX_ OP *o) { } o = old_ck(aTHX_ o); - if (hint & A_HINT_DO) { #if A_HAS_MULTIDEREF - if (old_ck == a_old_ck_rv2sv && o->op_flags & OPf_KIDS) { - OP *kid = cUNOPo->op_first; - if (kid && kid->op_type == OP_GV) + if (old_ck == a_old_ck_rv2sv && o->op_flags & OPf_KIDS) { + OP *kid = cUNOPo->op_first; + if (kid && kid->op_type == OP_GV) { + if (hint & A_HINT_DO) a_map_store(kid, kid->op_ppaddr, NULL, hint); + else + a_map_delete(kid); } + } #endif + + if (hint & A_HINT_DO) { a_map_store_root(o, o->op_ppaddr, hint); o->op_ppaddr = a_pp_deref; } else @@ -1250,7 +1015,7 @@ 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; + int enabled = 0; UV hint = a_hint(); switch (o->op_type) { @@ -1267,23 +1032,36 @@ static OP *a_ck_root(pTHX_ OP *o) { case OP_KEYS: old_ck = a_old_ck_keys; new_pp = a_pp_root_unop; - enabled = hint & A_HINT_FETCH; + enabled = hint & A_HINT_KEYS; break; case OP_VALUES: old_ck = a_old_ck_values; new_pp = a_pp_root_unop; - enabled = hint & A_HINT_FETCH; + enabled = hint & A_HINT_VALUES; break; } o = old_ck(aTHX_ o); if (hint & A_HINT_DO) { if (enabled) { - a_map_update_flags_topdown(o, hint | A_HINT_DEREF); +#if A_HAS_SCALARKEYS_OPT + if ((enabled == A_HINT_KEYS) && (o->op_flags & OPf_KIDS)) { + OP *kid = cUNOPo->op_first; + if (kid->op_type == OP_RV2HV) { + dA_MAP_THX; + const a_op_info *koi = a_map_fetch(kid); + a_map_store(kid, koi ? koi->old_pp : kid->op_ppaddr, NULL, + hint | A_HINT_SECOND); + if (!koi) + kid->op_ppaddr = a_pp_rv2hv; + } + } +#endif + a_map_update_flags_topdown(o, A_HINT_SECOND, hint | A_HINT_DEREF); a_map_store_root(o, o->op_ppaddr, hint); o->op_ppaddr = new_pp; } else { - a_map_cancel(o); + a_map_update_flags_topdown(o, 0, 0); } } else a_map_delete(o); @@ -1291,36 +1069,18 @@ static OP *a_ck_root(pTHX_ OP *o) { return o; } -/* ... Our peephole optimizer .............................................. */ - -static peep_t a_old_peep = 0; /* This is actually the rpeep past 5.13.5 */ +/* --- Our peephole optimizer ---------------------------------------------- */ -static void a_peep_rec(pTHX_ OP *o, ptable *seen); - -static void a_peep_rec(pTHX_ OP *o, ptable *seen) { -#define a_peep_rec(O) a_peep_rec(aTHX_ (O), seen) +static void xsh_peep_rec(pTHX_ OP *o, ptable *seen) { for (; o; o = o->op_next) { dA_MAP_THX; const a_op_info *oi = NULL; UV flags = 0; -#if !A_HAS_RPEEP - if (ptable_fetch(seen, o)) + if (xsh_peep_seen(o, seen)) break; - ptable_seen_store(seen, o, o); -#endif switch (o->op_type) { -#if A_HAS_RPEEP - case OP_NEXTSTATE: - case OP_DBSTATE: - case OP_STUB: - case OP_UNSTACK: - if (ptable_fetch(seen, o)) - return; - ptable_seen_store(seen, o, o); - break; -#endif case OP_PADSV: if (o->op_ppaddr != a_pp_deref) { oi = a_map_fetch(o); @@ -1352,191 +1112,138 @@ static void a_peep_rec(pTHX_ OP *o, ptable *seen) { o->op_ppaddr = oi->old_pp; break; case OP_RV2AV: - case OP_RV2HV: - if ( o->op_ppaddr != a_pp_rv2av - && o->op_ppaddr != a_pp_rv2hv - && o->op_ppaddr != a_pp_rv2hv_simple) + if (o->op_ppaddr != a_pp_rv2av) break; oi = a_map_fetch(o); if (!oi) break; if (!(oi->flags & A_HINT_DEREF)) - o->op_ppaddr = oi->old_pp; + o->op_ppaddr = oi->old_pp; + break; + case OP_RV2HV: + if (o->op_ppaddr != a_pp_rv2hv && o->op_ppaddr != a_pp_rv2hv_simple) + break; + oi = a_map_fetch(o); + if (!oi) + break; + if (!(oi->flags & A_HINT_DEREF)) { + o->op_ppaddr = oi->old_pp; + break; + } +#if A_HAS_SCALARKEYS_OPT + flags = oi->flags; + if ((flags & A_HINT_KEYS) && (flags & A_HINT_SECOND)) { + U8 want = o->op_flags & OPf_WANT; + if (want == OPf_WANT_VOID || want == OPf_WANT_SCALAR) + o->op_ppaddr = a_pp_rv2hv_dokeys; + else if (oi->old_pp == a_pp_rv2hv || oi->old_pp == a_pp_rv2hv_simple) + o->op_ppaddr = oi->old_pp; + } +#endif break; #if A_HAS_MULTIDEREF case OP_MULTIDEREF: if (o->op_ppaddr != a_pp_multideref) { - UV isexdel; oi = a_map_fetch(cUNOPo->op_first); if (!oi) break; - flags = oi->flags; - isexdel = o->op_private & (OPpMULTIDEREF_EXISTS|OPpMULTIDEREF_DELETE); - if ( ((flags & A_HINT_FETCH) && !isexdel) - || (flags & A_HINT_EXISTS) && (isexdel & OPpMULTIDEREF_EXISTS) - || (flags & A_HINT_DELETE) && (isexdel & OPpMULTIDEREF_DELETE)) { - a_map_store(o, o->op_ppaddr, oi->next, oi->flags); + flags = oi->flags; + if (a_do_multideref(o, flags)) { + a_map_store_root(o, o->op_ppaddr, flags & ~A_HINT_DEREF); o->op_ppaddr = a_pp_multideref; } } break; #endif -#if !A_HAS_RPEEP - case OP_MAPWHILE: - case OP_GREPWHILE: - case OP_AND: - case OP_OR: - case OP_ANDASSIGN: - case OP_ORASSIGN: - case OP_COND_EXPR: - case OP_RANGE: -# if A_HAS_PERL(5, 10, 0) - case OP_ONCE: - case OP_DOR: - case OP_DORASSIGN: -# endif - a_peep_rec(cLOGOPo->op_other); - break; - case OP_ENTERLOOP: - case OP_ENTERITER: - a_peep_rec(cLOOPo->op_redoop); - a_peep_rec(cLOOPo->op_nextop); - a_peep_rec(cLOOPo->op_lastop); - break; -# if A_HAS_PERL(5, 9, 5) - case OP_SUBST: - a_peep_rec(cPMOPo->op_pmstashstartu.op_pmreplstart); - break; -# else - case OP_QR: - case OP_MATCH: - case OP_SUBST: - a_peep_rec(cPMOPo->op_pmreplstart); - break; -# endif -#endif /* !A_HAS_RPEEP */ default: + xsh_peep_maybe_recurse(o, seen); break; } } } -static void a_peep(pTHX_ OP *o) { - dMY_CXT; - ptable *seen = MY_CXT.seen; - - a_old_peep(aTHX_ o); - - if (seen) { - ptable_seen_clear(seen); - a_peep_rec(o); - ptable_seen_clear(seen); - } -} - -/* --- Interpreter setup/teardown ------------------------------------------ */ - -static U32 a_initialized = 0; +/* --- Module setup/teardown ----------------------------------------------- */ -static void a_teardown(pTHX_ void *root) { +static void xsh_user_global_setup(pTHX) { + a_op_map = ptable_new(32); - if (!a_initialized) - return; - -#if A_MULTIPLICITY - if (aTHX != root) - return; +#ifdef USE_ITHREADS + MUTEX_INIT(&a_op_map_mutex); #endif - { - dMY_CXT; -# if A_THREADSAFE && A_WORKAROUND_REQUIRE_PROPAGATION - ptable_hints_free(MY_CXT.tbl); - MY_CXT.tbl = NULL; -# endif /* A_THREADSAFE && A_WORKAROUND_REQUIRE_PROPAGATION */ - ptable_seen_free(MY_CXT.seen); - MY_CXT.seen = NULL; - } + xsh_ck_replace(OP_PADANY, a_ck_padany, &a_old_ck_padany); + xsh_ck_replace(OP_PADSV, a_ck_padsv, &a_old_ck_padsv); - a_ck_restore(OP_PADANY, &a_old_ck_padany); - a_ck_restore(OP_PADSV, &a_old_ck_padsv); + xsh_ck_replace(OP_AELEM, a_ck_deref, &a_old_ck_aelem); + xsh_ck_replace(OP_HELEM, a_ck_deref, &a_old_ck_helem); + xsh_ck_replace(OP_RV2SV, a_ck_deref, &a_old_ck_rv2sv); - a_ck_restore(OP_AELEM, &a_old_ck_aelem); - a_ck_restore(OP_HELEM, &a_old_ck_helem); - a_ck_restore(OP_RV2SV, &a_old_ck_rv2sv); + xsh_ck_replace(OP_RV2AV, a_ck_rv2xv, &a_old_ck_rv2av); + xsh_ck_replace(OP_RV2HV, a_ck_rv2xv, &a_old_ck_rv2hv); - a_ck_restore(OP_RV2AV, &a_old_ck_rv2av); - a_ck_restore(OP_RV2HV, &a_old_ck_rv2hv); + xsh_ck_replace(OP_ASLICE, a_ck_xslice, &a_old_ck_aslice); + xsh_ck_replace(OP_HSLICE, a_ck_xslice, &a_old_ck_hslice); - a_ck_restore(OP_ASLICE, &a_old_ck_aslice); - a_ck_restore(OP_HSLICE, &a_old_ck_hslice); + xsh_ck_replace(OP_EXISTS, a_ck_root, &a_old_ck_exists); + xsh_ck_replace(OP_DELETE, a_ck_root, &a_old_ck_delete); + xsh_ck_replace(OP_KEYS, a_ck_root, &a_old_ck_keys); + xsh_ck_replace(OP_VALUES, a_ck_root, &a_old_ck_values); - a_ck_restore(OP_EXISTS, &a_old_ck_exists); - a_ck_restore(OP_DELETE, &a_old_ck_delete); - a_ck_restore(OP_KEYS, &a_old_ck_keys); - a_ck_restore(OP_VALUES, &a_old_ck_values); + return; +} -#if A_HAS_RPEEP - PL_rpeepp = a_old_peep; -#else - PL_peepp = a_old_peep; -#endif - a_old_peep = 0; +static void xsh_user_local_setup(pTHX) { + HV *stash; + + stash = gv_stashpvn(XSH_PACKAGE, XSH_PACKAGE_LEN, 1); + newCONSTSUB(stash, "A_HINT_STRICT", newSVuv(A_HINT_STRICT)); + newCONSTSUB(stash, "A_HINT_WARN", newSVuv(A_HINT_WARN)); + newCONSTSUB(stash, "A_HINT_FETCH", newSVuv(A_HINT_FETCH)); + newCONSTSUB(stash, "A_HINT_STORE", newSVuv(A_HINT_STORE)); + newCONSTSUB(stash, "A_HINT_KEYS", newSVuv(A_HINT_KEYS)); + newCONSTSUB(stash, "A_HINT_VALUES", newSVuv(A_HINT_VALUES)); + newCONSTSUB(stash, "A_HINT_EXISTS", newSVuv(A_HINT_EXISTS)); + newCONSTSUB(stash, "A_HINT_DELETE", newSVuv(A_HINT_DELETE)); + newCONSTSUB(stash, "A_HINT_MASK", newSVuv(A_HINT_MASK)); + newCONSTSUB(stash, "A_THREADSAFE", newSVuv(XSH_THREADSAFE)); + newCONSTSUB(stash, "A_FORKSAFE", newSVuv(XSH_FORKSAFE)); - a_initialized = 0; + return; } -static void a_setup(pTHX) { -#define a_setup() a_setup(aTHX) - if (a_initialized) - return; - - { - MY_CXT_INIT; -# if A_THREADSAFE && A_WORKAROUND_REQUIRE_PROPAGATION - MY_CXT.tbl = ptable_new(); - MY_CXT.owner = aTHX; -# endif /* A_THREADSAFE && A_WORKAROUND_REQUIRE_PROPAGATION */ - MY_CXT.seen = ptable_new(); - } +static void xsh_user_local_teardown(pTHX) { + return; +} - a_ck_replace(OP_PADANY, a_ck_padany, &a_old_ck_padany); - a_ck_replace(OP_PADSV, a_ck_padsv, &a_old_ck_padsv); +static void xsh_user_global_teardown(pTHX) { + xsh_ck_restore(OP_PADANY, &a_old_ck_padany); + xsh_ck_restore(OP_PADSV, &a_old_ck_padsv); - a_ck_replace(OP_AELEM, a_ck_deref, &a_old_ck_aelem); - a_ck_replace(OP_HELEM, a_ck_deref, &a_old_ck_helem); - a_ck_replace(OP_RV2SV, a_ck_deref, &a_old_ck_rv2sv); + xsh_ck_restore(OP_AELEM, &a_old_ck_aelem); + xsh_ck_restore(OP_HELEM, &a_old_ck_helem); + xsh_ck_restore(OP_RV2SV, &a_old_ck_rv2sv); - a_ck_replace(OP_RV2AV, a_ck_rv2xv, &a_old_ck_rv2av); - a_ck_replace(OP_RV2HV, a_ck_rv2xv, &a_old_ck_rv2hv); + xsh_ck_restore(OP_RV2AV, &a_old_ck_rv2av); + xsh_ck_restore(OP_RV2HV, &a_old_ck_rv2hv); - a_ck_replace(OP_ASLICE, a_ck_xslice, &a_old_ck_aslice); - a_ck_replace(OP_HSLICE, a_ck_xslice, &a_old_ck_hslice); + xsh_ck_restore(OP_ASLICE, &a_old_ck_aslice); + xsh_ck_restore(OP_HSLICE, &a_old_ck_hslice); - a_ck_replace(OP_EXISTS, a_ck_root, &a_old_ck_exists); - a_ck_replace(OP_DELETE, a_ck_root, &a_old_ck_delete); - a_ck_replace(OP_KEYS, a_ck_root, &a_old_ck_keys); - a_ck_replace(OP_VALUES, a_ck_root, &a_old_ck_values); + xsh_ck_restore(OP_EXISTS, &a_old_ck_exists); + xsh_ck_restore(OP_DELETE, &a_old_ck_delete); + xsh_ck_restore(OP_KEYS, &a_old_ck_keys); + xsh_ck_restore(OP_VALUES, &a_old_ck_values); -#if A_HAS_RPEEP - a_old_peep = PL_rpeepp; - PL_rpeepp = a_peep; -#else - a_old_peep = PL_peepp; - PL_peepp = a_peep; -#endif + ptable_map_free(a_op_map); + a_op_map = NULL; -#if A_MULTIPLICITY - call_atexit(a_teardown, aTHX); -#else - call_atexit(a_teardown, NULL); +#ifdef USE_ITHREADS + MUTEX_DESTROY(&a_op_map_mutex); #endif - a_initialized = 1; + return; } -static U32 a_booted = 0; - /* --- XS ------------------------------------------------------------------ */ MODULE = autovivification PACKAGE = autovivification @@ -1545,91 +1252,25 @@ PROTOTYPES: ENABLE BOOT: { - if (!a_booted++) { - HV *stash; - - a_op_map = ptable_new(); -#ifdef USE_ITHREADS - MUTEX_INIT(&a_op_map_mutex); -#endif - - PERL_HASH(a_hash, __PACKAGE__, __PACKAGE_LEN__); - - stash = gv_stashpvn(__PACKAGE__, __PACKAGE_LEN__, 1); - newCONSTSUB(stash, "A_HINT_STRICT", newSVuv(A_HINT_STRICT)); - newCONSTSUB(stash, "A_HINT_WARN", newSVuv(A_HINT_WARN)); - newCONSTSUB(stash, "A_HINT_FETCH", newSVuv(A_HINT_FETCH)); - newCONSTSUB(stash, "A_HINT_STORE", newSVuv(A_HINT_STORE)); - newCONSTSUB(stash, "A_HINT_EXISTS", newSVuv(A_HINT_EXISTS)); - newCONSTSUB(stash, "A_HINT_DELETE", newSVuv(A_HINT_DELETE)); - newCONSTSUB(stash, "A_HINT_MASK", newSVuv(A_HINT_MASK)); - newCONSTSUB(stash, "A_THREADSAFE", newSVuv(A_THREADSAFE)); - newCONSTSUB(stash, "A_FORKSAFE", newSVuv(A_FORKSAFE)); - } - - a_setup(); + xsh_setup(); } -#if A_THREADSAFE +#if XSH_THREADSAFE void CLONE(...) PROTOTYPE: DISABLE -PREINIT: -#if A_WORKAROUND_REQUIRE_PROPAGATION - ptable *t; -#endif - ptable *s; - GV *gv; -PPCODE: - { -#if A_WORKAROUND_REQUIRE_PROPAGATION - dMY_CXT; - { - a_ptable_clone_ud ud; - - t = ptable_new(); - a_ptable_clone_ud_init(ud, t, MY_CXT.owner); - ptable_walk(MY_CXT.tbl, a_ptable_clone, &ud); - a_ptable_clone_ud_deinit(ud); - } -#endif - s = ptable_new(); - } - { - MY_CXT_CLONE; -#if A_WORKAROUND_REQUIRE_PROPAGATION - MY_CXT.tbl = t; - MY_CXT.owner = aTHX; -#endif - MY_CXT.seen = s; - } - gv = gv_fetchpv(__PACKAGE__ "::_THREAD_CLEANUP", 0, SVt_PVCV); - if (gv) { - CV *cv = GvCV(gv); - if (!PL_endav) - PL_endav = newAV(); - SvREFCNT_inc(cv); - if (!av_store(PL_endav, av_len(PL_endav) + 1, (SV *) cv)) - SvREFCNT_dec(cv); - sv_magicext((SV *) PL_endav, NULL, PERL_MAGIC_ext, &a_endav_vtbl, NULL, 0); - } - XSRETURN(0); - -void -_THREAD_CLEANUP(...) -PROTOTYPE: DISABLE PPCODE: - a_thread_cleanup(aTHX_ NULL); + xsh_clone(); XSRETURN(0); -#endif /* A_THREADSAFE */ +#endif /* XSH_THREADSAFE */ SV * _tag(SV *hint) PROTOTYPE: $ CODE: - RETVAL = a_tag(SvOK(hint) ? SvUV(hint) : 0); + RETVAL = xsh_hints_tag(SvOK(hint) ? SvUV(hint) : 0); OUTPUT: RETVAL @@ -1639,6 +1280,6 @@ PROTOTYPE: $ CODE: if (!SvOK(tag)) XSRETURN_UNDEF; - RETVAL = newSVuv(a_detag(tag)); + RETVAL = newSVuv(xsh_hints_detag(tag)); OUTPUT: RETVAL