void raise3(Box* arg0, Box* arg1, Box* arg2) { // TODO switch this to PyErr_Normalize if (arg2 == None) arg2 = getTraceback(); if (isSubclass(arg0->cls, type_cls)) { BoxedClass* c = static_cast<BoxedClass*>(arg0); if (isSubclass(c, BaseException)) { Box* exc_obj; if (isSubclass(arg1->cls, BaseException)) { exc_obj = arg1; c = exc_obj->cls; } else if (arg1 != None) { exc_obj = runtimeCall(c, ArgPassSpec(1), arg1, NULL, NULL, NULL, NULL); } else { exc_obj = runtimeCall(c, ArgPassSpec(0), NULL, NULL, NULL, NULL, NULL); } raiseRaw(ExcInfo(c, exc_obj, arg2)); } } if (isSubclass(arg0->cls, BaseException)) { if (arg1 != None) raiseExcHelper(TypeError, "instance exception may not have a separate value"); raiseRaw(ExcInfo(arg0->cls, arg0, arg2)); } raiseExcHelper(TypeError, "exceptions must be old-style classes or derived from BaseException, not %s", getTypeName(arg0)); }
void raise3(Box* arg0, Box* arg1, Box* arg2) { // TODO switch this to PyErr_Normalize if (isSubclass(arg0->cls, type_cls)) { BoxedClass* c = static_cast<BoxedClass*>(arg0); if (isSubclass(c, BaseException)) { Box* exc_obj; if (arg1 != None) exc_obj = exceptionNew2(c, arg1); else exc_obj = exceptionNew1(c); raiseRaw(ExcInfo(c, exc_obj, arg2)); } } if (isSubclass(arg0->cls, BaseException)) { if (arg1 != None) raiseExcHelper(TypeError, "instance exception may not have a separate value"); raiseRaw(ExcInfo(arg0->cls, arg0, arg2)); } raiseExcHelper(TypeError, "exceptions must be old-style classes or derived from BaseException, not %s", getTypeName(arg0)->c_str()); }
// Have a special helper function for syntax errors, since we want to include the location // of the syntax error in the traceback, even though it is not part of the execution: void raiseSyntaxError(const char* msg, int lineno, int col_offset, llvm::StringRef file, llvm::StringRef func) { Box* exc = runtimeCall(SyntaxError, ArgPassSpec(1), boxString(msg), NULL, NULL, NULL, NULL); auto tb = new BoxedTraceback(LineInfo(lineno, col_offset, boxString(file), boxString(func)), None); assert(!PyErr_Occurred()); throw ExcInfo(exc->cls, exc, tb); }
Box* generatorSend(Box* s, Box* v) { 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__"); generatorSendInternal(self, v); // 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) raiseExcHelper(StopIteration, (const char*)nullptr); throw old_exc; } return self->returnValue; }
// Have a special helper function for syntax errors, since we want to include the location // of the syntax error in the traceback, even though it is not part of the execution: void raiseSyntaxError(const char* msg, int lineno, int col_offset, const std::string& file, const std::string& func) { Box* exc = runtimeCall(SyntaxError, ArgPassSpec(1), boxStrConstant(msg), NULL, NULL, NULL, NULL); auto tb = getTraceback(); std::vector<const LineInfo*> entries = tb->lines; entries.push_back(new LineInfo(lineno, col_offset, file, func)); raiseRaw(ExcInfo(exc->cls, exc, new BoxedTraceback(std::move(entries)))); }
// Have a special helper function for syntax errors, since we want to include the location // of the syntax error in the traceback, even though it is not part of the execution: void raiseSyntaxError(const char* msg, int lineno, int col_offset, const std::string& file, const std::string& func) { Box* exc = exceptionNew2(SyntaxError, boxStrConstant(msg)); auto entries = getTracebackEntries(); last_tb = std::move(entries); // TODO: leaks this! last_tb.push_back(new LineInfo(lineno, col_offset, file, func)); raiseRaw(ExcInfo(exc->cls, exc, None)); }
// takes the three arguments of a `raise' and produces the ExcInfo to throw ExcInfo excInfoForRaise(Box* type, Box* value, Box* tb) { assert(type && value && tb); // use None for default behavior, not nullptr // TODO switch this to PyErr_Normalize if (tb == None) { tb = NULL; } else if (tb != NULL && !PyTraceBack_Check(tb)) { raiseExcHelper(TypeError, "raise: arg 3 must be a traceback or None"); } /* Next, repeatedly, replace a tuple exception with its first item */ while (PyTuple_Check(type) && PyTuple_Size(type) > 0) { PyObject* tmp = type; type = PyTuple_GET_ITEM(type, 0); Py_INCREF(type); Py_DECREF(tmp); } if (PyExceptionClass_Check(type)) { PyErr_NormalizeException(&type, &value, &tb); if (!PyExceptionInstance_Check(value)) { raiseExcHelper(TypeError, "calling %s() should have returned an instance of " "BaseException, not '%s'", ((PyTypeObject*)type)->tp_name, Py_TYPE(value)->tp_name); } } else if (PyExceptionInstance_Check(type)) { /* Raising an instance. The value should be a dummy. */ if (value != Py_None) { raiseExcHelper(TypeError, "instance exception may not have a separate value"); } else { /* Normalize to raise <class>, <instance> */ Py_DECREF(value); value = type; type = PyExceptionInstance_Class(type); Py_INCREF(type); } } else { /* Not something you can raise. You get an exception anyway, just not what you specified :-) */ raiseExcHelper(TypeError, "exceptions must be old-style classes or " "derived from BaseException, not %s", type->cls->tp_name); } assert(PyExceptionClass_Check(type)); if (tb == NULL) { tb = None; } return ExcInfo(type, value, tb); }
// called from both generatorHasNext and generatorSend/generatorNext (but only if generatorHasNext hasn't been called) static void generatorSendInternal(BoxedGenerator* self, Box* v) { STAT_TIMER(t0, "us_timer_generator_switching", 0); if (self->running) raiseExcHelper(ValueError, "generator already executing"); // check if the generator already exited if (self->entryExited) { freeGeneratorStack(self); raiseExcHelper(StopIteration, (const char*)nullptr); } self->returnValue = 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 swapContext(&self->returnContext, self->context, (intptr_t)self); #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) { assert(self->entryExited); freeGeneratorStack(self); // don't raise StopIteration exceptions because those are handled specially. if (!self->exception.matches(StopIteration)) throw self->exception; return; } 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. self->exception = ExcInfo(NULL, NULL, NULL); return; } }
extern "C" Box* yield(BoxedGenerator* obj, Box* value) { STAT_TIMER(t0, "us_timer_generator_switching", 0); assert(obj->cls == generator_cls); BoxedGenerator* self = static_cast<BoxedGenerator*>(obj); self->returnValue = value; threading::popGenerator(); swapContext(&self->context, self->returnContext, 0); threading::pushGenerator(obj, obj->stack_begin, obj->returnContext); // if the generator receives a exception from the caller we have to throw it if (self->exception.type) { ExcInfo e = self->exception; self->exception = ExcInfo(NULL, NULL, NULL); throw e; } return self->returnValue; }
void raiseExc(Box* exc_obj) { auto entries = getTracebackEntries(); last_tb = std::move(entries); raiseRaw(ExcInfo(exc_obj->cls, exc_obj, None)); }
// 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; }
void raiseExc(Box* exc_obj) { raiseRaw(ExcInfo(exc_obj->cls, exc_obj, getTraceback())); }
void raiseExc(Box* exc_obj) { assert(!PyErr_Occurred()); throw ExcInfo(exc_obj->cls, exc_obj, None); }