This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Make SvIsCOW honest about globs
[perl5.git] / ext / XS-APItest / APItest.xs
index b0cbf6a..68533da 100644 (file)
@@ -6,6 +6,9 @@
 typedef SV *SVREF;
 typedef PTR_TBL_t *XS__APItest__PtrTable;
 
+#define croak_fail() croak("fail at " __FILE__ " line %d", __LINE__)
+#define croak_fail_ne(h, w) croak("fail %p!=%p at " __FILE__ " line %d", (h), (w), __LINE__)
+
 /* for my_cxt tests */
 
 #define MY_CXT_KEY "XS::APItest::_guts" XS_VERSION
@@ -22,10 +25,13 @@ typedef struct {
     int peep_recording;
     AV *peep_recorder;
     AV *rpeep_recorder;
+    AV *xop_record;
 } my_cxt_t;
 
 START_MY_CXT
 
+MGVTBL vtbl_foo, vtbl_bar;
+
 /* indirect functions to test the [pa]MY_CXT macros */
 
 int
@@ -89,8 +95,8 @@ test_freeent(freeent_function *f) {
 #else
     /* Storing then deleting something should ensure that a hash entry is
        available.  */
-    hv_store(test_hash, "", 0, &PL_sv_yes, 0);
-    hv_delete(test_hash, "", 0, 0);
+    (void) hv_store(test_hash, "", 0, &PL_sv_yes, 0);
+    (void) hv_delete(test_hash, "", 0, 0);
 
     /* We need to "inline" new_he here as it's static, and the functions we
        test expect to be able to call del_HE on the HE  */
@@ -118,7 +124,7 @@ test_freeent(freeent_function *f) {
     i = 0;
     do {
        mPUSHu(results[i]);
-    } while (++i < sizeof(results)/sizeof(results[0]));
+    } while (++i < (int)(sizeof(results)/sizeof(results[0])));
 
     /* Goodbye to our extra reference.  */
     SvREFCNT_dec(test_scalar);
@@ -129,6 +135,7 @@ static I32
 bitflip_key(pTHX_ IV action, SV *field) {
     MAGIC *mg = mg_find(field, PERL_MAGIC_uvar);
     SV *keysv;
+    PERL_UNUSED_ARG(action);
     if (mg && (keysv = mg->mg_obj)) {
        STRLEN len;
        const char *p = SvPV(keysv, len);
@@ -164,6 +171,7 @@ static I32
 rot13_key(pTHX_ IV action, SV *field) {
     MAGIC *mg = mg_find(field, PERL_MAGIC_uvar);
     SV *keysv;
+    PERL_UNUSED_ARG(action);
     if (mg && (keysv = mg->mg_obj)) {
        STRLEN len;
        const char *p = SvPV(keysv, len);
@@ -246,6 +254,8 @@ rot13_key(pTHX_ IV action, SV *field) {
 
 STATIC I32
 rmagical_a_dummy(pTHX_ IV idx, SV *sv) {
+    PERL_UNUSED_ARG(idx);
+    PERL_UNUSED_ARG(sv);
     return 0;
 }
 
@@ -257,6 +267,7 @@ blockhook_csc_start(pTHX_ int full)
     dMY_CXT;
     AV *const cur = GvAV(MY_CXT.cscgv);
 
+    PERL_UNUSED_ARG(full);
     SAVEGENERICSV(GvAV(MY_CXT.cscgv));
 
     if (cur) {
@@ -276,6 +287,7 @@ blockhook_csc_pre_end(pTHX_ OP **o)
 {
     dMY_CXT;
 
+    PERL_UNUSED_ARG(o);
     /* if we hit the end of a scope we missed the start of, we need to
      * unconditionally clear @CSC */
     if (GvAV(MY_CXT.cscgv) == MY_CXT.cscav && MY_CXT.cscav) {
@@ -303,6 +315,7 @@ blockhook_test_pre_end(pTHX_ OP **o)
 {
     dMY_CXT;
 
+    PERL_UNUSED_ARG(o);
     if (MY_CXT.bhk_record)
         av_push(MY_CXT.bhkav, newSVpvs("pre_end"));
 }
@@ -312,6 +325,7 @@ blockhook_test_post_end(pTHX_ OP **o)
 {
     dMY_CXT;
 
+    PERL_UNUSED_ARG(o);
     if (MY_CXT.bhk_record)
         av_push(MY_CXT.bhkav, newSVpvs("post_end"));
 }
@@ -375,6 +389,8 @@ my_rpeep (pTHX_ OP *o)
 STATIC OP *
 THX_ck_entersub_args_lists(pTHX_ OP *entersubop, GV *namegv, SV *ckobj)
 {
+    PERL_UNUSED_ARG(namegv);
+    PERL_UNUSED_ARG(ckobj);
     return ck_entersub_args_list(entersubop);
 }
 
@@ -382,6 +398,8 @@ STATIC OP *
 THX_ck_entersub_args_scalars(pTHX_ OP *entersubop, GV *namegv, SV *ckobj)
 {
     OP *aop = cUNOPx(entersubop)->op_first;
+    PERL_UNUSED_ARG(namegv);
+    PERL_UNUSED_ARG(ckobj);
     if (!aop->op_sibling)
        aop = cUNOPx(aop)->op_first;
     for (aop = aop->op_sibling; aop->op_sibling; aop = aop->op_sibling) {
@@ -395,6 +413,8 @@ THX_ck_entersub_multi_sum(pTHX_ OP *entersubop, GV *namegv, SV *ckobj)
 {
     OP *sumop = NULL;
     OP *pushop = cUNOPx(entersubop)->op_first;
+    PERL_UNUSED_ARG(namegv);
+    PERL_UNUSED_ARG(ckobj);
     if (!pushop->op_sibling)
        pushop = cUNOPx(pushop)->op_first;
     while (1) {
@@ -416,6 +436,157 @@ THX_ck_entersub_multi_sum(pTHX_ OP *entersubop, GV *namegv, SV *ckobj)
     return sumop;
 }
 
+STATIC void test_op_list_describe_part(SV *res, OP *o);
+STATIC void
+test_op_list_describe_part(SV *res, OP *o)
+{
+    sv_catpv(res, PL_op_name[o->op_type]);
+    switch (o->op_type) {
+       case OP_CONST: {
+           sv_catpvf(res, "(%d)", (int)SvIV(cSVOPx(o)->op_sv));
+       } break;
+    }
+    if (o->op_flags & OPf_KIDS) {
+       OP *k;
+       sv_catpvs(res, "[");
+       for (k = cUNOPx(o)->op_first; k; k = k->op_sibling)
+           test_op_list_describe_part(res, k);
+       sv_catpvs(res, "]");
+    } else {
+       sv_catpvs(res, ".");
+    }
+}
+
+STATIC char *
+test_op_list_describe(OP *o)
+{
+    SV *res = sv_2mortal(newSVpvs(""));
+    if (o)
+       test_op_list_describe_part(res, o);
+    return SvPVX(res);
+}
+
+/* the real new*OP functions have a tendency to call fold_constants, and
+ * other such unhelpful things, so we need our own versions for testing */
+
+#define mkUNOP(t, f) THX_mkUNOP(aTHX_ (t), (f))
+static OP *
+THX_mkUNOP(pTHX_ U32 type, OP *first)
+{
+    UNOP *unop;
+    NewOp(1103, unop, 1, UNOP);
+    unop->op_type   = (OPCODE)type;
+    unop->op_first  = first;
+    unop->op_flags  = OPf_KIDS;
+    return (OP *)unop;
+}
+
+#define mkBINOP(t, f, l) THX_mkBINOP(aTHX_ (t), (f), (l))
+static OP *
+THX_mkBINOP(pTHX_ U32 type, OP *first, OP *last)
+{
+    BINOP *binop;
+    NewOp(1103, binop, 1, BINOP);
+    binop->op_type      = (OPCODE)type;
+    binop->op_first     = first;
+    binop->op_flags     = OPf_KIDS;
+    binop->op_last      = last;
+    first->op_sibling   = last;
+    return (OP *)binop;
+}
+
+#define mkLISTOP(t, f, s, l) THX_mkLISTOP(aTHX_ (t), (f), (s), (l))
+static OP *
+THX_mkLISTOP(pTHX_ U32 type, OP *first, OP *sib, OP *last)
+{
+    LISTOP *listop;
+    NewOp(1103, listop, 1, LISTOP);
+    listop->op_type     = (OPCODE)type;
+    listop->op_flags    = OPf_KIDS;
+    listop->op_first    = first;
+    first->op_sibling   = sib;
+    sib->op_sibling     = last;
+    listop->op_last     = last;
+    return (OP *)listop;
+}
+
+static char *
+test_op_linklist_describe(OP *start)
+{
+    SV *rv = sv_2mortal(newSVpvs(""));
+    OP *o;
+    o = start = LINKLIST(start);
+    do {
+        sv_catpvs(rv, ".");
+        sv_catpv(rv, OP_NAME(o));
+        if (o->op_type == OP_CONST)
+            sv_catsv(rv, cSVOPo->op_sv);
+        o = o->op_next;
+    } while (o && o != start);
+    return SvPVX(rv);
+}
+
+/** establish_cleanup operator, ripped off from Scope::Cleanup **/
+
+STATIC void
+THX_run_cleanup(pTHX_ void *cleanup_code_ref)
+{
+    dSP;
+    ENTER;
+    SAVETMPS;
+    PUSHMARK(SP);
+    call_sv((SV*)cleanup_code_ref, G_VOID|G_DISCARD);
+    FREETMPS;
+    LEAVE;
+}
+
+STATIC OP *
+THX_pp_establish_cleanup(pTHX)
+{
+    dSP;
+    SV *cleanup_code_ref;
+    cleanup_code_ref = newSVsv(POPs);
+    SAVEFREESV(cleanup_code_ref);
+    SAVEDESTRUCTOR_X(THX_run_cleanup, cleanup_code_ref);
+    if(GIMME_V != G_VOID) PUSHs(&PL_sv_undef);
+    RETURN;
+}
+
+STATIC OP *
+THX_ck_entersub_establish_cleanup(pTHX_ OP *entersubop, GV *namegv, SV *ckobj)
+{
+    OP *pushop, *argop, *estop;
+    ck_entersub_args_proto(entersubop, namegv, ckobj);
+    pushop = cUNOPx(entersubop)->op_first;
+    if(!pushop->op_sibling) pushop = cUNOPx(pushop)->op_first;
+    argop = pushop->op_sibling;
+    pushop->op_sibling = argop->op_sibling;
+    argop->op_sibling = NULL;
+    op_free(entersubop);
+    NewOpSz(0, estop, sizeof(UNOP));
+    estop->op_type = OP_RAND;
+    estop->op_ppaddr = THX_pp_establish_cleanup;
+    cUNOPx(estop)->op_flags = OPf_KIDS;
+    cUNOPx(estop)->op_first = argop;
+    PL_hints |= HINT_BLOCK_SCOPE;
+    return estop;
+}
+
+STATIC OP *
+THX_ck_entersub_postinc(pTHX_ OP *entersubop, GV *namegv, SV *ckobj)
+{
+    OP *pushop, *argop;
+    ck_entersub_args_proto(entersubop, namegv, ckobj);
+    pushop = cUNOPx(entersubop)->op_first;
+    if(!pushop->op_sibling) pushop = cUNOPx(pushop)->op_first;
+    argop = pushop->op_sibling;
+    pushop->op_sibling = argop->op_sibling;
+    argop->op_sibling = NULL;
+    op_free(entersubop);
+    return newUNOP(OP_POSTINC, 0,
+       op_lvalue(op_contextualize(argop, G_SCALAR), OP_POSTINC));
+}
+
 /** RPN keyword parser **/
 
 #define sv_is_glob(sv) (SvTYPE(sv) == SVt_PVGV)
@@ -426,6 +597,13 @@ THX_ck_entersub_multi_sum(pTHX_ OP *entersubop, GV *namegv, SV *ckobj)
 
 static SV *hintkey_rpn_sv, *hintkey_calcrpn_sv, *hintkey_stufftest_sv;
 static SV *hintkey_swaptwostmts_sv, *hintkey_looprest_sv;
+static SV *hintkey_scopelessblock_sv;
+static SV *hintkey_stmtasexpr_sv, *hintkey_stmtsasexpr_sv;
+static SV *hintkey_loopblock_sv, *hintkey_blockasexpr_sv;
+static SV *hintkey_swaplabel_sv, *hintkey_labelconst_sv;
+static SV *hintkey_arrayfullexpr_sv, *hintkey_arraylistexpr_sv;
+static SV *hintkey_arraytermexpr_sv, *hintkey_arrayarithexpr_sv;
+static SV *hintkey_arrayexprflags_sv;
 static int (*next_keyword_plugin)(pTHX_ char *, STRLEN, OP **);
 
 /* low-level parser helpers */
@@ -593,19 +771,124 @@ static OP *THX_parse_keyword_swaptwostmts(pTHX)
     b = parse_fullstmt(0);
     if(a && b)
        PL_hints |= HINT_BLOCK_SCOPE;
-    /* should use append_list(), but that's not part of the public API */
-    return !a ? b : !b ? a : newLISTOP(OP_LINESEQ, 0, b, a);
+    return op_append_list(OP_LINESEQ, b, a);
 }
 
 #define parse_keyword_looprest() THX_parse_keyword_looprest(aTHX)
 static OP *THX_parse_keyword_looprest(pTHX)
 {
-    I32 condline;
+    return newWHILEOP(0, 1, NULL, newSVOP(OP_CONST, 0, &PL_sv_yes),
+                       parse_stmtseq(0), NULL, 1);
+}
+
+#define parse_keyword_scopelessblock() THX_parse_keyword_scopelessblock(aTHX)
+static OP *THX_parse_keyword_scopelessblock(pTHX)
+{
+    I32 c;
     OP *body;
-    condline = CopLINE(PL_curcop);
+    lex_read_space(0);
+    if(lex_peek_unichar(0) != '{'/*}*/) croak("syntax error");
+    lex_read_unichar(0);
     body = parse_stmtseq(0);
-    return newWHILEOP(0, 1, NULL, condline, newSVOP(OP_CONST, 0, &PL_sv_yes),
-                       body, NULL, 1);
+    c = lex_peek_unichar(0);
+    if(c != /*{*/'}' && c != /*[*/']' && c != /*(*/')') croak("syntax error");
+    lex_read_unichar(0);
+    return body;
+}
+
+#define parse_keyword_stmtasexpr() THX_parse_keyword_stmtasexpr(aTHX)
+static OP *THX_parse_keyword_stmtasexpr(pTHX)
+{
+    OP *o = parse_barestmt(0);
+    if (!o) o = newOP(OP_STUB, 0);
+    if (PL_hints & HINT_BLOCK_SCOPE) o->op_flags |= OPf_PARENS;
+    return op_scope(o);
+}
+
+#define parse_keyword_stmtsasexpr() THX_parse_keyword_stmtsasexpr(aTHX)
+static OP *THX_parse_keyword_stmtsasexpr(pTHX)
+{
+    OP *o;
+    lex_read_space(0);
+    if(lex_peek_unichar(0) != '{'/*}*/) croak("syntax error");
+    lex_read_unichar(0);
+    o = parse_stmtseq(0);
+    lex_read_space(0);
+    if(lex_peek_unichar(0) != /*{*/'}') croak("syntax error");
+    lex_read_unichar(0);
+    if (!o) o = newOP(OP_STUB, 0);
+    if (PL_hints & HINT_BLOCK_SCOPE) o->op_flags |= OPf_PARENS;
+    return op_scope(o);
+}
+
+#define parse_keyword_loopblock() THX_parse_keyword_loopblock(aTHX)
+static OP *THX_parse_keyword_loopblock(pTHX)
+{
+    return newWHILEOP(0, 1, NULL, newSVOP(OP_CONST, 0, &PL_sv_yes),
+                       parse_block(0), NULL, 1);
+}
+
+#define parse_keyword_blockasexpr() THX_parse_keyword_blockasexpr(aTHX)
+static OP *THX_parse_keyword_blockasexpr(pTHX)
+{
+    OP *o = parse_block(0);
+    if (!o) o = newOP(OP_STUB, 0);
+    if (PL_hints & HINT_BLOCK_SCOPE) o->op_flags |= OPf_PARENS;
+    return op_scope(o);
+}
+
+#define parse_keyword_swaplabel() THX_parse_keyword_swaplabel(aTHX)
+static OP *THX_parse_keyword_swaplabel(pTHX)
+{
+    OP *sop = parse_barestmt(0);
+    SV *label = parse_label(PARSE_OPTIONAL);
+    if (label) sv_2mortal(label);
+    return newSTATEOP(0, label ? savepv(SvPVX(label)) : NULL, sop);
+}
+
+#define parse_keyword_labelconst() THX_parse_keyword_labelconst(aTHX)
+static OP *THX_parse_keyword_labelconst(pTHX)
+{
+    return newSVOP(OP_CONST, 0, parse_label(0));
+}
+
+#define parse_keyword_arrayfullexpr() THX_parse_keyword_arrayfullexpr(aTHX)
+static OP *THX_parse_keyword_arrayfullexpr(pTHX)
+{
+    return newANONLIST(parse_fullexpr(0));
+}
+
+#define parse_keyword_arraylistexpr() THX_parse_keyword_arraylistexpr(aTHX)
+static OP *THX_parse_keyword_arraylistexpr(pTHX)
+{
+    return newANONLIST(parse_listexpr(0));
+}
+
+#define parse_keyword_arraytermexpr() THX_parse_keyword_arraytermexpr(aTHX)
+static OP *THX_parse_keyword_arraytermexpr(pTHX)
+{
+    return newANONLIST(parse_termexpr(0));
+}
+
+#define parse_keyword_arrayarithexpr() THX_parse_keyword_arrayarithexpr(aTHX)
+static OP *THX_parse_keyword_arrayarithexpr(pTHX)
+{
+    return newANONLIST(parse_arithexpr(0));
+}
+
+#define parse_keyword_arrayexprflags() THX_parse_keyword_arrayexprflags(aTHX)
+static OP *THX_parse_keyword_arrayexprflags(pTHX)
+{
+    U32 flags = 0;
+    I32 c;
+    OP *o;
+    lex_read_space(0);
+    c = lex_peek_unichar(0);
+    if (c != '!' && c != '?') croak("syntax error");
+    lex_read_unichar(0);
+    if (c == '?') flags |= PARSE_OPTIONAL;
+    o = parse_listexpr(flags);
+    return o ? newANONLIST(o) : newANONHASH(newOP(OP_STUB, 0));
 }
 
 /* plugin glue */
@@ -644,11 +927,95 @@ static int my_keyword_plugin(pTHX_
                    keyword_active(hintkey_looprest_sv)) {
        *op_ptr = parse_keyword_looprest();
        return KEYWORD_PLUGIN_STMT;
+    } else if(keyword_len == 14 && strnEQ(keyword_ptr, "scopelessblock", 14) &&
+                   keyword_active(hintkey_scopelessblock_sv)) {
+       *op_ptr = parse_keyword_scopelessblock();
+       return KEYWORD_PLUGIN_STMT;
+    } else if(keyword_len == 10 && strnEQ(keyword_ptr, "stmtasexpr", 10) &&
+                   keyword_active(hintkey_stmtasexpr_sv)) {
+       *op_ptr = parse_keyword_stmtasexpr();
+       return KEYWORD_PLUGIN_EXPR;
+    } else if(keyword_len == 11 && strnEQ(keyword_ptr, "stmtsasexpr", 11) &&
+                   keyword_active(hintkey_stmtsasexpr_sv)) {
+       *op_ptr = parse_keyword_stmtsasexpr();
+       return KEYWORD_PLUGIN_EXPR;
+    } else if(keyword_len == 9 && strnEQ(keyword_ptr, "loopblock", 9) &&
+                   keyword_active(hintkey_loopblock_sv)) {
+       *op_ptr = parse_keyword_loopblock();
+       return KEYWORD_PLUGIN_STMT;
+    } else if(keyword_len == 11 && strnEQ(keyword_ptr, "blockasexpr", 11) &&
+                   keyword_active(hintkey_blockasexpr_sv)) {
+       *op_ptr = parse_keyword_blockasexpr();
+       return KEYWORD_PLUGIN_EXPR;
+    } else if(keyword_len == 9 && strnEQ(keyword_ptr, "swaplabel", 9) &&
+                   keyword_active(hintkey_swaplabel_sv)) {
+       *op_ptr = parse_keyword_swaplabel();
+       return KEYWORD_PLUGIN_STMT;
+    } else if(keyword_len == 10 && strnEQ(keyword_ptr, "labelconst", 10) &&
+                   keyword_active(hintkey_labelconst_sv)) {
+       *op_ptr = parse_keyword_labelconst();
+       return KEYWORD_PLUGIN_EXPR;
+    } else if(keyword_len == 13 && strnEQ(keyword_ptr, "arrayfullexpr", 13) &&
+                   keyword_active(hintkey_arrayfullexpr_sv)) {
+       *op_ptr = parse_keyword_arrayfullexpr();
+       return KEYWORD_PLUGIN_EXPR;
+    } else if(keyword_len == 13 && strnEQ(keyword_ptr, "arraylistexpr", 13) &&
+                   keyword_active(hintkey_arraylistexpr_sv)) {
+       *op_ptr = parse_keyword_arraylistexpr();
+       return KEYWORD_PLUGIN_EXPR;
+    } else if(keyword_len == 13 && strnEQ(keyword_ptr, "arraytermexpr", 13) &&
+                   keyword_active(hintkey_arraytermexpr_sv)) {
+       *op_ptr = parse_keyword_arraytermexpr();
+       return KEYWORD_PLUGIN_EXPR;
+    } else if(keyword_len == 14 && strnEQ(keyword_ptr, "arrayarithexpr", 14) &&
+                   keyword_active(hintkey_arrayarithexpr_sv)) {
+       *op_ptr = parse_keyword_arrayarithexpr();
+       return KEYWORD_PLUGIN_EXPR;
+    } else if(keyword_len == 14 && strnEQ(keyword_ptr, "arrayexprflags", 14) &&
+                   keyword_active(hintkey_arrayexprflags_sv)) {
+       *op_ptr = parse_keyword_arrayexprflags();
+       return KEYWORD_PLUGIN_EXPR;
     } else {
        return next_keyword_plugin(aTHX_ keyword_ptr, keyword_len, op_ptr);
     }
 }
 
+static XOP my_xop;
+
+static OP *
+pp_xop(pTHX)
+{
+    return PL_op->op_next;
+}
+
+static void
+peep_xop(pTHX_ OP *o, OP *oldop)
+{
+    dMY_CXT;
+    av_push(MY_CXT.xop_record, newSVpvf("peep:%"UVxf, PTR2UV(o)));
+    av_push(MY_CXT.xop_record, newSVpvf("oldop:%"UVxf, PTR2UV(oldop)));
+}
+
+static I32
+filter_call(pTHX_ int idx, SV *buf_sv, int maxlen)
+{
+    SV   *my_sv = FILTER_DATA(idx);
+    char *p;
+    char *end;
+    int n = FILTER_READ(idx + 1, buf_sv, maxlen);
+
+    if (n<=0) return n;
+
+    p = SvPV_force_nolen(buf_sv);
+    end = p + SvCUR(buf_sv);
+    while (p < end) {
+       if (*p == 'o') *p = 'e';
+       p++;
+    }
+    return SvCUR(buf_sv);
+}
+
+
 XS(XS_XS__APItest__XSUB_XS_VERSION_undef);
 XS(XS_XS__APItest__XSUB_XS_VERSION_empty);
 XS(XS_XS__APItest__XSUB_XS_APIVERSION_invalid);
@@ -661,6 +1028,67 @@ INCLUDE: const-xs.inc
 
 INCLUDE: numeric.xs
 
+MODULE = XS::APItest::utf8     PACKAGE = XS::APItest::utf8
+
+int
+bytes_cmp_utf8(bytes, utf8)
+       SV *bytes
+       SV *utf8
+    PREINIT:
+       const U8 *b;
+       STRLEN blen;
+       const U8 *u;
+       STRLEN ulen;
+    CODE:
+       b = (const U8 *)SvPVbyte(bytes, blen);
+       u = (const U8 *)SvPVbyte(utf8, ulen);
+       RETVAL = bytes_cmp_utf8(b, blen, u, ulen);
+    OUTPUT:
+       RETVAL
+
+MODULE = XS::APItest:Overload  PACKAGE = XS::APItest::Overload
+
+void
+amagic_deref_call(sv, what)
+       SV *sv
+       int what
+    PPCODE:
+       /* The reference is owned by something else.  */
+       PUSHs(amagic_deref_call(sv, what));
+
+# I'd certainly like to discourage the use of this macro, given that we now
+# have amagic_deref_call
+
+void
+tryAMAGICunDEREF_var(sv, what)
+       SV *sv
+       int what
+    PPCODE:
+       {
+           SV **sp = &sv;
+           switch(what) {
+           case to_av_amg:
+               tryAMAGICunDEREF(to_av);
+               break;
+           case to_cv_amg:
+               tryAMAGICunDEREF(to_cv);
+               break;
+           case to_gv_amg:
+               tryAMAGICunDEREF(to_gv);
+               break;
+           case to_hv_amg:
+               tryAMAGICunDEREF(to_hv);
+               break;
+           case to_sv_amg:
+               tryAMAGICunDEREF(to_sv);
+               break;
+           default:
+               croak("Invalid value %d passed to tryAMAGICunDEREF_var", what);
+           }
+       }
+       /* The reference is owned by something else.  */
+       PUSHs(sv);
+
 MODULE = XS::APItest           PACKAGE = XS::APItest::XSUB
 
 BOOT:
@@ -948,9 +1376,7 @@ refcounted_he_exists(key, level=0)
        if (level) {
            croak("level must be zero, not %"IVdf, level);
        }
-       RETVAL = (Perl_refcounted_he_fetch(aTHX_ PL_curcop->cop_hints_hash,
-                                          key, NULL, 0, 0, 0)
-                 != &PL_sv_placeholder);
+       RETVAL = (cop_hints_fetch_sv(PL_curcop, key, 0, 0) != &PL_sv_placeholder);
        OUTPUT:
        RETVAL
 
@@ -962,8 +1388,7 @@ refcounted_he_fetch(key, level=0)
        if (level) {
            croak("level must be zero, not %"IVdf, level);
        }
-       RETVAL = Perl_refcounted_he_fetch(aTHX_ PL_curcop->cop_hints_hash, key,
-                                         NULL, 0, 0, 0);
+       RETVAL = cop_hints_fetch_sv(PL_curcop, key, 0, 0);
        SvREFCNT_inc(RETVAL);
        OUTPUT:
        RETVAL
@@ -1048,6 +1473,104 @@ MODULE = XS::APItest            PACKAGE = XS::APItest
 
 PROTOTYPES: DISABLE
 
+HV *
+xop_custom_ops ()
+    CODE:
+        RETVAL = PL_custom_ops;
+    OUTPUT:
+        RETVAL
+
+HV *
+xop_custom_op_names ()
+    CODE:
+        PL_custom_op_names = newHV();
+        RETVAL = PL_custom_op_names;
+    OUTPUT:
+        RETVAL
+
+HV *
+xop_custom_op_descs ()
+    CODE:
+        PL_custom_op_descs = newHV();
+        RETVAL = PL_custom_op_descs;
+    OUTPUT:
+        RETVAL
+
+void
+xop_register ()
+    CODE:
+        XopENTRY_set(&my_xop, xop_name, "my_xop");
+        XopENTRY_set(&my_xop, xop_desc, "XOP for testing");
+        XopENTRY_set(&my_xop, xop_class, OA_UNOP);
+        XopENTRY_set(&my_xop, xop_peep, peep_xop);
+        Perl_custom_op_register(aTHX_ pp_xop, &my_xop);
+
+void
+xop_clear ()
+    CODE:
+        XopDISABLE(&my_xop, xop_name);
+        XopDISABLE(&my_xop, xop_desc);
+        XopDISABLE(&my_xop, xop_class);
+        XopDISABLE(&my_xop, xop_peep);
+
+IV
+xop_my_xop ()
+    CODE:
+        RETVAL = PTR2IV(&my_xop);
+    OUTPUT:
+        RETVAL
+
+IV
+xop_ppaddr ()
+    CODE:
+        RETVAL = PTR2IV(pp_xop);
+    OUTPUT:
+        RETVAL
+
+IV
+xop_OA_UNOP ()
+    CODE:
+        RETVAL = OA_UNOP;
+    OUTPUT:
+        RETVAL
+
+AV *
+xop_build_optree ()
+    CODE:
+        dMY_CXT;
+        UNOP *unop;
+        OP *kid;
+
+        MY_CXT.xop_record = newAV();
+
+        kid = newSVOP(OP_CONST, 0, newSViv(42));
+        
+        NewOp(1102, unop, 1, UNOP);
+        unop->op_type       = OP_CUSTOM;
+        unop->op_ppaddr     = pp_xop;
+        unop->op_flags      = OPf_KIDS;
+        unop->op_private    = 0;
+        unop->op_first      = kid;
+        unop->op_next       = NULL;
+        kid->op_next        = (OP*)unop;
+
+        av_push(MY_CXT.xop_record, newSVpvf("unop:%"UVxf, PTR2UV(unop)));
+        av_push(MY_CXT.xop_record, newSVpvf("kid:%"UVxf, PTR2UV(kid)));
+
+        av_push(MY_CXT.xop_record, newSVpvf("NAME:%s", OP_NAME((OP*)unop)));
+        av_push(MY_CXT.xop_record, newSVpvf("DESC:%s", OP_DESC((OP*)unop)));
+        av_push(MY_CXT.xop_record, newSVpvf("CLASS:%d", (int)OP_CLASS((OP*)unop)));
+
+        PL_rpeepp(aTHX_ kid);
+
+        FreeOp(kid);
+        FreeOp(unop);
+
+        RETVAL = MY_CXT.xop_record;
+        MY_CXT.xop_record = NULL;
+    OUTPUT:
+        RETVAL
+
 BOOT:
 {
     MY_CXT_INIT;
@@ -1058,18 +1581,18 @@ BOOT:
     MY_CXT.bhkav = get_av("XS::APItest::bhkav", GV_ADDMULTI);
     MY_CXT.bhk_record = 0;
 
-    BhkENTRY_set(&bhk_test, start, blockhook_test_start);
-    BhkENTRY_set(&bhk_test, pre_end, blockhook_test_pre_end);
-    BhkENTRY_set(&bhk_test, post_end, blockhook_test_post_end);
-    BhkENTRY_set(&bhk_test, eval, blockhook_test_eval);
+    BhkENTRY_set(&bhk_test, bhk_start, blockhook_test_start);
+    BhkENTRY_set(&bhk_test, bhk_pre_end, blockhook_test_pre_end);
+    BhkENTRY_set(&bhk_test, bhk_post_end, blockhook_test_post_end);
+    BhkENTRY_set(&bhk_test, bhk_eval, blockhook_test_eval);
     Perl_blockhook_register(aTHX_ &bhk_test);
 
     MY_CXT.cscgv = gv_fetchpvs("XS::APItest::COMPILE_SCOPE_CONTAINER",
         GV_ADDMULTI, SVt_PVAV);
     MY_CXT.cscav = GvAV(MY_CXT.cscgv);
 
-    BhkENTRY_set(&bhk_csc, start, blockhook_csc_start);
-    BhkENTRY_set(&bhk_csc, pre_end, blockhook_csc_pre_end);
+    BhkENTRY_set(&bhk_csc, bhk_start, blockhook_csc_start);
+    BhkENTRY_set(&bhk_csc, bhk_pre_end, blockhook_csc_pre_end);
     Perl_blockhook_register(aTHX_ &bhk_csc);
 
     MY_CXT.peep_recorder = newAV();
@@ -1085,6 +1608,7 @@ void
 CLONE(...)
     CODE:
     MY_CXT_CLONE;
+    PERL_UNUSED_VAR(items);
     MY_CXT.sv = newSVpv("initial_clone",0);
     MY_CXT.cscgv = gv_fetchpvs("XS::APItest::COMPILE_SCOPE_CONTAINER",
         GV_ADDMULTI, SVt_PVAV);
@@ -1412,12 +1936,12 @@ my_caller(level)
         gv = CvGV(dbcx->blk_sub.cv);
         ST(3) = isGV(gv) ? sv_2mortal(newSVpv(GvNAME(gv), 0)) : &PL_sv_undef;
 
-        ST(4) = cop_hints_fetchpvs(cx->blk_oldcop, "foo");
-        ST(5) = cop_hints_fetchpvn(cx->blk_oldcop, "foo", 3, 0, 0);
-        ST(6) = cop_hints_fetchsv(cx->blk_oldcop, 
-                sv_2mortal(newSVpvn("foo", 3)), 0);
+        ST(4) = cop_hints_fetch_pvs(cx->blk_oldcop, "foo", 0);
+        ST(5) = cop_hints_fetch_pvn(cx->blk_oldcop, "foo", 3, 0, 0);
+        ST(6) = cop_hints_fetch_sv(cx->blk_oldcop, 
+                sv_2mortal(newSVpvn("foo", 3)), 0, 0);
 
-        hv = cop_hints_2hv(cx->blk_oldcop);
+        hv = cop_hints_2hv(cx->blk_oldcop, 0);
         ST(7) = hv ? sv_2mortal(newRV_noinc((SV *)hv)) : &PL_sv_undef;
 
         XSRETURN(8);
@@ -1489,6 +2013,17 @@ my_exit(int exitcode)
         PPCODE:
         my_exit(exitcode);
 
+U8
+first_byte(sv)
+       SV *sv
+   CODE:
+    char *s;
+    STRLEN len;
+       s = SvPVbyte(sv, len);
+       RETVAL = s[0];
+   OUTPUT:
+    RETVAL
+
 I32
 sv_count()
         CODE:
@@ -1511,63 +2046,63 @@ test_magic_chain()
        MAGIC *callmg, *uvarmg;
     CODE:
        sv = sv_2mortal(newSV(0));
-       if (SvTYPE(sv) >= SVt_PVMG) croak("fail");
-       if (SvMAGICAL(sv)) croak("fail");
+       if (SvTYPE(sv) >= SVt_PVMG) croak_fail();
+       if (SvMAGICAL(sv)) croak_fail();
        sv_magic(sv, &PL_sv_yes, PERL_MAGIC_checkcall, (char*)&callmg, 0);
-       if (SvTYPE(sv) < SVt_PVMG) croak("fail");
-       if (!SvMAGICAL(sv)) croak("fail");
-       if (mg_find(sv, PERL_MAGIC_uvar)) croak("fail");
+       if (SvTYPE(sv) < SVt_PVMG) croak_fail();
+       if (!SvMAGICAL(sv)) croak_fail();
+       if (mg_find(sv, PERL_MAGIC_uvar)) croak_fail();
        callmg = mg_find(sv, PERL_MAGIC_checkcall);
-       if (!callmg) croak("fail");
+       if (!callmg) croak_fail();
        if (callmg->mg_obj != &PL_sv_yes || callmg->mg_ptr != (char*)&callmg)
-           croak("fail");
+           croak_fail();
        sv_magic(sv, &PL_sv_no, PERL_MAGIC_uvar, (char*)&uvarmg, 0);
-       if (SvTYPE(sv) < SVt_PVMG) croak("fail");
-       if (!SvMAGICAL(sv)) croak("fail");
-       if (mg_find(sv, PERL_MAGIC_checkcall) != callmg) croak("fail");
+       if (SvTYPE(sv) < SVt_PVMG) croak_fail();
+       if (!SvMAGICAL(sv)) croak_fail();
+       if (mg_find(sv, PERL_MAGIC_checkcall) != callmg) croak_fail();
        uvarmg = mg_find(sv, PERL_MAGIC_uvar);
-       if (!uvarmg) croak("fail");
+       if (!uvarmg) croak_fail();
        if (callmg->mg_obj != &PL_sv_yes || callmg->mg_ptr != (char*)&callmg)
-           croak("fail");
+           croak_fail();
        if (uvarmg->mg_obj != &PL_sv_no || uvarmg->mg_ptr != (char*)&uvarmg)
-           croak("fail");
+           croak_fail();
        mg_free_type(sv, PERL_MAGIC_vec);
-       if (SvTYPE(sv) < SVt_PVMG) croak("fail");
-       if (!SvMAGICAL(sv)) croak("fail");
-       if (mg_find(sv, PERL_MAGIC_checkcall) != callmg) croak("fail");
-       if (mg_find(sv, PERL_MAGIC_uvar) != uvarmg) croak("fail");
+       if (SvTYPE(sv) < SVt_PVMG) croak_fail();
+       if (!SvMAGICAL(sv)) croak_fail();
+       if (mg_find(sv, PERL_MAGIC_checkcall) != callmg) croak_fail();
+       if (mg_find(sv, PERL_MAGIC_uvar) != uvarmg) croak_fail();
        if (callmg->mg_obj != &PL_sv_yes || callmg->mg_ptr != (char*)&callmg)
-           croak("fail");
+           croak_fail();
        if (uvarmg->mg_obj != &PL_sv_no || uvarmg->mg_ptr != (char*)&uvarmg)
-           croak("fail");
+           croak_fail();
        mg_free_type(sv, PERL_MAGIC_uvar);
-       if (SvTYPE(sv) < SVt_PVMG) croak("fail");
-       if (!SvMAGICAL(sv)) croak("fail");
-       if (mg_find(sv, PERL_MAGIC_checkcall) != callmg) croak("fail");
-       if (mg_find(sv, PERL_MAGIC_uvar)) croak("fail");
+       if (SvTYPE(sv) < SVt_PVMG) croak_fail();
+       if (!SvMAGICAL(sv)) croak_fail();
+       if (mg_find(sv, PERL_MAGIC_checkcall) != callmg) croak_fail();
+       if (mg_find(sv, PERL_MAGIC_uvar)) croak_fail();
        if (callmg->mg_obj != &PL_sv_yes || callmg->mg_ptr != (char*)&callmg)
-           croak("fail");
+           croak_fail();
        sv_magic(sv, &PL_sv_no, PERL_MAGIC_uvar, (char*)&uvarmg, 0);
-       if (SvTYPE(sv) < SVt_PVMG) croak("fail");
-       if (!SvMAGICAL(sv)) croak("fail");
-       if (mg_find(sv, PERL_MAGIC_checkcall) != callmg) croak("fail");
+       if (SvTYPE(sv) < SVt_PVMG) croak_fail();
+       if (!SvMAGICAL(sv)) croak_fail();
+       if (mg_find(sv, PERL_MAGIC_checkcall) != callmg) croak_fail();
        uvarmg = mg_find(sv, PERL_MAGIC_uvar);
-       if (!uvarmg) croak("fail");
+       if (!uvarmg) croak_fail();
        if (callmg->mg_obj != &PL_sv_yes || callmg->mg_ptr != (char*)&callmg)
-           croak("fail");
+           croak_fail();
        if (uvarmg->mg_obj != &PL_sv_no || uvarmg->mg_ptr != (char*)&uvarmg)
-           croak("fail");
+           croak_fail();
        mg_free_type(sv, PERL_MAGIC_checkcall);
-       if (SvTYPE(sv) < SVt_PVMG) croak("fail");
-       if (!SvMAGICAL(sv)) croak("fail");
-       if (mg_find(sv, PERL_MAGIC_uvar) != uvarmg) croak("fail");
-       if (mg_find(sv, PERL_MAGIC_checkcall)) croak("fail");
+       if (SvTYPE(sv) < SVt_PVMG) croak_fail();
+       if (!SvMAGICAL(sv)) croak_fail();
+       if (mg_find(sv, PERL_MAGIC_uvar) != uvarmg) croak_fail();
+       if (mg_find(sv, PERL_MAGIC_checkcall)) croak_fail();
        if (uvarmg->mg_obj != &PL_sv_no || uvarmg->mg_ptr != (char*)&uvarmg)
-           croak("fail");
+           croak_fail();
        mg_free_type(sv, PERL_MAGIC_uvar);
-       if (SvMAGICAL(sv)) croak("fail");
-       if (mg_find(sv, PERL_MAGIC_checkcall)) croak("fail");
-       if (mg_find(sv, PERL_MAGIC_uvar)) croak("fail");
+       if (SvMAGICAL(sv)) croak_fail();
+       if (mg_find(sv, PERL_MAGIC_checkcall)) croak_fail();
+       if (mg_find(sv, PERL_MAGIC_uvar)) croak_fail();
 
 void
 test_op_contextualize()
@@ -1579,19 +2114,19 @@ test_op_contextualize()
        o = op_contextualize(o, G_SCALAR);
        if (o->op_type != OP_CONST ||
                (o->op_flags & OPf_WANT) != OPf_WANT_SCALAR)
-           croak("fail");
+           croak_fail();
        op_free(o);
        o = newSVOP(OP_CONST, 0, newSViv(0));
        o->op_flags &= ~OPf_WANT;
        o = op_contextualize(o, G_ARRAY);
        if (o->op_type != OP_CONST ||
                (o->op_flags & OPf_WANT) != OPf_WANT_LIST)
-           croak("fail");
+           croak_fail();
        op_free(o);
        o = newSVOP(OP_CONST, 0, newSViv(0));
        o->op_flags &= ~OPf_WANT;
        o = op_contextualize(o, G_VOID);
-       if (o->op_type != OP_NULL) croak("fail");
+       if (o->op_type != OP_NULL) croak_fail();
        op_free(o);
 
 void
@@ -1606,53 +2141,53 @@ test_rv2cv_op_cv()
        troc_cv = get_cv("XS::APItest::test_rv2cv_op_cv", 0);
        wibble_gv = gv_fetchpv("XS::APItest::wibble", 0, SVt_PVGV);
        o = newCVREF(0, newGVOP(OP_GV, 0, troc_gv));
-       if (rv2cv_op_cv(o, 0) != troc_cv) croak("fail");
+       if (rv2cv_op_cv(o, 0) != troc_cv) croak_fail();
        if (rv2cv_op_cv(o, RV2CVOPCV_RETURN_NAME_GV) != (CV*)troc_gv)
-           croak("fail");
+           croak_fail();
        o->op_private |= OPpENTERSUB_AMPER;
-       if (rv2cv_op_cv(o, 0)) croak("fail");
-       if (rv2cv_op_cv(o, RV2CVOPCV_RETURN_NAME_GV)) croak("fail");
+       if (rv2cv_op_cv(o, 0)) croak_fail();
+       if (rv2cv_op_cv(o, RV2CVOPCV_RETURN_NAME_GV)) croak_fail();
        o->op_private &= ~OPpENTERSUB_AMPER;
-       if (cUNOPx(o)->op_first->op_private & OPpEARLY_CV) croak("fail");
-       if (rv2cv_op_cv(o, RV2CVOPCV_MARK_EARLY) != troc_cv) croak("fail");
-       if (cUNOPx(o)->op_first->op_private & OPpEARLY_CV) croak("fail");
+       if (cUNOPx(o)->op_first->op_private & OPpEARLY_CV) croak_fail();
+       if (rv2cv_op_cv(o, RV2CVOPCV_MARK_EARLY) != troc_cv) croak_fail();
+       if (cUNOPx(o)->op_first->op_private & OPpEARLY_CV) croak_fail();
        op_free(o);
        o = newSVOP(OP_CONST, 0, newSVpv("XS::APItest::test_rv2cv_op_cv", 0));
        o->op_private = OPpCONST_BARE;
        o = newCVREF(0, o);
-       if (rv2cv_op_cv(o, 0) != troc_cv) croak("fail");
+       if (rv2cv_op_cv(o, 0) != troc_cv) croak_fail();
        if (rv2cv_op_cv(o, RV2CVOPCV_RETURN_NAME_GV) != (CV*)troc_gv)
-           croak("fail");
+           croak_fail();
        o->op_private |= OPpENTERSUB_AMPER;
-       if (rv2cv_op_cv(o, 0)) croak("fail");
-       if (rv2cv_op_cv(o, RV2CVOPCV_RETURN_NAME_GV)) croak("fail");
+       if (rv2cv_op_cv(o, 0)) croak_fail();
+       if (rv2cv_op_cv(o, RV2CVOPCV_RETURN_NAME_GV)) croak_fail();
        op_free(o);
        o = newCVREF(0, newSVOP(OP_CONST, 0, newRV_inc((SV*)troc_cv)));
-       if (rv2cv_op_cv(o, 0) != troc_cv) croak("fail");
+       if (rv2cv_op_cv(o, 0) != troc_cv) croak_fail();
        if (rv2cv_op_cv(o, RV2CVOPCV_RETURN_NAME_GV) != (CV*)troc_gv)
-           croak("fail");
+           croak_fail();
        o->op_private |= OPpENTERSUB_AMPER;
-       if (rv2cv_op_cv(o, 0)) croak("fail");
-       if (rv2cv_op_cv(o, RV2CVOPCV_RETURN_NAME_GV)) croak("fail");
+       if (rv2cv_op_cv(o, 0)) croak_fail();
+       if (rv2cv_op_cv(o, RV2CVOPCV_RETURN_NAME_GV)) croak_fail();
        o->op_private &= ~OPpENTERSUB_AMPER;
-       if (cUNOPx(o)->op_first->op_private & OPpEARLY_CV) croak("fail");
-       if (rv2cv_op_cv(o, RV2CVOPCV_MARK_EARLY) != troc_cv) croak("fail");
-       if (cUNOPx(o)->op_first->op_private & OPpEARLY_CV) croak("fail");
+       if (cUNOPx(o)->op_first->op_private & OPpEARLY_CV) croak_fail();
+       if (rv2cv_op_cv(o, RV2CVOPCV_MARK_EARLY) != troc_cv) croak_fail();
+       if (cUNOPx(o)->op_first->op_private & OPpEARLY_CV) croak_fail();
        op_free(o);
        o = newCVREF(0, newUNOP(OP_RAND, 0, newSVOP(OP_CONST, 0, newSViv(0))));
-       if (rv2cv_op_cv(o, 0)) croak("fail");
-       if (rv2cv_op_cv(o, RV2CVOPCV_RETURN_NAME_GV)) croak("fail");
+       if (rv2cv_op_cv(o, 0)) croak_fail();
+       if (rv2cv_op_cv(o, RV2CVOPCV_RETURN_NAME_GV)) croak_fail();
        o->op_private |= OPpENTERSUB_AMPER;
-       if (rv2cv_op_cv(o, 0)) croak("fail");
-       if (rv2cv_op_cv(o, RV2CVOPCV_RETURN_NAME_GV)) croak("fail");
+       if (rv2cv_op_cv(o, 0)) croak_fail();
+       if (rv2cv_op_cv(o, RV2CVOPCV_RETURN_NAME_GV)) croak_fail();
        o->op_private &= ~OPpENTERSUB_AMPER;
-       if (cUNOPx(o)->op_first->op_private & OPpEARLY_CV) croak("fail");
-       if (rv2cv_op_cv(o, RV2CVOPCV_MARK_EARLY)) croak("fail");
-       if (cUNOPx(o)->op_first->op_private & OPpEARLY_CV) croak("fail");
+       if (cUNOPx(o)->op_first->op_private & OPpEARLY_CV) croak_fail();
+       if (rv2cv_op_cv(o, RV2CVOPCV_MARK_EARLY)) croak_fail();
+       if (cUNOPx(o)->op_first->op_private & OPpEARLY_CV) croak_fail();
        op_free(o);
        o = newUNOP(OP_RAND, 0, newSVOP(OP_CONST, 0, newSViv(0)));
-       if (rv2cv_op_cv(o, 0)) croak("fail");
-       if (rv2cv_op_cv(o, RV2CVOPCV_RETURN_NAME_GV)) croak("fail");
+       if (rv2cv_op_cv(o, 0)) croak_fail();
+       if (rv2cv_op_cv(o, RV2CVOPCV_RETURN_NAME_GV)) croak_fail();
        op_free(o);
 
 void
@@ -1665,7 +2200,8 @@ test_cv_getset_call_checker()
 #define check_cc(cv, xckfun, xckobj) \
     do { \
        cv_get_call_checker((cv), &ckfun, &ckobj); \
-       if (ckfun != (xckfun) || ckobj != (xckobj)) croak("fail"); \
+       if (ckfun != (xckfun)) croak_fail_ne(FPTR2DPTR(void *, ckfun), xckfun); \
+       if (ckobj != (xckobj)) croak_fail_ne(FPTR2DPTR(void *, ckobj), xckobj); \
     } while(0)
        troc_cv = get_cv("XS::APItest::test_rv2cv_op_cv", 0);
        tsh_cv = get_cv("XS::APItest::test_savehints", 0);
@@ -1686,8 +2222,8 @@ test_cv_getset_call_checker()
                                    (SV*)troc_cv);
        check_cc(troc_cv, Perl_ck_entersub_args_proto_or_list, (SV*)troc_cv);
        check_cc(tsh_cv, Perl_ck_entersub_args_proto_or_list, (SV*)tsh_cv);
-       if (SvMAGICAL((SV*)troc_cv) || SvMAGIC((SV*)troc_cv)) croak("fail");
-       if (SvMAGICAL((SV*)tsh_cv) || SvMAGIC((SV*)tsh_cv)) croak("fail");
+       if (SvMAGICAL((SV*)troc_cv) || SvMAGIC((SV*)troc_cv)) croak_fail();
+       if (SvMAGICAL((SV*)tsh_cv) || SvMAGIC((SV*)tsh_cv)) croak_fail();
 #undef check_cc
 
 void
@@ -1720,6 +2256,120 @@ cv_set_call_checker_multi_sum(CV *cv)
        cv_set_call_checker(cv, THX_ck_entersub_multi_sum, &PL_sv_undef);
 
 void
+test_cophh()
+    PREINIT:
+       COPHH *a, *b;
+    CODE:
+#define check_ph(EXPR) \
+           do { if((EXPR) != &PL_sv_placeholder) croak("fail"); } while(0)
+#define check_iv(EXPR, EXPECT) \
+           do { if(SvIV(EXPR) != (EXPECT)) croak("fail"); } while(0)
+#define msvpvs(STR) sv_2mortal(newSVpvs(STR))
+#define msviv(VALUE) sv_2mortal(newSViv(VALUE))
+       a = cophh_new_empty();
+       check_ph(cophh_fetch_pvn(a, "foo_1", 5, 0, 0));
+       check_ph(cophh_fetch_pvs(a, "foo_1", 0));
+       check_ph(cophh_fetch_pv(a, "foo_1", 0, 0));
+       check_ph(cophh_fetch_sv(a, msvpvs("foo_1"), 0, 0));
+       a = cophh_store_pvn(a, "foo_1abc", 5, 0, msviv(111), 0);
+       a = cophh_store_pvs(a, "foo_2", msviv(222), 0);
+       a = cophh_store_pv(a, "foo_3", 0, msviv(333), 0);
+       a = cophh_store_sv(a, msvpvs("foo_4"), 0, msviv(444), 0);
+       check_iv(cophh_fetch_pvn(a, "foo_1xyz", 5, 0, 0), 111);
+       check_iv(cophh_fetch_pvs(a, "foo_1", 0), 111);
+       check_iv(cophh_fetch_pv(a, "foo_1", 0, 0), 111);
+       check_iv(cophh_fetch_sv(a, msvpvs("foo_1"), 0, 0), 111);
+       check_iv(cophh_fetch_pvs(a, "foo_2", 0), 222);
+       check_iv(cophh_fetch_pvs(a, "foo_3", 0), 333);
+       check_iv(cophh_fetch_pvs(a, "foo_4", 0), 444);
+       check_ph(cophh_fetch_pvs(a, "foo_5", 0));
+       b = cophh_copy(a);
+       b = cophh_store_pvs(b, "foo_1", msviv(1111), 0);
+       check_iv(cophh_fetch_pvs(a, "foo_1", 0), 111);
+       check_iv(cophh_fetch_pvs(a, "foo_2", 0), 222);
+       check_iv(cophh_fetch_pvs(a, "foo_3", 0), 333);
+       check_iv(cophh_fetch_pvs(a, "foo_4", 0), 444);
+       check_ph(cophh_fetch_pvs(a, "foo_5", 0));
+       check_iv(cophh_fetch_pvs(b, "foo_1", 0), 1111);
+       check_iv(cophh_fetch_pvs(b, "foo_2", 0), 222);
+       check_iv(cophh_fetch_pvs(b, "foo_3", 0), 333);
+       check_iv(cophh_fetch_pvs(b, "foo_4", 0), 444);
+       check_ph(cophh_fetch_pvs(b, "foo_5", 0));
+       a = cophh_delete_pvn(a, "foo_1abc", 5, 0, 0);
+       a = cophh_delete_pvs(a, "foo_2", 0);
+       b = cophh_delete_pv(b, "foo_3", 0, 0);
+       b = cophh_delete_sv(b, msvpvs("foo_4"), 0, 0);
+       check_ph(cophh_fetch_pvs(a, "foo_1", 0));
+       check_ph(cophh_fetch_pvs(a, "foo_2", 0));
+       check_iv(cophh_fetch_pvs(a, "foo_3", 0), 333);
+       check_iv(cophh_fetch_pvs(a, "foo_4", 0), 444);
+       check_ph(cophh_fetch_pvs(a, "foo_5", 0));
+       check_iv(cophh_fetch_pvs(b, "foo_1", 0), 1111);
+       check_iv(cophh_fetch_pvs(b, "foo_2", 0), 222);
+       check_ph(cophh_fetch_pvs(b, "foo_3", 0));
+       check_ph(cophh_fetch_pvs(b, "foo_4", 0));
+       check_ph(cophh_fetch_pvs(b, "foo_5", 0));
+       b = cophh_delete_pvs(b, "foo_3", 0);
+       b = cophh_delete_pvs(b, "foo_5", 0);
+       check_iv(cophh_fetch_pvs(b, "foo_1", 0), 1111);
+       check_iv(cophh_fetch_pvs(b, "foo_2", 0), 222);
+       check_ph(cophh_fetch_pvs(b, "foo_3", 0));
+       check_ph(cophh_fetch_pvs(b, "foo_4", 0));
+       check_ph(cophh_fetch_pvs(b, "foo_5", 0));
+       cophh_free(b);
+       check_ph(cophh_fetch_pvs(a, "foo_1", 0));
+       check_ph(cophh_fetch_pvs(a, "foo_2", 0));
+       check_iv(cophh_fetch_pvs(a, "foo_3", 0), 333);
+       check_iv(cophh_fetch_pvs(a, "foo_4", 0), 444);
+       check_ph(cophh_fetch_pvs(a, "foo_5", 0));
+       a = cophh_store_pvs(a, "foo_1", msviv(11111), COPHH_KEY_UTF8);
+       a = cophh_store_pvs(a, "foo_\xaa", msviv(123), 0);
+       a = cophh_store_pvs(a, "foo_\xc2\xbb", msviv(456), COPHH_KEY_UTF8);
+       a = cophh_store_pvs(a, "foo_\xc3\x8c", msviv(789), COPHH_KEY_UTF8);
+       a = cophh_store_pvs(a, "foo_\xd9\xa6", msviv(666), COPHH_KEY_UTF8);
+       check_iv(cophh_fetch_pvs(a, "foo_1", 0), 11111);
+       check_iv(cophh_fetch_pvs(a, "foo_1", COPHH_KEY_UTF8), 11111);
+       check_iv(cophh_fetch_pvs(a, "foo_\xaa", 0), 123);
+       check_iv(cophh_fetch_pvs(a, "foo_\xc2\xaa", COPHH_KEY_UTF8), 123);
+       check_ph(cophh_fetch_pvs(a, "foo_\xc2\xaa", 0));
+       check_iv(cophh_fetch_pvs(a, "foo_\xbb", 0), 456);
+       check_iv(cophh_fetch_pvs(a, "foo_\xc2\xbb", COPHH_KEY_UTF8), 456);
+       check_ph(cophh_fetch_pvs(a, "foo_\xc2\xbb", 0));
+       check_iv(cophh_fetch_pvs(a, "foo_\xcc", 0), 789);
+       check_iv(cophh_fetch_pvs(a, "foo_\xc3\x8c", COPHH_KEY_UTF8), 789);
+       check_ph(cophh_fetch_pvs(a, "foo_\xc2\x8c", 0));
+       check_iv(cophh_fetch_pvs(a, "foo_\xd9\xa6", COPHH_KEY_UTF8), 666);
+       check_ph(cophh_fetch_pvs(a, "foo_\xd9\xa6", 0));
+       ENTER;
+       SAVEFREECOPHH(a);
+       LEAVE;
+#undef check_ph
+#undef check_iv
+#undef msvpvs
+#undef msviv
+
+HV *
+example_cophh_2hv()
+    PREINIT:
+       COPHH *a;
+    CODE:
+#define msviv(VALUE) sv_2mortal(newSViv(VALUE))
+       a = cophh_new_empty();
+       a = cophh_store_pvs(a, "foo_0", msviv(999), 0);
+       a = cophh_store_pvs(a, "foo_1", msviv(111), 0);
+       a = cophh_store_pvs(a, "foo_\xaa", msviv(123), 0);
+       a = cophh_store_pvs(a, "foo_\xc2\xbb", msviv(456), COPHH_KEY_UTF8);
+       a = cophh_store_pvs(a, "foo_\xc3\x8c", msviv(789), COPHH_KEY_UTF8);
+       a = cophh_store_pvs(a, "foo_\xd9\xa6", msviv(666), COPHH_KEY_UTF8);
+       a = cophh_delete_pvs(a, "foo_0", 0);
+       a = cophh_delete_pvs(a, "foo_2", 0);
+       RETVAL = cophh_2hv(a, 0);
+       cophh_free(a);
+#undef msviv
+    OUTPUT:
+       RETVAL
+
+void
 test_savehints()
     PREINIT:
        SV **svp, *sv;
@@ -1729,38 +2379,38 @@ test_savehints()
 #define hint_ok(KEY, EXPECT) \
                ((svp = hv_fetchs(GvHV(PL_hintgv), KEY, 0)) && \
                    (sv = *svp) && SvIV(sv) == (EXPECT) && \
-                   (sv = cop_hints_fetchpvs(&PL_compiling, KEY)) && \
+                   (sv = cop_hints_fetch_pvs(&PL_compiling, KEY, 0)) && \
                    SvIV(sv) == (EXPECT))
 #define check_hint(KEY, EXPECT) \
-               do { if (!hint_ok(KEY, EXPECT)) croak("fail"); } while(0)
+               do { if (!hint_ok(KEY, EXPECT)) croak_fail(); } while(0)
        PL_hints |= HINT_LOCALIZE_HH;
        ENTER;
        SAVEHINTS();
        PL_hints &= HINT_INTEGER;
        store_hint("t0", 123);
        store_hint("t1", 456);
-       if (PL_hints & HINT_INTEGER) croak("fail");
+       if (PL_hints & HINT_INTEGER) croak_fail();
        check_hint("t0", 123); check_hint("t1", 456);
        ENTER;
        SAVEHINTS();
-       if (PL_hints & HINT_INTEGER) croak("fail");
+       if (PL_hints & HINT_INTEGER) croak_fail();
        check_hint("t0", 123); check_hint("t1", 456);
        PL_hints |= HINT_INTEGER;
        store_hint("t0", 321);
-       if (!(PL_hints & HINT_INTEGER)) croak("fail");
+       if (!(PL_hints & HINT_INTEGER)) croak_fail();
        check_hint("t0", 321); check_hint("t1", 456);
        LEAVE;
-       if (PL_hints & HINT_INTEGER) croak("fail");
+       if (PL_hints & HINT_INTEGER) croak_fail();
        check_hint("t0", 123); check_hint("t1", 456);
        ENTER;
        SAVEHINTS();
-       if (PL_hints & HINT_INTEGER) croak("fail");
+       if (PL_hints & HINT_INTEGER) croak_fail();
        check_hint("t0", 123); check_hint("t1", 456);
        store_hint("t1", 654);
-       if (PL_hints & HINT_INTEGER) croak("fail");
+       if (PL_hints & HINT_INTEGER) croak_fail();
        check_hint("t0", 123); check_hint("t1", 654);
        LEAVE;
-       if (PL_hints & HINT_INTEGER) croak("fail");
+       if (PL_hints & HINT_INTEGER) croak_fail();
        check_hint("t0", 123); check_hint("t1", 456);
        LEAVE;
 #undef store_hint
@@ -1776,18 +2426,191 @@ test_copyhints()
        ENTER;
        SAVEHINTS();
        sv_setiv_mg(*hv_fetchs(GvHV(PL_hintgv), "t0", 1), 123);
-       if (SvIV(cop_hints_fetchpvs(&PL_compiling, "t0")) != 123) croak("fail");
+       if (SvIV(cop_hints_fetch_pvs(&PL_compiling, "t0", 0)) != 123)
+           croak_fail();
        a = newHVhv(GvHV(PL_hintgv));
        sv_2mortal((SV*)a);
        sv_setiv_mg(*hv_fetchs(a, "t0", 1), 456);
-       if (SvIV(cop_hints_fetchpvs(&PL_compiling, "t0")) != 123) croak("fail");
+       if (SvIV(cop_hints_fetch_pvs(&PL_compiling, "t0", 0)) != 123)
+           croak_fail();
        b = hv_copy_hints_hv(a);
        sv_2mortal((SV*)b);
        sv_setiv_mg(*hv_fetchs(b, "t0", 1), 789);
-       if (SvIV(cop_hints_fetchpvs(&PL_compiling, "t0")) != 789) croak("fail");
+       if (SvIV(cop_hints_fetch_pvs(&PL_compiling, "t0", 0)) != 789)
+           croak_fail();
        LEAVE;
 
 void
+test_op_list()
+    PREINIT:
+       OP *a;
+    CODE:
+#define iv_op(iv) newSVOP(OP_CONST, 0, newSViv(iv))
+#define check_op(o, expect) \
+    do { \
+       if (strcmp(test_op_list_describe(o), (expect))) \
+           croak("fail %s %s", test_op_list_describe(o), (expect)); \
+    } while(0)
+       a = op_append_elem(OP_LIST, NULL, NULL);
+       check_op(a, "");
+       a = op_append_elem(OP_LIST, iv_op(1), a);
+       check_op(a, "const(1).");
+       a = op_append_elem(OP_LIST, NULL, a);
+       check_op(a, "const(1).");
+       a = op_append_elem(OP_LIST, a, iv_op(2));
+       check_op(a, "list[pushmark.const(1).const(2).]");
+       a = op_append_elem(OP_LIST, a, iv_op(3));
+       check_op(a, "list[pushmark.const(1).const(2).const(3).]");
+       a = op_append_elem(OP_LIST, a, NULL);
+       check_op(a, "list[pushmark.const(1).const(2).const(3).]");
+       a = op_append_elem(OP_LIST, NULL, a);
+       check_op(a, "list[pushmark.const(1).const(2).const(3).]");
+       a = op_append_elem(OP_LIST, iv_op(4), a);
+       check_op(a, "list[pushmark.const(4)."
+               "list[pushmark.const(1).const(2).const(3).]]");
+       a = op_append_elem(OP_LIST, a, iv_op(5));
+       check_op(a, "list[pushmark.const(4)."
+               "list[pushmark.const(1).const(2).const(3).]const(5).]");
+       a = op_append_elem(OP_LIST, a, 
+               op_append_elem(OP_LIST, iv_op(7), iv_op(6)));
+       check_op(a, "list[pushmark.const(4)."
+               "list[pushmark.const(1).const(2).const(3).]const(5)."
+               "list[pushmark.const(7).const(6).]]");
+       op_free(a);
+       a = op_append_elem(OP_LINESEQ, iv_op(1), iv_op(2));
+       check_op(a, "lineseq[const(1).const(2).]");
+       a = op_append_elem(OP_LINESEQ, a, iv_op(3));
+       check_op(a, "lineseq[const(1).const(2).const(3).]");
+       op_free(a);
+       a = op_append_elem(OP_LINESEQ,
+               op_append_elem(OP_LIST, iv_op(1), iv_op(2)),
+               iv_op(3));
+       check_op(a, "lineseq[list[pushmark.const(1).const(2).]const(3).]");
+       op_free(a);
+       a = op_prepend_elem(OP_LIST, NULL, NULL);
+       check_op(a, "");
+       a = op_prepend_elem(OP_LIST, a, iv_op(1));
+       check_op(a, "const(1).");
+       a = op_prepend_elem(OP_LIST, a, NULL);
+       check_op(a, "const(1).");
+       a = op_prepend_elem(OP_LIST, iv_op(2), a);
+       check_op(a, "list[pushmark.const(2).const(1).]");
+       a = op_prepend_elem(OP_LIST, iv_op(3), a);
+       check_op(a, "list[pushmark.const(3).const(2).const(1).]");
+       a = op_prepend_elem(OP_LIST, NULL, a);
+       check_op(a, "list[pushmark.const(3).const(2).const(1).]");
+       a = op_prepend_elem(OP_LIST, a, NULL);
+       check_op(a, "list[pushmark.const(3).const(2).const(1).]");
+       a = op_prepend_elem(OP_LIST, a, iv_op(4));
+       check_op(a, "list[pushmark."
+               "list[pushmark.const(3).const(2).const(1).]const(4).]");
+       a = op_prepend_elem(OP_LIST, iv_op(5), a);
+       check_op(a, "list[pushmark.const(5)."
+               "list[pushmark.const(3).const(2).const(1).]const(4).]");
+       a = op_prepend_elem(OP_LIST,
+               op_prepend_elem(OP_LIST, iv_op(6), iv_op(7)), a);
+       check_op(a, "list[pushmark.list[pushmark.const(6).const(7).]const(5)."
+               "list[pushmark.const(3).const(2).const(1).]const(4).]");
+       op_free(a);
+       a = op_prepend_elem(OP_LINESEQ, iv_op(2), iv_op(1));
+       check_op(a, "lineseq[const(2).const(1).]");
+       a = op_prepend_elem(OP_LINESEQ, iv_op(3), a);
+       check_op(a, "lineseq[const(3).const(2).const(1).]");
+       op_free(a);
+       a = op_prepend_elem(OP_LINESEQ, iv_op(3),
+               op_prepend_elem(OP_LIST, iv_op(2), iv_op(1)));
+       check_op(a, "lineseq[const(3).list[pushmark.const(2).const(1).]]");
+       op_free(a);
+       a = op_append_list(OP_LINESEQ, NULL, NULL);
+       check_op(a, "");
+       a = op_append_list(OP_LINESEQ, iv_op(1), a);
+       check_op(a, "const(1).");
+       a = op_append_list(OP_LINESEQ, NULL, a);
+       check_op(a, "const(1).");
+       a = op_append_list(OP_LINESEQ, a, iv_op(2));
+       check_op(a, "lineseq[const(1).const(2).]");
+       a = op_append_list(OP_LINESEQ, a, iv_op(3));
+       check_op(a, "lineseq[const(1).const(2).const(3).]");
+       a = op_append_list(OP_LINESEQ, iv_op(4), a);
+       check_op(a, "lineseq[const(4).const(1).const(2).const(3).]");
+       a = op_append_list(OP_LINESEQ, a, NULL);
+       check_op(a, "lineseq[const(4).const(1).const(2).const(3).]");
+       a = op_append_list(OP_LINESEQ, NULL, a);
+       check_op(a, "lineseq[const(4).const(1).const(2).const(3).]");
+       a = op_append_list(OP_LINESEQ, a,
+               op_append_list(OP_LINESEQ, iv_op(5), iv_op(6)));
+       check_op(a, "lineseq[const(4).const(1).const(2).const(3)."
+               "const(5).const(6).]");
+       op_free(a);
+       a = op_append_list(OP_LINESEQ,
+               op_append_list(OP_LINESEQ, iv_op(1), iv_op(2)),
+               op_append_list(OP_LIST, iv_op(3), iv_op(4)));
+       check_op(a, "lineseq[const(1).const(2)."
+               "list[pushmark.const(3).const(4).]]");
+       op_free(a);
+       a = op_append_list(OP_LINESEQ,
+               op_append_list(OP_LIST, iv_op(1), iv_op(2)),
+               op_append_list(OP_LINESEQ, iv_op(3), iv_op(4)));
+       check_op(a, "lineseq[list[pushmark.const(1).const(2).]"
+               "const(3).const(4).]");
+       op_free(a);
+#undef check_op
+
+void
+test_op_linklist ()
+    PREINIT:
+        OP *o;
+    CODE:
+#define check_ll(o, expect) \
+    STMT_START { \
+       if (strNE(test_op_linklist_describe(o), (expect))) \
+           croak("fail %s %s", test_op_linklist_describe(o), (expect)); \
+    } STMT_END
+        o = iv_op(1);
+        check_ll(o, ".const1");
+        op_free(o);
+
+        o = mkUNOP(OP_NOT, iv_op(1));
+        check_ll(o, ".const1.not");
+        op_free(o);
+
+        o = mkUNOP(OP_NOT, mkUNOP(OP_NEGATE, iv_op(1)));
+        check_ll(o, ".const1.negate.not");
+        op_free(o);
+
+        o = mkBINOP(OP_ADD, iv_op(1), iv_op(2));
+        check_ll(o, ".const1.const2.add");
+        op_free(o);
+
+        o = mkBINOP(OP_ADD, mkUNOP(OP_NOT, iv_op(1)), iv_op(2));
+        check_ll(o, ".const1.not.const2.add");
+        op_free(o);
+
+        o = mkUNOP(OP_NOT, mkBINOP(OP_ADD, iv_op(1), iv_op(2)));
+        check_ll(o, ".const1.const2.add.not");
+        op_free(o);
+
+        o = mkLISTOP(OP_LINESEQ, iv_op(1), iv_op(2), iv_op(3));
+        check_ll(o, ".const1.const2.const3.lineseq");
+        op_free(o);
+
+        o = mkLISTOP(OP_LINESEQ,
+                mkBINOP(OP_ADD, iv_op(1), iv_op(2)),
+                mkUNOP(OP_NOT, iv_op(3)),
+                mkLISTOP(OP_SUBSTR, iv_op(4), iv_op(5), iv_op(6)));
+        check_ll(o, ".const1.const2.add.const3.not"
+                    ".const4.const5.const6.substr.lineseq");
+        op_free(o);
+
+        o = mkBINOP(OP_ADD, iv_op(1), iv_op(2));
+        LINKLIST(o);
+        o = mkBINOP(OP_SUBTRACT, o, iv_op(3));
+        check_ll(o, ".const1.const2.add.const3.subtract");
+        op_free(o);
+#undef check_ll
+#undef iv_op
+
+void
 peep_enable ()
     PREINIT:
        dMY_CXT;
@@ -1821,6 +2644,130 @@ rpeep_record ()
     OUTPUT:
        RETVAL
 
+=pod
+
+multicall_each: call a sub for each item in the list. Used to test MULTICALL
+
+=cut
+
+void
+multicall_each(block,...)
+    SV * block
+PROTOTYPE: &@
+CODE:
+{
+    dMULTICALL;
+    int index;
+    GV *gv;
+    HV *stash;
+    I32 gimme = G_SCALAR;
+    SV **args = &PL_stack_base[ax];
+    CV *cv;
+
+    if(items <= 1) {
+       XSRETURN_UNDEF;
+    }
+    cv = sv_2cv(block, &stash, &gv, 0);
+    if (cv == Nullcv) {
+       croak("multicall_each: not a subroutine reference");
+    }
+    PUSH_MULTICALL(cv);
+    SAVESPTR(GvSV(PL_defgv));
+
+    for(index = 1 ; index < items ; index++) {
+       GvSV(PL_defgv) = args[index];
+       MULTICALL;
+    }
+    POP_MULTICALL;
+    XSRETURN_UNDEF;
+}
+
+#ifdef USE_ITHREADS
+
+void
+clone_with_stack()
+CODE:
+{
+    PerlInterpreter *interp = aTHX; /* The original interpreter */
+    PerlInterpreter *interp_dup;    /* The duplicate interpreter */
+    int oldscope = 1; /* We are responsible for all scopes */
+
+    interp_dup = perl_clone(interp, CLONEf_COPY_STACKS | CLONEf_CLONE_HOST );
+
+    /* destroy old perl */
+    PERL_SET_CONTEXT(interp);
+
+    POPSTACK_TO(PL_mainstack);
+    dounwind(-1);
+    LEAVE_SCOPE(0);
+
+    while (interp->Iscopestack_ix > 1)
+        LEAVE;
+    FREETMPS;
+
+    perl_destruct(interp);
+    perl_free(interp);
+
+    /* switch to new perl */
+    PERL_SET_CONTEXT(interp_dup);
+
+    /* continue after 'clone_with_stack' */
+    interp_dup->Iop = interp_dup->Iop->op_next;
+
+    /* run with new perl */
+    Perl_runops_standard(interp_dup);
+
+    /* We may have additional unclosed scopes if fork() was called
+     * from within a BEGIN block.  See perlfork.pod for more details.
+     * We cannot clean up these other scopes because they belong to a
+     * different interpreter, but we also cannot leave PL_scopestack_ix
+     * dangling because that can trigger an assertion in perl_destruct().
+     */
+    if (PL_scopestack_ix > oldscope) {
+        PL_scopestack[oldscope-1] = PL_scopestack[PL_scopestack_ix-1];
+        PL_scopestack_ix = oldscope;
+    }
+
+    perl_destruct(interp_dup);
+    perl_free(interp_dup);
+
+    /* call the real 'exit' not PerlProc_exit */
+#undef exit
+    exit(0);
+}
+
+#endif /* USE_ITHREDS */
+
+SV*
+take_svref(SVREF sv)
+CODE:
+    RETVAL = newRV_inc(sv);
+OUTPUT:
+    RETVAL
+
+SV*
+take_avref(AV* av)
+CODE:
+    RETVAL = newRV_inc((SV*)av);
+OUTPUT:
+    RETVAL
+
+SV*
+take_hvref(HV* hv)
+CODE:
+    RETVAL = newRV_inc((SV*)hv);
+OUTPUT:
+    RETVAL
+
+
+SV*
+take_cvref(CV* cv)
+CODE:
+    RETVAL = newRV_inc((SV*)cv);
+OUTPUT:
+    RETVAL
+
+
 BOOT:
        {
        HV* stash;
@@ -1842,6 +2789,153 @@ BOOT:
     hintkey_stufftest_sv = newSVpvs_share("XS::APItest/stufftest");
     hintkey_swaptwostmts_sv = newSVpvs_share("XS::APItest/swaptwostmts");
     hintkey_looprest_sv = newSVpvs_share("XS::APItest/looprest");
+    hintkey_scopelessblock_sv = newSVpvs_share("XS::APItest/scopelessblock");
+    hintkey_stmtasexpr_sv = newSVpvs_share("XS::APItest/stmtasexpr");
+    hintkey_stmtsasexpr_sv = newSVpvs_share("XS::APItest/stmtsasexpr");
+    hintkey_loopblock_sv = newSVpvs_share("XS::APItest/loopblock");
+    hintkey_blockasexpr_sv = newSVpvs_share("XS::APItest/blockasexpr");
+    hintkey_swaplabel_sv = newSVpvs_share("XS::APItest/swaplabel");
+    hintkey_labelconst_sv = newSVpvs_share("XS::APItest/labelconst");
+    hintkey_arrayfullexpr_sv = newSVpvs_share("XS::APItest/arrayfullexpr");
+    hintkey_arraylistexpr_sv = newSVpvs_share("XS::APItest/arraylistexpr");
+    hintkey_arraytermexpr_sv = newSVpvs_share("XS::APItest/arraytermexpr");
+    hintkey_arrayarithexpr_sv = newSVpvs_share("XS::APItest/arrayarithexpr");
+    hintkey_arrayexprflags_sv = newSVpvs_share("XS::APItest/arrayexprflags");
     next_keyword_plugin = PL_keyword_plugin;
     PL_keyword_plugin = my_keyword_plugin;
 }
+
+void
+establish_cleanup(...)
+PROTOTYPE: $
+CODE:
+    PERL_UNUSED_VAR(items);
+    croak("establish_cleanup called as a function");
+
+BOOT:
+{
+    CV *estcv = get_cv("XS::APItest::establish_cleanup", 0);
+    cv_set_call_checker(estcv, THX_ck_entersub_establish_cleanup, (SV*)estcv);
+}
+
+void
+postinc(...)
+PROTOTYPE: $
+CODE:
+    PERL_UNUSED_VAR(items);
+    croak("postinc called as a function");
+
+void
+filter()
+CODE:
+    filter_add(filter_call, NULL);
+
+BOOT:
+{
+    CV *asscv = get_cv("XS::APItest::postinc", 0);
+    cv_set_call_checker(asscv, THX_ck_entersub_postinc, (SV*)asscv);
+}
+
+SV *
+lv_temp_object()
+CODE:
+    RETVAL =
+         sv_bless(
+           newRV_noinc(newSV(0)),
+           gv_stashpvs("XS::APItest::TempObj",GV_ADD)
+         );             /* Package defined in test script */
+OUTPUT:
+    RETVAL
+
+void
+fill_hash_with_nulls(HV *hv)
+PREINIT:
+    UV i = 0;
+CODE:
+    for(; i < 1000; ++i) {
+       HE *entry = hv_fetch_ent(hv, sv_2mortal(newSVuv(i)), 1, 0);
+       SvREFCNT_dec(HeVAL(entry));
+       HeVAL(entry) = NULL;
+    }
+
+bool
+SvIsCOW(SV *sv)
+CODE:
+    RETVAL = SvIsCOW(sv);
+OUTPUT:
+    RETVAL
+
+MODULE = XS::APItest           PACKAGE = XS::APItest::Magic
+
+PROTOTYPES: DISABLE
+
+void
+sv_magic_foo(SV *sv, SV *thingy)
+ALIAS:
+    sv_magic_bar = 1
+CODE:
+    sv_magicext(SvRV(sv), NULL, PERL_MAGIC_ext, ix ? &vtbl_bar : &vtbl_foo, (const char *)thingy, 0);
+
+SV *
+mg_find_foo(SV *sv)
+ALIAS:
+    mg_find_bar = 1
+CODE:
+    MAGIC *mg = mg_findext(SvRV(sv), PERL_MAGIC_ext, ix ? &vtbl_bar : &vtbl_foo);
+    RETVAL = mg ? SvREFCNT_inc((SV *)mg->mg_ptr) : &PL_sv_undef;
+OUTPUT:
+    RETVAL
+
+void
+sv_unmagic_foo(SV *sv)
+ALIAS:
+    sv_unmagic_bar = 1
+CODE:
+    sv_unmagicext(SvRV(sv), PERL_MAGIC_ext, ix ? &vtbl_bar : &vtbl_foo);
+
+UV
+test_get_vtbl()
+    PREINIT:
+       MGVTBL *have;
+       MGVTBL *want;
+    CODE:
+#define test_get_this_vtable(name) \
+       want = CAT2(&PL_vtbl_, name); \
+       have = get_vtbl(CAT2(want_vtbl_, name)); \
+       if (have != want) \
+           croak("fail %p!=%p for get_vtbl(want_vtbl_" STRINGIFY(name) ") at " __FILE__ " line %d", have, want, __LINE__)
+
+       test_get_this_vtable(sv);
+       test_get_this_vtable(env);
+       test_get_this_vtable(envelem);
+       test_get_this_vtable(sigelem);
+       test_get_this_vtable(pack);
+       test_get_this_vtable(packelem);
+       test_get_this_vtable(dbline);
+       test_get_this_vtable(isa);
+       test_get_this_vtable(isaelem);
+       test_get_this_vtable(arylen);
+       test_get_this_vtable(mglob);
+       test_get_this_vtable(nkeys);
+       test_get_this_vtable(taint);
+       test_get_this_vtable(substr);
+       test_get_this_vtable(vec);
+       test_get_this_vtable(pos);
+       test_get_this_vtable(bm);
+       test_get_this_vtable(fm);
+       test_get_this_vtable(uvar);
+       test_get_this_vtable(defelem);
+       test_get_this_vtable(regexp);
+       test_get_this_vtable(regdata);
+       test_get_this_vtable(regdatum);
+#ifdef USE_LOCALE_COLLATE
+       test_get_this_vtable(collxfrm);
+#endif
+       test_get_this_vtable(amagic);
+       test_get_this_vtable(amagicelem);
+       test_get_this_vtable(backref);
+       test_get_this_vtable(utf8);
+
+       RETVAL = PTR2UV(get_vtbl(-1));
+    OUTPUT:
+       RETVAL