diff options
author | Tony Cook <tony@develop-help.com> | 2022-08-25 16:02:25 +1000 |
---|---|---|
committer | Tony Cook <tony@develop-help.com> | 2022-10-18 09:53:06 +1100 |
commit | b0bc598140d48d19b21d667a581c0dfa28a3749a (patch) | |
tree | 83af02259c23331399a9d98f0e6d5743bc342a1a | |
parent | 8d7fc27c6d9f61892eb3be8104a7d98690a46dd3 (diff) | |
download | perl-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.
-rw-r--r-- | op.c | 33 | ||||
-rw-r--r-- | t/op/lexsub.t | 13 |
2 files changed, 42 insertions, 4 deletions
@@ -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)) { diff --git a/t/op/lexsub.t b/t/op/lexsub.t index f085cd97e8..639fd29860 100644 --- a/t/op/lexsub.t +++ b/t/op/lexsub.t @@ -7,7 +7,7 @@ BEGIN { *bar::is = *is; *bar::like = *like; } -plan 150; +plan 151; # -------------------- our -------------------- # @@ -960,3 +960,14 @@ like runperl( 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]'; + +{ + # this would crash because find_lexical_cv() couldn't handle an + # intermediate scope which didn't include the sub + no warnings 'experimental::builtin'; + use builtin 'ceil'; + sub nested { + ok(eval 'ceil(1.5)', "no assertion failure calling a lexical sub from nested eval"); + } + nested(); +} |