Make changes to aliased *ISA work
authorFather Chrysostomos <sprout@cpan.org>
Sun, 14 Nov 2010 14:37:15 +0000 (06:37 -0800)
committerFather Chrysostomos <sprout@cpan.org>
Sun, 14 Nov 2010 18:59:13 +0000 (10:59 -0800)
This is a follow-up to 6624142.

Ref-to-glob assignment was not working after an *ISA-to-*ISA
assignment.

It needs to copy into mg_obj all the items in the mg_obj of the array
that is being replaced.

sv.c
t/mro/isa_aliases.t

diff --git a/sv.c b/sv.c
index f787b84..037c0dc 100644 (file)
--- a/sv.c
+++ b/sv.c
@@ -3810,18 +3810,46 @@ S_glob_assign_ref(pTHX_ SV *const dstr, SV *const sstr)
         /* The stash may have been detached from the symbol table, so
            check its name before doing anything. */
         && GvSTASH(dstr) && HvENAME(GvSTASH(dstr))
+        && sref != dref
        ) {
            MAGIC *mg;
+           MAGIC * const omg = dref && SvSMAGICAL(dref)
+                                ? mg_find(dref, PERL_MAGIC_isa)
+                                : NULL;
            if (SvSMAGICAL(sref) && (mg = mg_find(sref, PERL_MAGIC_isa))) {
                if (SvTYPE(mg->mg_obj) != SVt_PVAV) {
                    AV * const ary = newAV();
                    av_push(ary, mg->mg_obj); /* takes the refcount */
                    mg->mg_obj = (SV *)ary;
                }
-               av_push((AV *)mg->mg_obj, SvREFCNT_inc_simple_NN(dstr));
+               if (omg) {
+                   if (SvTYPE(omg->mg_obj) == SVt_PVAV) {
+                       SV **svp = AvARRAY((AV *)omg->mg_obj);
+                       I32 items = AvFILLp((AV *)omg->mg_obj) + 1;
+                       while (items--)
+                           av_push(
+                            (AV *)mg->mg_obj,
+                            SvREFCNT_inc_simple_NN(*svp++)
+                           );
+                   }
+                   else
+                       av_push(
+                        (AV *)mg->mg_obj,
+                        SvREFCNT_inc_simple_NN(omg->mg_obj)
+                       );
+               }
+               else
+                   av_push((AV *)mg->mg_obj,SvREFCNT_inc_simple_NN(dstr));
            }
-           else sv_magic(sref, dstr, PERL_MAGIC_isa, NULL, 0);
-           mro_isa_changed_in(GvSTASH(dstr));
+           else
+               sv_magic(
+                sref, omg ? omg->mg_obj : dstr, PERL_MAGIC_isa, NULL, 0
+               );
+           /* Since the *ISA assignment could have affected more than
+              one stash, don’t call mro_isa_changed_in directly, but let
+              magic_setisa do it for us, as it already has the logic for
+              dealing with globs vs arrays of globs. */
+           SvSETMAGIC(sref);
        }
        break;
     }
index 0635efc..abdedce 100644 (file)
@@ -2,7 +2,7 @@
 
 BEGIN { chdir 't'; @INC = '../lib'; require './test.pl' }
 
-plan 8;
+plan 12;
 
 @Foogh::ISA = "Bar";
 *Phoogh::ISA = *Foogh::ISA;
@@ -17,6 +17,18 @@ ok !Foogh->isa("Bar"),
 ok !Phoogh->isa("Bar"),
  '!isa on the stash that claimed the @ISA via glob assignment';
 
+@Foogh::ISA = "Bar";
+*Foogh::ISA = ["Baz"];
+
+ok 'Foogh'->isa("Baz"),
+ 'isa after glob-to-ref assignment when *ISA is shared';
+ok 'Phoogh'->isa("Baz"),
+ 'isa after glob-to-ref assignment on another stash when *ISA is shared';
+ok !Foogh->isa("Bar"),
+ '!isa after glob-to-ref assignment when *ISA is shared';
+ok !Phoogh->isa("Bar"),
+ '!isa after glob-to-ref assignment on another stash when *ISA is shared';
+
 @Foo::ISA = "Bar";
 *Phoo::ISA = \@Foo::ISA;
 @Foo::ISA = "Baz";