summaryrefslogtreecommitdiff
path: root/op.c
diff options
context:
space:
mode:
authorTony Cook <tony@develop-help.com>2022-08-25 16:02:25 +1000
committerTony Cook <tony@develop-help.com>2022-10-18 09:53:06 +1100
commitb0bc598140d48d19b21d667a581c0dfa28a3749a (patch)
tree83af02259c23331399a9d98f0e6d5743bc342a1a /op.c
parent8d7fc27c6d9f61892eb3be8104a7d98690a46dd3 (diff)
downloadperl-b0bc598140d48d19b21d667a581c0dfa28a3749a.tar.gz
handle intermediate pads not including the name in find_lexical_cv()
In the following each line is the pad for a scope, starting from the outermost scope. In non-eval cases a use of a lexical symbol in an inner pad also adds that name to the pad in the intermediate scope. So for code like: my $bar; sub foo { ... } sub f { sub g { foo($bar) } } we might get pads like: [&foo] [$bar] # defining pad [&foo] [$bar] # intermediate pad (for f) [&foo] [$bar] # using pad (for g) and each inner pad has an index of the same name in the parent scope. find_lexical_cv() followed that chain of indexes to find the definition, or at least the prototype, of the lexical sub. Unfortunately names can only be added to the pad at compile-time, so for an eval the intermediate scope is closed to further modifications, and the pad for the intermediate scope doesn't get an entry for the name, so code like: my $bar; sub foo { ... } sub f { eval 'foo($bar)' } we get pads like: [&foo] [$bar] # defining pad # intermediate pad (f) doesn't have the names [&foo] [$bar] # eval 'foo($bar)' pad but find_lexical_cv() assumed that the names would always exist in the intermediate pads, and failed an assertion. find_lexical_cv() now falls back to searching for the cv by name up the chain of nested pads. This doesn't fix a slightly related problem: my sub foo { } BEGIN { foo(); # Undefined subroutine &foo } The fix for that may make find_lexical_cv() unnecessary, but is a more complex fix.
Diffstat (limited to 'op.c')
-rw-r--r--op.c33
1 files changed, 30 insertions, 3 deletions
diff --git a/op.c b/op.c
index e6b5842e7f..f67d38cf68 100644
--- a/op.c
+++ b/op.c
@@ -13545,13 +13545,40 @@ subroutine.
CV *
Perl_find_lexical_cv(pTHX_ PADOFFSET off)
{
- PADNAME *name = PAD_COMPNAME(off);
+ const PADNAME *name = PAD_COMPNAME(off);
CV *compcv = PL_compcv;
while (PadnameOUTER(name)) {
- assert(PARENT_PAD_INDEX(name));
compcv = CvOUTSIDE(compcv);
- name = PadlistNAMESARRAY(CvPADLIST(compcv))
+ if (LIKELY(PARENT_PAD_INDEX(name))) {
+ name = PadlistNAMESARRAY(CvPADLIST(compcv))
[off = PARENT_PAD_INDEX(name)];
+ }
+ else {
+ /* In an eval() in an inner scope like a function, the
+ intermediate pad in the sub might not be populated with the
+ sub. So search harder.
+
+ It is possible we won't find the name in this
+ particular scope, but that's fine, if we don't we'll
+ find it in some outer scope. Finding it here will let us
+ go back to following the PARENT_PAD_INDEX() chain.
+ */
+ const PADNAMELIST * const names = PadlistNAMES(CvPADLIST(compcv));
+ PADNAME * const * const name_p = PadnamelistARRAY(names);
+ int offset;
+ for (offset = PadnamelistMAXNAMED(names); offset > 0; offset--) {
+ const PADNAME * const thisname = name_p[offset];
+ /* The pv is copied from the outer PADNAME to the
+ inner PADNAMEs so we don't need to compare the
+ string contents
+ */
+ if (thisname && PadnameLEN(thisname) == PadnameLEN(name)
+ && PadnamePV(thisname) == PadnamePV(name)) {
+ name = thisname;
+ break;
+ }
+ }
+ }
}
assert(!PadnameIsOUR(name));
if (!PadnameIsSTATE(name) && PadnamePROTOCV(name)) {