Skip to content

Commit 054ceee

Browse files
committed
Accept field VAR = EXPR on field vars
Allows non-constant expressions with side effects. Evaluated during the constructor of each instance.
1 parent e3b003d commit 054ceee

File tree

23 files changed

+1281
-878
lines changed

23 files changed

+1281
-878
lines changed

class.c

Lines changed: 246 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -44,67 +44,82 @@ Perl_newSVobject(pTHX_ Size_t fieldcount)
4444
return sv;
4545
}
4646

47-
XS(initfields);
48-
XS(initfields)
47+
PP(pp_initfield)
4948
{
50-
dXSARGS;
51-
assert(items == 1);
52-
53-
SV *instance = POPs;
54-
55-
HV *stash = (HV *)XSANY.any_ptr;
56-
assert(HvSTASH_IS_CLASS(stash));
57-
58-
struct xpvhv_aux *aux = HvAUX(stash);
59-
60-
if(aux->xhv_class_superclass) {
61-
struct xpvhv_aux *superaux = HvAUX(aux->xhv_class_superclass);
62-
63-
ENTER;
64-
SAVETMPS;
65-
66-
EXTEND(SP, 1);
67-
PUSHMARK(SP);
68-
PUSHs(instance);
69-
PUTBACK;
70-
71-
call_sv((SV *)superaux->xhv_class_initfields_cv, G_VOID);
72-
73-
SPAGAIN;
49+
dSP;
50+
UNOP_AUX_item *aux = cUNOP_AUX->op_aux;
7451

75-
FREETMPS;
76-
LEAVE;
77-
}
52+
SV *self = PAD_SVl(PADIX_SELF);
53+
assert(SvTYPE(SvRV(self)) == SVt_PVOBJ);
54+
SV *instance = SvRV(self);
7855

7956
SV **fields = ObjectFIELDS(instance);
8057

81-
PADNAMELIST *fieldnames = aux->xhv_class_fields;
82-
83-
for(U32 i = 0; fieldnames && i <= PadnamelistMAX(fieldnames); i++) {
84-
PADNAME *pn = PadnamelistARRAY(fieldnames)[i];
85-
PADOFFSET fieldix = PadnameFIELDINFO(pn)->fieldix;
86-
87-
SV *val = NULL;
58+
PADOFFSET fieldix = aux[0].uv;
8859

89-
switch(PadnamePV(pn)[0]) {
90-
case '$':
60+
SV *val;
61+
switch(PL_op->op_private & (OPpINITFIELD_AV|OPpINITFIELD_HV)) {
62+
case 0:
63+
if(PL_op->op_flags & OPf_STACKED)
64+
val = newSVsv(POPs);
65+
else
9166
val = newSV(0);
92-
break;
93-
94-
case '@':
95-
val = (SV *)newAV();
96-
break;
97-
98-
case '%':
99-
val = (SV *)newHV();
100-
break;
67+
break;
68+
69+
case OPpINITFIELD_AV:
70+
{
71+
AV *av;
72+
if(PL_op->op_flags & OPf_STACKED) {
73+
SV **svp = PL_stack_base + POPMARK + 1;
74+
STRLEN count = SP - svp + 1;
75+
76+
av = newAV_alloc_x(count);
77+
78+
av_extend(av, count);
79+
while(svp <= SP) {
80+
av_push_simple(av, newSVsv(*svp));
81+
svp++;
82+
}
83+
}
84+
else
85+
av = newAV();
86+
val = (SV *)av;
87+
break;
88+
}
10189

102-
default:
103-
NOT_REACHED;
90+
case OPpINITFIELD_HV:
91+
{
92+
HV *hv = newHV();
93+
if(PL_op->op_flags & OPf_STACKED) {
94+
SV **svp = PL_stack_base + POPMARK + 1;
95+
STRLEN svcount = SP - svp + 1;
96+
97+
if(svcount % 2)
98+
Perl_warner(aTHX_
99+
packWARN(WARN_MISC), "Odd number of elements in hash field initialization");
100+
101+
while(svp <= SP) {
102+
SV *key = *svp; svp++;
103+
SV *val = svp <= SP ? *svp : &PL_sv_undef; svp++;
104+
105+
hv_store_ent(hv, key, newSVsv(val), 0);
106+
}
107+
}
108+
val = (SV *)hv;
109+
break;
104110
}
111+
}
112+
113+
fields[fieldix] = val;
105114

106-
fields[fieldix] = val;
115+
PADOFFSET padix = PL_op->op_targ;
116+
if(padix) {
117+
SAVESPTR(PAD_SVl(padix));
118+
SV *sv = PAD_SVl(padix) = SvREFCNT_inc(val);
119+
save_freesv(sv);
107120
}
121+
122+
RETURN;
108123
}
109124

110125
XS(injected_constructor);
@@ -146,14 +161,16 @@ XS(injected_constructor)
146161
SvOBJECT_on(instance);
147162
SvSTASH_set(instance, MUTABLE_HV(SvREFCNT_inc_simple(stash)));
148163

164+
SV *self = sv_2mortal(newRV_noinc(instance));
165+
149166
assert(aux->xhv_class_initfields_cv);
150167
{
151168
ENTER;
152169
SAVETMPS;
153170

154171
EXTEND(SP, 1);
155172
PUSHMARK(SP);
156-
PUSHs(instance);
173+
PUSHs(self);
157174
PUTBACK;
158175

159176
call_sv((SV *)aux->xhv_class_initfields_cv, G_VOID);
@@ -164,8 +181,6 @@ XS(injected_constructor)
164181
LEAVE;
165182
}
166183

167-
SV *self = sv_2mortal(newRV_noinc(instance));
168-
169184
if(aux->xhv_class_adjust_blocks) {
170185
CV **cvp = (CV **)AvARRAY(aux->xhv_class_adjust_blocks);
171186
U32 nblocks = av_count(aux->xhv_class_adjust_blocks);
@@ -340,6 +355,25 @@ Perl_class_setup_stash(pTHX_ HV *stash)
340355
aux->xhv_aux_flags |= HvAUXf_IS_CLASS;
341356

342357
SAVEDESTRUCTOR_X(invoke_class_seal, stash);
358+
359+
/* Prepare a suspended compcv for parsing field init expressions */
360+
{
361+
I32 floor_ix = start_subparse(FALSE, 0);
362+
363+
CvIsMETHOD_on(PL_compcv);
364+
365+
/* We don't want to make `$self` visible during the expression but we
366+
* still need to give it a name. Make it unusable from pure perl
367+
*/
368+
PADOFFSET padix = pad_add_name_pvs("$(self)", 0, NULL, NULL);
369+
assert(padix == PADIX_SELF);
370+
PERL_UNUSED_VAR(padix);
371+
372+
Newx(aux->xhv_class_suspended_initfields_compcv, 1, struct suspended_compcv);
373+
suspend_compcv(aux->xhv_class_suspended_initfields_compcv);
374+
375+
LEAVE_SCOPE(floor_ix);
376+
}
343377
}
344378

345379
#define split_package_ver(value, pkgname, pkgversion) S_split_package_ver(aTHX_ value, pkgname, pkgversion)
@@ -555,14 +589,141 @@ Perl_class_seal_stash(pTHX_ HV *stash)
555589
assert(HvSTASH_IS_CLASS(stash));
556590
struct xpvhv_aux *aux = HvAUX(stash);
557591

592+
/* generate initfields CV */
558593
{
559-
CV *newcv = newXS_flags(NULL, initfields, __FILE__, NULL, 0);
560-
CvXSUBANY(newcv).any_ptr = stash;
594+
I32 floor_ix = PL_savestack_ix;
595+
SAVEI32(PL_subline);
596+
save_item(PL_subname);
597+
598+
resume_compcv_final(aux->xhv_class_suspended_initfields_compcv);
599+
600+
/* Some OP_INITFIELD ops will need to populate the pad with their
601+
* result because later ops will rely on it. There's no need to do
602+
* this for every op though. Store a mapping to work out which ones
603+
* we'll need.
604+
*/
605+
PADNAMELIST *pnl = PadlistNAMES(CvPADLIST(PL_compcv));
606+
HV *fieldix_to_padix = newHV();
607+
SAVEFREESV((SV *)fieldix_to_padix);
608+
609+
/* padix 0 == @_; padix 1 == $self. Start at 2 */
610+
for(PADOFFSET padix = 2; padix <= PadnamelistMAX(pnl); padix++) {
611+
PADNAME *pn = PadnamelistARRAY(pnl)[padix];
612+
if(!pn || !PadnameIsFIELD(pn))
613+
continue;
614+
615+
U32 fieldix = PadnameFIELDINFO(pn)->fieldix;
616+
hv_store_ent(fieldix_to_padix, sv_2mortal(newSVuv(fieldix)), newSVuv(padix), 0);
617+
}
618+
619+
OP *ops = NULL;
620+
621+
ops = op_append_list(OP_LINESEQ, ops,
622+
newUNOP_AUX(OP_METHSTART, 0, NULL, NULL));
623+
624+
if(aux->xhv_class_superclass) {
625+
HV *superstash = aux->xhv_class_superclass;
626+
assert(HvSTASH_IS_CLASS(superstash));
627+
struct xpvhv_aux *superaux = HvAUX(superstash);
628+
629+
/* Build an OP_ENTERSUB */
630+
OP *o = NULL;
631+
o = op_append_list(OP_LIST, o,
632+
newPADxVOP(OP_PADSV, 0, PADIX_SELF));
633+
/* TODO: This won't work at all well under `use threads` because
634+
* it embeds the CV * to the superclass initfields CV right into
635+
* the optree. Maybe we'll have to pop it in the pad or something
636+
*/
637+
o = op_append_list(OP_LIST, o,
638+
newSVOP(OP_CONST, 0, (SV *)superaux->xhv_class_initfields_cv));
639+
640+
ops = op_append_list(OP_LINESEQ, ops,
641+
op_convert_list(OP_ENTERSUB, OPf_WANT_VOID|OPf_STACKED, o));
642+
}
643+
644+
PADNAMELIST *fieldnames = aux->xhv_class_fields;
645+
646+
for(U32 i = 0; fieldnames && i <= PadnamelistMAX(fieldnames); i++) {
647+
PADNAME *pn = PadnamelistARRAY(fieldnames)[i];
648+
char sigil = PadnamePV(pn)[0];
649+
PADOFFSET fieldix = PadnameFIELDINFO(pn)->fieldix;
650+
651+
/* Extract the OP_{NEXT,DB}STATE op from the defop so we can
652+
* splice it in
653+
*/
654+
OP *valop = PadnameFIELDINFO(pn)->defop;
655+
if(valop && valop->op_type == OP_LINESEQ) {
656+
OP *o = cLISTOPx(valop)->op_first;
657+
cLISTOPx(valop)->op_first = NULL;
658+
cLISTOPx(valop)->op_last = NULL;
659+
/* have to clear the OPf_KIDS flag or op_free() will get upset */
660+
valop->op_flags &= ~OPf_KIDS;
661+
op_free(valop);
662+
assert(valop->op_type == OP_FREED);
663+
664+
OP *fieldcop = o;
665+
assert(fieldcop->op_type == OP_NEXTSTATE || fieldcop->op_type == OP_DBSTATE);
666+
o = OpSIBLING(o);
667+
OpLASTSIB_set(fieldcop, NULL);
668+
669+
valop = o;
670+
OpLASTSIB_set(valop, NULL);
671+
672+
ops = op_append_list(OP_LINESEQ, ops, fieldcop);
673+
}
674+
675+
U8 op_priv = 0;
676+
switch(sigil) {
677+
case '$':
678+
break;
679+
680+
case '@':
681+
op_priv = OPpINITFIELD_AV;
682+
break;
683+
684+
case '%':
685+
op_priv = OPpINITFIELD_HV;
686+
break;
687+
688+
default:
689+
NOT_REACHED;
690+
}
691+
692+
UNOP_AUX_item *aux;
693+
Newx(aux, 1, UNOP_AUX_item);
694+
695+
aux[0].uv = fieldix;
696+
697+
OP *fieldop = newUNOP_AUX(OP_INITFIELD, valop ? OPf_STACKED : 0, valop, aux);
698+
fieldop->op_private = op_priv;
699+
700+
HE *he;
701+
if((he = hv_fetch_ent(fieldix_to_padix, sv_2mortal(newSVuv(fieldix)), 0, 0)) &&
702+
SvOK(HeVAL(he))) {
703+
fieldop->op_targ = SvUV(HeVAL(he));
704+
}
705+
706+
ops = op_append_list(OP_LINESEQ, ops, fieldop);
707+
}
561708

562-
aux->xhv_class_initfields_cv = newcv;
709+
CV *initfields = newATTRSUB(floor_ix, NULL, NULL, NULL, ops);
710+
711+
aux->xhv_class_initfields_cv = initfields;
563712
}
564713
}
565714

715+
void
716+
Perl_class_prepare_initfield_parse(pTHX)
717+
{
718+
PERL_ARGS_ASSERT_CLASS_PREPARE_INITFIELD_PARSE;
719+
720+
assert(HvSTASH_IS_CLASS(PL_curstash));
721+
struct xpvhv_aux *aux = HvAUX(PL_curstash);
722+
723+
resume_compcv_and_save(aux->xhv_class_suspended_initfields_compcv);
724+
CvOUTSIDE_SEQ(PL_compcv) = PL_cop_seqmax;
725+
}
726+
566727
void
567728
Perl_class_prepare_method_parse(pTHX_ CV *cv)
568729
{
@@ -656,6 +817,7 @@ Perl_class_add_field(pTHX_ HV *stash, PADNAME *pn)
656817

657818
PadnameFIELDINFO(pn)->fieldix = fieldix;
658819
PadnameFIELDINFO(pn)->fieldstash = (HV *)SvREFCNT_inc(stash);
820+
PadnameFIELDINFO(pn)->defop = NULL;
659821

660822
if(!aux->xhv_class_fields)
661823
aux->xhv_class_fields = newPADNAMELIST(0);
@@ -664,6 +826,34 @@ Perl_class_add_field(pTHX_ HV *stash, PADNAME *pn)
664826
PadnameREFCNT_inc(pn);
665827
}
666828

829+
void
830+
Perl_class_set_field_defop(pTHX_ PADNAME *pn, OP *defop)
831+
{
832+
PERL_ARGS_ASSERT_CLASS_SET_FIELD_DEFOP;
833+
834+
assert(HvSTASH_IS_CLASS(PL_curstash));
835+
836+
forbid_outofblock_ops(defop, "field initialiser expression");
837+
838+
if(PadnameFIELDINFO(pn)->defop)
839+
op_free(PadnameFIELDINFO(pn)->defop);
840+
841+
char sigil = PadnamePV(pn)[0];
842+
switch(sigil) {
843+
case '$':
844+
defop = op_contextualize(defop, G_SCALAR);
845+
break;
846+
847+
case '@':
848+
case '%':
849+
defop = op_contextualize(op_force_list(defop), G_LIST);
850+
break;
851+
}
852+
853+
PadnameFIELDINFO(pn)->defop = newLISTOP(OP_LINESEQ, 0,
854+
newSTATEOP(0, NULL, NULL), defop);
855+
}
856+
667857
void
668858
Perl_class_add_ADJUST(pTHX_ HV *stash, CV *cv)
669859
{

embed.fnc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2356,7 +2356,7 @@ p |OP * |sawparens |NULLOK OP *o
23562356
Apd |OP * |op_contextualize \
23572357
|NN OP *o \
23582358
|I32 context
2359-
; Used in op.c
2359+
; Used in op.c and class.c
23602360
Apd |OP * |op_force_list |NULLOK OP *o
23612361
: Used in perly.y
23622362
p |OP * |scalar |NULLOK OP *o
@@ -3975,12 +3975,16 @@ Cp |void |class_add_field|NN HV *stash \
39753975
Cp |void |class_apply_attributes \
39763976
|NN HV *stash \
39773977
|NULLOK OP *attrlist
3978+
Cp |void |class_prepare_initfield_parse
39783979
Cp |void |class_prepare_method_parse \
39793980
|NN CV *cv
39803981
Cp |void |class_seal_stash \
39813982
|NN HV *stash
39823983
Cp |void |class_setup_stash \
39833984
|NN HV *stash
3985+
Cp |void |class_set_field_defop \
3986+
|NN PADNAME *pn \
3987+
|NN OP *defop
39843988
Cp |OP * |class_wrap_method_body \
39853989
|NULLOK OP *o
39863990
Cp |void |croak_kw_unless_class \

0 commit comments

Comments
 (0)