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