LCOV - code coverage report
Current view: top level - py - bc.c (source / functions) Hit Total Coverage
Test: unix_coverage_v1.24.0-7-g548babf8a.info Lines: 130 138 94.2 %
Date: 2024-10-30 09:06:48 Functions: 8 8 100.0 %
Branches: 66 74 89.2 %

           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) 2014 Damien P. George
       7                 :            :  * Copyright (c) 2014 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 <stdbool.h>
      29                 :            : #include <string.h>
      30                 :            : #include <assert.h>
      31                 :            : 
      32                 :            : #include "py/bc0.h"
      33                 :            : #include "py/bc.h"
      34                 :            : #include "py/objfun.h"
      35                 :            : 
      36                 :            : #if MICROPY_DEBUG_VERBOSE // print debugging info
      37                 :            : #define DEBUG_PRINT (1)
      38                 :            : #else // don't print debugging info
      39                 :            : #define DEBUG_PRINT (0)
      40                 :            : #define DEBUG_printf(...) (void)0
      41                 :            : #endif
      42                 :            : 
      43                 :     798916 : void mp_encode_uint(void *env, mp_encode_uint_allocator_t allocator, mp_uint_t val) {
      44                 :            :     // We store each 7 bits in a separate byte, and that's how many bytes needed
      45                 :     798916 :     byte buf[MP_ENCODE_UINT_MAX_BYTES];
      46                 :     798916 :     byte *p = buf + sizeof(buf);
      47                 :            :     // We encode in little-ending order, but store in big-endian, to help decoding
      48                 :     800854 :     do {
      49                 :     800854 :         *--p = val & 0x7f;
      50                 :     800854 :         val >>= 7;
      51         [ +  + ]:     800854 :     } while (val != 0);
      52                 :     798916 :     byte *c = allocator(env, buf + sizeof(buf) - p);
      53         [ +  + ]:     798918 :     if (c != NULL) {
      54         [ +  + ]:     786074 :         while (p != buf + sizeof(buf) - 1) {
      55                 :       1938 :             *c++ = *p++ | 0x80;
      56                 :            :         }
      57                 :     784136 :         *c = *p;
      58                 :            :     }
      59                 :     798918 : }
      60                 :            : 
      61                 :     161605 : mp_uint_t mp_decode_uint(const byte **ptr) {
      62                 :     161605 :     mp_uint_t unum = 0;
      63                 :     161605 :     byte val;
      64                 :     161605 :     const byte *p = *ptr;
      65                 :     161605 :     do {
      66                 :     161605 :         val = *p++;
      67                 :     161605 :         unum = (unum << 7) | (val & 0x7f);
      68         [ -  + ]:     161605 :     } while ((val & 0x80) != 0);
      69                 :     161605 :     *ptr = p;
      70                 :     161605 :     return unum;
      71                 :            : }
      72                 :            : 
      73                 :            : // This function is used to help reduce stack usage at the caller, for the case when
      74                 :            : // the caller doesn't need to increase the ptr argument.  If ptr is a local variable
      75                 :            : // and the caller uses mp_decode_uint(&ptr) instead of this function, then the compiler
      76                 :            : // must allocate a slot on the stack for ptr, and this slot cannot be reused for
      77                 :            : // anything else in the function because the pointer may have been stored in a global
      78                 :            : // and reused later in the function.
      79                 :     159478 : mp_uint_t mp_decode_uint_value(const byte *ptr) {
      80                 :     159478 :     return mp_decode_uint(&ptr);
      81                 :            : }
      82                 :            : 
      83                 :            : // This function is used to help reduce stack usage at the caller, for the case when
      84                 :            : // the caller doesn't need the actual value and just wants to skip over it.
      85                 :     311869 : const byte *mp_decode_uint_skip(const byte *ptr) {
      86                 :     311869 :     while ((*ptr++) & 0x80) {
      87         [ -  + ]:     311869 :     }
      88                 :     311869 :     return ptr;
      89                 :            : }
      90                 :            : 
      91                 :         12 : static NORETURN void fun_pos_args_mismatch(mp_obj_fun_bc_t *f, size_t expected, size_t given) {
      92                 :            :     #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE
      93                 :            :     // generic message, used also for other argument issues
      94                 :            :     (void)f;
      95                 :            :     (void)expected;
      96                 :            :     (void)given;
      97                 :            :     mp_arg_error_terse_mismatch();
      98                 :            :     #elif MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_NORMAL
      99                 :            :     (void)f;
     100                 :            :     mp_raise_msg_varg(&mp_type_TypeError,
     101                 :            :         MP_ERROR_TEXT("function takes %d positional arguments but %d were given"), expected, given);
     102                 :            :     #elif MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED
     103                 :         12 :     mp_raise_msg_varg(&mp_type_TypeError,
     104                 :         12 :         MP_ERROR_TEXT("%q() takes %d positional arguments but %d were given"),
     105                 :            :         mp_obj_fun_get_name(MP_OBJ_FROM_PTR(f)), expected, given);
     106                 :            :     #endif
     107                 :            : }
     108                 :            : 
     109                 :            : #if DEBUG_PRINT
     110                 :            : static void dump_args(const mp_obj_t *a, size_t sz) {
     111                 :            :     DEBUG_printf("%p: ", a);
     112                 :            :     for (size_t i = 0; i < sz; i++) {
     113                 :            :         DEBUG_printf("%p ", a[i]);
     114                 :            :     }
     115                 :            :     DEBUG_printf("\n");
     116                 :            : }
     117                 :            : #else
     118                 :            : #define dump_args(...) (void)0
     119                 :            : #endif
     120                 :            : 
     121                 :            : // On entry code_state should be allocated somewhere (stack/heap) and
     122                 :            : // contain the following valid entries:
     123                 :            : //    - code_state->fun_bc should contain a pointer to the function object
     124                 :            : //    - code_state->ip should contain a pointer to the beginning of the prelude
     125                 :            : //    - code_state->sp should be: &code_state->state[0] - 1
     126                 :            : //    - code_state->n_state should be the number of objects in the local state
     127                 :    9088264 : static void mp_setup_code_state_helper(mp_code_state_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args) {
     128                 :            :     // This function is pretty complicated.  It's main aim is to be efficient in speed and RAM
     129                 :            :     // usage for the common case of positional only args.
     130                 :            : 
     131                 :            :     // get the function object that we want to set up (could be bytecode or native code)
     132                 :    9088264 :     mp_obj_fun_bc_t *self = code_state->fun_bc;
     133                 :            : 
     134                 :            :     // Get cached n_state (rather than decode it again)
     135                 :    9088264 :     size_t n_state = code_state->n_state;
     136                 :            : 
     137                 :            :     // Decode prelude
     138                 :    9088264 :     size_t n_state_unused, n_exc_stack_unused, scope_flags, n_pos_args, n_kwonly_args, n_def_pos_args;
     139         [ +  + ]:    9647038 :     MP_BC_PRELUDE_SIG_DECODE_INTO(code_state->ip, n_state_unused, n_exc_stack_unused, scope_flags, n_pos_args, n_kwonly_args, n_def_pos_args);
     140         [ +  + ]:    9088712 :     MP_BC_PRELUDE_SIZE_DECODE(code_state->ip);
     141                 :    9088264 :     (void)n_state_unused;
     142                 :    9088264 :     (void)n_exc_stack_unused;
     143                 :            : 
     144                 :    9088264 :     mp_obj_t *code_state_state = code_state->sp + 1;
     145                 :    9088264 :     code_state->exc_sp_idx = 0;
     146                 :            : 
     147                 :            :     // zero out the local stack to begin with
     148                 :    9088264 :     memset(code_state_state, 0, n_state * sizeof(*code_state->state));
     149                 :            : 
     150                 :    9088264 :     const mp_obj_t *kwargs = args + n_args;
     151                 :            : 
     152                 :            :     // var_pos_kw_args points to the stack where the var-args tuple, and var-kw dict, should go (if they are needed)
     153                 :    9088264 :     mp_obj_t *var_pos_kw_args = &code_state_state[n_state - 1 - n_pos_args - n_kwonly_args];
     154                 :            : 
     155                 :            :     // check positional arguments
     156                 :            : 
     157         [ +  + ]:    9088264 :     if (n_args > n_pos_args) {
     158                 :            :         // given more than enough arguments
     159         [ +  + ]:       8130 :         if ((scope_flags & MP_SCOPE_FLAG_VARARGS) == 0) {
     160                 :          8 :             fun_pos_args_mismatch(self, n_pos_args, n_args);
     161                 :            :         }
     162                 :            :         // put extra arguments in varargs tuple
     163                 :       8122 :         *var_pos_kw_args-- = mp_obj_new_tuple(n_args - n_pos_args, args + n_pos_args);
     164                 :       8122 :         n_args = n_pos_args;
     165                 :            :     } else {
     166         [ +  + ]:    9080134 :         if ((scope_flags & MP_SCOPE_FLAG_VARARGS) != 0) {
     167                 :        152 :             DEBUG_printf("passing empty tuple as *args\n");
     168                 :        152 :             *var_pos_kw_args-- = mp_const_empty_tuple;
     169                 :            :         }
     170                 :            :         // Apply processing and check below only if we don't have kwargs,
     171                 :            :         // otherwise, kw handling code below has own extensive checks.
     172   [ +  #  +  # ]:    9080134 :         if (n_kw == 0 && (scope_flags & MP_SCOPE_FLAG_DEFKWARGS) == 0) {
     173         [ +  + ]:    9469189 :             if (n_args >= (size_t)(n_pos_args - n_def_pos_args)) {
     174                 :            :                 // given enough arguments, but may need to use some default arguments
     175         [ +  + ]:    9788549 :                 for (size_t i = n_args; i < n_pos_args; i++) {
     176                 :     319364 :                     code_state_state[n_state - 1 - i] = self->extra_args[i - (n_pos_args - n_def_pos_args)];
     177                 :            :                 }
     178                 :            :             } else {
     179                 :          4 :                 fun_pos_args_mismatch(self, n_pos_args - n_def_pos_args, n_args);
     180                 :            :             }
     181                 :            :         }
     182                 :            :     }
     183                 :            : 
     184                 :            :     // copy positional args into state
     185         [ +  + ]:   19590075 :     for (size_t i = 0; i < n_args; i++) {
     186                 :   10501823 :         code_state_state[n_state - 1 - i] = args[i];
     187                 :            :     }
     188                 :            : 
     189                 :            :     // check keyword arguments
     190                 :            : 
     191   [ +  +  #  + ]:    9088723 :     if (n_kw != 0 || (scope_flags & MP_SCOPE_FLAG_DEFKWARGS) != 0) {
     192                 :          0 :         DEBUG_printf("Initial args: ");
     193                 :          0 :         dump_args(code_state_state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args);
     194                 :            : 
     195                 :          0 :         mp_obj_t dict = MP_OBJ_NULL;
     196         [ +  # ]:          0 :         if ((scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) != 0) {
     197                 :        124 :             dict = mp_obj_new_dict(n_kw); // TODO: better go conservative with 0?
     198                 :        124 :             *var_pos_kw_args = dict;
     199                 :            :         }
     200                 :            : 
     201         [ #  + ]:          0 :         for (size_t i = 0; i < n_kw; i++) {
     202                 :            :             // the keys in kwargs are expected to be qstr objects
     203                 :          0 :             mp_obj_t wanted_arg_name = kwargs[2 * i];
     204                 :            : 
     205                 :            :             // get pointer to arg_names array
     206                 :          0 :             const uint8_t *arg_names = code_state->ip;
     207                 :          0 :             arg_names = mp_decode_uint_skip(arg_names);
     208                 :            : 
     209         [ +  + ]:       2309 :             for (size_t j = 0; j < n_pos_args + n_kwonly_args; j++) {
     210                 :       1729 :                 qstr arg_qstr = mp_decode_uint(&arg_names);
     211                 :            :                 #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE
     212                 :       1729 :                 arg_qstr = self->context->constants.qstr_table[arg_qstr];
     213                 :            :                 #endif
     214         [ +  + ]:       1729 :                 if (wanted_arg_name == MP_OBJ_NEW_QSTR(arg_qstr)) {
     215         [ +  + ]:        580 :                     if (code_state_state[n_state - 1 - j] != MP_OBJ_NULL) {
     216                 :         20 :                     error_multiple:
     217                 :         24 :                         mp_raise_msg_varg(&mp_type_TypeError,
     218                 :         24 :                             MP_ERROR_TEXT("function got multiple values for argument '%q'"), MP_OBJ_QSTR_VALUE(wanted_arg_name));
     219                 :            :                     }
     220                 :        560 :                     code_state_state[n_state - 1 - j] = kwargs[2 * i + 1];
     221                 :        560 :                     goto continue2;
     222                 :            :                 }
     223                 :            :             }
     224                 :            :             // Didn't find name match with positional args
     225         [ +  + ]:        580 :             if ((scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) == 0) {
     226                 :            :                 #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE
     227                 :            :                 mp_raise_TypeError(MP_ERROR_TEXT("unexpected keyword argument"));
     228                 :            :                 #else
     229                 :         12 :                 mp_raise_msg_varg(&mp_type_TypeError,
     230                 :         12 :                     MP_ERROR_TEXT("unexpected keyword argument '%q'"), MP_OBJ_QSTR_VALUE(wanted_arg_name));
     231                 :            :                 #endif
     232                 :            :             }
     233                 :        568 :             mp_map_elem_t *elem = mp_map_lookup(mp_obj_dict_get_map(dict), wanted_arg_name, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND);
     234         [ +  + ]:        568 :             if (elem->value == MP_OBJ_NULL) {
     235                 :        564 :                 elem->value = kwargs[2 * i + 1];
     236                 :            :             } else {
     237                 :          4 :                 goto error_multiple;
     238                 :            :             }
     239                 :       1124 :         continue2:;
     240                 :            :         }
     241                 :            : 
     242                 :        483 :         DEBUG_printf("Args with kws flattened: ");
     243                 :        483 :         dump_args(code_state_state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args);
     244                 :            : 
     245                 :            :         // fill in defaults for positional args
     246                 :        483 :         mp_obj_t *d = &code_state_state[n_state - n_pos_args];
     247                 :        483 :         mp_obj_t *s = &self->extra_args[n_def_pos_args - 1];
     248         [ +  + ]:        821 :         for (size_t i = n_def_pos_args; i > 0; i--, d++, s--) {
     249         [ +  + ]:        338 :             if (*d == MP_OBJ_NULL) {
     250                 :        164 :                 *d = *s;
     251                 :            :             }
     252                 :            :         }
     253                 :            : 
     254                 :            :         DEBUG_printf("Args after filling default positional: ");
     255                 :            :         dump_args(code_state_state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args);
     256                 :            : 
     257                 :            :         // Check that all mandatory positional args are specified
     258         [ +  + ]:       1003 :         while (d < &code_state_state[n_state]) {
     259         [ +  + ]:        524 :             if (*d++ == MP_OBJ_NULL) {
     260                 :          4 :                 mp_raise_msg_varg(&mp_type_TypeError,
     261                 :          4 :                     MP_ERROR_TEXT("function missing required positional argument #%d"), &code_state_state[n_state] - d);
     262                 :            :             }
     263                 :            :         }
     264                 :            : 
     265                 :            :         // Check that all mandatory keyword args are specified
     266                 :            :         // Fill in default kw args if we have them
     267                 :        479 :         const uint8_t *arg_names = mp_decode_uint_skip(code_state->ip);
     268         [ +  + ]:       1337 :         for (size_t i = 0; i < n_pos_args; i++) {
     269                 :        858 :             arg_names = mp_decode_uint_skip(arg_names);
     270                 :            :         }
     271         [ +  + ]:        791 :         for (size_t i = 0; i < n_kwonly_args; i++) {
     272                 :        320 :             qstr arg_qstr = mp_decode_uint(&arg_names);
     273                 :            :             #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE
     274                 :        320 :             arg_qstr = self->context->constants.qstr_table[arg_qstr];
     275                 :            :             #endif
     276         [ +  + ]:        320 :             if (code_state_state[n_state - 1 - n_pos_args - i] == MP_OBJ_NULL) {
     277                 :         80 :                 mp_map_elem_t *elem = NULL;
     278         [ +  + ]:         80 :                 if ((scope_flags & MP_SCOPE_FLAG_DEFKWARGS) != 0) {
     279                 :         72 :                     elem = mp_map_lookup(&((mp_obj_dict_t *)MP_OBJ_TO_PTR(self->extra_args[n_def_pos_args]))->map, MP_OBJ_NEW_QSTR(arg_qstr), MP_MAP_LOOKUP);
     280                 :            :                 }
     281         [ +  - ]:         72 :                 if (elem != NULL) {
     282                 :         72 :                     code_state_state[n_state - 1 - n_pos_args - i] = elem->value;
     283                 :            :                 } else {
     284                 :          8 :                     mp_raise_msg_varg(&mp_type_TypeError,
     285                 :          8 :                         MP_ERROR_TEXT("function missing required keyword argument '%q'"), arg_qstr);
     286                 :            :                 }
     287                 :            :             }
     288                 :            :         }
     289                 :            : 
     290                 :            :     } else {
     291                 :            :         // no keyword arguments given
     292         [ +  + ]:    9513931 :         if (n_kwonly_args != 0) {
     293                 :          4 :             mp_raise_TypeError(MP_ERROR_TEXT("function missing keyword-only argument"));
     294                 :            :         }
     295         [ +  + ]:    9513927 :         if ((scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) != 0) {
     296                 :        122 :             *var_pos_kw_args = mp_obj_new_dict(0);
     297                 :            :         }
     298                 :            :     }
     299                 :            : 
     300                 :            :     // jump over code info (source file, argument names and line-number mapping)
     301                 :    9514398 :     const uint8_t *ip = code_state->ip + n_info;
     302                 :            : 
     303                 :            :     // bytecode prelude: initialise closed over variables
     304         [ +  + ]:    9515554 :     for (; n_cell; --n_cell) {
     305                 :     180199 :         size_t local_num = *ip++;
     306                 :       1156 :         code_state_state[n_state - 1 - local_num] =
     307                 :     180199 :             mp_obj_new_cell(code_state_state[n_state - 1 - local_num]);
     308                 :            :     }
     309                 :            : 
     310                 :            :     // now that we skipped over the prelude, set the ip for the VM
     311                 :    9335355 :     code_state->ip = ip;
     312                 :            : 
     313                 :    9335355 :     DEBUG_printf("Calling: n_pos_args=%d, n_kwonly_args=%d\n", n_pos_args, n_kwonly_args);
     314                 :    9335355 :     dump_args(code_state_state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args);
     315                 :    9335355 :     dump_args(code_state_state, n_state);
     316                 :    9335355 : }
     317                 :            : 
     318                 :            : // On entry code_state should be allocated somewhere (stack/heap) and
     319                 :            : // contain the following valid entries:
     320                 :            : //    - code_state->fun_bc should contain a pointer to the function object
     321                 :            : //    - code_state->n_state should be the number of objects in the local state
     322                 :    7960935 : void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args) {
     323                 :    7960935 :     code_state->ip = code_state->fun_bc->bytecode;
     324                 :    7960935 :     code_state->sp = &code_state->state[0] - 1;
     325                 :            :     #if MICROPY_STACKLESS
     326                 :            :     code_state->prev = NULL;
     327                 :            :     #endif
     328                 :            :     #if MICROPY_PY_SYS_SETTRACE
     329                 :            :     code_state->prev_state = NULL;
     330                 :            :     code_state->frame = NULL;
     331                 :            :     #endif
     332                 :    7960935 :     mp_setup_code_state_helper(code_state, n_args, n_kw, args);
     333                 :    8237303 : }
     334                 :            : 
     335                 :            : #if MICROPY_EMIT_NATIVE
     336                 :            : // On entry code_state should be allocated somewhere (stack/heap) and
     337                 :            : // contain the following valid entries:
     338                 :            : //    - code_state->fun_bc should contain a pointer to the function object
     339                 :            : //    - code_state->n_state should be the number of objects in the local state
     340                 :    1063636 : void mp_setup_code_state_native(mp_code_state_native_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args) {
     341         [ +  + ]:    1063636 :     code_state->ip = mp_obj_fun_native_get_prelude_ptr(code_state->fun_bc);
     342                 :    1063636 :     code_state->sp = &code_state->state[0] - 1;
     343                 :    1063636 :     mp_setup_code_state_helper((mp_code_state_t *)code_state, n_args, n_kw, args);
     344                 :    1063604 : }
     345                 :            : #endif

Generated by: LCOV version 1.15-5-g462f71d