diff options
Diffstat (limited to 'py/objfun.c')
-rw-r--r-- | py/objfun.c | 77 |
1 files changed, 51 insertions, 26 deletions
diff --git a/py/objfun.c b/py/objfun.c index 940b64a66e..a5266eb184 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -152,17 +152,18 @@ bool mp_obj_fun_prepare_simple_args(mp_obj_t self_in, uint n_args, uint n_kw, co mp_obj_fun_bc_t *self = self_in; assert(n_kw == 0); + assert(self->n_kwonly_args == 0); assert(self->takes_var_args == 0); assert(self->takes_kw_args == 0); mp_obj_t *extra_args = self->extra_args + self->n_def_args; uint n_extra_args = 0; - if (n_args > self->n_args) { - goto arg_error; + if (n_args > self->n_pos_args) { + goto arg_error; } else { - extra_args -= self->n_args - n_args; - n_extra_args += self->n_args - n_args; + extra_args -= self->n_pos_args - n_args; + n_extra_args += self->n_pos_args - n_args; } *out_args1 = args; *out_args1_len = n_args; @@ -171,10 +172,15 @@ bool mp_obj_fun_prepare_simple_args(mp_obj_t self_in, uint n_args, uint n_kw, co return true; arg_error: - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "function takes %d positional arguments but %d were given", self->n_args, n_args)); + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "function takes %d positional arguments but %d were given", self->n_pos_args, n_args)); } 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. + // + // extra_args layout: def_args, var_arg tuple, kwonly args, var_kw dict + DEBUG_printf("Input n_args: %d, n_kw: %d\n", n_args, n_kw); DEBUG_printf("Input pos args: "); dump_args(args, n_args); @@ -187,19 +193,18 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_o 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) { + if (n_args > self->n_pos_args) { // given more than enough arguments if (!self->takes_var_args) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, - "function takes %d positional arguments but %d were given", self->n_args, n_args)); + "function takes %d positional arguments but %d were given", self->n_pos_args, n_args)); } // put extra arguments in varargs tuple - *extra_args = mp_obj_new_tuple(n_args - self->n_args, args + self->n_args); + *extra_args = mp_obj_new_tuple(n_args - self->n_pos_args, args + self->n_pos_args); n_extra_args = 1; - n_args = self->n_args; + n_args = self->n_pos_args; } else { if (self->takes_var_args) { DEBUG_printf("passing empty tuple as *args\n"); @@ -209,14 +214,14 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_o // 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) { + if (n_args >= self->n_pos_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; + extra_args -= self->n_pos_args - n_args; + n_extra_args += self->n_pos_args - n_args; } else { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "function takes at least %d positional arguments but %d were given", - self->n_args - self->n_def_args, n_args)); + self->n_pos_args - self->n_def_args, n_args)); } } } @@ -229,13 +234,13 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_o // 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--) { + mp_obj_t *flat_args = alloca((self->n_pos_args + self->n_kwonly_args) * sizeof(mp_obj_t)); + for (int i = self->n_pos_args + self->n_kwonly_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); + dump_args(flat_args, self->n_pos_args + self->n_kwonly_args); mp_obj_t dict = MP_OBJ_NULL; if (self->takes_kw_args) { @@ -243,7 +248,7 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_o } 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++) { + for (uint j = 0; j < self->n_pos_args + self->n_kwonly_args; j++) { if (arg_name == self->args[j]) { if (flat_args[j] != MP_OBJ_NULL) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, @@ -261,10 +266,10 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_o continue2:; } DEBUG_printf("Args with kws flattened: "); - dump_args(flat_args, self->n_args); + dump_args(flat_args, self->n_pos_args + self->n_kwonly_args); - // Now fill in defaults - mp_obj_t *d = &flat_args[self->n_args - 1]; + // Now fill in defaults for positional args + mp_obj_t *d = &flat_args[self->n_pos_args - 1]; 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) { @@ -272,9 +277,9 @@ continue2:; } } DEBUG_printf("Args after filling defaults: "); - dump_args(flat_args, self->n_args); + dump_args(flat_args, self->n_pos_args + self->n_kwonly_args); - // Now check that all mandatory args specified + // Check that all mandatory positional args are specified while (d >= flat_args) { if (*d-- == MP_OBJ_NULL) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, @@ -282,8 +287,16 @@ continue2:; } } + // Check that all mandatory keyword args are specified + for (int i = 0; i < self->n_kwonly_args; i++) { + if (flat_args[self->n_pos_args + i] == MP_OBJ_NULL) { + 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]))); + } + } + args = flat_args; - n_args = self->n_args; + n_args = self->n_pos_args + self->n_kwonly_args; if (self->takes_kw_args) { extra_args[n_extra_args] = dict; @@ -291,6 +304,10 @@ continue2:; } } 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) { extra_args[n_extra_args] = mp_obj_new_dict(0); n_extra_args += 1; @@ -320,7 +337,7 @@ const mp_obj_type_t mp_type_fun_bc = { .binary_op = fun_binary_op, }; -mp_obj_t mp_obj_new_fun_bc(uint scope_flags, qstr *args, uint n_args, mp_obj_t def_args_in, const byte *code) { +mp_obj_t mp_obj_new_fun_bc(uint scope_flags, qstr *args, uint n_pos_args, uint n_kwonly_args, mp_obj_t def_args_in, const byte *code) { uint n_def_args = 0; uint n_extra_args = 0; mp_obj_tuple_t *def_args = def_args_in; @@ -339,14 +356,22 @@ mp_obj_t mp_obj_new_fun_bc(uint scope_flags, qstr *args, uint n_args, mp_obj_t d o->base.type = &mp_type_fun_bc; o->globals = mp_globals_get(); o->args = args; - o->n_args = n_args; + o->n_pos_args = n_pos_args; + o->n_kwonly_args = n_kwonly_args; o->n_def_args = n_def_args; o->takes_var_args = (scope_flags & MP_SCOPE_FLAG_VARARGS) != 0; o->takes_kw_args = (scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) != 0; o->bytecode = code; + memset(o->extra_args, 0, n_extra_args * sizeof(mp_obj_t)); if (def_args != MP_OBJ_NULL) { memcpy(o->extra_args, def_args->items, n_def_args * sizeof(mp_obj_t)); } + if ((scope_flags & MP_SCOPE_FLAG_VARARGS) != 0) { + o->extra_args[n_def_args] = MP_OBJ_NULL; + } + if ((scope_flags & MP_SCOPE_FLAG_VARARGS) != 0) { + o->extra_args[n_extra_args - 1] = MP_OBJ_NULL; + } return o; } |