diff options
author | David Mitchell <davem@iabyn.com> | 2014-03-05 19:42:36 +0000 |
---|---|---|
committer | David Mitchell <davem@iabyn.com> | 2014-03-16 18:34:37 +0000 |
commit | e42737055d5d2ce05a8416a08c424c0d246ba839 (patch) | |
tree | 88ea299fbb587133fbe3296ce3459fa01915e95d /op.c | |
parent | 1022336546c7c8280b26bac6ed9c55bfd217804f (diff) | |
download | perl-e42737055d5d2ce05a8416a08c424c0d246ba839.tar.gz |
rpeep(): elide just-nulled ops
Perl_rpeep() currently spots "empty" ops like OP_NULL, OP_SCOPE
and elides them from the op_next chain (by setting oldop->op_next to point
to the op following the current op). However, if rpeep() itself
op_null()'s the current op, then when the main loop is re-entered, that
mechanism is bypassed. Modify re-entry to the loop in this case so that
the just nulled op re-processed and thus elided.
(Also document what the OP_SASSIGN/OP_SUBSTR optimisation is doing;
studying that was what originally led me to this general fix.)
Diffstat (limited to 'op.c')
-rw-r--r-- | op.c | 29 |
1 files changed, 27 insertions, 2 deletions
@@ -12074,6 +12074,23 @@ Perl_rpeep(pTHX_ OP *o) if (OP_GIMME(o,0) == G_VOID) { OP *right = cBINOP->op_first; if (right) { + /* sassign + * RIGHT + * substr + * pushmark + * arg1 + * arg2 + * ... + * becomes + * + * ex-sassign + * substr + * pushmark + * RIGHT + * arg1 + * arg2 + * ... + */ OP *left = right->op_sibling; if (left->op_type == OP_SUBSTR && (left->op_private & 7) < 4) { @@ -12099,8 +12116,16 @@ Perl_rpeep(pTHX_ OP *o) } } - oldoldop = oldop; - oldop = o; + /* did we just null the current op? If so, re-process it to handle + * eliding "empty" ops from the chain */ + if (o->op_type == OP_NULL && oldop && oldop->op_next == o) { + o->op_opt = 0; + o = oldop; + } + else { + oldoldop = oldop; + oldop = o; + } } LEAVE; } |