diff options
author | Father Chrysostomos <sprout@cpan.org> | 2010-10-30 18:19:56 -0700 |
---|---|---|
committer | Father Chrysostomos <sprout@cpan.org> | 2010-10-30 18:19:56 -0700 |
commit | 8b9e80a3f70002d0c2fb6549a8fd594f358b9525 (patch) | |
tree | 651ff37ff4aaa075555a6ab025b41229622874c3 /mro.c | |
parent | d7fbb1de45aa77d305eccd94d8c07d7a1491fc45 (diff) | |
download | perl-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.c | 16 |
1 files changed, 15 insertions, 1 deletions
@@ -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); |