b8c914
From 695d6585affc8f13711f013329fb4810ab89d833 Mon Sep 17 00:00:00 2001
b8c914
From: Father Chrysostomos <sprout@cpan.org>
b8c914
Date: Tue, 14 Nov 2017 18:55:55 -0800
b8c914
Subject: [PATCH] [perl #132442] Fix stack with do {my sub l; 1}
b8c914
MIME-Version: 1.0
b8c914
Content-Type: text/plain; charset=UTF-8
b8c914
Content-Transfer-Encoding: 8bit
b8c914
b8c914
A block in perl usually compiles to a leave op with an enter inside
b8c914
it, followed by the statements:
b8c914
b8c914
   leave
b8c914
     enter
b8c914
     nextstate
b8c914
     ... expr ...
b8c914
     nextstate
b8c914
     ... expr ...
b8c914
b8c914
If a block contains only one statement, and that statement is suffic-
b8c914
iently innocuous, then the enter/leave pair to create the scope at run
b8c914
time get skipped, and instead we have a simple scope op which is not
b8c914
even executed:
b8c914
b8c914
   scope
b8c914
     ex-nextstate
b8c914
     ... expr ...
b8c914
b8c914
The nextstate in this case also gets nulled.
b8c914
b8c914
In the case of do { my sub l; 1 } we were getting a variation of the
b8c914
latter, that looked like this:
b8c914
b8c914
   scope
b8c914
     introcv
b8c914
     clonecv
b8c914
     nextstate
b8c914
     ... expr ...
b8c914
b8c914
The problem here is that nextstate resets the stack, even though a new
b8c914
scope has not been pushed, so we end up with all existing stack items
b8c914
from the *outer* scope getting clobbered.
b8c914
b8c914
One can have fun with this and erase everything pushed on to the stack
b8c914
so far in a given statement:
b8c914
b8c914
$ ./perl -le 'print join "-", 1..10, do {my sub l; ","}, 11..20'
b8c914
11,12,13,14,15,16,17,18,19,20
b8c914
b8c914
Here I replaced the first argument to join() from within the do{}
b8c914
block, after having cleared the stack.
b8c914
b8c914
Why was the op tree was getting muddled up like this?  The ‘my sub’
b8c914
declaration does not immediately add any ops to the op tree; those ops
b8c914
get added when the current scope finishing compiling, since those ops
b8c914
must be inserted at the beginning of the block.
b8c914
b8c914
I have not fully looked into the order that things happen, and why the
b8c914
nextstate op does not get nulled; but it did not matter, because of
b8c914
the simple fix: Treat lexical sub declarations as ‘not innocuous’ by
b8c914
setting the HINT_BLOCK_SCOPE flag when a lexical sub is declared.
b8c914
Thus, we end up with an enter/leave pair, which creates a
b8c914
proper scope.
b8c914
b8c914
Signed-off-by: Petr Písař <ppisar@redhat.com>
b8c914
---
b8c914
 op.c          | 2 ++
b8c914
 t/op/lexsub.t | 5 ++++-
b8c914
 2 files changed, 6 insertions(+), 1 deletion(-)
b8c914
b8c914
diff --git a/op.c b/op.c
b8c914
index 8fa5aad876..c617ad2a00 100644
b8c914
--- a/op.c
b8c914
+++ b/op.c
b8c914
@@ -9243,6 +9243,8 @@ Perl_newMYSUB(pTHX_ I32 floor, OP *o, OP *proto, OP *attrs, OP *block)
b8c914
 
b8c914
     PERL_ARGS_ASSERT_NEWMYSUB;
b8c914
 
b8c914
+    PL_hints |= HINT_BLOCK_SCOPE;
b8c914
+
b8c914
     /* Find the pad slot for storing the new sub.
b8c914
        We cannot use PL_comppad, as it is the pad owned by the new sub.  We
b8c914
        need to look in CvOUTSIDE and find the pad belonging to the enclos-
b8c914
diff --git a/t/op/lexsub.t b/t/op/lexsub.t
b8c914
index 3fa17acdda..f085cd97e8 100644
b8c914
--- a/t/op/lexsub.t
b8c914
+++ b/t/op/lexsub.t
b8c914
@@ -7,7 +7,7 @@ BEGIN {
b8c914
     *bar::is = *is;
b8c914
     *bar::like = *like;
b8c914
 }
b8c914
-plan 149;
b8c914
+plan 150;
b8c914
 
b8c914
 # -------------------- our -------------------- #
b8c914
 
b8c914
@@ -957,3 +957,6 @@ like runperl(
b8c914
 {
b8c914
   my sub h; sub{my $x; sub{h}}
b8c914
 }
b8c914
+
b8c914
+is join("-", qw(aa bb), do { my sub lleexx; 123 }, qw(cc dd)),
b8c914
+  "aa-bb-123-cc-dd", 'do { my sub...} in a list [perl #132442]';
b8c914
-- 
b8c914
2.13.6
b8c914