summaryrefslogtreecommitdiff
path: root/mro.c
diff options
context:
space:
mode:
authorFather Chrysostomos <sprout@cpan.org>2010-10-30 18:19:56 -0700
committerFather Chrysostomos <sprout@cpan.org>2010-10-30 18:19:56 -0700
commit8b9e80a3f70002d0c2fb6549a8fd594f358b9525 (patch)
tree651ff37ff4aaa075555a6ab025b41229622874c3 /mro.c
parentd7fbb1de45aa77d305eccd94d8c07d7a1491fc45 (diff)
downloadperl-8b9e80a3f70002d0c2fb6549a8fd594f358b9525.tar.gz
[perl #77358] ISA warnings after aliasing packages
This commit makes mro_package_moved call mro_isa_changed_in on the stash that has been assigned if it did not have an effective name (HvENAME) before the assignment. This is what was happening: {package Right} # autovivify it @thing::ISA = qw[Left]; *Left:: = delete $::{"Right::"}; When Right is deleted, it is not attached to the symbol table any more, so it has no effective name. mro_isa_changed_in3 is called on it, which resets the caches and then proceeds to cache a new isa linearisation. The new linearisation, of course, does not use the name ‘Left’. When Right is assigned over the top of Left, Right is given an HvENAME of ‘Left’. Then mro_isa_changed_is3 is called on the original Left, which, in turn, calls mro_isa_changed on its sub- classes. So thing’s isa linearisation is calculated, which is just ‘thing’ + get_linear_isa(Left) (where ‘Left’ now refers to what was previously called Right). This ends up using the bad linearisation. So the linearisation of a heretofore effectively nameless stash must be recalculated, but after it has been given a name. If it has an effective name already, then it appears elsewhere in the symbol table, and its effective name does not change. (The name added simply becomes an alternative to switch to should the HvENAME become invalid.) So the existing isa cache is fine and we do not need to call mro_isa_changed_in. (That is a rather lengthy commit message, is it not?)
Diffstat (limited to 'mro.c')
-rw-r--r--mro.c16
1 files changed, 15 insertions, 1 deletions
diff --git a/mro.c b/mro.c
index 9e433d41c7..4d6563d228 100644
--- a/mro.c
+++ b/mro.c
@@ -626,6 +626,7 @@ Perl_mro_package_moved(pTHX_ HV * const stash, HV * const oldstash,
I32 riter = -1;
HV *seen = NULL;
HV *seen_stashes = NULL;
+ const bool stash_had_name = stash && HvENAME(stash);
/* If newname_len is negative, then gv is actually the caller’s hash of
stashes that have been seen so far. */
@@ -668,7 +669,20 @@ Perl_mro_package_moved(pTHX_ HV * const stash, HV * const oldstash,
hv_delete(PL_stashcache, newname, newname_len, G_DISCARD);
hv_ename_delete(oldstash, newname, newname_len);
}
- if(stash) hv_ename_add(stash, newname, newname_len);
+ if(stash) {
+ hv_ename_add(stash, newname, newname_len);
+
+ /* If this stash had been detached from the symbol table (so it
+ * had no HvENAME) before being assigned to spot whose name is in
+ * newname, then its isa cache would be stale (the effective name
+ * having changed), and subclasses of newname would then use that
+ * cache in the mro_isa_changed_in3(oldstash...) call below. (See
+ * [perl #77358].)
+ * If it did have a name, then its previous name is still
+ * used in isa caches, and there is no need for this call.
+ */
+ if(!stash_had_name) mro_isa_changed_in(stash);
+ }
mro_isa_changed_in3((HV *)oldstash, newname, newname_len);