Box* generatorThrow(Box* s, BoxedClass* exc_cls, Box* exc_val = nullptr, Box** args = nullptr) noexcept(S == CAPI) { assert(s->cls == generator_cls); BoxedGenerator* self = static_cast<BoxedGenerator*>(s); if (self->iterated_from__hasnext__ && !self->entryExited) Py_FatalError(".throw called on generator last advanced with __hasnext__"); Box* exc_tb = args ? args[0] : nullptr; if (exc_tb && exc_tb != Py_None && !PyTraceBack_Check(exc_tb)) { if (S == CAPI) { PyErr_SetString(TypeError, "throw() third argument must be a traceback object"); return NULL; } raiseExcHelper(TypeError, "throw() third argument must be a traceback object"); } if (!exc_val) exc_val = Py_None; if (!exc_tb) exc_tb = Py_None; ExcInfo exc_info = excInfoForRaise(incref(exc_cls), incref(exc_val), incref(exc_tb)); if (self->entryExited) { if (S == CAPI) { setCAPIException(exc_info); return NULL; } throw exc_info; } self->exception = exc_info; return generatorSend<S>(self, Py_None); }
Box* super_getattro(Box* _s, Box* _attr) noexcept { try { return superGetattribute(_s, _attr); } catch (ExcInfo e) { setCAPIException(e); return NULL; } }
extern "C" PyObject* PySeqIter_New(PyObject* seq) noexcept { try { return new BoxedSeqIter(seq, 0); } catch (ExcInfo e) { setCAPIException(e); return NULL; } }
extern "C" PyObject* PyFrozenSet_New(PyObject* iterable) noexcept { try { return runtimeCall(frozenset_cls, ArgPassSpec(iterable ? 1 : 0), iterable, NULL, NULL, NULL, NULL); } catch (ExcInfo e) { setCAPIException(e); return NULL; } }
extern "C" PyObject* PyDict_Copy(PyObject* o) noexcept { RELEASE_ASSERT(PyDict_Check(o), ""); try { return dictCopy(static_cast<BoxedDict*>(o)); } catch (ExcInfo e) { setCAPIException(e); return NULL; } }
extern "C" PyObject* _PyList_Extend(PyListObject* self, PyObject* b) noexcept { BoxedList* l = (BoxedList*)self; assert(l->cls == list_cls); try { return listIAdd(l, b); } catch (ExcInfo e) { setCAPIException(e); return NULL; } }
extern "C" PyObject* PySet_New(PyObject* iterable) noexcept { if (!iterable) return new BoxedSet(); // Fast path for empty set. try { return runtimeCall(set_cls, ArgPassSpec(iterable ? 1 : 0), iterable, NULL, NULL, NULL, NULL); } catch (ExcInfo e) { setCAPIException(e); return NULL; } }
extern "C" PyObject* PyObject_GetAttrString(PyObject* o, const char* attr) noexcept { // TODO do something like this? not sure if this is safe; will people expect that calling into a known function // won't end up doing a GIL check? // threading::GLDemoteRegion _gil_demote; try { return getattr(o, attr); } catch (ExcInfo e) { setCAPIException(e); return NULL; } }
static PyObject* dict_helper(PyObject* mp, std::function<Box*(BoxedDict*)> f) noexcept { if (mp == NULL || !PyDict_Check(mp)) { PyErr_BadInternalCall(); return NULL; } try { return f(static_cast<BoxedDict*>(mp)); } catch (ExcInfo e) { setCAPIException(e); return NULL; } }
extern "C" PyObject* _PyObject_Str(PyObject* v) noexcept { if (v == NULL) return boxStrConstant("<NULL>"); if (v->cls == str_cls) return v; try { return str(v); } catch (ExcInfo e) { setCAPIException(e); return NULL; } }
// Note: PySet_Add is allowed to apply to frozenset objects, though CPython has // an check to make sure the refcount is 1. // for example, the marshal library uses this to construct frozenset objects. extern "C" int PySet_Add(PyObject* set, PyObject* key) noexcept { if (!PyAnySet_Check(set)) { PyErr_BadInternalCall(); return -1; } try { _setAdd(static_cast<BoxedSet*>(set), key); return 0; } catch (ExcInfo e) { setCAPIException(e); return -1; } }
template <ExceptionStyle S> static Box* generatorSend(Box* s, Box* v) noexcept(S == CAPI) { assert(s->cls == generator_cls); BoxedGenerator* self = static_cast<BoxedGenerator*>(s); if (self->iterated_from__hasnext__) Py_FatalError(".throw called on generator last advanced with __hasnext__"); bool exc = generatorSendInternal<S>(self, v); if (S == CAPI && exc) return NULL; // throw StopIteration if the generator exited if (self->entryExited) { // But we can't just create a new exc because the generator may have exited because of an explicit // 'raise StopIterationSubClass, "test"' statement and we can't replace it with the generic StopIteration // exception. // That's why we set inside 'generatorSendInternal()' 'self->exception' to the raised StopIteration exception or // create a new one if the generator exited implicit. // CPython raises the custom exception just once, on the next generator 'next' it will we a normal StopIteration // exc. assert(self->exception.type == NULL || self->exception.matches(StopIteration)); ExcInfo old_exc = self->exception; // Clear the exception for GC purposes: self->exception = ExcInfo(nullptr, nullptr, nullptr); if (old_exc.type == NULL) { if (S == CAPI) { PyErr_SetObject(StopIteration, Py_None); return NULL; } else raiseExcHelper(StopIteration, (const char*)nullptr); } else { if (S == CAPI) { setCAPIException(old_exc); return NULL; } else throw old_exc; } } Box* rtn = self->returnValue; assert(rtn); self->returnValue = NULL; return rtn; }
extern "C" int PyList_SetSlice(PyObject* a, Py_ssize_t ilow, Py_ssize_t ihigh, PyObject* v) noexcept { if (!PyList_Check(a)) { PyErr_BadInternalCall(); return -1; } BoxedList* l = (BoxedList*)a; ASSERT(l->cls == list_cls, "%s", l->cls->tp_name); try { if (v) listSetitemSlice(l, new BoxedSlice(boxInt(ilow), boxInt(ihigh), None), v); else listDelitemSlice(l, new BoxedSlice(boxInt(ilow), boxInt(ihigh), None)); return 0; } catch (ExcInfo e) { setCAPIException(e); return -1; } }
template <ExceptionStyle S> Box* tupleGetitem(BoxedTuple* self, Box* slice) { if (S == CAPI) { try { return tupleGetitem<CXX>(self, slice); } catch (ExcInfo e) { setCAPIException(e); return NULL; } } assert(isSubclass(self->cls, tuple_cls)); if (PyIndex_Check(slice)) { Py_ssize_t i = PyNumber_AsSsize_t(slice, PyExc_IndexError); if (i == -1 && PyErr_Occurred()) throwCAPIException(); return tupleGetitemUnboxed(self, i); } else if (slice->cls == slice_cls) return tupleGetitemSlice(self, static_cast<BoxedSlice*>(slice)); else raiseExcHelper(TypeError, "tuple indices must be integers, not %s", getTypeName(slice)); }
extern "C" unsigned long PyLong_AsUnsignedLong(PyObject* vv) noexcept { assert(vv); if (vv->cls == int_cls) { long val = PyInt_AsLong(vv); if (val < 0) { PyErr_SetString(PyExc_OverflowError, "can't convert negative value " "to unsigned long"); return (unsigned long)-1; } return val; } RELEASE_ASSERT(PyLong_Check(vv), ""); BoxedLong* l = static_cast<BoxedLong*>(vv); try { return asUnsignedLong(l); } catch (ExcInfo e) { setCAPIException(e); return -1; } }
// called from both generatorHasNext and generatorSend/generatorNext (but only if generatorHasNext hasn't been called) template <ExceptionStyle S> static bool generatorSendInternal(BoxedGenerator* self, Box* v) noexcept(S == CAPI) { STAT_TIMER(t0, "us_timer_generator_switching", 0); if (!self->returnContext && v != Py_None) { if (S == CAPI) { PyErr_SetString(TypeError, "can't send non-None value to a just-started generator"); return true; } else raiseExcHelper(TypeError, "can't send non-None value to a just-started generator"); } if (self->running) { if (S == CAPI) { PyErr_SetString(ValueError, "generator already executing"); return true; } else raiseExcHelper(ValueError, "generator already executing"); } // check if the generator already exited if (self->entryExited) { freeGeneratorStack(self); if (S == CAPI) { PyErr_SetObject(StopIteration, Py_None); return true; } else raiseExcHelper(StopIteration, (const char*)nullptr); } assert(!self->returnValue); self->returnValue = incref(v); self->running = true; #if STAT_TIMERS if (!self->prev_stack) self->prev_stack = StatTimer::createStack(self->my_timer); else self->prev_stack = StatTimer::swapStack(self->prev_stack); #endif auto* top_caller_frame_info = (FrameInfo*)cur_thread_state.frame_info; swapContext(&self->returnContext, self->context, (intptr_t)self); assert(cur_thread_state.frame_info == top_caller_frame_info && "the generator should reset the frame info before the swapContext"); #if STAT_TIMERS self->prev_stack = StatTimer::swapStack(self->prev_stack); if (self->entryExited) { assert(self->prev_stack == &self->my_timer); assert(self->my_timer.isPaused()); } #endif self->running = false; // propagate exception to the caller if (self->exception.type) { freeGeneratorStack(self); // don't raise StopIteration exceptions because those are handled specially. if (!self->exception.matches(StopIteration)) { if (S == CAPI) { setCAPIException(self->exception); self->exception = ExcInfo(NULL, NULL, NULL); return true; } else { auto exc = self->exception; self->exception = ExcInfo(NULL, NULL, NULL); throw exc; } } return false; } if (self->entryExited) { freeGeneratorStack(self); // Reset the current exception. // We could directly create the StopIteration exception but we delay creating it because often the caller is not // interested in the exception (=generatorHasnext). If we really need it we will create it inside generatorSend. assert(!self->exception.type && "need to decref existing exception"); self->exception = ExcInfo(NULL, NULL, NULL); return false; } return false; }
Box* wrapperDescrTppCall(Box* _self, CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Box* arg1, Box* arg2, Box* arg3, Box** args, const std::vector<BoxedString*>* keyword_names) noexcept(S == CAPI) { if (S == CAPI) { try { return wrapperDescrTppCall<CXX>(_self, NULL, argspec, arg1, arg2, arg3, args, keyword_names); } catch (ExcInfo e) { setCAPIException(e); return NULL; } } if (rewrite_args) { // We are going to embed references to _self->d_base->wrapper and _self->d_wrapped rewrite_args->obj->addGuard((intptr_t)_self); rewrite_args->rewriter->addGCReference(_self); } STAT_TIMER(t0, "us_timer_boxedwrapperdecsriptor_call", (_self->cls->is_user_defined ? 10 : 20)); assert(_self->cls == &PyWrapperDescr_Type); PyWrapperDescrObject* self = reinterpret_cast<PyWrapperDescrObject*>(_self); int flags = self->d_base->flags; wrapperfunc wrapper = self->d_base->wrapper; ParamReceiveSpec paramspec(1, 0, true, false); if (flags == PyWrapperFlag_KEYWORDS) { paramspec = ParamReceiveSpec(1, 0, true, true); } else if (flags == PyWrapperFlag_PYSTON || flags == 0) { paramspec = ParamReceiveSpec(1, 0, true, false); } else if (flags == PyWrapperFlag_1ARG) { paramspec = ParamReceiveSpec(1, 0, false, false); } else if (flags == PyWrapperFlag_2ARG) { paramspec = ParamReceiveSpec(2, 0, false, false); } else { RELEASE_ASSERT(0, "%d", flags); } 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 Box* rtn; if (flags == PyWrapperFlag_KEYWORDS) { wrapperfunc_kwds wk = (wrapperfunc_kwds)wrapper; rtn = (*wk)(arg1, arg2, self->d_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->d_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->d_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->d_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->d_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->d_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->d_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->d_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; }; return callCXXFromStyle<S>([&]() { return rearrangeArgumentsAndCall(paramspec, NULL, self->d_base->name, NULL, rewrite_args, argspec, arg1, arg2, arg3, args, keyword_names, continuation); }); }
Box* methodDescrTppCall(Box* _self, CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Box* arg1, Box* arg2, Box* arg3, Box** args, const std::vector<BoxedString*>* keyword_names) noexcept(S == CAPI) { if (S == CAPI) { try { return methodDescrTppCall<CXX>(_self, NULL, argspec, arg1, arg2, arg3, args, keyword_names); } catch (ExcInfo e) { setCAPIException(e); return NULL; } } STAT_TIMER(t0, "us_timer_boxedmethoddescriptor__call__", 10); assert(_self->cls == &PyMethodDescr_Type || _self->cls == &PyClassMethodDescr_Type); PyMethodDescrObject* self = reinterpret_cast<PyMethodDescrObject*>(_self); bool is_classmethod = (_self->cls == &PyClassMethodDescr_Type); int ml_flags = self->d_method->ml_flags; int call_flags = ml_flags & ~(METH_CLASS | METH_COEXIST | METH_STATIC); if (rewrite_args && !rewrite_args->func_guarded) { rewrite_args->obj->addAttrGuard(offsetof(PyMethodDescrObject, d_method), (intptr_t)self->d_method); } ParamReceiveSpec paramspec(0, 0, false, false); Box** defaults = NULL; if (call_flags == METH_NOARGS) { paramspec = ParamReceiveSpec(1, 0, false, false); } else if (call_flags == METH_VARARGS) { paramspec = ParamReceiveSpec(1, 0, true, false); } else if (call_flags == (METH_VARARGS | METH_KEYWORDS)) { paramspec = ParamReceiveSpec(1, 0, true, true); } else if (call_flags == METH_O) { paramspec = ParamReceiveSpec(2, 0, false, false); } else if ((call_flags & ~(METH_O3 | METH_D3)) == 0) { int num_args = 0; if (call_flags & METH_O) num_args++; if (call_flags & METH_O2) num_args += 2; int num_defaults = 0; if (call_flags & METH_D1) num_defaults++; if (call_flags & METH_D2) num_defaults += 2; paramspec = ParamReceiveSpec(1 + num_args, num_defaults, false, false); if (num_defaults) { static Box* _defaults[] = { NULL, NULL, NULL }; assert(num_defaults <= 3); defaults = _defaults; } } else { RELEASE_ASSERT(0, "0x%x", call_flags); } bool arg1_class_guarded = false; if (rewrite_args && argspec.num_args >= 1) { // Try to do the guard before rearrangeArguments if possible: rewrite_args->arg1->addAttrGuard(offsetof(Box, cls), (intptr_t)arg1->cls); arg1_class_guarded = true; } auto continuation = [=](CallRewriteArgs* rewrite_args, Box* arg1, Box* arg2, Box* arg3, Box** args) { if (is_classmethod) { rewrite_args = NULL; if (!PyType_Check(arg1)) raiseExcHelper(TypeError, "descriptor '%s' requires a type but received a '%s'", self->d_method->ml_name, getFullTypeName(arg1).c_str()); } else { if (!isSubclass(arg1->cls, self->d_type)) raiseExcHelper(TypeError, "descriptor '%s' requires a '%s' arg1 but received a '%s'", self->d_method->ml_name, self->d_type->tp_name, 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->d_method->ml_meth(arg1, NULL); } if (rewrite_args) rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)self->d_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->d_method->ml_meth(arg1, arg2); } if (rewrite_args) rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)self->d_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->d_method->ml_meth)(arg1, arg2, arg3); } if (rewrite_args) rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)self->d_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->d_method->ml_meth(arg1, arg2); } if (rewrite_args) rewrite_args->out_rtn = rewrite_args->rewriter->call(true, (void*)self->d_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->d_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->d_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->d_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->d_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); } if (!rtn) throwCAPIException(); if (rewrite_args) { rewrite_args->rewriter->checkAndThrowCAPIException(rewrite_args->out_rtn); rewrite_args->out_success = true; } return rtn; }; return rearrangeArgumentsAndCall(paramspec, NULL, self->d_method->ml_name, defaults, rewrite_args, argspec, arg1, arg2, arg3, args, keyword_names, continuation); }