#define PERL_NO_GET_CONTEXT 1 #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #define PERL_VERSION_DECIMAL(r,v,s) (r*1000000 + v*1000 + s) #define PERL_DECIMAL_VERSION \ PERL_VERSION_DECIMAL(PERL_REVISION,PERL_VERSION,PERL_SUBVERSION) #define PERL_VERSION_GE(r,v,s) \ (PERL_DECIMAL_VERSION >= PERL_VERSION_DECIMAL(r,v,s)) #ifndef cBOOL # define cBOOL(x) ((bool)!!(x)) #endif /* !cBOOL */ #ifndef newSVpvs # define newSVpvs(s) newSVpvn(""s"", (sizeof(""s"")-1)) #endif /* !newSVpvs */ #ifndef OpMORESIB_set # define OpMORESIB_set(o, sib) ((o)->op_sibling = (sib)) # define OpLASTSIB_set(o, parent) ((o)->op_sibling = NULL) # define OpMAYBESIB_set(o, sib, parent) ((o)->op_sibling = (sib)) #endif /* !OpMORESIB_set */ #ifndef OpSIBLING # define OpHAS_SIBLING(o) (cBOOL((o)->op_sibling)) # define OpSIBLING(o) (0 + (o)->op_sibling) #endif /* !OpSIBLING */ #define QPFX xAd8NP3gxZglovQRL5Hn_ #define QPFXS STRINGIFY(QPFX) #define QCONCAT0(a,b) a##b #define QCONCAT1(a,b) QCONCAT0(a,b) #define QPFXD(name) QCONCAT1(QPFX, name) #if defined(WIN32) && PERL_VERSION_GE(5,13,6) # define MY_BASE_CALLCONV EXTERN_C # define MY_BASE_CALLCONV_S "EXTERN_C" #else /* !(WIN32 && >= 5.13.6) */ # define MY_BASE_CALLCONV PERL_CALLCONV # define MY_BASE_CALLCONV_S "PERL_CALLCONV" #endif /* !(WIN32 && >= 5.13.6) */ #define MY_EXPORT_CALLCONV MY_BASE_CALLCONV #if defined(WIN32) || defined(__CYGWIN__) # define MY_IMPORT_CALLCONV_S MY_BASE_CALLCONV_S" __declspec(dllimport)" #else # define MY_IMPORT_CALLCONV_S MY_BASE_CALLCONV_S #endif #ifndef rv2cv_op_cv # define RV2CVOPCV_MARK_EARLY 0x00000001 # define RV2CVOPCV_RETURN_NAME_GV 0x00000002 # define Perl_rv2cv_op_cv QPFXD(roc0) # define rv2cv_op_cv(cvop, flags) Perl_rv2cv_op_cv(aTHX_ cvop, flags) MY_EXPORT_CALLCONV CV *QPFXD(roc0)(pTHX_ OP *cvop, U32 flags) { OP *rvop; CV *cv; GV *gv; if(!(cvop->op_type == OP_RV2CV && !(cvop->op_private & OPpENTERSUB_AMPER) && (cvop->op_flags & OPf_KIDS))) return NULL; rvop = cUNOPx(cvop)->op_first; switch(rvop->op_type) { case OP_GV: { gv = cGVOPx_gv(rvop); cv = GvCVu(gv); if(!cv) { if(flags & RV2CVOPCV_MARK_EARLY) rvop->op_private |= OPpEARLY_CV; return NULL; } } break; #if PERL_VERSION_GE(5,11,2) case OP_CONST: { SV *rv = cSVOPx_sv(rvop); if(!SvROK(rv)) return NULL; cv = (CV*)SvRV(rv); gv = NULL; } break; #endif /* >=5.11.2 */ default: { return NULL; } break; } if(SvTYPE((SV*)cv) != SVt_PVCV) return NULL; if(flags & RV2CVOPCV_RETURN_NAME_GV) { if(!CvANON(cv) || !gv) gv = CvGV(cv); return (CV*)gv; } else { return cv; } } # define Q_PROVIDE_RV2CV_OP_CV 1 #endif /* !rv2cv_op_cv */ #ifndef ck_entersub_args_proto_or_list # ifndef newSV_type # define newSV_type(type) THX_newSV_type(aTHX_ type) static SV *THX_newSV_type(pTHX_ svtype type) { SV *sv = newSV(0); (void) SvUPGRADE(sv, type); return sv; } # endif /* !newSV_type */ # ifndef GvCV_set # define GvCV_set(gv, cv) (GvCV(gv) = (cv)) # endif /* !GvCV_set */ # ifndef CvGV_set # define CvGV_set(cv, gv) (CvGV(cv) = (gv)) # endif /* !CvGV_set */ # define entersub_extract_args(eo) THX_entersub_extract_args(aTHX_ eo) static OP *THX_entersub_extract_args(pTHX_ OP *entersubop) { OP *pushop, *aop, *bop, *cop; if(!(entersubop->op_flags & OPf_KIDS)) return NULL; pushop = cUNOPx(entersubop)->op_first; if(!OpHAS_SIBLING(pushop)) { if(!(pushop->op_flags & OPf_KIDS)) return NULL; pushop = cUNOPx(pushop)->op_first; if(!OpHAS_SIBLING(pushop)) return NULL; } for(bop = pushop; (cop = OpSIBLING(bop), OpHAS_SIBLING(cop)); bop = cop) ; if(bop == pushop) return NULL; aop = OpSIBLING(pushop); OpMORESIB_set(pushop, cop); OpLASTSIB_set(bop, NULL); return aop; } # define entersub_inject_args(eo, ao) THX_entersub_inject_args(aTHX_ eo, ao) static void THX_entersub_inject_args(pTHX_ OP *entersubop, OP *aop) { OP *pushop, *bop, *cop; if(!aop) return; if(!(entersubop->op_flags & OPf_KIDS)) { abort: while(aop) { bop = OpSIBLING(aop); op_free(aop); aop = bop; } return; } pushop = cUNOPx(entersubop)->op_first; if(!OpHAS_SIBLING(pushop)) { if(!(pushop->op_flags & OPf_KIDS)) goto abort; pushop = cUNOPx(pushop)->op_first; if(!OpHAS_SIBLING(pushop)) goto abort; } for(bop = aop; (cop = OpSIBLING(bop)); bop = cop) ; OpMORESIB_set(bop, OpSIBLING(pushop)); OpMORESIB_set(pushop, aop); } # define ck_entersub_args_stalk(eo, so) THX_ck_entersub_args_stalk(aTHX_ eo, so) static OP *THX_ck_entersub_args_stalk(pTHX_ OP *entersubop, OP *stalkcvop) { OP *stalkenterop = newLISTOP(OP_LIST, 0, newCVREF(0, stalkcvop), NULL); entersub_inject_args(stalkenterop, entersub_extract_args(entersubop)); stalkenterop = newUNOP(OP_ENTERSUB, OPf_STACKED, stalkenterop); entersub_inject_args(entersubop, entersub_extract_args(stalkenterop)); op_free(stalkenterop); return entersubop; } # define Perl_ck_entersub_args_list QPFXD(eal0) # define ck_entersub_args_list(o) Perl_ck_entersub_args_list(aTHX_ o) MY_EXPORT_CALLCONV OP *QPFXD(eal0)(pTHX_ OP *entersubop) { return ck_entersub_args_stalk(entersubop, newOP(OP_PADANY, 0)); } # define Perl_ck_entersub_args_proto QPFXD(eap0) # define ck_entersub_args_proto(o, gv, sv) \ Perl_ck_entersub_args_proto(aTHX_ o, gv, sv) MY_EXPORT_CALLCONV OP *QPFXD(eap0)(pTHX_ OP *entersubop, GV *namegv, SV *protosv) { const char *proto; STRLEN proto_len; CV *stalkcv; GV *stalkgv; if(SvTYPE(protosv) == SVt_PVCV ? !SvPOK(protosv) : !SvOK(protosv)) croak("panic: ck_entersub_args_proto CV with no proto"); proto = SvPV(protosv, proto_len); stalkcv = (CV*)newSV_type(SVt_PVCV); sv_setpvn((SV*)stalkcv, proto, proto_len); stalkgv = (GV*)sv_2mortal(newSV(0)); gv_init(stalkgv, GvSTASH(namegv), GvNAME(namegv), GvNAMELEN(namegv), 0); GvCV_set(stalkgv, stalkcv); CvGV_set(stalkcv, stalkgv); return ck_entersub_args_stalk(entersubop, newGVOP(OP_GV, 0, stalkgv)); } # define Perl_ck_entersub_args_proto_or_list QPFXD(ean0) # define ck_entersub_args_proto_or_list(o, gv, sv) \ Perl_ck_entersub_args_proto_or_list(aTHX_ o, gv, sv) MY_EXPORT_CALLCONV OP *QPFXD(ean0)(pTHX_ OP *entersubop, GV *namegv, SV *protosv) { if(SvTYPE(protosv) == SVt_PVCV ? SvPOK(protosv) : SvOK(protosv)) return ck_entersub_args_proto(entersubop, namegv, protosv); else return ck_entersub_args_list(entersubop); } # define Q_PROVIDE_CK_ENTERSUB_ARGS_PROTO_OR_LIST 1 #endif /* !ck_entersub_args_proto_or_list */ #ifndef cv_set_call_checker # ifndef Newxz # define Newxz(v,n,t) Newz(0,v,n,t) # endif /* !Newxz */ # ifndef SvMAGIC_set # define SvMAGIC_set(sv, mg) (SvMAGIC(sv) = (mg)) # endif /* !SvMAGIC_set */ # ifndef DPTR2FPTR # define DPTR2FPTR(t,x) ((t)(UV)(x)) # endif /* !DPTR2FPTR */ # ifndef FPTR2DPTR # define FPTR2DPTR(t,x) ((t)(UV)(x)) # endif /* !FPTR2DPTR */ # ifndef op_null # define op_null(o) THX_op_null(aTHX_ o) static void THX_op_null(pTHX_ OP *o) { if(o->op_type == OP_NULL) return; /* must not be used on any op requiring non-trivial clearing */ o->op_targ = o->op_type; o->op_type = OP_NULL; o->op_ppaddr = PL_ppaddr[OP_NULL]; } # endif /* !op_null */ # ifndef mg_findext # define mg_findext(sv, type, vtbl) THX_mg_findext(aTHX_ sv, type, vtbl) static MAGIC *THX_mg_findext(pTHX_ SV *sv, int type, MGVTBL const *vtbl) { MAGIC *mg; if(sv) for(mg = SvMAGIC(sv); mg; mg = mg->mg_moremagic) if(mg->mg_type == type && mg->mg_virtual == vtbl) return mg; return NULL; } # endif /* !mg_findext */ # ifndef sv_unmagicext # define sv_unmagicext(sv, type, vtbl) THX_sv_unmagicext(aTHX_ sv, type, vtbl) static int THX_sv_unmagicext(pTHX_ SV *sv, int type, MGVTBL const *vtbl) { MAGIC *mg, **mgp; if((vtbl && vtbl->svt_free) # ifdef PERL_MAGIC_regex_global || type == PERL_MAGIC_regex_global # endif /* PERL_MAGIC_regex_global */ ) /* exceeded intended usage of this reserve implementation */ return 0; if(SvTYPE(sv) < SVt_PVMG || !SvMAGIC(sv)) return 0; mgp = NULL; for(mg = mgp ? *mgp : SvMAGIC(sv); mg; mg = mgp ? *mgp : SvMAGIC(sv)) { if(mg->mg_type == type && mg->mg_virtual == vtbl) { if(mgp) *mgp = mg->mg_moremagic; else SvMAGIC_set(sv, mg->mg_moremagic); if(mg->mg_flags & MGf_REFCOUNTED) SvREFCNT_dec(mg->mg_obj); Safefree(mg); } else { mgp = &mg->mg_moremagic; } } SvMAGICAL_off(sv); mg_magical(sv); return 0; } # endif /* !sv_unmagicext */ # ifndef sv_magicext # define sv_magicext(sv, obj, type, vtbl, name, namlen) \ THX_sv_magicext(aTHX_ sv, obj, type, vtbl, name, namlen) static MAGIC *THX_sv_magicext(pTHX_ SV *sv, SV *obj, int type, MGVTBL const *vtbl, char const *name, I32 namlen) { MAGIC *mg; if(!(obj == &PL_sv_undef && !name && !namlen)) /* exceeded intended usage of this reserve implementation */ return NULL; Newxz(mg, 1, MAGIC); mg->mg_virtual = (MGVTBL*)vtbl; mg->mg_type = type; mg->mg_obj = &PL_sv_undef; (void) SvUPGRADE(sv, SVt_PVMG); mg->mg_moremagic = SvMAGIC(sv); SvMAGIC_set(sv, mg); SvMAGICAL_off(sv); mg_magical(sv); return mg; } # endif /* !sv_magicext */ # ifndef PERL_MAGIC_ext # define PERL_MAGIC_ext '~' # endif /* !PERL_MAGIC_ext */ # if !PERL_VERSION_GE(5,9,3) typedef OP *(*Perl_check_t)(pTHX_ OP *); # endif /* <5.9.3 */ # if !PERL_VERSION_GE(5,10,1) typedef unsigned Optype; # endif /* <5.10.1 */ # ifndef wrap_op_checker # define wrap_op_checker(c,n,o) THX_wrap_op_checker(aTHX_ c,n,o) static void THX_wrap_op_checker(pTHX_ Optype opcode, Perl_check_t new_checker, Perl_check_t *old_checker_p) { if(*old_checker_p) return; OP_REFCNT_LOCK; if(!*old_checker_p) { *old_checker_p = PL_check[opcode]; PL_check[opcode] = new_checker; } OP_REFCNT_UNLOCK; } # endif /* !wrap_op_checker */ static MGVTBL mgvtbl_checkcall; typedef OP *(*Perl_call_checker)(pTHX_ OP *, GV *, SV *); # define Perl_cv_get_call_checker QPFXD(gcc0) # define cv_get_call_checker(cv, THX_ckfun_p, ckobj_p) \ Perl_cv_get_call_checker(aTHX_ cv, THX_ckfun_p, ckobj_p) MY_EXPORT_CALLCONV void QPFXD(gcc0)(pTHX_ CV *cv, Perl_call_checker *THX_ckfun_p, SV **ckobj_p) { MAGIC *callmg = SvMAGICAL((SV*)cv) ? mg_findext((SV*)cv, PERL_MAGIC_ext, &mgvtbl_checkcall) : NULL; if(callmg) { *THX_ckfun_p = DPTR2FPTR(Perl_call_checker, callmg->mg_ptr); *ckobj_p = callmg->mg_obj; } else { *THX_ckfun_p = Perl_ck_entersub_args_proto_or_list; *ckobj_p = (SV*)cv; } } # define Perl_cv_set_call_checker QPFXD(scc0) # define cv_set_call_checker(cv, THX_ckfun, ckobj) \ Perl_cv_set_call_checker(aTHX_ cv, THX_ckfun, ckobj) MY_EXPORT_CALLCONV void QPFXD(scc0)(pTHX_ CV *cv, Perl_call_checker THX_ckfun, SV *ckobj) { if(THX_ckfun == Perl_ck_entersub_args_proto_or_list && ckobj == (SV*)cv) { if(SvMAGICAL((SV*)cv)) sv_unmagicext((SV*)cv, PERL_MAGIC_ext, &mgvtbl_checkcall); } else { MAGIC *callmg = mg_findext((SV*)cv, PERL_MAGIC_ext, &mgvtbl_checkcall); if(!callmg) callmg = sv_magicext((SV*)cv, &PL_sv_undef, PERL_MAGIC_ext, &mgvtbl_checkcall, NULL, 0); if(callmg->mg_flags & MGf_REFCOUNTED) { SvREFCNT_dec(callmg->mg_obj); callmg->mg_flags &= ~MGf_REFCOUNTED; } callmg->mg_ptr = FPTR2DPTR(char *, THX_ckfun); callmg->mg_obj = ckobj; if(ckobj != (SV*)cv) { SvREFCNT_inc(ckobj); callmg->mg_flags |= MGf_REFCOUNTED; } } } static OP *(*THX_nxck_entersub)(pTHX_ OP *); static OP *THX_myck_entersub(pTHX_ OP *entersubop) { OP *aop, *cvop; CV *cv; GV *namegv; Perl_call_checker THX_ckfun; SV *ckobj; aop = cUNOPx(entersubop)->op_first; if(!OpHAS_SIBLING(aop)) aop = cUNOPx(aop)->op_first; aop = OpSIBLING(aop); for(cvop = aop; OpHAS_SIBLING(cvop); cvop = OpSIBLING(cvop)) ; if(!(cv = rv2cv_op_cv(cvop, 0))) return THX_nxck_entersub(aTHX_ entersubop); cv_get_call_checker(cv, &THX_ckfun, &ckobj); if(THX_ckfun == Perl_ck_entersub_args_proto_or_list && ckobj == (SV*)cv) return THX_nxck_entersub(aTHX_ entersubop); namegv = (GV*)rv2cv_op_cv(cvop, RV2CVOPCV_MARK_EARLY|RV2CVOPCV_RETURN_NAME_GV); entersubop->op_private |= OPpENTERSUB_HASTARG; entersubop->op_private |= (PL_hints & HINT_STRICT_REFS); if(PERLDB_SUB && PL_curstash != PL_debstash) entersubop->op_private |= OPpENTERSUB_DB; op_null(cvop); return THX_ckfun(aTHX_ entersubop, namegv, ckobj); } # define Q_PROVIDE_CV_SET_CALL_CHECKER 1 #endif /* !cv_set_call_checker */ MODULE = Devel::CallChecker PACKAGE = Devel::CallChecker PROTOTYPES: DISABLE BOOT: #if Q_PROVIDE_CV_SET_CALL_CHECKER wrap_op_checker(OP_ENTERSUB, THX_myck_entersub, &THX_nxck_entersub); #endif /* Q_PROVIDE_CV_SET_CALL_CHECKER */ SV * callchecker0_h() CODE: RETVAL = newSVpvs( "/* DO NOT EDIT -- generated " "by Devel::CallChecker version "XS_VERSION" */\n" "#ifndef "QPFXS"INCLUDED\n" "#define "QPFXS"INCLUDED 1\n" "#ifndef PERL_VERSION\n" " #error you must include perl.h before callchecker0.h\n" "#elif !(PERL_REVISION == "STRINGIFY(PERL_REVISION) " && PERL_VERSION == "STRINGIFY(PERL_VERSION) #if PERL_VERSION & 1 " && PERL_SUBVERSION == "STRINGIFY(PERL_SUBVERSION) #endif /* PERL_VERSION & 1 */ ")\n" " #error this callchecker0.h is for Perl " STRINGIFY(PERL_REVISION)"."STRINGIFY(PERL_VERSION) #if PERL_VERSION & 1 "."STRINGIFY(PERL_SUBVERSION) #endif /* PERL_VERSION & 1 */ " only\n" "#endif /* Perl version mismatch */\n" #define DEFFN(RETTYPE, PUBNAME, PRIVNAME, ARGTYPES, ARGNAMES) \ MY_IMPORT_CALLCONV_S" "RETTYPE" "QPFXS PRIVNAME"(pTHX_ "ARGTYPES");\n" \ "#define Perl_"PUBNAME" "QPFXS PRIVNAME"\n" \ "#define "PUBNAME"("ARGNAMES") Perl_"PUBNAME"(aTHX_ "ARGNAMES")\n" #if Q_PROVIDE_RV2CV_OP_CV "#define RV2CVOPCV_MARK_EARLY 0x00000001\n" "#define RV2CVOPCV_RETURN_NAME_GV 0x00000002\n" DEFFN("CV *", "rv2cv_op_cv", "roc0", "OP *, U32", "cvop, flags") #endif /* Q_PROVIDE_RV2CV_OP_CV */ #if Q_PROVIDE_CK_ENTERSUB_ARGS_PROTO_OR_LIST DEFFN("OP *", "ck_entersub_args_list", "eal0", "OP *", "o") DEFFN("OP *", "ck_entersub_args_proto", "eap0", "OP *, GV *, SV *", "o, gv, sv") DEFFN("OP *", "ck_entersub_args_proto_or_list", "ean0", "OP *, GV *, SV *", "o, gv, sv") #endif /* Q_PROVIDE_CK_ENTERSUB_ARGS_PROTO_OR_LIST */ #if Q_PROVIDE_CV_SET_CALL_CHECKER "typedef OP *(*Perl_call_checker)(pTHX_ OP *, GV *, SV *);\n" DEFFN("void", "cv_get_call_checker", "gcc0", "CV *, Perl_call_checker *, SV **", "cv, fp, op") DEFFN("void", "cv_set_call_checker", "scc0", "CV *, Perl_call_checker, SV *", "cv, f, o") #endif /* Q_PROVIDE_CV_SET_CALL_CHECKER */ "#endif /* !"QPFXS"INCLUDED */\n" ); OUTPUT: RETVAL