LCOV - code coverage report
Current view: top level - ports/unix - main.c (source / functions) Hit Total Coverage
Test: unix_coverage_v1.24.0-7-g548babf8a.info Lines: 225 306 73.5 %
Date: 2024-10-30 09:06:48 Functions: 11 14 78.6 %
Branches: 102 156 65.4 %

           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-2017 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 <stdint.h>
      29                 :            : #include <stdbool.h>
      30                 :            : #include <stdio.h>
      31                 :            : #include <string.h>
      32                 :            : #include <stdlib.h>
      33                 :            : #include <stdarg.h>
      34                 :            : #include <unistd.h>
      35                 :            : #include <ctype.h>
      36                 :            : #include <sys/stat.h>
      37                 :            : #include <sys/types.h>
      38                 :            : #include <errno.h>
      39                 :            : #include <signal.h>
      40                 :            : 
      41                 :            : #include "py/compile.h"
      42                 :            : #include "py/runtime.h"
      43                 :            : #include "py/builtin.h"
      44                 :            : #include "py/repl.h"
      45                 :            : #include "py/gc.h"
      46                 :            : #include "py/objstr.h"
      47                 :            : #include "py/cstack.h"
      48                 :            : #include "py/mphal.h"
      49                 :            : #include "py/mpthread.h"
      50                 :            : #include "extmod/misc.h"
      51                 :            : #include "extmod/modplatform.h"
      52                 :            : #include "extmod/vfs.h"
      53                 :            : #include "extmod/vfs_posix.h"
      54                 :            : #include "genhdr/mpversion.h"
      55                 :            : #include "input.h"
      56                 :            : 
      57                 :            : // Command line options, with their defaults
      58                 :            : static bool compile_only = false;
      59                 :            : static uint emit_opt = MP_EMIT_OPT_NONE;
      60                 :            : 
      61                 :            : #if MICROPY_ENABLE_GC
      62                 :            : // Heap size of GC heap (if enabled)
      63                 :            : // Make it larger on a 64 bit machine, because pointers are larger.
      64                 :            : long heap_size = 1024 * 1024 * (sizeof(mp_uint_t) / 4);
      65                 :            : #endif
      66                 :            : 
      67                 :            : // Number of heaps to assign by default if MICROPY_GC_SPLIT_HEAP=1
      68                 :            : #ifndef MICROPY_GC_SPLIT_HEAP_N_HEAPS
      69                 :            : #define MICROPY_GC_SPLIT_HEAP_N_HEAPS (1)
      70                 :            : #endif
      71                 :            : 
      72                 :            : #if !MICROPY_PY_SYS_PATH
      73                 :            : #error "The unix port requires MICROPY_PY_SYS_PATH=1"
      74                 :            : #endif
      75                 :            : 
      76                 :            : #if !MICROPY_PY_SYS_ARGV
      77                 :            : #error "The unix port requires MICROPY_PY_SYS_ARGV=1"
      78                 :            : #endif
      79                 :            : 
      80                 :        126 : static void stderr_print_strn(void *env, const char *str, size_t len) {
      81                 :        126 :     (void)env;
      82                 :        126 :     ssize_t ret;
      83   [ -  +  -  - ]:        126 :     MP_HAL_RETRY_SYSCALL(ret, write(STDERR_FILENO, str, len), {});
      84                 :        126 :     mp_os_dupterm_tx_strn(str, len);
      85                 :        126 : }
      86                 :            : 
      87                 :            : const mp_print_t mp_stderr_print = {NULL, stderr_print_strn};
      88                 :            : 
      89                 :            : #define FORCED_EXIT (0x100)
      90                 :            : // If exc is SystemExit, return value where FORCED_EXIT bit set,
      91                 :            : // and lower 8 bits are SystemExit value. For all other exceptions,
      92                 :            : // return 1.
      93                 :         39 : static int handle_uncaught_exception(mp_obj_base_t *exc) {
      94                 :            :     // check for SystemExit
      95         [ +  + ]:         39 :     if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(exc->type), MP_OBJ_FROM_PTR(&mp_type_SystemExit))) {
      96                 :            :         // None is an exit value of 0; an int is its value; anything else is 1
      97                 :         34 :         mp_obj_t exit_val = mp_obj_exception_get_value(MP_OBJ_FROM_PTR(exc));
      98                 :         34 :         mp_int_t val = 0;
      99   [ -  +  -  - ]:         34 :         if (exit_val != mp_const_none && !mp_obj_get_int_maybe(exit_val, &val)) {
     100                 :          0 :             val = 1;
     101                 :            :         }
     102                 :         34 :         return FORCED_EXIT | (val & 255);
     103                 :            :     }
     104                 :            : 
     105                 :            :     // Report all other exceptions
     106                 :          5 :     mp_obj_print_exception(&mp_stderr_print, MP_OBJ_FROM_PTR(exc));
     107                 :          5 :     return 1;
     108                 :            : }
     109                 :            : 
     110                 :            : #define LEX_SRC_STR (1)
     111                 :            : #define LEX_SRC_VSTR (2)
     112                 :            : #define LEX_SRC_FILENAME (3)
     113                 :            : #define LEX_SRC_STDIN (4)
     114                 :            : 
     115                 :            : // Returns standard error codes: 0 for success, 1 for all other errors,
     116                 :            : // except if FORCED_EXIT bit is set then script raised SystemExit and the
     117                 :            : // value of the exit is in the lower 8 bits of the return value
     118                 :       2246 : static int execute_from_lexer(int source_kind, const void *source, mp_parse_input_kind_t input_kind, bool is_repl) {
     119                 :       2246 :     mp_hal_set_interrupt_char(CHAR_CTRL_C);
     120                 :            : 
     121                 :       2246 :     nlr_buf_t nlr;
     122         [ +  + ]:       2246 :     if (nlr_push(&nlr) == 0) {
     123                 :            :         // create lexer based on source kind
     124                 :       2246 :         mp_lexer_t *lex;
     125         [ +  + ]:       2246 :         if (source_kind == LEX_SRC_STR) {
     126                 :          2 :             const char *line = source;
     127                 :          2 :             lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, line, strlen(line), false);
     128         [ +  + ]:       2244 :         } else if (source_kind == LEX_SRC_VSTR) {
     129                 :        211 :             const vstr_t *vstr = source;
     130                 :        211 :             lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, vstr->buf, vstr->len, false);
     131         [ +  + ]:       2033 :         } else if (source_kind == LEX_SRC_FILENAME) {
     132                 :       2032 :             const char *filename = (const char *)source;
     133                 :       2032 :             lex = mp_lexer_new_from_file(qstr_from_str(filename));
     134                 :            :         } else { // LEX_SRC_STDIN
     135                 :          1 :             lex = mp_lexer_new_from_fd(MP_QSTR__lt_stdin_gt_, 0, false);
     136                 :            :         }
     137                 :            : 
     138                 :       2246 :         qstr source_name = lex->source_name;
     139                 :            : 
     140                 :            :         #if MICROPY_PY___FILE__
     141         [ +  + ]:       2246 :         if (input_kind == MP_PARSE_FILE_INPUT) {
     142                 :       2035 :             mp_store_global(MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name));
     143                 :            :         }
     144                 :            :         #endif
     145                 :            : 
     146                 :       2246 :         mp_parse_tree_t parse_tree = mp_parse(lex, input_kind);
     147                 :            : 
     148                 :            :         #if defined(MICROPY_UNIX_COVERAGE)
     149                 :            :         // allow to print the parse tree in the coverage build
     150         [ +  + ]:       2246 :         if (mp_verbose_flag >= 3) {
     151                 :          2 :             printf("----------------\n");
     152                 :          2 :             mp_parse_node_print(&mp_plat_print, parse_tree.root, 0);
     153                 :          2 :             printf("----------------\n");
     154                 :            :         }
     155                 :            :         #endif
     156                 :            : 
     157                 :       2246 :         mp_obj_t module_fun = mp_compile(&parse_tree, source_name, is_repl);
     158                 :            : 
     159         [ +  - ]:       2241 :         if (!compile_only) {
     160                 :            :             // execute it
     161                 :       2241 :             mp_call_function_0(module_fun);
     162                 :            :         }
     163                 :            : 
     164                 :       2211 :         mp_hal_set_interrupt_char(-1);
     165                 :       2211 :         mp_handle_pending(true);
     166                 :       2211 :         nlr_pop();
     167                 :       2211 :         return 0;
     168                 :            : 
     169                 :            :     } else {
     170                 :            :         // uncaught exception
     171                 :         35 :         mp_hal_set_interrupt_char(-1);
     172                 :         35 :         mp_handle_pending(false);
     173                 :         35 :         return handle_uncaught_exception(nlr.ret_val);
     174                 :            :     }
     175                 :            : }
     176                 :            : 
     177                 :            : #if MICROPY_USE_READLINE == 1
     178                 :            : #include "shared/readline/readline.h"
     179                 :            : #else
     180                 :            : static char *strjoin(const char *s1, int sep_char, const char *s2) {
     181                 :            :     int l1 = strlen(s1);
     182                 :            :     int l2 = strlen(s2);
     183                 :            :     char *s = malloc(l1 + l2 + 2);
     184                 :            :     memcpy(s, s1, l1);
     185                 :            :     if (sep_char != 0) {
     186                 :            :         s[l1] = sep_char;
     187                 :            :         l1 += 1;
     188                 :            :     }
     189                 :            :     memcpy(s + l1, s2, l2);
     190                 :            :     s[l1 + l2] = 0;
     191                 :            :     return s;
     192                 :            : }
     193                 :            : #endif
     194                 :            : 
     195                 :         28 : static int do_repl(void) {
     196                 :         28 :     mp_hal_stdout_tx_str(MICROPY_BANNER_NAME_AND_VERSION);
     197                 :         28 :     mp_hal_stdout_tx_str("; " MICROPY_BANNER_MACHINE);
     198                 :         28 :     mp_hal_stdout_tx_str("\nUse Ctrl-D to exit, Ctrl-E for paste mode\n");
     199                 :            : 
     200                 :            :     #if MICROPY_USE_READLINE == 1
     201                 :            : 
     202                 :            :     // use MicroPython supplied readline
     203                 :            : 
     204                 :         28 :     vstr_t line;
     205                 :         28 :     vstr_init(&line, 16);
     206                 :        239 :     for (;;) {
     207                 :        239 :         mp_hal_stdio_mode_raw();
     208                 :            : 
     209                 :         10 :     input_restart:
     210                 :        249 :         vstr_reset(&line);
     211                 :        249 :         int ret = readline(&line, mp_repl_get_ps1());
     212                 :        249 :         mp_parse_input_kind_t parse_input_kind = MP_PARSE_SINGLE_INPUT;
     213                 :            : 
     214         [ -  + ]:        249 :         if (ret == CHAR_CTRL_C) {
     215                 :            :             // cancel input
     216                 :          0 :             mp_hal_stdout_tx_str("\r\n");
     217                 :          0 :             goto input_restart;
     218         [ +  + ]:        249 :         } else if (ret == CHAR_CTRL_D) {
     219                 :            :             // EOF
     220                 :         28 :             printf("\n");
     221                 :         28 :             mp_hal_stdio_mode_orig();
     222                 :         28 :             vstr_clear(&line);
     223                 :         28 :             return 0;
     224         [ -  + ]:        221 :         } else if (ret == CHAR_CTRL_E) {
     225                 :            :             // paste mode
     226                 :          0 :             mp_hal_stdout_tx_str("\npaste mode; Ctrl-C to cancel, Ctrl-D to finish\n=== ");
     227                 :          0 :             vstr_reset(&line);
     228                 :          0 :             for (;;) {
     229                 :          0 :                 char c = mp_hal_stdin_rx_chr();
     230         [ #  # ]:          0 :                 if (c == CHAR_CTRL_C) {
     231                 :            :                     // cancel everything
     232                 :          0 :                     mp_hal_stdout_tx_str("\n");
     233                 :          0 :                     goto input_restart;
     234         [ #  # ]:          0 :                 } else if (c == CHAR_CTRL_D) {
     235                 :            :                     // end of input
     236                 :          0 :                     mp_hal_stdout_tx_str("\n");
     237                 :          0 :                     break;
     238                 :            :                 } else {
     239                 :            :                     // add char to buffer and echo
     240                 :          0 :                     vstr_add_byte(&line, c);
     241         [ #  # ]:          0 :                     if (c == '\r') {
     242                 :          0 :                         mp_hal_stdout_tx_str("\n=== ");
     243                 :            :                     } else {
     244                 :          0 :                         mp_hal_stdout_tx_strn(&c, 1);
     245                 :            :                     }
     246                 :            :                 }
     247                 :            :             }
     248                 :          0 :             parse_input_kind = MP_PARSE_FILE_INPUT;
     249         [ +  + ]:        221 :         } else if (line.len == 0) {
     250         [ -  + ]:         10 :             if (ret != 0) {
     251                 :          0 :                 printf("\n");
     252                 :            :             }
     253                 :         10 :             goto input_restart;
     254                 :            :         } else {
     255                 :            :             // got a line with non-zero length, see if it needs continuing
     256         [ +  + ]:        253 :             while (mp_repl_continue_with_input(vstr_null_terminated_str(&line))) {
     257                 :         42 :                 vstr_add_byte(&line, '\n');
     258                 :         42 :                 ret = readline(&line, mp_repl_get_ps2());
     259         [ -  + ]:         42 :                 if (ret == CHAR_CTRL_C) {
     260                 :            :                     // cancel everything
     261                 :          0 :                     printf("\n");
     262                 :          0 :                     goto input_restart;
     263         [ +  - ]:         42 :                 } else if (ret == CHAR_CTRL_D) {
     264                 :            :                     // stop entering compound statement
     265                 :            :                     break;
     266                 :            :                 }
     267                 :            :             }
     268                 :            :         }
     269                 :            : 
     270                 :        211 :         mp_hal_stdio_mode_orig();
     271                 :            : 
     272                 :        211 :         ret = execute_from_lexer(LEX_SRC_VSTR, &line, parse_input_kind, true);
     273         [ +  - ]:        211 :         if (ret & FORCED_EXIT) {
     274                 :            :             return ret;
     275                 :            :         }
     276                 :            :     }
     277                 :            : 
     278                 :            :     #else
     279                 :            : 
     280                 :            :     // use simple readline
     281                 :            : 
     282                 :            :     for (;;) {
     283                 :            :         char *line = prompt((char *)mp_repl_get_ps1());
     284                 :            :         if (line == NULL) {
     285                 :            :             // EOF
     286                 :            :             return 0;
     287                 :            :         }
     288                 :            :         while (mp_repl_continue_with_input(line)) {
     289                 :            :             char *line2 = prompt((char *)mp_repl_get_ps2());
     290                 :            :             if (line2 == NULL) {
     291                 :            :                 break;
     292                 :            :             }
     293                 :            :             char *line3 = strjoin(line, '\n', line2);
     294                 :            :             free(line);
     295                 :            :             free(line2);
     296                 :            :             line = line3;
     297                 :            :         }
     298                 :            : 
     299                 :            :         int ret = execute_from_lexer(LEX_SRC_STR, line, MP_PARSE_SINGLE_INPUT, true);
     300                 :            :         free(line);
     301                 :            :         if (ret & FORCED_EXIT) {
     302                 :            :             return ret;
     303                 :            :         }
     304                 :            :     }
     305                 :            : 
     306                 :            :     #endif
     307                 :            : }
     308                 :            : 
     309                 :       2032 : static int do_file(const char *file) {
     310                 :       2032 :     return execute_from_lexer(LEX_SRC_FILENAME, file, MP_PARSE_FILE_INPUT, false);
     311                 :            : }
     312                 :            : 
     313                 :          2 : static int do_str(const char *str) {
     314                 :          2 :     return execute_from_lexer(LEX_SRC_STR, str, MP_PARSE_FILE_INPUT, false);
     315                 :            : }
     316                 :            : 
     317                 :          0 : static void print_help(char **argv) {
     318                 :          0 :     printf(
     319                 :            :         "usage: %s [<opts>] [-X <implopt>] [-c <command> | -m <module> | <filename>]\n"
     320                 :            :         "Options:\n"
     321                 :            :         "--version : show version information\n"
     322                 :            :         "-h : print this help message\n"
     323                 :            :         "-i : enable inspection via REPL after running command/module/file\n"
     324                 :            :         #if MICROPY_DEBUG_PRINTERS
     325                 :            :         "-v : verbose (trace various operations); can be multiple\n"
     326                 :            :         #endif
     327                 :            :         "-O[N] : apply bytecode optimizations of level N\n"
     328                 :            :         "\n"
     329                 :            :         "Implementation specific options (-X):\n", argv[0]
     330                 :            :         );
     331                 :          0 :     int impl_opts_cnt = 0;
     332                 :          0 :     printf(
     333                 :            :         "  compile-only                 -- parse and compile only\n"
     334                 :            :         #if MICROPY_EMIT_NATIVE
     335                 :            :         "  emit={bytecode,native,viper} -- set the default code emitter\n"
     336                 :            :         #else
     337                 :            :         "  emit=bytecode                -- set the default code emitter\n"
     338                 :            :         #endif
     339                 :            :         );
     340                 :          0 :     impl_opts_cnt++;
     341                 :            :     #if MICROPY_ENABLE_GC
     342                 :          0 :     printf(
     343                 :            :         "  heapsize=<n>[w][K|M] -- set the heap size for the GC (default %ld)\n"
     344                 :            :         , heap_size);
     345                 :          0 :     impl_opts_cnt++;
     346                 :            :     #endif
     347                 :            :     #if defined(__APPLE__)
     348                 :            :     printf("  realtime -- set thread priority to realtime\n");
     349                 :            :     impl_opts_cnt++;
     350                 :            :     #endif
     351                 :            : 
     352                 :          0 :     if (impl_opts_cnt == 0) {
     353                 :            :         printf("  (none)\n");
     354                 :            :     }
     355                 :          0 : }
     356                 :            : 
     357                 :          0 : static int invalid_args(void) {
     358                 :          0 :     fprintf(stderr, "Invalid command line arguments. Use -h option for help.\n");
     359                 :          0 :     return 1;
     360                 :            : }
     361                 :            : 
     362                 :            : // Process options which set interpreter init options
     363                 :       3420 : static void pre_process_options(int argc, char **argv) {
     364         [ +  + ]:       6732 :     for (int a = 1; a < argc; a++) {
     365         [ +  + ]:       6707 :         if (argv[a][0] == '-') {
     366   [ +  +  +  + ]:       4675 :             if (strcmp(argv[a], "-c") == 0 || strcmp(argv[a], "-m") == 0) {
     367                 :            :                 break; // Everything after this is a command/module and arguments for it
     368                 :            :             }
     369         [ -  + ]:       3312 :             if (strcmp(argv[a], "-h") == 0) {
     370                 :          0 :                 print_help(argv);
     371                 :          0 :                 exit(0);
     372                 :            :             }
     373         [ -  + ]:       3312 :             if (strcmp(argv[a], "--version") == 0) {
     374                 :          0 :                 printf(MICROPY_BANNER_NAME_AND_VERSION "; " MICROPY_BANNER_MACHINE "\n");
     375                 :          0 :                 exit(0);
     376                 :            :             }
     377         [ +  + ]:       3312 :             if (strcmp(argv[a], "-X") == 0) {
     378         [ -  + ]:       3286 :                 if (a + 1 >= argc) {
     379                 :          0 :                     exit(invalid_args());
     380                 :            :                 }
     381                 :       3286 :                 if (0) {
     382         [ -  + ]:       3286 :                 } else if (strcmp(argv[a + 1], "compile-only") == 0) {
     383                 :          0 :                     compile_only = true;
     384         [ +  + ]:       3286 :                 } else if (strcmp(argv[a + 1], "emit=bytecode") == 0) {
     385                 :       1675 :                     emit_opt = MP_EMIT_OPT_BYTECODE;
     386                 :            :                 #if MICROPY_EMIT_NATIVE
     387         [ +  - ]:       1611 :                 } else if (strcmp(argv[a + 1], "emit=native") == 0) {
     388                 :       1611 :                     emit_opt = MP_EMIT_OPT_NATIVE_PYTHON;
     389         [ #  # ]:          0 :                 } else if (strcmp(argv[a + 1], "emit=viper") == 0) {
     390                 :          0 :                     emit_opt = MP_EMIT_OPT_VIPER;
     391                 :            :                 #endif
     392                 :            :                 #if MICROPY_ENABLE_GC
     393         [ #  # ]:          0 :                 } else if (strncmp(argv[a + 1], "heapsize=", sizeof("heapsize=") - 1) == 0) {
     394                 :          0 :                     char *end;
     395                 :          0 :                     heap_size = strtol(argv[a + 1] + sizeof("heapsize=") - 1, &end, 0);
     396                 :            :                     // Don't bring unneeded libc dependencies like tolower()
     397                 :            :                     // If there's 'w' immediately after number, adjust it for
     398                 :            :                     // target word size. Note that it should be *before* size
     399                 :            :                     // suffix like K or M, to avoid confusion with kilowords,
     400                 :            :                     // etc. the size is still in bytes, just can be adjusted
     401                 :            :                     // for word size (taking 32bit as baseline).
     402                 :          0 :                     bool word_adjust = false;
     403         [ #  # ]:          0 :                     if ((*end | 0x20) == 'w') {
     404                 :          0 :                         word_adjust = true;
     405                 :          0 :                         end++;
     406                 :            :                     }
     407         [ #  # ]:          0 :                     if ((*end | 0x20) == 'k') {
     408                 :          0 :                         heap_size *= 1024;
     409         [ #  # ]:          0 :                     } else if ((*end | 0x20) == 'm') {
     410                 :          0 :                         heap_size *= 1024 * 1024;
     411                 :            :                     } else {
     412                 :            :                         // Compensate for ++ below
     413                 :          0 :                         --end;
     414                 :            :                     }
     415         [ #  # ]:          0 :                     if (*++end != 0) {
     416                 :          0 :                         goto invalid_arg;
     417                 :            :                     }
     418         [ #  # ]:          0 :                     if (word_adjust) {
     419                 :          0 :                         heap_size = heap_size * MP_BYTES_PER_OBJ_WORD / 4;
     420                 :            :                     }
     421                 :            :                     // If requested size too small, we'll crash anyway
     422         [ #  # ]:          0 :                     if (heap_size < 700) {
     423                 :          0 :                         goto invalid_arg;
     424                 :            :                     }
     425                 :            :                 #endif
     426                 :            :                 #if defined(__APPLE__)
     427                 :            :                 } else if (strcmp(argv[a + 1], "realtime") == 0) {
     428                 :            :                     #if MICROPY_PY_THREAD
     429                 :            :                     mp_thread_is_realtime_enabled = true;
     430                 :            :                     #endif
     431                 :            :                     // main thread was already initialized before the option
     432                 :            :                     // was parsed, so we have to enable realtime here.
     433                 :            :                     mp_thread_set_realtime();
     434                 :            :                 #endif
     435                 :            :                 } else {
     436                 :          0 :                 invalid_arg:
     437                 :          0 :                     exit(invalid_args());
     438                 :            :                 }
     439                 :            :                 a++;
     440                 :            :             }
     441                 :            :         } else {
     442                 :            :             break; // Not an option but a file
     443                 :            :         }
     444                 :            :     }
     445                 :       3420 : }
     446                 :            : 
     447                 :       3397 : static void set_sys_argv(char *argv[], int argc, int start_arg) {
     448         [ +  + ]:       6792 :     for (int i = start_arg; i < argc; i++) {
     449                 :       3395 :         mp_obj_list_append(mp_sys_argv, MP_OBJ_NEW_QSTR(qstr_from_str(argv[i])));
     450                 :            :     }
     451                 :       3397 : }
     452                 :            : 
     453                 :            : #if MICROPY_PY_SYS_EXECUTABLE
     454                 :            : extern mp_obj_str_t mp_sys_executable_obj;
     455                 :            : static char executable_path[MICROPY_ALLOC_PATH_MAX];
     456                 :            : 
     457                 :       3420 : static void sys_set_excecutable(char *argv0) {
     458         [ +  - ]:       3420 :     if (realpath(argv0, executable_path)) {
     459                 :       3420 :         mp_obj_str_set_data(&mp_sys_executable_obj, (byte *)executable_path, strlen(executable_path));
     460                 :            :     }
     461                 :       3420 : }
     462                 :            : #endif
     463                 :            : 
     464                 :            : #ifdef _WIN32
     465                 :            : #define PATHLIST_SEP_CHAR ';'
     466                 :            : #else
     467                 :            : #define PATHLIST_SEP_CHAR ':'
     468                 :            : #endif
     469                 :            : 
     470                 :            : MP_NOINLINE int main_(int argc, char **argv);
     471                 :            : 
     472                 :       3420 : int main(int argc, char **argv) {
     473                 :            :     #if MICROPY_PY_THREAD
     474                 :       3420 :     mp_thread_init();
     475                 :            :     #endif
     476                 :            : 
     477                 :            :     // Define a reasonable stack limit to detect stack overflow.
     478                 :       3420 :     mp_uint_t stack_size = 40000 * (sizeof(void *) / 4);
     479                 :            :     #if defined(__arm__) && !defined(__thumb2__)
     480                 :            :     // ARM (non-Thumb) architectures require more stack.
     481                 :            :     stack_size *= 2;
     482                 :            :     #endif
     483                 :            : 
     484                 :            :     // We should capture stack top ASAP after start, and it should be
     485                 :            :     // captured guaranteedly before any other stack variables are allocated.
     486                 :            :     // For this, actual main (renamed main_) should not be inlined into
     487                 :            :     // this function. main_() itself may have other functions inlined (with
     488                 :            :     // their own stack variables), that's why we need this main/main_ split.
     489                 :       3420 :     mp_cstack_init_with_sp_here(stack_size);
     490                 :       3420 :     return main_(argc, argv);
     491                 :            : }
     492                 :            : 
     493                 :       3420 : MP_NOINLINE int main_(int argc, char **argv) {
     494                 :            :     #ifdef SIGPIPE
     495                 :            :     // Do not raise SIGPIPE, instead return EPIPE. Otherwise, e.g. writing
     496                 :            :     // to peer-closed socket will lead to sudden termination of MicroPython
     497                 :            :     // process. SIGPIPE is particularly nasty, because unix shell doesn't
     498                 :            :     // print anything for it, so the above looks like completely sudden and
     499                 :            :     // silent termination for unknown reason. Ignoring SIGPIPE is also what
     500                 :            :     // CPython does. Note that this may lead to problems using MicroPython
     501                 :            :     // scripts as pipe filters, but again, that's what CPython does. So,
     502                 :            :     // scripts which want to follow unix shell pipe semantics (where SIGPIPE
     503                 :            :     // means "pipe was requested to terminate, it's not an error"), should
     504                 :            :     // catch EPIPE themselves.
     505                 :       3420 :     signal(SIGPIPE, SIG_IGN);
     506                 :            :     #endif
     507                 :            : 
     508                 :       3420 :     pre_process_options(argc, argv);
     509                 :            : 
     510                 :            :     #if MICROPY_ENABLE_GC
     511                 :            :     #if !MICROPY_GC_SPLIT_HEAP
     512                 :            :     char *heap = malloc(heap_size);
     513                 :            :     gc_init(heap, heap + heap_size);
     514                 :            :     #else
     515                 :       3420 :     assert(MICROPY_GC_SPLIT_HEAP_N_HEAPS > 0);
     516                 :       3420 :     char *heaps[MICROPY_GC_SPLIT_HEAP_N_HEAPS];
     517                 :       3420 :     long multi_heap_size = heap_size / MICROPY_GC_SPLIT_HEAP_N_HEAPS;
     518         [ +  + ]:      17100 :     for (size_t i = 0; i < MICROPY_GC_SPLIT_HEAP_N_HEAPS; i++) {
     519                 :      13680 :         heaps[i] = malloc(multi_heap_size);
     520         [ +  + ]:      13680 :         if (i == 0) {
     521                 :       3420 :             gc_init(heaps[i], heaps[i] + multi_heap_size);
     522                 :            :         } else {
     523                 :      10260 :             gc_add(heaps[i], heaps[i] + multi_heap_size);
     524                 :            :         }
     525                 :            :     }
     526                 :            :     #endif
     527                 :            :     #endif
     528                 :            : 
     529                 :            :     #if MICROPY_ENABLE_PYSTACK
     530                 :            :     static mp_obj_t pystack[1024];
     531                 :            :     mp_pystack_init(pystack, &pystack[MP_ARRAY_SIZE(pystack)]);
     532                 :            :     #endif
     533                 :            : 
     534                 :       3420 :     mp_init();
     535                 :            : 
     536                 :            :     #if MICROPY_EMIT_NATIVE
     537                 :            :     // Set default emitter options
     538                 :       3420 :     MP_STATE_VM(default_emit_opt) = emit_opt;
     539                 :            :     #else
     540                 :            :     (void)emit_opt;
     541                 :            :     #endif
     542                 :            : 
     543                 :            :     #if MICROPY_VFS_POSIX
     544                 :            :     {
     545                 :            :         // Mount the host FS at the root of our internal VFS
     546                 :       6840 :         mp_obj_t args[2] = {
     547                 :       3420 :             MP_OBJ_TYPE_GET_SLOT(&mp_type_vfs_posix, make_new)(&mp_type_vfs_posix, 0, 0, NULL),
     548                 :            :             MP_OBJ_NEW_QSTR(MP_QSTR__slash_),
     549                 :            :         };
     550                 :       3420 :         mp_vfs_mount(2, args, (mp_map_t *)&mp_const_empty_map);
     551                 :       3420 :         MP_STATE_VM(vfs_cur) = MP_STATE_VM(vfs_mount_table);
     552                 :            :     }
     553                 :            :     #endif
     554                 :            : 
     555                 :            :     {
     556                 :            :         // sys.path starts as [""]
     557                 :       3420 :         mp_sys_path = mp_obj_new_list(0, NULL);
     558                 :       3420 :         mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR_));
     559                 :            : 
     560                 :            :         // Add colon-separated entries from MICROPYPATH.
     561                 :       3420 :         char *home = getenv("HOME");
     562                 :       3420 :         char *path = getenv("MICROPYPATH");
     563         [ +  + ]:       3420 :         if (path == NULL) {
     564                 :          6 :             path = MICROPY_PY_SYS_PATH_DEFAULT;
     565                 :            :         }
     566         [ -  + ]:       3420 :         if (*path == PATHLIST_SEP_CHAR) {
     567                 :            :             // First entry is empty. We've already added an empty entry to sys.path, so skip it.
     568                 :          0 :             ++path;
     569                 :            :         }
     570                 :            :         // GCC targeting RISC-V 64 reports a warning about `path_remaining` being clobbered by
     571                 :            :         // either setjmp or vfork if that variable it is allocated on the stack.  This may
     572                 :            :         // probably be a compiler error as it occurs on a few recent GCC releases (up to 14.1.0)
     573                 :            :         // but LLVM doesn't report any warnings.
     574                 :       3420 :         static bool path_remaining;
     575                 :       3420 :         path_remaining = *path;
     576         [ +  + ]:      10266 :         while (path_remaining) {
     577                 :       6846 :             char *path_entry_end = strchr(path, PATHLIST_SEP_CHAR);
     578         [ +  + ]:       6846 :             if (path_entry_end == NULL) {
     579                 :       3420 :                 path_entry_end = path + strlen(path);
     580                 :       3420 :                 path_remaining = false;
     581                 :            :             }
     582   [ +  +  +  -  :       6852 :             if (path[0] == '~' && path[1] == '/' && home != NULL) {
                   +  - ]
     583                 :            :                 // Expand standalone ~ to $HOME
     584                 :          6 :                 int home_l = strlen(home);
     585                 :          6 :                 vstr_t vstr;
     586                 :          6 :                 vstr_init(&vstr, home_l + (path_entry_end - path - 1) + 1);
     587                 :          6 :                 vstr_add_strn(&vstr, home, home_l);
     588                 :          6 :                 vstr_add_strn(&vstr, path + 1, path_entry_end - path - 1);
     589                 :          6 :                 mp_obj_list_append(mp_sys_path, mp_obj_new_str_from_vstr(&vstr));
     590                 :            :             } else {
     591                 :       6840 :                 mp_obj_list_append(mp_sys_path, mp_obj_new_str_via_qstr(path, path_entry_end - path));
     592                 :            :             }
     593                 :       6846 :             path = path_entry_end + 1;
     594                 :            :         }
     595                 :            :     }
     596                 :            : 
     597                 :       3420 :     mp_obj_list_init(MP_OBJ_TO_PTR(mp_sys_argv), 0);
     598                 :            : 
     599                 :            :     #if defined(MICROPY_UNIX_COVERAGE)
     600                 :            :     {
     601                 :       3420 :         MP_DECLARE_CONST_FUN_OBJ_0(extra_coverage_obj);
     602                 :       3420 :         MP_DECLARE_CONST_FUN_OBJ_0(extra_cpp_coverage_obj);
     603                 :       3420 :         mp_store_global(MP_QSTR_extra_coverage, MP_OBJ_FROM_PTR(&extra_coverage_obj));
     604                 :       3420 :         mp_store_global(MP_QSTR_extra_cpp_coverage, MP_OBJ_FROM_PTR(&extra_cpp_coverage_obj));
     605                 :            :     }
     606                 :            :     #endif
     607                 :            : 
     608                 :            :     // Here is some example code to create a class and instance of that class.
     609                 :            :     // First is the Python, then the C code.
     610                 :            :     //
     611                 :            :     // class TestClass:
     612                 :            :     //     pass
     613                 :            :     // test_obj = TestClass()
     614                 :            :     // test_obj.attr = 42
     615                 :            :     //
     616                 :            :     // mp_obj_t test_class_type, test_class_instance;
     617                 :            :     // test_class_type = mp_obj_new_type(qstr_from_str("TestClass"), mp_const_empty_tuple, mp_obj_new_dict(0));
     618                 :            :     // mp_store_name(qstr_from_str("test_obj"), test_class_instance = mp_call_function_0(test_class_type));
     619                 :            :     // mp_store_attr(test_class_instance, qstr_from_str("attr"), mp_obj_new_int(42));
     620                 :            : 
     621                 :            :     /*
     622                 :            :     printf("bytes:\n");
     623                 :            :     printf("    total %d\n", m_get_total_bytes_allocated());
     624                 :            :     printf("    cur   %d\n", m_get_current_bytes_allocated());
     625                 :            :     printf("    peak  %d\n", m_get_peak_bytes_allocated());
     626                 :            :     */
     627                 :            : 
     628                 :            :     #if MICROPY_PY_SYS_EXECUTABLE
     629                 :       3420 :     sys_set_excecutable(argv[0]);
     630                 :            :     #endif
     631                 :            : 
     632                 :       3420 :     const int NOTHING_EXECUTED = -2;
     633                 :       3420 :     int ret = NOTHING_EXECUTED;
     634                 :       3420 :     bool inspect = false;
     635         [ +  + ]:       6732 :     for (int a = 1; a < argc; a++) {
     636         [ +  + ]:       6707 :         if (argv[a][0] == '-') {
     637         [ +  + ]:       4675 :             if (strcmp(argv[a], "-i") == 0) {
     638                 :            :                 inspect = true;
     639         [ +  + ]:       4673 :             } else if (strcmp(argv[a], "-c") == 0) {
     640         [ -  + ]:          2 :                 if (a + 1 >= argc) {
     641                 :          0 :                     return invalid_args();
     642                 :            :                 }
     643                 :          2 :                 set_sys_argv(argv, a + 1, a); // The -c becomes first item of sys.argv, as in CPython
     644                 :          2 :                 set_sys_argv(argv, argc, a + 2); // Then what comes after the command
     645                 :          2 :                 ret = do_str(argv[a + 1]);
     646                 :          2 :                 break;
     647         [ +  + ]:       4671 :             } else if (strcmp(argv[a], "-m") == 0) {
     648         [ -  + ]:       1361 :                 if (a + 1 >= argc) {
     649                 :          4 :                     return invalid_args();
     650                 :            :                 }
     651                 :       1361 :                 mp_obj_t import_args[4];
     652                 :       1361 :                 import_args[0] = mp_obj_new_str_from_cstr(argv[a + 1]);
     653                 :       1361 :                 import_args[1] = import_args[2] = mp_const_none;
     654                 :            :                 // Ask __import__ to handle imported module specially - set its __name__
     655                 :            :                 // to __main__, and also return this leaf module, not top-level package
     656                 :            :                 // containing it.
     657                 :       1361 :                 import_args[3] = mp_const_false;
     658                 :            :                 // TODO: https://docs.python.org/3/using/cmdline.html#cmdoption-m :
     659                 :            :                 // "the first element of sys.argv will be the full path to
     660                 :            :                 // the module file (while the module file is being located,
     661                 :            :                 // the first element will be set to "-m")."
     662                 :       1361 :                 set_sys_argv(argv, argc, a + 1);
     663                 :            : 
     664                 :       1361 :                 mp_obj_t mod;
     665                 :       1361 :                 nlr_buf_t nlr;
     666                 :            : 
     667                 :            :                 // Allocating subpkg_tried on the stack can lead to compiler warnings about this
     668                 :            :                 // variable being clobbered when nlr is implemented using setjmp/longjmp.  Its
     669                 :            :                 // value must be preserved across calls to setjmp/longjmp.
     670                 :       1361 :                 static bool subpkg_tried;
     671                 :       1361 :                 subpkg_tried = false;
     672                 :            : 
     673                 :       1361 :             reimport:
     674         [ +  + ]:       1361 :                 if (nlr_push(&nlr) == 0) {
     675                 :       1361 :                     mod = mp_builtin___import__(MP_ARRAY_SIZE(import_args), import_args);
     676                 :       1357 :                     nlr_pop();
     677                 :            :                 } else {
     678                 :            :                     // uncaught exception
     679                 :          4 :                     return handle_uncaught_exception(nlr.ret_val) & 0xff;
     680                 :            :                 }
     681                 :            : 
     682                 :            :                 // If this module is a package, see if it has a `__main__.py`.
     683                 :       1357 :                 mp_obj_t dest[2];
     684                 :       1357 :                 mp_load_method_protected(mod, MP_QSTR___path__, dest, true);
     685   [ -  +  -  - ]:       1357 :                 if (dest[0] != MP_OBJ_NULL && !subpkg_tried) {
     686                 :          0 :                     subpkg_tried = true;
     687                 :          0 :                     vstr_t vstr;
     688                 :          0 :                     int len = strlen(argv[a + 1]);
     689                 :          0 :                     vstr_init(&vstr, len + sizeof(".__main__"));
     690                 :          0 :                     vstr_add_strn(&vstr, argv[a + 1], len);
     691                 :          0 :                     vstr_add_strn(&vstr, ".__main__", sizeof(".__main__") - 1);
     692                 :          0 :                     import_args[0] = mp_obj_new_str_from_vstr(&vstr);
     693                 :          0 :                     goto reimport;
     694                 :            :                 }
     695                 :            : 
     696                 :       1357 :                 ret = 0;
     697                 :       1357 :                 break;
     698         [ +  + ]:       3310 :             } else if (strcmp(argv[a], "-X") == 0) {
     699                 :       3286 :                 a += 1;
     700                 :            :             #if MICROPY_DEBUG_PRINTERS
     701         [ +  + ]:         24 :             } else if (strcmp(argv[a], "-v") == 0) {
     702                 :         22 :                 mp_verbose_flag++;
     703                 :            :             #endif
     704         [ +  - ]:          2 :             } else if (strncmp(argv[a], "-O", 2) == 0) {
     705         [ -  + ]:          2 :                 if (unichar_isdigit(argv[a][2])) {
     706                 :          0 :                     MP_STATE_VM(mp_optimise_value) = argv[a][2] & 0xf;
     707                 :            :                 } else {
     708                 :          2 :                     MP_STATE_VM(mp_optimise_value) = 0;
     709         [ +  + ]:          4 :                     for (char *p = argv[a] + 1; *p && *p == 'O'; p++, MP_STATE_VM(mp_optimise_value)++) {;
     710                 :            :                     }
     711                 :            :                 }
     712                 :            :             } else {
     713                 :          0 :                 return invalid_args();
     714                 :            :             }
     715                 :            :         } else {
     716                 :       2032 :             char *pathbuf = malloc(PATH_MAX);
     717                 :       2032 :             char *basedir = realpath(argv[a], pathbuf);
     718         [ -  + ]:       2032 :             if (basedir == NULL) {
     719                 :          0 :                 mp_printf(&mp_stderr_print, "%s: can't open file '%s': [Errno %d] %s\n", argv[0], argv[a], errno, strerror(errno));
     720                 :          0 :                 free(pathbuf);
     721                 :            :                 // CPython exits with 2 in such case
     722                 :          0 :                 ret = 2;
     723                 :          0 :                 break;
     724                 :            :             }
     725                 :            : 
     726                 :            :             // Set base dir of the script as first entry in sys.path.
     727                 :       2032 :             char *p = strrchr(basedir, '/');
     728                 :       2032 :             mp_obj_list_store(mp_sys_path, MP_OBJ_NEW_SMALL_INT(0), mp_obj_new_str_via_qstr(basedir, p - basedir));
     729                 :       2032 :             free(pathbuf);
     730                 :            : 
     731                 :       2032 :             set_sys_argv(argv, argc, a);
     732                 :       2032 :             ret = do_file(argv[a]);
     733                 :       2032 :             break;
     734                 :            :         }
     735                 :            :     }
     736                 :            : 
     737                 :       3416 :     const char *inspect_env = getenv("MICROPYINSPECT");
     738   [ +  +  +  - ]:       3416 :     if (inspect_env && inspect_env[0] != '\0') {
     739                 :          2 :         inspect = true;
     740                 :            :     }
     741         [ +  + ]:       3416 :     if (ret == NOTHING_EXECUTED || inspect) {
     742   [ +  +  -  + ]:         29 :         if (isatty(0) || inspect) {
     743                 :         28 :             prompt_read_history();
     744                 :         28 :             ret = do_repl();
     745                 :         28 :             prompt_write_history();
     746                 :            :         } else {
     747                 :          1 :             ret = execute_from_lexer(LEX_SRC_STDIN, NULL, MP_PARSE_FILE_INPUT, false);
     748                 :            :         }
     749                 :            :     }
     750                 :            : 
     751                 :            :     #if MICROPY_PY_SYS_SETTRACE
     752                 :            :     MP_STATE_THREAD(prof_trace_callback) = MP_OBJ_NULL;
     753                 :            :     #endif
     754                 :            : 
     755                 :            :     #if MICROPY_PY_SYS_ATEXIT
     756                 :            :     // Beware, the sys.settrace callback should be disabled before running sys.atexit.
     757         [ +  + ]:       3416 :     if (mp_obj_is_callable(MP_STATE_VM(sys_exitfunc))) {
     758                 :          2 :         mp_call_function_0(MP_STATE_VM(sys_exitfunc));
     759                 :            :     }
     760                 :            :     #endif
     761                 :            : 
     762                 :            :     #if MICROPY_PY_MICROPYTHON_MEM_INFO
     763         [ +  + ]:       3416 :     if (mp_verbose_flag) {
     764                 :         10 :         mp_micropython_mem_info(0, NULL);
     765                 :            :     }
     766                 :            :     #endif
     767                 :            : 
     768                 :            :     #if MICROPY_PY_BLUETOOTH
     769                 :            :     void mp_bluetooth_deinit(void);
     770                 :            :     mp_bluetooth_deinit();
     771                 :            :     #endif
     772                 :            : 
     773                 :            :     #if MICROPY_PY_THREAD
     774                 :       3416 :     mp_thread_deinit();
     775                 :            :     #endif
     776                 :            : 
     777                 :            :     #if defined(MICROPY_UNIX_COVERAGE)
     778                 :       3416 :     gc_sweep_all();
     779                 :            :     #endif
     780                 :            : 
     781                 :       3416 :     mp_deinit();
     782                 :            : 
     783                 :            :     #if MICROPY_ENABLE_GC && !defined(NDEBUG)
     784                 :            :     // We don't really need to free memory since we are about to exit the
     785                 :            :     // process, but doing so helps to find memory leaks.
     786                 :            :     #if !MICROPY_GC_SPLIT_HEAP
     787                 :            :     free(heap);
     788                 :            :     #else
     789         [ +  + ]:      17080 :     for (size_t i = 0; i < MICROPY_GC_SPLIT_HEAP_N_HEAPS; i++) {
     790                 :      13664 :         free(heaps[i]);
     791                 :            :     }
     792                 :            :     #endif
     793                 :            :     #endif
     794                 :            : 
     795                 :            :     // printf("total bytes = %d\n", m_get_total_bytes_allocated());
     796                 :       3416 :     return ret & 0xff;
     797                 :            : }
     798                 :            : 
     799                 :          0 : void nlr_jump_fail(void *val) {
     800                 :            :     #if MICROPY_USE_READLINE == 1
     801                 :          0 :     mp_hal_stdio_mode_orig();
     802                 :            :     #endif
     803                 :          0 :     fprintf(stderr, "FATAL: uncaught NLR %p\n", val);
     804                 :          0 :     exit(1);
     805                 :            : }

Generated by: LCOV version 1.15-5-g462f71d