summaryrefslogtreecommitdiff
path: root/hv.c
diff options
context:
space:
mode:
authorTony Cook <tony@develop-help.com>2016-01-11 14:45:43 +1100
committerTony Cook <tony@develop-help.com>2016-01-11 14:45:43 +1100
commit6146d9e1c87d449f5c7e9c953a2e9051e32b1696 (patch)
tree30172d6d3376991c085cd7c267953e42b86a8cbb /hv.c
parent5c8e69ec416d40935c4c2d1f240cdd70f076929e (diff)
downloadperl-6146d9e1c87d449f5c7e9c953a2e9051e32b1696.tar.gz
[perl #123788] update isa magic stash records when *ISA is deleted
Diffstat (limited to 'hv.c')
-rw-r--r--hv.c67
1 files changed, 66 insertions, 1 deletions
diff --git a/hv.c b/hv.c
index 3bab3e2323..7b5ad95878 100644
--- a/hv.c
+++ b/hv.c
@@ -1166,8 +1166,73 @@ S_hv_delete_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
sv_2mortal((SV *)gv)
);
}
- else if (klen == 3 && strnEQ(key, "ISA", 3))
+ else if (klen == 3 && strnEQ(key, "ISA", 3) && GvAV(gv)) {
+ AV *isa = GvAV(gv);
+ MAGIC *mg = mg_find((SV*)isa, PERL_MAGIC_isa);
+
mro_changes = 1;
+ if (mg) {
+ if (mg->mg_obj == (SV*)gv) {
+ /* This is the only stash this ISA was used for.
+ * The isaelem magic asserts if there's no
+ * isa magic on the array, so explicitly
+ * remove the magic on both the array and its
+ * elements. @ISA shouldn't be /too/ large.
+ */
+ SV **svp, **end;
+ strip_magic:
+ svp = AvARRAY(isa);
+ end = svp + AvFILLp(isa)+1;
+ while (svp < end) {
+ if (*svp)
+ mg_free_type(*svp, PERL_MAGIC_isaelem);
+ ++svp;
+ }
+ mg_free_type((SV*)GvAV(gv), PERL_MAGIC_isa);
+ }
+ else {
+ /* mg_obj is an array of stashes
+ Note that the array doesn't keep a reference
+ count on the stashes.
+ */
+ AV *av = (AV*)mg->mg_obj;
+ SV **svp, **arrayp;
+ SSize_t index;
+ SSize_t items;
+
+ assert(SvTYPE(mg->mg_obj) == SVt_PVAV);
+
+ /* remove the stash from the magic array */
+ arrayp = svp = AvARRAY(av);
+ items = AvFILLp(av) + 1;
+ if (items == 1) {
+ assert(*arrayp == (SV *)gv);
+ mg->mg_obj = NULL;
+ /* avoid a double free on the last stash */
+ AvFILLp(av) = -1;
+ /* The magic isn't MGf_REFCOUNTED, so release
+ * the array manually.
+ */
+ SvREFCNT_dec_NN(av);
+ goto strip_magic;
+ }
+ else {
+ while (items--) {
+ if (*svp == (SV*)gv)
+ break;
+ ++svp;
+ }
+ index = svp - arrayp;
+ assert(index >= 0 && index <= AvFILLp(av));
+ if (index < AvFILLp(av)) {
+ arrayp[index] = arrayp[AvFILLp(av)];
+ }
+ arrayp[AvFILLp(av)] = NULL;
+ --AvFILLp(av);
+ }
+ }
+ }
+ }
}
sv = d_flags & G_DISCARD ? HeVAL(entry) : sv_2mortal(HeVAL(entry));