+/* --- Stolen chunk of B --------------------------------------------------- */
+
+typedef enum {
+ OPc_NULL,
+ OPc_BASEOP,
+ OPc_UNOP,
+ OPc_BINOP,
+ OPc_LOGOP,
+ OPc_LISTOP,
+ OPc_PMOP,
+ OPc_SVOP,
+ OPc_PADOP,
+ OPc_PVOP,
+ OPc_LOOP,
+ OPc_COP,
+#if XSH_HAS_PERL(5, 21, 5)
+ OPc_METHOP,
+#endif
+#if XSH_HAS_PERL(5, 21, 7)
+ OPc_UNOP_AUX,
+#endif
+ OPc_MAX
+} opclass;
+
+static const char *const vmg_opclassnames[] = {
+ "B::NULL",
+ "B::OP",
+ "B::UNOP",
+ "B::BINOP",
+ "B::LOGOP",
+ "B::LISTOP",
+ "B::PMOP",
+ "B::SVOP",
+ "B::PADOP",
+ "B::PVOP",
+ "B::LOOP",
+ "B::COP",
+#if XSH_HAS_PERL(5, 21, 5)
+ "B::METHOP",
+#endif
+#if XSH_HAS_PERL(5, 21, 7)
+ "B::UNOP_AUX",
+#endif
+ NULL
+};
+
+static opclass vmg_opclass(pTHX_ const OP *o) {
+#define vmg_opclass(O) vmg_opclass(aTHX_ (O))
+#if 0
+ if (!o)
+ return OPc_NULL;
+#endif
+
+ if (o->op_type == 0) {
+#if XSH_HAS_PERL(5, 21, 7)
+ if (o->op_targ == OP_NEXTSTATE || o->op_targ == OP_DBSTATE)
+ return OPc_COP;
+#endif
+ return (o->op_flags & OPf_KIDS) ? OPc_UNOP : OPc_BASEOP;
+ }
+
+ if (o->op_type == OP_SASSIGN)
+ return ((o->op_private & OPpASSIGN_BACKWARDS) ? OPc_UNOP : OPc_BINOP);
+
+ if (o->op_type == OP_AELEMFAST) {
+#if PERL_VERSION <= 14
+ if (o->op_flags & OPf_SPECIAL)
+ return OPc_BASEOP;
+ else
+#endif
+#ifdef USE_ITHREADS
+ return OPc_PADOP;
+#else
+ return OPc_SVOP;
+#endif
+ }
+
+#ifdef USE_ITHREADS
+ if (o->op_type == OP_GV || o->op_type == OP_GVSV || o->op_type == OP_RCATLINE)
+ return OPc_PADOP;
+#endif
+
+ switch (OP_CLASS(o)) {
+ case OA_BASEOP:
+ return OPc_BASEOP;
+ case OA_UNOP:
+ return OPc_UNOP;
+ case OA_BINOP:
+ return OPc_BINOP;
+ case OA_LOGOP:
+ return OPc_LOGOP;
+ case OA_LISTOP:
+ return OPc_LISTOP;
+ case OA_PMOP:
+ return OPc_PMOP;
+ case OA_SVOP:
+ return OPc_SVOP;
+ case OA_PADOP:
+ return OPc_PADOP;
+ case OA_PVOP_OR_SVOP:
+ return (
+#if XSH_HAS_PERL(5, 13, 7)
+ (o->op_type != OP_CUSTOM) &&
+#endif
+ (o->op_private & (OPpTRANS_TO_UTF|OPpTRANS_FROM_UTF)))
+#if defined(USE_ITHREADS) && XSH_HAS_PERL(5, 8, 9)
+ ? OPc_PADOP : OPc_PVOP;
+#else
+ ? OPc_SVOP : OPc_PVOP;
+#endif
+ case OA_LOOP:
+ return OPc_LOOP;
+ case OA_COP:
+ return OPc_COP;
+ case OA_BASEOP_OR_UNOP:
+ return (o->op_flags & OPf_KIDS) ? OPc_UNOP : OPc_BASEOP;
+ case OA_FILESTATOP:
+ return ((o->op_flags & OPf_KIDS) ? OPc_UNOP :
+#ifdef USE_ITHREADS
+ (o->op_flags & OPf_REF) ? OPc_PADOP : OPc_BASEOP);
+#else
+ (o->op_flags & OPf_REF) ? OPc_SVOP : OPc_BASEOP);
+#endif
+ case OA_LOOPEXOP:
+ if (o->op_flags & OPf_STACKED)
+ return OPc_UNOP;
+ else if (o->op_flags & OPf_SPECIAL)
+ return OPc_BASEOP;
+ else
+ return OPc_PVOP;
+#if XSH_HAS_PERL(5, 21, 5)
+ case OA_METHOP:
+ return OPc_METHOP;
+#endif
+#if XSH_HAS_PERL(5, 21, 7)
+ case OA_UNOP_AUX:
+ return OPc_UNOP_AUX;
+#endif
+ }
+
+ return OPc_BASEOP;
+}
+
+/* --- Error messages ------------------------------------------------------ */
+
+static const char vmg_invalid_wiz[] = "Invalid wizard object";
+static const char vmg_wrongargnum[] = "Wrong number of arguments";
+
+/* --- Thread-local storage ------------------------------------------------ */
+
+typedef struct {
+ HV *b__op_stashes[OPc_MAX];
+ I32 depth;
+ MAGIC *freed_tokens;
+#if VMG_PROPAGATE_ERRSV_NEEDS_TRAMPOLINE
+ vmg_trampoline propagate_errsv;
+#endif
+#if VMG_RESET_RMG_NEEDS_TRAMPOLINE
+ vmg_trampoline reset_rmg;
+#endif
+} xsh_user_cxt_t;
+
+#if XSH_THREADSAFE
+
+static void xsh_user_clone(pTHX_ const xsh_user_cxt_t *old_cxt, xsh_user_cxt_t *new_cxt) {
+ int c;
+
+ for (c = OPc_NULL; c < OPc_MAX; ++c) {
+ new_cxt->b__op_stashes[c] = old_cxt->b__op_stashes[c]
+ ? gv_stashpv(vmg_opclassnames[c], 1)
+ : NULL;
+ }
+
+ new_cxt->depth = old_cxt->depth;
+ new_cxt->freed_tokens = NULL;
+
+ return;
+}
+
+
+#endif /* XSH_THREADSAFE */
+
+#define XSH_THREADS_NEED_TEARDOWN_LATE 1
+
+#include "xsh/threads.h"
+
+/* --- <vmg_vtable> structure ---------------------------------------------- */
+
+#if XSH_THREADSAFE