Blob Blame History Raw
#define PERL_NO_GET_CONTEXT 1
#include "EXTERN.h"
#include "perl.h"
#include "callck_callchecker0.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 PERL_UNUSED_VAR
# define PERL_UNUSED_VAR(x) ((void)x)
#endif /* !PERL_UNUSED_VAR */

#ifndef PERL_UNUSED_ARG
# define PERL_UNUSED_ARG(x) PERL_UNUSED_VAR(x)
#endif /* !PERL_UNUSED_ARG */

#ifndef FPTR2DPTR
# define FPTR2DPTR(t,x) ((t)(UV)(x))
#endif /* !FPTR2DPTR */

#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 */

#ifndef op_contextualize
# define op_contextualize(o, c) THX_op_contextualize(aTHX_ o, c)
static OP *THX_op_contextualize(pTHX_ OP *o, I32 c)
{
	if(c == G_SCALAR) {
		OP *sib, *assop, *nullop;
		sib = o->op_sibling;
		o->op_sibling = NULL;
		assop = newASSIGNOP(0, newOP(OP_NULL, 0), 0, o);
		o = cBINOPx(assop)->op_first;
		nullop = newOP(OP_NULL, 0);
		nullop->op_sibling = o->op_sibling;
		cBINOPx(assop)->op_first = nullop;
		if(!nullop->op_sibling) cBINOPx(assop)->op_last = nullop;
		op_free(assop);
		o->op_sibling = sib;
		return o;
	} else {
		croak("reserve op_contextualize abused");
	}
}
#endif /* !op_contextualize */

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);
}

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 (!OpHAS_SIBLING(aop))
		aop = cUNOPx(aop)->op_first;
	for (aop = OpSIBLING(aop); OpHAS_SIBLING(aop); aop = OpSIBLING(aop)) {
		op_contextualize(aop, G_SCALAR);
	}
	return entersubop;
}

static OP *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 (!OpHAS_SIBLING(pushop))
		pushop = cUNOPx(pushop)->op_first;
	while (1) {
		OP *aop = OpSIBLING(pushop);
		OP *as;
		if (!OpHAS_SIBLING(aop)) break;
		as = OpSIBLING(aop);
		OpMORESIB_set(pushop, as);
		OpLASTSIB_set(aop, NULL);
		op_contextualize(aop, G_SCALAR);
		if (sumop) {
			sumop = newBINOP(OP_ADD, 0, sumop, aop);
		} else {
			sumop = aop;
		}
	}
	if (!sumop)
		sumop = newSVOP(OP_CONST, 0, newSViv(0));
	op_free(entersubop);
	return sumop;
}

MODULE = t::callck PACKAGE = t::callck

PROTOTYPES: DISABLE

void
test_cv_getset_call_checker()
PROTOTYPE:
PREINIT:
	CV *t0_cv, *t1_cv;
	Perl_call_checker ckfun;
	SV *ckobj;
CODE:
#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__)
#define check_cc(cv, xckfun, xckobj) \
	do { \
		cv_get_call_checker((cv), &ckfun, &ckobj); \
		if (ckfun != (xckfun)) \
			croak_fail_ne(FPTR2DPTR(void *, ckfun), xckfun); \
		if (ckobj != (xckobj)) \
			croak_fail_ne(FPTR2DPTR(void *, ckobj), xckobj); \
	} while(0)
	t0_cv = get_cv("t::callck::t0", 0);
	t1_cv = get_cv("t::callck::t1", 0);
	check_cc(t0_cv, Perl_ck_entersub_args_proto_or_list, (SV*)t0_cv);
	check_cc(t1_cv, Perl_ck_entersub_args_proto_or_list, (SV*)t1_cv);
	cv_set_call_checker(t1_cv, Perl_ck_entersub_args_proto_or_list,
				&PL_sv_yes);
	check_cc(t0_cv, Perl_ck_entersub_args_proto_or_list, (SV*)t0_cv);
	check_cc(t1_cv, Perl_ck_entersub_args_proto_or_list, &PL_sv_yes);
	cv_set_call_checker(t0_cv, THX_ck_entersub_args_scalars, &PL_sv_no);
	check_cc(t0_cv, THX_ck_entersub_args_scalars, &PL_sv_no);
	check_cc(t1_cv, Perl_ck_entersub_args_proto_or_list, &PL_sv_yes);
	cv_set_call_checker(t1_cv, Perl_ck_entersub_args_proto_or_list,
				(SV*)t1_cv);
	check_cc(t0_cv, THX_ck_entersub_args_scalars, &PL_sv_no);
	check_cc(t1_cv, Perl_ck_entersub_args_proto_or_list, (SV*)t1_cv);
	cv_set_call_checker(t0_cv, Perl_ck_entersub_args_proto_or_list,
				(SV*)t0_cv);
	check_cc(t0_cv, Perl_ck_entersub_args_proto_or_list, (SV*)t0_cv);
	check_cc(t1_cv, Perl_ck_entersub_args_proto_or_list, (SV*)t1_cv);
	if (SvMAGICAL((SV*)t0_cv) || SvMAGIC((SV*)t0_cv)) croak_fail();
	if (SvMAGICAL((SV*)t1_cv) || SvMAGIC((SV*)t1_cv)) croak_fail();
#undef check_cc
#undef croak_fail_ne
#undef croak_fail

void
t0()
PROTOTYPE:
CODE:
	;

void
t1()
PROTOTYPE:
CODE:
	;

void
cv_set_call_checker_lists(CV *cv)
PROTOTYPE: $
CODE:
	cv_set_call_checker(cv, THX_ck_entersub_args_lists, &PL_sv_undef);

void
cv_set_call_checker_scalars(CV *cv)
PROTOTYPE: $
CODE:
	cv_set_call_checker(cv, THX_ck_entersub_args_scalars, &PL_sv_undef);

void
cv_set_call_checker_proto(CV *cv, SV *proto)
PROTOTYPE: $$
CODE:
	if (SvROK(proto))
		proto = SvRV(proto);
	cv_set_call_checker(cv, Perl_ck_entersub_args_proto, proto);

void
cv_set_call_checker_proto_or_list(CV *cv, SV *proto)
PROTOTYPE: $$
CODE:
	if (SvROK(proto))
		proto = SvRV(proto);
	cv_set_call_checker(cv, Perl_ck_entersub_args_proto_or_list, proto);

void
cv_set_call_checker_multi_sum(CV *cv)
PROTOTYPE: $
CODE:
	cv_set_call_checker(cv, THX_ck_entersub_multi_sum, &PL_sv_undef);