This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
add SAVEFREECOPHH()
[perl5.git] / ext / XS-APItest / APItest.xs
index 55aa1de..386fda9 100644 (file)
@@ -7,6 +7,7 @@ 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 */
 
@@ -508,6 +509,67 @@ test_op_linklist_describe(OP *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, *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);
+    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)
@@ -521,6 +583,7 @@ 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 int (*next_keyword_plugin)(pTHX_ char *, STRLEN, OP **);
 
 /* low-level parser helpers */
@@ -694,12 +757,8 @@ static OP *THX_parse_keyword_swaptwostmts(pTHX)
 #define parse_keyword_looprest() THX_parse_keyword_looprest(aTHX)
 static OP *THX_parse_keyword_looprest(pTHX)
 {
-    I32 condline;
-    OP *body;
-    condline = CopLINE(PL_curcop);
-    body = parse_stmtseq(0);
-    return newWHILEOP(0, 1, NULL, condline, newSVOP(OP_CONST, 0, &PL_sv_yes),
-                       body, NULL, 1);
+    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)
@@ -720,11 +779,10 @@ static OP *THX_parse_keyword_scopelessblock(pTHX)
 #define parse_keyword_stmtasexpr() THX_parse_keyword_stmtasexpr(aTHX)
 static OP *THX_parse_keyword_stmtasexpr(pTHX)
 {
-    OP *o = parse_fullstmt(0);
-    o = op_prepend_elem(OP_LINESEQ, newOP(OP_ENTER, 0), o);
-    o->op_type = OP_LEAVE;
-    o->op_ppaddr = PL_ppaddr[OP_LEAVE];
-    return o;
+    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)
@@ -738,31 +796,40 @@ static OP *THX_parse_keyword_stmtsasexpr(pTHX)
     lex_read_space(0);
     if(lex_peek_unichar(0) != /*{*/'}') croak("syntax error");
     lex_read_unichar(0);
-    o = op_prepend_elem(OP_LINESEQ, newOP(OP_ENTER, 0), o);
-    o->op_type = OP_LEAVE;
-    o->op_ppaddr = PL_ppaddr[OP_LEAVE];
-    return o;
+    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)
 {
-    I32 condline;
-    OP *body;
-    condline = CopLINE(PL_curcop);
-    body = parse_block(0);
-    return newWHILEOP(0, 1, NULL, condline, newSVOP(OP_CONST, 0, &PL_sv_yes),
-                       body, NULL, 1);
+    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);
-    o = op_prepend_elem(OP_LINESEQ, newOP(OP_ENTER, 0), o);
-    o->op_type = OP_LEAVE;
-    o->op_ppaddr = PL_ppaddr[OP_LEAVE];
-    return o;
+    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));
 }
 
 /* plugin glue */
@@ -821,6 +888,14 @@ static int my_keyword_plugin(pTHX_
                    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 {
        return next_keyword_plugin(aTHX_ keyword_ptr, keyword_len, op_ptr);
     }
@@ -1663,6 +1738,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:
@@ -1839,7 +1925,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);
@@ -1978,7 +2065,9 @@ test_cophh()
        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));
-       cophh_free(a);
+       ENTER;
+       SAVEFREECOPHH(a);
+       LEAVE;
 #undef check_ph
 #undef check_iv
 #undef msvpvs
@@ -2345,6 +2434,32 @@ BOOT:
     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");
     next_keyword_plugin = PL_keyword_plugin;
     PL_keyword_plugin = my_keyword_plugin;
 }
+
+void
+establish_cleanup(...)
+PROTOTYPE: $
+CODE:
+    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:
+    croak("postinc called as a function");
+
+BOOT:
+{
+    CV *asscv = get_cv("XS::APItest::postinc", 0);
+    cv_set_call_checker(asscv, THX_ck_entersub_postinc, (SV*)asscv);
+}