Branch data Line data Source code
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 : :
75 : 3424 : 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 : 3424 : }
80 : :
81 : : #else
82 : : #define mp_emergency_exception_buf_size MP_STATE_VM(mp_emergency_exception_buf_size)
83 : :
84 : : #include "py/mphal.h" // for MICROPY_BEGIN_ATOMIC_SECTION/MICROPY_END_ATOMIC_SECTION
85 : :
86 : : void mp_init_emergency_exception_buf(void) {
87 : : mp_emergency_exception_buf_size = 0;
88 : : MP_STATE_VM(mp_emergency_exception_buf) = NULL;
89 : : }
90 : :
91 : : mp_obj_t mp_alloc_emergency_exception_buf(mp_obj_t size_in) {
92 : : mp_int_t size = mp_obj_get_int(size_in);
93 : : void *buf = NULL;
94 : : if (size > 0) {
95 : : buf = m_new(byte, size);
96 : : }
97 : :
98 : : int old_size = mp_emergency_exception_buf_size;
99 : : void *old_buf = MP_STATE_VM(mp_emergency_exception_buf);
100 : :
101 : : // Update the 2 variables atomically so that an interrupt can't occur
102 : : // between the assignments.
103 : : mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION();
104 : : mp_emergency_exception_buf_size = size;
105 : : MP_STATE_VM(mp_emergency_exception_buf) = buf;
106 : : MICROPY_END_ATOMIC_SECTION(atomic_state);
107 : :
108 : : if (old_buf != NULL) {
109 : : m_del(byte, old_buf, old_size);
110 : : }
111 : : return mp_const_none;
112 : : }
113 : : #endif
114 : : #endif // MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
115 : :
116 : 450708 : bool mp_obj_is_native_exception_instance(mp_obj_t self_in) {
117 [ + + ]: 450708 : return MP_OBJ_TYPE_GET_SLOT_OR_NULL(mp_obj_get_type(self_in), make_new) == mp_obj_exception_make_new;
118 : : }
119 : :
120 : 450592 : static mp_obj_exception_t *get_native_exception(mp_obj_t self_in) {
121 [ - + ]: 450592 : assert(mp_obj_is_exception_instance(self_in));
122 [ + + ]: 450592 : if (mp_obj_is_native_exception_instance(self_in)) {
123 : : return MP_OBJ_TO_PTR(self_in);
124 : : } else {
125 : 207 : return MP_OBJ_TO_PTR(((mp_obj_instance_t *)MP_OBJ_TO_PTR(self_in))->subobj[0]);
126 : : }
127 : : }
128 : :
129 : 988 : static void decompress_error_text_maybe(mp_obj_exception_t *o) {
130 : : #if MICROPY_ROM_TEXT_COMPRESSION
131 [ + + + + : 988 : if (o->args->len == 1 && mp_obj_is_exact_type(o->args->items[0], &mp_type_str)) {
+ - ]
132 : 350 : mp_obj_str_t *o_str = MP_OBJ_TO_PTR(o->args->items[0]);
133 [ + + ]: 350 : if (MP_IS_COMPRESSED_ROM_STRING(o_str->data)) {
134 : 177 : byte *buf = m_new_maybe(byte, MP_MAX_UNCOMPRESSED_TEXT_LEN + 1);
135 [ + + ]: 177 : if (!buf) {
136 : : #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
137 : : // Try and use the emergency exception buf if enough space is available.
138 : 8 : buf = (byte *)((uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + EMG_BUF_STR_BUF_OFFSET);
139 : 8 : size_t avail = (uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + mp_emergency_exception_buf_size - buf;
140 : 8 : if (avail < MP_MAX_UNCOMPRESSED_TEXT_LEN + 1) {
141 : : // No way to decompress, fallback to no message text.
142 : : o->args = (mp_obj_tuple_t *)&mp_const_empty_tuple_obj;
143 : : return;
144 : : }
145 : : #else
146 : : o->args = (mp_obj_tuple_t *)&mp_const_empty_tuple_obj;
147 : : return;
148 : : #endif
149 : : }
150 : 177 : mp_decompress_rom_string(buf, (mp_rom_error_text_t)o_str->data);
151 : 177 : o_str->data = buf;
152 : 177 : o_str->len = strlen((const char *)buf);
153 : 177 : o_str->hash = 0;
154 : : }
155 : : // Lazily compute the string hash.
156 [ + + ]: 350 : if (o_str->hash == 0) {
157 : 340 : o_str->hash = qstr_compute_hash(o_str->data, o_str->len);
158 : : }
159 : : }
160 : : #endif
161 : : }
162 : :
163 : 644 : void mp_obj_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) {
164 : 644 : mp_obj_exception_t *o = MP_OBJ_TO_PTR(o_in);
165 : 644 : mp_print_kind_t k = kind & ~PRINT_EXC_SUBCLASS;
166 : 644 : bool is_subclass = kind & PRINT_EXC_SUBCLASS;
167 [ + + + + ]: 644 : if (!is_subclass && (k == PRINT_REPR || k == PRINT_EXC)) {
168 : 358 : mp_print_str(print, qstr_str(o->base.type->name));
169 : : }
170 : :
171 [ + + ]: 400 : if (k == PRINT_EXC) {
172 : 50 : mp_print_str(print, ": ");
173 : : }
174 : :
175 : 644 : decompress_error_text_maybe(o);
176 : :
177 [ + + ]: 644 : if (k == PRINT_STR || k == PRINT_EXC) {
178 [ + - + + ]: 298 : if (o->args == NULL || o->args->len == 0) {
179 : 21 : mp_print_str(print, "");
180 : 21 : return;
181 : : }
182 : :
183 : : #if MICROPY_PY_ERRNO
184 : : // try to provide a nice OSError error message
185 [ + + + + : 277 : if (o->base.type == &mp_type_OSError && o->args->len > 0 && o->args->len < 3 && mp_obj_is_small_int(o->args->items[0])) {
+ - ]
186 : 38 : qstr qst = mp_errno_to_str(o->args->items[0]);
187 [ + + ]: 38 : if (qst != MP_QSTRnull) {
188 : 32 : mp_printf(print, "[Errno " INT_FMT "] %q", MP_OBJ_SMALL_INT_VALUE(o->args->items[0]), qst);
189 [ + + ]: 32 : if (o->args->len > 1) {
190 : 4 : mp_print_str(print, ": ");
191 : 4 : mp_obj_print_helper(print, o->args->items[1], PRINT_STR);
192 : : }
193 : 32 : return;
194 : : }
195 : : }
196 : : #endif
197 : :
198 [ + + ]: 245 : if (o->args->len == 1) {
199 : 219 : mp_obj_print_helper(print, o->args->items[0], PRINT_STR);
200 : 219 : return;
201 : : }
202 : : }
203 : :
204 : 372 : mp_obj_tuple_print(print, MP_OBJ_FROM_PTR(o->args), kind);
205 : : }
206 : :
207 : 11693 : 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) {
208 : 11693 : mp_arg_check_num(n_args, n_kw, 0, MP_OBJ_FUN_ARGS_MAX, false);
209 : :
210 : : // Try to allocate memory for the exception, with fallback to emergency exception object
211 : 11693 : mp_obj_exception_t *o_exc = m_new_obj_maybe(mp_obj_exception_t);
212 [ + + ]: 11693 : if (o_exc == NULL) {
213 : 139 : o_exc = &MP_STATE_VM(mp_emergency_exception_obj);
214 : : }
215 : :
216 : : // Populate the exception object
217 : 11693 : o_exc->base.type = type;
218 : 11693 : o_exc->traceback_data = NULL;
219 : :
220 : 11693 : mp_obj_tuple_t *o_tuple;
221 [ + + ]: 11693 : if (n_args == 0) {
222 : : // No args, can use the empty tuple straight away
223 : : o_tuple = (mp_obj_tuple_t *)&mp_const_empty_tuple_obj;
224 : : } else {
225 : : // Try to allocate memory for the tuple containing the args
226 : 9644 : o_tuple = m_new_obj_var_maybe(mp_obj_tuple_t, items, mp_obj_t, n_args);
227 : :
228 : : #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
229 : : // If we are called by mp_obj_new_exception_msg_varg then it will have
230 : : // reserved room (after the traceback data) for a tuple with 1 element.
231 : : // Otherwise we are free to use the whole buffer after the traceback data.
232 [ + + ]: 9644 : if (o_tuple == NULL && mp_emergency_exception_buf_size >=
233 [ + + ]: 139 : (mp_int_t)(EMG_BUF_TUPLE_OFFSET + EMG_BUF_TUPLE_SIZE(n_args))) {
234 : : o_tuple = (mp_obj_tuple_t *)
235 : : ((uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + EMG_BUF_TUPLE_OFFSET);
236 : : }
237 : : #endif
238 : :
239 : : if (o_tuple == NULL) {
240 : : // No memory for a tuple, fallback to an empty tuple
241 : : o_tuple = (mp_obj_tuple_t *)&mp_const_empty_tuple_obj;
242 : : } else {
243 : : // Have memory for a tuple so populate it
244 : 9640 : o_tuple->base.type = &mp_type_tuple;
245 : 9640 : o_tuple->len = n_args;
246 : 9640 : memcpy(o_tuple->items, args, n_args * sizeof(mp_obj_t));
247 : : }
248 : : }
249 : :
250 : : // Store the tuple of args in the exception object
251 : 11693 : o_exc->args = o_tuple;
252 : :
253 : 11693 : return MP_OBJ_FROM_PTR(o_exc);
254 : : }
255 : :
256 : : // Get exception "value" - that is, first argument, or None
257 : 299814 : mp_obj_t mp_obj_exception_get_value(mp_obj_t self_in) {
258 : 299814 : mp_obj_exception_t *self = get_native_exception(self_in);
259 [ + + ]: 299814 : if (self->args->len == 0) {
260 : : return mp_const_none;
261 : : } else {
262 : 172 : decompress_error_text_maybe(self);
263 : 172 : return self->args->items[0];
264 : : }
265 : : }
266 : :
267 : 299850 : void mp_obj_exception_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
268 : 299850 : mp_obj_exception_t *self = MP_OBJ_TO_PTR(self_in);
269 [ + + ]: 299850 : if (dest[0] != MP_OBJ_NULL) {
270 : : // store/delete attribute
271 [ + + + - ]: 299382 : if (attr == MP_QSTR___traceback__ && dest[1] == mp_const_none) {
272 : : // We allow 'exc.__traceback__ = None' assignment as low-level
273 : : // optimization of pre-allocating exception instance and raising
274 : : // it repeatedly - this avoids memory allocation during raise.
275 : : // However, uPy will keep adding traceback entries to such
276 : : // exception instance, so before throwing it, traceback should
277 : : // be cleared like above.
278 : 299380 : self->traceback_len = 0;
279 : 299380 : dest[0] = MP_OBJ_NULL; // indicate success
280 : : }
281 : 299382 : return;
282 : : }
283 [ + + ]: 468 : if (attr == MP_QSTR_args) {
284 : 172 : decompress_error_text_maybe(self);
285 : 172 : dest[0] = MP_OBJ_FROM_PTR(self->args);
286 [ + + ]: 296 : } else if (attr == MP_QSTR_value || attr == MP_QSTR_errno) {
287 : : // These are aliases for args[0]: .value for StopIteration and .errno for OSError.
288 : : // For efficiency let these attributes apply to all exception instances.
289 : 256 : dest[0] = mp_obj_exception_get_value(self_in);
290 : : }
291 : : }
292 : :
293 : : MP_DEFINE_CONST_OBJ_TYPE(
294 : : mp_type_BaseException,
295 : : MP_QSTR_BaseException,
296 : : MP_TYPE_FLAG_NONE,
297 : : make_new, mp_obj_exception_make_new,
298 : : print, mp_obj_exception_print,
299 : : attr, mp_obj_exception_attr
300 : : );
301 : :
302 : : // *FORMAT-OFF*
303 : :
304 : : // List of all exceptions, arranged as in the table at:
305 : : // http://docs.python.org/3/library/exceptions.html
306 : : MP_DEFINE_EXCEPTION(SystemExit, BaseException)
307 : : MP_DEFINE_EXCEPTION(KeyboardInterrupt, BaseException)
308 : : MP_DEFINE_EXCEPTION(GeneratorExit, BaseException)
309 : : MP_DEFINE_EXCEPTION(Exception, BaseException)
310 : : #if MICROPY_PY_ASYNC_AWAIT
311 : : MP_DEFINE_EXCEPTION(StopAsyncIteration, Exception)
312 : : #endif
313 : : MP_DEFINE_EXCEPTION(StopIteration, Exception)
314 : : MP_DEFINE_EXCEPTION(ArithmeticError, Exception)
315 : : //MP_DEFINE_EXCEPTION(FloatingPointError, ArithmeticError)
316 : : MP_DEFINE_EXCEPTION(OverflowError, ArithmeticError)
317 : : MP_DEFINE_EXCEPTION(ZeroDivisionError, ArithmeticError)
318 : : MP_DEFINE_EXCEPTION(AssertionError, Exception)
319 : : MP_DEFINE_EXCEPTION(AttributeError, Exception)
320 : : //MP_DEFINE_EXCEPTION(BufferError, Exception)
321 : : MP_DEFINE_EXCEPTION(EOFError, Exception)
322 : : MP_DEFINE_EXCEPTION(ImportError, Exception)
323 : : MP_DEFINE_EXCEPTION(LookupError, Exception)
324 : : MP_DEFINE_EXCEPTION(IndexError, LookupError)
325 : : MP_DEFINE_EXCEPTION(KeyError, LookupError)
326 : : MP_DEFINE_EXCEPTION(MemoryError, Exception)
327 : : MP_DEFINE_EXCEPTION(NameError, Exception)
328 : : /*
329 : : MP_DEFINE_EXCEPTION(UnboundLocalError, NameError)
330 : : */
331 : : MP_DEFINE_EXCEPTION(OSError, Exception)
332 : : /*
333 : : MP_DEFINE_EXCEPTION(BlockingIOError, OSError)
334 : : MP_DEFINE_EXCEPTION(ChildProcessError, OSError)
335 : : MP_DEFINE_EXCEPTION(ConnectionError, OSError)
336 : : MP_DEFINE_EXCEPTION(BrokenPipeError, ConnectionError)
337 : : MP_DEFINE_EXCEPTION(ConnectionAbortedError, ConnectionError)
338 : : MP_DEFINE_EXCEPTION(ConnectionRefusedError, ConnectionError)
339 : : MP_DEFINE_EXCEPTION(ConnectionResetError, ConnectionError)
340 : : MP_DEFINE_EXCEPTION(InterruptedError, OSError)
341 : : MP_DEFINE_EXCEPTION(IsADirectoryError, OSError)
342 : : MP_DEFINE_EXCEPTION(NotADirectoryError, OSError)
343 : : MP_DEFINE_EXCEPTION(PermissionError, OSError)
344 : : MP_DEFINE_EXCEPTION(ProcessLookupError, OSError)
345 : : MP_DEFINE_EXCEPTION(TimeoutError, OSError)
346 : : MP_DEFINE_EXCEPTION(FileExistsError, OSError)
347 : : MP_DEFINE_EXCEPTION(FileNotFoundError, OSError)
348 : : MP_DEFINE_EXCEPTION(ReferenceError, Exception)
349 : : */
350 : : MP_DEFINE_EXCEPTION(RuntimeError, Exception)
351 : : MP_DEFINE_EXCEPTION(NotImplementedError, RuntimeError)
352 : : MP_DEFINE_EXCEPTION(SyntaxError, Exception)
353 : : MP_DEFINE_EXCEPTION(IndentationError, SyntaxError)
354 : : /*
355 : : MP_DEFINE_EXCEPTION(TabError, IndentationError)
356 : : */
357 : : //MP_DEFINE_EXCEPTION(SystemError, Exception)
358 : : MP_DEFINE_EXCEPTION(TypeError, Exception)
359 : : #if MICROPY_EMIT_NATIVE
360 : : MP_DEFINE_EXCEPTION(ViperTypeError, TypeError)
361 : : #endif
362 : : MP_DEFINE_EXCEPTION(ValueError, Exception)
363 : : #if MICROPY_PY_BUILTINS_STR_UNICODE
364 : : MP_DEFINE_EXCEPTION(UnicodeError, ValueError)
365 : : //TODO: Implement more UnicodeError subclasses which take arguments
366 : : #endif
367 : : /*
368 : : MP_DEFINE_EXCEPTION(Warning, Exception)
369 : : MP_DEFINE_EXCEPTION(DeprecationWarning, Warning)
370 : : MP_DEFINE_EXCEPTION(PendingDeprecationWarning, Warning)
371 : : MP_DEFINE_EXCEPTION(RuntimeWarning, Warning)
372 : : MP_DEFINE_EXCEPTION(SyntaxWarning, Warning)
373 : : MP_DEFINE_EXCEPTION(UserWarning, Warning)
374 : : MP_DEFINE_EXCEPTION(FutureWarning, Warning)
375 : : MP_DEFINE_EXCEPTION(ImportWarning, Warning)
376 : : MP_DEFINE_EXCEPTION(UnicodeWarning, Warning)
377 : : MP_DEFINE_EXCEPTION(BytesWarning, Warning)
378 : : MP_DEFINE_EXCEPTION(ResourceWarning, Warning)
379 : : */
380 : :
381 : : // *FORMAT-ON*
382 : :
383 : 1085 : mp_obj_t mp_obj_new_exception(const mp_obj_type_t *exc_type) {
384 [ + - - + ]: 1085 : assert(MP_OBJ_TYPE_GET_SLOT_OR_NULL(exc_type, make_new) == mp_obj_exception_make_new);
385 : 1085 : return mp_obj_exception_make_new(exc_type, 0, 0, NULL);
386 : : }
387 : :
388 : 2 : mp_obj_t mp_obj_new_exception_args(const mp_obj_type_t *exc_type, size_t n_args, const mp_obj_t *args) {
389 [ + - - + ]: 2 : assert(MP_OBJ_TYPE_GET_SLOT_OR_NULL(exc_type, make_new) == mp_obj_exception_make_new);
390 : 2 : return mp_obj_exception_make_new(exc_type, n_args, 0, args);
391 : : }
392 : :
393 : : #if MICROPY_ERROR_REPORTING != MICROPY_ERROR_REPORTING_NONE
394 : :
395 : 7314 : mp_obj_t mp_obj_new_exception_msg(const mp_obj_type_t *exc_type, mp_rom_error_text_t msg) {
396 : : // Check that the given type is an exception type
397 [ + - - + ]: 7314 : assert(MP_OBJ_TYPE_GET_SLOT_OR_NULL(exc_type, make_new) == mp_obj_exception_make_new);
398 : :
399 : : // Try to allocate memory for the message
400 : 7314 : mp_obj_str_t *o_str = m_new_obj_maybe(mp_obj_str_t);
401 : :
402 : : #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
403 : : // If memory allocation failed and there is an emergency buffer then try to use
404 : : // that buffer to store the string object, reserving room at the start for the
405 : : // traceback and 1-tuple.
406 [ + + ]: 7314 : if (o_str == NULL
407 : : && mp_emergency_exception_buf_size >= (mp_int_t)(EMG_BUF_STR_OFFSET + sizeof(mp_obj_str_t))) {
408 : 106 : o_str = (mp_obj_str_t *)((uint8_t *)MP_STATE_VM(mp_emergency_exception_buf)
409 : : + EMG_BUF_STR_OFFSET);
410 : : }
411 : : #endif
412 : :
413 : 106 : if (o_str == NULL) {
414 : : // No memory for the string object so create the exception with no args
415 : : return mp_obj_exception_make_new(exc_type, 0, 0, NULL);
416 : : }
417 : :
418 : : // Create the string object and call mp_obj_exception_make_new to create the exception
419 : 7314 : o_str->base.type = &mp_type_str;
420 : 7314 : o_str->len = strlen((const char *)msg);
421 : 7314 : o_str->data = (const byte *)msg;
422 : : #if MICROPY_ROM_TEXT_COMPRESSION
423 : 7314 : o_str->hash = 0; // will be computed only if string object is accessed
424 : : #else
425 : : o_str->hash = qstr_compute_hash(o_str->data, o_str->len);
426 : : #endif
427 : 7314 : mp_obj_t arg = MP_OBJ_FROM_PTR(o_str);
428 : 7314 : return mp_obj_exception_make_new(exc_type, 1, 0, &arg);
429 : : }
430 : :
431 : : // The following struct and function implement a simple printer that conservatively
432 : : // allocates memory and truncates the output data if no more memory can be obtained.
433 : : // It leaves room for a null byte at the end of the buffer.
434 : :
435 : : struct _exc_printer_t {
436 : : bool allow_realloc;
437 : : size_t alloc;
438 : : size_t len;
439 : : byte *buf;
440 : : };
441 : :
442 : 5412 : static void exc_add_strn(void *data, const char *str, size_t len) {
443 : 5412 : struct _exc_printer_t *pr = data;
444 [ + + ]: 5412 : if (pr->len + len >= pr->alloc) {
445 : : // Not enough room for data plus a null byte so try to grow the buffer
446 [ + + ]: 1597 : if (pr->allow_realloc) {
447 : 1582 : size_t new_alloc = pr->alloc + len + 16;
448 : 1582 : byte *new_buf = m_renew_maybe(byte, pr->buf, pr->alloc, new_alloc, true);
449 [ + + ]: 1582 : if (new_buf == NULL) {
450 : 4 : pr->allow_realloc = false;
451 : 4 : len = pr->alloc - pr->len - 1;
452 : : } else {
453 : 1578 : pr->alloc = new_alloc;
454 : 1578 : pr->buf = new_buf;
455 : : }
456 : : } else {
457 : 15 : len = pr->alloc - pr->len - 1;
458 : : }
459 : : }
460 : 5412 : memcpy(pr->buf + pr->len, str, len);
461 : 5412 : pr->len += len;
462 : 5412 : }
463 : :
464 : 92 : mp_obj_t mp_obj_new_exception_msg_varg(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, ...) {
465 : 92 : va_list args;
466 : 92 : va_start(args, fmt);
467 : 92 : mp_obj_t exc = mp_obj_new_exception_msg_vlist(exc_type, fmt, args);
468 : 92 : va_end(args);
469 : 92 : return exc;
470 : : }
471 : :
472 : 1316 : 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) {
473 [ - + ]: 1316 : assert(fmt != NULL);
474 : :
475 : : // Check that the given type is an exception type
476 [ + - - + ]: 1316 : assert(MP_OBJ_TYPE_GET_SLOT_OR_NULL(exc_type, make_new) == mp_obj_exception_make_new);
477 : :
478 : : // Try to allocate memory for the message
479 : 1316 : mp_obj_str_t *o_str = m_new_obj_maybe(mp_obj_str_t);
480 : 1316 : size_t o_str_alloc = strlen((const char *)fmt) + 1;
481 : 1316 : byte *o_str_buf = m_new_maybe(byte, o_str_alloc);
482 : :
483 : 1316 : bool used_emg_buf = false;
484 : : #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
485 : : // If memory allocation failed and there is an emergency buffer then try to use
486 : : // that buffer to store the string object and its data (at least 16 bytes for
487 : : // the string data), reserving room at the start for the traceback and 1-tuple.
488 : 1316 : if ((o_str == NULL || o_str_buf == NULL)
489 [ + + ]: 1316 : && mp_emergency_exception_buf_size >= (mp_int_t)(EMG_BUF_STR_OFFSET + sizeof(mp_obj_str_t) + 16)) {
490 : 20 : used_emg_buf = true;
491 : 20 : o_str = (mp_obj_str_t *)((uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + EMG_BUF_STR_OFFSET);
492 : 20 : o_str_buf = (byte *)((uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + EMG_BUF_STR_BUF_OFFSET);
493 : 20 : o_str_alloc = (uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + mp_emergency_exception_buf_size - o_str_buf;
494 : : }
495 : : #endif
496 : :
497 [ - + ]: 1316 : if (o_str == NULL) {
498 : : // No memory for the string object so create the exception with no args.
499 : : // The exception will only have a type and no message (compression is irrelevant).
500 : 0 : return mp_obj_exception_make_new(exc_type, 0, 0, NULL);
501 : : }
502 : :
503 [ - + ]: 1316 : if (o_str_buf == NULL) {
504 : : // No memory for the string buffer: assume that the fmt string is in ROM
505 : : // and use that data as the data of the string.
506 : : // The string will point directly to the compressed data -- will need to be decompressed
507 : : // prior to display (this case is identical to mp_obj_new_exception_msg above).
508 : 0 : o_str->len = o_str_alloc - 1; // will be equal to strlen(fmt)
509 : 0 : o_str->data = (const byte *)fmt;
510 : : } else {
511 : : // We have some memory to format the string.
512 : : // TODO: Optimise this to format-while-decompressing (and not require the temp stack space).
513 : 1316 : struct _exc_printer_t exc_pr = {!used_emg_buf, o_str_alloc, 0, o_str_buf};
514 : 1316 : mp_print_t print = {&exc_pr, exc_add_strn};
515 : 1316 : const char *fmt2 = (const char *)fmt;
516 : : #if MICROPY_ROM_TEXT_COMPRESSION
517 : 1316 : byte decompressed[MP_MAX_UNCOMPRESSED_TEXT_LEN];
518 [ + - ]: 1316 : if (MP_IS_COMPRESSED_ROM_STRING(fmt)) {
519 : 1316 : mp_decompress_rom_string(decompressed, fmt);
520 : 1316 : fmt2 = (const char *)decompressed;
521 : : }
522 : : #endif
523 : 1316 : mp_vprintf(&print, fmt2, args);
524 : 1316 : exc_pr.buf[exc_pr.len] = '\0';
525 : 1316 : o_str->len = exc_pr.len;
526 : 1316 : o_str->data = exc_pr.buf;
527 : : }
528 : :
529 : : // Create the string object and call mp_obj_exception_make_new to create the exception
530 : 1316 : o_str->base.type = &mp_type_str;
531 : : #if MICROPY_ROM_TEXT_COMPRESSION
532 : 1316 : o_str->hash = 0; // will be computed only if string object is accessed
533 : : #else
534 : : o_str->hash = qstr_compute_hash(o_str->data, o_str->len);
535 : : #endif
536 : 1316 : mp_obj_t arg = MP_OBJ_FROM_PTR(o_str);
537 : 1316 : return mp_obj_exception_make_new(exc_type, 1, 0, &arg);
538 : : }
539 : :
540 : : #endif
541 : :
542 : : // return true if the given object is an exception type
543 : 1554321 : bool mp_obj_is_exception_type(mp_obj_t self_in) {
544 [ - + - + : 1554321 : if (mp_obj_is_type(self_in, &mp_type_type)) {
- + - + +
+ + + ]
545 : : // optimisation when self_in is a builtin exception
546 : 1094685 : mp_obj_type_t *self = MP_OBJ_TO_PTR(self_in);
547 [ + - + + ]: 1094685 : if (MP_OBJ_TYPE_GET_SLOT_OR_NULL(self, make_new) == mp_obj_exception_make_new) {
548 : : return true;
549 : : }
550 : : }
551 : 461450 : return mp_obj_is_subclass_fast(self_in, MP_OBJ_FROM_PTR(&mp_type_BaseException));
552 : : }
553 : :
554 : : // return true if the given object is an instance of an exception type
555 : 1082033 : bool mp_obj_is_exception_instance(mp_obj_t self_in) {
556 : 1082033 : return mp_obj_is_exception_type(MP_OBJ_FROM_PTR(mp_obj_get_type(self_in)));
557 : : }
558 : :
559 : : // Return true if exception (type or instance) is a subclass of given
560 : : // exception type. Assumes exc_type is a subclass of BaseException, as
561 : : // defined by mp_obj_is_exception_type(exc_type).
562 : 171451 : bool mp_obj_exception_match(mp_obj_t exc, mp_const_obj_t exc_type) {
563 : : // if exc is an instance of an exception, then extract and use its type
564 [ + + ]: 171451 : if (mp_obj_is_exception_instance(exc)) {
565 : 171341 : exc = MP_OBJ_FROM_PTR(mp_obj_get_type(exc));
566 : : }
567 : 171451 : return mp_obj_is_subclass_fast(exc, exc_type);
568 : : }
569 : :
570 : : // traceback handling functions
571 : :
572 : 0 : void mp_obj_exception_clear_traceback(mp_obj_t self_in) {
573 : 0 : mp_obj_exception_t *self = get_native_exception(self_in);
574 : : // just set the traceback to the null object
575 : : // we don't want to call any memory management functions here
576 : 0 : self->traceback_data = NULL;
577 : 0 : }
578 : :
579 : 150728 : void mp_obj_exception_add_traceback(mp_obj_t self_in, qstr file, size_t line, qstr block) {
580 : 150728 : mp_obj_exception_t *self = get_native_exception(self_in);
581 : :
582 : : // append this traceback info to traceback data
583 : : // if memory allocation fails (eg because gc is locked), just return
584 : :
585 : : #if MICROPY_PY_SYS_TRACEBACKLIMIT
586 : 150728 : mp_int_t max_traceback = MP_OBJ_SMALL_INT_VALUE(MP_STATE_VM(sys_mutable[MP_SYS_MUTABLE_TRACEBACKLIMIT]));
587 [ + + ]: 150728 : if (max_traceback <= 0) {
588 : : return;
589 [ + + + + ]: 150708 : } else if (self->traceback_data != NULL && self->traceback_len >= max_traceback * TRACEBACK_ENTRY_LEN) {
590 : 20 : self->traceback_len -= TRACEBACK_ENTRY_LEN;
591 : 20 : memmove(self->traceback_data, self->traceback_data + TRACEBACK_ENTRY_LEN, self->traceback_len * sizeof(self->traceback_data[0]));
592 : : }
593 : : #endif
594 : :
595 [ + + ]: 150708 : if (self->traceback_data == NULL) {
596 : 8542 : self->traceback_data = m_new_maybe(size_t, TRACEBACK_ENTRY_LEN);
597 [ + + ]: 8542 : if (self->traceback_data == NULL) {
598 : : #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
599 : 72 : if (mp_emergency_exception_buf_size >= (mp_int_t)(EMG_BUF_TRACEBACK_OFFSET + EMG_BUF_TRACEBACK_SIZE)) {
600 : : // There is room in the emergency buffer for traceback data
601 : 72 : size_t *tb = (size_t *)((uint8_t *)MP_STATE_VM(mp_emergency_exception_buf)
602 : : + EMG_BUF_TRACEBACK_OFFSET);
603 : 72 : self->traceback_data = tb;
604 : 72 : self->traceback_alloc = EMG_BUF_TRACEBACK_SIZE / sizeof(size_t);
605 : : } else {
606 : : // Can't allocate and no room in emergency buffer
607 : : return;
608 : : }
609 : : #else
610 : : // Can't allocate
611 : : return;
612 : : #endif
613 : : } else {
614 : : // Allocated the traceback data on the heap
615 : 8470 : self->traceback_alloc = TRACEBACK_ENTRY_LEN;
616 : : }
617 : 8542 : self->traceback_len = 0;
618 [ + + ]: 142166 : } else if (self->traceback_len + TRACEBACK_ENTRY_LEN > self->traceback_alloc) {
619 : : #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
620 [ + + ]: 1587 : if (self->traceback_data == (size_t *)MP_STATE_VM(mp_emergency_exception_buf)) {
621 : : // Can't resize the emergency buffer
622 : : return;
623 : : }
624 : : #endif
625 : : // be conservative with growing traceback data
626 : 1350 : size_t *tb_data = m_renew_maybe(size_t, self->traceback_data, self->traceback_alloc,
627 : : self->traceback_alloc + TRACEBACK_ENTRY_LEN, true);
628 [ + + ]: 1350 : if (tb_data == NULL) {
629 : : return;
630 : : }
631 : 1346 : self->traceback_data = tb_data;
632 : 1346 : self->traceback_alloc += TRACEBACK_ENTRY_LEN;
633 : : }
634 : :
635 : 150467 : size_t *tb_data = &self->traceback_data[self->traceback_len];
636 : 150467 : self->traceback_len += TRACEBACK_ENTRY_LEN;
637 : 150467 : tb_data[0] = file;
638 : 150467 : tb_data[1] = line;
639 : 150467 : tb_data[2] = block;
640 : : }
641 : :
642 : 50 : void mp_obj_exception_get_traceback(mp_obj_t self_in, size_t *n, size_t **values) {
643 : 50 : mp_obj_exception_t *self = get_native_exception(self_in);
644 : :
645 [ + + ]: 50 : if (self->traceback_data == NULL) {
646 : 22 : *n = 0;
647 : 22 : *values = NULL;
648 : : } else {
649 : 28 : *n = self->traceback_len;
650 : 28 : *values = self->traceback_data;
651 : : }
652 : 50 : }
|