+
+#ifdef PERL_RC_STACK
+
+/* this is a wrapper for all runops-style functions. It temporarily
+ * reifies the stack if necessary, then calls the real runops function
+ */
+int
+Perl_runops_wrap(pTHX)
+{
+ /* runops loops assume a ref-counted stack. If we have been called via a
+ * wrapper (pp_wrap or xs_wrap) with the top half of the stack not
+ * reference-counted, or with a non-real stack, temporarily convert it
+ * to reference-counted. This is because the si_stack_nonrc_base
+ * mechanism only allows a single split in the stack, not multiple
+ * stripes.
+ * At the end, we revert the stack (or part thereof) to non-refcounted
+ * to keep whoever our caller is happy.
+ *
+ * If what we call croaks, catch it, revert, then rethrow.
+ */
+
+ I32 cut; /* the cut point between refcnted and non-refcnted */
+ bool was_real = cBOOL(AvREAL(PL_curstack));
+ I32 old_base = PL_curstackinfo->si_stack_nonrc_base;
+
+ if (was_real && !old_base) {
+ PL_runops(aTHX); /* call the real loop */
+ return 0;
+ }
+
+ if (was_real) {
+ cut = old_base;
+ assert(PL_stack_base + cut <= PL_stack_sp + 1);
+ PL_curstackinfo->si_stack_nonrc_base = 0;
+ }
+ else {
+ assert(!old_base);
+ assert(!AvREIFY(PL_curstack));
+ AvREAL_on(PL_curstack);
+ /* skip the PL_sv_undef guard at PL_stack_base[0] but still
+ * signal adjusting may be needed on return by setting to a
+ * non-zero value - even if stack is empty */
+ cut = 1;
+ }
+
+ if (cut) {
+ SV **svp = PL_stack_base + cut;
+ while (svp <= PL_stack_sp) {
+ SvREFCNT_inc_simple_void(*svp);
+ svp++;
+ }
+ }
+
+ AV * old_curstack = PL_curstack;
+
+ /* run the real loop while catching exceptions */
+ dJMPENV;
+ int ret;
+ JMPENV_PUSH(ret);
+ switch (ret) {
+ case 0: /* normal return from JMPENV_PUSH */
+ cur_env.je_mustcatch = cur_env.je_prev->je_mustcatch;
+ PL_runops(aTHX); /* call the real loop */
+
+ revert:
+ /* revert stack back its non-ref-counted state */
+ assert(AvREAL(PL_curstack));
+
+ if (cut) {
+ /* undo the stack reification that took place at the beginning of
+ * this function */
+ if (UNLIKELY(!was_real))
+ AvREAL_off(PL_curstack);
+
+ SSize_t n = PL_stack_sp - (PL_stack_base + cut) + 1;
+ if (n > 0) {
+ /* we need to decrement the refcount of every SV from cut
+ * upwards; but this may prematurely free them, so
+ * mortalise them instead */
+ EXTEND_MORTAL(n);
+ for (SSize_t i = 0; i < n; i ++) {
+ SV* sv = PL_stack_base[cut + i];
+ if (sv)
+ PL_tmps_stack[++PL_tmps_ix] = sv;
+ }
+ }
+
+ I32 sp1 = PL_stack_sp - PL_stack_base + 1;
+ PL_curstackinfo->si_stack_nonrc_base =
+ old_base > sp1 ? sp1 : old_base;
+ }
+ break;
+
+ case 3: /* exception trapped by eval - stack only partially unwound */
+
+ /* if the exception has already unwound to before the current
+ * stack, no need to fix it up */
+ if (old_curstack == PL_curstack)
+ goto revert;
+ break;
+
+ default:
+ break;
+ }
+
+ JMPENV_POP;
+
+ if (ret) {
+ JMPENV_JUMP(ret); /* re-throw the exception */
+ NOT_REACHED; /* NOTREACHED */
+ }
+
+ return 0;
+}
+
+#endif
+
+/*
+ * ex: set ts=8 sts=4 sw=4 et:
+ */