- /* When we reach this place, POPSUB has already been called (with our fake
- * argarray). GvAV(PL_defgv) points to the savearray (that is, what @_ was
- * before uplevel). argarray is either the fake AV we created in su_uplevel()
- * or some empty replacement POPSUB creates when @_ is reified. In both cases
- * we have to destroy it before the context stack is swapped back to its
- * original state. */
- SvREFCNT_dec(sud->fake_argarray);
-
- /* PUSHSUB was exerted with the original callback, but after calling
- * pp_entersub() we hijacked the blk_sub.cv member of the fresh sub context
- * with the renamed CV. Thus POPSUB and LEAVESUB applied to this CV, not the
- * original. Repair this imbalance right now. */
- if (!(CvDEPTH(sud->callback) = sud->callback_depth))
- LEAVESUB(sud->callback);
-
- /* Free the renamed cv. */
+ if (sud->callback) {
+ PERL_CONTEXT *cx = cxstack + sud->cxix;
+ AV *argarray = MUTABLE_AV(su_at_underscore(sud->callback));
+
+ /* We have to fix the pad entry for @_ in the original callback because it
+ * may have been reified. */
+ if (AvREAL(argarray)) {
+ const I32 fill = AvFILLp(argarray);
+ SvREFCNT_dec(argarray);
+ argarray = newAV();
+ AvREAL_off(argarray);
+ AvREIFY_on(argarray);
+ av_extend(argarray, fill);
+ su_at_underscore(sud->callback) = MUTABLE_SV(argarray);
+ } else {
+ CLEAR_ARGARRAY(argarray);
+ }
+
+ /* If the old cv member is our renamed CV, it means that this place has been
+ * reached without a goto() happening, and the old argarray member is
+ * actually our fake argarray. Destroy it properly in that case. */
+ if (cx->blk_sub.cv == sud->renamed) {
+ SvREFCNT_dec(cx->blk_sub.argarray);
+ cx->blk_sub.argarray = argarray;
+ }
+
+ CvDEPTH(sud->callback)--;
+ SvREFCNT_dec(sud->callback);
+ }
+
+ /* Free the renamed CV. We must do it ourselves so that we can force the
+ * depth to be 0, or perl would complain about it being "still in use".
+ * But we *know* that it cannot be so. */