#define PERL_NO_GET_CONTEXT 1
#include "EXTERN.h"
#include "perl.h"
#include "callchecker0.h"
#include "XSUB.h"
#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 */
#ifndef CvISXSUB
# define CvISXSUB(cv) !!CvXSUB(cv)
#endif /* !CvISXSUB */
#ifndef CvISXSUB_on
# define CvISXSUB_on(cv) ((void) (cv))
#endif /* !CvISXSUB_on */
#ifndef sv_setpvs
# define sv_setpvs(SV, STR) sv_setpvn(SV, ""STR"", sizeof(STR)-1)
#endif /* !sv_setpvs */
#ifndef gv_stashpvs
# define gv_stashpvs(name, flags) gv_stashpvn(""name"", sizeof(name)-1, flags)
#endif /* !gv_stashpvs */
#ifndef PERL_UNUSED_ARG
# define PERL_UNUSED_ARG(x) PERL_UNUSED_VAR(x)
#endif /* !PERL_UNUSED_ARG */
#ifndef Newx
# define Newx(v,n,t) New(0,v,n,t)
#endif /* !Newx */
#ifndef ptr_table_new
struct q_ptr_tbl_ent {
struct q_ptr_tbl_ent *next;
void *from, *to;
};
# undef PTR_TBL_t
# define PTR_TBL_t struct q_ptr_tbl_ent *
# define ptr_table_new() THX_ptr_table_new(aTHX)
static PTR_TBL_t *THX_ptr_table_new(pTHX)
{
PTR_TBL_t *tbl;
Newx(tbl, 1, PTR_TBL_t);
*tbl = NULL;
return tbl;
}
# define ptr_table_free(tbl) THX_ptr_table_free(aTHX_ tbl)
static void THX_ptr_table_free(pTHX_ PTR_TBL_t *tbl)
{
struct q_ptr_tbl_ent *ent = *tbl;
Safefree(tbl);
while(ent) {
struct q_ptr_tbl_ent *nent = ent->next;
Safefree(ent);
ent = nent;
}
}
# define ptr_table_store(tbl, from, to) THX_ptr_table_store(aTHX_ tbl, from, to)
static void THX_ptr_table_store(pTHX_ PTR_TBL_t *tbl, void *from, void *to)
{
struct q_ptr_tbl_ent *ent;
Newx(ent, 1, struct q_ptr_tbl_ent);
ent->next = *tbl;
ent->from = from;
ent->to = to;
*tbl = ent;
}
# define ptr_table_fetch(tbl, from) THX_ptr_table_fetch(aTHX_ tbl, from)
static void *THX_ptr_table_fetch(pTHX_ PTR_TBL_t *tbl, void *from)
{
struct q_ptr_tbl_ent *ent;
for(ent = *tbl; ent; ent = ent->next) {
if(ent->from == from) return ent->to;
}
return NULL;
}
#endif /* !ptr_table_new */
#ifndef DPTR2FPTR
# define DPTR2FPTR(t,x) ((t)(UV)(x))
#endif /* !DPTR2FPTR */
#ifndef FPTR2DPTR
# define FPTR2DPTR(t,x) ((t)(UV)(x))
#endif /* !FPTR2DPTR */
#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 */
/*
* representing op pointer as B::OP object reference
*/
static HV *stash_bop;
#define decode_bop(bopref) THX_decode_bop(aTHX_ bopref)
static OP *THX_decode_bop(pTHX_ SV *bopref)
{
/*
* This logic comes from B's typemap entry for B::OP. It does
* not check for the alleged B::OP object being blessed into a
* B::OP class.
*/
if(!SvROK(bopref)) croak("bad B::OP reference");
return INT2PTR(OP*, SvIV(SvRV(bopref)));
}
#define encode_bop(op) THX_encode_bop(aTHX_ op)
static SV *THX_encode_bop(pTHX_ OP *op)
{
/*
* All the logic for blessing into the right B::OP class
* is in internal functions in B. We really want to call
* make_op_object() from B.xs. This is a roundabout way of
* getting to it.
*/
OP stalkop;
SV *stalkbop, *bop;
stalkop.op_next = op;
stalkbop = sv_2mortal(newRV_noinc(newSViv(INT2PTR(IV, &stalkop))));
sv_bless(stalkbop, stash_bop);
{
dSP;
PUSHMARK(SP);
XPUSHs(stalkbop);
PUTBACK;
call_method("next", G_SCALAR);
SPAGAIN;
bop = POPs;
PUTBACK;
}
return bop;
}
/*
* representing C call-checker function as Perl sub
*/
static void xsfunc_c_ckfun(pTHX_ CV *);
static PTR_TBL_t *ckfun_cap_map;
#define ckfun_encode_c_as_perl(cckfun) THX_ckfun_encode_c_as_perl(aTHX_ cckfun)
static CV *THX_ckfun_encode_c_as_perl(pTHX_ Perl_call_checker cckfun)
{
void *vcckfun = FPTR2DPTR(void *, cckfun);
CV *ckfun;
if((ckfun = ptr_table_fetch(ckfun_cap_map, vcckfun)))
return ckfun;
ckfun = (CV*)newSV_type(SVt_PVCV);
sv_setpvs((SV*)ckfun, "$$$");
CvXSUBANY(ckfun).any_ptr = vcckfun;
CvXSUB(ckfun) = xsfunc_c_ckfun;
CvISXSUB_on(ckfun);
ptr_table_store(ckfun_cap_map, vcckfun, (void*)ckfun);
return ckfun;
}
#define ckfun_perl_is_encoded_c(ckfun) THX_ckfun_perl_is_encoded_c(aTHX_ ckfun)
static bool THX_ckfun_perl_is_encoded_c(pTHX_ CV *ckfun)
{
return CvISXSUB(ckfun) && CvXSUB(ckfun) == xsfunc_c_ckfun;
}
#define ckfun_decode_c_as_perl(ckfun) THX_ckfun_decode_c_as_perl(aTHX_ ckfun)
static Perl_call_checker THX_ckfun_decode_c_as_perl(pTHX_ CV *ckfun)
{
return DPTR2FPTR(Perl_call_checker, CvXSUBANY(ckfun).any_ptr);
}
static void xsfunc_c_ckfun(pTHX_ CV *ckfun)
{
SV *ckobj_st, *namegv_st, *entersubop_st, *ckobj;
GV *namegv;
OP *entersubop;
Perl_call_checker cckfun = ckfun_decode_c_as_perl(ckfun);
dSP; dMARK;
if(SP - MARK != 3) {
bad_args:
croak("non-Perl call checker called incorrectly");
}
ckobj_st = POPs;
namegv_st = POPs;
entersubop_st = TOPs;
PUTBACK;
if(!SvROK(ckobj_st)) goto bad_args;
ckobj = SvRV(ckobj_st);
if(!SvROK(namegv_st)) goto bad_args;
namegv = (GV*)SvRV(namegv_st);
if(SvTYPE((SV*)namegv) != SVt_PVGV) goto bad_args;
entersubop = decode_bop(entersubop_st);
entersubop = cckfun(aTHX_ entersubop, namegv, ckobj);
entersubop_st = encode_bop(entersubop);
SPAGAIN;
TOPs = entersubop_st;
}
/*
* representing Perl call-checker sub as C function
*/
static OP *cckfun_perl_ckfun(pTHX_ OP *entersubop, GV *namegv, SV *cckobj);
#define ckfun_encode_perl_as_c(ckfun, ckobj, cckfun_p, cckobj_p) \
THX_ckfun_encode_perl_as_c(aTHX_ ckfun, ckobj, cckfun_p, cckobj_p)
static void THX_ckfun_encode_perl_as_c(pTHX_
CV *ckfun, SV *ckobj, Perl_call_checker *cckfun_p, SV **cckobj_p)
{
SV *cckobj = sv_2mortal((SV*)newAV());
av_extend((AV*)cckobj, 1);
av_store((AV*)cckobj, 0, SvREFCNT_inc((SV*)ckfun));
av_store((AV*)cckobj, 1, SvREFCNT_inc(ckobj));
*cckfun_p = cckfun_perl_ckfun;
*cckobj_p = cckobj;
}
#define ckfun_c_is_encoded_perl(cckfun) \
THX_ckfun_c_is_encoded_perl(aTHX_ cckfun)
static bool THX_ckfun_c_is_encoded_perl(pTHX_ Perl_call_checker cckfun)
{
return cckfun == cckfun_perl_ckfun;
}
#define ckfun_decode_perl_as_c(cckfun, cckobj, ckfun_p, ckobj_p) \
THX_ckfun_decode_perl_as_c(aTHX_ cckfun, cckobj, ckfun_p, ckobj_p)
static void THX_ckfun_decode_perl_as_c(pTHX_
Perl_call_checker cckfun, SV *cckobj, CV **ckfun_p, SV **ckobj_p)
{
SV **valp;
PERL_UNUSED_ARG(cckfun);
if(SvTYPE(cckobj) != SVt_PVAV || av_len((AV*)cckobj) != 1) {
bad_args:
croak("call checker shim called incorrectly");
}
*ckfun_p = (CV*)*av_fetch((AV*)cckobj, 0, 0);
if(SvTYPE((SV*)*ckfun_p) != SVt_PVCV) goto bad_args;
valp = av_fetch((AV*)cckobj, 1, 0);
*ckobj_p = valp ? *valp : &PL_sv_undef;
}
static OP *cckfun_perl_ckfun(pTHX_ OP *entersubop, GV *namegv, SV *cckobj)
{
SV *ckobj_st, *namegv_st, *entersubop_st, *ckobj;
CV *ckfun;
ckfun_decode_perl_as_c(0, cckobj, &ckfun, &ckobj);
entersubop_st = encode_bop(entersubop);
namegv_st = sv_2mortal(newRV_inc((SV*)namegv));
ckobj_st = sv_2mortal(newRV_inc(ckobj));
ENTER;
{
dSP;
PUSHMARK(SP);
EXTEND(SP, 3);
PUSHs(entersubop_st);
PUSHs(namegv_st);
PUSHs(ckobj_st);
PUTBACK;
call_sv((SV*)ckfun, G_SCALAR);
SPAGAIN;
entersubop_st = POPs;
PUTBACK;
}
LEAVE;
return decode_bop(entersubop_st);
}
#define install_cv(cv, name) THX_install_cv(aTHX_ cv, name)
static void THX_install_cv(pTHX_ CV *cv, char const *name)
{
GV *gv = gv_fetchpv(name, GV_ADDMULTI, SVt_PVCV);
GvCV_set(gv, cv);
GvCVGEN(gv) = 0;
CvGV_set(cv, gv);
}
typedef SV *SVREF;
MODULE = B::CallChecker PACKAGE = B::CallChecker
PROTOTYPES: DISABLE
BOOT:
ckfun_cap_map = ptr_table_new();
stash_bop = gv_stashpvs("B::OP", 1);
install_cv(ckfun_encode_c_as_perl(Perl_ck_entersub_args_proto),
"B::CallChecker::ck_entersub_args_proto");
install_cv(ckfun_encode_c_as_perl(Perl_ck_entersub_args_proto_or_list),
"B::CallChecker::ck_entersub_args_proto_or_list");
void
cv_get_call_checker(CV *tgtcv)
PROTOTYPE: $
PREINIT:
Perl_call_checker cckfun;
SV *cckobj;
CV *ckfun;
SV *ckobj;
PPCODE:
PUTBACK;
cv_get_call_checker(tgtcv, &cckfun, &cckobj);
if(ckfun_c_is_encoded_perl(cckfun)) {
ckfun_decode_perl_as_c(cckfun, cckobj, &ckfun, &ckobj);
} else {
ckfun = ckfun_encode_c_as_perl(cckfun);
ckobj = cckobj;
}
SPAGAIN;
EXTEND(SP, 2);
PUSHs(sv_2mortal(newRV_inc((SV*)ckfun)));
PUSHs(sv_2mortal(newRV_inc(ckobj)));
void
cv_set_call_checker(CV *tgtcv, CV *ckfun, SVREF ckobj)
PROTOTYPE: $$$
PREINIT:
Perl_call_checker cckfun;
SV *cckobj;
CODE:
PUTBACK;
if(ckfun_perl_is_encoded_c(ckfun)) {
cckfun = ckfun_decode_c_as_perl(ckfun);
cckobj = ckobj;
} else {
ckfun_encode_perl_as_c(ckfun, ckobj, &cckfun, &cckobj);
}
cv_set_call_checker(tgtcv, cckfun, cckobj);
SPAGAIN;
OP *
ck_entersub_args_list(OP *entersubop)
PROTOTYPE: $
CODE:
PUTBACK;
RETVAL = ck_entersub_args_list(entersubop);
SPAGAIN;
OUTPUT:
RETVAL