X-Git-Url: http://git.vpit.fr/?a=blobdiff_plain;f=autovivification.xs;h=34cd2da1d2f95747a38c518f1b58063fd2966d8d;hb=fdfce5eb8dd5546bb8cbcd946c68b6d72068feee;hp=852723b0e88abcb252ac4ef8e034d46aa047a4e3;hpb=424fd5125232dfd072e9dee9b04eb8560ed471bf;p=perl%2Fmodules%2Fautovivification.git diff --git a/autovivification.xs b/autovivification.xs index 852723b..34cd2da 100644 --- a/autovivification.xs +++ b/autovivification.xs @@ -6,404 +6,103 @@ #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 - -#ifndef HvNAMELEN_get -# define HvNAMELEN_get(H) strlen(HvNAME_get(H)) -#endif - -#define A_HAS_PERL(R, V, S) (PERL_REVISION > (R) || (PERL_REVISION == (R) && (PERL_VERSION > (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION >= (S)))))) - -#ifndef A_WORKAROUND_REQUIRE_PROPAGATION -# define A_WORKAROUND_REQUIRE_PROPAGATION !A_HAS_PERL(5, 10, 1) -#endif +#define XSH_PACKAGE "autovivification" -#ifndef A_HAS_RPEEP -# define A_HAS_RPEEP A_HAS_PERL(5, 13, 5) -#endif - -#ifndef OpSIBLING -# ifdef OP_SIBLING -# define OpSIBLING(O) OP_SIBLING(O) -# else -# define OpSIBLING(O) ((O)->op_sibling) -# endif -#endif +#include "xsh/caps.h" +#include "xsh/util.h" +#include "xsh/ops.h" +#include "xsh/peep.h" -/* ... Thread safety and multiplicity ...................................... */ +/* ... Lexical hints ....................................................... */ -/* 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 - -#ifndef A_MULTIPLICITY -# if defined(MULTIPLICITY) || defined(PERL_IMPLICIT_CONTEXT) -# define A_MULTIPLICITY 1 -# else -# define A_MULTIPLICITY 0 -# endif -#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_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) -#ifndef tTHX -# define tTHX PerlInterpreter* -#endif +/* Only used in op flags */ +#define A_HINT_ROOT 64 +#define A_HINT_DEREF 128 -#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 +#define XSH_HINTS_TYPE_UV 1 -#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 -#endif +#include "xsh/hints.h" -typedef OP *(*a_ck_t)(pTHX_ OP *); +#define a_hint() xsh_hints_detag(xsh_hints_fetch()) -#ifdef wrap_op_checker +/* ... Thread-local storage ................................................ */ -# define a_ck_replace(T, NC, OCP) wrap_op_checker((T), (NC), (OCP)) +#define XSH_THREADS_COMPILE_TIME_PROTECTION 1 +#define XSH_THREADS_USER_CONTEXT 0 -#else +#include "xsh/threads.h" -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; -} +/* --- Compatibility wrappers ---------------------------------------------- */ +#ifndef HvNAME_get +# define HvNAME_get(H) HvNAME(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 HvNAMELEN_get +# define HvNAMELEN_get(H) strlen(HvNAME_get(H)) #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_MULTIDEREF +# define A_HAS_MULTIDEREF XSH_HAS_PERL(5, 21, 7) #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 */ +#endif /* A_HAS_MULTIDEREF */ -#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); -} - -/* ... op => info map ...................................................... */ +/* --- op => info map ------------------------------------------------------ */ typedef struct { OP *(*old_pp)(pTHX); @@ -411,30 +110,30 @@ 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; +static ptable *a_op_map = NULL; #ifdef USE_ITHREADS #define dA_MAP_THX a_op_info a_op_map_tmp_oi -STATIC perl_mutex a_op_map_mutex; - -#define A_LOCK(M) MUTEX_LOCK(M) -#define A_UNLOCK(M) MUTEX_UNLOCK(M) +static perl_mutex a_op_map_mutex; -STATIC const a_op_info *a_map_fetch(const OP *o, a_op_info *oi) { +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) { @@ -442,7 +141,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; } @@ -453,15 +152,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))) { @@ -476,25 +172,25 @@ 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) { +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) { +static const OP *a_map_descend(const OP *o) { switch (PL_opargs[o->op_type] & OA_CLASS_MASK) { case OA_BASEOP: case OA_UNOP: @@ -509,13 +205,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); @@ -530,16 +226,16 @@ 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 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; @@ -551,17 +247,17 @@ STATIC void a_map_update_flags_topdown(const OP *root, UV flags) { 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) { +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; @@ -573,14 +269,14 @@ 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; } /* ... Decide whether this expression should be autovivified or not ........ */ -STATIC UV a_map_resolve(const OP *o, const a_op_info *oi) { +static UV a_map_resolve(const OP *o, const a_op_info *oi) { UV flags = 0, rflags; const OP *root; const a_op_info *roi = oi; @@ -615,7 +311,7 @@ cancel: /* ... Inspired from pp_defined() .......................................... */ -STATIC int a_undef(pTHX_ SV *sv) { +static int a_undef(pTHX_ SV *sv) { #define a_undef(S) a_undef(aTHX_ (S)) switch (SvTYPE(sv)) { case SVt_NULL: @@ -649,7 +345,7 @@ STATIC int a_undef(pTHX_ SV *sv) { /* ... pp_rv2av ............................................................ */ -STATIC OP *a_pp_rv2av(pTHX) { +static OP *a_pp_rv2av(pTHX) { dA_MAP_THX; const a_op_info *oi; dSP; @@ -673,7 +369,7 @@ STATIC OP *a_pp_rv2av(pTHX) { /* ... pp_rv2hv ............................................................ */ -STATIC OP *a_pp_rv2hv_simple(pTHX) { +static OP *a_pp_rv2hv_simple(pTHX) { dA_MAP_THX; const a_op_info *oi; dSP; @@ -688,7 +384,7 @@ STATIC OP *a_pp_rv2hv_simple(pTHX) { return oi->old_pp(aTHX); } -STATIC OP *a_pp_rv2hv(pTHX) { +static OP *a_pp_rv2hv(pTHX) { dA_MAP_THX; const a_op_info *oi; dSP; @@ -710,7 +406,17 @@ STATIC OP *a_pp_rv2hv(pTHX) { /* ... pp_deref (aelem,helem,rv2sv,padsv) .................................. */ -STATIC OP *a_pp_deref(pTHX) { +static void a_cannot_vivify(pTHX_ UV flags) { +#define a_cannot_vivify(F) a_cannot_vivify(aTHX_ (F)) + if (flags & A_HINT_STRICT) + croak("Reference vivification forbidden"); + else if (flags & A_HINT_WARN) + warn("Reference was vivified"); + else /* A_HINT_STORE */ + croak("Can't vivify reference"); +} + +static OP *a_pp_deref(pTHX) { dA_MAP_THX; const a_op_info *oi; UV flags; @@ -726,14 +432,8 @@ STATIC OP *a_pp_deref(pTHX) { if (flags & (A_HINT_NOTIFY|A_HINT_STORE)) { SPAGAIN; - if (a_undef(TOPs)) { - if (flags & A_HINT_STRICT) - croak("Reference vivification forbidden"); - else if (flags & A_HINT_WARN) - warn("Reference was vivified"); - else /* A_HINT_STORE */ - croak("Can't vivify reference"); - } + if (a_undef(TOPs)) + a_cannot_vivify(flags); } return o; @@ -744,7 +444,7 @@ STATIC OP *a_pp_deref(pTHX) { /* ... pp_root (exists,delete,keys,values) ................................. */ -STATIC OP *a_pp_root_unop(pTHX) { +static OP *a_pp_root_unop(pTHX) { dSP; if (a_undef(TOPs)) { @@ -764,7 +464,7 @@ STATIC OP *a_pp_root_unop(pTHX) { } } -STATIC OP *a_pp_root_binop(pTHX) { +static OP *a_pp_root_binop(pTHX) { dSP; if (a_undef(TOPm1s)) { @@ -783,9 +483,318 @@ STATIC OP *a_pp_root_binop(pTHX) { } } +#if A_HAS_MULTIDEREF + +/* ... 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)) + { + OP *o = PL_op; + ENTER; + SAVEOP(); + PL_op = op; + PL_op->op_ppaddr(aTHX); + PL_op = o; + LEAVE; + } + + { + SV *ret; + dSP; + ret = POPs; + PUTBACK; + return ret; + } +} + +static void a_do_fake_pp_unop_init(pTHX_ UNOP *unop, U32 type, U32 flags) { +#define a_do_fake_pp_unop_init(O, T, F) a_do_fake_pp_unop_init(aTHX_ (O), (T), (F)) + unop->op_type = type; + unop->op_flags = OPf_WANT_SCALAR | (~OPf_WANT & flags); + unop->op_private = 0; + unop->op_first = NULL; + unop->op_ppaddr = PL_ppaddr[type]; +} + +static SV *a_do_fake_pp_unop_arg1(pTHX_ U32 type, U32 flags, SV *arg) { +#define a_do_fake_pp_unop_arg1(T, F, A) a_do_fake_pp_unop_arg1(aTHX_ (T), (F), (A)) + UNOP unop; + dSP; + + a_do_fake_pp_unop_init(&unop, type, flags); + + EXTEND(SP, 1); + PUSHs(arg); + PUTBACK; + + return a_do_fake_pp((OP *) &unop); +} + +static SV *a_do_fake_pp_unop_arg2(pTHX_ U32 type, U32 flags, SV *arg1, SV *arg2) { +#define a_do_fake_pp_unop_arg2(T, F, A1, A2) a_do_fake_pp_unop_arg2(aTHX_ (T), (F), (A1), (A2)) + UNOP unop; + dSP; + + a_do_fake_pp_unop_init(&unop, type, flags); + + EXTEND(SP, 2); + PUSHs(arg1); + PUSHs(arg2); + PUTBACK; + + 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_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_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; + UV actions; + UV flags = 0; + SV *sv = NULL; + dSP; + + { + dA_MAP_THX; + const a_op_info *oi = a_map_fetch(PL_op); + XSH_ASSERT(oi); + flags = a_do_multideref(PL_op, oi->flags); + if (!flags) + return oi->old_pp(aTHX); + } + + items = cUNOP_AUXx(PL_op)->op_aux; + actions = items->uv; + + PL_multideref_pc = items; + + while (1) { + switch (actions & MDEREF_ACTION_MASK) { + case MDEREF_reload: + actions = (++items)->uv; + continue; + case MDEREF_AV_padav_aelem: /* $lex[...] */ + sv = PAD_SVl((++items)->pad_offset); + if (a_undef(sv)) + goto ret_undef; + goto do_AV_aelem; + case MDEREF_AV_gvav_aelem: /* $pkg[...] */ + sv = UNOP_AUX_item_sv(++items); + XSH_ASSERT(isGV_with_GP(sv)); + sv = (SV *) GvAVn((GV *) sv); + if (a_undef(sv)) + goto ret_undef; + goto do_AV_aelem; + case MDEREF_AV_pop_rv2av_aelem: /* expr->[...] */ + sv = POPs; + if (a_undef(sv)) + goto ret_undef; + goto do_AV_rv2av_aelem; + case MDEREF_AV_gvsv_vivify_rv2av_aelem: /* $pkg->[...] */ + sv = UNOP_AUX_item_sv(++items); + 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); + /* FALLTHROUGH */ + case MDEREF_AV_vivify_rv2av_aelem: /* vivify, ->[...] */ + if (a_undef(sv)) + goto ret_undef; +do_AV_vivify_rv2av_aelem: + sv = a_vivify_ref(sv, 0); +do_AV_rv2av_aelem: + sv = a_do_pp_rv2av(sv); +do_AV_aelem: + { + SV *esv; + XSH_ASSERT(SvTYPE(sv) == SVt_PVAV); + switch (actions & MDEREF_INDEX_MASK) { + case MDEREF_INDEX_none: + goto finish; + case MDEREF_INDEX_const: + esv = sv_2mortal(newSViv((++items)->iv)); + break; + case MDEREF_INDEX_padsv: + esv = PAD_SVl((++items)->pad_offset); + goto check_elem; + case MDEREF_INDEX_gvsv: + esv = UNOP_AUX_item_sv(++items); + XSH_ASSERT(isGV_with_GP(esv)); + esv = GvSVn((GV *) esv); +check_elem: + if (UNLIKELY(SvROK(esv) && !SvGAMAGIC(esv) && ckWARN(WARN_MISC))) + Perl_warner(aTHX_ packWARN(WARN_MISC), + "Use of reference \"%"SVf"\" as array index", + SVfARG(esv)); + break; + } + PL_multideref_pc = items; + if (actions & MDEREF_FLAG_last) { + 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); + break; + case A_HINT_DELETE: + sv = a_do_pp_adelete(sv, esv); + break; + } + goto finish; + } + sv = a_do_pp_afetch(sv, esv); + break; + } + case MDEREF_HV_padhv_helem: /* $lex{...} */ + sv = PAD_SVl((++items)->pad_offset); + if (a_undef(sv)) + goto ret_undef; + goto do_HV_helem; + case MDEREF_HV_gvhv_helem: /* $pkg{...} */ + sv = UNOP_AUX_item_sv(++items); + XSH_ASSERT(isGV_with_GP(sv)); + sv = (SV *) GvHVn((GV *) sv); + if (a_undef(sv)) + goto ret_undef; + goto do_HV_helem; + case MDEREF_HV_pop_rv2hv_helem: /* expr->{...} */ + sv = POPs; + if (a_undef(sv)) + goto ret_undef; + goto do_HV_rv2hv_helem; + case MDEREF_HV_gvsv_vivify_rv2hv_helem: /* $pkg->{...} */ + sv = UNOP_AUX_item_sv(++items); + 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); + /* FALLTHROUGH */ + case MDEREF_HV_vivify_rv2hv_helem: /* vivify, ->{...} */ + if (a_undef(sv)) + goto ret_undef; +do_HV_vivify_rv2hv_helem: + sv = a_vivify_ref(sv, 1); +do_HV_rv2hv_helem: + sv = a_do_pp_rv2hv(sv); +do_HV_helem: + { + SV *key; + XSH_ASSERT(SvTYPE(sv) == SVt_PVHV); + switch (actions & MDEREF_INDEX_MASK) { + case MDEREF_INDEX_none: + goto finish; + case MDEREF_INDEX_const: + key = UNOP_AUX_item_sv(++items); + break; + case MDEREF_INDEX_padsv: + key = PAD_SVl((++items)->pad_offset); + break; + case MDEREF_INDEX_gvsv: + key = UNOP_AUX_item_sv(++items); + XSH_ASSERT(isGV_with_GP(key)); + key = GvSVn((GV *) key); + break; + } + PL_multideref_pc = items; + if (actions & MDEREF_FLAG_last) { + 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); + break; + case A_HINT_DELETE: + sv = a_do_pp_hdelete(sv, key); + break; + default: + break; + } + goto finish; + } + sv = a_do_pp_hfetch(sv, key); + break; + } + } + + actions >>= MDEREF_SHIFT; + } + +ret_undef: + if (flags & (A_HINT_NOTIFY|A_HINT_STORE)) + a_cannot_vivify(flags); + if (flags & A_HINT_EXISTS) + sv = &PL_sv_no; + else + sv = &PL_sv_undef; +finish: + XPUSHs(sv); + RETURN; +} + +#endif /* A_HAS_MULTIDEREF */ + /* --- Check functions ----------------------------------------------------- */ -STATIC void a_recheck_rv2xv(pTHX_ OP *o, OPCODE type, OP *(*new_pp)(pTHX)) { +static void a_recheck_rv2xv(pTHX_ OP *o, OPCODE type, OP *(*new_pp)(pTHX)) { #define a_recheck_rv2xv(O, T, PP) a_recheck_rv2xv(aTHX_ (O), (T), (PP)) if (o->op_type == type && o->op_ppaddr != new_pp @@ -808,9 +817,9 @@ STATIC void a_recheck_rv2xv(pTHX_ OP *o, OPCODE type, OP *(*new_pp)(pTHX)) { * the op entry in the op map in the padany check function, and we set their * op_ppaddr member in our peephole optimizer replacement below. */ -STATIC OP *(*a_old_ck_padany)(pTHX_ OP *) = 0; +static OP *(*a_old_ck_padany)(pTHX_ OP *) = 0; -STATIC OP *a_ck_padany(pTHX_ OP *o) { +static OP *a_ck_padany(pTHX_ OP *o) { UV hint; o = a_old_ck_padany(aTHX_ o); @@ -824,9 +833,9 @@ STATIC OP *a_ck_padany(pTHX_ OP *o) { return o; } -STATIC OP *(*a_old_ck_padsv)(pTHX_ OP *) = 0; +static OP *(*a_old_ck_padsv)(pTHX_ OP *) = 0; -STATIC OP *a_ck_padsv(pTHX_ OP *o) { +static OP *a_ck_padsv(pTHX_ OP *o) { UV hint; o = a_old_ck_padsv(aTHX_ o); @@ -848,11 +857,11 @@ STATIC OP *a_ck_padsv(pTHX_ OP *o) { * modifying context, so the expression can't be resolved yet. It will be at the * first invocation of a_pp_deref() for this expression. */ -STATIC OP *(*a_old_ck_aelem)(pTHX_ OP *) = 0; -STATIC OP *(*a_old_ck_helem)(pTHX_ OP *) = 0; -STATIC OP *(*a_old_ck_rv2sv)(pTHX_ OP *) = 0; +static OP *(*a_old_ck_aelem)(pTHX_ OP *) = 0; +static OP *(*a_old_ck_helem)(pTHX_ OP *) = 0; +static OP *(*a_old_ck_rv2sv)(pTHX_ OP *) = 0; -STATIC OP *a_ck_deref(pTHX_ OP *o) { +static OP *a_ck_deref(pTHX_ OP *o) { OP * (*old_ck)(pTHX_ OP *o) = 0; UV hint = a_hint(); @@ -873,6 +882,18 @@ STATIC OP *a_ck_deref(pTHX_ OP *o) { } o = old_ck(aTHX_ o); +#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 (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; @@ -889,10 +910,10 @@ STATIC OP *a_ck_deref(pTHX_ OP *o) { * rv2[ah]v, resolution is handled by the first call to a_pp_deref() in the * expression. */ -STATIC OP *(*a_old_ck_rv2av)(pTHX_ OP *) = 0; -STATIC OP *(*a_old_ck_rv2hv)(pTHX_ OP *) = 0; +static OP *(*a_old_ck_rv2av)(pTHX_ OP *) = 0; +static OP *(*a_old_ck_rv2hv)(pTHX_ OP *) = 0; -STATIC OP *a_ck_rv2xv(pTHX_ OP *o) { +static OP *a_ck_rv2xv(pTHX_ OP *o) { OP * (*old_ck)(pTHX_ OP *o) = 0; OP * (*new_pp)(pTHX) = 0; UV hint; @@ -923,10 +944,10 @@ STATIC OP *a_ck_rv2xv(pTHX_ OP *o) { * root so that the rest of the expression will see the right context when * resolving. That's why we don't replace the ppaddr. */ -STATIC OP *(*a_old_ck_aslice)(pTHX_ OP *) = 0; -STATIC OP *(*a_old_ck_hslice)(pTHX_ OP *) = 0; +static OP *(*a_old_ck_aslice)(pTHX_ OP *) = 0; +static OP *(*a_old_ck_hslice)(pTHX_ OP *) = 0; -STATIC OP *a_ck_xslice(pTHX_ OP *o) { +static OP *a_ck_xslice(pTHX_ OP *o) { OP * (*old_ck)(pTHX_ OP *o) = 0; UV hint = a_hint(); @@ -955,15 +976,15 @@ STATIC OP *a_ck_xslice(pTHX_ OP *o) { /* Those ops are only found at the root of a dereferencing expression. We can * then resolve at compile time if vivification must take place or not. */ -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_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) { +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) { @@ -1004,36 +1025,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); @@ -1076,162 +1079,107 @@ STATIC void a_peep_rec(pTHX_ OP *o, ptable *seen) { if (!(oi->flags & A_HINT_DEREF)) o->op_ppaddr = oi->old_pp; break; -#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); +#if A_HAS_MULTIDEREF + case OP_MULTIDEREF: + if (o->op_ppaddr != a_pp_multideref) { + oi = a_map_fetch(cUNOPo->op_first); + if (!oi) + break; + 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 -#endif /* !A_HAS_RPEEP */ +#endif 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; - -STATIC void a_teardown(pTHX_ void *root) { +/* --- Module setup/teardown ----------------------------------------------- */ - if (!a_initialized) - return; +static void xsh_user_global_setup(pTHX) { + a_op_map = ptable_new(32); -#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; - a_initialized = 0; -} + 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_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)); -STATIC void a_setup(pTHX) { -#define a_setup() a_setup(aTHX) - if (a_initialized) - return; + 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 @@ -1240,91 +1188,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 @@ -1334,6 +1216,6 @@ PROTOTYPE: $ CODE: if (!SvOK(tag)) XSRETURN_UNDEF; - RETVAL = newSVuv(a_detag(tag)); + RETVAL = newSVuv(xsh_hints_detag(tag)); OUTPUT: RETVAL