this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2#include "exception-builtins.h"
3
4#include <unistd.h>
5
6#include <cerrno>
7#include <cinttypes>
8
9#include "builtins-module.h"
10#include "builtins.h"
11#include "frame.h"
12#include "module-builtins.h"
13#include "object-builtins.h"
14#include "objects.h"
15#include "runtime.h"
16#include "set-builtins.h"
17#include "sys-module.h"
18#include "traceback-builtins.h"
19#include "tuple-builtins.h"
20#include "type-builtins.h"
21
22namespace py {
23
24static const BuiltinAttribute kBaseExceptionAttributes[] = {
25 {ID(args), RawBaseException::kArgsOffset},
26 {ID(_base_exception__traceback), RawBaseException::kTracebackOffset,
27 AttributeFlags::kHidden},
28 {ID(_base_exception__cause), RawBaseException::kCauseOffset,
29 AttributeFlags::kHidden},
30 {ID(_base_exception__context), RawBaseException::kContextOffset,
31 AttributeFlags::kHidden},
32 {ID(__suppress_context__), RawBaseException::kSuppressContextOffset},
33};
34
35static const BuiltinAttribute kImportErrorAttributes[] = {
36 {ID(msg), RawImportError::kMsgOffset},
37 {ID(name), RawImportError::kNameOffset},
38 {ID(path), RawImportError::kPathOffset},
39};
40
41static const BuiltinAttribute kStopIterationAttributes[] = {
42 {ID(value), RawStopIteration::kValueOffset},
43};
44
45static const BuiltinAttribute kSyntaxErrorAttributes[] = {
46 {ID(filename), RawSyntaxError::kFilenameOffset},
47 {ID(lineno), RawSyntaxError::kLinenoOffset},
48 {ID(msg), RawSyntaxError::kMsgOffset},
49 {ID(offset), RawSyntaxError::kOffsetOffset},
50 {ID(print_file_and_line), RawSyntaxError::kPrintFileAndLineOffset},
51 {ID(text), RawSyntaxError::kTextOffset},
52};
53
54static const BuiltinAttribute kSystemExitAttributes[] = {
55 {ID(code), RawSystemExit::kCodeOffset},
56};
57
58static const BuiltinAttribute kUnicodeErrorBaseAttributes[] = {
59 {ID(encoding), RawUnicodeErrorBase::kEncodingOffset},
60 {ID(object), RawUnicodeErrorBase::kObjectOffset},
61 {ID(start), RawUnicodeErrorBase::kStartOffset},
62 {ID(end), RawUnicodeErrorBase::kEndOffset},
63 {ID(reason), RawUnicodeErrorBase::kReasonOffset},
64};
65
66struct ExceptionTypeSpec {
67 SymbolId name;
68 LayoutId layout_id;
69 LayoutId superclass_id;
70 View<BuiltinAttribute> attributes;
71};
72
73static const ExceptionTypeSpec kExceptionSpecs[] = {
74 {ID(BaseException), LayoutId::kBaseException, LayoutId::kObject,
75 kBaseExceptionAttributes},
76 {ID(Exception), LayoutId::kException, LayoutId::kBaseException,
77 kNoAttributes},
78 {ID(KeyboardInterrupt), LayoutId::kKeyboardInterrupt,
79 LayoutId::kBaseException, kNoAttributes},
80 {ID(GeneratorExit), LayoutId::kGeneratorExit, LayoutId::kBaseException,
81 kNoAttributes},
82 {ID(SystemExit), LayoutId::kSystemExit, LayoutId::kBaseException,
83 kSystemExitAttributes},
84 {ID(ArithmeticError), LayoutId::kArithmeticError, LayoutId::kException,
85 kNoAttributes},
86 {ID(AssertionError), LayoutId::kAssertionError, LayoutId::kException,
87 kNoAttributes},
88 {ID(AttributeError), LayoutId::kAttributeError, LayoutId::kException,
89 kNoAttributes},
90 {ID(BufferError), LayoutId::kBufferError, LayoutId::kException,
91 kNoAttributes},
92 {ID(EOFError), LayoutId::kEOFError, LayoutId::kException, kNoAttributes},
93 {ID(ImportError), LayoutId::kImportError, LayoutId::kException,
94 kImportErrorAttributes},
95 {ID(LookupError), LayoutId::kLookupError, LayoutId::kException,
96 kNoAttributes},
97 {ID(MemoryError), LayoutId::kMemoryError, LayoutId::kException,
98 kNoAttributes},
99 {ID(NameError), LayoutId::kNameError, LayoutId::kException, kNoAttributes},
100 {ID(OSError), LayoutId::kOSError, LayoutId::kException, kNoAttributes},
101 {ID(ReferenceError), LayoutId::kReferenceError, LayoutId::kException,
102 kNoAttributes},
103 {ID(RuntimeError), LayoutId::kRuntimeError, LayoutId::kException,
104 kNoAttributes},
105 {ID(StopIteration), LayoutId::kStopIteration, LayoutId::kException,
106 kStopIterationAttributes},
107 {ID(StopAsyncIteration), LayoutId::kStopAsyncIteration,
108 LayoutId::kException, kNoAttributes},
109 {ID(SyntaxError), LayoutId::kSyntaxError, LayoutId::kException,
110 kSyntaxErrorAttributes},
111 {ID(SystemError), LayoutId::kSystemError, LayoutId::kException,
112 kNoAttributes},
113 {ID(TypeError), LayoutId::kTypeError, LayoutId::kException, kNoAttributes},
114 {ID(ValueError), LayoutId::kValueError, LayoutId::kException,
115 kNoAttributes},
116 {ID(Warning), LayoutId::kWarning, LayoutId::kException, kNoAttributes},
117 {ID(FloatingPointError), LayoutId::kFloatingPointError,
118 LayoutId::kArithmeticError, kNoAttributes},
119 {ID(OverflowError), LayoutId::kOverflowError, LayoutId::kArithmeticError,
120 kNoAttributes},
121 {ID(ZeroDivisionError), LayoutId::kZeroDivisionError,
122 LayoutId::kArithmeticError, kNoAttributes},
123 {ID(ModuleNotFoundError), LayoutId::kModuleNotFoundError,
124 LayoutId::kImportError, kNoAttributes},
125 {ID(IndexError), LayoutId::kIndexError, LayoutId::kLookupError,
126 kNoAttributes},
127 {ID(KeyError), LayoutId::kKeyError, LayoutId::kLookupError, kNoAttributes},
128 {ID(UnboundLocalError), LayoutId::kUnboundLocalError, LayoutId::kNameError,
129 kNoAttributes},
130 {ID(BlockingIOError), LayoutId::kBlockingIOError, LayoutId::kOSError,
131 kNoAttributes},
132 {ID(ChildProcessError), LayoutId::kChildProcessError, LayoutId::kOSError,
133 kNoAttributes},
134 {ID(ConnectionError), LayoutId::kConnectionError, LayoutId::kOSError,
135 kNoAttributes},
136 {ID(FileExistsError), LayoutId::kFileExistsError, LayoutId::kOSError,
137 kNoAttributes},
138 {ID(FileNotFoundError), LayoutId::kFileNotFoundError, LayoutId::kOSError,
139 kNoAttributes},
140 {ID(InterruptedError), LayoutId::kInterruptedError, LayoutId::kOSError,
141 kNoAttributes},
142 {ID(IsADirectoryError), LayoutId::kIsADirectoryError, LayoutId::kOSError,
143 kNoAttributes},
144 {ID(NotADirectoryError), LayoutId::kNotADirectoryError, LayoutId::kOSError,
145 kNoAttributes},
146 {ID(PermissionError), LayoutId::kPermissionError, LayoutId::kOSError,
147 kNoAttributes},
148 {ID(ProcessLookupError), LayoutId::kProcessLookupError, LayoutId::kOSError,
149 kNoAttributes},
150 {ID(TimeoutError), LayoutId::kTimeoutError, LayoutId::kOSError,
151 kNoAttributes},
152 {ID(BrokenPipeError), LayoutId::kBrokenPipeError,
153 LayoutId::kConnectionError, kNoAttributes},
154 {ID(ConnectionAbortedError), LayoutId::kConnectionAbortedError,
155 LayoutId::kConnectionError, kNoAttributes},
156 {ID(ConnectionRefusedError), LayoutId::kConnectionRefusedError,
157 LayoutId::kConnectionError, kNoAttributes},
158 {ID(ConnectionResetError), LayoutId::kConnectionResetError,
159 LayoutId::kConnectionError, kNoAttributes},
160 {ID(NotImplementedError), LayoutId::kNotImplementedError,
161 LayoutId::kRuntimeError, kNoAttributes},
162 {ID(RecursionError), LayoutId::kRecursionError, LayoutId::kRuntimeError,
163 kNoAttributes},
164 {ID(IndentationError), LayoutId::kIndentationError, LayoutId::kSyntaxError,
165 kNoAttributes},
166 {ID(TabError), LayoutId::kTabError, LayoutId::kIndentationError,
167 kNoAttributes},
168 {ID(UserWarning), LayoutId::kUserWarning, LayoutId::kWarning,
169 kNoAttributes},
170 {ID(DeprecationWarning), LayoutId::kDeprecationWarning, LayoutId::kWarning,
171 kNoAttributes},
172 {ID(PendingDeprecationWarning), LayoutId::kPendingDeprecationWarning,
173 LayoutId::kWarning, kNoAttributes},
174 {ID(SyntaxWarning), LayoutId::kSyntaxWarning, LayoutId::kWarning,
175 kNoAttributes},
176 {ID(RuntimeWarning), LayoutId::kRuntimeWarning, LayoutId::kWarning,
177 kNoAttributes},
178 {ID(FutureWarning), LayoutId::kFutureWarning, LayoutId::kWarning,
179 kNoAttributes},
180 {ID(ImportWarning), LayoutId::kImportWarning, LayoutId::kWarning,
181 kNoAttributes},
182 {ID(UnicodeWarning), LayoutId::kUnicodeWarning, LayoutId::kWarning,
183 kNoAttributes},
184 {ID(BytesWarning), LayoutId::kBytesWarning, LayoutId::kWarning,
185 kNoAttributes},
186 {ID(ResourceWarning), LayoutId::kResourceWarning, LayoutId::kWarning,
187 kNoAttributes},
188 {ID(UnicodeError), LayoutId::kUnicodeError, LayoutId::kValueError,
189 kNoAttributes},
190 {ID(UnicodeDecodeError), LayoutId::kUnicodeDecodeError,
191 LayoutId::kUnicodeError, kUnicodeErrorBaseAttributes},
192 {ID(UnicodeEncodeError), LayoutId::kUnicodeEncodeError,
193 LayoutId::kUnicodeError, kUnicodeErrorBaseAttributes},
194 {ID(UnicodeTranslateError), LayoutId::kUnicodeTranslateError,
195 LayoutId::kUnicodeError, kUnicodeErrorBaseAttributes},
196};
197
198LayoutId errorLayoutFromErrno(int errno_value) {
199 switch (errno_value) {
200 case EACCES:
201 return LayoutId::kPermissionError;
202 case EAGAIN: // duplicates EWOULDBLOCK
203 return LayoutId::kBlockingIOError;
204 case EALREADY:
205 return LayoutId::kBlockingIOError;
206 case EINPROGRESS:
207 return LayoutId::kBlockingIOError;
208 case ECHILD:
209 return LayoutId::kChildProcessError;
210 case ECONNABORTED:
211 return LayoutId::kConnectionAbortedError;
212 case ECONNREFUSED:
213 return LayoutId::kConnectionRefusedError;
214 case ECONNRESET:
215 return LayoutId::kConnectionResetError;
216 case EEXIST:
217 return LayoutId::kFileExistsError;
218 case ENOENT:
219 return LayoutId::kFileNotFoundError;
220 case EINTR:
221 return LayoutId::kInterruptedError;
222 case EISDIR:
223 return LayoutId::kIsADirectoryError;
224 case ENOTDIR:
225 return LayoutId::kNotADirectoryError;
226 case EPERM:
227 return LayoutId::kPermissionError;
228 case EPIPE:
229 return LayoutId::kBrokenPipeError;
230 case ESRCH:
231 return LayoutId::kProcessLookupError;
232 case ETIMEDOUT:
233 return LayoutId::kTimeoutError;
234 default:
235 return LayoutId::kOSError;
236 }
237}
238
239bool givenExceptionMatches(Thread* thread, const Object& given,
240 const Object& exc) {
241 HandleScope scope(thread);
242 if (exc.isTuple()) {
243 Tuple tuple(&scope, *exc);
244 Object item(&scope, NoneType::object());
245 for (word i = 0; i < tuple.length(); i++) {
246 item = tuple.at(i);
247 if (givenExceptionMatches(thread, given, item)) {
248 return true;
249 }
250 }
251 return false;
252 }
253 Runtime* runtime = thread->runtime();
254 Object given_type(&scope, *given);
255 if (runtime->isInstanceOfBaseException(*given_type)) {
256 given_type = runtime->typeOf(*given);
257 }
258 if (runtime->isInstanceOfType(*given_type) &&
259 runtime->isInstanceOfType(*exc)) {
260 Type subtype(&scope, *given_type);
261 Type supertype(&scope, *exc);
262 if (subtype.isBaseExceptionSubclass() &&
263 supertype.isBaseExceptionSubclass()) {
264 return typeIsSubclass(*subtype, *supertype);
265 }
266 }
267 return *given_type == *exc;
268}
269
270RawObject createException(Thread* thread, const Type& type,
271 const Object& value) {
272 if (value.isNoneType()) {
273 return Interpreter::call0(thread, type);
274 }
275 if (thread->runtime()->isInstanceOfTuple(*value)) {
276 HandleScope scope(thread);
277 thread->stackPush(*type);
278 Tuple args(&scope, tupleUnderlying(*value));
279 word length = args.length();
280 for (word i = 0; i < length; i++) {
281 thread->stackPush(args.at(i));
282 }
283 return Interpreter::call(thread, /*nargs=*/length);
284 }
285 return Interpreter::call1(thread, type, value);
286}
287
288void normalizeException(Thread* thread, Object* exc, Object* val,
289 Object* traceback) {
290 Runtime* runtime = thread->runtime();
291 HandleScope scope(thread);
292
293 auto normalize = [&] {
294 if (!runtime->isInstanceOfType(**exc)) return true;
295 Type type(&scope, **exc);
296 if (!type.isBaseExceptionSubclass()) return true;
297 Object value(&scope, **val);
298 Type value_type(&scope, runtime->typeOf(*value));
299
300 // TODO(bsimmers): Extend this to support all the weird cases allowed by
301 // PyObject_IsSubclass.
302 if (!typeIsSubclass(*value_type, *type)) {
303 // value isn't an instance of type. Replace it with type(value).
304 value = createException(thread, type, value);
305 if (value.isError()) return false;
306 *val = *value;
307 } else if (*value_type != *type) {
308 // value_type is more specific than type, so use it instead.
309 *exc = *value_type;
310 }
311
312 return true;
313 };
314
315 // If a new exception is raised during normalization, attempt to normalize
316 // that exception. If this process repeats too many times, give up and throw a
317 // RecursionError. If even that exception fails to normalize, abort.
318 const int normalize_limit = 32;
319 for (word i = 0; i <= normalize_limit; i++) {
320 if (normalize()) return;
321
322 if (i == normalize_limit - 1) {
323 thread->raiseWithFmt(
324 LayoutId::kRecursionError,
325 "maximum recursion depth exceeded while normalizing an exception");
326 }
327
328 *exc = thread->pendingExceptionType();
329 *val = thread->pendingExceptionValue();
330 Object new_tb(&scope, thread->pendingExceptionTraceback());
331 if (!new_tb.isNoneType()) *traceback = *new_tb;
332 thread->clearPendingException();
333 }
334
335 if (runtime->isInstanceOfType(**exc)) {
336 Type type(&scope, **exc);
337 if (type.builtinBase() == LayoutId::kMemoryError) {
338 UNIMPLEMENTED(
339 "Cannot recover from MemoryErrors while normalizing exceptions.");
340 }
341 UNIMPLEMENTED(
342 "Cannot recover from the recursive normalization of an exception.");
343 }
344}
345
346static void printPendingExceptionImpl(Thread* thread, bool set_sys_last_vars) {
347 HandleScope scope(thread);
348 Runtime* runtime = thread->runtime();
349 Object type(&scope, thread->pendingExceptionType());
350 Object system_exit(&scope, runtime->typeAt(LayoutId::kSystemExit));
351 if (givenExceptionMatches(thread, type, system_exit)) {
352 handleSystemExit(thread);
353 }
354
355 Object value(&scope, thread->pendingExceptionValue());
356 Object tb(&scope, thread->pendingExceptionTraceback());
357 thread->clearPendingException();
358 if (type.isNoneType()) return;
359
360 normalizeException(thread, &type, &value, &tb);
361 BaseException exc(&scope, *value);
362 exc.setTraceback(*tb);
363
364 if (set_sys_last_vars) {
365 Module sys(&scope, runtime->findModuleById(ID(sys)));
366 moduleAtPutById(thread, sys, ID(last_type), type);
367 moduleAtPutById(thread, sys, ID(last_value), value);
368 moduleAtPutById(thread, sys, ID(last_traceback), tb);
369 }
370
371 Object hook(&scope,
372 runtime->lookupNameInModule(thread, ID(sys), ID(excepthook)));
373 if (hook.isError()) {
374 writeStderr(thread, "sys.excepthook is missing\n");
375 if (displayException(thread, value, tb).isError()) {
376 thread->clearPendingException();
377 }
378 return;
379 }
380
381 Object result(&scope, Interpreter::call3(thread, hook, type, value, tb));
382 if (!result.isError()) return;
383 Object type2(&scope, thread->pendingExceptionType());
384 if (givenExceptionMatches(thread, type2, system_exit)) {
385 handleSystemExit(thread);
386 }
387 Object value2(&scope, thread->pendingExceptionValue());
388 Object tb2(&scope, thread->pendingExceptionTraceback());
389 thread->clearPendingException();
390 normalizeException(thread, &type2, &value2, &tb2);
391 writeStderr(thread, "Error in sys.excepthook:\n");
392 if (displayException(thread, value2, tb2).isError()) {
393 thread->clearPendingException();
394 }
395 writeStderr(thread, "\nOriginal exception was:\n");
396 if (displayException(thread, value, tb).isError()) {
397 thread->clearPendingException();
398 }
399}
400
401void printPendingException(Thread* thread) {
402 printPendingExceptionImpl(thread, false);
403}
404
405void printPendingExceptionWithSysLastVars(Thread* thread) {
406 printPendingExceptionImpl(thread, true);
407}
408
409// If value has all the attributes of a well-formed SyntaxError, return true and
410// populate all of the given parameters. In that case, filename will be a str
411// and text will be None or a str. Otherwise, return false and the contents of
412// all out parameters are unspecified.
413static bool parseSyntaxError(Thread* thread, const Object& value,
414 Object* message, Object* filename, word* lineno,
415 word* offset, Object* text) {
416 auto fail = [thread] {
417 thread->clearPendingException();
418 return false;
419 };
420
421 HandleScope scope(thread);
422 Runtime* runtime = thread->runtime();
423 Object result(&scope, runtime->attributeAtById(thread, value, ID(msg)));
424 if (result.isError()) return fail();
425 *message = *result;
426
427 result = runtime->attributeAtById(thread, value, ID(filename));
428 if (result.isError()) return fail();
429 if (result.isNoneType()) {
430 *filename = runtime->newStrFromCStr("<string>");
431 } else if (runtime->isInstanceOfStr(*result)) {
432 *filename = *result;
433 } else {
434 return false;
435 }
436
437 result = runtime->attributeAtById(thread, value, ID(lineno));
438 if (result.isError()) return fail();
439 if (runtime->isInstanceOfInt(*result)) {
440 Int ival(&scope, intUnderlying(*result));
441 if (ival.numDigits() > 1) return false;
442 *lineno = ival.asWord();
443 } else {
444 return false;
445 }
446
447 result = runtime->attributeAtById(thread, value, ID(offset));
448 if (result.isError()) return fail();
449 if (result.isNoneType()) {
450 *offset = -1;
451 } else if (runtime->isInstanceOfInt(*result)) {
452 Int ival(&scope, intUnderlying(*result));
453 if (ival.numDigits() > 1) return false;
454 *offset = ival.asWord();
455 } else {
456 return false;
457 }
458
459 result = runtime->attributeAtById(thread, value, ID(text));
460 if (result.isError()) return fail();
461 if (result.isNoneType() || runtime->isInstanceOfStr(*result)) {
462 *text = *result;
463 } else {
464 return false;
465 }
466
467 return true;
468}
469
470static RawObject fileWriteString(Thread* thread, const Object& file,
471 const char* c_str) {
472 HandleScope scope(thread);
473 Str str(&scope, thread->runtime()->newStrFromCStr(c_str));
474 return thread->invokeMethod2(file, ID(write), str);
475}
476
477static RawObject fileWriteObjectStr(Thread* thread, const Object& file,
478 const Object& str) {
479 return thread->invokeMethod2(file, ID(write), str);
480}
481
482// Used to wrap an expression that may return an Error that should be forwarded,
483// or a value that should be ignored otherwise.
484//
485// TODO(bsimmers): Most of the functions that use this should be rewritten in
486// Python once we have enough library support to do so, then we can delete the
487// macro.
488#define MAY_RAISE(expr) \
489 { \
490 RawObject result = (expr); \
491 if (result.isError()) return result; \
492 }
493
494// Print the source code snippet from a SyntaxError, with a ^ indicating the
495// position of the error.
496static RawObject printErrorText(Thread* thread, const Object& file, word offset,
497 const Object& text_obj) {
498 HandleScope scope(thread);
499 Str text_str(&scope, *text_obj);
500 word length = text_str.length();
501 // This is gross, but it greatly simplifies the string scanning done by the
502 // rest of the function, and makes maintaining compatibility with CPython
503 // easier.
504 unique_c_ptr<char[]> text_owner(text_str.toCStr());
505 const char* text = text_owner.get();
506
507 // Adjust text and offset to not print any lines before the one that has the
508 // cursor.
509 if (offset >= 0) {
510 if (offset > 0 && offset == length && text[offset - 1] == '\n') {
511 offset--;
512 }
513 for (;;) {
514 const char* newline = std::strchr(text, '\n');
515 if (newline == nullptr || newline - text >= offset) break;
516 word adjust = newline + 1 - text;
517 offset -= adjust;
518 length -= adjust;
519 text += adjust;
520 }
521 while (*text == ' ' || *text == '\t' || *text == '\f') {
522 text++;
523 offset--;
524 }
525 }
526
527 MAY_RAISE(fileWriteString(thread, file, " "));
528 MAY_RAISE(fileWriteString(thread, file, text));
529 if (*text == '\0' || text[length - 1] != '\n') {
530 MAY_RAISE(fileWriteString(thread, file, "\n"));
531 }
532 if (offset == -1) return NoneType::object();
533 MAY_RAISE(fileWriteString(thread, file, " "));
534 while (--offset > 0) MAY_RAISE(fileWriteString(thread, file, " "));
535 MAY_RAISE(fileWriteString(thread, file, "^\n"));
536 return NoneType::object();
537}
538
539// Print the traceback, type, and message of a single exception.
540static RawObject printSingleException(Thread* thread, const Object& file,
541 const Object& value_in) {
542 HandleScope scope(thread);
543 Runtime* runtime = thread->runtime();
544 Object value(&scope, *value_in);
545 Type type(&scope, runtime->typeOf(*value));
546 Str type_name(&scope, type.name());
547
548 if (!runtime->isInstanceOfBaseException(*value)) {
549 MAY_RAISE(fileWriteString(
550 thread, file,
551 "TypeError: print_exception(): Exception expected for value, "));
552
553 MAY_RAISE(fileWriteObjectStr(thread, file, type_name));
554 MAY_RAISE(fileWriteString(thread, file, " found\n"));
555 return NoneType::object();
556 }
557
558 BaseException exc(&scope, *value);
559 Object tb_obj(&scope, exc.traceback());
560 if (tb_obj.isTraceback()) {
561 Traceback traceback(&scope, *tb_obj);
562 MAY_RAISE(tracebackWrite(thread, traceback, file));
563 }
564
565 if (runtime->attributeAtById(thread, value, ID(print_file_and_line))
566 .isError()) {
567 // Ignore the AttributeError or whatever else went wrong during lookup.
568 thread->clearPendingException();
569 } else {
570 Object message(&scope, NoneType::object());
571 Object filename(&scope, NoneType::object());
572 Object text(&scope, NoneType::object());
573 word lineno, offset;
574 if (parseSyntaxError(thread, value, &message, &filename, &lineno, &offset,
575 &text)) {
576 value = *message;
577 Str filename_str(&scope, *filename);
578 unique_c_ptr<char[]> filename_c_str(filename_str.toCStr());
579 Str line(&scope, runtime->newStrFromFmt(" File \"%s\", line %w\n",
580 filename_c_str.get(), lineno));
581 MAY_RAISE(fileWriteObjectStr(thread, file, line));
582 if (!text.isNoneType()) {
583 MAY_RAISE(printErrorText(thread, file, offset, text));
584 }
585 }
586 }
587
588 Object module(&scope, runtime->attributeAtById(thread, type, ID(__module__)));
589 if (module.isError() || !runtime->isInstanceOfStr(*module)) {
590 if (module.isError()) thread->clearPendingException();
591 MAY_RAISE(fileWriteString(thread, file, "<unknown>"));
592 } else {
593 Str module_str(&scope, *module);
594 if (!module_str.equals(runtime->symbols()->at(ID(builtins)))) {
595 MAY_RAISE(fileWriteObjectStr(thread, file, module_str));
596 MAY_RAISE(fileWriteString(thread, file, "."));
597 }
598 }
599
600 MAY_RAISE(fileWriteObjectStr(thread, file, type_name));
601 Object str_obj(&scope, thread->invokeFunction1(ID(builtins), ID(str), value));
602 if (str_obj.isError()) {
603 thread->clearPendingException();
604 MAY_RAISE(fileWriteString(thread, file, ": <exception str() failed>"));
605 } else {
606 Str str(&scope, strUnderlying(*str_obj));
607 if (str != Str::empty()) {
608 MAY_RAISE(fileWriteString(thread, file, ": "));
609 MAY_RAISE(fileWriteObjectStr(thread, file, str));
610 }
611 }
612
613 MAY_RAISE(fileWriteString(thread, file, "\n"));
614 return NoneType::object();
615}
616
617// Print the given exception and any cause or context exceptions it chains to.
618static RawObject printExceptionChain(Thread* thread, const Object& file,
619 const Object& value, const Set& seen) {
620 Runtime* runtime = thread->runtime();
621 HandleScope scope(thread);
622 Object hash_obj(&scope, Interpreter::hash(thread, value));
623 if (hash_obj.isErrorException()) return *hash_obj;
624 word hash = SmallInt::cast(*hash_obj).value();
625 setAdd(thread, seen, value, hash);
626
627 if (runtime->isInstanceOfBaseException(*value)) {
628 BaseException exc(&scope, *value);
629 Object cause(&scope, exc.cause());
630 Object context(&scope, exc.context());
631 if (!cause.isNoneType()) {
632 hash_obj = Interpreter::hash(thread, cause);
633 if (hash_obj.isErrorException()) return *hash_obj;
634 hash = SmallInt::cast(*hash_obj).value();
635 if (!setIncludes(thread, seen, cause, hash)) {
636 MAY_RAISE(printExceptionChain(thread, file, cause, seen));
637 MAY_RAISE(
638 fileWriteString(thread, file,
639 "\nThe above exception was the direct cause of the "
640 "following exception:\n\n"));
641 }
642 } else if (!context.isNoneType() &&
643 exc.suppressContext() != RawBool::trueObj()) {
644 hash_obj = Interpreter::hash(thread, context);
645 if (hash_obj.isErrorException()) return *hash_obj;
646 hash = SmallInt::cast(*hash_obj).value();
647 if (!setIncludes(thread, seen, context, hash)) {
648 MAY_RAISE(printExceptionChain(thread, file, context, seen));
649 MAY_RAISE(
650 fileWriteString(thread, file,
651 "\nDuring handling of the above exception, another "
652 "exception occurred:\n\n"));
653 }
654 }
655 }
656
657 MAY_RAISE(printSingleException(thread, file, value));
658 return NoneType::object();
659}
660
661#undef MAY_RAISE
662
663RawObject displayException(Thread* thread, const Object& value,
664 const Object& traceback) {
665 HandleScope scope(thread);
666 Runtime* runtime = thread->runtime();
667 if (runtime->isInstanceOfBaseException(*value) && traceback.isTraceback()) {
668 BaseException exc(&scope, *value);
669 if (exc.traceback().isNoneType()) exc.setTraceback(*traceback);
670 }
671
672 ValueCell sys_stderr_cell(&scope, runtime->sysStderr());
673 if (sys_stderr_cell.isUnbound()) {
674 fputs("lost sys.stderr\n", stderr);
675 return NoneType::object();
676 }
677 Object sys_stderr(&scope, sys_stderr_cell.value());
678 if (sys_stderr.isNoneType()) {
679 return NoneType::object();
680 }
681 Set seen(&scope, runtime->newSet());
682 return printExceptionChain(thread, sys_stderr, value, seen);
683}
684
685void handleSystemExit(Thread* thread) {
686 auto do_exit = [thread](int exit_code) {
687 thread->clearPendingException();
688 Runtime* runtime = thread->runtime();
689 delete runtime;
690 std::exit(exit_code);
691 };
692
693 Runtime* runtime = thread->runtime();
694 HandleScope scope(thread);
695 Object arg(&scope, thread->pendingExceptionValue());
696 if (runtime->isInstanceOfSystemExit(*arg)) {
697 // The exception could be raised by either native or managed code. If
698 // native, there will be no SystemExit object. If managed, there will
699 // be one to unpack.
700 SystemExit exc(&scope, *arg);
701 arg = exc.code();
702 }
703 if (arg.isNoneType()) do_exit(EXIT_SUCCESS);
704 if (runtime->isInstanceOfInt(*arg)) {
705 // We could convert and check for overflow error, but the overflow error
706 // should get cleared anyway.
707 do_exit(intUnderlying(*arg).asWordSaturated());
708 }
709
710 // The calls below can't have an exception pending
711 thread->clearPendingException();
712
713 Object result(&scope, thread->invokeMethod1(arg, ID(__str__)));
714 if (!runtime->isInstanceOfStr(*result)) {
715 // The calls below can't have an exception pending
716 thread->clearPendingException();
717 // No __repr__ method or __repr__ raised. Either way, we can't handle it.
718 result = runtime->newStrFromCStr("");
719 }
720
721 Str result_str(&scope, *result);
722 ValueCell sys_stderr_cell(&scope, runtime->sysStderr());
723 if (sys_stderr_cell.isUnbound() || sys_stderr_cell.value().isNoneType()) {
724 unique_c_ptr<char> buf(result_str.toCStr());
725 fwrite(buf.get(), 1, result_str.length(), stderr);
726 fputc('\n', stderr);
727 } else {
728 Object file(&scope, sys_stderr_cell.value());
729 fileWriteObjectStr(thread, file, result_str);
730 thread->clearPendingException();
731 fileWriteString(thread, file, "\n");
732 }
733 do_exit(EXIT_FAILURE);
734}
735
736RawObject METH(BaseException, __init__)(Thread* thread, Arguments args) {
737 HandleScope scope(thread);
738 Object self_obj(&scope, args.get(0));
739 if (!thread->runtime()->isInstanceOfBaseException(*self_obj)) {
740 return thread->raiseRequiresType(self_obj, ID(BaseException));
741 }
742 BaseException self(&scope, *self_obj);
743 Object args_obj(&scope, args.get(1));
744 self.setArgs(*args_obj);
745 self.setCause(Unbound::object());
746 self.setContext(Unbound::object());
747 self.setTraceback(Unbound::object());
748 self.setSuppressContext(RawBool::falseObj());
749 return NoneType::object();
750}
751
752RawObject METH(StopIteration, __init__)(Thread* thread, Arguments args) {
753 HandleScope scope(thread);
754 Object self_obj(&scope, args.get(0));
755 if (!thread->runtime()->isInstanceOfStopIteration(*self_obj)) {
756 return thread->raiseRequiresType(self_obj, ID(StopIteration));
757 }
758 StopIteration self(&scope, *self_obj);
759 Object args_obj(&scope, args.get(1));
760 self.setArgs(*args_obj);
761 self.setCause(Unbound::object());
762 self.setContext(Unbound::object());
763 self.setTraceback(Unbound::object());
764 self.setSuppressContext(RawBool::falseObj());
765 Tuple tuple(&scope, self.args());
766 if (tuple.length() > 0) self.setValue(tuple.at(0));
767 return NoneType::object();
768}
769
770RawObject METH(SystemExit, __init__)(Thread* thread, Arguments args) {
771 HandleScope scope(thread);
772 Object self_obj(&scope, args.get(0));
773 if (!thread->runtime()->isInstanceOfSystemExit(*self_obj)) {
774 return thread->raiseRequiresType(self_obj, ID(SystemExit));
775 }
776 SystemExit self(&scope, *self_obj);
777 RawObject result = METH(BaseException, __init__)(thread, args);
778 if (result.isError()) {
779 return result;
780 }
781 Tuple tuple(&scope, self.args());
782 if (tuple.length() > 0) {
783 self.setCode(tuple.at(0));
784 }
785 return NoneType::object();
786}
787
788void initializeExceptionTypes(Thread* thread) {
789 HandleScope scope(thread);
790 Runtime* runtime = thread->runtime();
791 Layout layout(&scope, runtime->layoutAt(LayoutId::kNoneType));
792 Type type(&scope, layout.describedType());
793 for (ExceptionTypeSpec spec : kExceptionSpecs) {
794 Layout super_layout(&scope, runtime->layoutAt(spec.superclass_id));
795 word size = (Tuple::cast(super_layout.inObjectAttributes()).length() +
796 spec.attributes.length()) *
797 kPointerSize;
798 type = addBuiltinType(thread, spec.name, spec.layout_id, spec.superclass_id,
799 spec.attributes, size, /*basetype=*/true);
800 builtinTypeEnableTupleOverflow(thread, type);
801 }
802}
803
804} // namespace py