// this is a classmethod STATIC mp_obj_t dict_fromkeys(mp_uint_t n_args, const mp_obj_t *args) { assert(2 <= n_args && n_args <= 3); mp_obj_t iter = mp_getiter(args[1]); mp_obj_t len = mp_obj_len_maybe(iter); mp_obj_t value = mp_const_none; mp_obj_t next = NULL; mp_obj_t self_out; if (n_args > 2) { value = args[2]; } if (len == MP_OBJ_NULL) { /* object's type doesn't have a __len__ slot */ self_out = mp_obj_new_dict(0); } else { self_out = mp_obj_new_dict(MP_OBJ_SMALL_INT_VALUE(len)); } mp_obj_dict_t *self = MP_OBJ_CAST(self_out); while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) { mp_map_lookup(&self->map, next, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value; } return self_out; }
// this is a classmethod static mp_obj_t dict_fromkeys(uint n_args, const mp_obj_t *args) { assert(2 <= n_args && n_args <= 3); mp_obj_t iter = rt_getiter(args[1]); mp_obj_t len = mp_obj_len_maybe(iter); mp_obj_t value = mp_const_none; mp_obj_t next = NULL; mp_obj_dict_t *self = NULL; if (n_args > 2) { value = args[2]; } if (len == MP_OBJ_NULL) { /* object's type doesn't have a __len__ slot */ self = mp_obj_new_dict(0); } else { self = mp_obj_new_dict(MP_OBJ_SMALL_INT_VALUE(len)); } while ((next = rt_iternext(iter)) != mp_const_stop_iteration) { mp_map_lookup(&self->map, next, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value; } return self; }
// this is a classmethod STATIC mp_obj_t dict_fromkeys(size_t n_args, const mp_obj_t *args) { mp_obj_t iter = mp_getiter(args[1], NULL); mp_obj_t value = mp_const_none; mp_obj_t next = MP_OBJ_NULL; if (n_args > 2) { value = args[2]; } // optimisation to allocate result based on len of argument mp_obj_t self_out; mp_obj_t len = mp_obj_len_maybe(args[1]); if (len == MP_OBJ_NULL) { /* object's type doesn't have a __len__ slot */ self_out = mp_obj_new_dict(0); } else { self_out = mp_obj_new_dict(MP_OBJ_SMALL_INT_VALUE(len)); } mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_out); while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) { mp_map_lookup(&self->map, next, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value; } return self_out; }
static mp_obj_t dict_copy(mp_obj_t self_in) { assert(MP_OBJ_IS_TYPE(self_in, &dict_type)); mp_obj_dict_t *self = self_in; mp_obj_dict_t *other = mp_obj_new_dict(self->map.alloc); other->map.used = self->map.used; memcpy(other->map.table, self->map.table, self->map.alloc * sizeof(mp_map_elem_t)); return other; }
STATIC mp_obj_t dict_make_new(mp_obj_t type_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) { mp_obj_t dict = mp_obj_new_dict(0); if (n_args > 0 || n_kw > 0) { mp_obj_t args2[2] = {dict, args[0]}; // args[0] is always valid, even if it's not a positional arg mp_map_t kwargs; mp_map_init_fixed_table(&kwargs, n_kw, args + n_args); dict_update(n_args + 1, args2, &kwargs); // dict_update will check that n_args + 1 == 1 or 2 } return dict; }
STATIC mp_obj_t dict_copy(mp_obj_t self_in) { assert(MP_OBJ_IS_TYPE(self_in, &mp_type_dict)); mp_obj_dict_t *self = self_in; mp_obj_dict_t *other = mp_obj_new_dict(self->map.alloc); other->map.used = self->map.used; other->map.all_keys_are_qstrs = self->map.all_keys_are_qstrs; other->map.table_is_fixed_array = 0; memcpy(other->map.table, self->map.table, self->map.alloc * sizeof(mp_map_elem_t)); return other; }
STATIC mp_obj_t dict_copy(mp_obj_t self_in) { mp_check_self(mp_obj_is_dict_type(self_in)); mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_in); mp_obj_t other_out = mp_obj_new_dict(self->map.alloc); mp_obj_dict_t *other = MP_OBJ_TO_PTR(other_out); other->base.type = self->base.type; other->map.used = self->map.used; other->map.all_keys_are_qstrs = self->map.all_keys_are_qstrs; other->map.is_fixed = 0; other->map.is_ordered = self->map.is_ordered; memcpy(other->map.table, self->map.table, self->map.alloc * sizeof(mp_map_elem_t)); return other_out; }
STATIC mp_obj_t dict_copy(mp_obj_t self_in) { assert(MP_OBJ_IS_DICT_TYPE(self_in)); mp_obj_dict_t *self = MP_OBJ_CAST(self_in); mp_obj_t other_out = mp_obj_new_dict(self->map.alloc); mp_obj_dict_t *other = MP_OBJ_CAST(other_out); other->base.type = self->base.type; other->map.used = self->map.used; other->map.all_keys_are_qstrs = self->map.all_keys_are_qstrs; other->map.is_fixed = 0; other->map.is_ordered = self->map.is_ordered; memcpy(other->map.table, self->map.table, self->map.alloc * sizeof(mp_map_elem_t)); return other_out; }
STATIC mp_obj_t namedtuple_asdict(mp_obj_t self_in) { mp_obj_namedtuple_t *self = MP_OBJ_TO_PTR(self_in); const qstr *fields = ((mp_obj_namedtuple_type_t*)self->tuple.base.type)->fields; mp_obj_t dict = mp_obj_new_dict(self->tuple.len); //make it an OrderedDict mp_obj_dict_t *dictObj = MP_OBJ_TO_PTR(dict); dictObj->base.type = &mp_type_ordereddict; dictObj->map.is_ordered = 1; for (size_t i = 0; i < self->tuple.len; ++i) { mp_obj_dict_store(dict, MP_OBJ_NEW_QSTR(fields[i]), self->tuple.items[i]); } return dict; }
STATIC mp_obj_t dict_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) { mp_obj_t dict; switch (n_args) { case 0: dict = mp_obj_new_dict(0); break; case 1: { if (MP_OBJ_IS_TYPE(args[0], &mp_type_dict)) { return dict_copy(args[0]); } // TODO create dict from an arbitrary mapping! // Make dict from iterable of pairs mp_obj_t iterable = mp_getiter(args[0]); mp_obj_t dict = mp_obj_new_dict(0); // TODO: support arbitrary seq as a pair mp_obj_t item; while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { mp_obj_t *sub_items; mp_obj_get_array_fixed_n(item, 2, &sub_items); mp_obj_dict_store(dict, sub_items[0], sub_items[1]); } return dict; } default: nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "dict takes at most 1 argument")); } // add to the new dict any keyword args for (const mp_obj_t *a = args + n_args; n_kw > 0; n_kw--, a += 2) { mp_obj_dict_store(dict, a[0], a[1]); } return dict; }
static mp_obj_t pin_map_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *args) { pin_map_obj_t *self = self_in; mp_check_nargs(n_args, 1, 2, n_kw, false); if (n_args > 1) { if (!self->map_dict) { self->map_dict = mp_obj_new_dict(1); } mp_obj_dict_store(self->map_dict, args[0], args[1]); return mp_const_none; } // Run an argument through the mapper and return the result. return (mp_obj_t)pin_map_user_obj(args[0]); }
STATIC mp_obj_t dict_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_obj_t dict_out = mp_obj_new_dict(0); mp_obj_dict_t *dict = MP_OBJ_TO_PTR(dict_out); dict->base.type = type; #if MICROPY_PY_COLLECTIONS_ORDEREDDICT if (type == &mp_type_ordereddict) { dict->map.is_ordered = 1; } #endif if (n_args > 0 || n_kw > 0) { mp_obj_t args2[2] = {dict_out, args[0]}; // args[0] is always valid, even if it's not a positional arg mp_map_t kwargs; mp_map_init_fixed_table(&kwargs, n_kw, args + n_args); dict_update(n_args + 1, args2, &kwargs); // dict_update will check that n_args + 1 == 1 or 2 } return dict_out; }
// args[0] is function from class body // args[1] is class name // args[2:] are base objects STATIC mp_obj_t mp_builtin___build_class__(size_t n_args, const mp_obj_t *args) { assert(2 <= n_args); // set the new classes __locals__ object mp_obj_dict_t *old_locals = mp_locals_get(); mp_obj_t class_locals = mp_obj_new_dict(0); mp_locals_set(MP_OBJ_TO_PTR(class_locals)); // call the class code mp_obj_t cell = mp_call_function_0(args[0]); // restore old __locals__ object mp_locals_set(old_locals); // get the class type (meta object) from the base objects mp_obj_t meta; if (n_args == 2) { // no explicit bases, so use 'type' meta = MP_OBJ_FROM_PTR(&mp_type_type); } else { // use type of first base object meta = MP_OBJ_FROM_PTR(mp_obj_get_type(args[2])); } // TODO do proper metaclass resolution for multiple base objects // create the new class using a call to the meta object mp_obj_t meta_args[3]; meta_args[0] = args[1]; // class name meta_args[1] = mp_obj_new_tuple(n_args - 2, args + 2); // tuple of bases meta_args[2] = class_locals; // dict of members mp_obj_t new_class = mp_call_function_n_kw(meta, 3, 0, meta_args); // store into cell if neede if (cell != mp_const_none) { mp_obj_cell_set(cell, new_class); } return new_class; }
mp_obj_t mp_obj_new_module(qstr module_name) { mp_map_elem_t *el = mp_map_lookup(&MP_STATE_VM(mp_loaded_modules_map), MP_OBJ_NEW_QSTR(module_name), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); // We could error out if module already exists, but let C extensions // add new members to existing modules. if (el->value != MP_OBJ_NULL) { return el->value; } // create new module object mp_obj_module_t *o = m_new_obj(mp_obj_module_t); o->base.type = &mp_type_module; o->name = module_name; o->globals = MP_OBJ_TO_PTR(mp_obj_new_dict(MICROPY_MODULE_DICT_SIZE)); // store __name__ entry in the module mp_obj_dict_store(MP_OBJ_FROM_PTR(o->globals), MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(module_name)); // store the new module into the slot in the global dict holding all modules el->value = MP_OBJ_FROM_PTR(o); // return the new module return MP_OBJ_FROM_PTR(o); }
STATIC void module_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { mp_obj_module_t *self = MP_OBJ_TO_PTR(self_in); if (dest[0] == MP_OBJ_NULL) { // load attribute mp_map_elem_t *elem = mp_map_lookup(&self->globals->map, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP); if (elem != NULL) { dest[0] = elem->value; } } else { // delete/store attribute mp_obj_dict_t *dict = self->globals; if (dict->map.is_fixed) { #if MICROPY_CAN_OVERRIDE_BUILTINS if (dict == &mp_module_builtins_globals) { if (MP_STATE_VM(mp_module_builtins_override_dict) == NULL) { MP_STATE_VM(mp_module_builtins_override_dict) = MP_OBJ_TO_PTR(mp_obj_new_dict(1)); } dict = MP_STATE_VM(mp_module_builtins_override_dict); } else #endif { // can't delete or store to fixed map return; } } if (dest[1] == MP_OBJ_NULL) { // delete attribute mp_obj_dict_delete(MP_OBJ_FROM_PTR(dict), MP_OBJ_NEW_QSTR(attr)); } else { // store attribute // TODO CPython allows STORE_ATTR to a module, but is this the correct implementation? mp_obj_dict_store(MP_OBJ_FROM_PTR(dict), MP_OBJ_NEW_QSTR(attr), dest[1]); } dest[0] = MP_OBJ_NULL; // indicate success } }
int main(int argc, char **argv) { volatile int stack_dummy; stack_top = (void*)&stack_dummy; pre_process_options(argc, argv); #if MICROPY_ENABLE_GC char *heap = malloc(heap_size); gc_init(heap, heap + heap_size); #endif qstr_init(); mp_init(); char *home = getenv("HOME"); char *path = getenv("MICROPYPATH"); if (path == NULL) { path = "~/.micropython/lib:/usr/lib/micropython"; } uint path_num = 1; // [0] is for current dir (or base dir of the script) for (char *p = path; p != NULL; p = strchr(p, ':')) { path_num++; if (p != NULL) { p++; } } mp_obj_list_init(mp_sys_path, path_num); mp_obj_t *path_items; mp_obj_list_get(mp_sys_path, &path_num, &path_items); path_items[0] = MP_OBJ_NEW_QSTR(MP_QSTR_); char *p = path; for (int i = 1; i < path_num; i++) { char *p1 = strchr(p, ':'); if (p1 == NULL) { p1 = p + strlen(p); } if (p[0] == '~' && p[1] == '/' && home != NULL) { // Expand standalone ~ to $HOME CHECKBUF(buf, PATH_MAX); CHECKBUF_APPEND(buf, home, strlen(home)); CHECKBUF_APPEND(buf, p + 1, p1 - p - 1); path_items[i] = MP_OBJ_NEW_QSTR(qstr_from_strn(buf, CHECKBUF_LEN(buf))); } else { path_items[i] = MP_OBJ_NEW_QSTR(qstr_from_strn(p, p1 - p)); } p = p1 + 1; } mp_obj_list_init(mp_sys_argv, 0); mp_store_name(qstr_from_str("test"), test_obj_new(42)); mp_store_name(qstr_from_str("mem_info"), mp_make_function_n(0, mem_info)); mp_store_name(qstr_from_str("qstr_info"), mp_make_function_n(0, qstr_info)); #if MICROPY_ENABLE_GC mp_store_name(qstr_from_str("gc"), (mp_obj_t)&pyb_gc_obj); #endif // Here is some example code to create a class and instance of that class. // First is the Python, then the C code. // // class TestClass: // pass // test_obj = TestClass() // test_obj.attr = 42 mp_obj_t test_class_type, test_class_instance; test_class_type = mp_obj_new_type(QSTR_FROM_STR_STATIC("TestClass"), mp_const_empty_tuple, mp_obj_new_dict(0)); mp_store_name(QSTR_FROM_STR_STATIC("test_obj"), test_class_instance = mp_call_function_0(test_class_type)); mp_store_attr(test_class_instance, QSTR_FROM_STR_STATIC("attr"), mp_obj_new_int(42)); /* printf("bytes:\n"); printf(" total %d\n", m_get_total_bytes_allocated()); printf(" cur %d\n", m_get_current_bytes_allocated()); printf(" peak %d\n", m_get_peak_bytes_allocated()); */ bool executed = false; for (int a = 1; a < argc; a++) { if (argv[a][0] == '-') { if (strcmp(argv[a], "-c") == 0) { if (a + 1 >= argc) { return usage(argv); } do_str(argv[a + 1]); executed = true; a += 1; } else if (strcmp(argv[a], "-X") == 0) { a += 1; } else { return usage(argv); } } else { char *basedir = realpath(argv[a], NULL); if (basedir == NULL) { fprintf(stderr, "%s: can't open file '%s': [Errno %d] ", argv[0], argv[1], errno); perror(""); // CPython exits with 2 in such case exit(2); } // Set base dir of the script as first entry in sys.path char *p = strrchr(basedir, '/'); path_items[0] = MP_OBJ_NEW_QSTR(qstr_from_strn(basedir, p - basedir)); free(basedir); for (int i = a; i < argc; i++) { mp_obj_list_append(mp_sys_argv, MP_OBJ_NEW_QSTR(qstr_from_str(argv[i]))); } do_file(argv[a]); executed = true; break; } } if (!executed) { do_repl(); } mp_deinit(); //printf("total bytes = %d\n", m_get_total_bytes_allocated()); return 0; }
// This function implements a simple non-recursive JSON parser. // // The JSON specification is at http://www.ietf.org/rfc/rfc4627.txt // The parser here will parse any valid JSON and return the correct // corresponding Python object. It allows through a superset of JSON, since // it treats commas and colons as "whitespace", and doesn't care if // brackets/braces are correctly paired. It will raise a ValueError if the // input is outside it's specs. // // Most of the work is parsing the primitives (null, false, true, numbers, // strings). It does 1 pass over the input string and so is easily extended to // being able to parse from a non-seekable stream. It tries to be fast and // small in code size, while not using more RAM than necessary. STATIC mp_obj_t mod_ujson_loads(mp_obj_t obj) { mp_uint_t len; const char *s = mp_obj_str_get_data(obj, &len); const char *top = s + len; vstr_t vstr; vstr_init(&vstr, 8); mp_obj_list_t stack; // we use a list as a simple stack for nested JSON stack.len = 0; stack.items = NULL; mp_obj_t stack_top = MP_OBJ_NULL; mp_obj_type_t *stack_top_type = NULL; mp_obj_t stack_key = MP_OBJ_NULL; for (;;) { cont: if (s == top) { break; } mp_obj_t next = MP_OBJ_NULL; bool enter = false; switch (*s) { case ',': case ':': case ' ': case '\t': case '\n': case '\r': s += 1; goto cont; case 'n': if (s + 3 < top && s[1] == 'u' && s[2] == 'l' && s[3] == 'l') { s += 4; next = mp_const_none; } else { goto fail; } break; case 'f': if (s + 4 < top && s[1] == 'a' && s[2] == 'l' && s[3] == 's' && s[4] == 'e') { s += 5; next = mp_const_false; } else { goto fail; } break; case 't': if (s + 3 < top && s[1] == 'r' && s[2] == 'u' && s[3] == 'e') { s += 4; next = mp_const_true; } else { goto fail; } break; case '"': vstr_reset(&vstr); for (s++; s < top && *s != '"';) { byte c = *s; if (c == '\\') { s++; c = *s; switch (c) { case 'b': c = 0x08; break; case 'f': c = 0x0c; break; case 'n': c = 0x0a; break; case 'r': c = 0x0d; break; case 't': c = 0x09; break; case 'u': { if (s + 4 >= top) { goto fail; } mp_uint_t num = 0; for (int i = 0; i < 4; i++) { c = (*++s | 0x20) - '0'; if (c > 9) { c -= ('a' - ('9' + 1)); } num = (num << 4) | c; } vstr_add_char(&vstr, num); goto str_cont; } } } vstr_add_byte(&vstr, c); str_cont: s++; } if (s == top) { goto fail; } s++; next = mp_obj_new_str(vstr.buf, vstr.len, false); break; case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { bool flt = false; vstr_reset(&vstr); for (; s < top; s++) { if (*s == '.' || *s == 'E' || *s == 'e') { flt = true; } else if (*s == '-' || unichar_isdigit(*s)) { // pass } else { break; } vstr_add_byte(&vstr, *s); } if (flt) { next = mp_parse_num_decimal(vstr.buf, vstr.len, false, false, NULL); } else { next = mp_parse_num_integer(vstr.buf, vstr.len, 10, NULL); } break; } case '[': next = mp_obj_new_list(0, NULL); enter = true; s += 1; break; case '{': next = mp_obj_new_dict(0); enter = true; s += 1; break; case '}': case ']': { s += 1; if (stack_top == MP_OBJ_NULL) { // no object at all goto fail; } if (stack.len == 0) { // finished; compound object goto success; } stack.len -= 1; stack_top = stack.items[stack.len]; stack_top_type = mp_obj_get_type(stack_top); goto cont; } default: goto fail; } if (stack_top == MP_OBJ_NULL) { stack_top = next; stack_top_type = mp_obj_get_type(stack_top); if (!enter) { // finished; single primitive only goto success; } } else { // append to list or dict if (stack_top_type == &mp_type_list) { mp_obj_list_append(stack_top, next); } else { if (stack_key == MP_OBJ_NULL) { stack_key = next; if (enter) { goto fail; } } else { mp_obj_dict_store(stack_top, stack_key, next); stack_key = MP_OBJ_NULL; } } if (enter) { if (stack.items == NULL) { mp_obj_list_init(&stack, 1); stack.items[0] = stack_top; } else { mp_obj_list_append(MP_OBJ_FROM_PTR(&stack), stack_top); } stack_top = next; stack_top_type = mp_obj_get_type(stack_top); } } } success: // eat trailing whitespace while (s < top && unichar_isspace(*s)) { s++; } if (s < top) { // unexpected chars goto fail; } if (stack_top == MP_OBJ_NULL || stack.len != 0) { // not exactly 1 object goto fail; } vstr_clear(&vstr); return stack_top; fail: nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "syntax error in JSON")); }
STATIC mp_obj_t mod_ujson_load(mp_obj_t stream_obj) { const mp_stream_p_t *stream_p = mp_get_stream_raise(stream_obj, MP_STREAM_OP_READ); ujson_stream_t s = {stream_obj, stream_p->read, 0, 0}; vstr_t vstr; vstr_init(&vstr, 8); mp_obj_list_t stack; // we use a list as a simple stack for nested JSON stack.len = 0; stack.items = NULL; mp_obj_t stack_top = MP_OBJ_NULL; mp_obj_type_t *stack_top_type = NULL; mp_obj_t stack_key = MP_OBJ_NULL; S_NEXT(s); for (;;) { cont: if (S_END(s)) { break; } mp_obj_t next = MP_OBJ_NULL; bool enter = false; byte cur = S_CUR(s); S_NEXT(s); switch (cur) { case ',': case ':': case ' ': case '\t': case '\n': case '\r': goto cont; case 'n': if (S_CUR(s) == 'u' && S_NEXT(s) == 'l' && S_NEXT(s) == 'l') { S_NEXT(s); next = mp_const_none; } else { goto fail; } break; case 'f': if (S_CUR(s) == 'a' && S_NEXT(s) == 'l' && S_NEXT(s) == 's' && S_NEXT(s) == 'e') { S_NEXT(s); next = mp_const_false; } else { goto fail; } break; case 't': if (S_CUR(s) == 'r' && S_NEXT(s) == 'u' && S_NEXT(s) == 'e') { S_NEXT(s); next = mp_const_true; } else { goto fail; } break; case '"': vstr_reset(&vstr); for (; !S_END(s) && S_CUR(s) != '"';) { byte c = S_CUR(s); if (c == '\\') { c = S_NEXT(s); switch (c) { case 'b': c = 0x08; break; case 'f': c = 0x0c; break; case 'n': c = 0x0a; break; case 'r': c = 0x0d; break; case 't': c = 0x09; break; case 'u': { mp_uint_t num = 0; for (int i = 0; i < 4; i++) { c = (S_NEXT(s) | 0x20) - '0'; if (c > 9) { c -= ('a' - ('9' + 1)); } num = (num << 4) | c; } vstr_add_char(&vstr, num); goto str_cont; } } } vstr_add_byte(&vstr, c); str_cont: S_NEXT(s); } if (S_END(s)) { goto fail; } S_NEXT(s); next = mp_obj_new_str(vstr.buf, vstr.len, false); break; case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { bool flt = false; vstr_reset(&vstr); for (;;) { vstr_add_byte(&vstr, cur); cur = S_CUR(s); if (cur == '.' || cur == 'E' || cur == 'e') { flt = true; } else if (cur == '-' || unichar_isdigit(cur)) { // pass } else { break; } S_NEXT(s); } if (flt) { next = mp_parse_num_decimal(vstr.buf, vstr.len, false, false, NULL); } else { next = mp_parse_num_integer(vstr.buf, vstr.len, 10, NULL); } break; } case '[': next = mp_obj_new_list(0, NULL); enter = true; break; case '{': next = mp_obj_new_dict(0); enter = true; break; case '}': case ']': { if (stack_top == MP_OBJ_NULL) { // no object at all goto fail; } if (stack.len == 0) { // finished; compound object goto success; } stack.len -= 1; stack_top = stack.items[stack.len]; stack_top_type = mp_obj_get_type(stack_top); goto cont; } default: goto fail; } if (stack_top == MP_OBJ_NULL) { stack_top = next; stack_top_type = mp_obj_get_type(stack_top); if (!enter) { // finished; single primitive only goto success; } } else { // append to list or dict if (stack_top_type == &mp_type_list) { mp_obj_list_append(stack_top, next); } else { if (stack_key == MP_OBJ_NULL) { stack_key = next; if (enter) { goto fail; } } else { mp_obj_dict_store(stack_top, stack_key, next); stack_key = MP_OBJ_NULL; } } if (enter) { if (stack.items == NULL) { mp_obj_list_init(&stack, 1); stack.items[0] = stack_top; } else { mp_obj_list_append(MP_OBJ_FROM_PTR(&stack), stack_top); } stack_top = next; stack_top_type = mp_obj_get_type(stack_top); } } } success: // eat trailing whitespace while (unichar_isspace(S_CUR(s))) { S_NEXT(s); } if (!S_END(s)) { // unexpected chars goto fail; } if (stack_top == MP_OBJ_NULL || stack.len != 0) { // not exactly 1 object goto fail; } vstr_clear(&vstr); return stack_top; fail: nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "syntax error in JSON")); }
// 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); }
// 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); }
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 dict_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) { // TODO create from an iterable! return mp_obj_new_dict(0); }
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)); }