summaryrefslogtreecommitdiffstatshomepage
path: root/py/objfun.c
diff options
context:
space:
mode:
Diffstat (limited to 'py/objfun.c')
-rw-r--r--py/objfun.c77
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;
}