rewrite to generate actual ops, not source code
[p5sagit/Function-Parameters.git] / padop_on_crack.c.inc
diff --git a/padop_on_crack.c.inc b/padop_on_crack.c.inc
new file mode 100644 (file)
index 0000000..f40ded3
--- /dev/null
@@ -0,0 +1,627 @@
+/*
+ * This code was copied from perl/pad.c and perl/op.c and subsequently
+ * butchered by Lukas Mai (2012).
+ */
+/* vi: set ft=c inde=: */
+
+#define COP_SEQ_RANGE_LOW_set(SV, VAL) \
+       do { ((XPVNV*)SvANY(SV))->xnv_u.xpad_cop_seq.xlow = (VAL); } while (0)
+#define COP_SEQ_RANGE_HIGH_set(SV, VAL) \
+       do { ((XPVNV*)SvANY(SV))->xnv_u.xpad_cop_seq.xhigh = (VAL); } while (0)
+
+static void S_pad_block_start(pTHX_ int full) {
+       dVAR;
+       ASSERT_CURPAD_ACTIVE("pad_block_start");
+       SAVEI32(PL_comppad_name_floor);
+       PL_comppad_name_floor = AvFILLp(PL_comppad_name);
+       if (full)
+               PL_comppad_name_fill = PL_comppad_name_floor;
+       if (PL_comppad_name_floor < 0)
+               PL_comppad_name_floor = 0;
+       SAVEI32(PL_min_intro_pending);
+       SAVEI32(PL_max_intro_pending);
+       PL_min_intro_pending = 0;
+       SAVEI32(PL_comppad_name_fill);
+       SAVEI32(PL_padix_floor);
+       PL_padix_floor = PL_padix;
+       PL_pad_reset_pending = FALSE;
+}
+
+static int S_block_start(pTHX_ int full) {
+       dVAR;
+       const int retval = PL_savestack_ix;
+
+       S_pad_block_start(full);
+       SAVEHINTS();
+       PL_hints &= ~HINT_BLOCK_SCOPE;
+       SAVECOMPILEWARNINGS();
+       PL_compiling.cop_warnings = DUP_WARNINGS(PL_compiling.cop_warnings);
+
+       CALL_BLOCK_HOOKS(bhk_start, full);
+
+       return retval;
+}
+
+/* Check for in place reverse and sort assignments like "@a = reverse @a"
+   and modify the optree to make them work inplace */
+
+static void S_inplace_aassign(pTHX_ OP *o) {
+       OP *modop, *modop_pushmark;
+       OP *oright;
+       OP *oleft, *oleft_pushmark;
+
+       assert((o->op_flags & OPf_WANT) == OPf_WANT_VOID);
+
+       assert(cUNOPo->op_first->op_type == OP_NULL);
+       modop_pushmark = cUNOPx(cUNOPo->op_first)->op_first;
+       assert(modop_pushmark->op_type == OP_PUSHMARK);
+       modop = modop_pushmark->op_sibling;
+
+       if (modop->op_type != OP_SORT && modop->op_type != OP_REVERSE)
+               return;
+
+       /* no other operation except sort/reverse */
+       if (modop->op_sibling)
+               return;
+
+       assert(cUNOPx(modop)->op_first->op_type == OP_PUSHMARK);
+       if (!(oright = cUNOPx(modop)->op_first->op_sibling)) return;
+
+       if (modop->op_flags & OPf_STACKED) {
+               /* skip sort subroutine/block */
+               assert(oright->op_type == OP_NULL);
+               oright = oright->op_sibling;
+       }
+
+       assert(cUNOPo->op_first->op_sibling->op_type == OP_NULL);
+       oleft_pushmark = cUNOPx(cUNOPo->op_first->op_sibling)->op_first;
+       assert(oleft_pushmark->op_type == OP_PUSHMARK);
+       oleft = oleft_pushmark->op_sibling;
+
+       /* Check the lhs is an array */
+       if (!oleft ||
+               (oleft->op_type != OP_RV2AV && oleft->op_type != OP_PADAV)
+               || oleft->op_sibling
+               || (oleft->op_private & OPpLVAL_INTRO)
+       )
+               return;
+
+       /* Only one thing on the rhs */
+       if (oright->op_sibling)
+               return;
+
+       /* check the array is the same on both sides */
+       if (oleft->op_type == OP_RV2AV) {
+               if (oright->op_type != OP_RV2AV
+                       || !cUNOPx(oright)->op_first
+                       || cUNOPx(oright)->op_first->op_type != OP_GV
+                       || cUNOPx(oleft )->op_first->op_type != OP_GV
+                       || cGVOPx_gv(cUNOPx(oleft)->op_first) !=
+                       cGVOPx_gv(cUNOPx(oright)->op_first)
+               )
+                       return;
+       }
+       else if (oright->op_type != OP_PADAV
+                        || oright->op_targ != oleft->op_targ
+       )
+               return;
+
+       /* This actually is an inplace assignment */
+
+       modop->op_private |= OPpSORT_INPLACE;
+
+       /* transfer MODishness etc from LHS arg to RHS arg */
+       oright->op_flags = oleft->op_flags;
+
+       /* remove the aassign op and the lhs */
+       op_null(o);
+       op_null(oleft_pushmark);
+       if (oleft->op_type == OP_RV2AV && cUNOPx(oleft)->op_first)
+               op_null(cUNOPx(oleft)->op_first);
+       op_null(oleft);
+}
+
+static OP *S_scalarvoid(pTHX_ OP *);
+
+static OP *S_scalar(pTHX_ OP *o) {
+       dVAR;
+       OP *kid;
+
+       /* assumes no premature commitment */
+       if (!o || (PL_parser && PL_parser->error_count)
+               || (o->op_flags & OPf_WANT)
+               || o->op_type == OP_RETURN)
+       {
+               return o;
+       }
+
+       o->op_flags = (o->op_flags & ~OPf_WANT) | OPf_WANT_SCALAR;
+
+       switch (o->op_type) {
+               case OP_REPEAT:
+                       S_scalar(cBINOPo->op_first);
+                       break;
+               case OP_OR:
+               case OP_AND:
+               case OP_COND_EXPR:
+                       for (kid = cUNOPo->op_first->op_sibling; kid; kid = kid->op_sibling)
+                               S_scalar(kid);
+                       break;
+                       /* FALL THROUGH */
+               case OP_SPLIT:
+               case OP_MATCH:
+               case OP_QR:
+               case OP_SUBST:
+               case OP_NULL:
+               default:
+                       if (o->op_flags & OPf_KIDS) {
+                               for (kid = cUNOPo->op_first; kid; kid = kid->op_sibling)
+                                       S_scalar(kid);
+                       }
+                       break;
+               case OP_LEAVE:
+               case OP_LEAVETRY:
+                       kid = cLISTOPo->op_first;
+                       S_scalar(kid);
+                       kid = kid->op_sibling;
+do_kids:
+                       while (kid) {
+                               OP *sib = kid->op_sibling;
+                               if (sib && kid->op_type != OP_LEAVEWHEN)
+                                       S_scalarvoid(kid);
+                               else
+                                       S_scalar(kid);
+                               kid = sib;
+                       }
+                       PL_curcop = &PL_compiling;
+                       break;
+               case OP_SCOPE:
+               case OP_LINESEQ:
+               case OP_LIST:
+                       kid = cLISTOPo->op_first;
+                       goto do_kids;
+               case OP_SORT:
+                       Perl_ck_warner(aTHX_ packWARN(WARN_VOID), "Useless use of sort in scalar context");
+                       break;
+       }
+       return o;
+}
+
+static OP *S_scalarkids(pTHX_ OP *o) {
+    if (o && o->op_flags & OPf_KIDS) {
+        OP *kid;
+       for (kid = cLISTOPo->op_first; kid; kid = kid->op_sibling)
+           S_scalar(kid);
+    }
+    return o;
+}
+
+static OP *S_scalarvoid(pTHX_ OP *o) {
+       dVAR;
+       OP *kid;
+       const char *useless = NULL;
+       U32 useless_is_utf8 = 0;
+       SV *sv;
+       U8 want;
+
+       PERL_ARGS_ASSERT_SCALARVOID;
+
+       if (
+               o->op_type == OP_NEXTSTATE ||
+               o->op_type == OP_DBSTATE || (
+                       o->op_type == OP_NULL && (
+                               o->op_targ == OP_NEXTSTATE ||
+                               o->op_targ == OP_DBSTATE
+                       )
+               )
+       ) {
+               PL_curcop = (COP*)o;            /* for warning below */
+       }
+
+       /* assumes no premature commitment */
+       want = o->op_flags & OPf_WANT;
+       if (
+               (want && want != OPf_WANT_SCALAR) ||
+               (PL_parser && PL_parser->error_count) ||
+               o->op_type == OP_RETURN ||
+               o->op_type == OP_REQUIRE ||
+               o->op_type == OP_LEAVEWHEN
+       ) {
+               return o;
+       }
+
+       if (
+               (o->op_private & OPpTARGET_MY) &&
+               (PL_opargs[o->op_type] & OA_TARGLEX)
+               /* OPp share the meaning */
+       ) {
+               return S_scalar(o);                     /* As if inside SASSIGN */
+       }
+
+       o->op_flags = (o->op_flags & ~OPf_WANT) | OPf_WANT_VOID;
+
+       switch (o->op_type) {
+               default:
+                       if (!(PL_opargs[o->op_type] & OA_FOLDCONST))
+                               break;
+                       /* FALL THROUGH */
+               case OP_REPEAT:
+                       if (o->op_flags & OPf_STACKED)
+                               break;
+                       goto func_ops;
+               case OP_SUBSTR:
+                       if (o->op_private == 4)
+                               break;
+                       /* FALL THROUGH */
+               case OP_GVSV:
+               case OP_WANTARRAY:
+               case OP_GV:
+               case OP_SMARTMATCH:
+               case OP_PADSV:
+               case OP_PADAV:
+               case OP_PADHV:
+               case OP_PADANY:
+               case OP_AV2ARYLEN:
+               case OP_REF:
+               case OP_REFGEN:
+               case OP_SREFGEN:
+               case OP_DEFINED:
+               case OP_HEX:
+               case OP_OCT:
+               case OP_LENGTH:
+               case OP_VEC:
+               case OP_INDEX:
+               case OP_RINDEX:
+               case OP_SPRINTF:
+               case OP_AELEM:
+               case OP_AELEMFAST:
+               case OP_AELEMFAST_LEX:
+               case OP_ASLICE:
+               case OP_HELEM:
+               case OP_HSLICE:
+               case OP_UNPACK:
+               case OP_PACK:
+               case OP_JOIN:
+               case OP_LSLICE:
+               case OP_ANONLIST:
+               case OP_ANONHASH:
+               case OP_SORT:
+               case OP_REVERSE:
+               case OP_RANGE:
+               case OP_FLIP:
+               case OP_FLOP:
+               case OP_CALLER:
+               case OP_FILENO:
+               case OP_EOF:
+               case OP_TELL:
+               case OP_GETSOCKNAME:
+               case OP_GETPEERNAME:
+               case OP_READLINK:
+               case OP_TELLDIR:
+               case OP_GETPPID:
+               case OP_GETPGRP:
+               case OP_GETPRIORITY:
+               case OP_TIME:
+               case OP_TMS:
+               case OP_LOCALTIME:
+               case OP_GMTIME:
+               case OP_GHBYNAME:
+               case OP_GHBYADDR:
+               case OP_GHOSTENT:
+               case OP_GNBYNAME:
+               case OP_GNBYADDR:
+               case OP_GNETENT:
+               case OP_GPBYNAME:
+               case OP_GPBYNUMBER:
+               case OP_GPROTOENT:
+               case OP_GSBYNAME:
+               case OP_GSBYPORT:
+               case OP_GSERVENT:
+               case OP_GPWNAM:
+               case OP_GPWUID:
+               case OP_GGRNAM:
+               case OP_GGRGID:
+               case OP_GETLOGIN:
+               case OP_PROTOTYPE:
+               case OP_RUNCV:
+func_ops:
+                       if (!(o->op_private & (OPpLVAL_INTRO|OPpOUR_INTRO)))
+                               /* Otherwise it's "Useless use of grep iterator" */
+                               useless = OP_DESC(o);
+                       break;
+
+               case OP_SPLIT:
+                       kid = cLISTOPo->op_first;
+                       if (kid && kid->op_type == OP_PUSHRE
+#ifdef USE_ITHREADS
+                               && !((PMOP*)kid)->op_pmreplrootu.op_pmtargetoff)
+#else
+                               && !((PMOP*)kid)->op_pmreplrootu.op_pmtargetgv)
+#endif
+                                       useless = OP_DESC(o);
+                       break;
+
+               case OP_NOT:
+                       kid = cUNOPo->op_first;
+                       if (kid->op_type != OP_MATCH && kid->op_type != OP_SUBST &&
+                               kid->op_type != OP_TRANS && kid->op_type != OP_TRANSR) {
+                               goto func_ops;
+                       }
+                       useless = "negative pattern binding (!~)";
+                       break;
+
+               case OP_SUBST:
+                       if (cPMOPo->op_pmflags & PMf_NONDESTRUCT)
+                               useless = "non-destructive substitution (s///r)";
+                       break;
+
+               case OP_TRANSR:
+                       useless = "non-destructive transliteration (tr///r)";
+                       break;
+
+               case OP_RV2GV:
+               case OP_RV2SV:
+               case OP_RV2AV:
+               case OP_RV2HV:
+                       if (!(o->op_private & (OPpLVAL_INTRO|OPpOUR_INTRO)) &&
+                               (!o->op_sibling || o->op_sibling->op_type != OP_READLINE))
+                               useless = "a variable";
+                       break;
+
+               case OP_CONST:
+                       sv = cSVOPo_sv;
+                       if (cSVOPo->op_private & OPpCONST_STRICT) {
+                               //no_bareword_allowed(o);
+                               *((int *)NULL) += 1;
+                       } else {
+                               if (ckWARN(WARN_VOID)) {
+                                       /* don't warn on optimised away booleans, eg 
+                                        * use constant Foo, 5; Foo || print; */
+                                       if (cSVOPo->op_private & OPpCONST_SHORTCIRCUIT)
+                                               useless = NULL;
+                                       /* the constants 0 and 1 are permitted as they are
+                                          conventionally used as dummies in constructs like
+                                          1 while some_condition_with_side_effects;  */
+                                       else if (SvNIOK(sv) && (SvNV(sv) == 0.0 || SvNV(sv) == 1.0))
+                                               useless = NULL;
+                                       else if (SvPOK(sv)) {
+                                               /* perl4's way of mixing documentation and code
+                                                  (before the invention of POD) was based on a
+                                                  trick to mix nroff and perl code. The trick was
+                                                  built upon these three nroff macros being used in
+                                                  void context. The pink camel has the details in
+                                                  the script wrapman near page 319. */
+                                               const char * const maybe_macro = SvPVX_const(sv);
+                                               if (strnEQ(maybe_macro, "di", 2) ||
+                                                       strnEQ(maybe_macro, "ds", 2) ||
+                                                       strnEQ(maybe_macro, "ig", 2))
+                                                       useless = NULL;
+                                               else {
+                                                       SV * const dsv = newSVpvs("");
+                                                       SV* msv = sv_2mortal(Perl_newSVpvf(aTHX_
+                                                                                                                          "a constant (%s)",
+                                                                                                                          pv_pretty(dsv, maybe_macro, SvCUR(sv), 32, NULL, NULL,
+                                                                                                                                                PERL_PV_PRETTY_DUMP | PERL_PV_ESCAPE_NOCLEAR | PERL_PV_ESCAPE_UNI_DETECT )));
+                                                       SvREFCNT_dec(dsv);
+                                                       useless = SvPV_nolen(msv);
+                                                       useless_is_utf8 = SvUTF8(msv);
+                                               }
+                                       }
+                                       else if (SvOK(sv)) {
+                                               SV* msv = sv_2mortal(Perl_newSVpvf(aTHX_
+                                                                                                                  "a constant (%"SVf")", sv));
+                                               useless = SvPV_nolen(msv);
+                                       }
+                                       else
+                                               useless = "a constant (undef)";
+                               }
+                       }
+                       op_null(o);             /* don't execute or even remember it */
+                       break;
+
+               case OP_POSTINC:
+                       o->op_type = OP_PREINC;         /* pre-increment is faster */
+                       o->op_ppaddr = PL_ppaddr[OP_PREINC];
+                       break;
+
+               case OP_POSTDEC:
+                       o->op_type = OP_PREDEC;         /* pre-decrement is faster */
+                       o->op_ppaddr = PL_ppaddr[OP_PREDEC];
+                       break;
+
+               case OP_I_POSTINC:
+                       o->op_type = OP_I_PREINC;       /* pre-increment is faster */
+                       o->op_ppaddr = PL_ppaddr[OP_I_PREINC];
+                       break;
+
+               case OP_I_POSTDEC:
+                       o->op_type = OP_I_PREDEC;       /* pre-decrement is faster */
+                       o->op_ppaddr = PL_ppaddr[OP_I_PREDEC];
+                       break;
+
+               case OP_SASSIGN: {
+                       OP *rv2gv;
+                       UNOP *refgen, *rv2cv;
+                       LISTOP *exlist;
+
+                       if ((o->op_private & ~OPpASSIGN_BACKWARDS) != 2)
+                               break;
+
+                       rv2gv = ((BINOP *)o)->op_last;
+                       if (!rv2gv || rv2gv->op_type != OP_RV2GV)
+                               break;
+
+                       refgen = (UNOP *)((BINOP *)o)->op_first;
+
+                       if (!refgen || refgen->op_type != OP_REFGEN)
+                               break;
+
+                       exlist = (LISTOP *)refgen->op_first;
+                       if (!exlist || exlist->op_type != OP_NULL
+                               || exlist->op_targ != OP_LIST)
+                               break;
+
+                       if (exlist->op_first->op_type != OP_PUSHMARK)
+                               break;
+
+                       rv2cv = (UNOP*)exlist->op_last;
+
+                       if (rv2cv->op_type != OP_RV2CV)
+                               break;
+
+                       assert ((rv2gv->op_private & OPpDONT_INIT_GV) == 0);
+                       assert ((o->op_private & OPpASSIGN_CV_TO_GV) == 0);
+                       assert ((rv2cv->op_private & OPpMAY_RETURN_CONSTANT) == 0);
+
+                       o->op_private |= OPpASSIGN_CV_TO_GV;
+                       rv2gv->op_private |= OPpDONT_INIT_GV;
+                       rv2cv->op_private |= OPpMAY_RETURN_CONSTANT;
+
+                       break;
+               }
+
+               case OP_AASSIGN: {
+                       S_inplace_aassign(o);
+                       break;
+               }
+
+               case OP_OR:
+               case OP_AND:
+                       kid = cLOGOPo->op_first;
+                       if (kid->op_type == OP_NOT
+                               && (kid->op_flags & OPf_KIDS)
+                               && !PL_madskills) {
+                               if (o->op_type == OP_AND) {
+                                       o->op_type = OP_OR;
+                                       o->op_ppaddr = PL_ppaddr[OP_OR];
+                               } else {
+                                       o->op_type = OP_AND;
+                                       o->op_ppaddr = PL_ppaddr[OP_AND];
+                               }
+                               op_null(kid);
+                       }
+
+               case OP_DOR:
+               case OP_COND_EXPR:
+               case OP_ENTERGIVEN:
+               case OP_ENTERWHEN:
+                       for (kid = cUNOPo->op_first->op_sibling; kid; kid = kid->op_sibling)
+                               S_scalarvoid(kid);
+                       break;
+
+               case OP_NULL:
+                       if (o->op_flags & OPf_STACKED)
+                               break;
+                       /* FALL THROUGH */
+               case OP_NEXTSTATE:
+               case OP_DBSTATE:
+               case OP_ENTERTRY:
+               case OP_ENTER:
+                       if (!(o->op_flags & OPf_KIDS))
+                               break;
+                       /* FALL THROUGH */
+               case OP_SCOPE:
+               case OP_LEAVE:
+               case OP_LEAVETRY:
+               case OP_LEAVELOOP:
+               case OP_LINESEQ:
+               case OP_LIST:
+               case OP_LEAVEGIVEN:
+               case OP_LEAVEWHEN:
+                       for (kid = cLISTOPo->op_first; kid; kid = kid->op_sibling)
+                               S_scalarvoid(kid);
+                       break;
+               case OP_ENTEREVAL:
+                       S_scalarkids(o);
+                       break;
+               case OP_SCALAR:
+                       return S_scalar(o);
+       }
+       if (useless)
+               Perl_ck_warner(aTHX_ packWARN(WARN_VOID), "Useless use of %"SVf" in void context",
+                                          newSVpvn_flags(useless, strlen(useless),
+                                                                         SVs_TEMP | ( useless_is_utf8 ? SVf_UTF8 : 0 )));
+       return o;
+}
+
+static OP *S_scalarseq(pTHX_ OP *o) {
+       dVAR;
+       if (o) {
+               const OPCODE type = o->op_type;
+
+               if (type == OP_LINESEQ || type == OP_SCOPE ||
+                   type == OP_LEAVE || type == OP_LEAVETRY)
+               {
+                       OP *kid;
+                       for (kid = cLISTOPo->op_first; kid; kid = kid->op_sibling) {
+                               if (kid->op_sibling) {
+                                       S_scalarvoid(kid);
+                               }
+                       }
+                       PL_curcop = &PL_compiling;
+               }
+               o->op_flags &= ~OPf_PARENS;
+               if (PL_hints & HINT_BLOCK_SCOPE)
+                       o->op_flags |= OPf_PARENS;
+       }
+       else
+               o = newOP(OP_STUB, 0);
+       return o;
+}
+
+static void S_pad_leavemy(pTHX) {
+       dVAR;
+       I32 off;
+       SV * const * const svp = AvARRAY(PL_comppad_name);
+
+       PL_pad_reset_pending = FALSE;
+
+       ASSERT_CURPAD_ACTIVE("pad_leavemy");
+       if (PL_min_intro_pending && PL_comppad_name_fill < PL_min_intro_pending) {
+               for (off = PL_max_intro_pending; off >= PL_min_intro_pending; off--) {
+                       const SV * const sv = svp[off];
+                       if (sv && sv != &PL_sv_undef && !SvFAKE(sv))
+                               Perl_ck_warner_d(aTHX_ packWARN(WARN_INTERNAL),
+                                                                "%"SVf" never introduced",
+                                                                SVfARG(sv));
+               }
+       }
+       /* "Deintroduce" my variables that are leaving with this scope. */
+       for (off = AvFILLp(PL_comppad_name); off > PL_comppad_name_fill; off--) {
+               const SV * const sv = svp[off];
+               if (sv && sv != &PL_sv_undef && !SvFAKE(sv)
+                       && COP_SEQ_RANGE_HIGH(sv) == PERL_PADSEQ_INTRO)
+               {
+                       COP_SEQ_RANGE_HIGH_set(sv, PL_cop_seqmax);
+                       DEBUG_Xv(PerlIO_printf(Perl_debug_log,
+                                                                  "Pad leavemy: %ld \"%s\", (%lu,%lu)\n",
+                                                                  (long)off, SvPVX_const(sv),
+                                                                  (unsigned long)COP_SEQ_RANGE_LOW(sv),
+                                                                  (unsigned long)COP_SEQ_RANGE_HIGH(sv))
+                       );
+               }
+       }
+       PL_cop_seqmax++;
+       if (PL_cop_seqmax == PERL_PADSEQ_INTRO) /* not a legal value */
+               PL_cop_seqmax++;
+       DEBUG_Xv(PerlIO_printf(Perl_debug_log,
+                                                  "Pad leavemy: seq = %ld\n", (long)PL_cop_seqmax));
+}
+
+static OP *S_block_end(pTHX_ I32 floor, OP *seq) {
+       dVAR;
+       const int needblockscope = PL_hints & HINT_BLOCK_SCOPE;
+       OP *retval = S_scalarseq(seq);
+
+       CALL_BLOCK_HOOKS(bhk_pre_end, &retval);
+
+       LEAVE_SCOPE(floor);
+       CopHINTS_set(&PL_compiling, PL_hints);
+       if (needblockscope)
+               PL_hints |= HINT_BLOCK_SCOPE; /* propagate out */
+       S_pad_leavemy();
+
+       CALL_BLOCK_HOOKS(bhk_post_end, &retval);
+
+       return retval;
+}