-typedef OP *(*indirect_ck_t)(pTHX_ OP *);
-
-#ifdef wrap_op_checker
-
-# define indirect_ck_replace(T, NC, OCP) wrap_op_checker((T), (NC), (OCP))
-
-#else
-
-static void indirect_ck_replace(pTHX_ OPCODE type, indirect_ck_t new_ck, indirect_ck_t *old_ck_p) {
-#define indirect_ck_replace(T, NC, OCP) indirect_ck_replace(aTHX_ (T), (NC), (OCP))
- I_CHECK_LOCK;
- if (!*old_ck_p) {
- *old_ck_p = PL_check[type];
- PL_check[type] = new_ck;
- }
- I_CHECK_UNLOCK;
-}
-
-#endif
-
-static void indirect_ck_restore(pTHX_ OPCODE type, indirect_ck_t *old_ck_p) {
-#define indirect_ck_restore(T, OCP) indirect_ck_restore(aTHX_ (T), (OCP))
- I_CHECK_LOCK;
- if (*old_ck_p) {
- PL_check[type] = *old_ck_p;
- *old_ck_p = 0;
- }
- I_CHECK_UNLOCK;
-}
-
-/* --- Helpers ------------------------------------------------------------- */
-
-/* ... Check if the module is loaded ....................................... */
-
-static I32 indirect_loaded = 0;
-
-#if I_THREADSAFE
-
-#define PTABLE_NAME ptable_loaded
-#define PTABLE_NEED_DELETE 1
-#define PTABLE_NEED_WALK 0
-
-#include "ptable.h"
-
-#define ptable_loaded_store(T, K, V) ptable_loaded_store(aPTBLMS_ (T), (K), (V))
-#define ptable_loaded_delete(T, K) ptable_loaded_delete(aPTBLMS_ (T), (K))
-#define ptable_loaded_free(T) ptable_loaded_free(aPTBLMS_ (T))
-
-static ptable *indirect_loaded_cxts = NULL;
-
-static int indirect_is_loaded(pTHX_ void *cxt) {
-#define indirect_is_loaded(C) indirect_is_loaded(aTHX_ (C))
- int res = 0;
-
- I_LOADED_LOCK;
- if (indirect_loaded_cxts && ptable_fetch(indirect_loaded_cxts, cxt))
- res = 1;
- I_LOADED_UNLOCK;
-
- return res;
-}
-
-static int indirect_set_loaded_locked(pTHX_ void *cxt) {
-#define indirect_set_loaded_locked(C) indirect_set_loaded_locked(aTHX_ (C))
- int global_setup = 0;
-
- if (indirect_loaded <= 0) {
- I_ASSERT(indirect_loaded == 0);
- I_ASSERT(!indirect_loaded_cxts);
- indirect_loaded_cxts = ptable_new();
- global_setup = 1;
- }
- ++indirect_loaded;
- I_ASSERT(indirect_loaded_cxts);
- ptable_loaded_store(indirect_loaded_cxts, cxt, cxt);
-
- return global_setup;
-}
-
-static int indirect_clear_loaded_locked(pTHX_ void *cxt) {
-#define indirect_clear_loaded_locked(C) indirect_clear_loaded_locked(aTHX_ (C))
- int global_teardown = 0;
-
- if (indirect_loaded > 1) {
- I_ASSERT(indirect_loaded_cxts);
- ptable_loaded_delete(indirect_loaded_cxts, cxt);
- --indirect_loaded;
- } else if (indirect_loaded_cxts) {
- I_ASSERT(indirect_loaded == 1);
- ptable_loaded_free(indirect_loaded_cxts);
- indirect_loaded_cxts = NULL;
- indirect_loaded = 0;
- global_teardown = 1;
- }
-
- return global_teardown;
-}
-
-#else
-
-#define indirect_is_loaded(C) (indirect_loaded > 0)
-#define indirect_set_loaded_locked(C) ((indirect_loaded++ <= 0) ? 1 : 0)
-#define indirect_clear_loaded_locked(C) ((--indirect_loaded <= 0) ? 1 : 0)
-
-#endif
-
-/* ... Thread-safe hints ................................................... */
-
-#if I_WORKAROUND_REQUIRE_PROPAGATION
-
-typedef struct {
- SV *code;
- IV require_tag;
-} indirect_hint_t;
-
-#define I_HINT_STRUCT 1
-
-#define I_HINT_CODE(H) ((H)->code)
-
-#define I_HINT_FREE(H) { \
- indirect_hint_t *h = (H); \
- SvREFCNT_dec(h->code); \
- PerlMemShared_free(h); \
-}
-
-#else /* I_WORKAROUND_REQUIRE_PROPAGATION */
-
-typedef SV indirect_hint_t;
-
-#define I_HINT_STRUCT 0
-
-#define I_HINT_CODE(H) (H)
-
-#define I_HINT_FREE(H) SvREFCNT_dec(H);
-
-#endif /* !I_WORKAROUND_REQUIRE_PROPAGATION */
-
-#if I_THREADSAFE
-
-#define PTABLE_NAME ptable_hints
-#define PTABLE_VAL_FREE(V) I_HINT_FREE(V)
-#define PTABLE_NEED_DELETE 0
-#define PTABLE_NEED_WALK 1
-
-#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 /* I_THREADSAFE */
-
-/* Define the op->str ptable here because we need to be able to clean it during
- * thread cleanup. */
-
-typedef struct {
- char *buf;
- STRLEN pos;
- STRLEN size;
- STRLEN len;
- line_t line;
-} indirect_op_info_t;
-
-#define PTABLE_NAME ptable
-#define PTABLE_VAL_FREE(V) if (V) { Safefree(((indirect_op_info_t *) (V))->buf); Safefree(V); }
-#define PTABLE_NEED_DELETE 1
-#define PTABLE_NEED_WALK 0
-
-#define pPTBL pTHX
-#define pPTBL_ pTHX_
-#define aPTBL aTHX
-#define aPTBL_ aTHX_
-
-#include "ptable.h"
-
-#define ptable_store(T, K, V) ptable_store(aTHX_ (T), (K), (V))
-#define ptable_delete(T, K) ptable_delete(aTHX_ (T), (K))
-#define ptable_clear(T) ptable_clear(aTHX_ (T))
-#define ptable_free(T) ptable_free(aTHX_ (T))
-
-#define MY_CXT_KEY __PACKAGE__ "::_guts" XS_VERSION
-
-typedef struct {
-#if I_THREADSAFE
- ptable *tbl; /* It really is a ptable_hints */
- tTHX owner;
-#endif
- ptable *map;
- SV *global_code;
-} my_cxt_t;
-
-START_MY_CXT
-
-#if I_THREADSAFE
-
-typedef struct {
- ptable *tbl;
-#if I_HAS_PERL(5, 13, 2)
- CLONE_PARAMS *params;
-#else
- CLONE_PARAMS params;
-#endif
-} indirect_ptable_clone_ud;
-
-#if I_HAS_PERL(5, 13, 2)
-# define indirect_ptable_clone_ud_init(U, T, O) \
- (U).tbl = (T); \
- (U).params = Perl_clone_params_new((O), aTHX)
-# define indirect_ptable_clone_ud_deinit(U) Perl_clone_params_del((U).params)
-# define indirect_dup_inc(S, U) SvREFCNT_inc(sv_dup((S), (U)->params))
-#else
-# define indirect_ptable_clone_ud_init(U, T, O) \
- (U).tbl = (T); \
- (U).params.stashes = newAV(); \
- (U).params.flags = 0; \
- (U).params.proto_perl = (O)
-# define indirect_ptable_clone_ud_deinit(U) SvREFCNT_dec((U).params.stashes)
-# define indirect_dup_inc(S, U) SvREFCNT_inc(sv_dup((S), &((U)->params)))
-#endif
-
-static void indirect_ptable_clone(pTHX_ ptable_ent *ent, void *ud_) {
- indirect_ptable_clone_ud *ud = ud_;
- indirect_hint_t *h1 = ent->val;
- indirect_hint_t *h2;
-
-#if I_HINT_STRUCT
-
- h2 = PerlMemShared_malloc(sizeof *h2);
- h2->code = indirect_dup_inc(h1->code, ud);
-#if I_WORKAROUND_REQUIRE_PROPAGATION
- h2->require_tag = PTR2IV(indirect_dup_inc(INT2PTR(SV *, h1->require_tag), ud));
-#endif
-
-#else /* I_HINT_STRUCT */
-
- h2 = indirect_dup_inc(h1, ud);
-
-#endif /* !I_HINT_STRUCT */
-
- ptable_hints_store(ud->tbl, ent->key, h2);
-}
-
-#endif /* I_THREADSAFE */
-
-#if I_WORKAROUND_REQUIRE_PROPAGATION
-
-static IV indirect_require_tag(pTHX) {
-#define indirect_require_tag() indirect_require_tag(aTHX)
- const CV *cv, *outside;
-
- cv = PL_compcv;
-
- 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;
-
- 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;
-
- 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;
- }
- }
- }
-
- cv = PL_main_cv;
- }
-
-get_enclosing_cv:
- for (outside = CvOUTSIDE(cv); outside; outside = CvOUTSIDE(cv))
- cv = outside;
-
- return PTR2IV(cv);
-}
-
-#endif /* I_WORKAROUND_REQUIRE_PROPAGATION */
-
-static SV *indirect_tag(pTHX_ SV *value) {
-#define indirect_tag(V) indirect_tag(aTHX_ (V))
- indirect_hint_t *h;
- SV *code = NULL;
-#if I_THREADSAFE
- dMY_CXT;
-
- if (!MY_CXT.tbl)
- return newSViv(0);
-#endif /* I_THREADSAFE */
-
- if (SvROK(value)) {
- value = SvRV(value);
- if (SvTYPE(value) >= SVt_PVCV) {
- code = value;
- SvREFCNT_inc_simple_void_NN(code);
- }
- }
-
-#if I_HINT_STRUCT
- h = PerlMemShared_malloc(sizeof *h);
- h->code = code;
-# if I_WORKAROUND_REQUIRE_PROPAGATION
- h->require_tag = indirect_require_tag();
-# endif /* I_WORKAROUND_REQUIRE_PROPAGATION */
-#else /* I_HINT_STRUCT */
- h = code;
-#endif /* !I_HINT_STRUCT */
-
-#if I_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 /* I_THREADSAFE */
-
- return newSViv(PTR2IV(h));
-}
-
-static SV *indirect_detag(pTHX_ const SV *hint) {
-#define indirect_detag(H) indirect_detag(aTHX_ (H))
- indirect_hint_t *h;
-#if I_THREADSAFE || I_WORKAROUND_REQUIRE_PROPAGATION
- dMY_CXT;
-#endif
-
-#if I_THREADSAFE
- if (!MY_CXT.tbl)
- return NULL;
-#endif /* I_THREADSAFE */
-
- h = INT2PTR(indirect_hint_t *, SvIVX(hint));
-#if I_THREADSAFE
- h = ptable_fetch(MY_CXT.tbl, h);
-#endif /* I_THREADSAFE */
-
-#if I_WORKAROUND_REQUIRE_PROPAGATION
- if (indirect_require_tag() != h->require_tag)
- return MY_CXT.global_code;
-#endif /* I_WORKAROUND_REQUIRE_PROPAGATION */
-
- return I_HINT_CODE(h);
-}
-
-static VOL U32 indirect_hash = 0;
-
-static SV *indirect_hint(pTHX) {
-#define indirect_hint() indirect_hint(aTHX)
- SV *hint = NULL;
-
- if (IN_PERL_RUNTIME)
- return NULL;
-
-#if I_HAS_PERL(5, 10, 0) || defined(PL_parser)
- if (!PL_parser)
- return NULL;
-#endif
-
-#ifdef cop_hints_fetch_pvn
- hint = cop_hints_fetch_pvn(PL_curcop, __PACKAGE__, __PACKAGE_LEN__,
- indirect_hash, 0);
-#elif I_HAS_PERL(5, 9, 5)
- hint = Perl_refcounted_he_fetch(aTHX_ PL_curcop->cop_hints_hash,
- NULL,
- __PACKAGE__, __PACKAGE_LEN__,
- 0,
- indirect_hash);
-#else
- {
- SV **val = hv_fetch(GvHV(PL_hintgv), __PACKAGE__, __PACKAGE_LEN__, 0);
- if (val)
- hint = *val;
- }
-#endif
-
- if (hint && SvIOK(hint)) {
- return indirect_detag(hint);
- } else {
- dMY_CXT;
- if (indirect_is_loaded(&MY_CXT))
- return MY_CXT.global_code;
- else
- return NULL;
- }
-}
-
-/* ... op -> source position ............................................... */
-
-static void indirect_map_store(pTHX_ const OP *o, STRLEN pos, SV *sv, line_t line) {
-#define indirect_map_store(O, P, N, L) indirect_map_store(aTHX_ (O), (P), (N), (L))
- indirect_op_info_t *oi;
- const char *s;
- STRLEN len;
- dMY_CXT;
-
- /* No need to check for MY_CXT.map != NULL because this code path is always
- * guarded by indirect_hint(). */
-
- if (!(oi = ptable_fetch(MY_CXT.map, o))) {
- Newx(oi, 1, indirect_op_info_t);
- ptable_store(MY_CXT.map, o, oi);
- oi->buf = NULL;
- oi->size = 0;
- }
-
- if (sv) {
- s = SvPV_const(sv, len);
- } else {
- s = "{";
- len = 1;
- }
-
- if (len > oi->size) {
- Safefree(oi->buf);
- Newx(oi->buf, len, char);
- oi->size = len;
- }
- Copy(s, oi->buf, len, char);
-
- oi->len = len;
- oi->pos = pos;
- oi->line = line;
-}
-
-static const indirect_op_info_t *indirect_map_fetch(pTHX_ const OP *o) {
-#define indirect_map_fetch(O) indirect_map_fetch(aTHX_ (O))
- dMY_CXT;
-
- /* No need to check for MY_CXT.map != NULL because this code path is always
- * guarded by indirect_hint(). */
-
- return ptable_fetch(MY_CXT.map, o);
-}
-
-static void indirect_map_delete(pTHX_ const OP *o) {
-#define indirect_map_delete(O) indirect_map_delete(aTHX_ (O))
- dMY_CXT;
-
- if (indirect_is_loaded(&MY_CXT) && MY_CXT.map)
- ptable_delete(MY_CXT.map, o);
-}
-
-/* --- Safe version of call_sv() ------------------------------------------- */