diff options
author | Jim Mussared <jim.mussared@gmail.com> | 2020-01-29 14:27:33 +1100 |
---|---|---|
committer | Damien George <damien.p.george@gmail.com> | 2020-04-05 15:02:06 +1000 |
commit | 85858e72dfdc3e941c2e620e94de05ad663138b1 (patch) | |
tree | d801c11e07380213b7e2abbb3066e2a90636369c /py/objexcept.c | |
parent | 92c83bd16b55daf81157100243ded6d7d0e56538 (diff) |
py/objexcept: Allow compression of exception message text.
The decompression of error-strings is only done if the string is accessed
via printing or via er.args. Tests are added for this feature to ensure
the decompression works.
Diffstat (limited to 'py/objexcept.c')
-rw-r--r-- | py/objexcept.c | 95 |
1 files changed, 81 insertions, 14 deletions
diff --git a/py/objexcept.c b/py/objexcept.c index ef99019d3..594c4c650 100644 --- a/py/objexcept.c +++ b/py/objexcept.c @@ -38,6 +38,15 @@ #include "py/gc.h" #include "py/mperrno.h" +// Extract the MP_MAX_UNCOMPRESSED_TEXT_LEN macro from "genhdr/compressed.data.h" +#if MICROPY_ROM_TEXT_COMPRESSION +#define MP_MATCH_COMPRESSED(...) // Ignore +#define MP_COMPRESSED_DATA(...) // Ignore +#include "genhdr/compressed.data.h" +#undef MP_MATCH_COMPRESSED +#undef MP_COMPRESSED_DATA +#endif + // Number of items per traceback entry (file, line, block) #define TRACEBACK_ENTRY_LEN (3) @@ -57,6 +66,7 @@ #define EMG_BUF_TUPLE_OFFSET (EMG_BUF_TRACEBACK_OFFSET + EMG_BUF_TRACEBACK_SIZE) #define EMG_BUF_TUPLE_SIZE(n_args) (sizeof(mp_obj_tuple_t) + n_args * sizeof(mp_obj_t)) #define EMG_BUF_STR_OFFSET (EMG_BUF_TUPLE_OFFSET + EMG_BUF_TUPLE_SIZE(1)) +#define EMG_BUF_STR_BUF_OFFSET (EMG_BUF_STR_OFFSET + sizeof(mp_obj_str_t)) #if MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE > 0 #define mp_emergency_exception_buf_size MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE @@ -100,6 +110,40 @@ mp_obj_t mp_alloc_emergency_exception_buf(mp_obj_t size_in) { #endif #endif // MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF +STATIC void decompress_error_text_maybe(mp_obj_exception_t *o) { + #if MICROPY_ROM_TEXT_COMPRESSION + if (o->args->len == 1 && mp_obj_is_type(o->args->items[0], &mp_type_str)) { + mp_obj_str_t *o_str = MP_OBJ_TO_PTR(o->args->items[0]); + if (MP_IS_COMPRESSED_ROM_STRING(o_str->data)) { + byte *buf = m_new_maybe(byte, MP_MAX_UNCOMPRESSED_TEXT_LEN + 1); + if (!buf) { + #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF + // Try and use the emergency exception buf if enough space is available. + buf = (byte *)((uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + EMG_BUF_STR_BUF_OFFSET); + size_t avail = (uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + mp_emergency_exception_buf_size - buf; + if (avail < MP_MAX_UNCOMPRESSED_TEXT_LEN + 1) { + // No way to decompress, fallback to no message text. + o->args = (mp_obj_tuple_t *)&mp_const_empty_tuple_obj; + return; + } + #else + o->args = (mp_obj_tuple_t *)&mp_const_empty_tuple_obj; + return; + #endif + } + mp_decompress_rom_string(buf, (mp_rom_error_text_t)o_str->data); + o_str->data = buf; + o_str->len = strlen((const char *)buf); + o_str->hash = 0; + } + // Lazily compute the string hash. + if (o_str->hash == 0) { + o_str->hash = qstr_compute_hash(o_str->data, o_str->len); + } + } + #endif +} + void mp_obj_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { mp_obj_exception_t *o = MP_OBJ_TO_PTR(o_in); mp_print_kind_t k = kind & ~PRINT_EXC_SUBCLASS; @@ -112,6 +156,8 @@ void mp_obj_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kin mp_print_str(print, ": "); } + decompress_error_text_maybe(o); + if (k == PRINT_STR || k == PRINT_EXC) { if (o->args == NULL || o->args->len == 0) { mp_print_str(print, ""); @@ -131,6 +177,7 @@ void mp_obj_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kin return; } } + mp_obj_tuple_print(print, MP_OBJ_FROM_PTR(o->args), kind); } @@ -189,6 +236,7 @@ mp_obj_t mp_obj_exception_get_value(mp_obj_t self_in) { if (self->args->len == 0) { return mp_const_none; } else { + decompress_error_text_maybe(self); return self->args->items[0]; } } @@ -210,6 +258,7 @@ void mp_obj_exception_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { return; } if (attr == MP_QSTR_args) { + decompress_error_text_maybe(self); dest[0] = MP_OBJ_FROM_PTR(self->args); } else if (self->base.type == &mp_type_StopIteration && attr == MP_QSTR_value) { dest[0] = mp_obj_exception_get_value(self_in); @@ -323,7 +372,7 @@ mp_obj_t mp_obj_new_exception_args(const mp_obj_type_t *exc_type, size_t n_args, return mp_obj_exception_make_new(exc_type, n_args, 0, args); } -mp_obj_t mp_obj_new_exception_msg(const mp_obj_type_t *exc_type, const char *msg) { +mp_obj_t mp_obj_new_exception_msg(const mp_obj_type_t *exc_type, mp_rom_error_text_t msg) { // Check that the given type is an exception type assert(exc_type->make_new == mp_obj_exception_make_new); @@ -348,9 +397,13 @@ mp_obj_t mp_obj_new_exception_msg(const mp_obj_type_t *exc_type, const char *msg // Create the string object and call mp_obj_exception_make_new to create the exception o_str->base.type = &mp_type_str; - o_str->len = strlen(msg); + o_str->len = strlen((const char *)msg); o_str->data = (const byte *)msg; + #if MICROPY_ROM_TEXT_COMPRESSION + o_str->hash = 0; // will be computed only if string object is accessed + #else o_str->hash = qstr_compute_hash(o_str->data, o_str->len); + #endif mp_obj_t arg = MP_OBJ_FROM_PTR(o_str); return mp_obj_exception_make_new(exc_type, 1, 0, &arg); } @@ -388,7 +441,7 @@ STATIC void exc_add_strn(void *data, const char *str, size_t len) { pr->len += len; } -mp_obj_t mp_obj_new_exception_msg_varg(const mp_obj_type_t *exc_type, const char *fmt, ...) { +mp_obj_t mp_obj_new_exception_msg_varg(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, ...) { va_list args; va_start(args, fmt); mp_obj_t exc = mp_obj_new_exception_msg_vlist(exc_type, fmt, args); @@ -396,7 +449,7 @@ mp_obj_t mp_obj_new_exception_msg_varg(const mp_obj_type_t *exc_type, const char return exc; } -mp_obj_t mp_obj_new_exception_msg_vlist(const mp_obj_type_t *exc_type, const char *fmt, va_list args) { +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) { assert(fmt != NULL); // Check that the given type is an exception type @@ -404,7 +457,7 @@ mp_obj_t mp_obj_new_exception_msg_vlist(const mp_obj_type_t *exc_type, const cha // Try to allocate memory for the message mp_obj_str_t *o_str = m_new_obj_maybe(mp_obj_str_t); - size_t o_str_alloc = strlen(fmt) + 1; + size_t o_str_alloc = strlen((const char *)fmt) + 1; byte *o_str_buf = m_new_maybe(byte, o_str_alloc); bool used_emg_buf = false; @@ -415,29 +468,39 @@ mp_obj_t mp_obj_new_exception_msg_vlist(const mp_obj_type_t *exc_type, const cha if ((o_str == NULL || o_str_buf == NULL) && mp_emergency_exception_buf_size >= EMG_BUF_STR_OFFSET + sizeof(mp_obj_str_t) + 16) { used_emg_buf = true; - o_str = (mp_obj_str_t *)((uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) - + EMG_BUF_STR_OFFSET); - o_str_buf = (byte *)&o_str[1]; - o_str_alloc = (uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) - + mp_emergency_exception_buf_size - o_str_buf; + o_str = (mp_obj_str_t *)((uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + EMG_BUF_STR_OFFSET); + o_str_buf = (byte *)((uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + EMG_BUF_STR_BUF_OFFSET); + o_str_alloc = (uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + mp_emergency_exception_buf_size - o_str_buf; } #endif if (o_str == NULL) { - // No memory for the string object so create the exception with no args + // No memory for the string object so create the exception with no args. + // The exception will only have a type and no message (compression is irrelevant). return mp_obj_exception_make_new(exc_type, 0, 0, NULL); } if (o_str_buf == NULL) { // No memory for the string buffer: assume that the fmt string is in ROM - // and use that data as the data of the string + // and use that data as the data of the string. + // The string will point directly to the compressed data -- will need to be decompressed + // prior to display (this case is identical to mp_obj_new_exception_msg above). o_str->len = o_str_alloc - 1; // will be equal to strlen(fmt) o_str->data = (const byte *)fmt; } else { - // We have some memory to format the string + // We have some memory to format the string. + // TODO: Optimise this to format-while-decompressing (and not require the temp stack space). struct _exc_printer_t exc_pr = {!used_emg_buf, o_str_alloc, 0, o_str_buf}; mp_print_t print = {&exc_pr, exc_add_strn}; - mp_vprintf(&print, fmt, args); + const char *fmt2 = (const char *)fmt; + #if MICROPY_ROM_TEXT_COMPRESSION + byte decompressed[MP_MAX_UNCOMPRESSED_TEXT_LEN]; + if (MP_IS_COMPRESSED_ROM_STRING(fmt)) { + mp_decompress_rom_string(decompressed, fmt); + fmt2 = (const char *)decompressed; + } + #endif + mp_vprintf(&print, fmt2, args); exc_pr.buf[exc_pr.len] = '\0'; o_str->len = exc_pr.len; o_str->data = exc_pr.buf; @@ -445,7 +508,11 @@ mp_obj_t mp_obj_new_exception_msg_vlist(const mp_obj_type_t *exc_type, const cha // Create the string object and call mp_obj_exception_make_new to create the exception o_str->base.type = &mp_type_str; + #if MICROPY_ROM_TEXT_COMPRESSION + o_str->hash = 0; // will be computed only if string object is accessed + #else o_str->hash = qstr_compute_hash(o_str->data, o_str->len); + #endif mp_obj_t arg = MP_OBJ_FROM_PTR(o_str); return mp_obj_exception_make_new(exc_type, 1, 0, &arg); } |