3838#include "py/gc.h"
3939#include "py/mperrno.h"
4040
41+ // Extract the MP_MAX_UNCOMPRESSED_TEXT_LEN macro from "genhdr/compressed.data.h"
42+ #if MICROPY_ROM_TEXT_COMPRESSION
43+ #define MP_MATCH_COMPRESSED (...) // Ignore
44+ #define MP_COMPRESSED_DATA (...) // Ignore
45+ #include "genhdr/compressed.data.h"
46+ #undef MP_MATCH_COMPRESSED
47+ #undef MP_COMPRESSED_DATA
48+ #endif
49+
4150// Number of items per traceback entry (file, line, block)
4251#define TRACEBACK_ENTRY_LEN (3)
4352
5766#define EMG_BUF_TUPLE_OFFSET (EMG_BUF_TRACEBACK_OFFSET + EMG_BUF_TRACEBACK_SIZE)
5867#define EMG_BUF_TUPLE_SIZE (n_args ) (sizeof(mp_obj_tuple_t) + n_args * sizeof(mp_obj_t))
5968#define EMG_BUF_STR_OFFSET (EMG_BUF_TUPLE_OFFSET + EMG_BUF_TUPLE_SIZE(1))
69+ #define EMG_BUF_STR_BUF_OFFSET (EMG_BUF_STR_OFFSET + sizeof(mp_obj_str_t))
6070
6171#if MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE > 0
6272#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) {
100110#endif
101111#endif // MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
102112
113+ STATIC void decompress_error_text_maybe (mp_obj_exception_t * o ) {
114+ #if MICROPY_ROM_TEXT_COMPRESSION
115+ if (o -> args -> len == 1 && mp_obj_is_type (o -> args -> items [0 ], & mp_type_str )) {
116+ mp_obj_str_t * o_str = MP_OBJ_TO_PTR (o -> args -> items [0 ]);
117+ if (MP_IS_COMPRESSED_ROM_STRING (o_str -> data )) {
118+ byte * buf = m_new_maybe (byte , MP_MAX_UNCOMPRESSED_TEXT_LEN + 1 );
119+ if (!buf ) {
120+ #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
121+ // Try and use the emergency exception buf if enough space is available.
122+ buf = (byte * )((uint8_t * )MP_STATE_VM (mp_emergency_exception_buf ) + EMG_BUF_STR_BUF_OFFSET );
123+ size_t avail = (uint8_t * )MP_STATE_VM (mp_emergency_exception_buf ) + mp_emergency_exception_buf_size - buf ;
124+ if (avail < MP_MAX_UNCOMPRESSED_TEXT_LEN + 1 ) {
125+ // No way to decompress, fallback to no message text.
126+ o -> args = (mp_obj_tuple_t * )& mp_const_empty_tuple_obj ;
127+ return ;
128+ }
129+ #else
130+ o -> args = (mp_obj_tuple_t * )& mp_const_empty_tuple_obj ;
131+ return ;
132+ #endif
133+ }
134+ mp_decompress_rom_string (buf , (mp_rom_error_text_t )o_str -> data );
135+ o_str -> data = buf ;
136+ o_str -> len = strlen ((const char * )buf );
137+ o_str -> hash = 0 ;
138+ }
139+ // Lazily compute the string hash.
140+ if (o_str -> hash == 0 ) {
141+ o_str -> hash = qstr_compute_hash (o_str -> data , o_str -> len );
142+ }
143+ }
144+ #endif
145+ }
146+
103147void mp_obj_exception_print (const mp_print_t * print , mp_obj_t o_in , mp_print_kind_t kind ) {
104148 mp_obj_exception_t * o = MP_OBJ_TO_PTR (o_in );
105149 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
112156 mp_print_str (print , ": " );
113157 }
114158
159+ decompress_error_text_maybe (o );
160+
115161 if (k == PRINT_STR || k == PRINT_EXC ) {
116162 if (o -> args == NULL || o -> args -> len == 0 ) {
117163 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
131177 return ;
132178 }
133179 }
180+
134181 mp_obj_tuple_print (print , MP_OBJ_FROM_PTR (o -> args ), kind );
135182}
136183
@@ -189,6 +236,7 @@ mp_obj_t mp_obj_exception_get_value(mp_obj_t self_in) {
189236 if (self -> args -> len == 0 ) {
190237 return mp_const_none ;
191238 } else {
239+ decompress_error_text_maybe (self );
192240 return self -> args -> items [0 ];
193241 }
194242}
@@ -210,6 +258,7 @@ void mp_obj_exception_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
210258 return ;
211259 }
212260 if (attr == MP_QSTR_args ) {
261+ decompress_error_text_maybe (self );
213262 dest [0 ] = MP_OBJ_FROM_PTR (self -> args );
214263 } else if (self -> base .type == & mp_type_StopIteration && attr == MP_QSTR_value ) {
215264 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,
323372 return mp_obj_exception_make_new (exc_type , n_args , 0 , args );
324373}
325374
326- mp_obj_t mp_obj_new_exception_msg (const mp_obj_type_t * exc_type , const char * msg ) {
375+ mp_obj_t mp_obj_new_exception_msg (const mp_obj_type_t * exc_type , mp_rom_error_text_t msg ) {
327376 // Check that the given type is an exception type
328377 assert (exc_type -> make_new == mp_obj_exception_make_new );
329378
@@ -348,9 +397,13 @@ mp_obj_t mp_obj_new_exception_msg(const mp_obj_type_t *exc_type, const char *msg
348397
349398 // Create the string object and call mp_obj_exception_make_new to create the exception
350399 o_str -> base .type = & mp_type_str ;
351- o_str -> len = strlen (msg );
400+ o_str -> len = strlen (( const char * ) msg );
352401 o_str -> data = (const byte * )msg ;
402+ #if MICROPY_ROM_TEXT_COMPRESSION
403+ o_str -> hash = 0 ; // will be computed only if string object is accessed
404+ #else
353405 o_str -> hash = qstr_compute_hash (o_str -> data , o_str -> len );
406+ #endif
354407 mp_obj_t arg = MP_OBJ_FROM_PTR (o_str );
355408 return mp_obj_exception_make_new (exc_type , 1 , 0 , & arg );
356409}
@@ -388,23 +441,23 @@ STATIC void exc_add_strn(void *data, const char *str, size_t len) {
388441 pr -> len += len ;
389442}
390443
391- mp_obj_t mp_obj_new_exception_msg_varg (const mp_obj_type_t * exc_type , const char * fmt , ...) {
444+ mp_obj_t mp_obj_new_exception_msg_varg (const mp_obj_type_t * exc_type , mp_rom_error_text_t fmt , ...) {
392445 va_list args ;
393446 va_start (args , fmt );
394447 mp_obj_t exc = mp_obj_new_exception_msg_vlist (exc_type , fmt , args );
395448 va_end (args );
396449 return exc ;
397450}
398451
399- mp_obj_t mp_obj_new_exception_msg_vlist (const mp_obj_type_t * exc_type , const char * fmt , va_list args ) {
452+ 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 ) {
400453 assert (fmt != NULL );
401454
402455 // Check that the given type is an exception type
403456 assert (exc_type -> make_new == mp_obj_exception_make_new );
404457
405458 // Try to allocate memory for the message
406459 mp_obj_str_t * o_str = m_new_obj_maybe (mp_obj_str_t );
407- size_t o_str_alloc = strlen (fmt ) + 1 ;
460+ size_t o_str_alloc = strlen (( const char * ) fmt ) + 1 ;
408461 byte * o_str_buf = m_new_maybe (byte , o_str_alloc );
409462
410463 bool used_emg_buf = false;
@@ -415,37 +468,51 @@ mp_obj_t mp_obj_new_exception_msg_vlist(const mp_obj_type_t *exc_type, const cha
415468 if ((o_str == NULL || o_str_buf == NULL )
416469 && mp_emergency_exception_buf_size >= EMG_BUF_STR_OFFSET + sizeof (mp_obj_str_t ) + 16 ) {
417470 used_emg_buf = true;
418- o_str = (mp_obj_str_t * )((uint8_t * )MP_STATE_VM (mp_emergency_exception_buf )
419- + EMG_BUF_STR_OFFSET );
420- o_str_buf = (byte * )& o_str [1 ];
421- o_str_alloc = (uint8_t * )MP_STATE_VM (mp_emergency_exception_buf )
422- + mp_emergency_exception_buf_size - o_str_buf ;
471+ o_str = (mp_obj_str_t * )((uint8_t * )MP_STATE_VM (mp_emergency_exception_buf ) + EMG_BUF_STR_OFFSET );
472+ o_str_buf = (byte * )((uint8_t * )MP_STATE_VM (mp_emergency_exception_buf ) + EMG_BUF_STR_BUF_OFFSET );
473+ o_str_alloc = (uint8_t * )MP_STATE_VM (mp_emergency_exception_buf ) + mp_emergency_exception_buf_size - o_str_buf ;
423474 }
424475 #endif
425476
426477 if (o_str == NULL ) {
427- // No memory for the string object so create the exception with no args
478+ // No memory for the string object so create the exception with no args.
479+ // The exception will only have a type and no message (compression is irrelevant).
428480 return mp_obj_exception_make_new (exc_type , 0 , 0 , NULL );
429481 }
430482
431483 if (o_str_buf == NULL ) {
432484 // No memory for the string buffer: assume that the fmt string is in ROM
433- // and use that data as the data of the string
485+ // and use that data as the data of the string.
486+ // The string will point directly to the compressed data -- will need to be decompressed
487+ // prior to display (this case is identical to mp_obj_new_exception_msg above).
434488 o_str -> len = o_str_alloc - 1 ; // will be equal to strlen(fmt)
435489 o_str -> data = (const byte * )fmt ;
436490 } else {
437- // We have some memory to format the string
491+ // We have some memory to format the string.
492+ // TODO: Optimise this to format-while-decompressing (and not require the temp stack space).
438493 struct _exc_printer_t exc_pr = {!used_emg_buf , o_str_alloc , 0 , o_str_buf };
439494 mp_print_t print = {& exc_pr , exc_add_strn };
440- mp_vprintf (& print , fmt , args );
495+ const char * fmt2 = (const char * )fmt ;
496+ #if MICROPY_ROM_TEXT_COMPRESSION
497+ byte decompressed [MP_MAX_UNCOMPRESSED_TEXT_LEN ];
498+ if (MP_IS_COMPRESSED_ROM_STRING (fmt )) {
499+ mp_decompress_rom_string (decompressed , fmt );
500+ fmt2 = (const char * )decompressed ;
501+ }
502+ #endif
503+ mp_vprintf (& print , fmt2 , args );
441504 exc_pr .buf [exc_pr .len ] = '\0' ;
442505 o_str -> len = exc_pr .len ;
443506 o_str -> data = exc_pr .buf ;
444507 }
445508
446509 // Create the string object and call mp_obj_exception_make_new to create the exception
447510 o_str -> base .type = & mp_type_str ;
511+ #if MICROPY_ROM_TEXT_COMPRESSION
512+ o_str -> hash = 0 ; // will be computed only if string object is accessed
513+ #else
448514 o_str -> hash = qstr_compute_hash (o_str -> data , o_str -> len );
515+ #endif
449516 mp_obj_t arg = MP_OBJ_FROM_PTR (o_str );
450517 return mp_obj_exception_make_new (exc_type , 1 , 0 , & arg );
451518}
0 commit comments