From 5c7f989f67f71424025b2d9c9bde6736daade82f Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 3 May 2020 17:13:39 +1000 Subject: [PATCH 1/8] lib/utils/pyexec: Make parse-compile function public as pyexe_exec_src. --- lib/utils/pyexec.c | 47 ++++++++++++++++++++-------------------------- lib/utils/pyexec.h | 9 +++++++++ 2 files changed, 29 insertions(+), 27 deletions(-) diff --git a/lib/utils/pyexec.c b/lib/utils/pyexec.c index ec20daff4b23d..d57662011f98c 100644 --- a/lib/utils/pyexec.c +++ b/lib/utils/pyexec.c @@ -50,19 +50,12 @@ int pyexec_system_exit = 0; STATIC bool repl_display_debugging_info = 0; #endif -#define EXEC_FLAG_PRINT_EOF (1) -#define EXEC_FLAG_ALLOW_DEBUGGING (2) -#define EXEC_FLAG_IS_REPL (4) -#define EXEC_FLAG_SOURCE_IS_RAW_CODE (8) -#define EXEC_FLAG_SOURCE_IS_VSTR (16) -#define EXEC_FLAG_SOURCE_IS_FILENAME (32) - // parses, compiles and executes the code in the lexer // frees the lexer before returning -// EXEC_FLAG_PRINT_EOF prints 2 EOF chars: 1 after normal output, 1 after exception output -// EXEC_FLAG_ALLOW_DEBUGGING allows debugging info to be printed after executing the code -// EXEC_FLAG_IS_REPL is used for REPL inputs (flag passed on to mp_compile) -STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input_kind, int exec_flags) { +// PYEXEC_FLAG_PRINT_EOF prints 2 EOF chars: 1 after normal output, 1 after exception output +// PYEXEC_FLAG_ALLOW_DEBUGGING allows debugging info to be printed after executing the code +// PYEXEC_FLAG_IS_REPL is used for REPL inputs (flag passed on to mp_compile) +int pyexec_exec_src(const void *source, mp_parse_input_kind_t input_kind, int exec_flags) { int ret = 0; #if MICROPY_REPL_INFO uint32_t start = 0; @@ -75,7 +68,7 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input if (nlr_push(&nlr) == 0) { mp_obj_t module_fun; #if MICROPY_MODULE_FROZEN_MPY - if (exec_flags & EXEC_FLAG_SOURCE_IS_RAW_CODE) { + if (exec_flags & PYEXEC_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 @@ -83,10 +76,10 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input { #if MICROPY_ENABLE_COMPILER mp_lexer_t *lex; - if (exec_flags & EXEC_FLAG_SOURCE_IS_VSTR) { + if (exec_flags & PYEXEC_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_FILENAME) { + } else if (exec_flags & PYEXEC_FLAG_SOURCE_IS_FILENAME) { lex = mp_lexer_new_from_file(source); } else { lex = (mp_lexer_t *)source; @@ -94,7 +87,7 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input // source is a lexer, parse and compile the script qstr source_name = lex->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); + module_fun = mp_compile(&parse_tree, source_name, exec_flags & PYEXEC_FLAG_IS_REPL); #else mp_raise_msg(&mp_type_RuntimeError, "script compilation not supported"); #endif @@ -110,7 +103,7 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input mp_handle_pending(true); // handle any pending exceptions (and any callbacks) nlr_pop(); ret = 1; - if (exec_flags & EXEC_FLAG_PRINT_EOF) { + if (exec_flags & PYEXEC_FLAG_PRINT_EOF) { mp_hal_stdout_tx_strn("\x04", 1); } } else { @@ -118,7 +111,7 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input mp_hal_set_interrupt_char(-1); // disable interrupt mp_handle_pending(false); // clear any pending exceptions (and run any callbacks) // print EOF after normal output - if (exec_flags & EXEC_FLAG_PRINT_EOF) { + if (exec_flags & PYEXEC_FLAG_PRINT_EOF) { mp_hal_stdout_tx_strn("\x04", 1); } // check for SystemExit @@ -133,7 +126,7 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input #if MICROPY_REPL_INFO // display debugging info if wanted - if ((exec_flags & EXEC_FLAG_ALLOW_DEBUGGING) && repl_display_debugging_info) { + if ((exec_flags & PYEXEC_FLAG_ALLOW_DEBUGGING) && repl_display_debugging_info) { mp_uint_t ticks = mp_hal_ticks_ms() - start; // TODO implement a function that does this properly printf("took " UINT_FMT " ms\n", ticks); // qstr info @@ -153,7 +146,7 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input } #endif - if (exec_flags & EXEC_FLAG_PRINT_EOF) { + if (exec_flags & PYEXEC_FLAG_PRINT_EOF) { mp_hal_stdout_tx_strn("\x04", 1); } @@ -226,7 +219,7 @@ STATIC int pyexec_raw_repl_process_char(int c) { return PYEXEC_FORCED_EXIT; } - int ret = parse_compile_execute(MP_STATE_VM(repl_line), MP_PARSE_FILE_INPUT, EXEC_FLAG_PRINT_EOF | EXEC_FLAG_SOURCE_IS_VSTR); + int ret = pyexec_exec_src(MP_STATE_VM(repl_line), MP_PARSE_FILE_INPUT, PYEXEC_FLAG_PRINT_EOF | PYEXEC_FLAG_SOURCE_IS_VSTR); if (ret & PYEXEC_FORCED_EXIT) { return ret; } @@ -247,7 +240,7 @@ STATIC int pyexec_friendly_repl_process_char(int c) { } else if (c == CHAR_CTRL_D) { // end of input mp_hal_stdout_tx_str("\r\n"); - int ret = parse_compile_execute(MP_STATE_VM(repl_line), MP_PARSE_FILE_INPUT, EXEC_FLAG_ALLOW_DEBUGGING | EXEC_FLAG_IS_REPL | EXEC_FLAG_SOURCE_IS_VSTR); + int ret = pyexec_exec_src(MP_STATE_VM(repl_line), MP_PARSE_FILE_INPUT, PYEXEC_FLAG_ALLOW_DEBUGGING | PYEXEC_FLAG_IS_REPL | PYEXEC_FLAG_SOURCE_IS_VSTR); if (ret & PYEXEC_FORCED_EXIT) { return ret; } @@ -336,7 +329,7 @@ STATIC int pyexec_friendly_repl_process_char(int c) { } exec:; - int ret = parse_compile_execute(MP_STATE_VM(repl_line), MP_PARSE_SINGLE_INPUT, EXEC_FLAG_ALLOW_DEBUGGING | EXEC_FLAG_IS_REPL | EXEC_FLAG_SOURCE_IS_VSTR); + int ret = pyexec_exec_src(MP_STATE_VM(repl_line), MP_PARSE_SINGLE_INPUT, PYEXEC_FLAG_ALLOW_DEBUGGING | PYEXEC_FLAG_IS_REPL | PYEXEC_FLAG_SOURCE_IS_VSTR); if (ret & PYEXEC_FORCED_EXIT) { return ret; } @@ -408,7 +401,7 @@ int pyexec_raw_repl(void) { return PYEXEC_FORCED_EXIT; } - int ret = parse_compile_execute(&line, MP_PARSE_FILE_INPUT, EXEC_FLAG_PRINT_EOF | EXEC_FLAG_SOURCE_IS_VSTR); + int ret = pyexec_exec_src(&line, MP_PARSE_FILE_INPUT, PYEXEC_FLAG_PRINT_EOF | PYEXEC_FLAG_SOURCE_IS_VSTR); if (ret & PYEXEC_FORCED_EXIT) { return ret; } @@ -537,7 +530,7 @@ int pyexec_friendly_repl(void) { } } - ret = parse_compile_execute(&line, parse_input_kind, EXEC_FLAG_ALLOW_DEBUGGING | EXEC_FLAG_IS_REPL | EXEC_FLAG_SOURCE_IS_VSTR); + ret = pyexec_exec_src(&line, parse_input_kind, PYEXEC_FLAG_ALLOW_DEBUGGING | PYEXEC_FLAG_IS_REPL | PYEXEC_FLAG_SOURCE_IS_VSTR); if (ret & PYEXEC_FORCED_EXIT) { return ret; } @@ -548,7 +541,7 @@ int pyexec_friendly_repl(void) { #endif // MICROPY_ENABLE_COMPILER int pyexec_file(const char *filename) { - return parse_compile_execute(filename, MP_PARSE_FILE_INPUT, EXEC_FLAG_SOURCE_IS_FILENAME); + return pyexec_exec_src(filename, MP_PARSE_FILE_INPUT, PYEXEC_FLAG_SOURCE_IS_FILENAME); } int pyexec_file_if_exists(const char *filename) { @@ -571,12 +564,12 @@ int pyexec_frozen_module(const char *name) { switch (frozen_type) { #if MICROPY_MODULE_FROZEN_STR case MP_FROZEN_STR: - return parse_compile_execute(frozen_data, MP_PARSE_FILE_INPUT, 0); + return pyexec_exec_src(frozen_data, MP_PARSE_FILE_INPUT, 0); #endif #if MICROPY_MODULE_FROZEN_MPY case MP_FROZEN_MPY: - return parse_compile_execute(frozen_data, MP_PARSE_FILE_INPUT, EXEC_FLAG_SOURCE_IS_RAW_CODE); + return pyexec_exec_src(frozen_data, MP_PARSE_FILE_INPUT, PYEXEC_FLAG_SOURCE_IS_RAW_CODE); #endif default: diff --git a/lib/utils/pyexec.h b/lib/utils/pyexec.h index f69c5ce9a884e..da3335ef025be 100644 --- a/lib/utils/pyexec.h +++ b/lib/utils/pyexec.h @@ -27,6 +27,7 @@ #define MICROPY_INCLUDED_LIB_UTILS_PYEXEC_H #include "py/obj.h" +#include "py/parse.h" typedef enum { PYEXEC_MODE_FRIENDLY_REPL, @@ -40,9 +41,17 @@ extern pyexec_mode_kind_t pyexec_mode_kind; // It will reset to 0 at the start of each execution (eg each REPL entry). extern int pyexec_system_exit; +#define PYEXEC_FLAG_PRINT_EOF (0x0001) +#define PYEXEC_FLAG_ALLOW_DEBUGGING (0x0002) +#define PYEXEC_FLAG_IS_REPL (0x0004) +#define PYEXEC_FLAG_SOURCE_IS_RAW_CODE (0x0008) +#define PYEXEC_FLAG_SOURCE_IS_VSTR (0x0010) +#define PYEXEC_FLAG_SOURCE_IS_FILENAME (0x0020) + #define PYEXEC_FORCED_EXIT (0x100) #define PYEXEC_SWITCH_MODE (0x200) +int pyexec_exec_src(const void *source, mp_parse_input_kind_t input_kind, int exec_flags); int pyexec_raw_repl(void); int pyexec_friendly_repl(void); int pyexec_file(const char *filename); From 1186ba0e4514fee2929b0b00fc36eb8c76f21647 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 3 May 2020 17:25:11 +1000 Subject: [PATCH 2/8] lib/utils/pyexec: Invert return code of pyexec execution functions. So a return value of "0" is success, non-zero is error. --- lib/utils/pyexec.c | 11 ++++++----- ports/cc3200/mptask.c | 4 ++-- ports/stm32/main.c | 4 ++-- ports/teensy/main.c | 4 ++-- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/lib/utils/pyexec.c b/lib/utils/pyexec.c index d57662011f98c..d712f72db9799 100644 --- a/lib/utils/pyexec.c +++ b/lib/utils/pyexec.c @@ -50,8 +50,9 @@ int pyexec_system_exit = 0; STATIC bool repl_display_debugging_info = 0; #endif -// parses, compiles and executes the code in the lexer -// frees the lexer before returning +// Parses, compiles and executes the code in the lexer. +// Frees the lexer before returning. +// Returns 0 for success, non-zero for an error or SystemExit. // PYEXEC_FLAG_PRINT_EOF prints 2 EOF chars: 1 after normal output, 1 after exception output // PYEXEC_FLAG_ALLOW_DEBUGGING allows debugging info to be printed after executing the code // PYEXEC_FLAG_IS_REPL is used for REPL inputs (flag passed on to mp_compile) @@ -62,7 +63,7 @@ int pyexec_exec_src(const void *source, mp_parse_input_kind_t input_kind, int ex #endif // by default a SystemExit exception returns 0 - pyexec_system_exit = 0; + pyexec_system_exit = 1; nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { @@ -102,7 +103,7 @@ int pyexec_exec_src(const void *source, mp_parse_input_kind_t input_kind, int ex mp_hal_set_interrupt_char(-1); // disable interrupt mp_handle_pending(true); // handle any pending exceptions (and any callbacks) nlr_pop(); - ret = 1; + ret = 0; if (exec_flags & PYEXEC_FLAG_PRINT_EOF) { mp_hal_stdout_tx_strn("\x04", 1); } @@ -120,7 +121,7 @@ int pyexec_exec_src(const void *source, mp_parse_input_kind_t input_kind, int ex ret = pyexec_system_exit; } else { mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val)); - ret = 0; + ret = 1; } } diff --git a/ports/cc3200/mptask.c b/ports/cc3200/mptask.c index f06a502776087..be7688316e07f 100644 --- a/ports/cc3200/mptask.c +++ b/ports/cc3200/mptask.c @@ -185,7 +185,7 @@ void TASK_MicroPython (void *pvParameters) { if (ret & PYEXEC_FORCED_EXIT) { goto soft_reset_exit; } - if (!ret) { + if (ret) { // flash the system led mperror_signal_error(); } @@ -210,7 +210,7 @@ void TASK_MicroPython (void *pvParameters) { if (ret & PYEXEC_FORCED_EXIT) { goto soft_reset_exit; } - if (!ret) { + if (ret) { // flash the system led mperror_signal_error(); } diff --git a/ports/stm32/main.c b/ports/stm32/main.c index b5dbfa50fd5cc..4c762f8d9ad89 100644 --- a/ports/stm32/main.c +++ b/ports/stm32/main.c @@ -660,7 +660,7 @@ void stm32_main(uint32_t reset_mode) { if (ret & PYEXEC_FORCED_EXIT) { goto soft_reset_exit; } - if (!ret) { + if (ret) { flash_error(4); } } @@ -721,7 +721,7 @@ void stm32_main(uint32_t reset_mode) { if (ret & PYEXEC_FORCED_EXIT) { goto soft_reset_exit; } - if (!ret) { + if (ret) { flash_error(3); } } diff --git a/ports/teensy/main.c b/ports/teensy/main.c index df3fd1ffcf5ac..1f27686266dbb 100644 --- a/ports/teensy/main.c +++ b/ports/teensy/main.c @@ -302,7 +302,7 @@ int main(void) { #if MICROPY_MODULE_FROZEN pyexec_frozen_module("boot.py"); #else - if (!pyexec_file_if_exists("/boot.py")) { + if (pyexec_file_if_exists("/boot.py")) { flash_error(4); } #endif @@ -322,7 +322,7 @@ int main(void) { } else { vstr_add_str(vstr, mp_obj_str_get_str(pyb_config_main)); } - if (!pyexec_file_if_exists(vstr_null_terminated_str(vstr))) { + if (pyexec_file_if_exists(vstr_null_terminated_str(vstr))) { flash_error(3); } vstr_free(vstr); From 045ac3aa7ac4fcbd5007e5ba8e16a0fe85e6f4e9 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 3 May 2020 17:36:08 +1000 Subject: [PATCH 3/8] lib/utils/pyexec: Factor uncaught-exc-handler, support SystemExit arg. --- lib/utils/pyexec.c | 34 ++++++++++++++++++++++++---------- lib/utils/pyexec.h | 1 + 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/lib/utils/pyexec.c b/lib/utils/pyexec.c index d712f72db9799..5b71c1108eddf 100644 --- a/lib/utils/pyexec.c +++ b/lib/utils/pyexec.c @@ -62,8 +62,8 @@ int pyexec_exec_src(const void *source, mp_parse_input_kind_t input_kind, int ex uint32_t start = 0; #endif - // by default a SystemExit exception returns 0 - pyexec_system_exit = 1; + // By default a SystemExit exception returns a value based on its argument. + pyexec_system_exit = 0; nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { @@ -115,14 +115,7 @@ int pyexec_exec_src(const void *source, mp_parse_input_kind_t input_kind, int ex if (exec_flags & PYEXEC_FLAG_PRINT_EOF) { mp_hal_stdout_tx_strn("\x04", 1); } - // check for SystemExit - if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(((mp_obj_base_t *)nlr.ret_val)->type), MP_OBJ_FROM_PTR(&mp_type_SystemExit))) { - // at the moment, the value of SystemExit is unused - ret = pyexec_system_exit; - } else { - mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val)); - ret = 1; - } + ret = pyexec_handle_uncaught_exception(nlr.ret_val); } #if MICROPY_REPL_INFO @@ -154,6 +147,27 @@ int pyexec_exec_src(const void *source, mp_parse_input_kind_t input_kind, int ex return ret; } +// If exc is SystemExit, return pyexec_system_exit or'd with lower 8 bits of SystemExit value. +// For all other exceptions, return 1. +int pyexec_handle_uncaught_exception(mp_obj_base_t *exc) { + if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(exc->type), MP_OBJ_FROM_PTR(&mp_type_SystemExit))) { + // SystemExit was raised, evaluate its argument: + // - None is an exit value of 0; + // - an int is its value; + // - anything else is 1. + mp_obj_t exit_val = mp_obj_exception_get_value(MP_OBJ_FROM_PTR(exc)); + mp_int_t val = 0; + if (exit_val != mp_const_none && !mp_obj_get_int_maybe(exit_val, &val)) { + val = 1; + } + return pyexec_system_exit | (val & 255); + } else { + // Report all other exceptions and return 1 to indicate error. + mp_obj_print_exception(MICROPY_ERROR_PRINTER, MP_OBJ_FROM_PTR(exc)); + return 1; + } +} + #if MICROPY_ENABLE_COMPILER #if MICROPY_REPL_EVENT_DRIVEN diff --git a/lib/utils/pyexec.h b/lib/utils/pyexec.h index da3335ef025be..f2f6e7db11a4b 100644 --- a/lib/utils/pyexec.h +++ b/lib/utils/pyexec.h @@ -52,6 +52,7 @@ extern int pyexec_system_exit; #define PYEXEC_SWITCH_MODE (0x200) int pyexec_exec_src(const void *source, mp_parse_input_kind_t input_kind, int exec_flags); +int pyexec_handle_uncaught_exception(mp_obj_base_t *exc); int pyexec_raw_repl(void); int pyexec_friendly_repl(void); int pyexec_file(const char *filename); From 07ffd24fcf8098575c7635323aa0b1f1580ce9bc Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 3 May 2020 17:36:31 +1000 Subject: [PATCH 4/8] lib/utils/pyexec: Add optional features from unix port. --- lib/utils/pyexec.c | 35 ++++++++++++++++++++++++++++------- lib/utils/pyexec.h | 10 +++++++--- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/lib/utils/pyexec.c b/lib/utils/pyexec.c index 5b71c1108eddf..ac9909317feed 100644 --- a/lib/utils/pyexec.c +++ b/lib/utils/pyexec.c @@ -82,12 +82,30 @@ int pyexec_exec_src(const void *source, mp_parse_input_kind_t input_kind, int ex lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, vstr->buf, vstr->len, 0); } else if (exec_flags & PYEXEC_FLAG_SOURCE_IS_FILENAME) { lex = mp_lexer_new_from_file(source); + #if MICROPY_HELPER_LEXER_UNIX + } else if (exec_flags & PYEXEC_FLAG_SOURCE_IS_FD) { + int fd = (int)(intptr_t)source; + lex = mp_lexer_new_from_fd(MP_QSTR__lt_stdin_gt_, fd, false); + #endif } else { lex = (mp_lexer_t *)source; } // source is a lexer, parse and compile the script qstr source_name = lex->source_name; + #if MICROPY_PY___FILE__ + if (input_kind == MP_PARSE_FILE_INPUT) { + mp_store_global(MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name)); + } + #endif mp_parse_tree_t parse_tree = mp_parse(lex, input_kind); + #if defined(MICROPY_UNIX_COVERAGE) + // allow to print the parse tree in the coverage build + if (mp_verbose_flag >= 3) { + printf("----------------\n"); + mp_parse_node_print(parse_tree.root, 0); + printf("----------------\n"); + } + #endif module_fun = mp_compile(&parse_tree, source_name, exec_flags & PYEXEC_FLAG_IS_REPL); #else mp_raise_msg(&mp_type_RuntimeError, "script compilation not supported"); @@ -95,13 +113,16 @@ int pyexec_exec_src(const void *source, mp_parse_input_kind_t input_kind, int ex } // execute code - mp_hal_set_interrupt_char(CHAR_CTRL_C); // allow ctrl-C to interrupt us - #if MICROPY_REPL_INFO - start = mp_hal_ticks_ms(); - #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) + if (!(exec_flags & PYEXEC_FLAG_COMPILE_ONLY)) { + mp_hal_set_interrupt_char(CHAR_CTRL_C); // allow ctrl-C to interrupt us + #if MICROPY_REPL_INFO + start = mp_hal_ticks_ms(); + #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(); ret = 0; if (exec_flags & PYEXEC_FLAG_PRINT_EOF) { diff --git a/lib/utils/pyexec.h b/lib/utils/pyexec.h index f2f6e7db11a4b..962a3422970d0 100644 --- a/lib/utils/pyexec.h +++ b/lib/utils/pyexec.h @@ -44,9 +44,13 @@ extern int pyexec_system_exit; #define PYEXEC_FLAG_PRINT_EOF (0x0001) #define PYEXEC_FLAG_ALLOW_DEBUGGING (0x0002) #define PYEXEC_FLAG_IS_REPL (0x0004) -#define PYEXEC_FLAG_SOURCE_IS_RAW_CODE (0x0008) -#define PYEXEC_FLAG_SOURCE_IS_VSTR (0x0010) -#define PYEXEC_FLAG_SOURCE_IS_FILENAME (0x0020) +#define PYEXEC_FLAG_COMPILE_ONLY (0x0008) +#define PYEXEC_FLAG_SOURCE_IS_RAW_CODE (0x0010) +#define PYEXEC_FLAG_SOURCE_IS_VSTR (0x0020) +#define PYEXEC_FLAG_SOURCE_IS_FILENAME (0x0040) +#if MICROPY_HELPER_LEXER_UNIX +#define PYEXEC_FLAG_SOURCE_IS_FD (0x0080) +#endif #define PYEXEC_FORCED_EXIT (0x100) #define PYEXEC_SWITCH_MODE (0x200) From bee77115c6544a3aba8c84033092284bd2452fcd Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 3 May 2020 17:53:22 +1000 Subject: [PATCH 5/8] lib/utils/pyexec: Allow initial pyexec_system_exit to be configured. Unix port exits when SystemExit is raised, bare-metal does nothing. --- lib/utils/pyexec.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/utils/pyexec.c b/lib/utils/pyexec.c index ac9909317feed..aa7b3266447c3 100644 --- a/lib/utils/pyexec.c +++ b/lib/utils/pyexec.c @@ -63,7 +63,10 @@ int pyexec_exec_src(const void *source, mp_parse_input_kind_t input_kind, int ex #endif // By default a SystemExit exception returns a value based on its argument. - pyexec_system_exit = 0; + #ifndef MICROPY_PYEXEC_SYSTEM_EXIT_DEFAULT + #define MICROPY_PYEXEC_SYSTEM_EXIT_DEFAULT (0) + #endif + pyexec_system_exit = MICROPY_PYEXEC_SYSTEM_EXIT_DEFAULT; nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { From f26bceb03750c015ccf3f62119be61a089aacbda Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 3 May 2020 21:46:38 +1000 Subject: [PATCH 6/8] lib/utils/pyexec: Support changing the stdio mode to/from raw. --- lib/utils/pyexec.c | 14 ++++++++++++-- py/mphal.h | 8 ++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/utils/pyexec.c b/lib/utils/pyexec.c index aa7b3266447c3..da99a5a1e32e6 100644 --- a/lib/utils/pyexec.c +++ b/lib/utils/pyexec.c @@ -482,6 +482,8 @@ int pyexec_friendly_repl(void) { */ for (;;) { + mp_hal_stdio_mode_raw(); + input_restart: #if MICROPY_HW_ENABLE_USB @@ -510,20 +512,23 @@ int pyexec_friendly_repl(void) { if (ret == CHAR_CTRL_A) { // change to raw REPL mp_hal_stdout_tx_str("\r\n"); + mp_hal_stdio_mode_orig(); vstr_clear(&line); pyexec_mode_kind = PYEXEC_MODE_RAW_REPL; return 0; } else if (ret == CHAR_CTRL_B) { // reset friendly REPL mp_hal_stdout_tx_str("\r\n"); + mp_hal_stdio_mode_orig(); goto friendly_repl_reset; } else if (ret == CHAR_CTRL_C) { // break mp_hal_stdout_tx_str("\r\n"); - continue; + goto input_restart; } else if (ret == CHAR_CTRL_D) { // exit for a soft reset mp_hal_stdout_tx_str("\r\n"); + mp_hal_stdio_mode_orig(); vstr_clear(&line); return PYEXEC_FORCED_EXIT; } else if (ret == CHAR_CTRL_E) { @@ -552,7 +557,10 @@ int pyexec_friendly_repl(void) { } parse_input_kind = MP_PARSE_FILE_INPUT; } else if (vstr_len(&line) == 0) { - continue; + if (ret != 0) { + mp_hal_stdout_tx_str("\r\n"); + } + goto input_restart; } else { // got a line with non-zero length, see if it needs continuing while (mp_repl_continue_with_input(vstr_null_terminated_str(&line))) { @@ -569,6 +577,8 @@ int pyexec_friendly_repl(void) { } } + mp_hal_stdio_mode_orig(); + ret = pyexec_exec_src(&line, parse_input_kind, PYEXEC_FLAG_ALLOW_DEBUGGING | PYEXEC_FLAG_IS_REPL | PYEXEC_FLAG_SOURCE_IS_VSTR); if (ret & PYEXEC_FORCED_EXIT) { return ret; diff --git a/py/mphal.h b/py/mphal.h index 66d80705a60ee..f73a961ad9862 100644 --- a/py/mphal.h +++ b/py/mphal.h @@ -34,6 +34,14 @@ #include #endif +#ifndef mp_hal_stdio_mode_raw +#define mp_hal_stdio_mode_raw() +#endif + +#ifndef mp_hal_stdio_mode_orig +#define mp_hal_stdio_mode_orig() +#endif + #ifndef mp_hal_stdio_poll uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags); #endif From 01e45d746de919d827bb2abd032c992778ce99d3 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 3 May 2020 21:46:45 +1000 Subject: [PATCH 7/8] lib/utils/pyexec: Support compiling without a board/mcu defined. --- lib/utils/pyexec.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/utils/pyexec.c b/lib/utils/pyexec.c index da99a5a1e32e6..bdaf83cfc80ed 100644 --- a/lib/utils/pyexec.c +++ b/lib/utils/pyexec.c @@ -458,7 +458,12 @@ int pyexec_friendly_repl(void) { #endif friendly_repl_reset: + #ifdef MICROPY_HW_BOARD_NAME mp_hal_stdout_tx_str("MicroPython " MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE "; " MICROPY_HW_BOARD_NAME " with " MICROPY_HW_MCU_NAME "\r\n"); + #else + mp_hal_stdout_tx_str("MicroPython " MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE "; " MICROPY_PY_SYS_PLATFORM " version\r\nUse Ctrl-D to exit, Ctrl-E for paste mode\n"); + #endif + #if MICROPY_PY_BUILTINS_HELP mp_hal_stdout_tx_str("Type \"help()\" for more information.\r\n"); #endif From 1470b5d9da70185a32047f1a531e77eb5f7a2acc Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 3 May 2020 17:53:56 +1000 Subject: [PATCH 8/8] unix: Use pyexec for executing files/str/stdin and for REPL. --- ports/unix/Makefile | 1 + ports/unix/main.c | 193 ++++---------------------------------- ports/unix/mpconfigport.h | 2 + ports/unix/mphalport.h | 5 +- 4 files changed, 27 insertions(+), 174 deletions(-) diff --git a/ports/unix/Makefile b/ports/unix/Makefile index ec14166149ba8..00ac7f82aa430 100644 --- a/ports/unix/Makefile +++ b/ports/unix/Makefile @@ -206,6 +206,7 @@ LIB_SRC_C += $(addprefix lib/,\ $(LIB_SRC_C_EXTRA) \ timeutils/timeutils.c \ utils/gchelper_generic.c \ + utils/pyexec.c \ ) OBJ = $(PY_O) diff --git a/ports/unix/main.c b/ports/unix/main.c index 5251fe8ae29a6..3910dffc7617a 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -46,6 +46,7 @@ #include "py/stackctrl.h" #include "py/mphal.h" #include "py/mpthread.h" +#include "lib/utils/pyexec.h" #include "extmod/misc.h" #include "extmod/vfs.h" #include "extmod/vfs_posix.h" @@ -53,7 +54,7 @@ #include "input.h" // Command line options, with their defaults -STATIC bool compile_only = false; +STATIC int compile_only = 0; STATIC uint emit_opt = MP_EMIT_OPT_NONE; #if MICROPY_ENABLE_GC @@ -71,93 +72,6 @@ STATIC void stderr_print_strn(void *env, const char *str, size_t len) { const mp_print_t mp_stderr_print = {NULL, stderr_print_strn}; -#define FORCED_EXIT (0x100) -// If exc is SystemExit, return value where FORCED_EXIT bit set, -// and lower 8 bits are SystemExit value. For all other exceptions, -// return 1. -STATIC int handle_uncaught_exception(mp_obj_base_t *exc) { - // check for SystemExit - if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(exc->type), MP_OBJ_FROM_PTR(&mp_type_SystemExit))) { - // None is an exit value of 0; an int is its value; anything else is 1 - mp_obj_t exit_val = mp_obj_exception_get_value(MP_OBJ_FROM_PTR(exc)); - mp_int_t val = 0; - if (exit_val != mp_const_none && !mp_obj_get_int_maybe(exit_val, &val)) { - val = 1; - } - return FORCED_EXIT | (val & 255); - } - - // Report all other exceptions - mp_obj_print_exception(&mp_stderr_print, MP_OBJ_FROM_PTR(exc)); - return 1; -} - -#define LEX_SRC_STR (1) -#define LEX_SRC_VSTR (2) -#define LEX_SRC_FILENAME (3) -#define LEX_SRC_STDIN (4) - -// Returns standard error codes: 0 for success, 1 for all other errors, -// except if FORCED_EXIT bit is set then script raised SystemExit and the -// value of the exit is in the lower 8 bits of the return value -STATIC int execute_from_lexer(int source_kind, const void *source, mp_parse_input_kind_t input_kind, bool is_repl) { - mp_hal_set_interrupt_char(CHAR_CTRL_C); - - nlr_buf_t nlr; - if (nlr_push(&nlr) == 0) { - // create lexer based on source kind - mp_lexer_t *lex; - if (source_kind == LEX_SRC_STR) { - const char *line = source; - lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, line, strlen(line), false); - } else if (source_kind == LEX_SRC_VSTR) { - const vstr_t *vstr = source; - lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, vstr->buf, vstr->len, false); - } else if (source_kind == LEX_SRC_FILENAME) { - lex = mp_lexer_new_from_file((const char *)source); - } else { // LEX_SRC_STDIN - lex = mp_lexer_new_from_fd(MP_QSTR__lt_stdin_gt_, 0, false); - } - - qstr source_name = lex->source_name; - - #if MICROPY_PY___FILE__ - if (input_kind == MP_PARSE_FILE_INPUT) { - mp_store_global(MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name)); - } - #endif - - mp_parse_tree_t parse_tree = mp_parse(lex, input_kind); - - #if defined(MICROPY_UNIX_COVERAGE) - // allow to print the parse tree in the coverage build - if (mp_verbose_flag >= 3) { - printf("----------------\n"); - mp_parse_node_print(parse_tree.root, 0); - printf("----------------\n"); - } - #endif - - mp_obj_t module_fun = mp_compile(&parse_tree, source_name, is_repl); - - if (!compile_only) { - // execute it - mp_call_function_0(module_fun); - } - - mp_hal_set_interrupt_char(-1); - mp_handle_pending(true); - nlr_pop(); - return 0; - - } else { - // uncaught exception - mp_hal_set_interrupt_char(-1); - mp_handle_pending(false); - return handle_uncaught_exception(nlr.ret_val); - } -} - #if MICROPY_USE_READLINE == 1 #include "lib/mp-readline/readline.h" #else @@ -177,91 +91,18 @@ STATIC char *strjoin(const char *s1, int sep_char, const char *s2) { #endif STATIC int do_repl(void) { - mp_hal_stdout_tx_str("MicroPython " MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE "; " - MICROPY_PY_SYS_PLATFORM " version\nUse Ctrl-D to exit, Ctrl-E for paste mode\n"); - #if MICROPY_USE_READLINE == 1 // use MicroPython supplied readline - - vstr_t line; - vstr_init(&line, 16); - for (;;) { - mp_hal_stdio_mode_raw(); - - input_restart: - vstr_reset(&line); - int ret = readline(&line, ">>> "); - mp_parse_input_kind_t parse_input_kind = MP_PARSE_SINGLE_INPUT; - - if (ret == CHAR_CTRL_C) { - // cancel input - mp_hal_stdout_tx_str("\r\n"); - goto input_restart; - } else if (ret == CHAR_CTRL_D) { - // EOF - printf("\n"); - mp_hal_stdio_mode_orig(); - vstr_clear(&line); - return 0; - } else if (ret == CHAR_CTRL_E) { - // paste mode - mp_hal_stdout_tx_str("\npaste mode; Ctrl-C to cancel, Ctrl-D to finish\n=== "); - vstr_reset(&line); - for (;;) { - char c = mp_hal_stdin_rx_chr(); - if (c == CHAR_CTRL_C) { - // cancel everything - mp_hal_stdout_tx_str("\n"); - goto input_restart; - } else if (c == CHAR_CTRL_D) { - // end of input - mp_hal_stdout_tx_str("\n"); - break; - } else { - // add char to buffer and echo - vstr_add_byte(&line, c); - if (c == '\r') { - mp_hal_stdout_tx_str("\n=== "); - } else { - mp_hal_stdout_tx_strn(&c, 1); - } - } - } - parse_input_kind = MP_PARSE_FILE_INPUT; - } else if (line.len == 0) { - if (ret != 0) { - printf("\n"); - } - goto input_restart; - } else { - // got a line with non-zero length, see if it needs continuing - while (mp_repl_continue_with_input(vstr_null_terminated_str(&line))) { - vstr_add_byte(&line, '\n'); - ret = readline(&line, "... "); - if (ret == CHAR_CTRL_C) { - // cancel everything - printf("\n"); - goto input_restart; - } else if (ret == CHAR_CTRL_D) { - // stop entering compound statement - break; - } - } - } - - mp_hal_stdio_mode_orig(); - - ret = execute_from_lexer(LEX_SRC_VSTR, &line, parse_input_kind, true); - if (ret & FORCED_EXIT) { - return ret; - } - } + return pyexec_friendly_repl(); #else // use simple readline + mp_hal_stdout_tx_str("MicroPython " MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE "; " + MICROPY_PY_SYS_PLATFORM " version\nUse Ctrl-D to exit, Ctrl-E for paste mode\n"); + for (;;) { char *line = prompt(">>> "); if (line == NULL) { @@ -279,8 +120,11 @@ STATIC int do_repl(void) { line = line3; } - int ret = execute_from_lexer(LEX_SRC_STR, line, MP_PARSE_SINGLE_INPUT, true); - if (ret & FORCED_EXIT) { + vstr_t vstr; + vstr.buf = (char *)line; + vstr.len = strlen(line); + int ret = pyexec_exec_src(&vstr, MP_PARSE_SINGLE_INPUT, PYEXEC_FLAG_IS_REPL | PYEXEC_FLAG_SOURCE_IS_VSTR | compile_only); + if (ret & PYEXEC_FORCED_EXIT) { return ret; } free(line); @@ -290,11 +134,14 @@ STATIC int do_repl(void) { } STATIC int do_file(const char *file) { - return execute_from_lexer(LEX_SRC_FILENAME, file, MP_PARSE_FILE_INPUT, false); + return pyexec_exec_src(file, MP_PARSE_FILE_INPUT, PYEXEC_FLAG_SOURCE_IS_FILENAME | compile_only); } STATIC int do_str(const char *str) { - return execute_from_lexer(LEX_SRC_STR, str, MP_PARSE_FILE_INPUT, false); + vstr_t vstr; + vstr.buf = (char *)str; + vstr.len = strlen(str); + return pyexec_exec_src(&vstr, MP_PARSE_FILE_INPUT, PYEXEC_FLAG_SOURCE_IS_VSTR | compile_only); } STATIC void print_help(char **argv) { @@ -351,7 +198,7 @@ STATIC void pre_process_options(int argc, char **argv) { } if (0) { } else if (strcmp(argv[a + 1], "compile-only") == 0) { - compile_only = true; + compile_only = PYEXEC_FLAG_COMPILE_ONLY; } else if (strcmp(argv[a + 1], "emit=bytecode") == 0) { emit_opt = MP_EMIT_OPT_BYTECODE; #if MICROPY_EMIT_NATIVE @@ -567,7 +414,7 @@ MP_NOINLINE int main_(int argc, char **argv) { return invalid_args(); } ret = do_str(argv[a + 1]); - if (ret & FORCED_EXIT) { + if (ret & PYEXEC_FORCED_EXIT) { break; } a += 1; @@ -598,7 +445,7 @@ MP_NOINLINE int main_(int argc, char **argv) { nlr_pop(); } else { // uncaught exception - return handle_uncaught_exception(nlr.ret_val) & 0xff; + return pyexec_handle_uncaught_exception(nlr.ret_val) & 0xff; } if (mp_obj_is_package(mod) && !subpkg_tried) { @@ -662,7 +509,7 @@ MP_NOINLINE int main_(int argc, char **argv) { ret = do_repl(); prompt_write_history(); } else { - ret = execute_from_lexer(LEX_SRC_STDIN, NULL, MP_PARSE_FILE_INPUT, false); + ret = pyexec_exec_src((void*)(uintptr_t)STDIN_FILENO, MP_PARSE_FILE_INPUT, PYEXEC_FLAG_SOURCE_IS_FD | compile_only); } } diff --git a/ports/unix/mpconfigport.h b/ports/unix/mpconfigport.h index f3c61c18f100d..055752a6b9996 100644 --- a/ports/unix/mpconfigport.h +++ b/ports/unix/mpconfigport.h @@ -357,4 +357,6 @@ struct _mp_bluetooth_btstack_root_pointers_t; #include #define MICROPY_UNIX_MACHINE_IDLE sched_yield(); +#define MICROPY_PYEXEC_SYSTEM_EXIT_DEFAULT (PYEXEC_FORCED_EXIT) + #endif // MICROPY_UNIX_MINIMAL diff --git a/ports/unix/mphalport.h b/ports/unix/mphalport.h index 95ad63221ef95..2dec22e94be11 100644 --- a/ports/unix/mphalport.h +++ b/ports/unix/mphalport.h @@ -32,10 +32,13 @@ void mp_hal_set_interrupt_char(char c); -#define mp_hal_stdio_poll unused // this is not implemented, nor needed +#define mp_hal_stdio_mode_raw mp_hal_stdio_mode_raw +#define mp_hal_stdio_mode_orig mp_hal_stdio_mode_orig void mp_hal_stdio_mode_raw(void); void mp_hal_stdio_mode_orig(void); +#define mp_hal_stdio_poll unused // this is not implemented, nor needed + #if MICROPY_PY_BUILTINS_INPUT && MICROPY_USE_READLINE == 0 #include