#include "EXTERN.h"
#define PERL_IN_CLASS_C
#include "perl.h"
#include "XSUB.h"
enum
{
PADIX_SELF = 1,
PADIX_PARAMS = 2,
};
void
Perl_croak_kw_unless_class(pTHX_
const
char
*kw)
{
PERL_ARGS_ASSERT_CROAK_KW_UNLESS_CLASS;
if
(!HvSTASH_IS_CLASS(PL_curstash))
croak(
"Cannot '%s' outside of a 'class'"
, kw);
}
#define newSVobject(fieldcount) Perl_newSVobject(aTHX_ fieldcount)
SV *
Perl_newSVobject(pTHX_ Size_t fieldcount)
{
SV *sv = newSV_type(SVt_PVOBJ);
Newx(ObjectFIELDS(sv), fieldcount, SV *);
ObjectMAXFIELD(sv) = fieldcount - 1;
Zero(ObjectFIELDS(sv), fieldcount, SV *);
return
sv;
}
PP(pp_initfield)
{
UNOP_AUX_item *aux = cUNOP_AUX->op_aux;
SV *self = PAD_SVl(PADIX_SELF);
assert
(SvTYPE(SvRV(self)) == SVt_PVOBJ);
SV *instance = SvRV(self);
SV **fields = ObjectFIELDS(instance);
PADOFFSET fieldix = aux[0].uv;
SV *val = NULL;
switch
(PL_op->op_private & (OPpINITFIELD_AV|OPpINITFIELD_HV)) {
case
0:
if
(PL_op->op_flags & OPf_STACKED) {
val = newSVsv(*PL_stack_sp);
rpp_popfree_1();
}
else
val = newSV(0);
break
;
case
OPpINITFIELD_AV:
{
AV *av;
if
(PL_op->op_flags & OPf_STACKED) {
SV **svp = PL_stack_base + POPMARK + 1;
STRLEN count = PL_stack_sp - svp + 1;
av = newAV_alloc_x(count);
while
(svp <= PL_stack_sp) {
av_push_simple(av, newSVsv(*svp));
svp++;
}
rpp_popfree_to(PL_stack_sp - count);
}
else
av = newAV();
val = (SV *)av;
break
;
}
case
OPpINITFIELD_HV:
{
HV *hv = newHV();
if
(PL_op->op_flags & OPf_STACKED) {
SV **svp = PL_stack_base + POPMARK + 1;
STRLEN svcount = PL_stack_sp - svp + 1;
if
(svcount % 2)
Perl_warner(aTHX_
packWARN(WARN_MISC),
"Odd number of elements in hash field initialization"
);
while
(svp <= PL_stack_sp) {
SV *key = *svp; svp++;
SV *val = svp <= PL_stack_sp ? *svp : &PL_sv_undef; svp++;
(
void
)hv_store_ent(hv, key, newSVsv(val), 0);
}
rpp_popfree_to(PL_stack_sp - svcount);
}
val = (SV *)hv;
break
;
}
}
fields[fieldix] = val;
PADOFFSET padix = PL_op->op_targ;
if
(padix) {
SAVESPTR(PAD_SVl(padix));
SV *sv = PAD_SVl(padix) = SvREFCNT_inc(val);
save_freesv(sv);
}
return
NORMAL;
}
XS(injected_constructor);
XS(injected_constructor)
{
dXSARGS;
HV *stash = (HV *)XSANY.any_sv;
assert
(HvSTASH_IS_CLASS(stash));
struct
xpvhv_aux *aux = HvAUX(stash);
if
((items - 1) % 2)
Perl_warn(aTHX_
"Odd number of arguments passed to %"
HvNAMEf_QUOTEDPREFIX
" constructor"
,
HvNAMEfARG(stash));
if
(!aux->xhv_class_initfields_cv) {
Perl_croak(aTHX_
"Cannot create an object of incomplete class %"
HvNAMEf_QUOTEDPREFIX,
HvNAMEfARG(stash));
}
HV *params = NULL;
{
params = newHV();
SAVEFREESV((SV *)params);
for
(SSize_t i = 1; i < items; i += 2) {
SV *name = ST(i);
SV *val = (i+1 < items) ? ST(i+1) : &PL_sv_undef;
(
void
)hv_store_ent(params, name, SvREFCNT_inc(val), 0);
}
}
SV *instance = newSVobject(aux->xhv_class_next_fieldix);
SvOBJECT_on(instance);
SvSTASH_set(instance, MUTABLE_HV(SvREFCNT_inc_simple(stash)));
SV *self = sv_2mortal(newRV_noinc(instance));
assert
(aux->xhv_class_initfields_cv);
{
ENTER;
SAVETMPS;
EXTEND(SP, 2);
PUSHMARK(SP);
PUSHs(self);
if
(params)
PUSHs((SV *)params);
else
PUSHs(&PL_sv_undef);
PUTBACK;
call_sv((SV *)aux->xhv_class_initfields_cv, G_VOID);
SPAGAIN;
FREETMPS;
LEAVE;
}
if
(aux->xhv_class_adjust_blocks) {
CV **cvp = (CV **)AvARRAY(aux->xhv_class_adjust_blocks);
U32 nblocks = av_count(aux->xhv_class_adjust_blocks);
for
(U32 i = 0; i < nblocks; i++) {
ENTER;
SAVETMPS;
SPAGAIN;
EXTEND(SP, 2);
PUSHMARK(SP);
PUSHs(self);
PUTBACK;
call_sv((SV *)cvp[i], G_VOID);
SPAGAIN;
FREETMPS;
LEAVE;
}
}
if
(params && hv_iterinit(params) > 0) {
HE *he = hv_iternext(params);
SV *paramnames = newSVsv(HeSVKEY_force(he));
SAVEFREESV(paramnames);
while
((he = hv_iternext(params)))
Perl_sv_catpvf(aTHX_ paramnames,
", %"
SVf, SVfARG(HeSVKEY_force(he)));
croak(
"Unrecognised parameters for %"
HvNAMEf_QUOTEDPREFIX
" constructor: %"
SVf,
HvNAMEfARG(stash), SVfARG(paramnames));
}
EXTEND(SP, 1);
ST(0) = self;
XSRETURN(1);
}
PP(pp_methstart)
{
SV *self = *(av_fetch(GvAV(PL_defgv), 0, 1));
SV *rv = NULL;
CV *curcv;
if
(LIKELY(CxTYPE(CX_CUR()) == CXt_SUB))
curcv = CX_CUR()->blk_sub.cv;
else
curcv = find_runcv(NULL);
if
(!SvROK(self) ||
!SvOBJECT((rv = SvRV(self))) ||
SvTYPE(rv) != SVt_PVOBJ) {
HEK *namehek = CvGvNAME_HEK(curcv);
croak(
namehek ?
"Cannot invoke method %"
HEKf_QUOTEDPREFIX
" on a non-instance"
:
"Cannot invoke method on a non-instance"
,
namehek);
}
if
(CvSTASH(curcv) != SvSTASH(rv) &&
!sv_derived_from_hv(self, CvSTASH(curcv)))
croak(
"Cannot invoke a method of %"
HvNAMEf_QUOTEDPREFIX
" on an instance of %"
HvNAMEf_QUOTEDPREFIX,
HvNAMEfARG(CvSTASH(curcv)), HvNAMEfARG(SvSTASH(rv)));
save_clearsv(&PAD_SVl(PADIX_SELF));
sv_setsv(PAD_SVl(PADIX_SELF), self);
UNOP_AUX_item *aux = cUNOP_AUX->op_aux;
if
(aux) {
assert
(SvTYPE(SvRV(self)) == SVt_PVOBJ);
SV *instance = SvRV(self);
SV **fieldp = ObjectFIELDS(instance);
U32 fieldcount = (aux++)->uv;
U32 max_fieldix = (aux++)->uv;
assert
((U32)(ObjectMAXFIELD(instance)+1) > max_fieldix);
PERL_UNUSED_VAR(max_fieldix);
for
(Size_t i = 0; i < fieldcount; i++) {
PADOFFSET padix = (aux++)->uv;
U32 fieldix = (aux++)->uv;
assert
(fieldp[fieldix]);
SAVESPTR(PAD_SVl(padix));
SV *sv = PAD_SVl(padix) = SvREFCNT_inc(fieldp[fieldix]);
save_freesv(sv);
}
}
self = av_shift(GvAV(PL_defgv));
if
(AvREAL(GvAV(PL_defgv)))
SvREFCNT_dec_NN(self);
if
(PL_op->op_private & OPpINITFIELDS) {
SV *params = *av_fetch(GvAV(PL_defgv), 0, 0);
if
(params && SvTYPE(params) == SVt_PVHV) {
SAVESPTR(PAD_SVl(PADIX_PARAMS));
PAD_SVl(PADIX_PARAMS) = SvREFCNT_inc(params);
save_freesv(params);
}
}
return
NORMAL;
}
static
void
invoke_class_seal(pTHX_
void
*_arg)
{
class_seal_stash((HV *)_arg);
}
void
Perl_class_setup_stash(pTHX_ HV *stash)
{
PERL_ARGS_ASSERT_CLASS_SETUP_STASH;
assert
(HvHasAUX(stash));
if
(HvSTASH_IS_CLASS(stash)) {
croak(
"Cannot reopen existing class %"
HvNAMEf_QUOTEDPREFIX,
HvNAMEfARG(stash));
}
{
SV *isaname = newSVpvf(
"%"
HEKf
"::ISA"
, HvNAME_HEK(stash));
sv_2mortal(isaname);
AV *isa = get_av(SvPV_nolen(isaname), (SvFLAGS(isaname) & SVf_UTF8));
if
(isa && av_count(isa) > 0)
croak(
"Cannot create class %"
HEKf
" as it already has a non-empty @ISA"
,
HvNAME_HEK(stash));
}
char
*classname = HvNAME(stash);
U32 nameflags = HvNAMEUTF8(stash) ? SVf_UTF8 : 0;
{
SV *newname = Perl_newSVpvf(aTHX_
"%s::new"
, classname);
SAVEFREESV(newname);
CV *newcv = newXS_flags(SvPV_nolen(newname), injected_constructor, __FILE__, NULL, nameflags);
CvXSUBANY(newcv).any_sv = (SV *)stash;
CvREFCOUNTED_ANYSV_on(newcv);
}
struct
xpvhv_aux *aux = HvAUX(stash);
aux->xhv_class_superclass = NULL;
aux->xhv_class_initfields_cv = NULL;
aux->xhv_class_adjust_blocks = NULL;
aux->xhv_class_fields = NULL;
aux->xhv_class_next_fieldix = 0;
aux->xhv_class_param_map = NULL;
aux->xhv_aux_flags |= HvAUXf_IS_CLASS;
SAVEDESTRUCTOR_X(invoke_class_seal, stash);
{
I32 floor_ix = start_subparse(FALSE, 0);
CvIsMETHOD_on(PL_compcv);
PADOFFSET padix = pad_add_name_pvs(
"$(self)"
, 0, NULL, NULL);
assert
(padix == PADIX_SELF);
padix = pad_add_name_pvs(
"%(params)"
, 0, NULL, NULL);
assert
(padix == PADIX_PARAMS);
PERL_UNUSED_VAR(padix);
Newx(aux->xhv_class_suspended_initfields_compcv, 1,
struct
suspended_compcv);
suspend_compcv(aux->xhv_class_suspended_initfields_compcv);
LEAVE_SCOPE(floor_ix);
}
}
#define split_package_ver(value, pkgname, pkgversion) S_split_package_ver(aTHX_ value, pkgname, pkgversion)
static
const
char
*S_split_package_ver(pTHX_ SV *value, SV *pkgname, SV *pkgversion)
{
const
char
*start = SvPVX(value),
*p = start,
*end = start + SvCUR(value);
while
(*p && !isSPACE_utf8_safe(p, end))
p += UTF8SKIP(p);
sv_setpvn(pkgname, start, p - start);
if
(SvUTF8(value))
SvUTF8_on(pkgname);
while
(*p && isSPACE_utf8_safe(p, end))
p += UTF8SKIP(p);
if
(*p) {
start = p;
if
(*p ==
'v'
)
p++;
while
(*p &&
strchr
(
"0123456789._"
, *p))
p++;
SV *tmpsv = newSVpvn(start, p - start);
SAVEFREESV(tmpsv);
scan_version(SvPVX(tmpsv), pkgversion, FALSE);
}
while
(*p && isSPACE_utf8_safe(p, end))
p += UTF8SKIP(p);
return
p;
}
#define ensure_module_version(module, version) S_ensure_module_version(aTHX_ module, version)
static
void
S_ensure_module_version(pTHX_ SV *module, SV *version)
{
ENTER;
PUSHMARK(PL_stack_sp);
rpp_xpush_2(module, version);
call_method(
"VERSION"
, G_VOID);
LEAVE;
}
#define split_attr_nameval(sv, namp, valp) S_split_attr_nameval(aTHX_ sv, namp, valp)
static
void
S_split_attr_nameval(pTHX_ SV *sv, SV **namp, SV **valp)
{
STRLEN svlen = SvCUR(sv);
bool
do_utf8 = SvUTF8(sv);
const
char
*paren_at = (
const
char
*)
memchr
(SvPVX(sv),
'('
, svlen);
if
(paren_at) {
STRLEN namelen = paren_at - SvPVX(sv);
if
(SvPVX(sv)[svlen-1] !=
')'
)
croak(
"Malformed attribute string"
);
*namp = sv_2mortal(newSVpvn_utf8(SvPVX(sv), namelen, do_utf8));
const
char
*value_at = paren_at + 1;
const
char
*value_max = SvPVX(sv) + svlen - 2;
while
(value_at < value_max && isSPACE(*value_at))
value_at += 1;
while
(value_max > value_at && isSPACE(*value_max))
value_max -= 1;
if
(value_max >= value_at)
*valp = sv_2mortal(newSVpvn_utf8(value_at, value_max - value_at + 1, do_utf8));
else
*valp = NULL;
}
else
{
*namp = sv;
*valp = NULL;
}
}
static
void
apply_class_attribute_isa(pTHX_ HV *stash, SV *value)
{
assert
(HvSTASH_IS_CLASS(stash));
struct
xpvhv_aux *aux = HvAUX(stash);
SV *superclassname = sv_newmortal(), *superclassver = sv_newmortal();
const
char
*end = split_package_ver(value, superclassname, superclassver);
if
(*end)
croak(
"Unexpected characters while parsing class :isa attribute: %s"
, end);
if
(aux->xhv_class_superclass)
croak(
"Class already has a superclass, cannot add another"
);
HV *superstash = gv_stashsv(superclassname, 0);
if
(!superstash || !HvSTASH_IS_CLASS(superstash)) {
load_module(PERL_LOADMOD_NOIMPORT, newSVsv(superclassname), NULL, NULL);
superstash = gv_stashsv(superclassname, 0);
}
if
(!superstash || !HvSTASH_IS_CLASS(superstash))
croak(
"Class :isa attribute requires a class but %"
HvNAMEf_QUOTEDPREFIX
" is not one"
,
HvNAMEfARG(superstash));
if
(superclassver && SvOK(superclassver))
ensure_module_version(superclassname, superclassver);
{
SV *isaname = newSVpvf(
"%"
HEKf
"::ISA"
, HvNAME_HEK(stash));
sv_2mortal(isaname);
AV *isa = get_av(SvPV_nolen(isaname), GV_ADD | (SvFLAGS(isaname) & SVf_UTF8));
ENTER;
SAVESETSVFLAGS((SV *)isa, SVf_READONLY|SVf_PROTECT, SVf_READONLY|SVf_PROTECT);
SvREADONLY_off((SV *)isa);
av_push(isa, newSVsv(value));
LEAVE;
}
aux->xhv_class_superclass = (HV *)SvREFCNT_inc(superstash);
struct
xpvhv_aux *superaux = HvAUX(superstash);
aux->xhv_class_next_fieldix = superaux->xhv_class_next_fieldix;
if
(superaux->xhv_class_adjust_blocks) {
if
(!aux->xhv_class_adjust_blocks)
aux->xhv_class_adjust_blocks = newAV();
for
(SSize_t i = 0; i <= AvFILL(superaux->xhv_class_adjust_blocks); i++)
av_push(aux->xhv_class_adjust_blocks, AvARRAY(superaux->xhv_class_adjust_blocks)[i]);
}
if
(superaux->xhv_class_param_map) {
aux->xhv_class_param_map = newHVhv(superaux->xhv_class_param_map);
}
}
static
struct
{
const
char
*name;
bool
requires_value;
void
(*apply)(pTHX_ HV *stash, SV *value);
}
const
class_attributes[] = {
{ .name =
"isa"
,
.requires_value =
true
,
.apply = &apply_class_attribute_isa,
},
{ NULL,
false
, NULL }
};
static
void
S_class_apply_attribute(pTHX_ HV *stash, OP *attr)
{
assert
(attr->op_type == OP_CONST);
SV *name, *value;
split_attr_nameval(cSVOPx_sv(attr), &name, &value);
for
(
int
i = 0; class_attributes[i].name; i++) {
if
(!strEQ(SvPVX(name), class_attributes[i].name))
continue
;
if
(class_attributes[i].requires_value && !(value && SvOK(value)))
croak(
"Class attribute %"
SVf
" requires a value"
, SVfARG(name));
(*class_attributes[i].apply)(aTHX_ stash, value);
return
;
}
croak(
"Unrecognized class attribute %"
SVf, SVfARG(name));
}
void
Perl_class_apply_attributes(pTHX_ HV *stash, OP *attrlist)
{
PERL_ARGS_ASSERT_CLASS_APPLY_ATTRIBUTES;
if
(!attrlist)
return
;
if
(attrlist->op_type == OP_NULL) {
op_free(attrlist);
return
;
}
if
(attrlist->op_type == OP_LIST) {
OP *o = cLISTOPx(attrlist)->op_first;
assert
(o->op_type == OP_PUSHMARK);
o = OpSIBLING(o);
for
(; o; o = OpSIBLING(o))
S_class_apply_attribute(aTHX_ stash, o);
}
else
S_class_apply_attribute(aTHX_ stash, attrlist);
op_free(attrlist);
}
void
Perl_class_seal_stash(pTHX_ HV *stash)
{
PERL_ARGS_ASSERT_CLASS_SEAL_STASH;
assert
(HvSTASH_IS_CLASS(stash));
struct
xpvhv_aux *aux = HvAUX(stash);
if
(PL_parser->error_count == 0) {
I32 floor_ix = PL_savestack_ix;
SAVEI32(PL_subline);
save_item(PL_subname);
resume_compcv_final(aux->xhv_class_suspended_initfields_compcv);
PADNAMELIST *pnl = PadlistNAMES(CvPADLIST(PL_compcv));
HV *fieldix_to_padix = newHV();
SAVEFREESV((SV *)fieldix_to_padix);
for
(PADOFFSET padix = 2; padix <= PadnamelistMAX(pnl); padix++) {
PADNAME *pn = PadnamelistARRAY(pnl)[padix];
if
(!pn || !PadnameIsFIELD(pn))
continue
;
U32 fieldix = PadnameFIELDINFO(pn)->fieldix;
(
void
)hv_store_ent(fieldix_to_padix, sv_2mortal(newSVuv(fieldix)), newSVuv(padix), 0);
}
OP *ops = NULL;
ops = op_append_list(OP_LINESEQ, ops,
newUNOP_AUX(OP_METHSTART, OPpINITFIELDS << 8, NULL, NULL));
if
(aux->xhv_class_superclass) {
HV *superstash = aux->xhv_class_superclass;
assert
(HvSTASH_IS_CLASS(superstash));
struct
xpvhv_aux *superaux = HvAUX(superstash);
OP *o = newLISTOPn(OP_ENTERSUB, OPf_WANT_VOID|OPf_STACKED,
newPADxVOP(OP_PADSV, 0, PADIX_SELF),
newPADxVOP(OP_PADHV, OPf_REF, PADIX_PARAMS),
newSVOP(OP_CONST, 0, (SV *)superaux->xhv_class_initfields_cv),
NULL);
ops = op_append_list(OP_LINESEQ, ops, o);
}
PADNAMELIST *fieldnames = aux->xhv_class_fields;
for
(SSize_t i = 0; fieldnames && i <= PadnamelistMAX(fieldnames); i++) {
PADNAME *pn = PadnamelistARRAY(fieldnames)[i];
char
sigil = PadnamePV(pn)[0];
PADOFFSET fieldix = PadnameFIELDINFO(pn)->fieldix;
OP *valop = PadnameFIELDINFO(pn)->defop;
if
(valop && valop->op_type == OP_LINESEQ) {
OP *o = cLISTOPx(valop)->op_first;
cLISTOPx(valop)->op_first = NULL;
cLISTOPx(valop)->op_last = NULL;
valop->op_flags &= ~OPf_KIDS;
op_free(valop);
OP *fieldcop = o;
assert
(fieldcop->op_type == OP_NEXTSTATE || fieldcop->op_type == OP_DBSTATE);
o = OpSIBLING(o);
OpLASTSIB_set(fieldcop, NULL);
valop = o;
OpLASTSIB_set(valop, NULL);
ops = op_append_list(OP_LINESEQ, ops, fieldcop);
}
SV *paramname = PadnameFIELDINFO(pn)->paramname;
U8 op_priv = 0;
switch
(sigil) {
case
'$'
:
if
(paramname) {
if
(!valop) {
SV *message =
newSVpvf(
"Required parameter '%"
SVf
"' is missing for %"
HvNAMEf_QUOTEDPREFIX
" constructor"
,
SVfARG(paramname), HvNAMEfARG(stash));
valop = newLISTOPn(OP_DIE, 0,
newSVOP(OP_CONST, 0, message),
NULL);
}
OP *helemop =
newBINOP(OP_HELEM, 0,
newPADxVOP(OP_PADHV, OPf_REF, PADIX_PARAMS),
newSVOP(OP_CONST, 0, SvREFCNT_inc(paramname)));
if
(PadnameFIELDINFO(pn)->def_if_undef) {
valop = newLOGOP(OP_DOR, 0,
newUNOP(OP_DELETE, 0, helemop), valop);
}
else
if
(PadnameFIELDINFO(pn)->def_if_false) {
valop = newLOGOP(OP_OR, 0,
newUNOP(OP_DELETE, 0, helemop), valop);
}
else
{
valop = newLOGOP(OP_HELEMEXISTSOR, OPpHELEMEXISTSOR_DELETE << 8,
helemop, valop);
}
valop = op_contextualize(valop, G_SCALAR);
}
break
;
case
'@'
:
op_priv = OPpINITFIELD_AV;
break
;
case
'%'
:
op_priv = OPpINITFIELD_HV;
break
;
default
:
NOT_REACHED;
}
UNOP_AUX_item *aux;
aux = (UNOP_AUX_item *)PerlMemShared_malloc(
sizeof
(UNOP_AUX_item) * 2);
aux[0].uv = fieldix;
OP *fieldop = newUNOP_AUX(OP_INITFIELD, valop ? OPf_STACKED : 0, valop, aux);
fieldop->op_private = op_priv;
HE *he;
if
((he = hv_fetch_ent(fieldix_to_padix, sv_2mortal(newSVuv(fieldix)), 0, 0)) &&
SvOK(HeVAL(he))) {
fieldop->op_targ = SvUV(HeVAL(he));
}
ops = op_append_list(OP_LINESEQ, ops, fieldop);
}
CvIsMETHOD_off(PL_compcv);
CV *initfields = newATTRSUB(floor_ix, NULL, NULL, NULL, ops);
CvIsMETHOD_on(initfields);
aux->xhv_class_initfields_cv = initfields;
}
else
{
PADNAMELIST *fieldnames = aux->xhv_class_fields;
if
(fieldnames) {
for
(SSize_t i = PadnamelistMAX(fieldnames); i >= 0 ; i--) {
PADNAME *pn = PadnamelistARRAY(fieldnames)[i];
op_free(PadnameFIELDINFO(pn)->defop);
}
}
}
}
void
Perl_class_prepare_initfield_parse(pTHX)
{
PERL_ARGS_ASSERT_CLASS_PREPARE_INITFIELD_PARSE;
assert
(HvSTASH_IS_CLASS(PL_curstash));
struct
xpvhv_aux *aux = HvAUX(PL_curstash);
resume_compcv_and_save(aux->xhv_class_suspended_initfields_compcv);
CvOUTSIDE_SEQ(PL_compcv) = PL_cop_seqmax;
}
void
Perl_class_prepare_method_parse(pTHX_ CV *cv)
{
PERL_ARGS_ASSERT_CLASS_PREPARE_METHOD_PARSE;
assert
(cv == PL_compcv);
assert
(HvSTASH_IS_CLASS(PL_curstash));
assert
(PL_comppad_name_fill == 0);
PADOFFSET padix;
padix = pad_add_name_pvs(
"$self"
, 0, NULL, NULL);
assert
(padix == PADIX_SELF);
PERL_UNUSED_VAR(padix);
intro_my();
CvNOWARN_AMBIGUOUS_on(cv);
CvIsMETHOD_on(cv);
}
OP *
Perl_class_wrap_method_body(pTHX_ OP *o)
{
PERL_ARGS_ASSERT_CLASS_WRAP_METHOD_BODY;
if
(!o)
return
o;
PADNAMELIST *pnl = PadlistNAMES(CvPADLIST(PL_compcv));
AV *fieldmap = newAV();
UV max_fieldix = 0;
SAVEFREESV((SV *)fieldmap);
for
(PADOFFSET padix = 2; padix <= PadnamelistMAX(pnl); padix++) {
PADNAME *pn = PadnamelistARRAY(pnl)[padix];
if
(!pn || !PadnameIsFIELD(pn))
continue
;
U32 fieldix = PadnameFIELDINFO(pn)->fieldix;
if
(fieldix > max_fieldix)
max_fieldix = fieldix;
av_push_simple(fieldmap, newSVuv(padix));
av_push_simple(fieldmap, newSVuv(fieldix));
}
UNOP_AUX_item *aux = NULL;
if
(av_count(fieldmap)) {
aux = (UNOP_AUX_item *)PerlMemShared_malloc(
sizeof
(UNOP_AUX_item)
* (2 + av_count(fieldmap))
);
UNOP_AUX_item *ap = aux;
(ap++)->uv = av_count(fieldmap) / 2;
(ap++)->uv = max_fieldix;
for
(Size_t i = 0; i < av_count(fieldmap); i++)
(ap++)->uv = SvUV(AvARRAY(fieldmap)[i]);
}
if
(o->op_type != OP_LINESEQ)
o = newLISTOP(OP_LINESEQ, 0, o, NULL);
op_sibling_splice(o, NULL, 0, newUNOP_AUX(OP_METHSTART, 0, NULL, aux));
return
o;
}
void
Perl_class_add_field(pTHX_ HV *stash, PADNAME *pn)
{
PERL_ARGS_ASSERT_CLASS_ADD_FIELD;
assert
(HvSTASH_IS_CLASS(stash));
struct
xpvhv_aux *aux = HvAUX(stash);
PADOFFSET fieldix = aux->xhv_class_next_fieldix;
aux->xhv_class_next_fieldix++;
Newxz(PadnameFIELDINFO(pn), 1,
struct
padname_fieldinfo);
PadnameFLAGS(pn) |= PADNAMEf_FIELD;
PadnameFIELDINFO(pn)->refcount = 1;
PadnameFIELDINFO(pn)->fieldix = fieldix;
PadnameFIELDINFO(pn)->fieldstash = (HV *)SvREFCNT_inc(stash);
if
(!aux->xhv_class_fields)
aux->xhv_class_fields = newPADNAMELIST(0);
padnamelist_store(aux->xhv_class_fields, PadnamelistMAX(aux->xhv_class_fields)+1, pn);
PadnameREFCNT_inc(pn);
}
static
void
apply_field_attribute_param(pTHX_ PADNAME *pn, SV *value)
{
if
(!value)
value = newSVpvn_utf8(PadnamePV(pn) + 1, PadnameLEN(pn) - 1, PadnameUTF8(pn));
if
(PadnamePV(pn)[0] !=
'$'
)
croak(
"Only scalar fields can take a :param attribute"
);
if
(PadnameFIELDINFO(pn)->paramname)
croak(
"Field already has a parameter name, cannot add another"
);
HV *stash = PadnameFIELDINFO(pn)->fieldstash;
assert
(HvSTASH_IS_CLASS(stash));
struct
xpvhv_aux *aux = HvAUX(stash);
if
(aux->xhv_class_param_map &&
hv_exists_ent(aux->xhv_class_param_map, value, 0))
croak(
"Cannot assign :param(%"
SVf
") to field %"
SVf
" because that name is already in use"
,
SVfARG(value), SVfARG(PadnameSV(pn)));
PadnameFIELDINFO(pn)->paramname = SvREFCNT_inc(value);
if
(!aux->xhv_class_param_map)
aux->xhv_class_param_map = newHV();
(
void
)hv_store_ent(aux->xhv_class_param_map, value, newSVuv(PadnameFIELDINFO(pn)->fieldix), 0);
}
static
void
apply_field_attribute_reader(pTHX_ PADNAME *pn, SV *value)
{
if
(value)
SvREFCNT_inc(value);
else
value = newSVpvn_utf8(PadnamePV(pn) + 1, PadnameLEN(pn) - 1, PadnameUTF8(pn));
PADOFFSET fieldix = PadnameFIELDINFO(pn)->fieldix;
I32 floor_ix = start_subparse(FALSE, 0);
SAVEFREESV(PL_compcv);
I32 save_ix = block_start(TRUE);
PADOFFSET padix;
padix = pad_add_name_pvs(
"$self"
, 0, NULL, NULL);
assert
(padix == PADIX_SELF);
padix = pad_add_name_pvn(PadnamePV(pn), PadnameLEN(pn), 0, NULL, NULL);
intro_my();
OP *methstartop;
{
UNOP_AUX_item *aux;
aux = (UNOP_AUX_item *)PerlMemShared_malloc(
sizeof
(UNOP_AUX_item) * (2 + 2));
UNOP_AUX_item *ap = aux;
(ap++)->uv = 1;
(ap++)->uv = fieldix;
(ap++)->uv = padix;
(ap++)->uv = fieldix;
methstartop = newUNOP_AUX(OP_METHSTART, 0, NULL, aux);
}
OP *argcheckop;
{
struct
op_argcheck_aux *aux = (
struct
op_argcheck_aux *)
PerlMemShared_malloc(
sizeof
(*aux));
aux->params = 0;
aux->opt_params = 0;
aux->slurpy = 0;
argcheckop = newUNOP_AUX(OP_ARGCHECK, 0, NULL, (UNOP_AUX_item *)aux);
}
OP *retop;
{
OPCODE optype = 0;
switch
(PadnamePV(pn)[0]) {
case
'$'
: optype = OP_PADSV;
break
;
case
'@'
: optype = OP_PADAV;
break
;
case
'%'
: optype = OP_PADHV;
break
;
default
: NOT_REACHED;
}
retop = newLISTOP(OP_RETURN, 0,
newOP(OP_PUSHMARK, 0),
newPADxVOP(optype, 0, padix));
}
OP *ops = newLISTOPn(OP_LINESEQ, 0,
methstartop,
argcheckop,
retop,
NULL);
SvREFCNT_inc(PL_compcv);
ops = block_end(save_ix, ops);
OP *nameop = newSVOP(OP_CONST, 0, value);
CV *cv = newATTRSUB(floor_ix, nameop, NULL, NULL, ops);
CvIsMETHOD_on(cv);
}
static
struct
{
const
char
*name;
bool
requires_value;
void
(*apply)(pTHX_ PADNAME *pn, SV *value);
}
const
field_attributes[] = {
{ .name =
"param"
,
.requires_value =
false
,
.apply = &apply_field_attribute_param,
},
{ .name =
"reader"
,
.requires_value =
false
,
.apply = &apply_field_attribute_reader,
},
{ NULL,
false
, NULL }
};
static
void
S_class_apply_field_attribute(pTHX_ PADNAME *pn, OP *attr)
{
assert
(attr->op_type == OP_CONST);
SV *name, *value;
split_attr_nameval(cSVOPx_sv(attr), &name, &value);
for
(
int
i = 0; field_attributes[i].name; i++) {
if
(!strEQ(SvPVX(name), field_attributes[i].name))
continue
;
if
(field_attributes[i].requires_value && !(value && SvOK(value)))
croak(
"Field attribute %"
SVf
" requires a value"
, SVfARG(name));
(*field_attributes[i].apply)(aTHX_ pn, value);
return
;
}
croak(
"Unrecognized field attribute %"
SVf, SVfARG(name));
}
void
Perl_class_apply_field_attributes(pTHX_ PADNAME *pn, OP *attrlist)
{
PERL_ARGS_ASSERT_CLASS_APPLY_FIELD_ATTRIBUTES;
if
(!attrlist)
return
;
if
(attrlist->op_type == OP_NULL) {
op_free(attrlist);
return
;
}
if
(attrlist->op_type == OP_LIST) {
OP *o = cLISTOPx(attrlist)->op_first;
assert
(o->op_type == OP_PUSHMARK);
o = OpSIBLING(o);
for
(; o; o = OpSIBLING(o))
S_class_apply_field_attribute(aTHX_ pn, o);
}
else
S_class_apply_field_attribute(aTHX_ pn, attrlist);
op_free(attrlist);
}
void
Perl_class_set_field_defop(pTHX_ PADNAME *pn, OPCODE defmode, OP *defop)
{
PERL_ARGS_ASSERT_CLASS_SET_FIELD_DEFOP;
assert
(defmode == 0 || defmode == OP_ORASSIGN || defmode == OP_DORASSIGN);
assert
(HvSTASH_IS_CLASS(PL_curstash));
op_free(PadnameFIELDINFO(pn)->defop);
PadnameFIELDINFO(pn)->defop = defop;
forbid_outofblock_ops(defop,
"field initialiser expression"
);
char
sigil = PadnamePV(pn)[0];
switch
(sigil) {
case
'$'
:
defop = op_contextualize(defop, G_SCALAR);
break
;
case
'@'
:
case
'%'
:
defop = op_contextualize(op_force_list(defop), G_LIST);
break
;
}
PadnameFIELDINFO(pn)->defop = newLISTOP(OP_LINESEQ, 0,
newSTATEOP(0, NULL, NULL), defop);
switch
(defmode) {
case
OP_DORASSIGN:
PadnameFIELDINFO(pn)->def_if_undef =
true
;
break
;
case
OP_ORASSIGN:
PadnameFIELDINFO(pn)->def_if_false =
true
;
break
;
}
}
void
Perl_class_add_ADJUST(pTHX_ HV *stash, CV *cv)
{
PERL_ARGS_ASSERT_CLASS_ADD_ADJUST;
assert
(HvSTASH_IS_CLASS(stash));
struct
xpvhv_aux *aux = HvAUX(stash);
if
(!aux->xhv_class_adjust_blocks)
aux->xhv_class_adjust_blocks = newAV();
av_push(aux->xhv_class_adjust_blocks, (SV *)cv);
}
OP *
Perl_ck_classname(pTHX_ OP *o)
{
if
(!CvIsMETHOD(PL_compcv))
croak(
"Cannot use __CLASS__ outside of a method or field initializer expression"
);
return
o;
}
PP(pp_classname)
{
dTARGET;
SV *self = PAD_SVl(PADIX_SELF);
assert
(SvTYPE(SvRV(self)) == SVt_PVOBJ);
rpp_xpush_1(TARG);
sv_ref(TARG, SvRV(self),
true
);
return
NORMAL;
}