1 /*
2 * This file is part of the MicroPython project, http://micropython.org/
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (c) 2013, 2014 Damien P. George
7 * Copyright (c) 2014-2016 Paul Sokolovsky
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a copy
10 * of this software and associated documentation files (the "Software"), to deal
11 * in the Software without restriction, including without limitation the rights
12 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 * copies of the Software, and to permit persons to whom the Software is
14 * furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 * THE SOFTWARE.
26 */
27
28 #include <string.h>
29 #include <stdarg.h>
30 #include <assert.h>
31 #include <stdio.h>
32
33 #include "py/objlist.h"
34 #include "py/objstr.h"
35 #include "py/objtuple.h"
36 #include "py/objtype.h"
37 #include "py/runtime.h"
38 #include "py/gc.h"
39 #include "py/mperrno.h"
40
41 #if MICROPY_ROM_TEXT_COMPRESSION && !defined(NO_QSTR)
42 // Extract the MP_MAX_UNCOMPRESSED_TEXT_LEN macro from "genhdr/compressed.data.h".
43 // Only need this if compression enabled and in a regular build (i.e. not during QSTR extraction).
44 #define MP_MATCH_COMPRESSED(...) // Ignore
45 #define MP_COMPRESSED_DATA(...) // Ignore
46 #include "genhdr/compressed.data.h"
47 #undef MP_MATCH_COMPRESSED
48 #undef MP_COMPRESSED_DATA
49 #endif
50
51 // Number of items per traceback entry (file, line, block)
52 #define TRACEBACK_ENTRY_LEN (3)
53
54 // Optionally allocated buffer for storing some traceback, the tuple argument,
55 // and possible string object and data, for when the heap is locked.
56 #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
57
58 // When used the layout of the emergency exception buffer is:
59 // - traceback entry (file, line, block)
60 // - traceback entry (file, line, block)
61 // - mp_obj_tuple_t object
62 // - n_args * mp_obj_t for tuple
63 // - mp_obj_str_t object
64 // - string data
65 #define EMG_BUF_TRACEBACK_OFFSET (0)
66 #define EMG_BUF_TRACEBACK_SIZE (2 * TRACEBACK_ENTRY_LEN * sizeof(size_t))
67 #define EMG_BUF_TUPLE_OFFSET (EMG_BUF_TRACEBACK_OFFSET + EMG_BUF_TRACEBACK_SIZE)
68 #define EMG_BUF_TUPLE_SIZE(n_args) (sizeof(mp_obj_tuple_t) + n_args * sizeof(mp_obj_t))
69 #define EMG_BUF_STR_OFFSET (EMG_BUF_TUPLE_OFFSET + EMG_BUF_TUPLE_SIZE(1))
70 #define EMG_BUF_STR_BUF_OFFSET (EMG_BUF_STR_OFFSET + sizeof(mp_obj_str_t))
71
72 #if MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE > 0
73 #define mp_emergency_exception_buf_size MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE
74
mp_init_emergency_exception_buf(void)75 void mp_init_emergency_exception_buf(void) {
76 // Nothing to do since the buffer was declared statically. We put this
77 // definition here so that the calling code can call this function
78 // regardless of how its configured (makes the calling code a bit cleaner).
79 }
80
81 #else
82 #define mp_emergency_exception_buf_size MP_STATE_VM(mp_emergency_exception_buf_size)
83
mp_init_emergency_exception_buf(void)84 void mp_init_emergency_exception_buf(void) {
85 mp_emergency_exception_buf_size = 0;
86 MP_STATE_VM(mp_emergency_exception_buf) = NULL;
87 }
88
mp_alloc_emergency_exception_buf(mp_obj_t size_in)89 mp_obj_t mp_alloc_emergency_exception_buf(mp_obj_t size_in) {
90 mp_int_t size = mp_obj_get_int(size_in);
91 void *buf = NULL;
92 if (size > 0) {
93 buf = m_new(byte, size);
94 }
95
96 int old_size = mp_emergency_exception_buf_size;
97 void *old_buf = MP_STATE_VM(mp_emergency_exception_buf);
98
99 // Update the 2 variables atomically so that an interrupt can't occur
100 // between the assignments.
101 mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION();
102 mp_emergency_exception_buf_size = size;
103 MP_STATE_VM(mp_emergency_exception_buf) = buf;
104 MICROPY_END_ATOMIC_SECTION(atomic_state);
105
106 if (old_buf != NULL) {
107 m_del(byte, old_buf, old_size);
108 }
109 return mp_const_none;
110 }
111 #endif
112 #endif // MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
113
get_native_exception(mp_obj_t self_in)114 STATIC mp_obj_exception_t *get_native_exception(mp_obj_t self_in) {
115 assert(mp_obj_is_exception_instance(self_in));
116 if (mp_obj_is_native_exception_instance(self_in)) {
117 return MP_OBJ_TO_PTR(self_in);
118 } else {
119 return MP_OBJ_TO_PTR(((mp_obj_instance_t *)MP_OBJ_TO_PTR(self_in))->subobj[0]);
120 }
121 }
122
decompress_error_text_maybe(mp_obj_exception_t * o)123 STATIC void decompress_error_text_maybe(mp_obj_exception_t *o) {
124 #if MICROPY_ROM_TEXT_COMPRESSION
125 if (o->args->len == 1 && mp_obj_is_type(o->args->items[0], &mp_type_str)) {
126 mp_obj_str_t *o_str = MP_OBJ_TO_PTR(o->args->items[0]);
127 if (MP_IS_COMPRESSED_ROM_STRING(o_str->data)) {
128 byte *buf = m_new_maybe(byte, MP_MAX_UNCOMPRESSED_TEXT_LEN + 1);
129 if (!buf) {
130 #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
131 // Try and use the emergency exception buf if enough space is available.
132 buf = (byte *)((uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + EMG_BUF_STR_BUF_OFFSET);
133 size_t avail = (uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + mp_emergency_exception_buf_size - buf;
134 if (avail < MP_MAX_UNCOMPRESSED_TEXT_LEN + 1) {
135 // No way to decompress, fallback to no message text.
136 o->args = (mp_obj_tuple_t *)&mp_const_empty_tuple_obj;
137 return;
138 }
139 #else
140 o->args = (mp_obj_tuple_t *)&mp_const_empty_tuple_obj;
141 return;
142 #endif
143 }
144 mp_decompress_rom_string(buf, (mp_rom_error_text_t)o_str->data);
145 o_str->data = buf;
146 o_str->len = strlen((const char *)buf);
147 o_str->hash = 0;
148 }
149 // Lazily compute the string hash.
150 if (o_str->hash == 0) {
151 o_str->hash = qstr_compute_hash(o_str->data, o_str->len);
152 }
153 }
154 #endif
155 }
156
mp_obj_exception_print(const mp_print_t * print,mp_obj_t o_in,mp_print_kind_t kind)157 void mp_obj_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) {
158 mp_obj_exception_t *o = MP_OBJ_TO_PTR(o_in);
159 mp_print_kind_t k = kind & ~PRINT_EXC_SUBCLASS;
160 bool is_subclass = kind & PRINT_EXC_SUBCLASS;
161 if (!is_subclass && (k == PRINT_REPR || k == PRINT_EXC)) {
162 mp_print_str(print, qstr_str(o->base.type->name));
163 }
164
165 if (k == PRINT_EXC) {
166 mp_print_str(print, ": ");
167 }
168
169 decompress_error_text_maybe(o);
170
171 if (k == PRINT_STR || k == PRINT_EXC) {
172 if (o->args == NULL || o->args->len == 0) {
173 mp_print_str(print, "");
174 return;
175 }
176
177 #if MICROPY_PY_UERRNO
178 // try to provide a nice OSError error message
179 if (o->base.type == &mp_type_OSError && o->args->len > 0 && o->args->len < 3 && mp_obj_is_small_int(o->args->items[0])) {
180 qstr qst = mp_errno_to_str(o->args->items[0]);
181 if (qst != MP_QSTRnull) {
182 mp_printf(print, "[Errno " INT_FMT "] %q", MP_OBJ_SMALL_INT_VALUE(o->args->items[0]), qst);
183 if (o->args->len > 1) {
184 mp_print_str(print, ": ");
185 mp_obj_print_helper(print, o->args->items[1], PRINT_STR);
186 }
187 return;
188 }
189 }
190 #endif
191
192 if (o->args->len == 1) {
193 mp_obj_print_helper(print, o->args->items[0], PRINT_STR);
194 return;
195 }
196 }
197
198 mp_obj_tuple_print(print, MP_OBJ_FROM_PTR(o->args), kind);
199 }
200
mp_obj_exception_make_new(const mp_obj_type_t * type,size_t n_args,size_t n_kw,const mp_obj_t * args)201 mp_obj_t mp_obj_exception_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
202 mp_arg_check_num(n_args, n_kw, 0, MP_OBJ_FUN_ARGS_MAX, false);
203
204 // Try to allocate memory for the exception, with fallback to emergency exception object
205 mp_obj_exception_t *o_exc = m_new_obj_maybe(mp_obj_exception_t);
206 if (o_exc == NULL) {
207 o_exc = &MP_STATE_VM(mp_emergency_exception_obj);
208 }
209
210 // Populate the exception object
211 o_exc->base.type = type;
212 o_exc->traceback_data = NULL;
213
214 mp_obj_tuple_t *o_tuple;
215 if (n_args == 0) {
216 // No args, can use the empty tuple straightaway
217 o_tuple = (mp_obj_tuple_t *)&mp_const_empty_tuple_obj;
218 } else {
219 // Try to allocate memory for the tuple containing the args
220 o_tuple = m_new_obj_var_maybe(mp_obj_tuple_t, mp_obj_t, n_args);
221
222 #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
223 // If we are called by mp_obj_new_exception_msg_varg then it will have
224 // reserved room (after the traceback data) for a tuple with 1 element.
225 // Otherwise we are free to use the whole buffer after the traceback data.
226 if (o_tuple == NULL && mp_emergency_exception_buf_size >=
227 (mp_int_t)(EMG_BUF_TUPLE_OFFSET + EMG_BUF_TUPLE_SIZE(n_args))) {
228 o_tuple = (mp_obj_tuple_t *)
229 ((uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + EMG_BUF_TUPLE_OFFSET);
230 }
231 #endif
232
233 if (o_tuple == NULL) {
234 // No memory for a tuple, fallback to an empty tuple
235 o_tuple = (mp_obj_tuple_t *)&mp_const_empty_tuple_obj;
236 } else {
237 // Have memory for a tuple so populate it
238 o_tuple->base.type = &mp_type_tuple;
239 o_tuple->len = n_args;
240 memcpy(o_tuple->items, args, n_args * sizeof(mp_obj_t));
241 }
242 }
243
244 // Store the tuple of args in the exception object
245 o_exc->args = o_tuple;
246
247 return MP_OBJ_FROM_PTR(o_exc);
248 }
249
250 // Get exception "value" - that is, first argument, or None
mp_obj_exception_get_value(mp_obj_t self_in)251 mp_obj_t mp_obj_exception_get_value(mp_obj_t self_in) {
252 mp_obj_exception_t *self = get_native_exception(self_in);
253 if (self->args->len == 0) {
254 return mp_const_none;
255 } else {
256 decompress_error_text_maybe(self);
257 return self->args->items[0];
258 }
259 }
260
mp_obj_exception_attr(mp_obj_t self_in,qstr attr,mp_obj_t * dest)261 void mp_obj_exception_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
262 mp_obj_exception_t *self = MP_OBJ_TO_PTR(self_in);
263 if (dest[0] != MP_OBJ_NULL) {
264 // store/delete attribute
265 if (attr == MP_QSTR___traceback__ && dest[1] == mp_const_none) {
266 // We allow 'exc.__traceback__ = None' assignment as low-level
267 // optimization of pre-allocating exception instance and raising
268 // it repeatedly - this avoids memory allocation during raise.
269 // However, uPy will keep adding traceback entries to such
270 // exception instance, so before throwing it, traceback should
271 // be cleared like above.
272 self->traceback_len = 0;
273 dest[0] = MP_OBJ_NULL; // indicate success
274 }
275 return;
276 }
277 if (attr == MP_QSTR_args) {
278 decompress_error_text_maybe(self);
279 dest[0] = MP_OBJ_FROM_PTR(self->args);
280 } else if (attr == MP_QSTR_value || attr == MP_QSTR_errno) {
281 // These are aliases for args[0]: .value for StopIteration and .errno for OSError.
282 // For efficiency let these attributes apply to all exception instances.
283 dest[0] = mp_obj_exception_get_value(self_in);
284 }
285 }
286
287 const mp_obj_type_t mp_type_BaseException = {
288 { &mp_type_type },
289 .name = MP_QSTR_BaseException,
290 .print = mp_obj_exception_print,
291 .make_new = mp_obj_exception_make_new,
292 .attr = mp_obj_exception_attr,
293 };
294
295 // *FORMAT-OFF*
296
297 // List of all exceptions, arranged as in the table at:
298 // http://docs.python.org/3/library/exceptions.html
MP_DEFINE_EXCEPTION(SystemExit,BaseException)299 MP_DEFINE_EXCEPTION(SystemExit, BaseException)
300 MP_DEFINE_EXCEPTION(KeyboardInterrupt, BaseException)
301 MP_DEFINE_EXCEPTION(GeneratorExit, BaseException)
302 MP_DEFINE_EXCEPTION(Exception, BaseException)
303 #if MICROPY_PY_ASYNC_AWAIT
304 MP_DEFINE_EXCEPTION(StopAsyncIteration, Exception)
305 #endif
306 MP_DEFINE_EXCEPTION(StopIteration, Exception)
307 MP_DEFINE_EXCEPTION(ArithmeticError, Exception)
308 //MP_DEFINE_EXCEPTION(FloatingPointError, ArithmeticError)
309 MP_DEFINE_EXCEPTION(OverflowError, ArithmeticError)
310 MP_DEFINE_EXCEPTION(ZeroDivisionError, ArithmeticError)
311 MP_DEFINE_EXCEPTION(AssertionError, Exception)
312 MP_DEFINE_EXCEPTION(AttributeError, Exception)
313 //MP_DEFINE_EXCEPTION(BufferError, Exception)
314 MP_DEFINE_EXCEPTION(EOFError, Exception)
315 MP_DEFINE_EXCEPTION(ImportError, Exception)
316 MP_DEFINE_EXCEPTION(LookupError, Exception)
317 MP_DEFINE_EXCEPTION(IndexError, LookupError)
318 MP_DEFINE_EXCEPTION(KeyError, LookupError)
319 MP_DEFINE_EXCEPTION(MemoryError, Exception)
320 MP_DEFINE_EXCEPTION(NameError, Exception)
321 /*
322 MP_DEFINE_EXCEPTION(UnboundLocalError, NameError)
323 */
324 MP_DEFINE_EXCEPTION(OSError, Exception)
325 /*
326 MP_DEFINE_EXCEPTION(BlockingIOError, OSError)
327 MP_DEFINE_EXCEPTION(ChildProcessError, OSError)
328 MP_DEFINE_EXCEPTION(ConnectionError, OSError)
329 MP_DEFINE_EXCEPTION(BrokenPipeError, ConnectionError)
330 MP_DEFINE_EXCEPTION(ConnectionAbortedError, ConnectionError)
331 MP_DEFINE_EXCEPTION(ConnectionRefusedError, ConnectionError)
332 MP_DEFINE_EXCEPTION(ConnectionResetError, ConnectionError)
333 MP_DEFINE_EXCEPTION(InterruptedError, OSError)
334 MP_DEFINE_EXCEPTION(IsADirectoryError, OSError)
335 MP_DEFINE_EXCEPTION(NotADirectoryError, OSError)
336 MP_DEFINE_EXCEPTION(PermissionError, OSError)
337 MP_DEFINE_EXCEPTION(ProcessLookupError, OSError)
338 MP_DEFINE_EXCEPTION(TimeoutError, OSError)
339 MP_DEFINE_EXCEPTION(FileExistsError, OSError)
340 MP_DEFINE_EXCEPTION(FileNotFoundError, OSError)
341 MP_DEFINE_EXCEPTION(ReferenceError, Exception)
342 */
343 MP_DEFINE_EXCEPTION(RuntimeError, Exception)
344 MP_DEFINE_EXCEPTION(NotImplementedError, RuntimeError)
345 MP_DEFINE_EXCEPTION(SyntaxError, Exception)
346 MP_DEFINE_EXCEPTION(IndentationError, SyntaxError)
347 /*
348 MP_DEFINE_EXCEPTION(TabError, IndentationError)
349 */
350 //MP_DEFINE_EXCEPTION(SystemError, Exception)
351 MP_DEFINE_EXCEPTION(TypeError, Exception)
352 #if MICROPY_EMIT_NATIVE
353 MP_DEFINE_EXCEPTION(ViperTypeError, TypeError)
354 #endif
355 MP_DEFINE_EXCEPTION(ValueError, Exception)
356 #if MICROPY_PY_BUILTINS_STR_UNICODE
357 MP_DEFINE_EXCEPTION(UnicodeError, ValueError)
358 //TODO: Implement more UnicodeError subclasses which take arguments
359 #endif
360 /*
361 MP_DEFINE_EXCEPTION(Warning, Exception)
362 MP_DEFINE_EXCEPTION(DeprecationWarning, Warning)
363 MP_DEFINE_EXCEPTION(PendingDeprecationWarning, Warning)
364 MP_DEFINE_EXCEPTION(RuntimeWarning, Warning)
365 MP_DEFINE_EXCEPTION(SyntaxWarning, Warning)
366 MP_DEFINE_EXCEPTION(UserWarning, Warning)
367 MP_DEFINE_EXCEPTION(FutureWarning, Warning)
368 MP_DEFINE_EXCEPTION(ImportWarning, Warning)
369 MP_DEFINE_EXCEPTION(UnicodeWarning, Warning)
370 MP_DEFINE_EXCEPTION(BytesWarning, Warning)
371 MP_DEFINE_EXCEPTION(ResourceWarning, Warning)
372 */
373
374 // *FORMAT-ON*
375
376 mp_obj_t mp_obj_new_exception(const mp_obj_type_t *exc_type) {
377 assert(exc_type->make_new == mp_obj_exception_make_new);
378 return mp_obj_exception_make_new(exc_type, 0, 0, NULL);
379 }
380
mp_obj_new_exception_args(const mp_obj_type_t * exc_type,size_t n_args,const mp_obj_t * args)381 mp_obj_t mp_obj_new_exception_args(const mp_obj_type_t *exc_type, size_t n_args, const mp_obj_t *args) {
382 assert(exc_type->make_new == mp_obj_exception_make_new);
383 return mp_obj_exception_make_new(exc_type, n_args, 0, args);
384 }
385
386 #if MICROPY_ERROR_REPORTING != MICROPY_ERROR_REPORTING_NONE
387
mp_obj_new_exception_msg(const mp_obj_type_t * exc_type,mp_rom_error_text_t msg)388 mp_obj_t mp_obj_new_exception_msg(const mp_obj_type_t *exc_type, mp_rom_error_text_t msg) {
389 // Check that the given type is an exception type
390 assert(exc_type->make_new == mp_obj_exception_make_new);
391
392 // Try to allocate memory for the message
393 mp_obj_str_t *o_str = m_new_obj_maybe(mp_obj_str_t);
394
395 #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
396 // If memory allocation failed and there is an emergency buffer then try to use
397 // that buffer to store the string object, reserving room at the start for the
398 // traceback and 1-tuple.
399 if (o_str == NULL
400 && mp_emergency_exception_buf_size >= (mp_int_t)(EMG_BUF_STR_OFFSET + sizeof(mp_obj_str_t))) {
401 o_str = (mp_obj_str_t *)((uint8_t *)MP_STATE_VM(mp_emergency_exception_buf)
402 + EMG_BUF_STR_OFFSET);
403 }
404 #endif
405
406 if (o_str == NULL) {
407 // No memory for the string object so create the exception with no args
408 return mp_obj_exception_make_new(exc_type, 0, 0, NULL);
409 }
410
411 // Create the string object and call mp_obj_exception_make_new to create the exception
412 o_str->base.type = &mp_type_str;
413 o_str->len = strlen((const char *)msg);
414 o_str->data = (const byte *)msg;
415 #if MICROPY_ROM_TEXT_COMPRESSION
416 o_str->hash = 0; // will be computed only if string object is accessed
417 #else
418 o_str->hash = qstr_compute_hash(o_str->data, o_str->len);
419 #endif
420 mp_obj_t arg = MP_OBJ_FROM_PTR(o_str);
421 return mp_obj_exception_make_new(exc_type, 1, 0, &arg);
422 }
423
424 // The following struct and function implement a simple printer that conservatively
425 // allocates memory and truncates the output data if no more memory can be obtained.
426 // It leaves room for a null byte at the end of the buffer.
427
428 struct _exc_printer_t {
429 bool allow_realloc;
430 size_t alloc;
431 size_t len;
432 byte *buf;
433 };
434
exc_add_strn(void * data,const char * str,size_t len)435 STATIC void exc_add_strn(void *data, const char *str, size_t len) {
436 struct _exc_printer_t *pr = data;
437 if (pr->len + len >= pr->alloc) {
438 // Not enough room for data plus a null byte so try to grow the buffer
439 if (pr->allow_realloc) {
440 size_t new_alloc = pr->alloc + len + 16;
441 byte *new_buf = m_renew_maybe(byte, pr->buf, pr->alloc, new_alloc, true);
442 if (new_buf == NULL) {
443 pr->allow_realloc = false;
444 len = pr->alloc - pr->len - 1;
445 } else {
446 pr->alloc = new_alloc;
447 pr->buf = new_buf;
448 }
449 } else {
450 len = pr->alloc - pr->len - 1;
451 }
452 }
453 memcpy(pr->buf + pr->len, str, len);
454 pr->len += len;
455 }
456
mp_obj_new_exception_msg_varg(const mp_obj_type_t * exc_type,mp_rom_error_text_t fmt,...)457 mp_obj_t mp_obj_new_exception_msg_varg(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, ...) {
458 va_list args;
459 va_start(args, fmt);
460 mp_obj_t exc = mp_obj_new_exception_msg_vlist(exc_type, fmt, args);
461 va_end(args);
462 return exc;
463 }
464
mp_obj_new_exception_msg_vlist(const mp_obj_type_t * exc_type,mp_rom_error_text_t fmt,va_list args)465 mp_obj_t mp_obj_new_exception_msg_vlist(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, va_list args) {
466 assert(fmt != NULL);
467
468 // Check that the given type is an exception type
469 assert(exc_type->make_new == mp_obj_exception_make_new);
470
471 // Try to allocate memory for the message
472 mp_obj_str_t *o_str = m_new_obj_maybe(mp_obj_str_t);
473 size_t o_str_alloc = strlen((const char *)fmt) + 1;
474 byte *o_str_buf = m_new_maybe(byte, o_str_alloc);
475
476 bool used_emg_buf = false;
477 #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
478 // If memory allocation failed and there is an emergency buffer then try to use
479 // that buffer to store the string object and its data (at least 16 bytes for
480 // the string data), reserving room at the start for the traceback and 1-tuple.
481 if ((o_str == NULL || o_str_buf == NULL)
482 && mp_emergency_exception_buf_size >= (mp_int_t)(EMG_BUF_STR_OFFSET + sizeof(mp_obj_str_t) + 16)) {
483 used_emg_buf = true;
484 o_str = (mp_obj_str_t *)((uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + EMG_BUF_STR_OFFSET);
485 o_str_buf = (byte *)((uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + EMG_BUF_STR_BUF_OFFSET);
486 o_str_alloc = (uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + mp_emergency_exception_buf_size - o_str_buf;
487 }
488 #endif
489
490 if (o_str == NULL) {
491 // No memory for the string object so create the exception with no args.
492 // The exception will only have a type and no message (compression is irrelevant).
493 return mp_obj_exception_make_new(exc_type, 0, 0, NULL);
494 }
495
496 if (o_str_buf == NULL) {
497 // No memory for the string buffer: assume that the fmt string is in ROM
498 // and use that data as the data of the string.
499 // The string will point directly to the compressed data -- will need to be decompressed
500 // prior to display (this case is identical to mp_obj_new_exception_msg above).
501 o_str->len = o_str_alloc - 1; // will be equal to strlen(fmt)
502 o_str->data = (const byte *)fmt;
503 } else {
504 // We have some memory to format the string.
505 // TODO: Optimise this to format-while-decompressing (and not require the temp stack space).
506 struct _exc_printer_t exc_pr = {!used_emg_buf, o_str_alloc, 0, o_str_buf};
507 mp_print_t print = {&exc_pr, exc_add_strn};
508 const char *fmt2 = (const char *)fmt;
509 #if MICROPY_ROM_TEXT_COMPRESSION
510 byte decompressed[MP_MAX_UNCOMPRESSED_TEXT_LEN];
511 if (MP_IS_COMPRESSED_ROM_STRING(fmt)) {
512 mp_decompress_rom_string(decompressed, fmt);
513 fmt2 = (const char *)decompressed;
514 }
515 #endif
516 mp_vprintf(&print, fmt2, args);
517 exc_pr.buf[exc_pr.len] = '\0';
518 o_str->len = exc_pr.len;
519 o_str->data = exc_pr.buf;
520 }
521
522 // Create the string object and call mp_obj_exception_make_new to create the exception
523 o_str->base.type = &mp_type_str;
524 #if MICROPY_ROM_TEXT_COMPRESSION
525 o_str->hash = 0; // will be computed only if string object is accessed
526 #else
527 o_str->hash = qstr_compute_hash(o_str->data, o_str->len);
528 #endif
529 mp_obj_t arg = MP_OBJ_FROM_PTR(o_str);
530 return mp_obj_exception_make_new(exc_type, 1, 0, &arg);
531 }
532
533 #endif
534
535 // return true if the given object is an exception type
mp_obj_is_exception_type(mp_obj_t self_in)536 bool mp_obj_is_exception_type(mp_obj_t self_in) {
537 if (mp_obj_is_type(self_in, &mp_type_type)) {
538 // optimisation when self_in is a builtin exception
539 mp_obj_type_t *self = MP_OBJ_TO_PTR(self_in);
540 if (self->make_new == mp_obj_exception_make_new) {
541 return true;
542 }
543 }
544 return mp_obj_is_subclass_fast(self_in, MP_OBJ_FROM_PTR(&mp_type_BaseException));
545 }
546
547 // return true if the given object is an instance of an exception type
mp_obj_is_exception_instance(mp_obj_t self_in)548 bool mp_obj_is_exception_instance(mp_obj_t self_in) {
549 return mp_obj_is_exception_type(MP_OBJ_FROM_PTR(mp_obj_get_type(self_in)));
550 }
551
552 // Return true if exception (type or instance) is a subclass of given
553 // exception type. Assumes exc_type is a subclass of BaseException, as
554 // defined by mp_obj_is_exception_type(exc_type).
mp_obj_exception_match(mp_obj_t exc,mp_const_obj_t exc_type)555 bool mp_obj_exception_match(mp_obj_t exc, mp_const_obj_t exc_type) {
556 // if exc is an instance of an exception, then extract and use its type
557 if (mp_obj_is_exception_instance(exc)) {
558 exc = MP_OBJ_FROM_PTR(mp_obj_get_type(exc));
559 }
560 return mp_obj_is_subclass_fast(exc, exc_type);
561 }
562
563 // traceback handling functions
564
mp_obj_exception_clear_traceback(mp_obj_t self_in)565 void mp_obj_exception_clear_traceback(mp_obj_t self_in) {
566 mp_obj_exception_t *self = get_native_exception(self_in);
567 // just set the traceback to the null object
568 // we don't want to call any memory management functions here
569 self->traceback_data = NULL;
570 }
571
mp_obj_exception_add_traceback(mp_obj_t self_in,qstr file,size_t line,qstr block)572 void mp_obj_exception_add_traceback(mp_obj_t self_in, qstr file, size_t line, qstr block) {
573 mp_obj_exception_t *self = get_native_exception(self_in);
574
575 // append this traceback info to traceback data
576 // if memory allocation fails (eg because gc is locked), just return
577
578 if (self->traceback_data == NULL) {
579 self->traceback_data = m_new_maybe(size_t, TRACEBACK_ENTRY_LEN);
580 if (self->traceback_data == NULL) {
581 #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
582 if (mp_emergency_exception_buf_size >= (mp_int_t)(EMG_BUF_TRACEBACK_OFFSET + EMG_BUF_TRACEBACK_SIZE)) {
583 // There is room in the emergency buffer for traceback data
584 size_t *tb = (size_t *)((uint8_t *)MP_STATE_VM(mp_emergency_exception_buf)
585 + EMG_BUF_TRACEBACK_OFFSET);
586 self->traceback_data = tb;
587 self->traceback_alloc = EMG_BUF_TRACEBACK_SIZE / sizeof(size_t);
588 } else {
589 // Can't allocate and no room in emergency buffer
590 return;
591 }
592 #else
593 // Can't allocate
594 return;
595 #endif
596 } else {
597 // Allocated the traceback data on the heap
598 self->traceback_alloc = TRACEBACK_ENTRY_LEN;
599 }
600 self->traceback_len = 0;
601 } else if (self->traceback_len + TRACEBACK_ENTRY_LEN > self->traceback_alloc) {
602 #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
603 if (self->traceback_data == (size_t *)MP_STATE_VM(mp_emergency_exception_buf)) {
604 // Can't resize the emergency buffer
605 return;
606 }
607 #endif
608 // be conservative with growing traceback data
609 size_t *tb_data = m_renew_maybe(size_t, self->traceback_data, self->traceback_alloc,
610 self->traceback_alloc + TRACEBACK_ENTRY_LEN, true);
611 if (tb_data == NULL) {
612 return;
613 }
614 self->traceback_data = tb_data;
615 self->traceback_alloc += TRACEBACK_ENTRY_LEN;
616 }
617
618 size_t *tb_data = &self->traceback_data[self->traceback_len];
619 self->traceback_len += TRACEBACK_ENTRY_LEN;
620 tb_data[0] = file;
621 tb_data[1] = line;
622 tb_data[2] = block;
623 }
624
mp_obj_exception_get_traceback(mp_obj_t self_in,size_t * n,size_t ** values)625 void mp_obj_exception_get_traceback(mp_obj_t self_in, size_t *n, size_t **values) {
626 mp_obj_exception_t *self = get_native_exception(self_in);
627
628 if (self->traceback_data == NULL) {
629 *n = 0;
630 *values = NULL;
631 } else {
632 *n = self->traceback_len;
633 *values = self->traceback_data;
634 }
635 }
636