1 /* Copyright (c) 1997-2021
2    Ewgenij Gawrilow, Michael Joswig, and the polymake team
3    Technische Universität Berlin, Germany
4    https://polymake.org
5 
6    This program is free software; you can redistribute it and/or modify it
7    under the terms of the GNU General Public License as published by the
8    Free Software Foundation; either version 2, or (at your option) any
9    later version: http://www.gnu.org/licenses/gpl.txt.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 --------------------------------------------------------------------------------
16 */
17 
18 #include "polymake/perl/Ext.h"
19 
20 namespace pm { namespace perl { namespace glue {
21 
22 namespace {
23 
24 Perl_ppaddr_t def_pp_LEAVE, def_pp_OPEN;
25 
26 template <typename LocalHandler>
27 struct local_wrapper {
undopm::perl::glue::__anon132beb6e0111::local_wrapper28    static void undo(pTHX_ void* p)
29    {
30       // make a local copy because the save stack can be reallocated during execution of undo action
31       const LocalHandler handler(*reinterpret_cast<LocalHandler*>(PL_savestack + (PL_savestack_ix - PTR2IV(p))));
32       handler.undo(aTHX);
33    }
34 
allocpm::perl::glue::__anon132beb6e0111::local_wrapper35    static void* alloc(pTHX)
36    {
37       const I32 save_ix = PL_savestack_ix;
38       (void)SSNEWt(1, LocalHandler);
39       save_destructor_x(&undo, NUM2PTR(void*, PL_savestack_ix - save_ix));
40       return &(PL_savestack[save_ix]);
41    }
42 };
43 
44 template <typename LocalHandler, typename... Args>
local_do(pTHX_ Args &&...args)45 void local_do(pTHX_ Args&&... args)
46 {
47    new(local_wrapper<LocalHandler>::alloc(aTHX)) LocalHandler(aTHX_ args...);
48 }
49 
50 // --------------------
51 
52 struct local_ref_handler {
53    SV* var;
54    void* orig_any;
55    U32 orig_flags;
56    char* orig_pv;       // as a representative for SV_HEAD_UNION
57    SV* temp_owner;
58 
local_ref_handlerpm::perl::glue::__anon132beb6e0111::local_ref_handler59    local_ref_handler(pTHX_ SV* var_, SV* value)
60       : var(var_)
61       , orig_any(SvANY(var_))
62       , orig_flags(SvFLAGS(var_) & ~SVs_TEMP)
63       , orig_pv(var_->sv_u.svu_pv)
64       , temp_owner(value)
65    {
66       var->sv_u.svu_pv = value->sv_u.svu_pv;
67       SvANY(var) = SvANY(value);
68       SvFLAGS(var) = SvFLAGS(value) & ~SVs_TEMP;
69       SvREFCNT_inc_simple_void_NN(var);
70       SvREFCNT_inc_simple_void_NN(value);
71    }
72 
undopm::perl::glue::__anon132beb6e0111::local_ref_handler73    void undo(pTHX) const
74    {
75       SvANY(var) = orig_any;
76       SvFLAGS(temp_owner) = SvFLAGS(var);
77       temp_owner->sv_u.svu_pv = var->sv_u.svu_pv;
78       var->sv_u.svu_pv = orig_pv;
79       SvFLAGS(var) = orig_flags;
80       SvREFCNT_dec(var);
81       SvREFCNT_dec(temp_owner);
82    }
83 };
84 
85 }
86 
parse_expression_in_parens(pTHX)87 OP* parse_expression_in_parens(pTHX)
88 {
89    lex_read_space(0);
90    if (PL_parser->bufptr == PL_parser->bufend || *PL_parser->bufptr != '(')
91       return nullptr;
92    lex_read_to(PL_parser->bufptr+1);
93    OP* o = parse_termexpr(0);
94    if (!o) return nullptr;
95    lex_read_space(0);
96    if (PL_parser->bufptr == PL_parser->bufend || *PL_parser->bufptr != ')') {
97       op_free(o);
98       return nullptr;
99    }
100    lex_read_to(PL_parser->bufptr+1);
101    return o;
102 }
103 
104 }
105 namespace ops {
106 
107 using namespace pm::perl::glue;
108 
local_ref(pTHX)109 OP* local_ref(pTHX)
110 {
111    dSP;
112    SV* left = POPs;
113    SV* right = GIMME_V != G_VOID ? TOPs : POPs;
114    if (!SvROK(right))
115       DIE(aTHX_ "local ref value must be a reference");
116    SV* value = SvRV(right);
117    SV* var;
118    switch (SvTYPE(value)) {
119    case SVt_PVAV:
120       if (SvTYPE(left) == SVt_PVGV) {
121          var = (SV*)GvAV(left);
122          if (!var || !GvIMPORTED_AV(left))
123             DIE(aTHX_ "local ref target array not declared");
124          break;
125       } else if (SvROK(left)) {
126          var = SvRV(left);
127          if (SvTYPE(var) == SVt_PVAV) break;
128       }
129       DIE(aTHX_ "local ref illegal/incompatible arguments: array references expected");
130    case SVt_PVHV:
131       if (SvTYPE(left) == SVt_PVGV) {
132          var = (SV*)GvHV(left);
133          if (!var || !GvIMPORTED_HV(left))
134             DIE(aTHX_ "local ref target hash not declared");
135          break;
136       } else if (SvROK(left)) {
137          var = SvRV(left);
138          if (SvTYPE(var) == SVt_PVHV) break;
139       }
140       DIE(aTHX_ "local ref illegal/incompatible arguments: hash reference expected");
141    case SVt_PVCV:
142       if (SvTYPE(left) == SVt_PVGV) {
143          var = (SV*)GvCV(left);
144          if (!var)
145             DIE(aTHX_ "local ref target sub not defined");
146          break;
147       } else if (SvROK(left)) {
148          var = SvRV(left);
149          if (SvTYPE(var) == SVt_PVCV) break;
150       }
151       DIE(aTHX_ "local ref illegal/incompatible arguments: code reference expected");
152    default:
153       DIE(aTHX_ "local ref unsupported value type: must be an array, hash or code reference");
154    }
155    local_do<local_ref_handler>(aTHX_ var, value);
156    RETURN;
157 }
158 
159 }
160 namespace glue { namespace {
161 
parse_local_ref(pTHX_ OP ** op_ptr)162 int parse_local_ref(pTHX_ OP** op_ptr)
163 {
164    op_keeper<OP> o(aTHX_ parse_termexpr(0));
165    if (!o || o->op_type != OP_SASSIGN)
166       return KEYWORD_PLUGIN_DECLINE;
167    o->op_ppaddr = ops::local_ref;
168    *op_ptr = o.release();
169    PL_hints |= HINT_BLOCK_SCOPE;
170    return KEYWORD_PLUGIN_EXPR;
171 }
172 
173 // --------------------
174 
175 struct local_scalar_handler {
176    SV* var;
177    SV orig;
178 
local_scalar_handlerpm::perl::glue::__anon132beb6e0211::local_scalar_handler179    local_scalar_handler(pTHX_ SV* var_, SV* value)
180       : var(var_)
181    {
182       orig.sv_any = var->sv_any;
183       orig.sv_refcnt = var->sv_refcnt;
184       orig.sv_flags = var->sv_flags;
185       orig.sv_u.svu_pv = var->sv_u.svu_pv;
186       var->sv_any = nullptr;
187       var->sv_flags = 0;
188       var->sv_refcnt = 1;
189       sv_setsv(var, value);
190    }
191 
undopm::perl::glue::__anon132beb6e0211::local_scalar_handler192    void undo(pTHX) const
193    {
194       if (SvREFCNT(var) > 1) {
195          SvREFCNT_dec(var);
196       } else {
197          SvREFCNT(var) = 0;
198          sv_clear(var);
199       }
200       var->sv_any = orig.sv_any;
201       var->sv_refcnt = orig.sv_refcnt;
202       var->sv_flags = orig.sv_flags;
203       var->sv_u.svu_pv = orig.sv_u.svu_pv;
204    }
205 };
206 
local_scalar_op(pTHX)207 OP* local_scalar_op(pTHX)
208 {
209    dSP;
210    SV* left = POPs;
211    SV* right = GIMME_V != G_VOID ? TOPs : POPs;
212    local_do<local_scalar_handler>(aTHX_ left, right);
213    RETURN;
214 }
215 
216 // --------------------
217 
218 } }
219 namespace ops {
220 
221 void
localize_scalar(pTHX_ SV * var,SV * value)222 localize_scalar(pTHX_ SV* var, SV* value)
223 {
224    local_do<local_scalar_handler>(aTHX_ var, value);
225 }
226 
227 void
localize_scalar(pTHX_ SV * var)228 localize_scalar(pTHX_ SV* var)
229 {
230    localize_scalar(aTHX_ var, sv_mortalcopy(var));
231 }
232 
233 }
234 namespace glue { namespace {
235 
local_save_scalar_op(pTHX)236 OP* local_save_scalar_op(pTHX)
237 {
238    dSP;
239    SV* var = GIMME_V != G_VOID ? TOPs : POPs;
240    ops::localize_scalar(aTHX_ var);
241    RETURN;
242 }
243 
244 // --------------------
245 
246 struct local_incr_handler {
247    SV* var;
248    IV incr;
249 
local_incr_handlerpm::perl::glue::__anon132beb6e0311::local_incr_handler250    local_incr_handler(pTHX_ SV* var_, IV incr_)
251       : var(var_)
252       , incr(incr_) {}
253 
undopm::perl::glue::__anon132beb6e0311::local_incr_handler254    void undo(pTHX) const
255    {
256       if (SvIOK(var))
257          sv_setiv(var, SvIVX(var) - incr);
258       else if (SvNOK(var))
259          sv_setnv(var, SvNVX(var) - NV(incr));
260       else
261          Perl_croak(aTHX_ "undoing local increment: variable is no more numerical");
262    }
263 };
264 
local_incr_op(pTHX)265 OP* local_incr_op(pTHX)
266 {
267    dSP;
268    SV* var = GIMME_V != G_VOID ? TOPs : POPs;
269    local_do<local_incr_handler>(aTHX_ var, 1 - PL_op->op_private);
270    RETURN;
271 }
272 
parse_local_scalar(pTHX_ OP ** op_ptr)273 int parse_local_scalar(pTHX_ OP** op_ptr)
274 {
275    op_keeper<OP> o(aTHX_ parse_termexpr(0));
276    if (!o) return KEYWORD_PLUGIN_DECLINE;
277    if (o->op_type == OP_SASSIGN) {
278       OP* left = ((BINOP*)o.operator->())->op_last;
279       if (left->op_type != OP_PADSV && left->op_type != OP_ENTERSUB && left->op_type != OP_RV2SV) {
280          report_parse_error("local scalar applicable to lexical variables, scalars delivered by dereferencing or returned from subs");
281          return KEYWORD_PLUGIN_DECLINE;
282       }
283       o->op_ppaddr = local_scalar_op;
284    } else {
285       OP* var = o.release();
286       switch (var->op_type) {
287       case OP_PREINC:
288       case OP_I_PREINC:
289          o = PmNewCustomOP(UNOP, 0, var);
290          o->op_ppaddr = local_incr_op;
291          o->op_private = 0;
292          break;
293       case OP_PREDEC:
294       case OP_I_PREDEC:
295          o = PmNewCustomOP(UNOP, 0, var);
296          o->op_ppaddr = local_incr_op;
297          o->op_private = 2;
298          break;
299       case OP_POSTINC:
300       case OP_I_POSTINC:
301          report_parse_error("local scalar not compatible with post-increment");
302          return KEYWORD_PLUGIN_DECLINE;
303       case OP_POSTDEC:
304       case OP_I_POSTDEC:
305          report_parse_error("local scalar not compatible with post-decrement");
306          return KEYWORD_PLUGIN_DECLINE;
307       default:
308          o = PmNewCustomOP(UNOP, 0, op_lvalue(var, var->op_type));
309          o->op_ppaddr = local_save_scalar_op;
310          break;
311       }
312    }
313    *op_ptr = o.release();
314    PL_hints |= HINT_BLOCK_SCOPE;
315    return KEYWORD_PLUGIN_EXPR;
316 }
317 
318 // --------------------
319 
320 struct local_push_unshift_handler {
321    AV* av;
322    IV n;
323 
local_push_unshift_handlerpm::perl::glue::__anon132beb6e0311::local_push_unshift_handler324    local_push_unshift_handler(AV* av_, IV n_)
325       : av(av_)
326       , n(n_) {}
327 
insert_elemspm::perl::glue::__anon132beb6e0311::local_push_unshift_handler328    void insert_elems(pTHX_ SV* const * src, SV** dst)
329    {
330       for (SV* const * const src_end = src + n; src < src_end; ++src, ++dst) {
331          SV* d = *src;
332          if (SvREADONLY(d) || !SvTEMP(d))
333             *dst = newSVsv(d);
334          else
335             *dst = SvREFCNT_inc_simple_NN(d);
336       }
337       AvFILLp(av) += n;
338    }
339 };
340 
341 struct local_push_handler : local_push_unshift_handler {
342 
local_push_handlerpm::perl::glue::__anon132beb6e0311::local_push_handler343    local_push_handler(pTHX_ AV* av_, SV* const * src, IV n_)
344       : local_push_unshift_handler(av_, n_)
345    {
346       av_extend(av, AvFILLp(av) + n);
347       insert_elems(aTHX_ src, AvARRAY(av)+AvFILLp(av)+1);
348    }
349 
undopm::perl::glue::__anon132beb6e0311::local_push_handler350    void undo(pTHX) const
351    {
352       for (SV **e = AvARRAY(av) + AvFILLp(av), **stop = e-n; e > stop; --e) {
353          SvREFCNT_dec(*e);
354          *e = PmEmptyArraySlot;
355       }
356       AvFILLp(av) -= n;
357    }
358 };
359 
360 struct local_unshift_handler : local_push_unshift_handler {
361 
local_unshift_handlerpm::perl::glue::__anon132beb6e0311::local_unshift_handler362    local_unshift_handler(pTHX_ AV* av_, SV* const * src, IV n_)
363       : local_push_unshift_handler(av_, n_)
364    {
365       av_extend(av, AvFILLp(av)+n);
366       SV** dst = AvARRAY(av);
367       Move(dst, dst + n_, AvFILLp(av)+1, SV*);
368       insert_elems(aTHX_ src, dst);
369    }
370 
undopm::perl::glue::__anon132beb6e0311::local_unshift_handler371    void undo(pTHX) const
372    {
373       SV **e, **stop;
374       for (stop = AvARRAY(av)-1, e = stop + n; e > stop; --e)
375          SvREFCNT_dec(*e);
376       AvFILLp(av) -= n;
377       ++stop;
378       Move(stop + n, stop, AvFILLp(av)+1, SV*);
379       for (e = stop + AvFILLp(av)+1, stop = e+n; e < stop; ++e)
380          *e = PmEmptyArraySlot;
381    }
382 };
383 
384 template <bool is_unshift>
local_push_unshift_op(pTHX)385 OP* local_push_unshift_op(pTHX)
386 {
387    dSP;  dMARK;  dORIGMARK;
388    AV* av = (AV*)*++MARK;
389    IV n = SP - MARK;
390    if (n > 0)
391       local_do<std::conditional_t<is_unshift, local_unshift_handler, local_push_handler>>(aTHX_ av, MARK+1, n);
392    SP = ORIGMARK;
393    RETURN;
394 }
395 
396 template <bool is_unshift>
parse_local_push_unshift(pTHX_ OP ** op_ptr)397 int parse_local_push_unshift(pTHX_ OP** op_ptr)
398 {
399    OP* o = parse_termexpr(0);
400    if (!o) return KEYWORD_PLUGIN_DECLINE;
401    o->op_ppaddr = local_push_unshift_op<is_unshift>;
402    *op_ptr = o;
403    PL_hints |= HINT_BLOCK_SCOPE;
404    return KEYWORD_PLUGIN_EXPR;
405 }
406 
407 // --------------------
408 
409 struct local_pop_handler {
410    AV* av;
411    SV* val;
412 
local_pop_handlerpm::perl::glue::__anon132beb6e0311::local_pop_handler413    local_pop_handler(pTHX_ AV* av_)
414       : av((AV*)SvREFCNT_inc_simple_NN(av_))
415    {
416       val = av_pop(av);
417    }
418 
undopm::perl::glue::__anon132beb6e0311::local_pop_handler419    void undo(pTHX) const
420    {
421       auto localizing = PL_localizing;
422       PL_localizing = 2;
423       av_push(av, val);
424       PL_localizing = localizing;
425       SvREFCNT_dec(av);
426    }
427 };
428 
local_pop_op(pTHX)429 OP* local_pop_op(pTHX)
430 {
431    dSP;
432    AV* av = (AV*)POPs;
433    SV* ret = nullptr;
434    if (AvFILLp(av) >= 0) {
435       ret = AvARRAY(av)[AvFILLp(av)];
436       local_do<local_pop_handler>(aTHX_ av);
437    }
438    if (GIMME_V != G_VOID) {
439       if (!ret) ret = &PL_sv_undef;
440       PUSHs(ret);
441    }
442    RETURN;
443 }
444 
parse_local_pop(pTHX_ OP ** op_ptr)445 int parse_local_pop(pTHX_ OP** op_ptr)
446 {
447    OP* o = parse_termexpr(0);
448    if (!o) return KEYWORD_PLUGIN_DECLINE;
449    o->op_ppaddr = local_pop_op;
450    o->op_private |= OPpLVAL_INTRO;
451    *op_ptr = o;
452    PL_hints |= HINT_BLOCK_SCOPE;
453    return KEYWORD_PLUGIN_EXPR;
454 }
455 
456 // --------------------
457 
458 struct local_shift_handler {
459    AV* av;
460    SV* val;
461 
local_shift_handlerpm::perl::glue::__anon132beb6e0311::local_shift_handler462    local_shift_handler(pTHX_ AV* av_)
463       : av(av_)
464    {
465       SvREFCNT_inc_simple_void_NN(av);
466       val = av_shift(av);
467    }
468 
undopm::perl::glue::__anon132beb6e0311::local_shift_handler469    void undo(pTHX) const
470    {
471       auto localizing = PL_localizing;
472       PL_localizing = 2;
473       av_unshift(av, 1);
474       PL_localizing = localizing;
475       AvARRAY(av)[0] = val;
476       SvREFCNT_dec(av);
477    }
478 };
479 
local_shift_op(pTHX)480 OP* local_shift_op(pTHX)
481 {
482    dSP;
483    AV* av = (AV*)POPs;
484    SV* ret = nullptr;
485    if (AvFILLp(av) >= 0) {
486       ret = AvARRAY(av)[0];
487       local_do<local_shift_handler>(aTHX_ av);
488    }
489    if (GIMME_V != G_VOID) {
490       if (!ret) ret = &PL_sv_undef;
491       PUSHs(ret);
492    }
493    RETURN;
494 }
495 
parse_local_shift(pTHX_ OP ** op_ptr)496 int parse_local_shift(pTHX_ OP** op_ptr)
497 {
498    OP* o = parse_termexpr(0);
499    if (!o) return KEYWORD_PLUGIN_DECLINE;
500    o->op_ppaddr = local_shift_op;
501    o->op_private |= OPpLVAL_INTRO;
502    *op_ptr = o;
503    PL_hints |= HINT_BLOCK_SCOPE;
504    return KEYWORD_PLUGIN_EXPR;
505 }
506 
507 // --------------------
508 
509 struct local_splice_handler {
510    AV* av;
511    IV stretch;
512 
local_splice_handlerpm::perl::glue::__anon132beb6e0311::local_splice_handler513    local_splice_handler(pTHX_ AV* av_, IV first, IV size)
514       : av(av_)
515    {
516       SvREFCNT_inc_simple_void_NN(av);
517       AvFILLp(av) -= size;
518       if (first == 0) {
519          AvARRAY(av) += size;
520          stretch = size;
521       } else {
522          stretch = -size;
523       }
524    }
525 
undopm::perl::glue::__anon132beb6e0311::local_splice_handler526    void undo(pTHX) const
527    {
528       if (stretch > 0) {
529          AvARRAY(av) -= stretch;
530          AvFILLp(av) += stretch;
531       } else {
532          AvFILLp(av) -= stretch;
533       }
534       SvREFCNT_dec(av);
535    }
536 };
537 
local_splice_op(pTHX)538 OP* local_splice_op(pTHX)
539 {
540    dSP;  dMARK;  dORIGMARK;
541    AV* av = (AV*)*++MARK;
542    if (MARK+2 < SP)
543       DIE(aTHX_ "unsupported local splice with insertion");
544    const IV len = AvFILLp(av)+1;
545    IV first, size;
546    if (MARK < SP) {
547       ++MARK;
548       first = SvIV(*MARK);
549       if (first < 0) {
550          first += len;
551          if (first < 0)
552             DIE(aTHX_ "local splice start index too low");
553       } else if (first > len) {
554          first = len;
555       }
556       if (MARK < SP) {
557          if (first != 0)
558             DIE(aTHX_ "unsupported local splice in the middle");
559          ++MARK;
560          size = SvIV(*MARK);
561          if (size < 0) {
562             size += len;
563             if (size < 0)
564                DIE(aTHX_ "local splice size too low");
565          } else if (size > len) {
566             DIE(aTHX_ "local splice size too high");
567          }
568       } else {
569          size = len - first;
570       }
571    } else {
572       first = 0;
573       size = len;
574    }
575 
576    SP = ORIGMARK;
577    if (size != 0) {
578       if (GIMME_V == G_ARRAY) {
579          EXTEND(SP, size);
580          Copy(AvARRAY(av) + first, SP+1, size, SV*);
581          SP += size;
582       }
583       local_do<local_splice_handler>(aTHX_ av, first, size);
584    }
585    RETURN;
586 }
587 
parse_local_splice(pTHX_ OP ** op_ptr)588 int parse_local_splice(pTHX_ OP** op_ptr)
589 {
590    OP* o = parse_termexpr(0);
591    if (!o) return KEYWORD_PLUGIN_DECLINE;
592    o->op_ppaddr = local_splice_op;
593    *op_ptr = o;
594    PL_hints |= HINT_BLOCK_SCOPE;
595    return KEYWORD_PLUGIN_EXPR;
596 }
597 
598 // --------------------
599 
600 struct local_swap_handler {
601    AV* av;
602    IV ix1, ix2;
603 
local_swap_handlerpm::perl::glue::__anon132beb6e0311::local_swap_handler604    local_swap_handler(pTHX_ AV* av_, IV ix1_, IV ix2_)
605       : av(av_)
606       , ix1(ix1_)
607       , ix2(ix2_)
608    {
609       SvREFCNT_inc_simple_void_NN(av);
610       std::swap(AvARRAY(av)[ix1], AvARRAY(av)[ix2]);
611    }
612 
undopm::perl::glue::__anon132beb6e0311::local_swap_handler613    void undo(pTHX) const
614    {
615       std::swap(AvARRAY(av)[ix1], AvARRAY(av)[ix2]);
616       SvREFCNT_dec(av);
617    }
618 };
619 
local_swap_op(pTHX)620 OP* local_swap_op(pTHX)
621 {
622    dSP;
623    IV ix2 = POPi;
624    IV ix1 = POPi;
625    AV* av = (AV*)POPs;
626    if (ix1 < 0) ix1 += AvFILL(av)+1;
627    if (ix2 < 0) ix2 += AvFILL(av)+1;
628    if (ix1 > AvFILL(av) || ix2 > AvFILL(av)) DIE(aTHX_ "local swap: index out of range");
629    local_do<local_swap_handler>(aTHX_ av, ix1, ix2);
630    RETURN;
631 }
632 
parse_local_swap(pTHX_ OP ** op_ptr)633 int parse_local_swap(pTHX_ OP** op_ptr)
634 {
635    op_keeper<OP> o(aTHX_ parse_listexpr(0));
636    if (!o) return KEYWORD_PLUGIN_DECLINE;
637    o->op_ppaddr = local_swap_op;
638    o->op_type = OP_CUSTOM;
639    LISTOP* lo = (LISTOP*)o.operator->();
640    OP* pushmark = lo->op_first;
641    if (pushmark->op_type != OP_PUSHMARK || !OpHAS_SIBLING(pushmark))
642       return KEYWORD_PLUGIN_DECLINE;
643    OP* avop = OpSIBLING(pushmark);
644    if (avop->op_type != OP_RV2AV && avop->op_type != OP_PADAV || !OpHAS_SIBLING(avop)) {
645       report_parse_error("expected: local swap @array, index1, index2");
646       return KEYWORD_PLUGIN_DECLINE;
647    }
648    OP* ix1op = OpSIBLING(avop);
649    if (!OpHAS_SIBLING(ix1op)) {
650       report_parse_error("expected: local swap @array, index1, index2");
651       return KEYWORD_PLUGIN_DECLINE;
652    }
653    OP* ix2op = OpSIBLING(ix1op);
654    if (OpHAS_SIBLING(ix2op)) {
655       report_parse_error("expected: local swap @array, index1, index2");
656       return KEYWORD_PLUGIN_DECLINE;
657    }
658 
659    lo->op_first = doref(avop, OP_NULL, TRUE);
660    op_free(pushmark);
661    *op_ptr = o.release();
662    PL_hints |= HINT_BLOCK_SCOPE;
663    return KEYWORD_PLUGIN_EXPR;
664 }
665 
666 // --------------------
667 
668 struct local_bless_handler {
669    SV* var;
670    HV* old_stash;
671    I32 mg_flags;
672 
local_bless_handlerpm::perl::glue::__anon132beb6e0311::local_bless_handler673    local_bless_handler(pTHX_ SV* ref, HV* stash)
674       : var(SvRV(ref))
675    {
676       SvREFCNT_inc_simple_void_NN(var);
677       old_stash = (HV*)SvREFCNT_inc_NN(SvSTASH(var));
678       mg_flags = SvFLAGS(var) & (SVs_GMG | SVs_SMG | SVs_RMG | SVf_AMAGIC);
679       sv_bless(ref, stash);
680    }
681 
undopm::perl::glue::__anon132beb6e0311::local_bless_handler682    void undo(pTHX) const
683    {
684       HV* stash = SvSTASH(var);
685       SvSTASH_set(var, old_stash);
686       SvFLAGS(var) &= ~(SVs_GMG | SVs_SMG | SVs_RMG | SVf_AMAGIC);
687       SvFLAGS(var) |= mg_flags;
688       SvREFCNT_dec(var);
689       SvREFCNT_dec(stash);
690    }
691 };
692 
local_bless_op(pTHX)693 OP* local_bless_op(pTHX)
694 {
695    dSP;
696    HV* stash = MAXARG == 1 ? CopSTASH(PL_curcop) : gv_stashsv(POPs, GV_NOADD_NOINIT);
697    SV* sv = TOPs;
698    if (!SvROK(sv) || !SvOBJECT(SvRV(sv)))
699       DIE(aTHX_ "local bless applied to a non-object");
700    local_do<local_bless_handler>(aTHX_ sv, stash);
701    RETURN;
702 }
703 
parse_local_bless(pTHX_ OP ** op_ptr)704 int parse_local_bless(pTHX_ OP** op_ptr)
705 {
706    OP* o = parse_termexpr(0);
707    if (!o) return KEYWORD_PLUGIN_DECLINE;
708    o->op_ppaddr = local_bless_op;
709    *op_ptr = o;
710    PL_hints |= HINT_BLOCK_SCOPE;
711    return KEYWORD_PLUGIN_EXPR;
712 }
713 
714 // --------------------
715 
716 #if PerlVersion >= 5240
717 # define PmCxSaveStackIndex(cx) (cx->blk_oldsaveix)
718 #else
719 # define PmCxSaveStackIndex(cx) (PL_scopestack[cx->blk_oldscopesp-1])
720 #endif
721 
save_localizations(pTHX_ I32 & start,I32 & end)722 bool save_localizations(pTHX_ I32& start, I32& end)
723 {
724    PERL_CONTEXT* cx = cxstack + cxstack_ix;
725    assert(CxTYPE(cx) == CXt_BLOCK);
726 
727    const I32 save_start = PmCxSaveStackIndex(cx);
728    const I32 save_end = PL_savestack_ix;
729    I32 last_kept = save_end;
730    I32 tmp_save_end = -1;
731 
732    while (PL_savestack_ix > save_start) {
733       const UV save_code = PL_savestack[PL_savestack_ix-1].any_uv & SAVE_MASK;
734       int num_words = 0;
735       // all kinds of save operations generated for localizing values
736       switch (save_code) {
737       case SAVEt_GP:
738       case SAVEt_GVSV:
739       case SAVEt_GENERIC_SVREF:
740       case SAVEt_SV:
741       case SAVEt_AV:
742       case SAVEt_HV:
743       case SAVEt_ADELETE:
744          num_words = 3;
745          break;
746       case SAVEt_AELEM:
747       case SAVEt_HELEM:
748       case SAVEt_DELETE:
749 #if PerlVersion >= 5180
750       case SAVEt_GVSLOT:
751 #endif
752          num_words = 4;
753          break;
754       case SAVEt_DESTRUCTOR_X:
755          num_words = 3;
756          if (PL_savestack_ix-4 > save_start) {
757             const UV next_item = PL_savestack[PL_savestack_ix-4].any_uv;
758             if ((next_item & SAVE_MASK) == SAVEt_ALLOC)
759                num_words += 1 + int(next_item >> SAVE_TIGHT_SHIFT);
760          }
761          break;
762       }
763       if (num_words == 0) {
764          // restoring action not related to localization: execute immediately
765          // before that, rescue other actions onto mortals stack, otherwise they could be overwritten if this action triggers other localizations
766          if (tmp_save_end < 0 && last_kept != save_end) {
767             EXTEND_MORTAL(save_end - save_start);
768             PL_tmps_ix += save_end - save_start;
769             tmp_save_end = I32(PL_tmps_ix);
770             const I32 tmp_save_start = tmp_save_end - (save_end - last_kept);
771             Copy(&(PL_savestack[last_kept]), &(PL_tmps_stack[tmp_save_start]), save_end - last_kept, ANY);
772             last_kept = tmp_save_start;
773          }
774          leave_scope(PL_savestack_ix-1);
775       } else {
776          PL_savestack_ix -= num_words;
777          last_kept -= num_words;
778          if (tmp_save_end >= 0) // collecting the items on mortals stack
779             Copy(&(PL_savestack[PL_savestack_ix]), &(PL_tmps_stack[last_kept]), num_words, ANY);
780       }
781    }
782    assert(PL_savestack_ix == save_start);
783    start = last_kept;
784    if (tmp_save_end >= 0) {
785       end = tmp_save_end;
786       PL_tmps_ix -= save_end - save_start;
787       return true;
788    } else {
789       end = save_end;
790       return false;
791    }
792 }
793 
794 struct local_magic_t : MAGIC {
795    ANY locals[1];
796 };
797 
undo_saved_locals(pTHX_ SV * sv,MAGIC * mg)798 int undo_saved_locals(pTHX_ SV* sv, MAGIC* mg)
799 {
800    const local_magic_t* lmg = static_cast<local_magic_t*>(mg);
801    const I32 save_start = PL_savestack_ix;
802    const I32 num_saved = I32(lmg->mg_len);
803    SSGROW(num_saved);
804    Copy(&(lmg->locals[0]), &(PL_savestack[save_start]), num_saved, ANY);
805    PL_savestack_ix += num_saved;
806    leave_scope(save_start);
807    return 0;
808 }
809 
810 const MGVTBL local_magic_vtbl = { 0, 0, 0, 0, &undo_saved_locals };
811 
leave_local_block_op(pTHX)812 OP* leave_local_block_op(pTHX)
813 {
814    dSP;
815    dPOPss;
816    I32 save_start, save_end;
817    const bool moved_to_mortals = save_localizations(aTHX_ save_start, save_end);
818    const I32 num_saved = save_end - save_start;
819    if (num_saved > 0) {
820       if (SvTYPE(sv) == SVt_NULL) {
821          (void)SvUPGRADE(sv, SVt_PVMG);
822       } else if (SvOK(sv) || SvTYPE(sv) != SVt_PVMG) {
823          DIE(aTHX_ "local with: wrong storage value");
824       }
825       const size_t mgsz = sizeof(local_magic_t)+(num_saved-1)*sizeof(ANY);
826       char* mg_raw;
827       Newxz(mg_raw, mgsz, char);
828       local_magic_t* lmg = reinterpret_cast<local_magic_t*>(mg_raw);
829       lmg->mg_type = PERL_MAGIC_ext;
830       lmg->mg_virtual = const_cast<MGVTBL*>(&local_magic_vtbl);
831       lmg->mg_len = num_saved;
832       lmg->mg_moremagic = SvMAGIC(sv);
833       SvMAGIC_set(sv, lmg);
834       SvRMAGICAL_on(sv);
835       if (moved_to_mortals)
836          Copy(&(PL_tmps_stack[save_start]), &(lmg->locals[0]), num_saved, ANY);
837       else
838          Copy(&(PL_savestack[save_start]), &(lmg->locals[0]), num_saved, ANY);
839    }
840    PUTBACK;
841    return def_pp_LEAVE(aTHX);
842 }
843 
parse_local_block(pTHX_ OP ** op_ptr)844 int parse_local_block(pTHX_ OP** op_ptr)
845 {
846    op_keeper<OP> scope(aTHX_ parse_expression_in_parens(aTHX));
847    if (!scope) {
848       report_parse_error("expected: local with(EXPR) { BLOCK }");
849       return KEYWORD_PLUGIN_DECLINE;
850    }
851    lex_read_space(0);
852    if (PL_parser->bufptr == PL_parser->bufend || *PL_parser->bufptr != '{') {
853       report_parse_error("expected: local with(EXPR) { BLOCK }");
854       return KEYWORD_PLUGIN_DECLINE;
855    }
856    op_keeper<OP> b(aTHX_ parse_block(0));
857    if (!b || b->op_type != OP_LINESEQ)
858       return KEYWORD_PLUGIN_DECLINE;
859    OP* o = op_prepend_elem(OP_LINESEQ, newOP(OP_ENTER, 0), b.release());
860    o = op_append_elem(OP_LINESEQ, o, op_lvalue(scope.release(), OP_SASSIGN));
861    o->op_ppaddr = leave_local_block_op;
862    o->op_type = OP_CUSTOM;
863    *op_ptr = o;
864    return KEYWORD_PLUGIN_STMT;
865 }
866 
867 // --------------------
868 
leave_local_if_op(pTHX)869 OP* leave_local_if_op(pTHX)
870 {
871    I32 save_start, save_end;
872    const bool moved_to_mortals = save_localizations(aTHX_ save_start, save_end);
873    OP* ret = def_pp_LEAVE(aTHX);
874    const I32 num_words = save_end - save_start;
875    if (num_words != 0) {
876       if (moved_to_mortals)
877          Copy(&(PL_tmps_stack[save_start]), &(PL_savestack[PL_savestack_ix]), num_words, ANY);
878       PL_savestack_ix += num_words;
879    }
880    return ret;
881 }
882 
parse_local_if(pTHX_ OP ** op_ptr)883 int parse_local_if(pTHX_ OP** op_ptr)
884 {
885    PL_ppaddr[OP_LEAVE] = leave_local_if_op;
886    OP* o = parse_barestmt(0);
887    PL_ppaddr[OP_LEAVE] = def_pp_LEAVE;
888    if (!o) return KEYWORD_PLUGIN_DECLINE;
889    *op_ptr = o;
890    return KEYWORD_PLUGIN_STMT;
891 }
892 
893 // --------------------
894 
local_caller_op(pTHX)895 OP* local_caller_op(pTHX)
896 {
897    dSP;
898    dPOPss;
899    OP* op_next_state = (OP*)PL_curcop;
900    while ((op_next_state = OpSIBLING(op_next_state)) && op_next_state->op_type != OP_NEXTSTATE && op_next_state->op_type != OP_DBSTATE) ;
901    if (op_next_state) {
902       HV* src_stash = nullptr;
903       if (SvPOK(sv)) {
904          src_stash = gv_stashsv(sv, GV_ADD);
905          if (GIMME_V != G_VOID) PUSHs(sv_2mortal(newRV((SV*)src_stash)));
906       } else if (!SvROK(sv) || (src_stash = (HV*)SvRV(sv), SvTYPE(src_stash) != SVt_PVHV)) {
907          DIE(aTHX_ "invalid package specified in local caller");
908       } else if (GIMME_V != G_VOID) {
909          ++SP;
910       }
911 #if PerlVersion >= 5180 || !defined(USE_ITHREADS)
912       HV** stashp = &CopSTASH((COP*)op_next_state);
913       save_hptr(stashp);
914       *stashp = src_stash;
915 #else
916       char** stashnamep = &CopSTASHPV((COP*)op_next_state);
917       save_pptr(stashnamep);
918       *stashnamep = HvNAME(src_stash);
919       I32* stashlenp = &CopSTASH_len((COP*)op_next_state);
920       save_I32(stashlenp);
921       *stashlenp = HvNAMELEN(src_stash);
922 #endif
923    }
924    RETURN;
925 }
926 
parse_local_caller(pTHX_ OP ** op_ptr)927 int parse_local_caller(pTHX_ OP** op_ptr)
928 {
929    OP* expr = parse_termexpr(0);
930    if (!expr) return KEYWORD_PLUGIN_DECLINE;
931    OP* o = PmNewCustomOP(UNOP, 0, Perl_scalar(aTHX_ expr));
932    o->op_ppaddr = local_caller_op;
933    *op_ptr = o;
934    return KEYWORD_PLUGIN_EXPR;
935 }
936 
937 // --------------------
938 
939 struct local_saveio_handler {
940    GV* gv;
941    GV* saved;
942 
local_saveio_handlerpm::perl::glue::__anon132beb6e0311::local_saveio_handler943    local_saveio_handler(pTHX_ GV* gv_)
944       : gv((GV*)SvREFCNT_inc_simple_NN(gv_))
945    {
946       if (GvIOp(gv)) {
947          saved = (GV*)newSV(0);
948          gv_init(saved, nullptr, "__ANONIO__", 10, 0);
949          if (do_openn(saved, ">&=", 3, FALSE, 0, 0, nullptr, (SV**)&gv, 1)) {
950             do_close(gv, FALSE);
951          } else {
952             SvREFCNT_dec(saved);
953             saved = nullptr;
954          }
955       } else {
956          saved = nullptr;
957       }
958    }
959 
undopm::perl::glue::__anon132beb6e0311::local_saveio_handler960    void undo (pTHX) const
961    {
962       if (GvIOp(gv)) do_close(gv, FALSE);
963       if (saved) {
964          (void)do_openn(gv, ">&=", 3, FALSE, 0, 0, nullptr, (SV**)&saved, 1);
965          SvREFCNT_dec(saved);
966       }
967       SvREFCNT_dec(gv);
968    }
969 };
970 
local_close_op(pTHX)971 OP* local_close_op(pTHX)
972 {
973    dSP;
974    dPOPss;
975    if (SvTYPE(sv) != SVt_PVGV)
976       DIE(aTHX_ "not an IO handle in local close");
977    local_do<local_saveio_handler>(aTHX_ (GV*)sv);
978    RETURN;
979 }
980 
local_open_op(pTHX)981 OP* local_open_op(pTHX)
982 {
983    SV* sv = PL_stack_base[TOPMARK+1];
984    if (SvTYPE(sv) != SVt_PVGV)
985       DIE(aTHX_ "not an IO handle in local open");
986    local_do<local_saveio_handler>(aTHX_ (GV*)sv);
987    return def_pp_OPEN(aTHX);
988 }
989 
parse_local_open_close(pTHX_ OP ** op_ptr,OP * (* ppaddr)(pTHX))990 int parse_local_open_close(pTHX_ OP** op_ptr, OP* (*ppaddr)(pTHX))
991 {
992    OP* o = parse_termexpr(0);
993    if (!o) return KEYWORD_PLUGIN_DECLINE;
994    o->op_ppaddr = ppaddr;
995    *op_ptr = o;
996    PL_hints |= HINT_BLOCK_SCOPE;
997    return KEYWORD_PLUGIN_EXPR;
998 }
999 
1000 // --------------------
1001 
following_keyword(pTHX_ const AnyString & kw,bool skip_it=false)1002 bool following_keyword(pTHX_ const AnyString& kw, bool skip_it = false)
1003 {
1004    if (PL_parser->bufptr + kw.len < PL_parser->bufend
1005        && !strncmp(PL_parser->bufptr, kw.ptr, kw.len)
1006        && !isALNUM(PL_parser->bufptr[kw.len])) {
1007       if (skip_it)
1008          lex_read_to(PL_parser->bufptr + kw.len);
1009       return true;
1010    }
1011    return false;
1012 }
1013 
1014 #if defined(POLYMAKE_GATHER_CODE_COVERAGE)
prevent_unnecessary_scope(pTHX_ int (* parse_func)(pTHX_ OP **),OP ** op_ptr)1015 int prevent_unnecessary_scope(pTHX_ int (*parse_func)(pTHX_ OP**), OP** op_ptr)
1016 {
1017    const auto noopt = PERLDB_NOOPT;
1018    PL_perldb &= ~PERLDBf_NOOPT;
1019    const int result = parse_func(aTHX_ op_ptr);
1020    PL_perldb |= noopt;
1021    return result;
1022 }
1023 #else
prevent_unnecessary_scope(pTHX_ int (* parse_func)(pTHX_ OP **),OP ** op_ptr)1024 int prevent_unnecessary_scope(pTHX_ int (*parse_func)(pTHX_ OP**), OP** op_ptr)
1025 {
1026    return parse_func(aTHX_ op_ptr);
1027 }
1028 #endif
1029 
1030 }
1031 
parse_enhanced_local(pTHX_ OP ** op_ptr)1032 int parse_enhanced_local(pTHX_ OP** op_ptr)
1033 {
1034    lex_read_space(0);
1035    if (PL_parser->bufptr == PL_parser->bufend)
1036       return KEYWORD_PLUGIN_DECLINE;
1037    switch (*PL_parser->bufptr) {
1038    case 'b':
1039       if (following_keyword(aTHX_ "bless"))
1040          return parse_local_bless(aTHX_ op_ptr);
1041       break;
1042    case 'c':
1043       if (following_keyword(aTHX_ "caller", true))
1044          return parse_local_caller(aTHX_ op_ptr);
1045       if (following_keyword(aTHX_ "close"))
1046          return parse_local_open_close(aTHX_ op_ptr, local_close_op);
1047       break;
1048    case 'i':
1049       if (following_keyword(aTHX_ "if"))
1050          return prevent_unnecessary_scope(aTHX_ &parse_local_if, op_ptr);
1051       if (following_keyword(aTHX_ "interrupts", true))
1052          return parse_interrupts_op(aTHX_ true, op_ptr);
1053       break;
1054    case 'o':
1055       if (following_keyword(aTHX_ "open"))
1056          return parse_local_open_close(aTHX_ op_ptr, local_open_op);
1057       break;
1058    case 'p':
1059       if (following_keyword(aTHX_ "pop"))
1060          return parse_local_pop(aTHX_ op_ptr);
1061       if (following_keyword(aTHX_ "push"))
1062          return parse_local_push_unshift<false>(aTHX_ op_ptr);
1063       break;
1064    case 'r':
1065       if (following_keyword(aTHX_ "ref", true))
1066          return parse_local_ref(aTHX_ op_ptr);
1067       break;
1068    case 's':
1069       if (following_keyword(aTHX_ "scalar", true))
1070          return parse_local_scalar(aTHX_ op_ptr);
1071       if (following_keyword(aTHX_ "shift"))
1072          return parse_local_shift(aTHX_ op_ptr);
1073       if (following_keyword(aTHX_ "splice"))
1074          return parse_local_splice(aTHX_ op_ptr);
1075       if (following_keyword(aTHX_ "swap", true))
1076          return parse_local_swap(aTHX_ op_ptr);
1077       break;
1078    case 'u':
1079       if (following_keyword(aTHX_ "unshift"))
1080          return parse_local_push_unshift<true>(aTHX_ op_ptr);
1081       break;
1082    case 'w':
1083       if (following_keyword(aTHX_ "with", true))
1084          return prevent_unnecessary_scope(aTHX_ &parse_local_block, op_ptr);
1085       break;
1086    }
1087    return KEYWORD_PLUGIN_DECLINE;
1088 }
1089 
1090 }
1091 namespace ops {
1092 
init_globals(pTHX)1093 void init_globals(pTHX)
1094 {
1095    def_pp_LEAVE = PL_ppaddr[OP_LEAVE];
1096    def_pp_OPEN  = PL_ppaddr[OP_OPEN];
1097 }
1098 
1099 } } }
1100 
1101 // Local Variables:
1102 // mode:C++
1103 // c-basic-offset:3
1104 // indent-tabs-mode:nil
1105 // End:
1106