runtime/exception-builtins.cpp (700 lines of code) (raw):

// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com) #include "exception-builtins.h" #include <unistd.h> #include <cerrno> #include <cinttypes> #include "builtins-module.h" #include "builtins.h" #include "frame.h" #include "module-builtins.h" #include "object-builtins.h" #include "objects.h" #include "runtime.h" #include "set-builtins.h" #include "sys-module.h" #include "traceback-builtins.h" #include "tuple-builtins.h" #include "type-builtins.h" namespace py { static const BuiltinAttribute kBaseExceptionAttributes[] = { {ID(args), RawBaseException::kArgsOffset}, {ID(_base_exception__traceback), RawBaseException::kTracebackOffset, AttributeFlags::kHidden}, {ID(_base_exception__cause), RawBaseException::kCauseOffset, AttributeFlags::kHidden}, {ID(_base_exception__context), RawBaseException::kContextOffset, AttributeFlags::kHidden}, {ID(__suppress_context__), RawBaseException::kSuppressContextOffset}, }; static const BuiltinAttribute kImportErrorAttributes[] = { {ID(msg), RawImportError::kMsgOffset}, {ID(name), RawImportError::kNameOffset}, {ID(path), RawImportError::kPathOffset}, }; static const BuiltinAttribute kStopIterationAttributes[] = { {ID(value), RawStopIteration::kValueOffset}, }; static const BuiltinAttribute kSyntaxErrorAttributes[] = { {ID(filename), RawSyntaxError::kFilenameOffset}, {ID(lineno), RawSyntaxError::kLinenoOffset}, {ID(msg), RawSyntaxError::kMsgOffset}, {ID(offset), RawSyntaxError::kOffsetOffset}, {ID(print_file_and_line), RawSyntaxError::kPrintFileAndLineOffset}, {ID(text), RawSyntaxError::kTextOffset}, }; static const BuiltinAttribute kSystemExitAttributes[] = { {ID(code), RawSystemExit::kCodeOffset}, }; static const BuiltinAttribute kUnicodeErrorBaseAttributes[] = { {ID(encoding), RawUnicodeErrorBase::kEncodingOffset}, {ID(object), RawUnicodeErrorBase::kObjectOffset}, {ID(start), RawUnicodeErrorBase::kStartOffset}, {ID(end), RawUnicodeErrorBase::kEndOffset}, {ID(reason), RawUnicodeErrorBase::kReasonOffset}, }; struct ExceptionTypeSpec { SymbolId name; LayoutId layout_id; LayoutId superclass_id; View<BuiltinAttribute> attributes; }; static const ExceptionTypeSpec kExceptionSpecs[] = { {ID(BaseException), LayoutId::kBaseException, LayoutId::kObject, kBaseExceptionAttributes}, {ID(Exception), LayoutId::kException, LayoutId::kBaseException, kNoAttributes}, {ID(KeyboardInterrupt), LayoutId::kKeyboardInterrupt, LayoutId::kBaseException, kNoAttributes}, {ID(GeneratorExit), LayoutId::kGeneratorExit, LayoutId::kBaseException, kNoAttributes}, {ID(SystemExit), LayoutId::kSystemExit, LayoutId::kBaseException, kSystemExitAttributes}, {ID(ArithmeticError), LayoutId::kArithmeticError, LayoutId::kException, kNoAttributes}, {ID(AssertionError), LayoutId::kAssertionError, LayoutId::kException, kNoAttributes}, {ID(AttributeError), LayoutId::kAttributeError, LayoutId::kException, kNoAttributes}, {ID(BufferError), LayoutId::kBufferError, LayoutId::kException, kNoAttributes}, {ID(EOFError), LayoutId::kEOFError, LayoutId::kException, kNoAttributes}, {ID(ImportError), LayoutId::kImportError, LayoutId::kException, kImportErrorAttributes}, {ID(LookupError), LayoutId::kLookupError, LayoutId::kException, kNoAttributes}, {ID(MemoryError), LayoutId::kMemoryError, LayoutId::kException, kNoAttributes}, {ID(NameError), LayoutId::kNameError, LayoutId::kException, kNoAttributes}, {ID(OSError), LayoutId::kOSError, LayoutId::kException, kNoAttributes}, {ID(ReferenceError), LayoutId::kReferenceError, LayoutId::kException, kNoAttributes}, {ID(RuntimeError), LayoutId::kRuntimeError, LayoutId::kException, kNoAttributes}, {ID(StopIteration), LayoutId::kStopIteration, LayoutId::kException, kStopIterationAttributes}, {ID(StopAsyncIteration), LayoutId::kStopAsyncIteration, LayoutId::kException, kNoAttributes}, {ID(SyntaxError), LayoutId::kSyntaxError, LayoutId::kException, kSyntaxErrorAttributes}, {ID(SystemError), LayoutId::kSystemError, LayoutId::kException, kNoAttributes}, {ID(TypeError), LayoutId::kTypeError, LayoutId::kException, kNoAttributes}, {ID(ValueError), LayoutId::kValueError, LayoutId::kException, kNoAttributes}, {ID(Warning), LayoutId::kWarning, LayoutId::kException, kNoAttributes}, {ID(FloatingPointError), LayoutId::kFloatingPointError, LayoutId::kArithmeticError, kNoAttributes}, {ID(OverflowError), LayoutId::kOverflowError, LayoutId::kArithmeticError, kNoAttributes}, {ID(ZeroDivisionError), LayoutId::kZeroDivisionError, LayoutId::kArithmeticError, kNoAttributes}, {ID(ModuleNotFoundError), LayoutId::kModuleNotFoundError, LayoutId::kImportError, kNoAttributes}, {ID(IndexError), LayoutId::kIndexError, LayoutId::kLookupError, kNoAttributes}, {ID(KeyError), LayoutId::kKeyError, LayoutId::kLookupError, kNoAttributes}, {ID(UnboundLocalError), LayoutId::kUnboundLocalError, LayoutId::kNameError, kNoAttributes}, {ID(BlockingIOError), LayoutId::kBlockingIOError, LayoutId::kOSError, kNoAttributes}, {ID(ChildProcessError), LayoutId::kChildProcessError, LayoutId::kOSError, kNoAttributes}, {ID(ConnectionError), LayoutId::kConnectionError, LayoutId::kOSError, kNoAttributes}, {ID(FileExistsError), LayoutId::kFileExistsError, LayoutId::kOSError, kNoAttributes}, {ID(FileNotFoundError), LayoutId::kFileNotFoundError, LayoutId::kOSError, kNoAttributes}, {ID(InterruptedError), LayoutId::kInterruptedError, LayoutId::kOSError, kNoAttributes}, {ID(IsADirectoryError), LayoutId::kIsADirectoryError, LayoutId::kOSError, kNoAttributes}, {ID(NotADirectoryError), LayoutId::kNotADirectoryError, LayoutId::kOSError, kNoAttributes}, {ID(PermissionError), LayoutId::kPermissionError, LayoutId::kOSError, kNoAttributes}, {ID(ProcessLookupError), LayoutId::kProcessLookupError, LayoutId::kOSError, kNoAttributes}, {ID(TimeoutError), LayoutId::kTimeoutError, LayoutId::kOSError, kNoAttributes}, {ID(BrokenPipeError), LayoutId::kBrokenPipeError, LayoutId::kConnectionError, kNoAttributes}, {ID(ConnectionAbortedError), LayoutId::kConnectionAbortedError, LayoutId::kConnectionError, kNoAttributes}, {ID(ConnectionRefusedError), LayoutId::kConnectionRefusedError, LayoutId::kConnectionError, kNoAttributes}, {ID(ConnectionResetError), LayoutId::kConnectionResetError, LayoutId::kConnectionError, kNoAttributes}, {ID(NotImplementedError), LayoutId::kNotImplementedError, LayoutId::kRuntimeError, kNoAttributes}, {ID(RecursionError), LayoutId::kRecursionError, LayoutId::kRuntimeError, kNoAttributes}, {ID(IndentationError), LayoutId::kIndentationError, LayoutId::kSyntaxError, kNoAttributes}, {ID(TabError), LayoutId::kTabError, LayoutId::kIndentationError, kNoAttributes}, {ID(UserWarning), LayoutId::kUserWarning, LayoutId::kWarning, kNoAttributes}, {ID(DeprecationWarning), LayoutId::kDeprecationWarning, LayoutId::kWarning, kNoAttributes}, {ID(PendingDeprecationWarning), LayoutId::kPendingDeprecationWarning, LayoutId::kWarning, kNoAttributes}, {ID(SyntaxWarning), LayoutId::kSyntaxWarning, LayoutId::kWarning, kNoAttributes}, {ID(RuntimeWarning), LayoutId::kRuntimeWarning, LayoutId::kWarning, kNoAttributes}, {ID(FutureWarning), LayoutId::kFutureWarning, LayoutId::kWarning, kNoAttributes}, {ID(ImportWarning), LayoutId::kImportWarning, LayoutId::kWarning, kNoAttributes}, {ID(UnicodeWarning), LayoutId::kUnicodeWarning, LayoutId::kWarning, kNoAttributes}, {ID(BytesWarning), LayoutId::kBytesWarning, LayoutId::kWarning, kNoAttributes}, {ID(ResourceWarning), LayoutId::kResourceWarning, LayoutId::kWarning, kNoAttributes}, {ID(UnicodeError), LayoutId::kUnicodeError, LayoutId::kValueError, kNoAttributes}, {ID(UnicodeDecodeError), LayoutId::kUnicodeDecodeError, LayoutId::kUnicodeError, kUnicodeErrorBaseAttributes}, {ID(UnicodeEncodeError), LayoutId::kUnicodeEncodeError, LayoutId::kUnicodeError, kUnicodeErrorBaseAttributes}, {ID(UnicodeTranslateError), LayoutId::kUnicodeTranslateError, LayoutId::kUnicodeError, kUnicodeErrorBaseAttributes}, }; LayoutId errorLayoutFromErrno(int errno_value) { switch (errno_value) { case EACCES: return LayoutId::kPermissionError; case EAGAIN: // duplicates EWOULDBLOCK return LayoutId::kBlockingIOError; case EALREADY: return LayoutId::kBlockingIOError; case EINPROGRESS: return LayoutId::kBlockingIOError; case ECHILD: return LayoutId::kChildProcessError; case ECONNABORTED: return LayoutId::kConnectionAbortedError; case ECONNREFUSED: return LayoutId::kConnectionRefusedError; case ECONNRESET: return LayoutId::kConnectionResetError; case EEXIST: return LayoutId::kFileExistsError; case ENOENT: return LayoutId::kFileNotFoundError; case EINTR: return LayoutId::kInterruptedError; case EISDIR: return LayoutId::kIsADirectoryError; case ENOTDIR: return LayoutId::kNotADirectoryError; case EPERM: return LayoutId::kPermissionError; case EPIPE: return LayoutId::kBrokenPipeError; case ESRCH: return LayoutId::kProcessLookupError; case ETIMEDOUT: return LayoutId::kTimeoutError; default: return LayoutId::kOSError; } } bool givenExceptionMatches(Thread* thread, const Object& given, const Object& exc) { HandleScope scope(thread); if (exc.isTuple()) { Tuple tuple(&scope, *exc); Object item(&scope, NoneType::object()); for (word i = 0; i < tuple.length(); i++) { item = tuple.at(i); if (givenExceptionMatches(thread, given, item)) { return true; } } return false; } Runtime* runtime = thread->runtime(); Object given_type(&scope, *given); if (runtime->isInstanceOfBaseException(*given_type)) { given_type = runtime->typeOf(*given); } if (runtime->isInstanceOfType(*given_type) && runtime->isInstanceOfType(*exc)) { Type subtype(&scope, *given_type); Type supertype(&scope, *exc); if (subtype.isBaseExceptionSubclass() && supertype.isBaseExceptionSubclass()) { return typeIsSubclass(*subtype, *supertype); } } return *given_type == *exc; } RawObject createException(Thread* thread, const Type& type, const Object& value) { if (value.isNoneType()) { return Interpreter::call0(thread, type); } if (thread->runtime()->isInstanceOfTuple(*value)) { HandleScope scope(thread); thread->stackPush(*type); Tuple args(&scope, tupleUnderlying(*value)); word length = args.length(); for (word i = 0; i < length; i++) { thread->stackPush(args.at(i)); } return Interpreter::call(thread, /*nargs=*/length); } return Interpreter::call1(thread, type, value); } void normalizeException(Thread* thread, Object* exc, Object* val, Object* traceback) { Runtime* runtime = thread->runtime(); HandleScope scope(thread); auto normalize = [&] { if (!runtime->isInstanceOfType(**exc)) return true; Type type(&scope, **exc); if (!type.isBaseExceptionSubclass()) return true; Object value(&scope, **val); Type value_type(&scope, runtime->typeOf(*value)); // TODO(bsimmers): Extend this to support all the weird cases allowed by // PyObject_IsSubclass. if (!typeIsSubclass(*value_type, *type)) { // value isn't an instance of type. Replace it with type(value). value = createException(thread, type, value); if (value.isError()) return false; *val = *value; } else if (*value_type != *type) { // value_type is more specific than type, so use it instead. *exc = *value_type; } return true; }; // If a new exception is raised during normalization, attempt to normalize // that exception. If this process repeats too many times, give up and throw a // RecursionError. If even that exception fails to normalize, abort. const int normalize_limit = 32; for (word i = 0; i <= normalize_limit; i++) { if (normalize()) return; if (i == normalize_limit - 1) { thread->raiseWithFmt( LayoutId::kRecursionError, "maximum recursion depth exceeded while normalizing an exception"); } *exc = thread->pendingExceptionType(); *val = thread->pendingExceptionValue(); Object new_tb(&scope, thread->pendingExceptionTraceback()); if (!new_tb.isNoneType()) *traceback = *new_tb; thread->clearPendingException(); } if (runtime->isInstanceOfType(**exc)) { Type type(&scope, **exc); if (type.builtinBase() == LayoutId::kMemoryError) { UNIMPLEMENTED( "Cannot recover from MemoryErrors while normalizing exceptions."); } UNIMPLEMENTED( "Cannot recover from the recursive normalization of an exception."); } } static void printPendingExceptionImpl(Thread* thread, bool set_sys_last_vars) { HandleScope scope(thread); Runtime* runtime = thread->runtime(); Object type(&scope, thread->pendingExceptionType()); Object system_exit(&scope, runtime->typeAt(LayoutId::kSystemExit)); if (givenExceptionMatches(thread, type, system_exit)) { handleSystemExit(thread); } Object value(&scope, thread->pendingExceptionValue()); Object tb(&scope, thread->pendingExceptionTraceback()); thread->clearPendingException(); if (type.isNoneType()) return; normalizeException(thread, &type, &value, &tb); BaseException exc(&scope, *value); exc.setTraceback(*tb); if (set_sys_last_vars) { Module sys(&scope, runtime->findModuleById(ID(sys))); moduleAtPutById(thread, sys, ID(last_type), type); moduleAtPutById(thread, sys, ID(last_value), value); moduleAtPutById(thread, sys, ID(last_traceback), tb); } Object hook(&scope, runtime->lookupNameInModule(thread, ID(sys), ID(excepthook))); if (hook.isError()) { writeStderr(thread, "sys.excepthook is missing\n"); if (displayException(thread, value, tb).isError()) { thread->clearPendingException(); } return; } Object result(&scope, Interpreter::call3(thread, hook, type, value, tb)); if (!result.isError()) return; Object type2(&scope, thread->pendingExceptionType()); if (givenExceptionMatches(thread, type2, system_exit)) { handleSystemExit(thread); } Object value2(&scope, thread->pendingExceptionValue()); Object tb2(&scope, thread->pendingExceptionTraceback()); thread->clearPendingException(); normalizeException(thread, &type2, &value2, &tb2); writeStderr(thread, "Error in sys.excepthook:\n"); if (displayException(thread, value2, tb2).isError()) { thread->clearPendingException(); } writeStderr(thread, "\nOriginal exception was:\n"); if (displayException(thread, value, tb).isError()) { thread->clearPendingException(); } } void printPendingException(Thread* thread) { printPendingExceptionImpl(thread, false); } void printPendingExceptionWithSysLastVars(Thread* thread) { printPendingExceptionImpl(thread, true); } // If value has all the attributes of a well-formed SyntaxError, return true and // populate all of the given parameters. In that case, filename will be a str // and text will be None or a str. Otherwise, return false and the contents of // all out parameters are unspecified. static bool parseSyntaxError(Thread* thread, const Object& value, Object* message, Object* filename, word* lineno, word* offset, Object* text) { auto fail = [thread] { thread->clearPendingException(); return false; }; HandleScope scope(thread); Runtime* runtime = thread->runtime(); Object result(&scope, runtime->attributeAtById(thread, value, ID(msg))); if (result.isError()) return fail(); *message = *result; result = runtime->attributeAtById(thread, value, ID(filename)); if (result.isError()) return fail(); if (result.isNoneType()) { *filename = runtime->newStrFromCStr("<string>"); } else if (runtime->isInstanceOfStr(*result)) { *filename = *result; } else { return false; } result = runtime->attributeAtById(thread, value, ID(lineno)); if (result.isError()) return fail(); if (runtime->isInstanceOfInt(*result)) { Int ival(&scope, intUnderlying(*result)); if (ival.numDigits() > 1) return false; *lineno = ival.asWord(); } else { return false; } result = runtime->attributeAtById(thread, value, ID(offset)); if (result.isError()) return fail(); if (result.isNoneType()) { *offset = -1; } else if (runtime->isInstanceOfInt(*result)) { Int ival(&scope, intUnderlying(*result)); if (ival.numDigits() > 1) return false; *offset = ival.asWord(); } else { return false; } result = runtime->attributeAtById(thread, value, ID(text)); if (result.isError()) return fail(); if (result.isNoneType() || runtime->isInstanceOfStr(*result)) { *text = *result; } else { return false; } return true; } static RawObject fileWriteString(Thread* thread, const Object& file, const char* c_str) { HandleScope scope(thread); Str str(&scope, thread->runtime()->newStrFromCStr(c_str)); return thread->invokeMethod2(file, ID(write), str); } static RawObject fileWriteObjectStr(Thread* thread, const Object& file, const Object& str) { return thread->invokeMethod2(file, ID(write), str); } // Used to wrap an expression that may return an Error that should be forwarded, // or a value that should be ignored otherwise. // // TODO(bsimmers): Most of the functions that use this should be rewritten in // Python once we have enough library support to do so, then we can delete the // macro. #define MAY_RAISE(expr) \ { \ RawObject result = (expr); \ if (result.isError()) return result; \ } // Print the source code snippet from a SyntaxError, with a ^ indicating the // position of the error. static RawObject printErrorText(Thread* thread, const Object& file, word offset, const Object& text_obj) { HandleScope scope(thread); Str text_str(&scope, *text_obj); word length = text_str.length(); // This is gross, but it greatly simplifies the string scanning done by the // rest of the function, and makes maintaining compatibility with CPython // easier. unique_c_ptr<char[]> text_owner(text_str.toCStr()); const char* text = text_owner.get(); // Adjust text and offset to not print any lines before the one that has the // cursor. if (offset >= 0) { if (offset > 0 && offset == length && text[offset - 1] == '\n') { offset--; } for (;;) { const char* newline = std::strchr(text, '\n'); if (newline == nullptr || newline - text >= offset) break; word adjust = newline + 1 - text; offset -= adjust; length -= adjust; text += adjust; } while (*text == ' ' || *text == '\t' || *text == '\f') { text++; offset--; } } MAY_RAISE(fileWriteString(thread, file, " ")); MAY_RAISE(fileWriteString(thread, file, text)); if (*text == '\0' || text[length - 1] != '\n') { MAY_RAISE(fileWriteString(thread, file, "\n")); } if (offset == -1) return NoneType::object(); MAY_RAISE(fileWriteString(thread, file, " ")); while (--offset > 0) MAY_RAISE(fileWriteString(thread, file, " ")); MAY_RAISE(fileWriteString(thread, file, "^\n")); return NoneType::object(); } // Print the traceback, type, and message of a single exception. static RawObject printSingleException(Thread* thread, const Object& file, const Object& value_in) { HandleScope scope(thread); Runtime* runtime = thread->runtime(); Object value(&scope, *value_in); Type type(&scope, runtime->typeOf(*value)); Str type_name(&scope, type.name()); if (!runtime->isInstanceOfBaseException(*value)) { MAY_RAISE(fileWriteString( thread, file, "TypeError: print_exception(): Exception expected for value, ")); MAY_RAISE(fileWriteObjectStr(thread, file, type_name)); MAY_RAISE(fileWriteString(thread, file, " found\n")); return NoneType::object(); } BaseException exc(&scope, *value); Object tb_obj(&scope, exc.traceback()); if (tb_obj.isTraceback()) { Traceback traceback(&scope, *tb_obj); MAY_RAISE(tracebackWrite(thread, traceback, file)); } if (runtime->attributeAtById(thread, value, ID(print_file_and_line)) .isError()) { // Ignore the AttributeError or whatever else went wrong during lookup. thread->clearPendingException(); } else { Object message(&scope, NoneType::object()); Object filename(&scope, NoneType::object()); Object text(&scope, NoneType::object()); word lineno, offset; if (parseSyntaxError(thread, value, &message, &filename, &lineno, &offset, &text)) { value = *message; Str filename_str(&scope, *filename); unique_c_ptr<char[]> filename_c_str(filename_str.toCStr()); Str line(&scope, runtime->newStrFromFmt(" File \"%s\", line %w\n", filename_c_str.get(), lineno)); MAY_RAISE(fileWriteObjectStr(thread, file, line)); if (!text.isNoneType()) { MAY_RAISE(printErrorText(thread, file, offset, text)); } } } Object module(&scope, runtime->attributeAtById(thread, type, ID(__module__))); if (module.isError() || !runtime->isInstanceOfStr(*module)) { if (module.isError()) thread->clearPendingException(); MAY_RAISE(fileWriteString(thread, file, "<unknown>")); } else { Str module_str(&scope, *module); if (!module_str.equals(runtime->symbols()->at(ID(builtins)))) { MAY_RAISE(fileWriteObjectStr(thread, file, module_str)); MAY_RAISE(fileWriteString(thread, file, ".")); } } MAY_RAISE(fileWriteObjectStr(thread, file, type_name)); Object str_obj(&scope, thread->invokeFunction1(ID(builtins), ID(str), value)); if (str_obj.isError()) { thread->clearPendingException(); MAY_RAISE(fileWriteString(thread, file, ": <exception str() failed>")); } else { Str str(&scope, strUnderlying(*str_obj)); if (str != Str::empty()) { MAY_RAISE(fileWriteString(thread, file, ": ")); MAY_RAISE(fileWriteObjectStr(thread, file, str)); } } MAY_RAISE(fileWriteString(thread, file, "\n")); return NoneType::object(); } // Print the given exception and any cause or context exceptions it chains to. static RawObject printExceptionChain(Thread* thread, const Object& file, const Object& value, const Set& seen) { Runtime* runtime = thread->runtime(); HandleScope scope(thread); Object hash_obj(&scope, Interpreter::hash(thread, value)); if (hash_obj.isErrorException()) return *hash_obj; word hash = SmallInt::cast(*hash_obj).value(); setAdd(thread, seen, value, hash); if (runtime->isInstanceOfBaseException(*value)) { BaseException exc(&scope, *value); Object cause(&scope, exc.cause()); Object context(&scope, exc.context()); if (!cause.isNoneType()) { hash_obj = Interpreter::hash(thread, cause); if (hash_obj.isErrorException()) return *hash_obj; hash = SmallInt::cast(*hash_obj).value(); if (!setIncludes(thread, seen, cause, hash)) { MAY_RAISE(printExceptionChain(thread, file, cause, seen)); MAY_RAISE( fileWriteString(thread, file, "\nThe above exception was the direct cause of the " "following exception:\n\n")); } } else if (!context.isNoneType() && exc.suppressContext() != RawBool::trueObj()) { hash_obj = Interpreter::hash(thread, context); if (hash_obj.isErrorException()) return *hash_obj; hash = SmallInt::cast(*hash_obj).value(); if (!setIncludes(thread, seen, context, hash)) { MAY_RAISE(printExceptionChain(thread, file, context, seen)); MAY_RAISE( fileWriteString(thread, file, "\nDuring handling of the above exception, another " "exception occurred:\n\n")); } } } MAY_RAISE(printSingleException(thread, file, value)); return NoneType::object(); } #undef MAY_RAISE RawObject displayException(Thread* thread, const Object& value, const Object& traceback) { HandleScope scope(thread); Runtime* runtime = thread->runtime(); if (runtime->isInstanceOfBaseException(*value) && traceback.isTraceback()) { BaseException exc(&scope, *value); if (exc.traceback().isNoneType()) exc.setTraceback(*traceback); } ValueCell sys_stderr_cell(&scope, runtime->sysStderr()); if (sys_stderr_cell.isUnbound()) { fputs("lost sys.stderr\n", stderr); return NoneType::object(); } Object sys_stderr(&scope, sys_stderr_cell.value()); if (sys_stderr.isNoneType()) { return NoneType::object(); } Set seen(&scope, runtime->newSet()); return printExceptionChain(thread, sys_stderr, value, seen); } void handleSystemExit(Thread* thread) { auto do_exit = [thread](int exit_code) { thread->clearPendingException(); Runtime* runtime = thread->runtime(); delete runtime; std::exit(exit_code); }; Runtime* runtime = thread->runtime(); HandleScope scope(thread); Object arg(&scope, thread->pendingExceptionValue()); if (runtime->isInstanceOfSystemExit(*arg)) { // The exception could be raised by either native or managed code. If // native, there will be no SystemExit object. If managed, there will // be one to unpack. SystemExit exc(&scope, *arg); arg = exc.code(); } if (arg.isNoneType()) do_exit(EXIT_SUCCESS); if (runtime->isInstanceOfInt(*arg)) { // We could convert and check for overflow error, but the overflow error // should get cleared anyway. do_exit(intUnderlying(*arg).asWordSaturated()); } // The calls below can't have an exception pending thread->clearPendingException(); Object result(&scope, thread->invokeMethod1(arg, ID(__str__))); if (!runtime->isInstanceOfStr(*result)) { // The calls below can't have an exception pending thread->clearPendingException(); // No __repr__ method or __repr__ raised. Either way, we can't handle it. result = runtime->newStrFromCStr(""); } Str result_str(&scope, *result); ValueCell sys_stderr_cell(&scope, runtime->sysStderr()); if (sys_stderr_cell.isUnbound() || sys_stderr_cell.value().isNoneType()) { unique_c_ptr<char> buf(result_str.toCStr()); fwrite(buf.get(), 1, result_str.length(), stderr); fputc('\n', stderr); } else { Object file(&scope, sys_stderr_cell.value()); fileWriteObjectStr(thread, file, result_str); thread->clearPendingException(); fileWriteString(thread, file, "\n"); } do_exit(EXIT_FAILURE); } RawObject METH(BaseException, __init__)(Thread* thread, Arguments args) { HandleScope scope(thread); Object self_obj(&scope, args.get(0)); if (!thread->runtime()->isInstanceOfBaseException(*self_obj)) { return thread->raiseRequiresType(self_obj, ID(BaseException)); } BaseException self(&scope, *self_obj); Object args_obj(&scope, args.get(1)); self.setArgs(*args_obj); self.setCause(Unbound::object()); self.setContext(Unbound::object()); self.setTraceback(Unbound::object()); self.setSuppressContext(RawBool::falseObj()); return NoneType::object(); } RawObject METH(StopIteration, __init__)(Thread* thread, Arguments args) { HandleScope scope(thread); Object self_obj(&scope, args.get(0)); if (!thread->runtime()->isInstanceOfStopIteration(*self_obj)) { return thread->raiseRequiresType(self_obj, ID(StopIteration)); } StopIteration self(&scope, *self_obj); Object args_obj(&scope, args.get(1)); self.setArgs(*args_obj); self.setCause(Unbound::object()); self.setContext(Unbound::object()); self.setTraceback(Unbound::object()); self.setSuppressContext(RawBool::falseObj()); Tuple tuple(&scope, self.args()); if (tuple.length() > 0) self.setValue(tuple.at(0)); return NoneType::object(); } RawObject METH(SystemExit, __init__)(Thread* thread, Arguments args) { HandleScope scope(thread); Object self_obj(&scope, args.get(0)); if (!thread->runtime()->isInstanceOfSystemExit(*self_obj)) { return thread->raiseRequiresType(self_obj, ID(SystemExit)); } SystemExit self(&scope, *self_obj); RawObject result = METH(BaseException, __init__)(thread, args); if (result.isError()) { return result; } Tuple tuple(&scope, self.args()); if (tuple.length() > 0) { self.setCode(tuple.at(0)); } return NoneType::object(); } void initializeExceptionTypes(Thread* thread) { HandleScope scope(thread); Runtime* runtime = thread->runtime(); Layout layout(&scope, runtime->layoutAt(LayoutId::kNoneType)); Type type(&scope, layout.describedType()); for (ExceptionTypeSpec spec : kExceptionSpecs) { Layout super_layout(&scope, runtime->layoutAt(spec.superclass_id)); word size = (Tuple::cast(super_layout.inObjectAttributes()).length() + spec.attributes.length()) * kPointerSize; type = addBuiltinType(thread, spec.name, spec.layout_id, spec.superclass_id, spec.attributes, size, /*basetype=*/true); builtinTypeEnableTupleOverflow(thread, type); } } } // namespace py