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