This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
(perl #108276) remove recursion from optimize_op()
authorTony Cook <tony@develop-help.com>
Tue, 29 Jan 2019 05:29:04 +0000 (16:29 +1100)
committerTony Cook <tony@develop-help.com>
Mon, 4 Feb 2019 23:02:26 +0000 (10:02 +1100)
The prevented code like:

  ./miniperl -e 'my $line = "\$cond ? \$a : \n"; my $code = ($line x 100000) . "\$b;\n"; eval $code;'

from crashing due to stack overflow.

It does however take a long time to compile.

Because it doesn't strictly recurse through the op tree (due
to OP_SUBST), I couldn't use traverse_op_tree().

I considered wrapping a traverse_op_tree() loop inside a defer op
loop, so OP_SUBST would defer its op, but processing order is
somewhat important from setting PL_curcop.

This also processes the child ops in reverse order, I'm not sure if
that's a real problem (no tests failed), but the next commit fixes
that order.

op.c

diff --git a/op.c b/op.c
index 8ed48b2..0db0128 100644 (file)
--- a/op.c
+++ b/op.c
@@ -3459,9 +3459,10 @@ Perl_optimize_optree(pTHX_ OP* o)
 STATIC void
 S_optimize_op(pTHX_ OP* o)
 {
 STATIC void
 S_optimize_op(pTHX_ OP* o)
 {
-    OP *kid;
+    dDEFER_OP;
 
     PERL_ARGS_ASSERT_OPTIMIZE_OP;
 
     PERL_ARGS_ASSERT_OPTIMIZE_OP;
+    do {
     assert(o->op_type != OP_FREED);
 
     switch (o->op_type) {
     assert(o->op_type != OP_FREED);
 
     switch (o->op_type) {
@@ -3480,18 +3481,21 @@ S_optimize_op(pTHX_ OP* o)
 
     case OP_SUBST:
        if (cPMOPo->op_pmreplrootu.op_pmreplroot)
 
     case OP_SUBST:
        if (cPMOPo->op_pmreplrootu.op_pmreplroot)
-           optimize_op(cPMOPo->op_pmreplrootu.op_pmreplroot);
+           DEFER_OP(cPMOPo->op_pmreplrootu.op_pmreplroot);
        break;
 
     default:
        break;
     }
 
        break;
 
     default:
        break;
     }
 
-    if (!(o->op_flags & OPf_KIDS))
-        return;
+    if (o->op_flags & OPf_KIDS) {
+        OP *kid;
+        for (kid = cUNOPo->op_first; kid; kid = OpSIBLING(kid))
+            DEFER_OP(kid);
+    }
+    } while ( ( o = POP_DEFERRED_OP() ) );
 
 
-    for (kid = cUNOPo->op_first; kid; kid = OpSIBLING(kid))
-        optimize_op(kid);
+    DEFER_OP_CLEANUP;
 }
 
 
 }