STATIC mp_obj_t mp_builtin_getattr(uint n_args, const mp_obj_t *args) { assert(MP_OBJ_IS_QSTR(args[1])); mp_obj_t defval = MP_OBJ_NULL; if (n_args > 2) { defval = args[2]; } return mp_load_attr_default(args[0], MP_OBJ_QSTR_VALUE(args[1]), defval); }
STATIC mp_obj_t mp_builtin_hasattr(mp_obj_t object_in, mp_obj_t attr_in) { assert(MP_OBJ_IS_QSTR(attr_in)); mp_obj_t dest[2]; // TODO: https://docs.python.org/3.3/library/functions.html?highlight=hasattr#hasattr // explicitly says "This is implemented by calling getattr(object, name) and seeing // whether it raises an AttributeError or not.", so we should explicitly wrap this // in nlr_push and handle exception. mp_load_method_maybe(object_in, MP_OBJ_QSTR_VALUE(attr_in), dest); return MP_BOOL(dest[0] != MP_OBJ_NULL); }
/// \method getScanData() /// Return list of the scan data tupples (ad_type, description, value) /// STATIC mp_obj_t scan_entry_get_scan_data(mp_obj_t self_in) { ubluepy_scan_entry_obj_t * self = MP_OBJ_TO_PTR(self_in); mp_obj_t retval_list = mp_obj_new_list(0, NULL); // TODO: check if self->data is set mp_obj_array_t * data = MP_OBJ_TO_PTR(self->data); uint16_t byte_index = 0; while (byte_index < data->len) { mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL)); uint8_t adv_item_len = ((uint8_t * )data->items)[byte_index]; uint8_t adv_item_type = ((uint8_t * )data->items)[byte_index + 1]; mp_obj_t description = mp_const_none; mp_map_t *constant_map = mp_obj_dict_get_map(ubluepy_constants_ad_types_type.locals_dict); mp_map_elem_t *ad_types_table = MP_OBJ_TO_PTR(constant_map->table); uint16_t num_of_elements = constant_map->used; for (uint16_t i = 0; i < num_of_elements; i++) { mp_map_elem_t element = (mp_map_elem_t)*ad_types_table; ad_types_table++; uint16_t element_value = mp_obj_get_int(element.value); if (adv_item_type == element_value) { qstr key_qstr = MP_OBJ_QSTR_VALUE(element.key); const char * text = qstr_str(key_qstr); size_t len = qstr_len(key_qstr); vstr_t vstr; vstr_init(&vstr, len); vstr_printf(&vstr, "%s", text); description = mp_obj_new_str(vstr.buf, vstr.len); vstr_clear(&vstr); } } t->items[0] = MP_OBJ_NEW_SMALL_INT(adv_item_type); t->items[1] = description; t->items[2] = mp_obj_new_bytearray(adv_item_len - 1, &((uint8_t * )data->items)[byte_index + 2]); mp_obj_list_append(retval_list, MP_OBJ_FROM_PTR(t)); byte_index += adv_item_len + 1; } return retval_list; }
mp_obj_t mp_builtin___import__(mp_uint_t n_args, const mp_obj_t *args) { #if DEBUG_PRINT DEBUG_printf("__import__:\n"); for (mp_uint_t i = 0; i < n_args; i++) { DEBUG_printf(" "); mp_obj_print(args[i], PRINT_REPR); DEBUG_printf("\n"); } #endif mp_obj_t module_name = args[0]; mp_obj_t fromtuple = mp_const_none; mp_int_t level = 0; if (n_args >= 4) { fromtuple = args[3]; if (n_args >= 5) { level = MP_OBJ_SMALL_INT_VALUE(args[4]); } } mp_uint_t mod_len; const char *mod_str = mp_obj_str_get_data(module_name, &mod_len); if (level != 0) { // What we want to do here is to take name of current module, // chop <level> trailing components, and concatenate with passed-in // module name, thus resolving relative import name into absolue. // This even appears to be correct per // http://legacy.python.org/dev/peps/pep-0328/#relative-imports-and-name // "Relative imports use a module's __name__ attribute to determine that // module's position in the package hierarchy." level--; mp_obj_t this_name_q = mp_obj_dict_get(mp_globals_get(), MP_OBJ_NEW_QSTR(MP_QSTR___name__)); assert(this_name_q != MP_OBJ_NULL); #if MICROPY_CPYTHON_COMPAT if (MP_OBJ_QSTR_VALUE(this_name_q) == MP_QSTR___main__) { // This is a module run by -m command-line switch, get its real name from backup attribute this_name_q = mp_obj_dict_get(mp_globals_get(), MP_OBJ_NEW_QSTR(MP_QSTR___main__)); } #endif mp_map_t *globals_map = mp_obj_dict_get_map(mp_globals_get()); mp_map_elem_t *elem = mp_map_lookup(globals_map, MP_OBJ_NEW_QSTR(MP_QSTR___path__), MP_MAP_LOOKUP); bool is_pkg = (elem != NULL); #if DEBUG_PRINT DEBUG_printf("Current module/package: "); mp_obj_print(this_name_q, PRINT_REPR); DEBUG_printf(", is_package: %d", is_pkg); DEBUG_printf("\n"); #endif mp_uint_t this_name_l; const char *this_name = mp_obj_str_get_data(this_name_q, &this_name_l); const char *p = this_name + this_name_l; if (!is_pkg) { // We have module, but relative imports are anchored at package, so // go there. chop_component(this_name, &p); } uint dots_seen = 0; while (level--) { chop_component(this_name, &p); dots_seen++; } if (dots_seen == 0 && level >= 1) { // http://legacy.python.org/dev/peps/pep-0328/#relative-imports-and-name // "If the module's name does not contain any package information // (e.g. it is set to '__main__') then relative imports are // resolved as if the module were a top level module, regardless // of where the module is actually located on the file system." // Supposedly this if catches this condition and resolve it properly // TODO: But nobody knows for sure. This condition happens when // package's __init__.py does something like "import .submod". So, // maybe we should check for package here? But quote above doesn't // talk about packages, it talks about dot-less module names. DEBUG_printf("Warning: no dots in current module name and level>0\n"); p = this_name + this_name_l; } else if (level != -1) { nlr_raise(mp_obj_new_exception_msg(&mp_type_ImportError, "Invalid relative import")); } uint new_mod_l = (mod_len == 0 ? (size_t)(p - this_name) : (size_t)(p - this_name) + 1 + mod_len); char *new_mod = alloca(new_mod_l); memcpy(new_mod, this_name, p - this_name); if (mod_len != 0) { new_mod[p - this_name] = '.'; memcpy(new_mod + (p - this_name) + 1, mod_str, mod_len); } qstr new_mod_q = qstr_from_strn(new_mod, new_mod_l); DEBUG_printf("Resolved base name for relative import: '%s'\n", qstr_str(new_mod_q)); if (new_mod_q == MP_QSTR_) { // CPython raises SystemError nlr_raise(mp_obj_new_exception_msg(&mp_type_ImportError, "cannot perform relative import")); } module_name = MP_OBJ_NEW_QSTR(new_mod_q); mod_str = new_mod; mod_len = new_mod_l; } // check if module already exists qstr module_name_qstr = mp_obj_str_get_qstr(module_name); mp_obj_t module_obj = mp_module_get(module_name_qstr); if (module_obj != MP_OBJ_NULL) { DEBUG_printf("Module already loaded\n"); // If it's not a package, return module right away char *p = strchr(mod_str, '.'); if (p == NULL) { return module_obj; } // If fromlist is not empty, return leaf module if (fromtuple != mp_const_none) { return module_obj; } // Otherwise, we need to return top-level package qstr pkg_name = qstr_from_strn(mod_str, p - mod_str); return mp_module_get(pkg_name); } DEBUG_printf("Module not yet loaded\n"); #if MICROPY_MODULE_FROZEN mp_lexer_t *lex = mp_find_frozen_module(mod_str, mod_len); if (lex != NULL) { module_obj = mp_obj_new_module(module_name_qstr); // if args[3] (fromtuple) has magic value False, set up // this module for command-line "-m" option (set module's // name to __main__ instead of real name). // TODO: Duplicated below too. if (fromtuple == mp_const_false) { mp_obj_module_t *o = module_obj; mp_obj_dict_store(o->globals, MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR___main__)); } do_load_from_lexer(module_obj, lex, mod_str); return module_obj; } #endif uint last = 0; VSTR_FIXED(path, MICROPY_ALLOC_PATH_MAX) module_obj = MP_OBJ_NULL; mp_obj_t top_module_obj = MP_OBJ_NULL; mp_obj_t outer_module_obj = MP_OBJ_NULL; uint i; for (i = 1; i <= mod_len; i++) { if (i == mod_len || mod_str[i] == '.') { // create a qstr for the module name up to this depth qstr mod_name = qstr_from_strn(mod_str, i); DEBUG_printf("Processing module: %s\n", qstr_str(mod_name)); DEBUG_printf("Previous path: =%.*s=\n", vstr_len(&path), vstr_str(&path)); // find the file corresponding to the module name mp_import_stat_t stat; if (vstr_len(&path) == 0) { // first module in the dotted-name; search for a directory or file stat = find_file(mod_str, i, &path); } else { // latter module in the dotted-name; append to path vstr_add_char(&path, PATH_SEP_CHAR); vstr_add_strn(&path, mod_str + last, i - last); stat = stat_dir_or_file(&path); } DEBUG_printf("Current path: %.*s\n", vstr_len(&path), vstr_str(&path)); if (stat == MP_IMPORT_STAT_NO_EXIST) { #if MICROPY_MODULE_WEAK_LINKS // check if there is a weak link to this module if (i == mod_len) { mp_map_elem_t *el = mp_map_lookup((mp_map_t*)&mp_builtin_module_weak_links_map, MP_OBJ_NEW_QSTR(mod_name), MP_MAP_LOOKUP); if (el == NULL) { goto no_exist; } // found weak linked module module_obj = el->value; } else { no_exist: #else { #endif // couldn't find the file, so fail if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) { nlr_raise(mp_obj_new_exception_msg(&mp_type_ImportError, "module not found")); } else { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ImportError, "no module named '%q'", mod_name)); } } } else { // found the file, so get the module module_obj = mp_module_get(mod_name); } if (module_obj == MP_OBJ_NULL) { // module not already loaded, so load it! module_obj = mp_obj_new_module(mod_name); // if args[3] (fromtuple) has magic value False, set up // this module for command-line "-m" option (set module's // name to __main__ instead of real name). if (i == mod_len && fromtuple == mp_const_false) { mp_obj_module_t *o = module_obj; mp_obj_dict_store(o->globals, MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR___main__)); #if MICROPY_CPYTHON_COMPAT // Store real name in "__main__" attribute. Choosen semi-randonly, to reuse existing qstr's. mp_obj_dict_store(o->globals, MP_OBJ_NEW_QSTR(MP_QSTR___main__), MP_OBJ_NEW_QSTR(mod_name)); #endif } if (stat == MP_IMPORT_STAT_DIR) { DEBUG_printf("%.*s is dir\n", vstr_len(&path), vstr_str(&path)); // https://docs.python.org/3/reference/import.html // "Specifically, any module that contains a __path__ attribute is considered a package." mp_store_attr(module_obj, MP_QSTR___path__, mp_obj_new_str(vstr_str(&path), vstr_len(&path), false)); vstr_add_char(&path, PATH_SEP_CHAR); vstr_add_str(&path, "__init__.py"); if (mp_import_stat(vstr_null_terminated_str(&path)) != MP_IMPORT_STAT_FILE) { vstr_cut_tail_bytes(&path, sizeof("/__init__.py") - 1); // cut off /__init__.py mp_warning("%s is imported as namespace package", vstr_str(&path)); } else { do_load(module_obj, &path); vstr_cut_tail_bytes(&path, sizeof("/__init__.py") - 1); // cut off /__init__.py } } else { // MP_IMPORT_STAT_FILE do_load(module_obj, &path); // TODO: We cannot just break here, at the very least, we must execute // trailer code below. But otherwise if there're remaining components, // that would be (??) object path within module, not modules path within FS. // break; } } if (outer_module_obj != MP_OBJ_NULL) { qstr s = qstr_from_strn(mod_str + last, i - last); mp_store_attr(outer_module_obj, s, module_obj); } outer_module_obj = module_obj; if (top_module_obj == MP_OBJ_NULL) { top_module_obj = module_obj; } last = i + 1; } }
void mp_bytecode_print(const void *descr, const byte *ip, mp_uint_t len, const mp_uint_t *const_table) { mp_showbc_code_start = ip; // get bytecode parameters mp_uint_t n_state = mp_decode_uint(&ip); mp_uint_t n_exc_stack = mp_decode_uint(&ip); /*mp_uint_t scope_flags =*/ ip++; mp_uint_t n_pos_args = *ip++; mp_uint_t n_kwonly_args = *ip++; /*mp_uint_t n_def_pos_args =*/ ip++; const byte *code_info = ip; mp_uint_t code_info_size = mp_decode_uint(&code_info); ip += code_info_size; #if MICROPY_PERSISTENT_CODE qstr block_name = code_info[0] | (code_info[1] << 8); qstr source_file = code_info[2] | (code_info[3] << 8); code_info += 4; #else qstr block_name = mp_decode_uint(&code_info); qstr source_file = mp_decode_uint(&code_info); #endif printf("File %s, code block '%s' (descriptor: %p, bytecode @%p " UINT_FMT " bytes)\n", qstr_str(source_file), qstr_str(block_name), descr, mp_showbc_code_start, len); // raw bytecode dump printf("Raw bytecode (code_info_size=" UINT_FMT ", bytecode_size=" UINT_FMT "):\n", code_info_size, len - code_info_size); for (mp_uint_t i = 0; i < len; i++) { if (i > 0 && i % 16 == 0) { printf("\n"); } printf(" %02x", mp_showbc_code_start[i]); } printf("\n"); // bytecode prelude: arg names (as qstr objects) printf("arg names:"); for (mp_uint_t i = 0; i < n_pos_args + n_kwonly_args; i++) { printf(" %s", qstr_str(MP_OBJ_QSTR_VALUE(const_table[i]))); } printf("\n"); printf("(N_STATE " UINT_FMT ")\n", n_state); printf("(N_EXC_STACK " UINT_FMT ")\n", n_exc_stack); // for printing line number info const byte *bytecode_start = ip; // bytecode prelude: initialise closed over variables { uint local_num; while ((local_num = *ip++) != 255) { printf("(INIT_CELL %u)\n", local_num); } len -= ip - mp_showbc_code_start; } // print out line number info { mp_int_t bc = bytecode_start - ip; mp_uint_t source_line = 1; printf(" bc=" INT_FMT " line=" UINT_FMT "\n", bc, source_line); for (const byte* ci = code_info; *ci;) { if ((ci[0] & 0x80) == 0) { // 0b0LLBBBBB encoding bc += ci[0] & 0x1f; source_line += ci[0] >> 5; ci += 1; } else { // 0b1LLLBBBB 0bLLLLLLLL encoding (l's LSB in second byte) bc += ci[0] & 0xf; source_line += ((ci[0] << 4) & 0x700) | ci[1]; ci += 2; } printf(" bc=" INT_FMT " line=" UINT_FMT "\n", bc, source_line); }
STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *args) { DEBUG_printf("Input: "); dump_args(args, n_args); mp_obj_fun_bc_t *self = self_in; const mp_obj_t *kwargs = args + n_args; mp_obj_t *extra_args = self->extra_args + self->n_def_args; uint n_extra_args = 0; // check positional arguments if (n_args > self->n_args) { // given more than enough arguments if (!self->takes_var_args) { goto arg_error; } // put extra arguments in varargs tuple *extra_args = mp_obj_new_tuple(n_args - self->n_args, args + self->n_args); n_extra_args = 1; n_args = self->n_args; } else { if (self->takes_var_args) { DEBUG_printf("passing empty tuple as *args\n"); *extra_args = mp_const_empty_tuple; n_extra_args = 1; } // Apply processing and check below only if we don't have kwargs, // otherwise, kw handling code below has own extensive checks. if (n_kw == 0) { if (n_args >= self->n_args - self->n_def_args) { // given enough arguments, but may need to use some default arguments extra_args -= self->n_args - n_args; n_extra_args += self->n_args - n_args; } else { goto arg_error; } } } // check keyword arguments if (n_kw != 0) { // We cannot use dynamically-sized array here, because GCC indeed // deallocates it on leaving defining scope (unlike most static stack allocs). // So, we have 2 choices: allocate it unconditionally at the top of function // (wastes stack), or use alloca which is guaranteed to dealloc on func exit. //mp_obj_t flat_args[self->n_args]; mp_obj_t *flat_args = alloca(self->n_args * sizeof(mp_obj_t)); for (int i = self->n_args - 1; i >= 0; i--) { flat_args[i] = MP_OBJ_NULL; } memcpy(flat_args, args, sizeof(*args) * n_args); DEBUG_printf("Initial args: "); dump_args(flat_args, self->n_args); mp_obj_t dict = MP_OBJ_NULL; if (self->takes_kw_args) { dict = mp_obj_new_dict(n_kw); // TODO: better go conservative with 0? } for (uint i = 0; i < n_kw; i++) { qstr arg_name = MP_OBJ_QSTR_VALUE(kwargs[2 * i]); for (uint j = 0; j < self->n_args; j++) { if (arg_name == self->args[j]) { if (flat_args[j] != MP_OBJ_NULL) { nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "function got multiple values for argument '%s'", qstr_str(arg_name))); } flat_args[j] = kwargs[2 * i + 1]; goto continue2; } } // Didn't find name match with positional args if (!self->takes_kw_args) { nlr_jump(mp_obj_new_exception_msg(&mp_type_TypeError, "function does not take keyword arguments")); } mp_obj_dict_store(dict, kwargs[2 * i], kwargs[2 * i + 1]); continue2:; } DEBUG_printf("Args with kws flattened: "); dump_args(flat_args, self->n_args); // Now fill in defaults mp_obj_t *d = &flat_args[self->n_args - 1]; mp_obj_t *s = &self->extra_args[self->n_def_args - 1]; for (int i = self->n_def_args; i > 0; i--) { if (*d == MP_OBJ_NULL) { *d-- = *s--; } } DEBUG_printf("Args after filling defaults: "); dump_args(flat_args, self->n_args); // Now check that all mandatory args specified while (d >= flat_args) { if (*d-- == MP_OBJ_NULL) { nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "function missing required positional argument #%d", d - flat_args)); } } args = flat_args; n_args = self->n_args; if (self->takes_kw_args) { extra_args[n_extra_args] = dict; n_extra_args += 1; } } else { // no keyword arguments given if (self->takes_kw_args) { extra_args[n_extra_args] = mp_obj_new_dict(0); n_extra_args += 1; } } mp_map_t *old_globals = mp_globals_get(); mp_globals_set(self->globals); mp_obj_t result; DEBUG_printf("Calling: args=%p, n_args=%d, extra_args=%p, n_extra_args=%d\n", args, n_args, extra_args, n_extra_args); dump_args(args, n_args); dump_args(extra_args, n_extra_args); mp_vm_return_kind_t vm_return_kind = mp_execute_byte_code(self->bytecode, args, n_args, extra_args, n_extra_args, &result); mp_globals_set(old_globals); if (vm_return_kind == MP_VM_RETURN_NORMAL) { return result; } else { // MP_VM_RETURN_EXCEPTION nlr_jump(result); } arg_error: nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "function takes %d positional arguments but %d were given", self->n_args, n_args)); }
void mp_bytecode_print(const void *descr, mp_uint_t n_total_args, const byte *ip, mp_uint_t len) { mp_showbc_code_start = ip; // get code info size const byte *code_info = ip; mp_uint_t code_info_size = mp_decode_uint(&code_info); ip += code_info_size; qstr block_name = mp_decode_uint(&code_info); qstr source_file = mp_decode_uint(&code_info); printf("File %s, code block '%s' (descriptor: %p, bytecode @%p " UINT_FMT " bytes)\n", qstr_str(source_file), qstr_str(block_name), descr, code_info, len); // raw bytecode dump printf("Raw bytecode (code_info_size=" UINT_FMT ", bytecode_size=" UINT_FMT "):\n", code_info_size, len - code_info_size); for (mp_uint_t i = 0; i < len; i++) { if (i > 0 && i % 16 == 0) { printf("\n"); } printf(" %02x", mp_showbc_code_start[i]); } printf("\n"); // bytecode prelude: arg names (as qstr objects) printf("arg names:"); for (mp_uint_t i = 0; i < n_total_args; i++) { printf(" %s", qstr_str(MP_OBJ_QSTR_VALUE(*(mp_obj_t*)ip))); ip += sizeof(mp_obj_t); } printf("\n"); // bytecode prelude: state size and exception stack size; 16 bit uints { uint n_state = mp_decode_uint(&ip); uint n_exc_stack = mp_decode_uint(&ip); printf("(N_STATE %u)\n", n_state); printf("(N_EXC_STACK %u)\n", n_exc_stack); } // bytecode prelude: initialise closed over variables { uint local_num; while ((local_num = *ip++) != 255) { printf("(INIT_CELL %u)\n", local_num); } len -= ip - mp_showbc_code_start; } // print out line number info { mp_int_t bc = (mp_showbc_code_start + code_info_size) - ip; // start counting from the prelude mp_uint_t source_line = 1; printf(" bc=" INT_FMT " line=" UINT_FMT "\n", bc, source_line); for (const byte* ci = code_info; *ci;) { if ((ci[0] & 0x80) == 0) { // 0b0LLBBBBB encoding bc += ci[0] & 0x1f; source_line += ci[0] >> 5; ci += 1; } else { // 0b1LLLBBBB 0bLLLLLLLL encoding (l's LSB in second byte) bc += ci[0] & 0xf; source_line += ((ci[0] << 4) & 0x700) | ci[1]; ci += 2; } printf(" bc=" INT_FMT " line=" UINT_FMT "\n", bc, source_line); }
STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *args) { // This function is pretty complicated. It's main aim is to be efficient in speed and RAM // usage for the common case of positional only args. DEBUG_printf("Input n_args: %d, n_kw: %d\n", n_args, n_kw); DEBUG_printf("Input pos args: "); dump_args(args, n_args); DEBUG_printf("Input kw args: "); dump_args(args + n_args, n_kw * 2); mp_obj_fun_bc_t *self = self_in; DEBUG_printf("Func n_def_args: %d\n", self->n_def_args); const byte *ip = self->bytecode; // get code info size, and skip line number table machine_uint_t code_info_size = ip[0] | (ip[1] << 8) | (ip[2] << 16) | (ip[3] << 24); ip += code_info_size; // bytecode prelude: state size and exception stack size; 16 bit uints machine_uint_t n_state = ip[0] | (ip[1] << 8); machine_uint_t n_exc_stack = ip[2] | (ip[3] << 8); ip += 4; #if VM_DETECT_STACK_OVERFLOW n_state += 1; #endif // allocate state for locals and stack uint state_size = n_state * sizeof(mp_obj_t) + n_exc_stack * sizeof(mp_exc_stack_t); mp_code_state *code_state; if (state_size > VM_MAX_STATE_ON_STACK) { code_state = m_new_obj_var(mp_code_state, byte, state_size); } else { code_state = alloca(sizeof(mp_code_state) + state_size); } code_state->code_info = self->bytecode; code_state->sp = &code_state->state[0] - 1; code_state->exc_sp = (mp_exc_stack_t*)(code_state->state + n_state) - 1; code_state->n_state = n_state; // zero out the local stack to begin with memset(code_state->state, 0, n_state * sizeof(*code_state->state)); const mp_obj_t *kwargs = args + n_args; // var_pos_kw_args points to the stack where the var-args tuple, and var-kw dict, should go (if they are needed) mp_obj_t *var_pos_kw_args = &code_state->state[n_state - 1 - self->n_pos_args - self->n_kwonly_args]; // check positional arguments if (n_args > self->n_pos_args) { // given more than enough arguments if (!self->takes_var_args) { fun_pos_args_mismatch(self, self->n_pos_args, n_args); } // put extra arguments in varargs tuple *var_pos_kw_args-- = mp_obj_new_tuple(n_args - self->n_pos_args, args + self->n_pos_args); n_args = self->n_pos_args; } else { if (self->takes_var_args) { DEBUG_printf("passing empty tuple as *args\n"); *var_pos_kw_args-- = mp_const_empty_tuple; } // Apply processing and check below only if we don't have kwargs, // otherwise, kw handling code below has own extensive checks. if (n_kw == 0 && !self->has_def_kw_args) { if (n_args >= self->n_pos_args - self->n_def_args) { // given enough arguments, but may need to use some default arguments for (uint i = n_args; i < self->n_pos_args; i++) { code_state->state[n_state - 1 - i] = self->extra_args[i - (self->n_pos_args - self->n_def_args)]; } } else { fun_pos_args_mismatch(self, self->n_pos_args - self->n_def_args, n_args); } } } // copy positional args into state for (uint i = 0; i < n_args; i++) { code_state->state[n_state - 1 - i] = args[i]; } // check keyword arguments if (n_kw != 0 || self->has_def_kw_args) { DEBUG_printf("Initial args: "); dump_args(code_state->state + n_state - self->n_pos_args - self->n_kwonly_args, self->n_pos_args + self->n_kwonly_args); mp_obj_t dict = MP_OBJ_NULL; if (self->takes_kw_args) { dict = mp_obj_new_dict(n_kw); // TODO: better go conservative with 0? *var_pos_kw_args = dict; } for (uint i = 0; i < n_kw; i++) { qstr arg_name = MP_OBJ_QSTR_VALUE(kwargs[2 * i]); for (uint j = 0; j < self->n_pos_args + self->n_kwonly_args; j++) { if (arg_name == self->args[j]) { if (code_state->state[n_state - 1 - j] != MP_OBJ_NULL) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "function got multiple values for argument '%s'", qstr_str(arg_name))); } code_state->state[n_state - 1 - j] = kwargs[2 * i + 1]; goto continue2; } } // Didn't find name match with positional args if (!self->takes_kw_args) { nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "function does not take keyword arguments")); } mp_obj_dict_store(dict, kwargs[2 * i], kwargs[2 * i + 1]); continue2:; } DEBUG_printf("Args with kws flattened: "); dump_args(code_state->state + n_state - self->n_pos_args - self->n_kwonly_args, self->n_pos_args + self->n_kwonly_args); // fill in defaults for positional args mp_obj_t *d = &code_state->state[n_state - self->n_pos_args]; mp_obj_t *s = &self->extra_args[self->n_def_args - 1]; for (int i = self->n_def_args; i > 0; i--, d++, s--) { if (*d == MP_OBJ_NULL) { *d = *s; } } DEBUG_printf("Args after filling default positional: "); dump_args(code_state->state + n_state - self->n_pos_args - self->n_kwonly_args, self->n_pos_args + self->n_kwonly_args); // Check that all mandatory positional args are specified while (d < &code_state->state[n_state]) { if (*d++ == MP_OBJ_NULL) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "function missing required positional argument #%d", &code_state->state[n_state] - d)); } } // Check that all mandatory keyword args are specified // Fill in default kw args if we have them for (uint i = 0; i < self->n_kwonly_args; i++) { if (code_state->state[n_state - 1 - self->n_pos_args - i] == MP_OBJ_NULL) { mp_map_elem_t *elem = NULL; if (self->has_def_kw_args) { elem = mp_map_lookup(&((mp_obj_dict_t*)self->extra_args[self->n_def_args])->map, MP_OBJ_NEW_QSTR(self->args[self->n_pos_args + i]), MP_MAP_LOOKUP); } if (elem != NULL) { code_state->state[n_state - 1 - self->n_pos_args - i] = elem->value; } else { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "function missing required keyword argument '%s'", qstr_str(self->args[self->n_pos_args + i]))); } } } } else { // no keyword arguments given if (self->n_kwonly_args != 0) { nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "function missing keyword-only argument")); } if (self->takes_kw_args) { *var_pos_kw_args = mp_obj_new_dict(0); } } // bytecode prelude: initialise closed over variables for (uint n_local = *ip++; n_local > 0; n_local--) { uint local_num = *ip++; code_state->state[n_state - 1 - local_num] = mp_obj_new_cell(code_state->state[n_state - 1 - local_num]); } // now that we skipped over the prelude, set the ip for the VM code_state->ip = ip; DEBUG_printf("Calling: n_pos_args=%d, n_kwonly_args=%d\n", self->n_pos_args, self->n_kwonly_args); dump_args(code_state->state + n_state - self->n_pos_args - self->n_kwonly_args, self->n_pos_args + self->n_kwonly_args); dump_args(code_state->state, n_state); // execute the byte code with the correct globals context mp_obj_dict_t *old_globals = mp_globals_get(); mp_globals_set(self->globals); mp_vm_return_kind_t vm_return_kind = mp_execute_bytecode(code_state, MP_OBJ_NULL); mp_globals_set(old_globals); #if VM_DETECT_STACK_OVERFLOW if (vm_return_kind == MP_VM_RETURN_NORMAL) { if (code_state->sp < code_state->state) { printf("VM stack underflow: " INT_FMT "\n", code_state->sp - code_state->state); assert(0); } } // We can't check the case when an exception is returned in state[n_state - 1] // and there are no arguments, because in this case our detection slot may have // been overwritten by the returned exception (which is allowed). if (!(vm_return_kind == MP_VM_RETURN_EXCEPTION && self->n_pos_args + self->n_kwonly_args == 0)) { // Just check to see that we have at least 1 null object left in the state. bool overflow = true; for (uint i = 0; i < n_state - self->n_pos_args - self->n_kwonly_args; i++) { if (code_state->state[i] == MP_OBJ_NULL) { overflow = false; break; } } if (overflow) { printf("VM stack overflow state=%p n_state+1=" UINT_FMT "\n", code_state->state, n_state); assert(0); } } #endif mp_obj_t result; switch (vm_return_kind) { case MP_VM_RETURN_NORMAL: // return value is in *sp result = *code_state->sp; break; case MP_VM_RETURN_EXCEPTION: // return value is in state[n_state - 1] result = code_state->state[n_state - 1]; break; case MP_VM_RETURN_YIELD: // byte-code shouldn't yield default: assert(0); result = mp_const_none; vm_return_kind = MP_VM_RETURN_NORMAL; break; } // free the state if it was allocated on the heap if (state_size > VM_MAX_STATE_ON_STACK) { m_del_var(mp_code_state, byte, state_size, code_state); } if (vm_return_kind == MP_VM_RETURN_NORMAL) { return result; } else { // MP_VM_RETURN_EXCEPTION nlr_raise(result); } }
STATIC mp_obj_t new_namedtuple_type(mp_obj_t name_in, mp_obj_t fields_in) { qstr name = MP_OBJ_QSTR_VALUE(name_in); const char *fields = mp_obj_str_get_str(fields_in); return mp_obj_new_namedtuple_type(name, fields); }
mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) { #if DEBUG_PRINT DEBUG_printf("__import__:\n"); for (size_t i = 0; i < n_args; i++) { DEBUG_printf(" "); mp_obj_print(args[i], PRINT_REPR); DEBUG_printf("\n"); } #endif mp_obj_t module_name = args[0]; mp_obj_t fromtuple = mp_const_none; mp_int_t level = 0; if (n_args >= 4) { fromtuple = args[3]; if (n_args >= 5) { level = MP_OBJ_SMALL_INT_VALUE(args[4]); if (level < 0) { mp_raise_ValueError(NULL); } } } size_t mod_len; const char *mod_str = mp_obj_str_get_data(module_name, &mod_len); if (level != 0) { // What we want to do here is to take name of current module, // chop <level> trailing components, and concatenate with passed-in // module name, thus resolving relative import name into absolute. // This even appears to be correct per // http://legacy.python.org/dev/peps/pep-0328/#relative-imports-and-name // "Relative imports use a module's __name__ attribute to determine that // module's position in the package hierarchy." level--; mp_obj_t this_name_q = mp_obj_dict_get(MP_OBJ_FROM_PTR(mp_globals_get()), MP_OBJ_NEW_QSTR(MP_QSTR___name__)); assert(this_name_q != MP_OBJ_NULL); #if MICROPY_CPYTHON_COMPAT if (MP_OBJ_QSTR_VALUE(this_name_q) == MP_QSTR___main__) { // This is a module run by -m command-line switch, get its real name from backup attribute this_name_q = mp_obj_dict_get(MP_OBJ_FROM_PTR(mp_globals_get()), MP_OBJ_NEW_QSTR(MP_QSTR___main__)); } #endif mp_map_t *globals_map = &mp_globals_get()->map; mp_map_elem_t *elem = mp_map_lookup(globals_map, MP_OBJ_NEW_QSTR(MP_QSTR___path__), MP_MAP_LOOKUP); bool is_pkg = (elem != NULL); #if DEBUG_PRINT DEBUG_printf("Current module/package: "); mp_obj_print(this_name_q, PRINT_REPR); DEBUG_printf(", is_package: %d", is_pkg); DEBUG_printf("\n"); #endif size_t this_name_l; const char *this_name = mp_obj_str_get_data(this_name_q, &this_name_l); const char *p = this_name + this_name_l; if (!is_pkg) { // We have module, but relative imports are anchored at package, so // go there. chop_component(this_name, &p); } while (level--) { chop_component(this_name, &p); } // We must have some component left over to import from if (p == this_name) { mp_raise_ValueError("cannot perform relative import"); } uint new_mod_l = (mod_len == 0 ? (size_t)(p - this_name) : (size_t)(p - this_name) + 1 + mod_len); char *new_mod = mp_local_alloc(new_mod_l); memcpy(new_mod, this_name, p - this_name); if (mod_len != 0) { new_mod[p - this_name] = '.'; memcpy(new_mod + (p - this_name) + 1, mod_str, mod_len); } qstr new_mod_q = qstr_from_strn(new_mod, new_mod_l); mp_local_free(new_mod); DEBUG_printf("Resolved base name for relative import: '%s'\n", qstr_str(new_mod_q)); module_name = MP_OBJ_NEW_QSTR(new_mod_q); mod_str = qstr_str(new_mod_q); mod_len = new_mod_l; } // check if module already exists qstr module_name_qstr = mp_obj_str_get_qstr(module_name); mp_obj_t module_obj = mp_module_get(module_name_qstr); if (module_obj != MP_OBJ_NULL) { DEBUG_printf("Module already loaded\n"); // If it's not a package, return module right away char *p = strchr(mod_str, '.'); if (p == NULL) { return module_obj; } // If fromlist is not empty, return leaf module if (fromtuple != mp_const_none) { return module_obj; } // Otherwise, we need to return top-level package qstr pkg_name = qstr_from_strn(mod_str, p - mod_str); return mp_module_get(pkg_name); } DEBUG_printf("Module not yet loaded\n"); uint last = 0; VSTR_FIXED(path, MICROPY_ALLOC_PATH_MAX) module_obj = MP_OBJ_NULL; mp_obj_t top_module_obj = MP_OBJ_NULL; mp_obj_t outer_module_obj = MP_OBJ_NULL; uint i; for (i = 1; i <= mod_len; i++) { if (i == mod_len || mod_str[i] == '.') { // create a qstr for the module name up to this depth qstr mod_name = qstr_from_strn(mod_str, i); DEBUG_printf("Processing module: %s\n", qstr_str(mod_name)); DEBUG_printf("Previous path: =%.*s=\n", vstr_len(&path), vstr_str(&path)); // find the file corresponding to the module name mp_import_stat_t stat; if (vstr_len(&path) == 0) { // first module in the dotted-name; search for a directory or file stat = find_file(mod_str, i, &path); } else { // latter module in the dotted-name; append to path vstr_add_char(&path, PATH_SEP_CHAR); vstr_add_strn(&path, mod_str + last, i - last); stat = stat_dir_or_file(&path); } DEBUG_printf("Current path: %.*s\n", vstr_len(&path), vstr_str(&path)); if (stat == MP_IMPORT_STAT_NO_EXIST) { #if MICROPY_MODULE_WEAK_LINKS // check if there is a weak link to this module if (i == mod_len) { mp_map_elem_t *el = mp_map_lookup((mp_map_t*)&mp_builtin_module_weak_links_map, MP_OBJ_NEW_QSTR(mod_name), MP_MAP_LOOKUP); if (el == NULL) { goto no_exist; } // found weak linked module module_obj = el->value; mp_module_call_init(mod_name, module_obj); } else { no_exist: #else { #endif // couldn't find the file, so fail if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) { mp_raise_msg(&mp_type_ImportError, "module not found"); } else { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ImportError, "no module named '%q'", mod_name)); } } } else { // found the file, so get the module module_obj = mp_module_get(mod_name); } if (module_obj == MP_OBJ_NULL) { // module not already loaded, so load it! module_obj = mp_obj_new_module(mod_name); // if args[3] (fromtuple) has magic value False, set up // this module for command-line "-m" option (set module's // name to __main__ instead of real name). Do this only // for *modules* however - packages never have their names // replaced, instead they're -m'ed using a special __main__ // submodule in them. (This all apparently is done to not // touch package name itself, which is important for future // imports). if (i == mod_len && fromtuple == mp_const_false && stat != MP_IMPORT_STAT_DIR) { mp_obj_module_t *o = MP_OBJ_TO_PTR(module_obj); mp_obj_dict_store(MP_OBJ_FROM_PTR(o->globals), MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR___main__)); #if MICROPY_CPYTHON_COMPAT // Store module as "__main__" in the dictionary of loaded modules (returned by sys.modules). mp_obj_dict_store(MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_loaded_modules_dict)), MP_OBJ_NEW_QSTR(MP_QSTR___main__), module_obj); // Store real name in "__main__" attribute. Chosen semi-randonly, to reuse existing qstr's. mp_obj_dict_store(MP_OBJ_FROM_PTR(o->globals), MP_OBJ_NEW_QSTR(MP_QSTR___main__), MP_OBJ_NEW_QSTR(mod_name)); #endif } if (stat == MP_IMPORT_STAT_DIR) { DEBUG_printf("%.*s is dir\n", vstr_len(&path), vstr_str(&path)); // https://docs.python.org/3/reference/import.html // "Specifically, any module that contains a __path__ attribute is considered a package." mp_store_attr(module_obj, MP_QSTR___path__, mp_obj_new_str(vstr_str(&path), vstr_len(&path))); size_t orig_path_len = path.len; vstr_add_char(&path, PATH_SEP_CHAR); vstr_add_str(&path, "__init__.py"); if (stat_file_py_or_mpy(&path) != MP_IMPORT_STAT_FILE) { //mp_warning("%s is imported as namespace package", vstr_str(&path)); } else { do_load(module_obj, &path); } path.len = orig_path_len; } else { // MP_IMPORT_STAT_FILE do_load(module_obj, &path); // This should be the last component in the import path. If there are // remaining components then it's an ImportError because the current path // (the module that was just loaded) is not a package. This will be caught // on the next iteration because the file will not exist. } } if (outer_module_obj != MP_OBJ_NULL) { qstr s = qstr_from_strn(mod_str + last, i - last); mp_store_attr(outer_module_obj, s, module_obj); } outer_module_obj = module_obj; if (top_module_obj == MP_OBJ_NULL) { top_module_obj = module_obj; } last = i + 1; } }
// code_state should have ->ip filled in (pointing past code info block), // as well as ->n_state. void mp_setup_code_state(mp_code_state *code_state, mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *args) { mp_obj_fun_bc_t *self = self_in; machine_uint_t n_state = code_state->n_state; const byte *ip = code_state->ip; code_state->code_info = self->bytecode; code_state->sp = &code_state->state[0] - 1; code_state->exc_sp = (mp_exc_stack_t*)(code_state->state + n_state) - 1; // zero out the local stack to begin with memset(code_state->state, 0, n_state * sizeof(*code_state->state)); const mp_obj_t *kwargs = args + n_args; // var_pos_kw_args points to the stack where the var-args tuple, and var-kw dict, should go (if they are needed) mp_obj_t *var_pos_kw_args = &code_state->state[n_state - 1 - self->n_pos_args - self->n_kwonly_args]; // check positional arguments if (n_args > self->n_pos_args) { // given more than enough arguments if (!self->takes_var_args) { fun_pos_args_mismatch(self, self->n_pos_args, n_args); } // put extra arguments in varargs tuple *var_pos_kw_args-- = mp_obj_new_tuple(n_args - self->n_pos_args, args + self->n_pos_args); n_args = self->n_pos_args; } else { if (self->takes_var_args) { DEBUG_printf("passing empty tuple as *args\n"); *var_pos_kw_args-- = mp_const_empty_tuple; } // Apply processing and check below only if we don't have kwargs, // otherwise, kw handling code below has own extensive checks. if (n_kw == 0 && !self->has_def_kw_args) { if (n_args >= self->n_pos_args - self->n_def_args) { // given enough arguments, but may need to use some default arguments for (uint i = n_args; i < self->n_pos_args; i++) { code_state->state[n_state - 1 - i] = self->extra_args[i - (self->n_pos_args - self->n_def_args)]; } } else { fun_pos_args_mismatch(self, self->n_pos_args - self->n_def_args, n_args); } } } // copy positional args into state for (uint i = 0; i < n_args; i++) { code_state->state[n_state - 1 - i] = args[i]; } // check keyword arguments if (n_kw != 0 || self->has_def_kw_args) { DEBUG_printf("Initial args: "); dump_args(code_state->state + n_state - self->n_pos_args - self->n_kwonly_args, self->n_pos_args + self->n_kwonly_args); mp_obj_t dict = MP_OBJ_NULL; if (self->takes_kw_args) { dict = mp_obj_new_dict(n_kw); // TODO: better go conservative with 0? *var_pos_kw_args = dict; } for (uint i = 0; i < n_kw; i++) { qstr arg_name = MP_OBJ_QSTR_VALUE(kwargs[2 * i]); for (uint j = 0; j < self->n_pos_args + self->n_kwonly_args; j++) { if (arg_name == self->args[j]) { if (code_state->state[n_state - 1 - j] != MP_OBJ_NULL) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "function got multiple values for argument '%s'", qstr_str(arg_name))); } code_state->state[n_state - 1 - j] = kwargs[2 * i + 1]; goto continue2; } } // Didn't find name match with positional args if (!self->takes_kw_args) { nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "function does not take keyword arguments")); } mp_obj_dict_store(dict, kwargs[2 * i], kwargs[2 * i + 1]); continue2:; } DEBUG_printf("Args with kws flattened: "); dump_args(code_state->state + n_state - self->n_pos_args - self->n_kwonly_args, self->n_pos_args + self->n_kwonly_args); // fill in defaults for positional args mp_obj_t *d = &code_state->state[n_state - self->n_pos_args]; mp_obj_t *s = &self->extra_args[self->n_def_args - 1]; for (int i = self->n_def_args; i > 0; i--, d++, s--) { if (*d == MP_OBJ_NULL) { *d = *s; } } DEBUG_printf("Args after filling default positional: "); dump_args(code_state->state + n_state - self->n_pos_args - self->n_kwonly_args, self->n_pos_args + self->n_kwonly_args); // Check that all mandatory positional args are specified while (d < &code_state->state[n_state]) { if (*d++ == MP_OBJ_NULL) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "function missing required positional argument #%d", &code_state->state[n_state] - d)); } } // Check that all mandatory keyword args are specified // Fill in default kw args if we have them for (uint i = 0; i < self->n_kwonly_args; i++) { if (code_state->state[n_state - 1 - self->n_pos_args - i] == MP_OBJ_NULL) { mp_map_elem_t *elem = NULL; if (self->has_def_kw_args) { elem = mp_map_lookup(&((mp_obj_dict_t*)self->extra_args[self->n_def_args])->map, MP_OBJ_NEW_QSTR(self->args[self->n_pos_args + i]), MP_MAP_LOOKUP); } if (elem != NULL) { code_state->state[n_state - 1 - self->n_pos_args - i] = elem->value; } else { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "function missing required keyword argument '%s'", qstr_str(self->args[self->n_pos_args + i]))); } } } } else { // no keyword arguments given if (self->n_kwonly_args != 0) { nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "function missing keyword-only argument")); } if (self->takes_kw_args) { *var_pos_kw_args = mp_obj_new_dict(0); } } // bytecode prelude: initialise closed over variables for (uint n_local = *ip++; n_local > 0; n_local--) { uint local_num = *ip++; code_state->state[n_state - 1 - local_num] = mp_obj_new_cell(code_state->state[n_state - 1 - local_num]); } // now that we skipped over the prelude, set the ip for the VM code_state->ip = ip; DEBUG_printf("Calling: n_pos_args=%d, n_kwonly_args=%d\n", self->n_pos_args, self->n_kwonly_args); dump_args(code_state->state + n_state - self->n_pos_args - self->n_kwonly_args, self->n_pos_args + self->n_kwonly_args); dump_args(code_state->state, n_state); }
// On entry code_state should be allocated somewhere (stack/heap) and // contain the following valid entries: // - code_state->ip should contain the offset in bytes from the start of // the bytecode chunk to just after n_state and n_exc_stack // - code_state->n_state should be set to the state size (locals plus stack) void mp_setup_code_state(mp_code_state *code_state, mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, const mp_obj_t *args) { // This function is pretty complicated. It's main aim is to be efficient in speed and RAM // usage for the common case of positional only args. size_t n_state = code_state->n_state; // ip comes in as an offset into bytecode, so turn it into a true pointer code_state->ip = self->bytecode + (size_t)code_state->ip; // store pointer to constant table code_state->const_table = self->const_table; #if MICROPY_STACKLESS code_state->prev = NULL; #endif // get params size_t scope_flags = *code_state->ip++; size_t n_pos_args = *code_state->ip++; size_t n_kwonly_args = *code_state->ip++; size_t n_def_pos_args = *code_state->ip++; code_state->sp = &code_state->state[0] - 1; code_state->exc_sp = (mp_exc_stack_t*)(code_state->state + n_state) - 1; // zero out the local stack to begin with memset(code_state->state, 0, n_state * sizeof(*code_state->state)); const mp_obj_t *kwargs = args + n_args; // var_pos_kw_args points to the stack where the var-args tuple, and var-kw dict, should go (if they are needed) mp_obj_t *var_pos_kw_args = &code_state->state[n_state - 1 - n_pos_args - n_kwonly_args]; // check positional arguments if (n_args > n_pos_args) { // given more than enough arguments if ((scope_flags & MP_SCOPE_FLAG_VARARGS) == 0) { fun_pos_args_mismatch(self, n_pos_args, n_args); } // put extra arguments in varargs tuple *var_pos_kw_args-- = mp_obj_new_tuple(n_args - n_pos_args, args + n_pos_args); n_args = n_pos_args; } else { if ((scope_flags & MP_SCOPE_FLAG_VARARGS) != 0) { DEBUG_printf("passing empty tuple as *args\n"); *var_pos_kw_args-- = mp_const_empty_tuple; } // Apply processing and check below only if we don't have kwargs, // otherwise, kw handling code below has own extensive checks. if (n_kw == 0 && (scope_flags & MP_SCOPE_FLAG_DEFKWARGS) == 0) { if (n_args >= (size_t)(n_pos_args - n_def_pos_args)) { // given enough arguments, but may need to use some default arguments for (size_t i = n_args; i < n_pos_args; i++) { code_state->state[n_state - 1 - i] = self->extra_args[i - (n_pos_args - n_def_pos_args)]; } } else { fun_pos_args_mismatch(self, n_pos_args - n_def_pos_args, n_args); } } } // copy positional args into state for (size_t i = 0; i < n_args; i++) { code_state->state[n_state - 1 - i] = args[i]; } // check keyword arguments if (n_kw != 0 || (scope_flags & MP_SCOPE_FLAG_DEFKWARGS) != 0) { DEBUG_printf("Initial args: "); dump_args(code_state->state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args); mp_obj_t dict = MP_OBJ_NULL; if ((scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) != 0) { dict = mp_obj_new_dict(n_kw); // TODO: better go conservative with 0? *var_pos_kw_args = dict; } // get pointer to arg_names array const mp_obj_t *arg_names = (const mp_obj_t*)code_state->const_table; for (size_t i = 0; i < n_kw; i++) { mp_obj_t wanted_arg_name = kwargs[2 * i]; for (size_t j = 0; j < n_pos_args + n_kwonly_args; j++) { if (wanted_arg_name == arg_names[j]) { if (code_state->state[n_state - 1 - j] != MP_OBJ_NULL) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "function got multiple values for argument '%q'", MP_OBJ_QSTR_VALUE(wanted_arg_name))); } code_state->state[n_state - 1 - j] = kwargs[2 * i + 1]; goto continue2; } } // Didn't find name match with positional args if ((scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) == 0) { nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "function does not take keyword arguments")); } mp_obj_dict_store(dict, kwargs[2 * i], kwargs[2 * i + 1]); continue2:; } DEBUG_printf("Args with kws flattened: "); dump_args(code_state->state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args); // fill in defaults for positional args mp_obj_t *d = &code_state->state[n_state - n_pos_args]; mp_obj_t *s = &self->extra_args[n_def_pos_args - 1]; for (size_t i = n_def_pos_args; i > 0; i--, d++, s--) { if (*d == MP_OBJ_NULL) { *d = *s; } } DEBUG_printf("Args after filling default positional: "); dump_args(code_state->state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args); // Check that all mandatory positional args are specified while (d < &code_state->state[n_state]) { if (*d++ == MP_OBJ_NULL) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "function missing required positional argument #%d", &code_state->state[n_state] - d)); } } // Check that all mandatory keyword args are specified // Fill in default kw args if we have them for (size_t i = 0; i < n_kwonly_args; i++) { if (code_state->state[n_state - 1 - n_pos_args - i] == MP_OBJ_NULL) { mp_map_elem_t *elem = NULL; if ((scope_flags & MP_SCOPE_FLAG_DEFKWARGS) != 0) { elem = mp_map_lookup(&((mp_obj_dict_t*)MP_OBJ_TO_PTR(self->extra_args[n_def_pos_args]))->map, arg_names[n_pos_args + i], MP_MAP_LOOKUP); } if (elem != NULL) { code_state->state[n_state - 1 - n_pos_args - i] = elem->value; } else { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "function missing required keyword argument '%q'", MP_OBJ_QSTR_VALUE(arg_names[n_pos_args + i]))); } } } } else { // no keyword arguments given if (n_kwonly_args != 0) { nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "function missing keyword-only argument")); } if ((scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) != 0) { *var_pos_kw_args = mp_obj_new_dict(0); } } // get the ip and skip argument names const byte *ip = code_state->ip; // store pointer to code_info and jump over it { code_state->code_info = ip; const byte *ip2 = ip; size_t code_info_size = mp_decode_uint(&ip2); ip += code_info_size; } // bytecode prelude: initialise closed over variables size_t local_num; while ((local_num = *ip++) != 255) { code_state->state[n_state - 1 - local_num] = mp_obj_new_cell(code_state->state[n_state - 1 - local_num]); } // now that we skipped over the prelude, set the ip for the VM code_state->ip = ip; DEBUG_printf("Calling: n_pos_args=%d, n_kwonly_args=%d\n", n_pos_args, n_kwonly_args); dump_args(code_state->state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args); dump_args(code_state->state, n_state); }
// MP_MAP_LOOKUP behaviour: // - returns NULL if not found, else the slot it was found in with key,value non-null // MP_MAP_LOOKUP_ADD_IF_NOT_FOUND behaviour: // - returns slot, with key non-null and value=MP_OBJ_NULL if it was added // MP_MAP_LOOKUP_REMOVE_IF_FOUND behaviour: // - returns NULL if not found, else the slot if was found in with key null and value non-null mp_map_elem_t *mp_map_lookup(mp_map_t *map, mp_obj_t index, mp_map_lookup_kind_t lookup_kind) { if (map->is_fixed && lookup_kind != MP_MAP_LOOKUP) { // can't add/remove from a fixed array return NULL; } // Work out if we can compare just pointers bool compare_only_ptrs = map->all_keys_are_qstrs; if (compare_only_ptrs) { if (MP_OBJ_IS_QSTR(index)) { // Index is a qstr, so can just do ptr comparison. } else if (MP_OBJ_IS_TYPE(index, &mp_type_str)) { // Index is a non-interned string. // We can either intern the string, or force a full equality comparison. // We chose the latter, since interning costs time and potentially RAM, // and it won't necessarily benefit subsequent calls because these calls // most likely won't pass the newly-interned string. compare_only_ptrs = false; } else if (lookup_kind != MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) { // If we are not adding, then we can return straight away a failed // lookup because we know that the index will never be found. return NULL; } } // if the map is an ordered array then we must do a brute force linear search if (map->is_ordered) { for (mp_map_elem_t *elem = &map->table[0], *top = &map->table[map->used]; elem < top; elem++) { if (elem->key == index || (!compare_only_ptrs && mp_obj_equal(elem->key, index))) { if (MP_UNLIKELY(lookup_kind == MP_MAP_LOOKUP_REMOVE_IF_FOUND)) { // remove the found element by moving the rest of the array down mp_obj_t value = elem->value; --map->used; memmove(elem, elem + 1, (top - elem - 1) * sizeof(*elem)); // put the found element after the end so the caller can access it if needed elem = &map->table[map->used]; elem->key = MP_OBJ_NULL; elem->value = value; } return elem; } } if (MP_LIKELY(lookup_kind != MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)) { return NULL; } if (map->used == map->alloc) { // TODO: Alloc policy map->alloc += 4; map->table = m_renew(mp_map_elem_t, map->table, map->used, map->alloc); mp_seq_clear(map->table, map->used, map->alloc, sizeof(*map->table)); } mp_map_elem_t *elem = map->table + map->used++; elem->key = index; if (!MP_OBJ_IS_QSTR(index)) { map->all_keys_are_qstrs = 0; } return elem; } // map is a hash table (not an ordered array), so do a hash lookup if (map->alloc == 0) { if (lookup_kind == MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) { mp_map_rehash(map); } else { return NULL; } } // get hash of index, with fast path for common case of qstr mp_uint_t hash; if (MP_OBJ_IS_QSTR(index)) { hash = qstr_hash(MP_OBJ_QSTR_VALUE(index)); } else { hash = MP_OBJ_SMALL_INT_VALUE(mp_unary_op(MP_UNARY_OP_HASH, index)); } size_t pos = hash % map->alloc; size_t start_pos = pos; mp_map_elem_t *avail_slot = NULL; for (;;) { mp_map_elem_t *slot = &map->table[pos]; if (slot->key == MP_OBJ_NULL) { // found NULL slot, so index is not in table if (lookup_kind == MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) { map->used += 1; if (avail_slot == NULL) { avail_slot = slot; } avail_slot->key = index; avail_slot->value = MP_OBJ_NULL; if (!MP_OBJ_IS_QSTR(index)) { map->all_keys_are_qstrs = 0; } return avail_slot; } else { return NULL; } } else if (slot->key == MP_OBJ_SENTINEL) { // found deleted slot, remember for later if (avail_slot == NULL) { avail_slot = slot; } } else if (slot->key == index || (!compare_only_ptrs && mp_obj_equal(slot->key, index))) { // found index // Note: CPython does not replace the index; try x={True:'true'};x[1]='one';x if (lookup_kind == MP_MAP_LOOKUP_REMOVE_IF_FOUND) { // delete element in this slot map->used--; if (map->table[(pos + 1) % map->alloc].key == MP_OBJ_NULL) { // optimisation if next slot is empty slot->key = MP_OBJ_NULL; } else { slot->key = MP_OBJ_SENTINEL; } // keep slot->value so that caller can access it if needed } return slot; } // not yet found, keep searching in this table pos = (pos + 1) % map->alloc; if (pos == start_pos) { // search got back to starting position, so index is not in table if (lookup_kind == MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) { if (avail_slot != NULL) { // there was an available slot, so use that map->used++; avail_slot->key = index; avail_slot->value = MP_OBJ_NULL; if (!MP_OBJ_IS_QSTR(index)) { map->all_keys_are_qstrs = 0; } return avail_slot; } else { // not enough room in table, rehash it mp_map_rehash(map); // restart the search for the new element start_pos = pos = hash % map->alloc; } } else { return NULL; } } } }