// args are in reverse order in the array mp_obj_t fun_bc_call_n(mp_obj_t self_in, int n_args, const mp_obj_t *args) { mp_obj_fun_bc_t *self = self_in; if (n_args != self->n_args) { nlr_jump(mp_obj_new_exception_msg_2_args(rt_q_TypeError, "function takes %d positional arguments but %d were given", (const char*)(machine_int_t)self->n_args, (const char*)(machine_int_t)n_args)); } // optimisation: allow the compiler to optimise this tail call for // the common case when the globals don't need to be changed mp_map_t *old_globals = rt_globals_get(); if (self->globals == old_globals) { return mp_execute_byte_code(self->bytecode, args, n_args, self->n_state); } else { rt_globals_set(self->globals); mp_obj_t result = mp_execute_byte_code(self->bytecode, args, n_args, self->n_state); rt_globals_set(old_globals); return result; } }
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)); }