]> git.vpit.fr Git - perl/modules/Scope-Upper.git/blobdiff - Upper.xs
Only dump debug info when $Scope::Upper::DEBUG is true
[perl/modules/Scope-Upper.git] / Upper.xs
index 27397091936b248267348e9bbe075f900311671f..823b32e38a6556f6f486baec573d2b387467b85c 100644 (file)
--- a/Upper.xs
+++ b/Upper.xs
 
 #if SU_DEBUG
 # define SU_D(X) STMT_START X STMT_END
+static void su_debug_log(const char *fmt, ...) {
+ va_list va;
+ SV *sv;
+ dTHX;
+ va_start(va, fmt);
+ sv = get_sv(__PACKAGE__ "::DEBUG", 0);
+ if (sv && SvTRUE(sv))
+  PerlIO_vprintf(Perl_debug_log, fmt, va);
+ va_end(va);
+ return;
+}
 #else
 # define SU_D(X)
 #endif
@@ -72,7 +83,7 @@
 #endif
 
 #ifndef newSV_type
-STATIC SV *su_newSV_type(pTHX_ svtype t) {
+static SV *su_newSV_type(pTHX_ svtype t) {
  SV *sv = newSV(0);
  SvUPGRADE(sv, t);
  return sv;
@@ -182,7 +193,7 @@ STATIC SV *su_newSV_type(pTHX_ svtype t) {
 #endif
 
 #ifndef OP_GIMME_REVERSE
-STATIC U8 su_op_gimme_reverse(U8 gimme) {
+static U8 su_op_gimme_reverse(U8 gimme) {
  switch (gimme) {
   case G_VOID:
    return OPf_WANT_VOID;
@@ -249,13 +260,18 @@ STATIC U8 su_op_gimme_reverse(U8 gimme) {
 # undef  MY_CXT
 # define MY_CXT       su_globaldata
 # undef  START_MY_CXT
-# define START_MY_CXT STATIC my_cxt_t MY_CXT;
+# define START_MY_CXT static my_cxt_t MY_CXT;
 # undef  MY_CXT_INIT
 # define MY_CXT_INIT  NOOP
 # undef  MY_CXT_CLONE
 # define MY_CXT_CLONE NOOP
 #endif
 
+/* --- Error messages ------------------------------------------------------ */
+
+static const char su_stack_smash[]    = "Cannot target a scope outside of the current stack";
+static const char su_no_such_target[] = "No targetable %s scope in the current stack";
+
 /* --- Unique context ID global storage ------------------------------------ */
 
 /* ... Sequence ID counter ................................................. */
@@ -265,11 +281,11 @@ typedef struct {
  STRLEN  size;
 } su_uv_array;
 
-STATIC su_uv_array su_uid_seq_counter;
+static su_uv_array su_uid_seq_counter;
 
 #ifdef USE_ITHREADS
 
-STATIC perl_mutex su_uid_seq_counter_mutex;
+static perl_mutex su_uid_seq_counter_mutex;
 
 #define SU_LOCK(M)   MUTEX_LOCK(M)
 #define SU_UNLOCK(M) MUTEX_UNLOCK(M)
@@ -281,7 +297,7 @@ STATIC perl_mutex su_uid_seq_counter_mutex;
 
 #endif /* !USE_ITHREADS */
 
-STATIC UV su_uid_seq_next(pTHX_ UV depth) {
+static UV su_uid_seq_next(pTHX_ UV depth) {
 #define su_uid_seq_next(D) su_uid_seq_next(aTHX_ (D))
  UV seq;
  UV *seqs;
@@ -317,7 +333,7 @@ typedef struct {
 
 #define SU_UID_ACTIVE 1
 
-STATIC UV su_uid_depth(pTHX_ I32 cxix) {
+static UV su_uid_depth(pTHX_ I32 cxix) {
 #define su_uid_depth(I) su_uid_depth(aTHX_ (I))
  const PERL_SI *si;
  UV depth;
@@ -330,53 +346,47 @@ STATIC UV su_uid_depth(pTHX_ I32 cxix) {
 }
 
 typedef struct {
- su_uid **map;
- STRLEN   used;
- STRLEN   alloc;
+ su_uid *map;
+ STRLEN  used;
+ STRLEN  alloc;
 } su_uid_storage;
 
-STATIC void su_uid_storage_dup(pTHX_ su_uid_storage *new_cxt, const su_uid_storage *old_cxt, UV max_depth) {
+static void su_uid_storage_dup(pTHX_ su_uid_storage *new_cxt, const su_uid_storage *old_cxt, UV max_depth) {
 #define su_uid_storage_dup(N, O, D) su_uid_storage_dup(aTHX_ (N), (O), (D))
- su_uid **old_map = old_cxt->map;
+ su_uid *old_map = old_cxt->map;
 
  if (old_map) {
-  su_uid **new_map = new_cxt->map;
-  STRLEN old_used  = old_cxt->used;
-  STRLEN new_used, new_alloc;
-  STRLEN i;
+  su_uid *new_map  = new_cxt->map;
+  STRLEN  old_used = old_cxt->used;
+  STRLEN  new_used, new_alloc;
+  STRLEN  i;
 
-  new_used = max_depth < old_used ? max_depth : old_used;
+  new_used      = max_depth < old_used ? max_depth : old_used;
   new_cxt->used = new_used;
 
-  if (new_used <= new_cxt->alloc)
-   new_alloc = new_cxt->alloc;
-  else {
-   new_alloc = new_used;
-   Renew(new_map, new_alloc, su_uid *);
-   for (i = new_cxt->alloc; i < new_alloc; ++i)
-    new_map[i] = NULL;
+  if (new_used <= new_cxt->alloc) {
+   new_alloc      = new_cxt->alloc;
+  } else {
+   new_alloc      = new_used;
+   Renew(new_map, new_alloc, su_uid);
    new_cxt->map   = new_map;
    new_cxt->alloc = new_alloc;
   }
 
   for (i = 0; i < new_alloc; ++i) {
-   su_uid *new_uid = new_map[i];
+   su_uid *new_uid = new_map + i;
 
    if (i < new_used) { /* => i < max_depth && i < old_used */
-    su_uid *old_uid = old_map[i];
+    su_uid *old_uid = old_map + i;
 
     if (old_uid && (old_uid->flags & SU_UID_ACTIVE)) {
-     if (!new_uid) {
-      Newx(new_uid, 1, su_uid);
-      new_map[i] = new_uid;
-     }
      *new_uid = *old_uid;
      continue;
     }
    }
 
-   if (new_uid)
-    new_uid->flags &= ~SU_UID_ACTIVE;
+   new_uid->seq   = 0;
+   new_uid->flags = 0;
   }
  }
 
@@ -436,7 +446,7 @@ typedef struct {
  bool           died;
 } su_uplevel_ud;
 
-STATIC su_uplevel_ud *su_uplevel_ud_new(pTHX) {
+static su_uplevel_ud *su_uplevel_ud_new(pTHX) {
 #define su_uplevel_ud_new() su_uplevel_ud_new(aTHX)
  su_uplevel_ud *sud;
  PERL_SI       *si;
@@ -459,7 +469,7 @@ STATIC su_uplevel_ud *su_uplevel_ud_new(pTHX) {
  return sud;
 }
 
-STATIC void su_uplevel_ud_delete(pTHX_ su_uplevel_ud *sud) {
+static void su_uplevel_ud_delete(pTHX_ su_uplevel_ud *sud) {
 #define su_uplevel_ud_delete(S) su_uplevel_ud_delete(aTHX_ (S))
  PERL_SI *si = sud->si;
 
@@ -467,16 +477,7 @@ STATIC void su_uplevel_ud_delete(pTHX_ su_uplevel_ud *sud) {
  SvREFCNT_dec(si->si_stack);
  Safefree(si);
 
- if (sud->tmp_uid_storage.map) {
-  su_uid **map   = sud->tmp_uid_storage.map;
-  STRLEN   alloc = sud->tmp_uid_storage.alloc;
-  STRLEN   i;
-
-  for (i = 0; i < alloc; ++i)
-   Safefree(map[i]);
-
-  Safefree(map);
- }
+ Safefree(sud->tmp_uid_storage.map);
 
  Safefree(sud);
 
@@ -563,7 +564,7 @@ START_MY_CXT
 
 /* ... Saving array elements ............................................... */
 
-STATIC I32 su_av_key2idx(pTHX_ AV *av, I32 key) {
+static I32 su_av_key2idx(pTHX_ AV *av, I32 key) {
 #define su_av_key2idx(A, K) su_av_key2idx(aTHX_ (A), (K))
  I32 idx;
 
@@ -598,7 +599,7 @@ typedef struct {
  I32 idx;
 } su_ud_adelete;
 
-STATIC void su_adelete(pTHX_ void *ud_) {
+static void su_adelete(pTHX_ void *ud_) {
  su_ud_adelete *ud = (su_ud_adelete *) ud_;
 
  av_delete(ud->av, ud->idx, G_DISCARD);
@@ -607,7 +608,7 @@ STATIC void su_adelete(pTHX_ void *ud_) {
  Safefree(ud);
 }
 
-STATIC void su_save_adelete(pTHX_ AV *av, I32 idx) {
+static void su_save_adelete(pTHX_ AV *av, I32 idx) {
 #define su_save_adelete(A, K) su_save_adelete(aTHX_ (A), (K))
  su_ud_adelete *ud;
 
@@ -623,7 +624,7 @@ STATIC void su_save_adelete(pTHX_ AV *av, I32 idx) {
 
 #endif /* SAVEADELETE */
 
-STATIC void su_save_aelem(pTHX_ AV *av, SV *key, SV *val) {
+static void su_save_aelem(pTHX_ AV *av, SV *key, SV *val) {
 #define su_save_aelem(A, K, V) su_save_aelem(aTHX_ (A), (K), (V))
  I32 idx;
  I32 preeminent = 1;
@@ -653,7 +654,7 @@ STATIC void su_save_aelem(pTHX_ AV *av, SV *key, SV *val) {
 
 /* ... Saving hash elements ................................................ */
 
-STATIC void su_save_helem(pTHX_ HV *hv, SV *keysv, SV *val) {
+static void su_save_helem(pTHX_ HV *hv, SV *keysv, SV *val) {
 #define su_save_helem(H, K, V) su_save_helem(aTHX_ (H), (K), (V))
  I32 preeminent = 1;
  HE *he;
@@ -700,7 +701,7 @@ typedef struct {
  CV *old_cv;
 } su_save_gvcv_ud;
 
-STATIC void su_restore_gvcv(pTHX_ void *ud_) {
+static void su_restore_gvcv(pTHX_ void *ud_) {
  su_save_gvcv_ud *ud = ud_;
  GV              *gv = ud->gv;
 
@@ -711,7 +712,7 @@ STATIC void su_restore_gvcv(pTHX_ void *ud_) {
  Safefree(ud);
 }
 
-STATIC void su_save_gvcv(pTHX_ GV *gv) {
+static void su_save_gvcv(pTHX_ GV *gv) {
 #define su_save_gvcv(G) su_save_gvcv(aTHX_ (G))
  su_save_gvcv_ud *ud;
 
@@ -729,16 +730,23 @@ STATIC void su_save_gvcv(pTHX_ GV *gv) {
 /* --- Actions ------------------------------------------------------------- */
 
 typedef struct {
- I32 depth;
- I32 pad;
+ U8   type;
+ U8   private;
+ U8   pad;
+ /* spare */
+ I32  depth;
  I32 *origin;
- void (*handler)(pTHX_ void *);
 } su_ud_common;
 
-#define SU_UD_DEPTH(U)   (((su_ud_common *) (U))->depth)
+#define SU_UD_TYPE(U)    (((su_ud_common *) (U))->type)
+#define SU_UD_PRIVATE(U) (((su_ud_common *) (U))->private)
 #define SU_UD_PAD(U)     (((su_ud_common *) (U))->pad)
+#define SU_UD_DEPTH(U)   (((su_ud_common *) (U))->depth)
 #define SU_UD_ORIGIN(U)  (((su_ud_common *) (U))->origin)
-#define SU_UD_HANDLER(U) (((su_ud_common *) (U))->handler)
+
+#define SU_UD_TYPE_REAP     0
+#define SU_UD_TYPE_LOCALIZE 1
+#define SU_UD_TYPE_UID      2
 
 #define SU_UD_FREE(U) STMT_START { \
  if (SU_UD_ORIGIN(U)) Safefree(SU_UD_ORIGIN(U)); \
@@ -751,11 +759,12 @@ typedef struct {
 
 typedef struct {
  su_ud_common ci;
- SV *cb;
+ SV          *cb;
 } su_ud_reap;
 
-STATIC void su_call(pTHX_ void *ud_) {
- su_ud_reap *ud = (su_ud_reap *) ud_;
+#define SU_UD_REAP_CB(U) (((su_ud_reap *) (U))->cb)
+
+static void su_call(pTHX_ SV *cb) {
 #if SU_SAVE_LAST_CX
  I32 cxix;
  PERL_CONTEXT saved_cx;
@@ -763,11 +772,8 @@ STATIC void su_call(pTHX_ void *ud_) {
 
  dSP;
 
- SU_D({
-  PerlIO_printf(Perl_debug_log,
-                "%p: @@@ call\n%p: depth=%2d scope_ix=%2d save_ix=%2d\n",
-                 ud, ud, SU_UD_DEPTH(ud), PL_scopestack_ix, PL_savestack_ix);
- });
+ SU_D(su_debug_log("@@@ call scope_ix=%2d save_ix=%2d\n",
+                         PL_scopestack_ix, PL_savestack_ix));
 
  ENTER;
  SAVETMPS;
@@ -782,7 +788,7 @@ STATIC void su_call(pTHX_ void *ud_) {
  saved_cx = cxstack[cxix];
 #endif /* SU_SAVE_LAST_CX */
 
- call_sv(ud->cb, G_VOID);
+ call_sv(cb, G_VOID);
 
 #if SU_SAVE_LAST_CX
  cxstack[cxix] = saved_cx;
@@ -793,39 +799,32 @@ STATIC void su_call(pTHX_ void *ud_) {
  FREETMPS;
  LEAVE;
 
- SvREFCNT_dec(ud->cb);
- SU_UD_FREE(ud);
-}
-
-STATIC void su_reap(pTHX_ void *ud) {
-#define su_reap(U) su_reap(aTHX_ (U))
- SU_D({
-  PerlIO_printf(Perl_debug_log,
-                "%p: === reap\n%p: depth=%2d scope_ix=%2d save_ix=%2d\n",
-                 ud, ud, SU_UD_DEPTH(ud), PL_scopestack_ix, PL_savestack_ix);
- });
+ SvREFCNT_dec(cb);
 
SAVEDESTRUCTOR_X(su_call, ud);
return;
 }
 
 /* ... Localize & localize array/hash element .............................. */
 
 typedef struct {
  su_ud_common ci;
- SV    *sv;
- SV    *val;
- SV    *elem;
- svtype type;
+ SV          *sv;
+ SV          *val;
+ SV          *elem;
 } su_ud_localize;
 
+#define SU_UD_LOCALIZE_SV(U)   (((su_ud_localize *) (U))->sv)
+#define SU_UD_LOCALIZE_VAL(U)  (((su_ud_localize *) (U))->val)
+#define SU_UD_LOCALIZE_ELEM(U) (((su_ud_localize *) (U))->elem)
+
 #define SU_UD_LOCALIZE_FREE(U) STMT_START { \
- SvREFCNT_dec((U)->elem); \
- SvREFCNT_dec((U)->val);  \
- SvREFCNT_dec((U)->sv);   \
- SU_UD_FREE(U);           \
+ SvREFCNT_dec(SU_UD_LOCALIZE_ELEM(U)); \
+ SvREFCNT_dec(SU_UD_LOCALIZE_VAL(U)); \
+ SvREFCNT_dec(SU_UD_LOCALIZE_SV(U)); \
+ SU_UD_FREE(U); \
 } STMT_END
 
-STATIC I32 su_ud_localize_init(pTHX_ su_ud_localize *ud, SV *sv, SV *val, SV *elem) {
+static I32 su_ud_localize_init(pTHX_ su_ud_localize *ud, SV *sv, SV *val, SV *elem) {
 #define su_ud_localize_init(UD, S, V, E) su_ud_localize_init(aTHX_ (UD), (S), (V), (E))
  UV deref = 0;
  svtype t = SVt_NULL;
@@ -898,21 +897,22 @@ STATIC I32 su_ud_localize_init(pTHX_ su_ud_localize *ud, SV *sv, SV *val, SV *el
  }
  /* When deref is set, val isn't NULL */
 
+ SU_UD_PRIVATE(ud) = t;
+
  ud->sv   = sv;
  ud->val  = val ? newSVsv(deref ? SvRV(val) : val) : NULL;
  ud->elem = SvREFCNT_inc(elem);
- ud->type = t;
 
  return size;
 }
 
-STATIC void su_localize(pTHX_ void *ud_) {
+static void su_localize(pTHX_ void *ud_) {
 #define su_localize(U) su_localize(aTHX_ (U))
  su_ud_localize *ud = (su_ud_localize *) ud_;
  SV *sv   = ud->sv;
  SV *val  = ud->val;
  SV *elem = ud->elem;
- svtype t = ud->type;
+ svtype t = SU_UD_PRIVATE(ud);
  GV *gv;
 
  if (SvTYPE(sv) >= SVt_PVGV) {
@@ -930,10 +930,9 @@ STATIC void su_localize(pTHX_ void *ud_) {
  SU_D({
   SV *z = newSV(0);
   SvUPGRADE(z, t);
-  PerlIO_printf(Perl_debug_log, "%p: === localize a %s\n",ud, sv_reftype(z, 0));
-  PerlIO_printf(Perl_debug_log,
-                "%p: depth=%2d scope_ix=%2d save_ix=%2d\n",
-                 ud, SU_UD_DEPTH(ud), PL_scopestack_ix, PL_savestack_ix);
+  su_debug_log("%p: === localize a %s\n",ud, sv_reftype(z, 0));
+  su_debug_log("%p: depth=%2d scope_ix=%2d save_ix=%2d\n",
+                ud, SU_UD_DEPTH(ud), PL_scopestack_ix, PL_savestack_ix);
   SvREFCNT_dec(z);
  });
 
@@ -942,14 +941,14 @@ STATIC void su_localize(pTHX_ void *ud_) {
   case SVt_PVAV:
    if (elem) {
     su_save_aelem(GvAV(gv), elem, val);
-    goto done;
+    return;
    } else
     save_ary(gv);
    break;
   case SVt_PVHV:
    if (elem) {
     su_save_helem(GvHV(gv), elem, val);
-    goto done;
+    return;
    } else
     save_hash(gv);
    break;
@@ -967,130 +966,222 @@ STATIC void su_localize(pTHX_ void *ud_) {
  if (val)
   SvSetMagicSV((SV *) gv, val);
 
-done:
- SU_UD_LOCALIZE_FREE(ud);
+ return;
+}
+
+/* ... Unique context ID ................................................... */
+
+/* We must pass the index because MY_CXT.uid_storage might be reallocated
+ * between the UID fetch and the invalidation at the end of scope. */
+
+typedef struct {
+ su_ud_common ci;
+ I32          idx;
+} su_ud_uid;
+
+static void su_uid_drop(pTHX_ void *ud_) {
+ su_ud_uid *ud = ud_;
+ dMY_CXT;
+
+ MY_CXT.uid_storage.map[ud->idx].flags &= ~SU_UID_ACTIVE;
+
+ SU_UD_FREE(ud);
+
+ return;
 }
 
 /* --- Pop a context back -------------------------------------------------- */
 
-#if SU_DEBUG && defined(DEBUGGING)
+#ifdef DEBUGGING
 # define SU_CXNAME(C) PL_block_type[CxTYPE(C)]
 #else
-# define SU_CXNAME(C) "XXX"
+# if SU_HAS_PERL(5, 11, 0)
+static const char *su_block_type[] = {
+ "NULL",
+ "WHEN",
+ "BLOCK",
+ "GIVEN",
+ "LOOP_FOR",
+ "LOOP_PLAIN",
+ "LOOP_LAZYSV",
+ "LOOP_LAZYIV",
+ "SUB",
+ "FORMAT",
+ "EVAL",
+ "SUBST"
+};
+# elif SU_HAS_PERL(5, 9, 3)
+static const char *su_block_type[] = {
+ "NULL",
+ "SUB",
+ "EVAL",
+ "WHEN",
+ "SUBST",
+ "BLOCK",
+ "FORMAT",
+ "GIVEN",
+ "LOOP_FOR",
+ "LOOP_PLAIN",
+ "LOOP_LAZYSV",
+ "LOOP_LAZYIV"
+};
+# else
+static const char *su_block_type[] = {
+ "NULL",
+ "SUB",
+ "EVAL",
+ "LOOP",
+ "SUBST",
+ "BLOCK"
+};
+# endif
+# define SU_CXNAME(C) su_block_type[CxTYPE(C)]
 #endif
 
-STATIC void su_pop(pTHX_ void *ud) {
+static void su_pop(pTHX_ void *ud) {
 #define su_pop(U) su_pop(aTHX_ (U))
  I32 depth, base, mark, *origin;
  depth = SU_UD_DEPTH(ud);
 
- SU_D(
-  PerlIO_printf(Perl_debug_log,
-   "%p: --- pop a %s\n"
-   "%p: leave scope     at depth=%2d scope_ix=%2d cur_top=%2d cur_base=%2d\n",
-    ud, SU_CXNAME(cxstack + cxstack_ix),
-    ud, depth, PL_scopestack_ix,PL_savestack_ix,PL_scopestack[PL_scopestack_ix])
- );
+ SU_D(su_debug_log(
+  "%p: --- pop a %s\n"
+  "%p: leave scope     at depth=%2d scope_ix=%2d cur_top=%2d cur_base=%2d\n",
+   ud, SU_CXNAME(cxstack + cxstack_ix),
+   ud, depth, PL_scopestack_ix,PL_savestack_ix,PL_scopestack[PL_scopestack_ix]
+ ));
 
  origin = SU_UD_ORIGIN(ud);
  mark   = origin[depth];
  base   = origin[depth - 1];
 
- SU_D(PerlIO_printf(Perl_debug_log,
-                    "%p: original scope was %*c top=%2d     base=%2d\n",
-                     ud,                24, ' ',    mark,        base));
+ SU_D(su_debug_log("%p: original scope was %*c top=%2d     base=%2d\n",
+                    ud,                24, ' ',    mark,        base));
 
  if (base < mark) {
-  SU_D(PerlIO_printf(Perl_debug_log, "%p: clear leftovers\n", ud));
+#if SU_HAS_PERL(5, 19, 4)
+  I32 save = -1;
+  PERL_CONTEXT *cx;
+#endif
+
+  SU_D(su_debug_log("%p: clear leftovers\n", ud));
+
+#if SU_HAS_PERL(5, 19, 4)
+  cx = cxstack + cxstack_ix;
+  if (CxTYPE(cx) == CXt_SUB || CxTYPE(cx) == CXt_FORMAT)
+   save = PL_scopestack[cx->blk_oldscopesp - 1];
+#endif
+
   PL_savestack_ix = mark;
   leave_scope(base);
+
+#if SU_HAS_PERL(5, 19, 4)
+  if (CxTYPE(cx) == CXt_SUB || CxTYPE(cx) == CXt_FORMAT)
+   PL_scopestack[cx->blk_oldscopesp - 1] = save;
+#endif
  }
  PL_savestack_ix = base;
 
  SU_UD_DEPTH(ud) = --depth;
 
  if (depth > 0) {
-  I32 pad;
+  U8 pad;
 
-  if ((pad = SU_UD_PAD(ud))) {
+  if ((pad = SU_UD_PAD(ud)) > 0) {
    dMY_CXT;
    do {
-    SU_D(PerlIO_printf(Perl_debug_log,
+    SU_D(su_debug_log(
           "%p: push a pad slot at depth=%2d scope_ix=%2d save_ix=%2d\n",
            ud,                       depth, PL_scopestack_ix, PL_savestack_ix));
     SU_SAVE_PLACEHOLDER();
    } while (--pad);
   }
 
-  SU_D(PerlIO_printf(Perl_debug_log,
-          "%p: push destructor at depth=%2d scope_ix=%2d save_ix=%2d\n",
-           ud,                       depth, PL_scopestack_ix, PL_savestack_ix));
+  SU_D(su_debug_log(
+         "%p: push destructor at depth=%2d scope_ix=%2d save_ix=%2d\n",
+          ud,                        depth, PL_scopestack_ix, PL_savestack_ix));
   SAVEDESTRUCTOR_X(su_pop, ud);
  } else {
-  SU_UD_HANDLER(ud)(aTHX_ ud);
+  switch (SU_UD_TYPE(ud)) {
+   case SU_UD_TYPE_REAP: {
+    SU_D(su_debug_log("%p: === reap\n%p: depth=%2d scope_ix=%2d save_ix=%2d\n",
+                   ud, ud, SU_UD_DEPTH(ud), PL_scopestack_ix, PL_savestack_ix));
+    SAVEDESTRUCTOR_X(su_call, SU_UD_REAP_CB(ud));
+    SU_UD_FREE(ud);
+    break;
+   }
+   case SU_UD_TYPE_LOCALIZE:
+    su_localize(ud);
+    SU_UD_LOCALIZE_FREE(ud);
+    break;
+   case SU_UD_TYPE_UID:
+    SAVEDESTRUCTOR_X(su_uid_drop, ud);
+    break;
+  }
  }
 
- SU_D(PerlIO_printf(Perl_debug_log,
-                    "%p: --- end pop: cur_top=%2d == cur_base=%2d\n",
-                     ud, PL_savestack_ix, PL_scopestack[PL_scopestack_ix]));
+ SU_D(su_debug_log("%p: --- end pop: cur_top=%2d == cur_base=%2d\n",
+                    ud, PL_savestack_ix, PL_scopestack[PL_scopestack_ix]));
 }
 
 /* --- Initialize the stack and the action userdata ------------------------ */
 
-STATIC I32 su_init(pTHX_ void *ud, I32 cxix, I32 size) {
+static I32 su_init(pTHX_ void *ud, I32 cxix, I32 size) {
 #define su_init(U, C, S) su_init(aTHX_ (U), (C), (S))
- I32 i, depth = 1, pad, offset, *origin;
-
- SU_D(PerlIO_printf(Perl_debug_log, "%p: ### init for cx %d\n", ud, cxix));
-
- if (size <= SU_SAVE_DESTRUCTOR_SIZE)
+ I32 i, depth, offset, base, *origin;
+ U8 pad;
+
+ SU_D(su_debug_log("%p: ### init for cx %d\n", ud, cxix));
+
+ /* su_pop() is going to be called from leave_scope(), so before pushing the
+  * next callback, we'll want to flush the current scope stack slice first.
+  * However, if we want the next callback not to be processed immediately by
+  * the current leave_scope(), we'll need to hide it by artificially
+  * incrementing the scope stack marker before. For the intermediate bumps,
+  * we will only need a bump of SU_SAVE_DESTRUCTOR_SIZE items, but for the
+  * last one we will need a bump of size items. However, in order to preserve
+  * the natural ordering between scope stack markers, we cannot bump lower
+  * markers more than higher ones. This is why we bump the intermediate markers
+  * by the smallest multiple of SU_SAVE_PLACEHOLDER_SIZE greater or equal to
+  * max(SU_SAVE_DESTRUCTOR_SIZE, size). */
+
+ if (size <= SU_SAVE_DESTRUCTOR_SIZE) {
   pad = 0;
- else {
else {
   I32 extra = size - SU_SAVE_DESTRUCTOR_SIZE;
   pad = extra / SU_SAVE_PLACEHOLDER_SIZE;
   if (extra % SU_SAVE_PLACEHOLDER_SIZE)
    ++pad;
  }
  offset = SU_SAVE_DESTRUCTOR_SIZE + SU_SAVE_PLACEHOLDER_SIZE * pad;
+ SU_D(su_debug_log("%p: size=%d pad=%d offset=%d\n", ud, size, pad, offset));
 
- SU_D(PerlIO_printf(Perl_debug_log, "%p: size=%d pad=%d offset=%d\n",
                                    ud,    size,   pad,   offset));
+ depth = PL_scopestack_ix - cxstack[cxix].blk_oldscopesp;
SU_D(su_debug_log("%p: going down to depth %d\n", ud, depth));
 
- for (i = cxstack_ix; i > cxix; --i) {
-  PERL_CONTEXT *cx = cxstack + i;
-  switch (CxTYPE(cx)) {
-#if SU_HAS_PERL(5, 11, 0)
-   case CXt_LOOP_FOR:
-   case CXt_LOOP_PLAIN:
-   case CXt_LOOP_LAZYSV:
-   case CXt_LOOP_LAZYIV:
-#else
-   case CXt_LOOP:
-#endif
-    SU_D(PerlIO_printf(Perl_debug_log, "%p: cx %d is loop\n", ud, i));
-    depth += 2;
-    break;
-   default:
-    SU_D(PerlIO_printf(Perl_debug_log, "%p: cx %d is other\n", ud, i));
-    depth++;
-    break;
-  }
- }
- SU_D(PerlIO_printf(Perl_debug_log, "%p: going down to depth %d\n", ud, depth));
+ /* We need to bump all the intermediary stack markers just in case an
+  * exception is thrown before the target scope is reached. Indeed, in this
+  * case there might be arbitrary many scope frames flushed at the same time,
+  * and since we cannot know in advance whether this will happen or not, we
+  * have to make sure the final frame is protected for the actual action. But
+  * of course, in order to do that, we also need to bump all the previous stack
+  * markers. If not for this, it should have been possible to just bump the two
+  * next frames in su_pop(). */
 
  Newx(origin, depth + 1, I32);
- origin[0] = PL_scopestack[PL_scopestack_ix - depth];
- PL_scopestack[PL_scopestack_ix - depth] += size;
- for (i = depth - 1; i >= 1; --i) {
-  I32 j = PL_scopestack_ix - i;
-  origin[depth - i] = PL_scopestack[j];
+ base = PL_scopestack_ix - depth;
+ origin[0] = PL_scopestack[base];
+ PL_scopestack[base] += size;
+ for (i = 1; i < depth; ++i) {
+  I32 j = i + base;
+  /* origin[depth - i] == PL_scopestack[PL_scopestack_ix - i] */
+  origin[i] = PL_scopestack[j];
   PL_scopestack[j] += offset;
  }
  origin[depth] = PL_savestack_ix;
 
- SU_UD_ORIGIN(ud) = origin;
- SU_UD_DEPTH(ud)  = depth;
  SU_UD_PAD(ud)    = pad;
+ SU_UD_DEPTH(ud)  = depth;
+ SU_UD_ORIGIN(ud) = origin;
 
  /* Make sure the first destructor fires by pushing enough fake slots on the
   * stack. */
@@ -1098,24 +1189,21 @@ STATIC I32 su_init(pTHX_ void *ud, I32 cxix, I32 size) {
                                        <= PL_scopestack[PL_scopestack_ix - 1]) {
   dMY_CXT;
   do {
-   SU_D(PerlIO_printf(Perl_debug_log,
-                  "%p: push a fake slot      at scope_ix=%2d  save_ix=%2d\n",
-                   ud,                      PL_scopestack_ix, PL_savestack_ix));
+   SU_D(su_debug_log("%p: push a fake slot      at scope_ix=%2d  save_ix=%2d\n",
+                      ud,                   PL_scopestack_ix, PL_savestack_ix));
    SU_SAVE_PLACEHOLDER();
   } while (PL_savestack_ix + SU_SAVE_DESTRUCTOR_SIZE
                                         <= PL_scopestack[PL_scopestack_ix - 1]);
  }
- SU_D(PerlIO_printf(Perl_debug_log,
-                  "%p: push first destructor at scope_ix=%2d  save_ix=%2d\n",
-                   ud,                      PL_scopestack_ix, PL_savestack_ix));
+ SU_D(su_debug_log("%p: push first destructor at scope_ix=%2d  save_ix=%2d\n",
+                    ud,                     PL_scopestack_ix, PL_savestack_ix));
  SAVEDESTRUCTOR_X(su_pop, ud);
 
  SU_D({
   for (i = 0; i <= depth; ++i) {
    I32 j = PL_scopestack_ix  - i;
-   PerlIO_printf(Perl_debug_log,
-                 "%p: depth=%2d scope_ix=%2d saved_floor=%2d new_floor=%2d\n",
-                  ud,        i, j, origin[depth - i],
+   su_debug_log("%p: depth=%2d scope_ix=%2d saved_floor=%2d new_floor=%2d\n",
+                 ud,         i,           j, origin[depth - i],
                                    i == 0 ? PL_savestack_ix : PL_scopestack[j]);
   }
  });
@@ -1125,7 +1213,7 @@ STATIC I32 su_init(pTHX_ void *ud, I32 cxix, I32 size) {
 
 /* --- Unwind stack -------------------------------------------------------- */
 
-STATIC void su_unwind(pTHX_ void *ud_) {
+static void su_unwind(pTHX_ void *ud_) {
  dMY_CXT;
  I32 cxix  = MY_CXT.unwind_storage.cxix;
  I32 items = MY_CXT.unwind_storage.items;
@@ -1152,8 +1240,7 @@ STATIC void su_unwind(pTHX_ void *ud_) {
 
  SU_D({
   I32 gimme = GIMME_V;
-  PerlIO_printf(Perl_debug_log,
-                "%p: cx=%d gimme=%s items=%d sp=%d oldmark=%d mark=%d\n",
+  su_debug_log("%p: cx=%d gimme=%s items=%d sp=%d oldmark=%d mark=%d\n",
                 &MY_CXT, cxix,
                 gimme == G_VOID ? "void" : gimme == G_ARRAY ? "list" : "scalar",
                 items, PL_stack_sp - PL_stack_base, *PL_markstack_ptr, mark);
@@ -1181,7 +1268,7 @@ STATIC void su_unwind(pTHX_ void *ud_) {
 # define SU_RETOP_LOOP(C) ((C)->blk_loop.last_op->op_next)
 #endif
 
-STATIC void su_yield(pTHX_ void *ud_) {
+static void su_yield(pTHX_ void *ud_) {
  dMY_CXT;
  PERL_CONTEXT *cx;
  const char   *which = ud_;
@@ -1203,12 +1290,12 @@ STATIC void su_yield(pTHX_ void *ud_) {
 #if SU_HAS_PERL(5, 10, 0)
    if (cxix > 0) {
     PERL_CONTEXT *prev = cx - 1;
-    U8 type = CxTYPE(prev);
-    if ((type == CXt_GIVEN || type == CXt_WHEN)
+    U8       prev_type = CxTYPE(prev);
+    if ((prev_type == CXt_GIVEN || prev_type == CXt_WHEN)
         && (prev->blk_oldcop == cx->blk_oldcop)) {
      cxix--;
      cx = prev;
-     if (type == CXt_GIVEN)
+     if (prev_type == CXt_GIVEN)
       goto cxt_given;
      else
       goto cxt_when;
@@ -1356,7 +1443,7 @@ cxt_when:
 #define SU_UPLEVEL_SAVE(f, t) STMT_START { sud->old_##f = PL_##f; PL_##f = (t); } STMT_END
 #define SU_UPLEVEL_RESTORE(f) STMT_START { PL_##f = sud->old_##f; } STMT_END
 
-STATIC su_uplevel_ud *su_uplevel_storage_new(pTHX_ I32 cxix) {
+static su_uplevel_ud *su_uplevel_storage_new(pTHX_ I32 cxix) {
 #define su_uplevel_storage_new(I) su_uplevel_storage_new(aTHX_ (I))
  su_uplevel_ud *sud;
  UV depth;
@@ -1381,21 +1468,21 @@ STATIC su_uplevel_ud *su_uplevel_storage_new(pTHX_ I32 cxix) {
  return sud;
 }
 
-STATIC void su_uplevel_storage_delete(pTHX_ su_uplevel_ud *sud) {
+#if SU_HAS_PERL(5, 13, 7)
+
+static void su_uplevel_storage_delete(pTHX_ su_uplevel_ud *sud) {
 #define su_uplevel_storage_delete(S) su_uplevel_storage_delete(aTHX_ (S))
  dMY_CXT;
 
  sud->tmp_uid_storage = MY_CXT.uid_storage;
  MY_CXT.uid_storage   = sud->old_uid_storage;
  {
-  su_uid **map;
-  UV  i, alloc;
+  su_uid *map;
+  STRLEN  i, alloc;
   map   = sud->tmp_uid_storage.map;
   alloc = sud->tmp_uid_storage.alloc;
-  for (i = 0; i < alloc; ++i) {
-   if (map[i])
-    map[i]->flags &= SU_UID_ACTIVE;
-  }
+  for (i = 0; i < alloc; ++i)
+   map[i].flags &= ~SU_UID_ACTIVE;
  }
  MY_CXT.uplevel_storage.top = sud->next;
 
@@ -1408,7 +1495,9 @@ STATIC void su_uplevel_storage_delete(pTHX_ su_uplevel_ud *sud) {
  }
 }
 
-STATIC int su_uplevel_goto_static(const OP *o) {
+#endif
+
+static int su_uplevel_goto_static(const OP *o) {
  for (; o; o = OpSIBLING(o)) {
   /* goto ops are unops with kids. */
   if (!(o->op_flags & OPf_KIDS))
@@ -1433,7 +1522,7 @@ STATIC int su_uplevel_goto_static(const OP *o) {
 
 #if SU_UPLEVEL_HIJACKS_RUNOPS
 
-STATIC int su_uplevel_goto_runops(pTHX) {
+static int su_uplevel_goto_runops(pTHX) {
 #define su_uplevel_goto_runops() su_uplevel_goto_runops(aTHX)
  register OP *op;
  dVAR;
@@ -1493,7 +1582,7 @@ done:
 
 #define su_at_underscore(C) PadARRAY(PadlistARRAY(CvPADLIST(C))[CvDEPTH(C)])[0]
 
-STATIC void su_uplevel_restore(pTHX_ void *sus_) {
+static void su_uplevel_restore(pTHX_ void *sus_) {
  su_uplevel_ud *sud = sus_;
  PERL_SI *cur = sud->old_curstackinfo;
  PERL_SI *si  = sud->si;
@@ -1655,7 +1744,7 @@ found_it:
  return;
 }
 
-STATIC CV *su_cv_clone(pTHX_ CV *proto, GV *gv) {
+static CV *su_cv_clone(pTHX_ CV *proto, GV *gv) {
 #define su_cv_clone(P, G) su_cv_clone(aTHX_ (P), (G))
  dVAR;
  CV *cv;
@@ -1716,7 +1805,7 @@ STATIC CV *su_cv_clone(pTHX_ CV *proto, GV *gv) {
  return cv;
 }
 
-STATIC I32 su_uplevel(pTHX_ CV *callback, I32 cxix, I32 args) {
+static I32 su_uplevel(pTHX_ CV *callback, I32 cxix, I32 args) {
 #define su_uplevel(C, I, A) su_uplevel(aTHX_ (C), (I), (A))
  su_uplevel_ud *sud;
  const PERL_CONTEXT *cx = cxstack + cxix;
@@ -1897,10 +1986,10 @@ STATIC I32 su_uplevel(pTHX_ CV *callback, I32 cxix, I32 args) {
 
 /* --- Unique context ID --------------------------------------------------- */
 
-STATIC su_uid *su_uid_storage_fetch(pTHX_ UV depth) {
+static su_uid *su_uid_storage_fetch(pTHX_ UV depth) {
 #define su_uid_storage_fetch(D) su_uid_storage_fetch(aTHX_ (D))
- su_uid **map, *uid;
- STRLEN alloc;
+ su_uid *map;
+ STRLEN  alloc;
  dMY_CXT;
 
  map   = MY_CXT.uid_storage.map;
@@ -1909,30 +1998,23 @@ STATIC su_uid *su_uid_storage_fetch(pTHX_ UV depth) {
  if (depth >= alloc) {
   STRLEN i;
 
-  Renew(map, depth + 1, su_uid *);
-  for (i = alloc; i <= depth; ++i)
-   map[i] = NULL;
+  Renew(map, depth + 1, su_uid);
+  for (i = alloc; i <= depth; ++i) {
+   map[i].seq   = 0;
+   map[i].flags = 0;
+  }
 
   MY_CXT.uid_storage.map   = map;
   MY_CXT.uid_storage.alloc = depth + 1;
  }
 
- uid = map[depth];
-
- if (!uid) {
-  Newx(uid, 1, su_uid);
-  uid->seq   = 0;
-  uid->flags = 0;
-  map[depth] = uid;
- }
-
  if (depth >= MY_CXT.uid_storage.used)
   MY_CXT.uid_storage.used = depth + 1;
 
- return uid;
+ return map + depth;
 }
 
-STATIC int su_uid_storage_check(pTHX_ UV depth, UV seq) {
+static int su_uid_storage_check(pTHX_ UV depth, UV seq) {
 #define su_uid_storage_check(D, S) su_uid_storage_check(aTHX_ (D), (S))
  su_uid *uid;
  dMY_CXT;
@@ -1940,47 +2022,35 @@ STATIC int su_uid_storage_check(pTHX_ UV depth, UV seq) {
  if (depth >= MY_CXT.uid_storage.used)
   return 0;
 
- uid = MY_CXT.uid_storage.map[depth];
-
- return uid && (uid->seq == seq) && (uid->flags & SU_UID_ACTIVE);
-}
-
-STATIC void su_uid_drop(pTHX_ void *ud_) {
- su_uid *uid = ud_;
+ uid = MY_CXT.uid_storage.map + depth;
 
uid->flags &= ~SU_UID_ACTIVE;
return (uid->seq == seq) && (uid->flags & SU_UID_ACTIVE);
 }
 
-STATIC void su_uid_bump(pTHX_ void *ud_) {
- su_ud_reap *ud  = ud_;
-
- SAVEDESTRUCTOR_X(su_uid_drop, ud->cb);
-}
-
-STATIC SV *su_uid_get(pTHX_ I32 cxix) {
+static SV *su_uid_get(pTHX_ I32 cxix) {
 #define su_uid_get(I) su_uid_get(aTHX_ (I))
  su_uid *uid;
- SV *uid_sv;
- UV depth;
+ SV     *uid_sv;
+ UV      depth;
 
  depth = su_uid_depth(cxix);
  uid   = su_uid_storage_fetch(depth);
 
  if (!(uid->flags & SU_UID_ACTIVE)) {
-  su_ud_reap *ud;
+  su_ud_uid *ud;
 
-  uid->seq = su_uid_seq_next(depth);
+  uid->seq    = su_uid_seq_next(depth);
   uid->flags |= SU_UID_ACTIVE;
 
-  Newx(ud, 1, su_ud_reap);
-  SU_UD_ORIGIN(ud)  = NULL;
-  SU_UD_HANDLER(ud) = su_uid_bump;
-  ud->cb = (SV *) uid;
+  Newx(ud, 1, su_ud_uid);
+  SU_UD_TYPE(ud) = SU_UD_TYPE_UID;
+  ud->idx        = depth;
   su_init(ud, cxix, SU_SAVE_DESTRUCTOR_SIZE);
  }
 
  uid_sv = sv_newmortal();
  sv_setpvf(uid_sv, "%"UVuf"-%"UVuf, depth, uid->seq);
+
  return uid_sv;
 }
 
@@ -1992,7 +2062,7 @@ STATIC SV *su_uid_get(pTHX_ I32 cxix) {
 
 #define IS_NUMBER_IN_UV 0x1
 
-STATIC int su_grok_number(pTHX_ const char *s, STRLEN len, UV *valuep) {
+static int su_grok_number(pTHX_ const char *s, STRLEN len, UV *valuep) {
 #define su_grok_number(S, L, VP) su_grok_number(aTHX_ (S), (L), (VP))
  STRLEN i;
  SV *tmpsv;
@@ -2013,7 +2083,7 @@ STATIC int su_grok_number(pTHX_ const char *s, STRLEN len, UV *valuep) {
 
 #endif /* !grok_number */
 
-STATIC int su_uid_validate(pTHX_ SV *uid) {
+static int su_uid_validate(pTHX_ SV *uid) {
 #define su_uid_validate(U) su_uid_validate(aTHX_ (U))
  const char *s;
  STRLEN len, p = 0;
@@ -2045,7 +2115,7 @@ STATIC int su_uid_validate(pTHX_ SV *uid) {
 /* Remove sequences of BLOCKs having DB for stash, followed by a SUB context
  * for the debugger callback. */
 
-STATIC I32 su_context_skip_db(pTHX_ I32 cxix) {
+static I32 su_context_skip_db(pTHX_ I32 cxix) {
 #define su_context_skip_db(C) su_context_skip_db(aTHX_ (C))
  I32 i;
 
@@ -2080,7 +2150,7 @@ STATIC I32 su_context_skip_db(pTHX_ I32 cxix) {
 }
 
 
-STATIC I32 su_context_normalize_up(pTHX_ I32 cxix) {
+static I32 su_context_normalize_up(pTHX_ I32 cxix) {
 #define su_context_normalize_up(C) su_context_normalize_up(aTHX_ (C))
  PERL_CONTEXT *cx;
 
@@ -2116,7 +2186,7 @@ STATIC I32 su_context_normalize_up(pTHX_ I32 cxix) {
  return cxix;
 }
 
-STATIC I32 su_context_normalize_down(pTHX_ I32 cxix) {
+static I32 su_context_normalize_down(pTHX_ I32 cxix) {
 #define su_context_normalize_down(C) su_context_normalize_down(aTHX_ (C))
  PERL_CONTEXT *next;
 
@@ -2154,7 +2224,7 @@ STATIC I32 su_context_normalize_down(pTHX_ I32 cxix) {
 
 #define su_context_here() su_context_normalize_up(su_context_skip_db(cxstack_ix))
 
-STATIC I32 su_context_gimme(pTHX_ I32 cxix) {
+static I32 su_context_gimme(pTHX_ I32 cxix) {
 #define su_context_gimme(C) su_context_gimme(aTHX_ (C))
  I32 i;
 
@@ -2196,20 +2266,81 @@ STATIC I32 su_context_gimme(pTHX_ I32 cxix) {
  return G_VOID;
 }
 
+/* --- Global setup/teardown ----------------------------------------------- */
+
+static VOL U32 su_initialized = 0;
+
+static void su_global_teardown(pTHX_ void *root) {
+ if (!su_initialized)
+  return;
+
+#if SU_MULTIPLICITY
+ if (aTHX != root)
+  return;
+#endif
+
+ SU_LOCK(&su_uid_seq_counter_mutex);
+ PerlMemShared_free(su_uid_seq_counter.seqs);
+ su_uid_seq_counter.size = 0;
+ SU_UNLOCK(&su_uid_seq_counter_mutex);
+
+ MUTEX_DESTROY(&su_uid_seq_counter_mutex);
+
+ su_initialized = 0;
+
+ return;
+}
+
+XS(XS_Scope__Upper_unwind);
+XS(XS_Scope__Upper_yield);
+XS(XS_Scope__Upper_leave);
+
+#if SU_HAS_PERL(5, 9, 0)
+# define SU_XS_FILE_TYPE const char
+#else
+# define SU_XS_FILE_TYPE char
+#endif
+
+static void su_global_setup(pTHX_ SU_XS_FILE_TYPE *file) {
+#define su_global_setup(F) su_global_setup(aTHX_ (F))
+ HV *stash;
+
+ if (su_initialized)
+  return;
+
+ MUTEX_INIT(&su_uid_seq_counter_mutex);
+
+ SU_LOCK(&su_uid_seq_counter_mutex);
+ su_uid_seq_counter.seqs = NULL;
+ su_uid_seq_counter.size = 0;
+ SU_UNLOCK(&su_uid_seq_counter_mutex);
+
+ stash = gv_stashpv(__PACKAGE__, 1);
+ newCONSTSUB(stash, "TOP",           newSViv(0));
+ newCONSTSUB(stash, "SU_THREADSAFE", newSVuv(SU_THREADSAFE));
+
+ newXSproto("Scope::Upper::unwind", XS_Scope__Upper_unwind, file, NULL);
+ newXSproto("Scope::Upper::yield",  XS_Scope__Upper_yield,  file, NULL);
+ newXSproto("Scope::Upper::leave",  XS_Scope__Upper_leave,  file, NULL);
+
+#if SU_MULTIPLICITY
+ call_atexit(su_global_teardown, aTHX);
+#else
+ call_atexit(su_global_teardown, NULL);
+#endif
+
+ su_initialized = 1;
+
+ return;
+}
+
 /* --- Interpreter setup/teardown ------------------------------------------ */
 
-STATIC void su_teardown(pTHX_ void *param) {
+static void su_local_teardown(pTHX_ void *param) {
  su_uplevel_ud *cur;
- su_uid **map;
  dMY_CXT;
 
- map = MY_CXT.uid_storage.map;
- if (map) {
-  STRLEN i;
-  for (i = 0; i < MY_CXT.uid_storage.used; ++i)
-   Safefree(map[i]);
-  Safefree(map);
- }
+ Safefree(MY_CXT.uid_storage.map);
 
  cur = MY_CXT.uplevel_storage.root;
  if (cur) {
@@ -2224,8 +2355,8 @@ STATIC void su_teardown(pTHX_ void *param) {
  return;
 }
 
-STATIC void su_setup(pTHX) {
-#define su_setup() su_setup(aTHX)
+static void su_local_setup(pTHX) {
+#define su_local_setup() su_local_setup(aTHX)
  MY_CXT_INIT;
 
  MY_CXT.stack_placeholder = NULL;
@@ -2255,7 +2386,7 @@ STATIC void su_setup(pTHX) {
  MY_CXT.uid_storage.used  = 0;
  MY_CXT.uid_storage.alloc = 0;
 
- call_atexit(su_teardown, NULL);
+ call_atexit(su_local_teardown, NULL);
 
  return;
 }
@@ -2298,8 +2429,6 @@ default_cx:                     \
 # define SU_INFO_COUNT 10
 #endif
 
-XS(XS_Scope__Upper_unwind); /* prototype to pass -Wmissing-prototypes */
-
 XS(XS_Scope__Upper_unwind) {
 #ifdef dVAR
  dVAR; dXSARGS;
@@ -2342,9 +2471,7 @@ XS(XS_Scope__Upper_unwind) {
  croak("Can't return outside a subroutine");
 }
 
-STATIC const char su_yield_name[] = "yield";
-
-XS(XS_Scope__Upper_yield); /* prototype to pass -Wmissing-prototypes */
+static const char su_yield_name[] = "yield";
 
 XS(XS_Scope__Upper_yield) {
 #ifdef dVAR
@@ -2373,9 +2500,7 @@ XS(XS_Scope__Upper_yield) {
  return;
 }
 
-STATIC const char su_leave_name[] = "leave";
-
-XS(XS_Scope__Upper_leave); /* prototype to pass -Wmissing-prototypes */
+static const char su_leave_name[] = "leave";
 
 XS(XS_Scope__Upper_leave) {
 #ifdef dVAR
@@ -2404,22 +2529,8 @@ PROTOTYPES: ENABLE
 
 BOOT:
 {
- HV *stash;
-
- MUTEX_INIT(&su_uid_seq_counter_mutex);
-
- su_uid_seq_counter.seqs = NULL;
- su_uid_seq_counter.size = 0;
-
- stash = gv_stashpv(__PACKAGE__, 1);
- newCONSTSUB(stash, "TOP",           newSViv(0));
- newCONSTSUB(stash, "SU_THREADSAFE", newSVuv(SU_THREADSAFE));
-
- newXSproto("Scope::Upper::unwind", XS_Scope__Upper_unwind, file, NULL);
- newXSproto("Scope::Upper::yield",  XS_Scope__Upper_yield,  file, NULL);
- newXSproto("Scope::Upper::leave",  XS_Scope__Upper_leave,  file, NULL);
-
- su_setup();
+ su_global_setup(file);
+ su_local_setup();
 }
 
 #if SU_THREADSAFE
@@ -2470,6 +2581,8 @@ PPCODE:
   --cxix;
   cxix = su_context_skip_db(cxix);
   cxix = su_context_normalize_up(cxix);
+ } else {
+  warn(su_stack_smash);
  }
  EXTEND(SP, 1);
  mPUSHi(cxix);
@@ -2495,6 +2608,7 @@ PPCODE:
     XSRETURN(1);
   }
  }
+ warn(su_no_such_target, "subroutine");
  XSRETURN_UNDEF;
 
 void
@@ -2515,6 +2629,7 @@ PPCODE:
     XSRETURN(1);
   }
  }
+ warn(su_no_such_target, "eval");
  XSRETURN_UNDEF;
 
 void
@@ -2526,8 +2641,10 @@ PPCODE:
  SU_GET_LEVEL(0, 0);
  cxix = su_context_here();
  while (--level >= 0) {
-  if (cxix <= 0)
+  if (cxix <= 0) {
+   warn(su_stack_smash);
    break;
+  }
   --cxix;
   cxix = su_context_skip_db(cxix);
   cxix = su_context_normalize_up(cxix);
@@ -2557,6 +2674,8 @@ PPCODE:
   }
  }
 done:
+ if (level >= 0)
+  warn(su_stack_smash);
  EXTEND(SP, 1);
  mPUSHi(cxix);
  XSRETURN(1);
@@ -2705,7 +2824,9 @@ PPCODE:
     goto context_info_warnings_off;
 #endif
   } else if (old_warnings == pWARN_NONE) {
+#if !SU_HAS_PERL(5, 17, 4)
 context_info_warnings_off:
+#endif
    mask = su_newmortal_pvn(WARN_NONEstring, WARNsize);
   } else if (old_warnings == pWARN_ALL) {
    HV *bits;
@@ -2753,9 +2874,10 @@ CODE:
  SU_GET_CONTEXT(1, 1, su_context_skip_db(cxstack_ix));
  cxix = su_context_normalize_down(cxix);
  Newx(ud, 1, su_ud_reap);
- SU_UD_ORIGIN(ud)  = NULL;
- SU_UD_HANDLER(ud) = su_reap;
- ud->cb = newSVsv(hook);
+ SU_UD_TYPE(ud) = SU_UD_TYPE_REAP;
+ ud->cb         = (SvROK(hook) && SvTYPE(SvRV(hook)) >= SVt_PVCV)
+                  ? SvRV(hook) : hook;
+ SvREFCNT_inc_simple_void(ud->cb);
  su_init(ud, cxix, SU_SAVE_DESTRUCTOR_SIZE);
 
 void
@@ -2769,8 +2891,7 @@ CODE:
  SU_GET_CONTEXT(2, 2, su_context_skip_db(cxstack_ix));
  cxix = su_context_normalize_down(cxix);
  Newx(ud, 1, su_ud_localize);
- SU_UD_ORIGIN(ud)  = NULL;
- SU_UD_HANDLER(ud) = su_localize;
+ SU_UD_TYPE(ud) = SU_UD_TYPE_LOCALIZE;
  size = su_ud_localize_init(ud, sv, val, NULL);
  su_init(ud, cxix, size);
 
@@ -2787,10 +2908,11 @@ CODE:
  SU_GET_CONTEXT(3, 3, su_context_skip_db(cxstack_ix));
  cxix = su_context_normalize_down(cxix);
  Newx(ud, 1, su_ud_localize);
- SU_UD_ORIGIN(ud)  = NULL;
- SU_UD_HANDLER(ud) = su_localize;
+ /* Initialize SU_UD_ORIGIN(ud) in case SU_UD_LOCALIZE_FREE(ud) needs it */
+ SU_UD_ORIGIN(ud) = NULL;
+ SU_UD_TYPE(ud)   = SU_UD_TYPE_LOCALIZE;
  size = su_ud_localize_init(ud, sv, val, elem);
- if (ud->type != SVt_PVAV && ud->type != SVt_PVHV) {
+ if (SU_UD_PRIVATE(ud) != SVt_PVAV && SU_UD_PRIVATE(ud) != SVt_PVHV) {
   SU_UD_LOCALIZE_FREE(ud);
   croak("Can't localize an element of something that isn't an array or a hash");
  }
@@ -2807,8 +2929,7 @@ CODE:
  SU_GET_CONTEXT(2, 2, su_context_skip_db(cxstack_ix));
  cxix = su_context_normalize_down(cxix);
  Newx(ud, 1, su_ud_localize);
- SU_UD_ORIGIN(ud)  = NULL;
- SU_UD_HANDLER(ud) = su_localize;
+ SU_UD_TYPE(ud) = SU_UD_TYPE_LOCALIZE;
  size = su_ud_localize_init(ud, sv, NULL, elem);
  su_init(ud, cxix, size);