perlform.pod
[p5sagit/p5-mst-13.2.git] / mg.c
diff --git a/mg.c b/mg.c
index 185b4f5..e7472a6 100644 (file)
--- a/mg.c
+++ b/mg.c
 #  endif
 #endif
 
+#ifdef PERL_OBJECT
+#  define VTBL            this->*vtbl
+#else
+#  define VTBL                 *vtbl
+static void restore_magic _((void *p));
+#endif
+
 /*
  * Use the "DESTRUCTOR" scope cleanup to reinstate magic.
  */
 
-#ifdef PERL_OBJECT
-
-#define VTBL            this->*vtbl
-
-#else
 struct magic_state {
     SV* mgs_sv;
     U32 mgs_flags;
+    I32 mgs_ss_ix;
 };
-typedef struct magic_state MGS;
-
-static void restore_magic _((void *p));
-#define VTBL                   *vtbl
-
-#endif
+/* MGS is typedef'ed to struct magic_state in perl.h */
 
 STATIC void
-save_magic(MGS *mgs, SV *sv)
+save_magic(I32 mgs_ix, SV *sv)
 {
+    MGS* mgs;
     assert(SvMAGICAL(sv));
 
+    SAVEDESTRUCTOR(restore_magic, (void*)mgs_ix);
+
+    mgs = SSPTR(mgs_ix, MGS*);
     mgs->mgs_sv = sv;
     mgs->mgs_flags = SvMAGICAL(sv) | SvREADONLY(sv);
-    SAVEDESTRUCTOR(restore_magic, mgs);
+    mgs->mgs_ss_ix = PL_savestack_ix;   /* points after the saved destructor */
 
     SvMAGICAL_off(sv);
     SvREADONLY_off(sv);
@@ -63,9 +65,12 @@ save_magic(MGS *mgs, SV *sv)
 STATIC void
 restore_magic(void *p)
 {
-    MGS* mgs = (MGS*)p;
+    MGS* mgs = SSPTR((I32)p, MGS*);
     SV* sv = mgs->mgs_sv;
 
+    if (!sv)
+        return;
+
     if (SvTYPE(sv) >= SVt_PVMG && SvMAGIC(sv))
     {
        if (mgs->mgs_flags)
@@ -75,6 +80,24 @@ restore_magic(void *p)
        if (SvGMAGICAL(sv))
            SvFLAGS(sv) &= ~(SVf_IOK|SVf_NOK|SVf_POK);
     }
+
+    mgs->mgs_sv = NULL;  /* mark the MGS structure as restored */
+
+    /* If we're still on top of the stack, pop us off.  (That condition
+     * will be satisfied if restore_magic was called explicitly, but *not*
+     * if it's being called via leave_scope.)
+     * The reason for doing this is that otherwise, things like sv_2cv()
+     * may leave alloc gunk on the savestack, and some code
+     * (e.g. sighandler) doesn't expect that...
+     */
+    if (PL_savestack_ix == mgs->mgs_ss_ix)
+    {
+        assert(SSPOPINT == SAVEt_DESTRUCTOR);
+        PL_savestack_ix -= 2;
+        assert(SSPOPINT == SAVEt_ALLOC);
+        PL_savestack_ix -= SSPOPINT;
+    }
+
 }
 
 void
@@ -97,13 +120,13 @@ mg_magical(SV *sv)
 int
 mg_get(SV *sv)
 {
-    MGS mgs;
+    I32 mgs_ix;
     MAGIC* mg;
     MAGIC** mgp;
     int mgp_valid = 0;
 
-    ENTER;
-    save_magic(&mgs, sv);
+    mgs_ix = SSNEW(sizeof(MGS));
+    save_magic(mgs_ix, sv);
 
     mgp = &SvMAGIC(sv);
     while ((mg = *mgp) != 0) {
@@ -113,7 +136,7 @@ mg_get(SV *sv)
            /* Ignore this magic if it's been deleted */
            if ((mg == (mgp_valid ? *mgp : SvMAGIC(sv))) &&
                  (mg->mg_flags & MGf_GSKIP))
-               mgs.mgs_flags = 0;
+               (SSPTR(mgs_ix, MGS*))->mgs_flags = 0;
        }
        /* Advance to next magic (complicated by possible deletion) */
        if (mg == (mgp_valid ? *mgp : SvMAGIC(sv))) {
@@ -124,32 +147,32 @@ mg_get(SV *sv)
            mgp = &SvMAGIC(sv); /* Re-establish pointer after sv_upgrade */
     }
 
-    LEAVE;
+    restore_magic((void*)mgs_ix);
     return 0;
 }
 
 int
 mg_set(SV *sv)
 {
-    MGS mgs;
+    I32 mgs_ix;
     MAGIC* mg;
     MAGIC* nextmg;
 
-    ENTER;
-    save_magic(&mgs, sv);
+    mgs_ix = SSNEW(sizeof(MGS));
+    save_magic(mgs_ix, sv);
 
     for (mg = SvMAGIC(sv); mg; mg = nextmg) {
        MGVTBL* vtbl = mg->mg_virtual;
        nextmg = mg->mg_moremagic;      /* it may delete itself */
        if (mg->mg_flags & MGf_GSKIP) {
            mg->mg_flags &= ~MGf_GSKIP; /* setting requires another read */
-           mgs.mgs_flags = 0;
+           (SSPTR(mgs_ix, MGS*))->mgs_flags = 0;
        }
        if (vtbl && (vtbl->svt_set != NULL))
            (VTBL->svt_set)(sv, mg);
     }
 
-    LEAVE;
+    restore_magic((void*)mgs_ix);
     return 0;
 }
 
@@ -163,13 +186,13 @@ mg_length(SV *sv)
     for (mg = SvMAGIC(sv); mg; mg = mg->mg_moremagic) {
        MGVTBL* vtbl = mg->mg_virtual;
        if (vtbl && (vtbl->svt_len != NULL)) {
-           MGS mgs;
+            I32 mgs_ix;
 
-           ENTER;
-           save_magic(&mgs, sv);
+           mgs_ix = SSNEW(sizeof(MGS));
+           save_magic(mgs_ix, sv);
            /* omit MGf_GSKIP -- not changed here */
            len = (VTBL->svt_len)(sv, mg);
-           LEAVE;
+           restore_magic((void*)mgs_ix);
            return len;
        }
     }
@@ -187,11 +210,13 @@ mg_size(SV *sv)
     for (mg = SvMAGIC(sv); mg; mg = mg->mg_moremagic) {
        MGVTBL* vtbl = mg->mg_virtual;
        if (vtbl && (vtbl->svt_len != NULL)) {
-           MGS mgs;
-           ENTER;
+            I32 mgs_ix;
+
+           mgs_ix = SSNEW(sizeof(MGS));
+           save_magic(mgs_ix, sv);
            /* omit MGf_GSKIP -- not changed here */
            len = (VTBL->svt_len)(sv, mg);
-           LEAVE;
+           restore_magic((void*)mgs_ix);
            return len;
        }
     }
@@ -212,11 +237,11 @@ mg_size(SV *sv)
 int
 mg_clear(SV *sv)
 {
-    MGS mgs;
+    I32 mgs_ix;
     MAGIC* mg;
 
-    ENTER;
-    save_magic(&mgs, sv);
+    mgs_ix = SSNEW(sizeof(MGS));
+    save_magic(mgs_ix, sv);
 
     for (mg = SvMAGIC(sv); mg; mg = mg->mg_moremagic) {
        MGVTBL* vtbl = mg->mg_virtual;
@@ -226,7 +251,7 @@ mg_clear(SV *sv)
            (VTBL->svt_clear)(sv, mg);
     }
 
-    LEAVE;
+    restore_magic((void*)mgs_ix);
     return 0;
 }