diff --git a/Makefile b/Makefile
index be91dcc18afedb7fc1f2ec09b34c9514b1fcd8cd..f7fca75754bda43a7ad87b65bee31b807f5338ca 100644
--- a/Makefile
+++ b/Makefile
@@ -776,7 +776,7 @@ check$1 test$1: $(PYTHON_EXE_DEPS) pyston$1
 	@# we pass -I to cpython tests and skip failing ones because they are sloooow otherwise
 	$(PYTHON) $(TOOLS_DIR)/tester.py -R pyston$1 -j$(TEST_THREADS) -a=-S -k --exit-code-only --skip-failing -t50 $(TEST_DIR)/cpython $(ARGS)
 	$(PYTHON) $(TOOLS_DIR)/tester.py -R pyston$1 -j$(TEST_THREADS) -k -a=-S --exit-code-only --skip-failing -t600 $(TEST_DIR)/integration $(ARGS)
-	$(PYTHON) $(TOOLS_DIR)/tester.py -a=-X -R pyston$1 -j$(TEST_THREADS) -a=-n -a=-S -k $(TESTS_DIR) $(ARGS)
+	$(PYTHON) $(TOOLS_DIR)/tester.py -a=-X -R pyston$1 -j$(TEST_THREADS) -a=-n -a=-S -t50 -k $(TESTS_DIR) $(ARGS)
 	$(PYTHON) $(TOOLS_DIR)/tester.py -R pyston$1 -j$(TEST_THREADS) -a=-O -a=-S -k $(TESTS_DIR) $(ARGS)
 
 .PHONY: run$1 dbg$1
diff --git a/src/capi/typeobject.cpp b/src/capi/typeobject.cpp
index 6b76e182e8a6908fb59c647fa9cd991089aac883..b02e0f4628fc84efc4bf508445092f0143aa1597 100644
--- a/src/capi/typeobject.cpp
+++ b/src/capi/typeobject.cpp
@@ -3411,51 +3411,40 @@ static Box* tppProxyToTpCall(Box* self, CallRewriteArgs* rewrite_args, ArgPassSp
         paramspec.takes_kwargs = false;
     }
 
-    bool rewrite_success = false;
-    Box** oargs = NULL;
-    try {
-        rearrangeArguments(paramspec, NULL, "", NULL, rewrite_args, rewrite_success, argspec, arg1, arg2, arg3, args,
-                           oargs, keyword_names, NULL);
-    } catch (ExcInfo e) {
-        if (S == CAPI) {
-            setCAPIException(e);
-            return NULL;
-        } else
-            throw e;
-    }
+    auto continuation = [=](CallRewriteArgs* rewrite_args, Box* arg1, Box* arg2, Box* arg3, Box** args) {
+        if (!paramspec.takes_kwargs)
+            arg2 = NULL;
 
-    AUTO_DECREF(arg1);
-    if (!paramspec.takes_kwargs)
-        arg2 = NULL;
-    AUTO_XDECREF(arg2);
+        if (rewrite_args) {
+            if (!paramspec.takes_kwargs)
+                rewrite_args->arg2 = rewrite_args->rewriter->loadConst(0, Location::forArg(2));
+
+            // Currently, guard that the value of tp_call didn't change, and then
+            // emit a call to the current function address.
+            // It might be better to just load the current value of tp_call and call it
+            // (after guarding it's not null), or maybe not.  But the rewriter doesn't currently
+            // support calling a RewriterVar (can only call fixed function addresses).
+            RewriterVar* r_cls = rewrite_args->obj->getAttr(offsetof(Box, cls));
+            r_cls->addAttrGuard(offsetof(BoxedClass, tp_call), (intptr_t)self->cls->tp_call);
+
+            rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)self->cls->tp_call, rewrite_args->obj,
+                                                                 rewrite_args->arg1, rewrite_args->arg2);
+            rewrite_args->out_rtn->setType(RefType::OWNED);
+            if (S == CXX)
+                rewrite_args->rewriter->checkAndThrowCAPIException(rewrite_args->out_rtn);
+            rewrite_args->out_success = true;
+        }
 
-    if (!rewrite_success)
-        rewrite_args = NULL;
+        Box* r = self->cls->tp_call(self, arg1, arg2);
+        if (S == CXX && !r)
+            throwCAPIException();
+        return r;
+    };
 
-    if (rewrite_args) {
-        if (!paramspec.takes_kwargs)
-            rewrite_args->arg2 = rewrite_args->rewriter->loadConst(0, Location::forArg(2));
-
-        // Currently, guard that the value of tp_call didn't change, and then
-        // emit a call to the current function address.
-        // It might be better to just load the current value of tp_call and call it
-        // (after guarding it's not null), or maybe not.  But the rewriter doesn't currently
-        // support calling a RewriterVar (can only call fixed function addresses).
-        RewriterVar* r_cls = rewrite_args->obj->getAttr(offsetof(Box, cls));
-        r_cls->addAttrGuard(offsetof(BoxedClass, tp_call), (intptr_t)self->cls->tp_call);
-
-        rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)self->cls->tp_call, rewrite_args->obj,
-                                                             rewrite_args->arg1, rewrite_args->arg2);
-        rewrite_args->out_rtn->setType(RefType::OWNED);
-        if (S == CXX)
-            rewrite_args->rewriter->checkAndThrowCAPIException(rewrite_args->out_rtn);
-        rewrite_args->out_success = true;
-    }
-
-    Box* r = self->cls->tp_call(self, arg1, arg2);
-    if (!r && S == CXX)
-        throwCAPIException();
-    return r;
+    return callCXXFromStyle<S>([&]() {
+        return rearrangeArgumentsAndCall(paramspec, NULL, "", NULL, rewrite_args, argspec, arg1, arg2, arg3, args,
+                                         keyword_names, continuation);
+    });
 }
 
 extern "C" void PyType_RequestHcAttrs(PyTypeObject* cls, int offset) noexcept {
diff --git a/src/core/types.h b/src/core/types.h
index 5e10250b63480e3589a82bfa169a3d683756a827..6d70a060da1d519baf3444c110027cf4bf4ffa29 100644
--- a/src/core/types.h
+++ b/src/core/types.h
@@ -262,15 +262,16 @@ struct ParamReceiveSpec {
         assert(num_defaults <= MAX_DEFAULTS);
     }
 
-    bool operator==(ParamReceiveSpec rhs) {
+    bool operator==(ParamReceiveSpec rhs) const {
         return takes_varargs == rhs.takes_varargs && takes_kwargs == rhs.takes_kwargs
                && num_defaults == rhs.num_defaults && num_args == rhs.num_args;
     }
 
-    bool operator!=(ParamReceiveSpec rhs) { return !(*this == rhs); }
+    bool operator!=(ParamReceiveSpec rhs) const { return !(*this == rhs); }
 
-    int totalReceived() { return num_args + (takes_varargs ? 1 : 0) + (takes_kwargs ? 1 : 0); }
-    int kwargsIndex() { return num_args + (takes_varargs ? 1 : 0); }
+    int totalReceived() const { return num_args + (takes_varargs ? 1 : 0) + (takes_kwargs ? 1 : 0); }
+    int varargsIndex() const { return num_args; }
+    int kwargsIndex() const { return num_args + (takes_varargs ? 1 : 0); }
 };
 
 // Inline-caches contain fastpath code, and need to know that their fastpath is valid for a particular set
diff --git a/src/runtime/builtin_modules/builtins.cpp b/src/runtime/builtin_modules/builtins.cpp
index 06695cc9b5c85dc2e3772d77ede3372ea8f8cd6f..bb00bfdddae0ac969a8dbf6165106248dfa10a92 100644
--- a/src/runtime/builtin_modules/builtins.cpp
+++ b/src/runtime/builtin_modules/builtins.cpp
@@ -553,104 +553,103 @@ template <ExceptionStyle S>
 Box* getattrFuncInternal(BoxedFunctionBase* func, CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Box* arg1,
                          Box* arg2, Box* arg3, Box** args, const std::vector<BoxedString*>* keyword_names) {
     static Box* defaults[] = { NULL };
-    bool rewrite_success = false;
-    rearrangeArguments(ParamReceiveSpec(3, 1, false, false), NULL, "getattr", defaults, rewrite_args, rewrite_success,
-                       argspec, arg1, arg2, arg3, args, NULL, keyword_names, NULL);
-    if (!rewrite_success)
-        rewrite_args = NULL;
-
-    Box* obj = arg1;
-    Box* _str = arg2;
-    Box* default_value = arg3;
-
-    AUTO_DECREF(obj);
-    AUTO_DECREF(_str);
-    AUTO_XDECREF(default_value);
-
-    if (rewrite_args) {
-        // We need to make sure that the attribute string will be the same.
-        // Even though the passed string might not be the exact attribute name
-        // that we end up looking up (because we need to encode it or intern it),
-        // guarding on that object means (for strings and unicode) that the string
-        // value is fixed.
-        if (!PyString_CheckExact(_str) && !PyUnicode_CheckExact(_str))
-            rewrite_args = NULL;
-        else {
-            if (PyString_CheckExact(_str) && PyString_CHECK_INTERNED(_str) == SSTATE_INTERNED_IMMORTAL) {
-                // can avoid keeping the extra gc reference
-            } else {
-                rewrite_args->rewriter->addGCReference(_str);
-            }
 
-            rewrite_args->arg2->addGuard((intptr_t)arg2);
+    auto continuation = [=](CallRewriteArgs* rewrite_args, Box* arg1, Box* arg2, Box* arg3, Box** args) {
+        Box* obj = arg1;
+        Box* _str = arg2;
+        Box* default_value = arg3;
+
+        if (rewrite_args) {
+            // We need to make sure that the attribute string will be the same.
+            // Even though the passed string might not be the exact attribute name
+            // that we end up looking up (because we need to encode it or intern it),
+            // guarding on that object means (for strings and unicode) that the string
+            // value is fixed.
+            if (!PyString_CheckExact(_str) && !PyUnicode_CheckExact(_str))
+                rewrite_args = NULL;
+            else {
+                if (PyString_CheckExact(_str) && PyString_CHECK_INTERNED(_str) == SSTATE_INTERNED_IMMORTAL) {
+                    // can avoid keeping the extra gc reference
+                } else {
+                    rewrite_args->rewriter->addGCReference(_str);
+                }
+
+                rewrite_args->arg2->addGuard((intptr_t)arg2);
+            }
         }
-    }
 
-    _str = coerceUnicodeToStr<S>(_str);
-    if (S == CAPI && !_str)
-        return NULL;
+        _str = coerceUnicodeToStr<S>(_str);
 
-    if (!PyString_Check(_str)) {
-        Py_DECREF(_str);
-        if (S == CAPI) {
-            PyErr_SetString(TypeError, "getattr(): attribute name must be string");
-            return NULL;
-        } else
-            raiseExcHelper(TypeError, "getattr(): attribute name must be string");
-    }
+        if (S == CAPI && !_str)
+            return (Box*)NULL;
 
-    BoxedString* str = static_cast<BoxedString*>(_str);
-    if (!PyString_CHECK_INTERNED(str))
-        internStringMortalInplace(str);
-    AUTO_DECREF(str);
+        if (!PyString_Check(_str)) {
+            Py_DECREF(_str);
+            if (S == CAPI) {
+                PyErr_SetString(TypeError, "getattr(): attribute name must be string");
+                return (Box*)NULL;
+            } else
+                raiseExcHelper(TypeError, "getattr(): attribute name must be string");
+        }
 
-    Box* rtn;
-    RewriterVar* r_rtn;
-    if (rewrite_args) {
-        GetattrRewriteArgs grewrite_args(rewrite_args->rewriter, rewrite_args->arg1, rewrite_args->destination);
-        rtn = getattrInternal<CAPI>(obj, str, &grewrite_args);
-        // TODO could make the return valid in the NOEXC_POSSIBLE case via a helper
-        if (!grewrite_args.isSuccessful())
-            rewrite_args = NULL;
-        else {
-            ReturnConvention return_convention;
-            std::tie(r_rtn, return_convention) = grewrite_args.getReturn();
-
-            // Convert to NOEXC_POSSIBLE:
-            if (return_convention == ReturnConvention::NO_RETURN) {
-                return_convention = ReturnConvention::NOEXC_POSSIBLE;
-                r_rtn = rewrite_args->rewriter->loadConst(0);
-            } else if (return_convention == ReturnConvention::MAYBE_EXC) {
-                if (default_value)
-                    rewrite_args = NULL;
+        BoxedString* str = static_cast<BoxedString*>(_str);
+        if (!PyString_CHECK_INTERNED(str))
+            internStringMortalInplace(str);
+        AUTO_DECREF(str);
+
+        Box* rtn;
+        RewriterVar* r_rtn;
+        if (rewrite_args) {
+            GetattrRewriteArgs grewrite_args(rewrite_args->rewriter, rewrite_args->arg1, rewrite_args->destination);
+            rtn = getattrInternal<CAPI>(obj, str, &grewrite_args);
+            // TODO could make the return valid in the NOEXC_POSSIBLE case via a helper
+            if (!grewrite_args.isSuccessful())
+                rewrite_args = NULL;
+            else {
+                ReturnConvention return_convention;
+                std::tie(r_rtn, return_convention) = grewrite_args.getReturn();
+
+                // Convert to NOEXC_POSSIBLE:
+                if (return_convention == ReturnConvention::NO_RETURN) {
+                    return_convention = ReturnConvention::NOEXC_POSSIBLE;
+                    r_rtn = rewrite_args->rewriter->loadConst(0);
+                } else if (return_convention == ReturnConvention::MAYBE_EXC) {
+                    if (default_value)
+                        rewrite_args = NULL;
+                }
+                assert(!rewrite_args || return_convention == ReturnConvention::NOEXC_POSSIBLE
+                       || return_convention == ReturnConvention::HAS_RETURN
+                       || return_convention == ReturnConvention::CAPI_RETURN
+                       || (default_value == NULL && return_convention == ReturnConvention::MAYBE_EXC));
             }
-            assert(!rewrite_args || return_convention == ReturnConvention::NOEXC_POSSIBLE
-                   || return_convention == ReturnConvention::HAS_RETURN
-                   || return_convention == ReturnConvention::CAPI_RETURN
-                   || (default_value == NULL && return_convention == ReturnConvention::MAYBE_EXC));
+        } else {
+            rtn = getattrInternal<CAPI>(obj, str);
         }
-    } else {
-        rtn = getattrInternal<CAPI>(obj, str);
-    }
 
-    if (rewrite_args) {
-        assert(PyString_CHECK_INTERNED(str) == SSTATE_INTERNED_IMMORTAL);
-        RewriterVar* r_str = rewrite_args->rewriter->loadConst((intptr_t)str, Location::forArg(2));
-        RewriterVar* final_rtn
-            = rewrite_args->rewriter->call(false, (void*)getattrFuncHelper, r_rtn, rewrite_args->arg1, r_str,
-                                           rewrite_args->arg3)->setType(RefType::OWNED);
-        r_rtn->refConsumed();
+        if (rewrite_args) {
+            assert(PyString_CHECK_INTERNED(str) == SSTATE_INTERNED_IMMORTAL);
+            RewriterVar* r_str = rewrite_args->rewriter->loadConst((intptr_t)str, Location::forArg(2));
+            RewriterVar* final_rtn
+                = rewrite_args->rewriter->call(false, (void*)getattrFuncHelper, r_rtn, rewrite_args->arg1, r_str,
+                                               rewrite_args->arg3)->setType(RefType::OWNED);
+            r_rtn->refConsumed();
+
+            if (S == CXX)
+                rewrite_args->rewriter->checkAndThrowCAPIException(final_rtn);
+            rewrite_args->out_success = true;
+            rewrite_args->out_rtn = final_rtn;
+        }
 
-        if (S == CXX)
-            rewrite_args->rewriter->checkAndThrowCAPIException(final_rtn);
-        rewrite_args->out_success = true;
-        rewrite_args->out_rtn = final_rtn;
-    }
+        Box* r = getattrFuncHelper(rtn, obj, str, default_value);
+        if (S == CXX && !r)
+            throwCAPIException();
+        return r;
+    };
 
-    Box* r = getattrFuncHelper(rtn, obj, str, default_value);
-    if (S == CXX && !r)
-        throwCAPIException();
-    return r;
+    return callCXXFromStyle<S>([&]() {
+        return rearrangeArgumentsAndCall(ParamReceiveSpec(3, 1, false, false), NULL, "getattr", defaults, rewrite_args,
+                                         argspec, arg1, arg2, arg3, args, keyword_names, continuation);
+    });
 }
 
 Box* setattrFunc(Box* obj, Box* _str, Box* value) {
@@ -688,97 +687,96 @@ static Box* hasattrFuncHelper(STOLEN(Box*) return_val) noexcept {
 template <ExceptionStyle S>
 Box* hasattrFuncInternal(BoxedFunctionBase* func, CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Box* arg1,
                          Box* arg2, Box* arg3, Box** args, const std::vector<BoxedString*>* keyword_names) {
-    bool rewrite_success = false;
-    rearrangeArguments(ParamReceiveSpec(2, 0, false, false), NULL, "hasattr", NULL, rewrite_args, rewrite_success,
-                       argspec, arg1, arg2, arg3, args, NULL, keyword_names, NULL);
-    if (!rewrite_success)
-        rewrite_args = NULL;
-
-    AUTO_DECREF(arg1);
-    AUTO_DECREF(arg2);
-
-    Box* obj = arg1;
-    Box* _str = arg2;
-
-    if (rewrite_args) {
-        // We need to make sure that the attribute string will be the same.
-        // Even though the passed string might not be the exact attribute name
-        // that we end up looking up (because we need to encode it or intern it),
-        // guarding on that object means (for strings and unicode) that the string
-        // value is fixed.
-        if (!PyString_CheckExact(_str) && !PyUnicode_CheckExact(_str))
-            rewrite_args = NULL;
-        else {
-            if (PyString_CheckExact(_str) && PyString_CHECK_INTERNED(_str) == SSTATE_INTERNED_IMMORTAL) {
-                // can avoid keeping the extra gc reference
-            } else {
-                rewrite_args->rewriter->addGCReference(_str);
-            }
+    auto continuation = [=](CallRewriteArgs* rewrite_args, Box* arg1, Box* arg2, Box* arg3, Box** args) {
+        Box* obj = arg1;
+        Box* _str = arg2;
+
+        if (rewrite_args) {
+            // We need to make sure that the attribute string will be the same.
+            // Even though the passed string might not be the exact attribute name
+            // that we end up looking up (because we need to encode it or intern it),
+            // guarding on that object means (for strings and unicode) that the string
+            // value is fixed.
+            if (!PyString_CheckExact(_str) && !PyUnicode_CheckExact(_str))
+                rewrite_args = NULL;
+            else {
+                if (PyString_CheckExact(_str) && PyString_CHECK_INTERNED(_str) == SSTATE_INTERNED_IMMORTAL) {
+                    // can avoid keeping the extra gc reference
+                } else {
+                    rewrite_args->rewriter->addGCReference(_str);
+                }
 
-            rewrite_args->arg2->addGuard((intptr_t)arg2);
+                rewrite_args->arg2->addGuard((intptr_t)arg2);
+            }
         }
-    }
 
-    _str = coerceUnicodeToStr<S>(_str);
-    if (S == CAPI && !_str)
-        return NULL;
+        _str = coerceUnicodeToStr<S>(_str);
 
-    if (!PyString_Check(_str)) {
-        Py_DECREF(_str);
-        if (S == CAPI) {
-            PyErr_SetString(TypeError, "hasattr(): attribute name must be string");
-            return NULL;
-        } else
-            raiseExcHelper(TypeError, "hasattr(): attribute name must be string");
-    }
+        if (S == CAPI && !_str)
+            return (Box*)NULL;
 
-    BoxedString* str = static_cast<BoxedString*>(_str);
+        if (!PyString_Check(_str)) {
+            Py_DECREF(_str);
+            if (S == CAPI) {
+                PyErr_SetString(TypeError, "hasattr(): attribute name must be string");
+                return (Box*)NULL;
+            } else
+                raiseExcHelper(TypeError, "hasattr(): attribute name must be string");
+        }
 
-    if (!PyString_CHECK_INTERNED(str))
-        internStringMortalInplace(str);
-    AUTO_DECREF(str);
+        BoxedString* str = static_cast<BoxedString*>(_str);
 
-    Box* rtn;
-    RewriterVar* r_rtn;
-    if (rewrite_args) {
-        GetattrRewriteArgs grewrite_args(rewrite_args->rewriter, rewrite_args->arg1, rewrite_args->destination);
-        rtn = getattrInternal<CAPI>(obj, str, &grewrite_args);
-        if (!grewrite_args.isSuccessful())
-            rewrite_args = NULL;
-        else {
-            ReturnConvention return_convention;
-            std::tie(r_rtn, return_convention) = grewrite_args.getReturn();
-
-            // Convert to NOEXC_POSSIBLE:
-            if (return_convention == ReturnConvention::NO_RETURN) {
-                return_convention = ReturnConvention::NOEXC_POSSIBLE;
-                r_rtn = rewrite_args->rewriter->loadConst(0);
-            } else if (return_convention == ReturnConvention::MAYBE_EXC) {
+        if (!PyString_CHECK_INTERNED(str))
+            internStringMortalInplace(str);
+        AUTO_DECREF(str);
+
+        Box* rtn;
+        RewriterVar* r_rtn;
+        if (rewrite_args) {
+            GetattrRewriteArgs grewrite_args(rewrite_args->rewriter, rewrite_args->arg1, rewrite_args->destination);
+            rtn = getattrInternal<CAPI>(obj, str, &grewrite_args);
+            if (!grewrite_args.isSuccessful())
                 rewrite_args = NULL;
+            else {
+                ReturnConvention return_convention;
+                std::tie(r_rtn, return_convention) = grewrite_args.getReturn();
+
+                // Convert to NOEXC_POSSIBLE:
+                if (return_convention == ReturnConvention::NO_RETURN) {
+                    return_convention = ReturnConvention::NOEXC_POSSIBLE;
+                    r_rtn = rewrite_args->rewriter->loadConst(0);
+                } else if (return_convention == ReturnConvention::MAYBE_EXC) {
+                    rewrite_args = NULL;
+                }
+                assert(!rewrite_args || return_convention == ReturnConvention::NOEXC_POSSIBLE
+                       || return_convention == ReturnConvention::HAS_RETURN
+                       || return_convention == ReturnConvention::CAPI_RETURN);
             }
-            assert(!rewrite_args || return_convention == ReturnConvention::NOEXC_POSSIBLE
-                   || return_convention == ReturnConvention::HAS_RETURN
-                   || return_convention == ReturnConvention::CAPI_RETURN);
+        } else {
+            rtn = getattrInternal<CAPI>(obj, str);
         }
-    } else {
-        rtn = getattrInternal<CAPI>(obj, str);
-    }
 
-    if (rewrite_args) {
-        RewriterVar* final_rtn
-            = rewrite_args->rewriter->call(false, (void*)hasattrFuncHelper, r_rtn)->setType(RefType::OWNED);
-        r_rtn->refConsumed();
+        if (rewrite_args) {
+            RewriterVar* final_rtn
+                = rewrite_args->rewriter->call(false, (void*)hasattrFuncHelper, r_rtn)->setType(RefType::OWNED);
+            r_rtn->refConsumed();
 
-        if (S == CXX)
-            rewrite_args->rewriter->checkAndThrowCAPIException(final_rtn);
-        rewrite_args->out_success = true;
-        rewrite_args->out_rtn = final_rtn;
-    }
+            if (S == CXX)
+                rewrite_args->rewriter->checkAndThrowCAPIException(final_rtn);
+            rewrite_args->out_success = true;
+            rewrite_args->out_rtn = final_rtn;
+        }
 
-    Box* r = hasattrFuncHelper(rtn);
-    if (S == CXX && !r)
-        throwCAPIException();
-    return r;
+        Box* r = hasattrFuncHelper(rtn);
+        if (S == CXX && !r)
+            throwCAPIException();
+        return r;
+    };
+
+    return callCXXFromStyle<S>([&]() {
+        return rearrangeArgumentsAndCall(ParamReceiveSpec(2, 0, false, false), NULL, "hasattr", NULL, rewrite_args,
+                                         argspec, arg1, arg2, arg3, args, keyword_names, continuation);
+    });
 }
 
 Box* map2(Box* f, Box* container) {
diff --git a/src/runtime/capi.cpp b/src/runtime/capi.cpp
index 9af73cc7141535d4f3d891f9959c071f388dd90a..1a0dc47fad85872eccbb11b671f718e4e81baa0e 100644
--- a/src/runtime/capi.cpp
+++ b/src/runtime/capi.cpp
@@ -1797,85 +1797,82 @@ Box* BoxedCApiFunction::tppCall(Box* _self, CallRewriteArgs* rewrite_args, ArgPa
         // so we could just rearrangeArguments to the form that it wants and then call tp_new directly.
     }
 
-    bool rewrite_success = false;
-    rearrangeArguments(paramspec, NULL, self->method_def->ml_name, defaults, rewrite_args, rewrite_success, argspec,
-                       arg1, arg2, arg3, args, oargs, keyword_names, NULL);
-
-    AUTO_DECREF_ARGS(paramspec, arg1, arg2, arg3, oargs);
-
-    if (!rewrite_success)
-        rewrite_args = NULL;
-
-    RewriterVar* r_passthrough = NULL;
-    if (rewrite_args)
-        r_passthrough = rewrite_args->rewriter->loadConst((intptr_t)self->passthrough, Location::forArg(0));
-
-    Box* rtn;
-    if (flags == METH_VARARGS) {
-        rtn = (Box*)func(self->passthrough, arg1);
-        if (rewrite_args)
-            rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)func, r_passthrough, rewrite_args->arg1)
-                                        ->setType(RefType::OWNED);
-    } else if (flags == (METH_VARARGS | METH_KEYWORDS)) {
-        rtn = (Box*)((PyCFunctionWithKeywords)func)(self->passthrough, arg1, arg2);
-        if (rewrite_args)
-            rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)func, r_passthrough, rewrite_args->arg1,
-                                                                 rewrite_args->arg2)->setType(RefType::OWNED);
-    } else if (flags == METH_NOARGS) {
-        rtn = (Box*)func(self->passthrough, NULL);
-        if (rewrite_args)
-            rewrite_args->out_rtn
-                = rewrite_args->rewriter->call(true, (void*)func, r_passthrough,
-                                               rewrite_args->rewriter->loadConst(0, Location::forArg(1)))
-                      ->setType(RefType::OWNED);
-    } else if (flags == METH_O) {
-        rtn = (Box*)func(self->passthrough, arg1);
+    auto continuation = [=](CallRewriteArgs* rewrite_args, Box* arg1, Box* arg2, Box* arg3, Box** args) {
+        RewriterVar* r_passthrough = NULL;
         if (rewrite_args)
-            rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)func, r_passthrough, rewrite_args->arg1)
-                                        ->setType(RefType::OWNED);
-    } else if ((flags & ~(METH_O3 | METH_D3)) == 0) {
-        assert(paramspec.totalReceived() <= 3); // would need to pass through oargs
-        rtn = ((Box * (*)(Box*, Box*, Box*, Box*))func)(self->passthrough, arg1, arg2, arg3);
-        if (rewrite_args) {
-            if (paramspec.totalReceived() == 1)
+            r_passthrough = rewrite_args->rewriter->loadConst((intptr_t)self->passthrough, Location::forArg(0));
+
+        Box* rtn;
+        if (flags == METH_VARARGS) {
+            rtn = (Box*)func(self->passthrough, arg1);
+            if (rewrite_args)
                 rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)func, r_passthrough,
                                                                      rewrite_args->arg1)->setType(RefType::OWNED);
-            else if (paramspec.totalReceived() == 2)
+        } else if (flags == (METH_VARARGS | METH_KEYWORDS)) {
+            rtn = (Box*)((PyCFunctionWithKeywords)func)(self->passthrough, arg1, arg2);
+            if (rewrite_args)
                 rewrite_args->out_rtn
                     = rewrite_args->rewriter->call(true, (void*)func, r_passthrough, rewrite_args->arg1,
                                                    rewrite_args->arg2)->setType(RefType::OWNED);
-            else if (paramspec.totalReceived() == 3)
+        } else if (flags == METH_NOARGS) {
+            rtn = (Box*)func(self->passthrough, NULL);
+            if (rewrite_args)
                 rewrite_args->out_rtn
-                    = rewrite_args->rewriter->call(true, (void*)func, r_passthrough, rewrite_args->arg1,
-                                                   rewrite_args->arg2, rewrite_args->arg3)->setType(RefType::OWNED);
-            else
-                abort();
+                    = rewrite_args->rewriter->call(true, (void*)func, r_passthrough,
+                                                   rewrite_args->rewriter->loadConst(0, Location::forArg(1)))
+                          ->setType(RefType::OWNED);
+        } else if (flags == METH_O) {
+            rtn = (Box*)func(self->passthrough, arg1);
+            if (rewrite_args)
+                rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)func, r_passthrough,
+                                                                     rewrite_args->arg1)->setType(RefType::OWNED);
+        } else if ((flags & ~(METH_O3 | METH_D3)) == 0) {
+            assert(paramspec.totalReceived() <= 3); // would need to pass through oargs
+            rtn = ((Box * (*)(Box*, Box*, Box*, Box*))func)(self->passthrough, arg1, arg2, arg3);
+            if (rewrite_args) {
+                if (paramspec.totalReceived() == 1)
+                    rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)func, r_passthrough,
+                                                                         rewrite_args->arg1)->setType(RefType::OWNED);
+                else if (paramspec.totalReceived() == 2)
+                    rewrite_args->out_rtn
+                        = rewrite_args->rewriter->call(true, (void*)func, r_passthrough, rewrite_args->arg1,
+                                                       rewrite_args->arg2)->setType(RefType::OWNED);
+                else if (paramspec.totalReceived() == 3)
+                    rewrite_args->out_rtn
+                        = rewrite_args->rewriter->call(true, (void*)func, r_passthrough, rewrite_args->arg1,
+                                                       rewrite_args->arg2, rewrite_args->arg3)->setType(RefType::OWNED);
+                else
+                    abort();
+            }
+        } else if (flags == METH_OLDARGS) {
+            /* the really old style */
+
+            rewrite_args = NULL;
+
+            int size = PyTuple_GET_SIZE(arg1);
+            Box* arg = arg1;
+            if (size == 1)
+                arg = PyTuple_GET_ITEM(arg1, 0);
+            else if (size == 0)
+                arg = NULL;
+            rtn = func(self->passthrough, arg);
+        } else {
+            RELEASE_ASSERT(0, "0x%x", flags);
         }
-    } else if (flags == METH_OLDARGS) {
-        /* the really old style */
 
-        rewrite_args = NULL;
-
-        int size = PyTuple_GET_SIZE(arg1);
-        Box* arg = arg1;
-        if (size == 1)
-            arg = PyTuple_GET_ITEM(arg1, 0);
-        else if (size == 0)
-            arg = NULL;
-        rtn = func(self->passthrough, arg);
-    } else {
-        RELEASE_ASSERT(0, "0x%x", flags);
-    }
+        if (rewrite_args) {
+            rewrite_args->rewriter->checkAndThrowCAPIException(rewrite_args->out_rtn);
+            rewrite_args->out_success = true;
+        }
 
-    if (rewrite_args) {
-        rewrite_args->rewriter->checkAndThrowCAPIException(rewrite_args->out_rtn);
-        rewrite_args->out_success = true;
-    }
+        if (S == CXX && !rtn)
+            throwCAPIException();
+        assert(rtn && "should have set + thrown an exception!");
+        return rtn;
+    };
 
-    if (!rtn)
-        throwCAPIException();
-    assert(rtn && "should have set + thrown an exception!");
-    return rtn;
+    return rearrangeArgumentsAndCall(paramspec, NULL, self->method_def->ml_name, defaults, rewrite_args, argspec, arg1,
+                                     arg2, arg3, args, keyword_names, continuation);
 }
 
 /* extension modules might be compiled with GC support so these
diff --git a/src/runtime/descr.cpp b/src/runtime/descr.cpp
index 777ca32d7fda9557fe2b3ba8b3f02979f52e63b8..2eec041d87807d71305aeb5b29267798e6ae3751 100644
--- a/src/runtime/descr.cpp
+++ b/src/runtime/descr.cpp
@@ -311,15 +311,6 @@ Box* BoxedMethodDescriptor::tppCall(Box* _self, CallRewriteArgs* rewrite_args, A
         RELEASE_ASSERT(0, "0x%x", call_flags);
     }
 
-    Box** oargs = NULL;
-
-    Box* oargs_array[1] = { NULL };
-    if (paramspec.totalReceived() > 3) {
-        assert((paramspec.totalReceived() - 3) <= sizeof(oargs_array) / sizeof(oargs_array[0]));
-        oargs = oargs_array;
-    }
-    bool oargs_owned[1];
-
     bool arg1_class_guarded = false;
     if (rewrite_args && argspec.num_args >= 1) {
         // Try to do the guard before rearrangeArguments if possible:
@@ -327,105 +318,102 @@ Box* BoxedMethodDescriptor::tppCall(Box* _self, CallRewriteArgs* rewrite_args, A
         arg1_class_guarded = true;
     }
 
-    bool rewrite_success = false;
-    rearrangeArguments(paramspec, NULL, self->method->ml_name, defaults, rewrite_args, rewrite_success, argspec, arg1,
-                       arg2, arg3, args, oargs, keyword_names, oargs_owned);
-
-    AUTO_DECREF_ARGS(paramspec, arg1, arg2, arg3, oargs);
-
-    if (!rewrite_success)
-        rewrite_args = NULL;
-
-    if (ml_flags & METH_CLASS) {
-        rewrite_args = NULL;
-        if (!PyType_Check(arg1))
-            raiseExcHelper(TypeError, "descriptor '%s' requires a type but received a '%s'", self->method->ml_name,
-                           getFullTypeName(arg1).c_str());
-    } else {
-        if (!isSubclass(arg1->cls, self->type))
-            raiseExcHelper(TypeError, "descriptor '%s' requires a '%s' arg1 but received a '%s'", self->method->ml_name,
-                           getFullNameOfClass(self->type).c_str(), getFullTypeName(arg1).c_str());
-    }
-
-    if (rewrite_args && !arg1_class_guarded) {
-        rewrite_args->arg1->addAttrGuard(offsetof(Box, cls), (intptr_t)arg1->cls);
-    }
-
-    Box* rtn;
-    if (call_flags == METH_NOARGS) {
-        {
-            UNAVOIDABLE_STAT_TIMER(t0, "us_timer_in_builtins");
-            rtn = (Box*)self->method->ml_meth(arg1, NULL);
-        }
-        if (rewrite_args)
-            rewrite_args->out_rtn
-                = rewrite_args->rewriter->call(true, (void*)self->method->ml_meth, rewrite_args->arg1,
-                                               rewrite_args->rewriter->loadConst(0, Location::forArg(1)))
-                      ->setType(RefType::OWNED);
-    } else if (call_flags == METH_VARARGS) {
-        {
-            UNAVOIDABLE_STAT_TIMER(t0, "us_timer_in_builtins");
-            rtn = (Box*)self->method->ml_meth(arg1, arg2);
-        }
-        if (rewrite_args)
-            rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)self->method->ml_meth, rewrite_args->arg1,
-                                                                 rewrite_args->arg2)->setType(RefType::OWNED);
-    } else if (call_flags == (METH_VARARGS | METH_KEYWORDS)) {
-        {
-            UNAVOIDABLE_STAT_TIMER(t0, "us_timer_in_builtins");
-            rtn = (Box*)((PyCFunctionWithKeywords)self->method->ml_meth)(arg1, arg2, arg3);
-        }
-        if (rewrite_args)
-            rewrite_args->out_rtn
-                = rewrite_args->rewriter->call(true, (void*)self->method->ml_meth, rewrite_args->arg1,
-                                               rewrite_args->arg2, rewrite_args->arg3)->setType(RefType::OWNED);
-    } else if (call_flags == METH_O) {
-        {
-            UNAVOIDABLE_STAT_TIMER(t0, "us_timer_in_builtins");
-            rtn = (Box*)self->method->ml_meth(arg1, arg2);
+    auto continuation = [=](CallRewriteArgs* rewrite_args, Box* arg1, Box* arg2, Box* arg3, Box** args) {
+        if (ml_flags & METH_CLASS) {
+            rewrite_args = NULL;
+            if (!PyType_Check(arg1))
+                raiseExcHelper(TypeError, "descriptor '%s' requires a type but received a '%s'", self->method->ml_name,
+                               getFullTypeName(arg1).c_str());
+        } else {
+            if (!isSubclass(arg1->cls, self->type))
+                raiseExcHelper(TypeError, "descriptor '%s' requires a '%s' arg1 but received a '%s'",
+                               self->method->ml_name, getFullNameOfClass(self->type).c_str(),
+                               getFullTypeName(arg1).c_str());
         }
-        if (rewrite_args)
-            rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)self->method->ml_meth, rewrite_args->arg1,
-                                                                 rewrite_args->arg2)->setType(RefType::OWNED);
-    } else if ((call_flags & ~(METH_O3 | METH_D3)) == 0) {
-        {
-            UNAVOIDABLE_STAT_TIMER(t0, "us_timer_in_builtins");
-            rtn = ((Box * (*)(Box*, Box*, Box*, Box**))self->method->ml_meth)(arg1, arg2, arg3, oargs);
+
+        if (rewrite_args && !arg1_class_guarded) {
+            rewrite_args->arg1->addAttrGuard(offsetof(Box, cls), (intptr_t)arg1->cls);
         }
-        if (rewrite_args) {
-            if (paramspec.totalReceived() == 2)
+
+        Box* rtn;
+        if (call_flags == METH_NOARGS) {
+            {
+                UNAVOIDABLE_STAT_TIMER(t0, "us_timer_in_builtins");
+                rtn = (Box*)self->method->ml_meth(arg1, NULL);
+            }
+            if (rewrite_args)
+                rewrite_args->out_rtn
+                    = rewrite_args->rewriter->call(true, (void*)self->method->ml_meth, rewrite_args->arg1,
+                                                   rewrite_args->rewriter->loadConst(0, Location::forArg(1)))
+                          ->setType(RefType::OWNED);
+        } else if (call_flags == METH_VARARGS) {
+            {
+                UNAVOIDABLE_STAT_TIMER(t0, "us_timer_in_builtins");
+                rtn = (Box*)self->method->ml_meth(arg1, arg2);
+            }
+            if (rewrite_args)
                 rewrite_args->out_rtn
                     = rewrite_args->rewriter->call(true, (void*)self->method->ml_meth, rewrite_args->arg1,
                                                    rewrite_args->arg2)->setType(RefType::OWNED);
-            else if (paramspec.totalReceived() == 3)
+        } else if (call_flags == (METH_VARARGS | METH_KEYWORDS)) {
+            {
+                UNAVOIDABLE_STAT_TIMER(t0, "us_timer_in_builtins");
+                rtn = (Box*)((PyCFunctionWithKeywords)self->method->ml_meth)(arg1, arg2, arg3);
+            }
+            if (rewrite_args)
                 rewrite_args->out_rtn
                     = rewrite_args->rewriter->call(true, (void*)self->method->ml_meth, rewrite_args->arg1,
                                                    rewrite_args->arg2, rewrite_args->arg3)->setType(RefType::OWNED);
-            else if (paramspec.totalReceived() > 3)
+        } else if (call_flags == METH_O) {
+            {
+                UNAVOIDABLE_STAT_TIMER(t0, "us_timer_in_builtins");
+                rtn = (Box*)self->method->ml_meth(arg1, arg2);
+            }
+            if (rewrite_args)
                 rewrite_args->out_rtn
                     = rewrite_args->rewriter->call(true, (void*)self->method->ml_meth, rewrite_args->arg1,
-                                                   rewrite_args->arg2, rewrite_args->arg3,
-                                                   rewrite_args->args)->setType(RefType::OWNED);
-            else
-                abort();
+                                                   rewrite_args->arg2)->setType(RefType::OWNED);
+        } else if ((call_flags & ~(METH_O3 | METH_D3)) == 0) {
+            {
+                UNAVOIDABLE_STAT_TIMER(t0, "us_timer_in_builtins");
+                rtn = ((Box * (*)(Box*, Box*, Box*, Box**))self->method->ml_meth)(arg1, arg2, arg3, args);
+            }
+            if (rewrite_args) {
+                if (paramspec.totalReceived() == 2)
+                    rewrite_args->out_rtn
+                        = rewrite_args->rewriter->call(true, (void*)self->method->ml_meth, rewrite_args->arg1,
+                                                       rewrite_args->arg2)->setType(RefType::OWNED);
+                else if (paramspec.totalReceived() == 3)
+                    rewrite_args->out_rtn
+                        = rewrite_args->rewriter->call(true, (void*)self->method->ml_meth, rewrite_args->arg1,
+                                                       rewrite_args->arg2, rewrite_args->arg3)->setType(RefType::OWNED);
+                else if (paramspec.totalReceived() > 3)
+                    rewrite_args->out_rtn
+                        = rewrite_args->rewriter->call(true, (void*)self->method->ml_meth, rewrite_args->arg1,
+                                                       rewrite_args->arg2, rewrite_args->arg3,
+                                                       rewrite_args->args)->setType(RefType::OWNED);
+                else
+                    abort();
+            }
+        } else {
+            RELEASE_ASSERT(0, "0x%x", call_flags);
         }
-    } else {
-        RELEASE_ASSERT(0, "0x%x", call_flags);
-    }
 
-    if (!rtn)
-        throwCAPIException();
+        if (!rtn)
+            throwCAPIException();
 
-    if (rewrite_args && oargs)
-        decrefOargs(rewrite_args->args, oargs_owned, 1);
+        if (rewrite_args) {
+            rewrite_args->rewriter->checkAndThrowCAPIException(rewrite_args->out_rtn);
+            rewrite_args->out_success = true;
+        }
 
-    if (rewrite_args) {
-        rewrite_args->rewriter->checkAndThrowCAPIException(rewrite_args->out_rtn);
-        rewrite_args->out_success = true;
-    }
+        return rtn;
+    };
 
-    return rtn;
+    return rearrangeArgumentsAndCall(paramspec, NULL, self->method->ml_name, defaults, rewrite_args, argspec, arg1,
+                                     arg2, arg3, args, keyword_names, continuation);
 }
+
 static Box* methodGetName(Box* b, void*) {
     assert(b->cls == method_cls);
     const char* s = static_cast<BoxedMethodDescriptor*>(b)->method->ml_name;
@@ -646,74 +634,76 @@ Box* BoxedWrapperDescriptor::tppCall(Box* _self, CallRewriteArgs* rewrite_args,
         RELEASE_ASSERT(0, "%d", flags);
     }
 
-    Box** oargs = NULL;
-
-    bool rewrite_success = false;
-    rearrangeArguments(paramspec, NULL, self->wrapper->name.data(), NULL, rewrite_args, rewrite_success, argspec, arg1,
-                       arg2, arg3, args, oargs, keyword_names, NULL);
+    auto continuation = [=](CallRewriteArgs* rewrite_args, Box* arg1, Box* arg2, Box* arg3, Box** args) {
+#ifndef NDEBUG
+        if (paramspec.takes_varargs)
+            assert(arg2 && arg2->cls == tuple_cls);
+#endif
 
-    AUTO_DECREF_ARGS(paramspec, arg1, arg2, arg3, oargs);
+        Box* rtn;
+        if (flags == PyWrapperFlag_KEYWORDS) {
+            wrapperfunc_kwds wk = (wrapperfunc_kwds)wrapper;
+            rtn = (*wk)(arg1, arg2, self->wrapped, arg3);
 
-    if (paramspec.takes_varargs)
-        assert(arg2 && arg2->cls == tuple_cls);
-
-    if (!rewrite_success)
-        rewrite_args = NULL;
-
-    Box* rtn;
-    if (flags == PyWrapperFlag_KEYWORDS) {
-        wrapperfunc_kwds wk = (wrapperfunc_kwds)wrapper;
-        rtn = (*wk)(arg1, arg2, self->wrapped, arg3);
-
-        if (rewrite_args) {
-            auto rewriter = rewrite_args->rewriter;
-            rewrite_args->out_rtn = rewriter->call(true, (void*)wk, rewrite_args->arg1, rewrite_args->arg2,
-                                                   rewriter->loadConst((intptr_t)self->wrapped, Location::forArg(2)),
-                                                   rewrite_args->arg3)->setType(RefType::OWNED);
-            rewrite_args->rewriter->checkAndThrowCAPIException(rewrite_args->out_rtn);
-            rewrite_args->out_success = true;
-        }
-    } else if (flags == PyWrapperFlag_PYSTON || flags == 0) {
-        rtn = (*wrapper)(arg1, arg2, self->wrapped);
-
-        if (rewrite_args) {
-            auto rewriter = rewrite_args->rewriter;
-            rewrite_args->out_rtn = rewriter->call(true, (void*)wrapper, rewrite_args->arg1, rewrite_args->arg2,
-                                                   rewriter->loadConst((intptr_t)self->wrapped, Location::forArg(2)))
-                                        ->setType(RefType::OWNED);
-            rewrite_args->rewriter->checkAndThrowCAPIException(rewrite_args->out_rtn);
-            rewrite_args->out_success = true;
-        }
-    } else if (flags == PyWrapperFlag_1ARG) {
-        wrapperfunc_1arg wrapper_1arg = (wrapperfunc_1arg)wrapper;
-        rtn = (*wrapper_1arg)(arg1, self->wrapped);
-
-        if (rewrite_args) {
-            auto rewriter = rewrite_args->rewriter;
-            rewrite_args->out_rtn = rewriter->call(true, (void*)wrapper, rewrite_args->arg1,
-                                                   rewriter->loadConst((intptr_t)self->wrapped, Location::forArg(1)))
-                                        ->setType(RefType::OWNED);
-            rewrite_args->rewriter->checkAndThrowCAPIException(rewrite_args->out_rtn);
-            rewrite_args->out_success = true;
+            if (rewrite_args) {
+                auto rewriter = rewrite_args->rewriter;
+                rewrite_args->out_rtn
+                    = rewriter->call(true, (void*)wk, rewrite_args->arg1, rewrite_args->arg2,
+                                     rewriter->loadConst((intptr_t)self->wrapped, Location::forArg(2)),
+                                     rewrite_args->arg3)->setType(RefType::OWNED);
+                rewrite_args->rewriter->checkAndThrowCAPIException(rewrite_args->out_rtn);
+                rewrite_args->out_success = true;
+            }
+        } else if (flags == PyWrapperFlag_PYSTON || flags == 0) {
+            rtn = (*wrapper)(arg1, arg2, self->wrapped);
+
+            if (rewrite_args) {
+                auto rewriter = rewrite_args->rewriter;
+                rewrite_args->out_rtn
+                    = rewriter->call(true, (void*)wrapper, rewrite_args->arg1, rewrite_args->arg2,
+                                     rewriter->loadConst((intptr_t)self->wrapped, Location::forArg(2)))
+                          ->setType(RefType::OWNED);
+                rewrite_args->rewriter->checkAndThrowCAPIException(rewrite_args->out_rtn);
+                rewrite_args->out_success = true;
+            }
+        } else if (flags == PyWrapperFlag_1ARG) {
+            wrapperfunc_1arg wrapper_1arg = (wrapperfunc_1arg)wrapper;
+            rtn = (*wrapper_1arg)(arg1, self->wrapped);
+
+            if (rewrite_args) {
+                auto rewriter = rewrite_args->rewriter;
+                rewrite_args->out_rtn
+                    = rewriter->call(true, (void*)wrapper, rewrite_args->arg1,
+                                     rewriter->loadConst((intptr_t)self->wrapped, Location::forArg(1)))
+                          ->setType(RefType::OWNED);
+                rewrite_args->rewriter->checkAndThrowCAPIException(rewrite_args->out_rtn);
+                rewrite_args->out_success = true;
+            }
+        } else if (flags == PyWrapperFlag_2ARG) {
+            rtn = (*wrapper)(arg1, arg2, self->wrapped);
+
+            if (rewrite_args) {
+                auto rewriter = rewrite_args->rewriter;
+                rewrite_args->out_rtn
+                    = rewriter->call(true, (void*)wrapper, rewrite_args->arg1, rewrite_args->arg2,
+                                     rewriter->loadConst((intptr_t)self->wrapped, Location::forArg(2)))
+                          ->setType(RefType::OWNED);
+                rewrite_args->rewriter->checkAndThrowCAPIException(rewrite_args->out_rtn);
+                rewrite_args->out_success = true;
+            }
+        } else {
+            RELEASE_ASSERT(0, "%d", flags);
         }
-    } else if (flags == PyWrapperFlag_2ARG) {
-        rtn = (*wrapper)(arg1, arg2, self->wrapped);
 
-        if (rewrite_args) {
-            auto rewriter = rewrite_args->rewriter;
-            rewrite_args->out_rtn = rewriter->call(true, (void*)wrapper, rewrite_args->arg1, rewrite_args->arg2,
-                                                   rewriter->loadConst((intptr_t)self->wrapped, Location::forArg(2)))
-                                        ->setType(RefType::OWNED);
-            rewrite_args->rewriter->checkAndThrowCAPIException(rewrite_args->out_rtn);
-            rewrite_args->out_success = true;
-        }
-    } else {
-        RELEASE_ASSERT(0, "%d", flags);
-    }
+        if (S == CXX && !rtn)
+            throwCAPIException();
+        return rtn;
+    };
 
-    if (!rtn)
-        throwCAPIException();
-    return rtn;
+    return callCXXFromStyle<S>([&]() {
+        return rearrangeArgumentsAndCall(paramspec, NULL, self->wrapper->name.data(), NULL, rewrite_args, argspec, arg1,
+                                         arg2, arg3, args, keyword_names, continuation);
+    });
 }
 
 static Box* wrapperdescrGetDoc(Box* b, void*) {
diff --git a/src/runtime/generator.cpp b/src/runtime/generator.cpp
index 11aaf9218da656cd9819815e22bc281055c84440..08660d44c9e77cf28a45a0f2101389bc755bff62 100644
--- a/src/runtime/generator.cpp
+++ b/src/runtime/generator.cpp
@@ -429,11 +429,14 @@ extern "C" BoxedGenerator::BoxedGenerator(BoxedFunctionBase* function, Box* arg1
 #endif
 {
     Py_INCREF(function);
-    Py_XINCREF(arg1);
-    Py_XINCREF(arg2);
-    Py_XINCREF(arg3);
 
     int numArgs = function->md->numReceivedArgs();
+    if (numArgs > 0)
+        Py_XINCREF(arg1);
+    if (numArgs > 1)
+        Py_XINCREF(arg2);
+    if (numArgs > 2)
+        Py_XINCREF(arg3);
     if (numArgs > 3) {
         numArgs -= 3;
         this->args = new (numArgs) GCdArray();
@@ -631,15 +634,16 @@ static void generator_dealloc(BoxedGenerator* self) noexcept {
 
     int numArgs = self->function->md->numReceivedArgs();
     if (numArgs > 3) {
-        numArgs -= 3;
-        for (int i = 0; i < numArgs; i++) {
+        for (int i = 0; i < numArgs - 3; i++) {
             Py_CLEAR(self->args->elts[i]);
         }
     }
-
-    Py_CLEAR(self->arg1);
-    Py_CLEAR(self->arg2);
-    Py_CLEAR(self->arg3);
+    if (numArgs > 2)
+        Py_CLEAR(self->arg3);
+    if (numArgs > 1)
+        Py_CLEAR(self->arg2);
+    if (numArgs > 0)
+        Py_CLEAR(self->arg1);
 
     Py_CLEAR(self->function);
 
@@ -667,15 +671,16 @@ static int generator_traverse(BoxedGenerator* self, visitproc visit, void* arg)
 
     int numArgs = self->function->md->numReceivedArgs();
     if (numArgs > 3) {
-        numArgs -= 3;
-        for (int i = 0; i < numArgs; i++) {
+        for (int i = 0; i < numArgs - 3; i++) {
             Py_VISIT(self->args->elts[i]);
         }
     }
-
-    Py_VISIT(self->arg1);
-    Py_VISIT(self->arg2);
-    Py_VISIT(self->arg3);
+    if (numArgs > 2)
+        Py_VISIT(self->arg3);
+    if (numArgs > 1)
+        Py_VISIT(self->arg2);
+    if (numArgs > 0)
+        Py_VISIT(self->arg1);
 
     Py_VISIT(self->function);
 
diff --git a/src/runtime/objmodel.cpp b/src/runtime/objmodel.cpp
index 12b16d4395a77a0cf8ae32cb0144ca1420c0065d..26fa113d58583be1894c006c98902a08cf1f0aff 100644
--- a/src/runtime/objmodel.cpp
+++ b/src/runtime/objmodel.cpp
@@ -4032,10 +4032,34 @@ static llvm::StringRef getFunctionName(FunctionMetadata* f) {
     return "<unknown function>";
 }
 
-template <typename FuncNameCB>
+// A hacky little class that lets us save on some parameter size.
+// We want to pass enough information to rearrangeArgumentsAndCallInternal so that
+// it knows how to get the function name when it needs to (only when an exception is thrown),
+// and there are a couple different ways that it might need to do that.  So we pass through this
+// class that steals some bits from the pointers.
+class FuncNameGetter {
+private:
+    uint64_t data;
+
+    // Can't be the lsb since a const char* is only 1-byte aligned.
+    static constexpr uint64_t BIT = (1L << 63);
+
+public:
+    FuncNameGetter(const char* name) : data((uint64_t)name) { assert(!(data & BIT)); }
+    FuncNameGetter(FunctionMetadata* md) : data(((uint64_t)md) ^ BIT) { assert(data & BIT); }
+
+    const char* operator()() {
+        if (data & BIT) {
+            FunctionMetadata* md = (FunctionMetadata*)(data ^ BIT);
+            return getFunctionName(md).data();
+        } else {
+            return (const char*)data;
+        }
+    }
+};
 static int placeKeyword(const ParamNames* param_names, llvm::SmallVector<bool, 8>& params_filled, BoxedString* kw_name,
                         Box* kw_val, Box*& oarg1, Box*& oarg2, Box*& oarg3, Box** oargs, BoxedDict* okwargs,
-                        FuncNameCB func_name_cb) {
+                        FuncNameGetter func_name_cb) {
     assert(kw_val);
     assert(kw_name);
 
@@ -4120,25 +4144,10 @@ public:
     }
 };
 
-void decrefOargs(RewriterVar* oargs, bool* oargs_owned, int num_oargs) {
-    for (int i = 0; i < num_oargs; i++) {
-        if (oargs_owned[i]) {
-            oargs->deregisterOwnedAttr(i * sizeof(Box*));
-            oargs->getAttr(i * sizeof(Box*))->setType(RefType::OWNED);
-        }
-    }
-}
-
-template <Rewritable rewritable, typename FuncNameCB>
-void rearrangeArgumentsInternal(ParamReceiveSpec paramspec, const ParamNames* param_names, FuncNameCB func_name_cb,
-                                Box** defaults, _CallRewriteArgsBase* rewrite_args, bool& rewrite_success,
-                                ArgPassSpec argspec, Box*& oarg1, Box*& oarg2, Box*& oarg3, Box** args, Box** oargs,
-                                const std::vector<BoxedString*>* keyword_names, bool* oargs_owned) {
-    if (rewritable == NOT_REWRITABLE) {
-        assert(!rewrite_args);
-        rewrite_args = NULL;
-    }
-
+Box* rearrangeArgumentsAndCallInternal(ParamReceiveSpec paramspec, const ParamNames* param_names,
+                                       FuncNameGetter func_name_cb, Box** defaults, CallRewriteArgs* rewrite_args,
+                                       ArgPassSpec argspec, Box* arg1, Box* arg2, Box* arg3, Box** args,
+                                       const std::vector<BoxedString*>* keyword_names, FunctorPointer continuation) {
     /*
      * Procedure:
      * - First match up positional arguments; any extra go to varargs.  error if too many.
@@ -4147,44 +4156,17 @@ void rearrangeArgumentsInternal(ParamReceiveSpec paramspec, const ParamNames* pa
      * - error about missing parameters
      */
 
-    int num_output_args = paramspec.totalReceived();
-    int num_passed_args = argspec.totalPassed();
-
-    assert((oargs != NULL) == (num_output_args > 3));
     assert((defaults != NULL) == (paramspec.num_defaults != 0));
 
-    if (rewrite_args && oargs) {
-        assert(oargs_owned);
-        memset(oargs_owned, 0, (num_output_args - 3) * sizeof(bool));
-    }
-
-    if (rewrite_args) {
-        rewrite_success = false; // default case
-    }
-
-    auto propagate_args = [&]() {
-        if (num_output_args >= 1)
-            Py_XINCREF(oarg1);
-        if (num_output_args >= 2)
-            Py_XINCREF(oarg2);
-        if (num_output_args >= 3)
-            Py_XINCREF(oarg3);
-        if (num_output_args >= 3) {
-            memcpy(oargs, args, sizeof(Box*) * (num_output_args - 3));
-            for (int i = 0; i < num_output_args - 3; i++) {
-                Py_XINCREF(oargs[i]);
-            }
-        }
-    };
-
     // Super fast path:
     if (argspec.num_keywords == 0 && !argspec.has_starargs && !paramspec.takes_varargs && !argspec.has_kwargs
         && argspec.num_args == paramspec.num_args && !paramspec.takes_kwargs) {
-        rewrite_success = true;
-        propagate_args();
-        return;
+        return continuation(rewrite_args, arg1, arg2, arg3, args);
     }
 
+    int num_output_args = paramspec.totalReceived();
+    int num_passed_args = argspec.totalPassed();
+
     // Fast path: if it's a simple-enough call, we don't have to do anything special.  On a simple
     // django-admin test this covers something like 93% of all calls to callFunc.
     if (argspec.num_keywords == 0 && argspec.has_starargs == paramspec.takes_varargs && !argspec.has_kwargs
@@ -4195,7 +4177,7 @@ void rearrangeArgumentsInternal(ParamReceiveSpec paramspec, const ParamNames* pa
             assert(num_output_args == num_passed_args + 1);
             int idx = paramspec.kwargsIndex();
             assert(idx < 3);
-            getArg(idx, oarg1, oarg2, oarg3, NULL) = NULL; // pass NULL for kwargs
+            getArg(idx, arg1, arg2, arg3, NULL) = NULL; // pass NULL for kwargs
             if (rewrite_args) {
                 if (idx == 0)
                     rewrite_args->arg1 = rewrite_args->rewriter->loadConst(0)->setType(RefType::BORROWED);
@@ -4215,31 +4197,28 @@ void rearrangeArgumentsInternal(ParamReceiveSpec paramspec, const ParamNames* pa
         // received by the caller are always tuples).
         // This is why we can't pass kwargs here.
         if (argspec.has_starargs) {
-            Box* given_varargs = getArg(argspec.num_args + argspec.num_keywords, oarg1, oarg2, oarg3, args);
+            Box* given_varargs = getArg(argspec.num_args + argspec.num_keywords, arg1, arg2, arg3, args);
             if (given_varargs->cls == tuple_cls) {
                 if (rewrite_args) {
                     getArg(argspec.num_args + argspec.num_keywords, rewrite_args)
                         ->addAttrGuard(offsetof(Box, cls), (intptr_t)tuple_cls);
                 }
-                rewrite_success = true;
-                propagate_args();
-                return;
+                return continuation(rewrite_args, arg1, arg2, arg3, args);
             }
         } else {
-            rewrite_success = true;
-            propagate_args();
-            return;
+            return continuation(rewrite_args, arg1, arg2, arg3, args);
         }
     }
 
-    // Save the original values:
-    Box* arg1 = oarg1;
-    Box* arg2 = oarg2;
-    Box* arg3 = oarg3;
+    Box* oarg1, *oarg2, *oarg3;
     oarg1 = oarg2 = oarg3 = NULL;
 
-    if (oargs)
+    Box** oargs = NULL;
+    // TODO: could we reuse the args array?
+    if (num_output_args > 3) {
+        oargs = (Box**)alloca(sizeof(Box*) * (num_output_args - 3));
         memset(oargs, 0, sizeof(Box*) * (num_output_args - 3));
+    }
 
     // Clear any increfs we did for when we throw an exception:
     auto clear_refs = [&]() {
@@ -4268,9 +4247,6 @@ void rearrangeArgumentsInternal(ParamReceiveSpec paramspec, const ParamNames* pa
     // At this point we are not allowed to abort the rewrite any more, since we will start
     // modifying rewrite_args.
 
-    if (rewrite_args)
-        rewrite_success = true;
-
     if (rewrite_args) {
         // We might have trouble if we have more output args than input args,
         // such as if we need more space to pass defaults.
@@ -4346,6 +4322,7 @@ void rearrangeArgumentsInternal(ParamReceiveSpec paramspec, const ParamNames* pa
         unused_positional.push_back(PySequence_Fast_GET_ITEM(varargs, i));
     }
 
+    bool varargs_owned = false;
     if (paramspec.takes_varargs) {
         int varargs_idx = paramspec.num_args;
         if (rewrite_args) {
@@ -4354,7 +4331,6 @@ void rearrangeArgumentsInternal(ParamReceiveSpec paramspec, const ParamNames* pa
 
             RewriterVar* varargs_val;
             int varargs_size = unused_positional_rvars.size();
-            bool is_owned = false;
 
             if (varargs_size == 0) {
                 varargs_val
@@ -4374,7 +4350,7 @@ void rearrangeArgumentsInternal(ParamReceiveSpec paramspec, const ParamNames* pa
                 };
                 varargs_val = rewrite_args->rewriter->call(true, create_ptrs[varargs_size], unused_positional_rvars)
                                   ->setType(RefType::OWNED);
-                is_owned = true;
+                varargs_owned = true;
             }
 
             if (varargs_val) {
@@ -4385,13 +4361,12 @@ void rearrangeArgumentsInternal(ParamReceiveSpec paramspec, const ParamNames* pa
                 if (varargs_idx == 2)
                     rewrite_args->arg3 = varargs_val;
                 if (varargs_idx >= 3) {
-                    if (is_owned) {
+                    if (varargs_owned) {
                         rewrite_args->args->registerOwnedAttr((varargs_idx - 3) * sizeof(Box*));
 
                         rewrite_args->args->setAttr((varargs_idx - 3) * sizeof(Box*), varargs_val,
                                                     RewriterVar::SetattrType::HANDED_OFF);
 
-                        oargs_owned[varargs_idx - 3] = true;
                         varargs_val->refConsumed();
                     } else {
                         rewrite_args->args->setAttr((varargs_idx - 3) * sizeof(Box*), varargs_val);
@@ -4607,25 +4582,28 @@ void rearrangeArgumentsInternal(ParamReceiveSpec paramspec, const ParamNames* pa
         getArg(arg_idx, oarg1, oarg2, oarg3, oargs) = xincref(default_obj);
     }
 
-    cleanup.cancel();
+    if (rewrite_args && varargs_owned) {
+        Box* r = continuation(rewrite_args, oarg1, oarg2, oarg3, oargs);
+
+        int varargs_idx = paramspec.varargsIndex();
+        if (varargs_idx >= 3) {
+            rewrite_args->args->deregisterOwnedAttr((varargs_idx - 3) * sizeof(Box*));
+            rewrite_args->args->getAttr((varargs_idx - 3) * sizeof(Box*))->setType(RefType::OWNED);
+        }
+
+        return r;
+    } else {
+        return continuation(rewrite_args, oarg1, oarg2, oarg3, oargs);
+    }
 }
 
-template <Rewritable rewritable>
-void rearrangeArguments(ParamReceiveSpec paramspec, const ParamNames* param_names, const char* func_name,
-                        Box** defaults, _CallRewriteArgsBase* rewrite_args, bool& rewrite_success, ArgPassSpec argspec,
-                        Box*& oarg1, Box*& oarg2, Box*& oarg3, Box** args, Box** oargs,
-                        const std::vector<BoxedString*>* keyword_names, bool* oargs_owned) {
-    auto func = [func_name]() { return func_name; };
-    return rearrangeArgumentsInternal<rewritable>(paramspec, param_names, func, defaults, rewrite_args, rewrite_success,
-                                                  argspec, oarg1, oarg2, oarg3, args, oargs, keyword_names,
-                                                  oargs_owned);
-}
-template void rearrangeArguments<REWRITABLE>(ParamReceiveSpec, const ParamNames*, const char*, Box**,
-                                             _CallRewriteArgsBase*, bool&, ArgPassSpec, Box*&, Box*&, Box*&, Box**,
-                                             Box**, const std::vector<BoxedString*>*, bool*);
-template void rearrangeArguments<NOT_REWRITABLE>(ParamReceiveSpec, const ParamNames*, const char*, Box**,
-                                                 _CallRewriteArgsBase*, bool&, ArgPassSpec, Box*&, Box*&, Box*&, Box**,
-                                                 Box**, const std::vector<BoxedString*>*, bool*);
+Box* rearrangeArgumentsAndCall(ParamReceiveSpec paramspec, const ParamNames* param_names, const char* func_name,
+                               Box** defaults, CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Box* arg1, Box* arg2,
+                               Box* arg3, Box** args, const std::vector<BoxedString*>* keyword_names,
+                               FunctorPointer continuation) {
+    return rearrangeArgumentsAndCallInternal(paramspec, param_names, func_name, defaults, rewrite_args, argspec, arg1,
+                                             arg2, arg3, args, keyword_names, continuation);
+}
 
 static StatCounter slowpath_callfunc("slowpath_callfunc");
 template <ExceptionStyle S, Rewritable rewritable>
@@ -4658,50 +4636,57 @@ Box* callFunc(BoxedFunctionBase* func, CallRewriteArgs* rewrite_args, ArgPassSpe
         rewrite_args->rewriter->addDependenceOn(func->dependent_ics);
     }
 
-    Box** oargs = NULL;
-    bool* oargs_owned = NULL;
-    bool rewrite_success = false;
-
     int num_output_args = paramspec.totalReceived();
     int num_passed_args = argspec.totalPassed();
 
-    if (num_output_args > 3) {
-        int size = (num_output_args - 3) * sizeof(Box*);
-        oargs = (Box**)alloca(size);
-        memset(&oargs[0], 0, size);
+    bool rearrange_rewrite_failed = false;
+    auto orig_rewrite_args = rewrite_args;
+    auto continuation = [=, &rearrange_rewrite_failed](CallRewriteArgs* rewrite_args, Box* arg1, Box* arg2, Box* arg3,
+                                                       Box** args) {
+        if (orig_rewrite_args && !rewrite_args)
+            rearrange_rewrite_failed = true;
 
-        oargs_owned = (bool*)alloca((num_output_args - 3) * sizeof(bool));
-    }
+        BoxedClosure* closure = func->closure;
 
-    try {
-        auto func_name_cb = [md]() { return getFunctionName(md).data(); };
-        rearrangeArgumentsInternal<rewritable>(
-            paramspec, &md->param_names, func_name_cb, paramspec.num_defaults ? func->defaults->elts : NULL,
-            rewrite_args, rewrite_success, argspec, arg1, arg2, arg3, args, oargs, keyword_names, oargs_owned);
-    } catch (ExcInfo e) {
-        if (S == CAPI) {
-            setCAPIException(e);
-            return NULL;
-        } else
-            throw e;
-    }
+        // special handling for generators:
+        // the call to function containing a yield should just create a new generator object.
+        Box* res;
+        if (md->isGenerator()) {
+            res = createGenerator(func, arg1, arg2, arg3, args);
+
+            if (rewrite_args) {
+                RewriterVar* r_arg1 = num_output_args >= 1 ? rewrite_args->arg1 : rewrite_args->rewriter->loadConst(0);
+                RewriterVar* r_arg2 = num_output_args >= 2 ? rewrite_args->arg2 : rewrite_args->rewriter->loadConst(0);
+                RewriterVar* r_arg3 = num_output_args >= 3 ? rewrite_args->arg3 : rewrite_args->rewriter->loadConst(0);
+                RewriterVar* r_args = num_output_args >= 4 ? rewrite_args->args : rewrite_args->rewriter->loadConst(0);
+                rewrite_args->out_rtn
+                    = rewrite_args->rewriter->call(true, (void*)createGenerator, rewrite_args->obj, r_arg1, r_arg2,
+                                                   r_arg3, r_args)->setType(RefType::OWNED);
 
-    if (num_output_args < 1)
-        arg1 = NULL;
-    if (num_output_args < 2)
-        arg2 = NULL;
-    if (num_output_args < 3)
-        arg3 = NULL;
-    AUTO_XDECREF(arg1);
-    AUTO_XDECREF(arg2);
-    AUTO_XDECREF(arg3);
-    AUTO_XDECREF_ARRAY(oargs, num_output_args - 3);
+                rewrite_args->out_success = true;
+            }
+        } else {
+            res = callCLFunc<S, rewritable>(md, rewrite_args, num_output_args, closure, NULL, func->globals, arg1, arg2,
+                                            arg3, args);
+        }
 
-    if (rewrite_args && !rewrite_success) {
-// These are the cases that we weren't able to rewrite.
-// So instead, just rewrite them to be a call to callFunc, which helps a little bit.
-// TODO we should extract the rest of this function from the end of this block,
-// put it in a different function, and have the rewrites target that.
+        return res;
+    };
+
+    Box* r = callCXXFromStyle<S>([&] {
+        return rearrangeArgumentsAndCallInternal(paramspec, &md->param_names, md,
+                                                 paramspec.num_defaults ? func->defaults->elts : NULL, rewrite_args,
+                                                 argspec, arg1, arg2, arg3, args, keyword_names, continuation);
+    });
+    if (S == CAPI && !r)
+        return NULL;
+
+    if (rearrange_rewrite_failed) {
+// If we weren't able to rewrite, at least rewrite to callFunc, which helps a little bit.
+// Only do this if rearrangeArguments was the reason we couldn't rewrite, since it is one
+// of the few functions that is careful to not write anything out if it can't rewrite.
+//
+// TODO we should extract the continuation, put it in a different function, and have the rewrites target that.
 
 // Note(kmod): I tried moving this section to runtimeCallInternal, ie to the place that calls
 // callFunc.  The thought was that this would let us apply this same optimization to other
@@ -4760,34 +4745,7 @@ Box* callFunc(BoxedFunctionBase* func, CallRewriteArgs* rewrite_args, ArgPassSpe
         }
     }
 
-    BoxedClosure* closure = func->closure;
-
-    // special handling for generators:
-    // the call to function containing a yield should just create a new generator object.
-    Box* res;
-    if (md->isGenerator()) {
-        res = createGenerator(func, arg1, arg2, arg3, oargs);
-
-        if (rewrite_args) {
-            RewriterVar* r_arg1 = num_output_args >= 1 ? rewrite_args->arg1 : rewrite_args->rewriter->loadConst(0);
-            RewriterVar* r_arg2 = num_output_args >= 2 ? rewrite_args->arg2 : rewrite_args->rewriter->loadConst(0);
-            RewriterVar* r_arg3 = num_output_args >= 3 ? rewrite_args->arg3 : rewrite_args->rewriter->loadConst(0);
-            RewriterVar* r_args = num_output_args >= 4 ? rewrite_args->args : rewrite_args->rewriter->loadConst(0);
-            rewrite_args->out_rtn
-                = rewrite_args->rewriter->call(true, (void*)createGenerator, rewrite_args->obj, r_arg1, r_arg2, r_arg3,
-                                               r_args)->setType(RefType::OWNED);
-
-            rewrite_args->out_success = true;
-        }
-    } else {
-        res = callCLFunc<S, rewritable>(md, rewrite_args, num_output_args, closure, NULL, func->globals, arg1, arg2,
-                                        arg3, oargs);
-    }
-
-    if (rewrite_args && num_output_args > 3)
-        decrefOargs(rewrite_args->args, oargs_owned, num_output_args - 3);
-
-    return res;
+    return r;
 }
 
 template <ExceptionStyle S>
diff --git a/src/runtime/rewrite_args.h b/src/runtime/rewrite_args.h
index 912d032d7b0c583a7682d43fb7c7e1b46ab40476..b936ab00d3dc632e8a312a0948fc8195a8562d95 100644
--- a/src/runtime/rewrite_args.h
+++ b/src/runtime/rewrite_args.h
@@ -292,26 +292,28 @@ struct CompareRewriteArgs {
         : rewriter(rewriter), lhs(lhs), rhs(rhs), destination(destination), out_success(false), out_rtn(NULL) {}
 };
 
-// Passes the output arguments back through oarg.  Passes the rewrite success by setting rewrite_success.
-// Directly modifies rewrite_args args in place, but only if rewrite_success got set.
-// oargs needs to be pre-allocated by the caller, since it's assumed that they will want to use alloca.
+using FunctorPointer
+    = llvm::function_ref<Box*(CallRewriteArgs* rewrite_args, Box* arg1, Box* arg2, Box* arg3, Box** args)>;
+
+// rearrangeArgumentsAndCall maps from a given set of arguments (the structure specified by an ArgPassSpec) to the
+// parameter form than the receiving function expects (given by the ParamReceiveSpec).  After it does this, it will
+// call `continuation` and return the result.
+//
 // The caller is responsible for guarding for paramspec, argspec, param_names, and defaults.
-// TODO Fix this function's signature.  should we pass back out through args?  the common case is that they
-// match anyway.  Or maybe it should call a callback function, which could save on the common case.
 //
-// Reference semantics: takes borrowed references, and everything written out is an owned reference.
-template <Rewritable rewritable = REWRITABLE>
-void rearrangeArguments(ParamReceiveSpec paramspec, const ParamNames* param_names, const char* func_name,
-                        Box** defaults, _CallRewriteArgsBase* rewrite_args, bool& rewrite_success, ArgPassSpec argspec,
-                        Box*& arg1, Box*& arg2, Box*& arg3, Box** args, Box** oargs,
-                        const std::vector<BoxedString*>* keyword_names, bool* oargs_owned);
+// rearrangeArgumentsAndCall supports both CAPI- and CXX- exception styles for continuation, and will propagate them
+// back to the caller.  For now, it can also throw its own exceptions such as "not enough arguments", and will throw
+// them in the CXX style.
+Box* rearrangeArgumentsAndCall(ParamReceiveSpec paramspec, const ParamNames* param_names, const char* func_name,
+                               Box** defaults, CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Box* arg1, Box* arg2,
+                               Box* arg3, Box** args, const std::vector<BoxedString*>* keyword_names,
+                               FunctorPointer continuation);
 
 // new_args should be allocated by the caller if at least three args get passed in.
 // rewrite_args will get modified in place.
 ArgPassSpec bindObjIntoArgs(Box* bind_obj, RewriterVar* r_bind_obj, _CallRewriteArgsBase* rewrite_args,
                             ArgPassSpec argspec, Box*& arg1, Box*& arg2, Box*& arg3, Box** args, Box** new_args);
 
-void decrefOargs(RewriterVar* oargs, bool* oargs_owned, int oargs_size);
 } // namespace pyston
 
 #endif
diff --git a/src/runtime/types.cpp b/src/runtime/types.cpp
index 20988c4bafd6720c050d89360b11396101c32fc8..ba1d5e5052911cdc9bd1290a6111faa9f833a923 100644
--- a/src/runtime/types.cpp
+++ b/src/runtime/types.cpp
@@ -833,32 +833,25 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo
         assert(S == CXX && "implement me");
 
         ParamReceiveSpec paramspec(4, 3, false, false);
-        bool rewrite_success = false;
         static ParamNames param_names({ "", "string", "encoding", "errors" }, "", "");
         static Box* defaults[3] = { NULL, NULL, NULL };
-        Box* oargs[1] = { NULL };
-        bool oargs_owned[1];
 
-        rearrangeArguments(paramspec, &param_names, "unicode", defaults, rewrite_args, rewrite_success, argspec, arg1,
-                           arg2, arg3, args, oargs, keyword_names, oargs_owned);
-        assert(arg1 == cls);
+        auto continuation = [=](CallRewriteArgs* rewrite_args, Box* arg1, Box* arg2, Box* arg3, Box** args) {
+            assert(arg1 == cls);
 
-        AUTO_DECREF_ARGS(paramspec, arg1, arg2, arg3, oargs);
-
-        if (!rewrite_success)
-            rewrite_args = NULL;
-
-        if (rewrite_args) {
-            rewrite_args->out_rtn
-                = rewrite_args->rewriter->call(true, (void*)unicodeNewHelper, rewrite_args->arg1, rewrite_args->arg2,
-                                               rewrite_args->arg3, rewrite_args->args)->setType(RefType::OWNED);
+            if (rewrite_args) {
+                rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)unicodeNewHelper, rewrite_args->arg1,
+                                                                     rewrite_args->arg2, rewrite_args->arg3,
+                                                                     rewrite_args->args)->setType(RefType::OWNED);
 
-            decrefOargs(rewrite_args->args, oargs_owned, 1);
+                rewrite_args->out_success = true;
+            }
 
-            rewrite_args->out_success = true;
-        }
+            return unicodeNewHelper(static_cast<BoxedClass*>(arg1), arg2, arg3, args);
+        };
 
-        return unicodeNewHelper(cls, arg2, arg3, oargs);
+        return rearrangeArgumentsAndCall(paramspec, &param_names, "unicode", defaults, rewrite_args, argspec, arg1,
+                                         arg2, arg3, args, keyword_names, continuation);
     }
 
     if (cls->tp_new != object_cls->tp_new && cls->tp_new != slot_tp_new && cls->tp_new != BaseException->tp_new
@@ -870,27 +863,21 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo
         assert(S == CXX && "implement me");
 
         ParamReceiveSpec paramspec(1, false, true, true);
-        bool rewrite_success = false;
-        Box** oargs = NULL;
-        rearrangeArguments(paramspec, NULL, "", NULL, rewrite_args, rewrite_success, argspec, arg1, arg2, arg3, args,
-                           oargs, keyword_names, NULL);
-        assert(arg1 == cls);
 
-        AUTO_DECREF(arg1);
-        AUTO_DECREF(arg2);
-        AUTO_XDECREF(arg3);
-
-        if (!rewrite_success)
-            rewrite_args = NULL;
+        auto continuation = [=](CallRewriteArgs* rewrite_args, Box* arg1, Box* arg2, Box* arg3, Box** args) {
+            assert(arg1 == cls);
 
-        if (rewrite_args) {
-            rewrite_args->out_rtn
-                = rewrite_args->rewriter->call(true, (void*)cpythonTypeCall, rewrite_args->arg1, rewrite_args->arg2,
-                                               rewrite_args->arg3)->setType(RefType::OWNED);
-            rewrite_args->out_success = true;
-        }
+            if (rewrite_args) {
+                rewrite_args->out_rtn
+                    = rewrite_args->rewriter->call(true, (void*)cpythonTypeCall, rewrite_args->arg1, rewrite_args->arg2,
+                                                   rewrite_args->arg3)->setType(RefType::OWNED);
+                rewrite_args->out_success = true;
+            }
 
-        return cpythonTypeCall(cls, arg2, arg3);
+            return cpythonTypeCall(cls, arg2, arg3);
+        };
+        return rearrangeArgumentsAndCall(paramspec, NULL, "", NULL, rewrite_args, argspec, arg1, arg2, arg3, args,
+                                         keyword_names, continuation);
     }
 
     if (argspec.has_starargs || argspec.has_kwargs)
@@ -1196,27 +1183,6 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo
         // will be, whereas this block can't; I'm not sure how to merge the functionality.  That's
         // probably just evidence of the overall convolutedness of this function.
 
-        // TODO: instead of rewriting to the capi-format, maybe we should do the rearrangearguments
-        // inside the helper?
-        bool rewrite_success = false;
-        try {
-            rearrangeArguments(ParamReceiveSpec(1, 0, true, true), NULL, "", NULL, rewrite_args, rewrite_success,
-                               argspec, made, arg2, arg3, args, NULL, keyword_names, NULL);
-        } catch (ExcInfo e) {
-            if (S == CAPI) {
-                setCAPIException(e);
-                return NULL;
-            } else
-                throw e;
-        }
-
-        AUTO_DECREF(made);
-        AUTO_DECREF(arg2);
-        AUTO_XDECREF(arg3);
-
-        if (!rewrite_success)
-            rewrite_args = NULL;
-
         class InitHelper {
         public:
             static Box* call(STOLEN(Box*) made, BoxedClass* cls, Box* args, Box* kwargs) noexcept(S == CAPI) {
@@ -1234,17 +1200,26 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo
             }
         };
 
-        assert(arg2->cls == tuple_cls);
-        assert(!arg3 || arg3->cls == dict_cls);
+        // TODO: instead of rewriting to the capi-format, maybe we should do the rearrangearguments
+        // inside the helper?
+        auto continuation = [=](CallRewriteArgs* rewrite_args, Box* arg1, Box* arg2, Box* arg3, Box** args) {
+            assert(arg2->cls == tuple_cls);
+            assert(!arg3 || arg3->cls == dict_cls);
 
-        if (rewrite_args) {
-            rewrite_args->out_rtn
-                = rewrite_args->rewriter->call(true, (void*)InitHelper::call, r_made, r_ccls, rewrite_args->arg2,
-                                               rewrite_args->arg3)->setType(RefType::OWNED);
-            r_made->refConsumed();
-            rewrite_args->out_success = true;
-        }
-        return InitHelper::call(made, cls, arg2, arg3);
+            if (rewrite_args) {
+                rewrite_args->out_rtn
+                    = rewrite_args->rewriter->call(true, (void*)InitHelper::call, r_made, r_ccls, rewrite_args->arg2,
+                                                   rewrite_args->arg3)->setType(RefType::OWNED);
+                r_made->refConsumed();
+                rewrite_args->out_success = true;
+            }
+            return InitHelper::call(made, cls, arg2, arg3);
+        };
+
+        return callCXXFromStyle<S>([&]() {
+            return rearrangeArgumentsAndCall(ParamReceiveSpec(1, 0, true, true), NULL, "", NULL, rewrite_args, argspec,
+                                             made, arg2, arg3, args, keyword_names, continuation);
+        });
     }
 
     // If __new__ returns a subclass, supposed to call that subclass's __init__.
@@ -1343,30 +1318,35 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo
                 assert(tpinit == cls->tp_init);
             }
 
-            bool rewrite_success = false;
-            try {
-                rearrangeArguments(ParamReceiveSpec(1, 0, true, true), NULL, "", NULL, rewrite_args, rewrite_success,
-                                   argspec, made, arg2, arg3, args, NULL, keyword_names, NULL);
-            } catch (ExcInfo e) {
-                Py_DECREF(made);
-                if (S == CAPI) {
-                    setCAPIException(e);
-                    return NULL;
-                } else
-                    throw e;
-            }
+            auto continuation = [=](CallRewriteArgs* rewrite_args, Box* arg1, Box* arg2, Box* arg3, Box** args) {
+                assert(arg2->cls == tuple_cls);
+                assert(!arg3 || arg3->cls == dict_cls);
 
-            if (!rewrite_success)
-                rewrite_args = NULL;
+                int err = tpinit(made, arg2, arg3);
 
-            assert(arg2->cls == tuple_cls);
-            assert(!arg3 || arg3->cls == dict_cls);
+                if (err == -1)
+                    return (Box*)NULL;
+
+                if (rewrite_args) {
+                    auto r_err = rewrite_args->rewriter->call(true, (void*)tpinit, r_made, rewrite_args->arg2,
+                                                              rewrite_args->arg3);
+
+                    assert(S == CXX && "this need to be converted");
+                    rewrite_args->rewriter->checkAndThrowCAPIException(r_err, -1, assembler::MovType::L);
+                    rewrite_args->out_success = true;
+                }
+                return (Box*)1;
+            };
 
-            int err = tpinit(made, arg2, arg3);
-            Py_DECREF(made);
-            Py_DECREF(arg2);
-            Py_XDECREF(arg3);
-            if (err == -1) {
+            Box* _t;
+            try {
+                _t = rearrangeArgumentsAndCall(ParamReceiveSpec(1, 0, true, true), NULL, "", NULL, rewrite_args,
+                                               argspec, made, arg2, arg3, args, keyword_names, continuation);
+            } catch (ExcInfo e) {
+                setCAPIException(e);
+                _t = NULL;
+            }
+            if (_t == NULL) {
                 Py_DECREF(made);
                 if (S == CAPI)
                     return NULL;
@@ -1375,11 +1355,10 @@ static Box* typeCallInner(CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Bo
             }
 
             if (rewrite_args) {
-                auto r_err
-                    = rewrite_args->rewriter->call(true, (void*)tpinit, r_made, rewrite_args->arg2, rewrite_args->arg3);
-
-                assert(S == CXX && "this need to be converted");
-                rewrite_args->rewriter->checkAndThrowCAPIException(r_err, -1, assembler::MovType::L);
+                if (!rewrite_args->out_success)
+                    rewrite_args = NULL;
+                else
+                    rewrite_args->out_success = false;
             }
         }
     } else {
diff --git a/src/runtime/types.h b/src/runtime/types.h
index 51a402045192e31725e40c53bbdbabdfce2db902..9b975b4263cf13f789823b813d8c84299497ecac 100644
--- a/src/runtime/types.h
+++ b/src/runtime/types.h
@@ -413,41 +413,6 @@ public:
 #define AUTO_DECREF_ARRAY(x, size) AutoDecrefArray<false> CAT(_autodecref_, __LINE__)((x), (size))
 #define AUTO_XDECREF_ARRAY(x, size) AutoDecrefArray<true> CAT(_autodecref_, __LINE__)((x), (size))
 
-class AutoDecrefArgs {
-private:
-    int num_args;
-    Box* arg1, *arg2, *arg3;
-    Box** args;
-
-public:
-    AutoDecrefArgs(int num_args, Box* arg1, Box* arg2, Box* arg3, Box** args)
-        : num_args(num_args), arg1(arg1), arg2(arg2), arg3(arg3), args(args) {}
-    AutoDecrefArgs(ParamReceiveSpec paramspec, Box* arg1, Box* arg2, Box* arg3, Box** args)
-        : num_args(paramspec.totalReceived()), arg1(arg1), arg2(arg2), arg3(arg3), args(args) {}
-
-    ~AutoDecrefArgs() {
-        // TODO Minor optimization: only the last arg (kwargs) is allowed to be NULL.
-        switch (num_args) {
-            default:
-                for (int i = 0; i < num_args - 3; i++) {
-                    Py_XDECREF(args[i]);
-                }
-            case 3:
-                Py_XDECREF(arg3);
-            case 2:
-                Py_XDECREF(arg2);
-            case 1:
-                Py_XDECREF(arg1);
-            case 0:
-                break;
-        }
-    }
-};
-// Note: this captures the first three args by value (like AUTO_DECREF) but the array by reference.
-// You can also pass a ParamReceiveSpec instead of an int for num_args
-#define AUTO_DECREF_ARGS(num_args, arg1, arg2, arg3, args)                                                             \
-    AutoDecrefArgs CAT(_autodecref_, __LINE__)((num_args), (arg1), (arg2), (arg3), (args))
-
 template <typename B> B* incref(B* b) {
     Py_INCREF(b);
     return b;
@@ -457,6 +422,32 @@ template <typename B> B* xincref(B* b) {
     return b;
 }
 
+// Helper function: calls a CXX-style function from a templated function.  This is more efficient than the
+// easier-to-type version:
+//
+// try {
+//     return f();
+// } catch (ExcInfo e) {
+//     if (S == CAPI) {
+//         setCAPIException(e);
+//         return NULL;
+//     } else
+//         throw e;
+// }
+//
+// since this version does not need the try-catch block when called from a CXX-style function
+template <ExceptionStyle S, typename Functor> Box* callCXXFromStyle(Functor f) {
+    if (S == CAPI) {
+        try {
+            return f();
+        } catch (ExcInfo e) {
+            setCAPIException(e);
+            return NULL;
+        }
+    } else
+        return f();
+}
+
 //#define DISABLE_INT_FREELIST
 
 extern "C" int PyInt_ClearFreeList() noexcept;
diff --git a/test/tests/arg_refs.py b/test/tests/arg_refs.py
new file mode 100644
index 0000000000000000000000000000000000000000..787115aceffc28d851b95a5996f0cdcc1c82b00d
--- /dev/null
+++ b/test/tests/arg_refs.py
@@ -0,0 +1,10 @@
+# Tests to see if we add any extra refs to function arguments.
+
+import sys
+print sys.getrefcount(object())
+
+def f(o):
+    print sys.getrefcount(o)
+
+# This gives 3 for CPython and our interpreter, but 2 for the llvm tier:
+# f(object())