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