From bdf8bc58ed2cc7b678573aceca850eb67fe0fa8d Mon Sep 17 00:00:00 2001 From: microDev <70126934+microDev1@users.noreply.github.com> Date: Mon, 16 Aug 2021 22:22:22 +0530 Subject: [PATCH] allow exception raise inside atexit callback --- lib/utils/pyexec.c | 94 +++++++++++++++++++++------------ lib/utils/pyexec.h | 4 ++ main.c | 8 ++- shared-module/atexit/__init__.c | 16 +++--- shared-module/atexit/__init__.h | 8 ++- 5 files changed, 85 insertions(+), 45 deletions(-) diff --git a/lib/utils/pyexec.c b/lib/utils/pyexec.c index 2651189915..6ffe5d70a7 100644 --- a/lib/utils/pyexec.c +++ b/lib/utils/pyexec.c @@ -44,6 +44,10 @@ #include "lib/utils/pyexec.h" #include "genhdr/mpversion.h" +#if CIRCUITPY_ATEXIT +#include "shared-module/atexit/__init__.h" +#endif + pyexec_mode_kind_t pyexec_mode_kind = PYEXEC_MODE_FRIENDLY_REPL; int pyexec_system_exit = 0; @@ -58,6 +62,7 @@ STATIC bool repl_display_debugging_info = 0; #define EXEC_FLAG_SOURCE_IS_VSTR (16) #define EXEC_FLAG_SOURCE_IS_FILENAME (32) #define EXEC_FLAG_SOURCE_IS_READER (64) +#define EXEC_FLAG_SOURCE_IS_ATEXIT (128) // parses, compiles and executes the code in the lexer // frees the lexer before returning @@ -81,44 +86,49 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input nlr.ret_val = NULL; if (nlr_push(&nlr) == 0) { mp_obj_t module_fun; - #if MICROPY_MODULE_FROZEN_MPY - if (exec_flags & EXEC_FLAG_SOURCE_IS_RAW_CODE) { - // source is a raw_code object, create the function - module_fun = mp_make_function_from_raw_code(source, MP_OBJ_NULL, MP_OBJ_NULL); - } else + #if CIRCUITPY_ATEXIT + if (!(exec_flags & EXEC_FLAG_SOURCE_IS_ATEXIT)) #endif { - #if MICROPY_ENABLE_COMPILER - mp_lexer_t *lex; - if (exec_flags & EXEC_FLAG_SOURCE_IS_VSTR) { - const vstr_t *vstr = source; - lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, vstr->buf, vstr->len, 0); - } else if (exec_flags & EXEC_FLAG_SOURCE_IS_READER) { - lex = mp_lexer_new(MP_QSTR__lt_stdin_gt_, *(mp_reader_t *)source); - } else if (exec_flags & EXEC_FLAG_SOURCE_IS_FILENAME) { - lex = mp_lexer_new_from_file(source); - } else { - lex = (mp_lexer_t *)source; - } - // source is a lexer, parse and compile the script - qstr source_name = lex->source_name; - if (input_kind == MP_PARSE_FILE_INPUT) { - mp_store_global(MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name)); - } - mp_parse_tree_t parse_tree = mp_parse(lex, input_kind); - module_fun = mp_compile(&parse_tree, source_name, exec_flags & EXEC_FLAG_IS_REPL); - // Clear the parse tree because it has a heap pointer we don't need anymore. - *((uint32_t volatile *)&parse_tree.chunk) = 0; - #else - mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("script compilation not supported")); + #if MICROPY_MODULE_FROZEN_MPY + if (exec_flags & EXEC_FLAG_SOURCE_IS_RAW_CODE) { + // source is a raw_code object, create the function + module_fun = mp_make_function_from_raw_code(source, MP_OBJ_NULL, MP_OBJ_NULL); + } else #endif - } + { + #if MICROPY_ENABLE_COMPILER + mp_lexer_t *lex; + if (exec_flags & EXEC_FLAG_SOURCE_IS_VSTR) { + const vstr_t *vstr = source; + lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, vstr->buf, vstr->len, 0); + } else if (exec_flags & EXEC_FLAG_SOURCE_IS_READER) { + lex = mp_lexer_new(MP_QSTR__lt_stdin_gt_, *(mp_reader_t *)source); + } else if (exec_flags & EXEC_FLAG_SOURCE_IS_FILENAME) { + lex = mp_lexer_new_from_file(source); + } else { + lex = (mp_lexer_t *)source; + } + // source is a lexer, parse and compile the script + qstr source_name = lex->source_name; + if (input_kind == MP_PARSE_FILE_INPUT) { + mp_store_global(MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name)); + } + mp_parse_tree_t parse_tree = mp_parse(lex, input_kind); + module_fun = mp_compile(&parse_tree, source_name, exec_flags & EXEC_FLAG_IS_REPL); + // Clear the parse tree because it has a heap pointer we don't need anymore. + *((uint32_t volatile *)&parse_tree.chunk) = 0; + #else + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("script compilation not supported")); + #endif + } - // If the code was loaded from a file it's likely to be running for a while so we'll long - // live it and collect any garbage before running. - if (input_kind == MP_PARSE_FILE_INPUT) { - module_fun = make_obj_long_lived(module_fun, 6); - gc_collect(); + // If the code was loaded from a file it's likely to be running for a while so we'll long + // live it and collect any garbage before running. + if (input_kind == MP_PARSE_FILE_INPUT) { + module_fun = make_obj_long_lived(module_fun, 6); + gc_collect(); + } } // execute code @@ -126,7 +136,15 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input #if MICROPY_REPL_INFO start = mp_hal_ticks_ms(); #endif - mp_call_function_0(module_fun); + #if CIRCUITPY_ATEXIT + if (exec_flags & EXEC_FLAG_SOURCE_IS_ATEXIT) { + atexit_callback_t *callback = (atexit_callback_t *)source; + mp_call_function_n_kw(callback->func, callback->n_pos, callback->n_kw, callback->args); + } else + #endif + { + mp_call_function_0(module_fun); + } mp_hal_set_interrupt_char(-1); // disable interrupt mp_handle_pending(true); // handle any pending exceptions (and any callbacks) nlr_pop(); @@ -741,6 +759,12 @@ int pyexec_frozen_module(const char *name, pyexec_result_t *result) { } #endif +#if CIRCUITPY_ATEXIT +int pyexec_exit_handler(const void *source, pyexec_result_t *result) { + return parse_compile_execute(source, MP_PARSE_FILE_INPUT, EXEC_FLAG_SOURCE_IS_ATEXIT, result); +} +#endif + #if MICROPY_REPL_INFO mp_obj_t pyb_set_repl_info(mp_obj_t o_value) { repl_display_debugging_info = mp_obj_get_int(o_value); diff --git a/lib/utils/pyexec.h b/lib/utils/pyexec.h index 6f0252cfed..3d3c2d6c53 100644 --- a/lib/utils/pyexec.h +++ b/lib/utils/pyexec.h @@ -59,6 +59,10 @@ void pyexec_event_repl_init(void); int pyexec_event_repl_process_char(int c); extern uint8_t pyexec_repl_active; +#if CIRCUITPY_ATEXIT +int pyexec_exit_handler(const void *source, pyexec_result_t *result); +#endif + #if MICROPY_REPL_INFO mp_obj_t pyb_set_repl_info(mp_obj_t o_value); MP_DECLARE_CONST_FUN_OBJ_1(pyb_set_repl_info_obj); diff --git a/main.c b/main.c index 8216713901..74a706e063 100755 --- a/main.c +++ b/main.c @@ -218,7 +218,7 @@ STATIC bool maybe_run_list(const char * const * filenames, pyexec_result_t* exec mp_hal_stdout_tx_str(decompressed); pyexec_file(filename, exec_result); #if CIRCUITPY_ATEXIT - shared_module_atexit_execute(); + shared_module_atexit_execute(exec_result); #endif return true; } @@ -770,7 +770,11 @@ STATIC int run_repl(void) { exit_code = pyexec_friendly_repl(); } #if CIRCUITPY_ATEXIT - shared_module_atexit_execute(); + pyexec_result_t result; + shared_module_atexit_execute(&result); + if (result.return_code == PYEXEC_DEEP_SLEEP) { + exit_code = PYEXEC_DEEP_SLEEP; + } #endif cleanup_after_vm(heap, MP_OBJ_SENTINEL); #if CIRCUITPY_STATUS_LED diff --git a/shared-module/atexit/__init__.c b/shared-module/atexit/__init__.c index 01ebc49254..56271bbab8 100644 --- a/shared-module/atexit/__init__.c +++ b/shared-module/atexit/__init__.c @@ -28,11 +28,6 @@ #include "py/runtime.h" #include "shared-module/atexit/__init__.h" -typedef struct _atexit_callback_t { - size_t n_pos, n_kw; - mp_obj_t func, *args; -} atexit_callback_t; - static size_t callback_len = 0; static atexit_callback_t *callback = NULL; @@ -79,11 +74,18 @@ void shared_module_atexit_unregister(const mp_obj_t *func) { } } -void shared_module_atexit_execute(void) { +void shared_module_atexit_execute(pyexec_result_t *result) { if (callback) { for (size_t i = callback_len; i-- > 0;) { if (callback[i].func != mp_const_none) { - mp_call_function_n_kw(callback[i].func, callback[i].n_pos, callback[i].n_kw, callback[i].args); + if (result != NULL) { + pyexec_result_t res; + if (pyexec_exit_handler(&callback[i], &res) == PYEXEC_DEEP_SLEEP) { + *result = res; + } + } else { + pyexec_exit_handler(&callback[i], NULL); + } } } } diff --git a/shared-module/atexit/__init__.h b/shared-module/atexit/__init__.h index d637a57008..befccb3ea8 100644 --- a/shared-module/atexit/__init__.h +++ b/shared-module/atexit/__init__.h @@ -28,6 +28,12 @@ #define MICROPY_INCLUDED_SHARED_MODULE_ATEXIT___INIT___H #include "py/obj.h" +#include "lib/utils/pyexec.h" + +typedef struct _atexit_callback_t { + size_t n_pos, n_kw; + mp_obj_t func, *args; +} atexit_callback_t; extern void atexit_reset(void); extern void atexit_gc_collect(void); @@ -35,6 +41,6 @@ extern void atexit_gc_collect(void); extern void shared_module_atexit_register(mp_obj_t *func, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); extern void shared_module_atexit_unregister(const mp_obj_t *func); -extern void shared_module_atexit_execute(void); +extern void shared_module_atexit_execute(pyexec_result_t *result); #endif // MICROPY_INCLUDED_SHARED_MODULE_ATEXIT___INIT___H