diff options
-rw-r--r-- | py/bc.h | 3 | ||||
-rw-r--r-- | py/builtin.c | 9 | ||||
-rw-r--r-- | py/compile.c | 12 | ||||
-rw-r--r-- | py/emitglue.c | 6 | ||||
-rw-r--r-- | py/modsys.c | 6 | ||||
-rw-r--r-- | py/obj.h | 3 | ||||
-rw-r--r-- | py/objfun.c | 219 | ||||
-rw-r--r-- | py/objfun.h | 7 | ||||
-rw-r--r-- | py/objgenerator.c | 4 | ||||
-rw-r--r-- | py/objstr.c | 5 | ||||
-rw-r--r-- | py/objtype.c | 6 | ||||
-rwxr-xr-x | py/py-version.sh | 2 | ||||
-rw-r--r-- | py/qstrdefs.h | 4 | ||||
-rw-r--r-- | py/runtime.c | 4 | ||||
-rw-r--r-- | py/runtime.h | 3 | ||||
-rw-r--r-- | py/vm.c | 120 | ||||
-rw-r--r-- | tests/basics/fun-defargs.py | 2 | ||||
-rw-r--r-- | tests/basics/fun-kwonly.py | 9 | ||||
-rw-r--r-- | tests/basics/fun-kwonlydef.py | 36 | ||||
-rw-r--r-- | tests/basics/getattr1.py | 1 | ||||
-rw-r--r-- | unix/Makefile | 5 | ||||
-rw-r--r-- | unix/modos.c | 2 | ||||
-rw-r--r-- | unix/modtime.c | 6 | ||||
-rw-r--r-- | unix/mpconfigport.h | 1 |
24 files changed, 258 insertions, 217 deletions
@@ -49,8 +49,7 @@ typedef struct _mp_code_state { //mp_exc_stack_t exc_state[0]; } mp_code_state; -mp_vm_return_kind_t mp_execute_bytecode(const byte *code, const mp_obj_t *args, uint n_args, const mp_obj_t *args2, uint n_args2, mp_obj_t *ret); -mp_vm_return_kind_t mp_execute_bytecode2(mp_code_state *code_state, volatile mp_obj_t inject_exc); +mp_vm_return_kind_t mp_execute_bytecode(mp_code_state *code_state, volatile mp_obj_t inject_exc); void mp_bytecode_print(const void *descr, const byte *code, int len); void mp_bytecode_print2(const byte *code, int len); diff --git a/py/builtin.c b/py/builtin.c index 8621b0b003..834108f1b5 100644 --- a/py/builtin.c +++ b/py/builtin.c @@ -452,12 +452,17 @@ STATIC inline mp_obj_t mp_load_attr_default(mp_obj_t base, qstr attr, mp_obj_t d } 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 attr = args[1]; + if (MP_OBJ_IS_TYPE(attr, &mp_type_str)) { + attr = mp_obj_str_intern(attr); + } else if (!MP_OBJ_IS_QSTR(attr)) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "string required")); + } 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); + return mp_load_attr_default(args[0], MP_OBJ_QSTR_VALUE(attr), defval); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_getattr_obj, 2, 3, mp_builtin_getattr); diff --git a/py/compile.c b/py/compile.c index 1f0d90570e..946c8924b2 100644 --- a/py/compile.c +++ b/py/compile.c @@ -1035,7 +1035,10 @@ void compile_funcdef_param(compiler_t *comp, mp_parse_node_t pn) { if (comp->have_star) { comp->num_dict_params += 1; -#if !MICROPY_EMIT_CPYTHON +#if MICROPY_EMIT_CPYTHON + EMIT_ARG(load_const_str, MP_PARSE_NODE_LEAF_ARG(pn_id), false); + compile_node(comp, pn_equal); +#else // in Micro Python we put the default dict parameters into a dictionary using the bytecode if (comp->num_dict_params == 1) { // in Micro Python we put the default positional parameters into a tuple using the bytecode @@ -1048,11 +1051,10 @@ void compile_funcdef_param(compiler_t *comp, mp_parse_node_t pn) { // first default dict param, so make the map EMIT_ARG(build_map, 0); } -#endif - EMIT_ARG(load_const_str, MP_PARSE_NODE_LEAF_ARG(pn_id), false); + + // compile value then key, then store it to the dict compile_node(comp, pn_equal); -#if !MICROPY_EMIT_CPYTHON - // in Micro Python we put the default dict parameters into a dictionary using the bytecode + EMIT_ARG(load_const_str, MP_PARSE_NODE_LEAF_ARG(pn_id), false); EMIT(store_map); #endif } else { diff --git a/py/emitglue.c b/py/emitglue.c index a89ef6766a..f9b9460837 100644 --- a/py/emitglue.c +++ b/py/emitglue.c @@ -119,14 +119,14 @@ mp_obj_t mp_make_function_from_raw_code(mp_raw_code_t *rc, mp_obj_t def_args, mp // def_args must be MP_OBJ_NULL or a tuple assert(def_args == MP_OBJ_NULL || MP_OBJ_IS_TYPE(def_args, &mp_type_tuple)); - // TODO implement default kw args - assert(def_kw_args == MP_OBJ_NULL); + // def_kw_args must be MP_OBJ_NULL or a dict + assert(def_kw_args == MP_OBJ_NULL || MP_OBJ_IS_TYPE(def_kw_args, &mp_type_dict)); // make the function, depending on the raw code kind mp_obj_t fun; switch (rc->kind) { case MP_CODE_BYTECODE: - fun = mp_obj_new_fun_bc(rc->scope_flags, rc->arg_names, rc->n_pos_args, rc->n_kwonly_args, def_args, rc->u_byte.code); + fun = mp_obj_new_fun_bc(rc->scope_flags, rc->arg_names, rc->n_pos_args, rc->n_kwonly_args, def_args, def_kw_args, rc->u_byte.code); break; case MP_CODE_NATIVE_PY: fun = mp_make_function_n(rc->n_pos_args, rc->u_native.fun); diff --git a/py/modsys.c b/py/modsys.c index 738758bdf4..a99db1b7f8 100644 --- a/py/modsys.c +++ b/py/modsys.c @@ -51,6 +51,9 @@ mp_obj_list_t mp_sys_argv_obj; STATIC const mp_obj_tuple_t mp_sys_version_info_obj = {{&mp_type_tuple}, 3, {I(3), I(4), I(0)}}; #undef I STATIC const MP_DEFINE_STR_OBJ(version_obj, "3.4.0"); +#ifdef MICROPY_PY_SYS_PLATFORM +STATIC const MP_DEFINE_STR_OBJ(platform_obj, MICROPY_PY_SYS_PLATFORM); +#endif STATIC const mp_map_elem_t mp_module_sys_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_sys) }, @@ -59,6 +62,9 @@ STATIC const mp_map_elem_t mp_module_sys_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_argv), (mp_obj_t)&mp_sys_argv_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_version), (mp_obj_t)&version_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_version_info), (mp_obj_t)&mp_sys_version_info_obj }, +#ifdef MICROPY_PY_SYS_PLATFORM + { MP_OBJ_NEW_QSTR(MP_QSTR_platform), (mp_obj_t)&platform_obj }, +#endif #if MP_ENDIANNESS_LITTLE { MP_OBJ_NEW_QSTR(MP_QSTR_byteorder), MP_OBJ_NEW_QSTR(MP_QSTR_little) }, #else @@ -380,7 +380,7 @@ mp_obj_t mp_obj_new_exception_arg1(const mp_obj_type_t *exc_type, mp_obj_t arg); mp_obj_t mp_obj_new_exception_args(const mp_obj_type_t *exc_type, uint n_args, const mp_obj_t *args); mp_obj_t mp_obj_new_exception_msg(const mp_obj_type_t *exc_type, const char *msg); mp_obj_t mp_obj_new_exception_msg_varg(const mp_obj_type_t *exc_type, const char *fmt, ...); // counts args by number of % symbols in fmt, excluding %%; can only handle void* sizes (ie no float/double!) -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, 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, mp_obj_t def_kw_args, const byte *code); mp_obj_t mp_obj_new_fun_asm(uint n_args, void *fun); mp_obj_t mp_obj_new_gen_wrap(mp_obj_t fun); mp_obj_t mp_obj_new_closure(mp_obj_t fun, uint n_closed, const mp_obj_t *closed); @@ -468,6 +468,7 @@ uint mp_obj_str_get_len(mp_obj_t self_in); qstr mp_obj_str_get_qstr(mp_obj_t self_in); // use this if you will anyway convert the string to a qstr const char *mp_obj_str_get_str(mp_obj_t self_in); // use this only if you need the string to be null terminated const char *mp_obj_str_get_data(mp_obj_t self_in, uint *len); +mp_obj_t mp_obj_str_intern(mp_obj_t str); void mp_str_print_quoted(void (*print)(void *env, const char *fmt, ...), void *env, const byte *str_data, uint str_len); #if MICROPY_PY_BUILTINS_FLOAT diff --git a/py/objfun.c b/py/objfun.c index 5c085f883c..d63acc687b 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -236,11 +236,17 @@ arg_error: fun_pos_args_mismatch(self, self->n_pos_args, n_args); } +// With this macro you can tune the maximum number of function state bytes +// that will be allocated on the stack. Any function that needs more +// than this will use the heap. +#define VM_MAX_STATE_ON_STACK (10 * sizeof(machine_uint_t)) + +// Set this to enable a simple stack overflow check. +#define VM_DETECT_STACK_OVERFLOW (0) + 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: "); @@ -250,9 +256,42 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_o 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; - mp_obj_t *extra_args = self->extra_args + self->n_def_args; - uint n_extra_args = 0; + + // 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 @@ -262,57 +301,53 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_o fun_pos_args_mismatch(self, self->n_pos_args, n_args); } // put extra arguments in varargs tuple - *extra_args = mp_obj_new_tuple(n_args - self->n_pos_args, args + self->n_pos_args); - n_extra_args = 1; + *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"); - *extra_args = mp_const_empty_tuple; - n_extra_args = 1; + *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) { + 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 - extra_args -= self->n_pos_args - n_args; - n_extra_args += self->n_pos_args - n_args; + 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) { - // 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_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); + if (n_kw != 0 || self->has_def_kw_args) { DEBUG_printf("Initial args: "); - dump_args(flat_args, 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); 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 (flat_args[j] != MP_OBJ_NULL) { + 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))); } - flat_args[j] = kwargs[2 * i + 1]; + code_state->state[n_state - 1 - j] = kwargs[2 * i + 1]; goto continue2; } } @@ -323,43 +358,47 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_o 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_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); - // Now fill in defaults for positional args - mp_obj_t *d = &flat_args[self->n_pos_args - 1]; + // 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--) { + for (int i = self->n_def_args; i > 0; i--, d++, s--) { if (*d == MP_OBJ_NULL) { *d = *s; } } - DEBUG_printf("Args after filling defaults: "); - dump_args(flat_args, self->n_pos_args + self->n_kwonly_args); + + 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 >= flat_args) { - if (*d-- == MP_OBJ_NULL) { + 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", d - flat_args)); + "function missing required positional argument #%d", &code_state->state[n_state] - d)); } } // 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]))); + // 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]))); + } } } - args = flat_args; - n_args = self->n_pos_args + self->n_kwonly_args; - - if (self->takes_kw_args) { - extra_args[n_extra_args] = dict; - n_extra_args += 1; - } } else { // no keyword arguments given if (self->n_kwonly_args != 0) { @@ -367,20 +406,80 @@ continue2:; "function missing keyword-only argument")); } if (self->takes_kw_args) { - extra_args[n_extra_args] = mp_obj_new_dict(0); - n_extra_args += 1; + *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_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_bytecode(self->bytecode, args, n_args, extra_args, n_extra_args, &result); + 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 @@ -398,7 +497,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_pos_args, uint n_kwonly_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, mp_obj_t def_kw_args, const byte *code) { uint n_def_args = 0; uint n_extra_args = 0; mp_obj_tuple_t *def_args = def_args_in; @@ -407,10 +506,7 @@ mp_obj_t mp_obj_new_fun_bc(uint scope_flags, qstr *args, uint n_pos_args, uint n n_def_args = def_args->len; n_extra_args = def_args->len; } - if ((scope_flags & MP_SCOPE_FLAG_VARARGS) != 0) { - n_extra_args += 1; - } - if ((scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) != 0) { + if (def_kw_args != MP_OBJ_NULL) { n_extra_args += 1; } mp_obj_fun_bc_t *o = m_new_obj_var(mp_obj_fun_bc_t, mp_obj_t, n_extra_args); @@ -420,18 +516,15 @@ mp_obj_t mp_obj_new_fun_bc(uint scope_flags, qstr *args, uint n_pos_args, uint n o->n_pos_args = n_pos_args; o->n_kwonly_args = n_kwonly_args; o->n_def_args = n_def_args; + o->has_def_kw_args = def_kw_args != MP_OBJ_NULL; 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; + if (def_kw_args != MP_OBJ_NULL) { + o->extra_args[n_def_args] = def_kw_args; } return o; } diff --git a/py/objfun.h b/py/objfun.h index b049e5c7e8..f607a5eb6f 100644 --- a/py/objfun.h +++ b/py/objfun.h @@ -30,10 +30,15 @@ typedef struct _mp_obj_fun_bc_t { machine_uint_t n_pos_args : 16; // number of arguments this function takes machine_uint_t n_kwonly_args : 16; // number of arguments this function takes machine_uint_t n_def_args : 16; // number of default arguments + machine_uint_t has_def_kw_args : 1; // set if this function has default keyword args machine_uint_t takes_var_args : 1; // set if this function takes variable args machine_uint_t takes_kw_args : 1; // set if this function takes keyword args const byte *bytecode; // bytecode for the function qstr *args; // argument names (needed to resolve positional args passed as keywords) - // values of default args (if any), plus a slot at the end for var args and/or kw args (if it takes them) + // the following extra_args array is allocated space to take (in order): + // - values of positional default args (if any) + // - a single slot for default kw args dict (if it has them) + // - a single slot for var args tuple (if it takes them) + // - a single slot for kw args dict (if it takes them) mp_obj_t extra_args[]; } mp_obj_fun_bc_t; diff --git a/py/objgenerator.c b/py/objgenerator.c index 7326bced30..b816cc49d0 100644 --- a/py/objgenerator.c +++ b/py/objgenerator.c @@ -106,7 +106,7 @@ mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_ } mp_obj_dict_t *old_globals = mp_globals_get(); mp_globals_set(self->globals); - mp_vm_return_kind_t ret_kind = mp_execute_bytecode2(&self->code_state, throw_value); + mp_vm_return_kind_t ret_kind = mp_execute_bytecode(&self->code_state, throw_value); mp_globals_set(old_globals); switch (ret_kind) { @@ -263,7 +263,7 @@ mp_obj_t mp_obj_new_gen_instance(mp_obj_dict_t *globals, const byte *bytecode, o->code_state.exc_sp = (mp_exc_stack_t*)(o->code_state.state + n_state) - 1; o->code_state.n_state = n_state; - // copy args to end of state array, in reverse (that's how mp_execute_bytecode2 needs it) + // copy args to end of state array, in reverse (that's how mp_execute_bytecode needs it) for (uint i = 0; i < n_args; i++) { o->code_state.state[n_state - 1 - i] = args[i]; } diff --git a/py/objstr.c b/py/objstr.c index 4e70b00812..6656090c84 100644 --- a/py/objstr.c +++ b/py/objstr.c @@ -1751,6 +1751,11 @@ mp_obj_t mp_obj_new_str(const char* data, uint len, bool make_qstr_if_not_alread } } +mp_obj_t mp_obj_str_intern(mp_obj_t str) { + GET_STR_DATA_LEN(str, data, len); + return MP_OBJ_NEW_QSTR(qstr_from_strn((const char*)data, len)); +} + mp_obj_t mp_obj_new_bytes(const byte* data, uint len) { return mp_obj_new_str_of_type(&mp_type_bytes, data, len); } diff --git a/py/objtype.c b/py/objtype.c index dfe5eaa8f0..0d6674c33d 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -336,9 +336,9 @@ STATIC const qstr binary_op_method_name[] = { MP_BINARY_OP_INPLACE_FLOOR_DIVIDE, MP_BINARY_OP_INPLACE_TRUE_DIVIDE, MP_BINARY_OP_INPLACE_MODULO, - MP_BINARY_OP_INPLACE_POWER, - MP_BINARY_OP_LESS, - MP_BINARY_OP_MORE, + MP_BINARY_OP_INPLACE_POWER,*/ + [MP_BINARY_OP_LESS] = MP_QSTR___lt__, + /*MP_BINARY_OP_MORE, MP_BINARY_OP_EQUAL, MP_BINARY_OP_LESS_EQUAL, MP_BINARY_OP_MORE_EQUAL, diff --git a/py/py-version.sh b/py/py-version.sh index 93b8d7a1fd..88c3ebb986 100755 --- a/py/py-version.sh +++ b/py/py-version.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # Note: git describe doesn't work if no tag is available git_tag="$(git describe --dirty --always)" diff --git a/py/qstrdefs.h b/py/qstrdefs.h index 69182f2809..521d4399a5 100644 --- a/py/qstrdefs.h +++ b/py/qstrdefs.h @@ -60,6 +60,7 @@ Q(__str__) Q(__getattr__) Q(__del__) Q(__call__) +Q(__lt__) Q(micropython) Q(bytecode) @@ -343,6 +344,9 @@ Q(byteorder) Q(big) Q(exit) Q(little) +#ifdef MICROPY_PY_SYS_PLATFORM +Q(platform) +#endif Q(stdin) Q(stdout) Q(stderr) diff --git a/py/runtime.c b/py/runtime.c index b1fd19499c..d57bb686d1 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -1149,6 +1149,10 @@ void *m_malloc_fail(int num_bytes) { nlr_raise((mp_obj_t)&mp_const_MemoryError_obj); } +NORETURN void mp_not_implemented(const char *msg) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_NotImplementedError, msg)); +} + // these must correspond to the respective enum void *const mp_fun_table[MP_F_NUMBER_OF] = { mp_load_const_int, diff --git a/py/runtime.h b/py/runtime.h index dbd413180b..fb61c01dd1 100644 --- a/py/runtime.h +++ b/py/runtime.h @@ -112,6 +112,9 @@ mp_obj_t mp_import_name(qstr name, mp_obj_t fromlist, mp_obj_t level); mp_obj_t mp_import_from(mp_obj_t module, qstr name); void mp_import_all(mp_obj_t module); +// Raise NotImplementedError with given message +NORETURN void mp_not_implemented(const char *msg); + extern struct _mp_obj_list_t mp_sys_path_obj; extern struct _mp_obj_list_t mp_sys_argv_obj; #define mp_sys_path ((mp_obj_t)&mp_sys_path_obj) @@ -40,12 +40,6 @@ #include "bc.h" #include "objgenerator.h" -// With these macros you can tune the maximum number of function state bytes -// that will be allocated on the stack. Any function that needs more -// than this will use the heap. -#define VM_MAX_STATE_ON_STACK (10 * sizeof(machine_uint_t)) - -#define DETECT_VM_STACK_OVERFLOW (0) #if 0 #define TRACE(ip) mp_bytecode_print2(ip, 1); #else @@ -102,123 +96,13 @@ typedef enum { currently_in_except_block = MP_TAGPTR_TAG(exc_sp->val_sp); /* restore previous state */ \ exc_sp--; /* pop back to previous exception handler */ -mp_vm_return_kind_t mp_execute_bytecode(const byte *code, const mp_obj_t *args, uint n_args, - const mp_obj_t *args2, uint n_args2, mp_obj_t *ret) { - const byte *ip = code; - - // 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; - - // allocate state for locals and stack -#if DETECT_VM_STACK_OVERFLOW - n_state += 1; -#endif - - int 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 = code; - 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; - - // init args - for (uint i = 0; i < n_args; i++) { - code_state->state[n_state - 1 - i] = args[i]; - } - for (uint i = 0; i < n_args2; i++) { - code_state->state[n_state - 1 - n_args - i] = args2[i]; - } - - // set rest of state to MP_OBJ_NULL - for (uint i = 0; i < n_state - n_args - n_args2; i++) { - code_state->state[i] = MP_OBJ_NULL; - } - - // 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]); - } - - code_state->ip = ip; - - // execute the byte code - mp_vm_return_kind_t vm_return_kind = mp_execute_bytecode2(code_state, MP_OBJ_NULL); - -#if DETECT_VM_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 && n_args == 0 && n_args2 == 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 - n_args - n_args2; 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_vm_return_kind_t ret_kind; - switch (vm_return_kind) { - case MP_VM_RETURN_NORMAL: - // return value is in *sp - *ret = *code_state->sp; - ret_kind = MP_VM_RETURN_NORMAL; - break; - - case MP_VM_RETURN_EXCEPTION: - // return value is in state[n_state - 1] - *ret = code_state->state[n_state - 1]; - ret_kind = MP_VM_RETURN_EXCEPTION; - break; - - case MP_VM_RETURN_YIELD: // byte-code shouldn't yield - default: - assert(0); - *ret = mp_const_none; - ret_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); - } - return ret_kind; -} - // fastn has items in reverse order (fastn[0] is local[0], fastn[-1] is local[1], etc) // sp points to bottom of stack which grows up // returns: // MP_VM_RETURN_NORMAL, sp valid, return value in *sp // MP_VM_RETURN_YIELD, ip, sp valid, yielded value in *sp // MP_VM_RETURN_EXCEPTION, exception in fastn[0] -mp_vm_return_kind_t mp_execute_bytecode2(mp_code_state *code_state, volatile mp_obj_t inject_exc) { +mp_vm_return_kind_t mp_execute_bytecode(mp_code_state *code_state, volatile mp_obj_t inject_exc) { #if MICROPY_OPT_COMPUTED_GOTO #include "vmentrytable.h" #define DISPATCH() do { \ @@ -240,7 +124,7 @@ mp_vm_return_kind_t mp_execute_bytecode2(mp_code_state *code_state, volatile mp_ // loop and the exception handler, leading to very obscure bugs. #define RAISE(o) do { nlr_pop(); nlr.ret_val = o; goto exception_handler; } while(0) - // Pointers which are constant for particular invocation of mp_execute_bytecode2() + // Pointers which are constant for particular invocation of mp_execute_bytecode() mp_obj_t *const fastn = &code_state->state[code_state->n_state - 1]; mp_exc_stack_t *const exc_stack = (mp_exc_stack_t*)(code_state->state + code_state->n_state); diff --git a/tests/basics/fun-defargs.py b/tests/basics/fun-defargs.py index 0666b8c494..ed25f5739d 100644 --- a/tests/basics/fun-defargs.py +++ b/tests/basics/fun-defargs.py @@ -1,5 +1,5 @@ def fun1(val=5): - print(5) + print(val) fun1() fun1(10) diff --git a/tests/basics/fun-kwonly.py b/tests/basics/fun-kwonly.py index 8bda68d95a..bdff3a8210 100644 --- a/tests/basics/fun-kwonly.py +++ b/tests/basics/fun-kwonly.py @@ -43,15 +43,6 @@ def f(a, *, b, **kw): f(1, b=2) f(1, b=2, c=3) -## with a default value; not currently working -#def g(a, *, b=2, c): -# print(a, b, c) -# -#g(1, c=3) -#g(1, b=3, c=4) -#g(1, **{'c':3}) -#g(1, **{'b':'3', 'c':4}) - # with named star def f(*a, b, c): print(a, b, c) diff --git a/tests/basics/fun-kwonlydef.py b/tests/basics/fun-kwonlydef.py new file mode 100644 index 0000000000..56a08ee81c --- /dev/null +++ b/tests/basics/fun-kwonlydef.py @@ -0,0 +1,36 @@ +# test function args, keyword only with default value + +# a single arg with a default +def f1(*, a=1): + print(a) +f1() +f1(a=2) + +# 1 arg default, 1 not +def f2(*, a=1, b): + print(a, b) +f2(b=2) +f2(a=2, b=3) + +# 1 positional, 1 arg default, 1 not +def f3(a, *, b=2, c): + print(a, b, c) +f3(1, c=3) +f3(1, b=3, c=4) +f3(1, **{'c':3}) +f3(1, **{'b':'3', 'c':4}) + +# many args, not all with defaults +def f4(*, a=1, b, c=3, d, e=5, f): + print(a, b, c, d, e, f) +f4(b=2, d=4, f=6) +f4(a=11, b=2, d=4, f=6) +f4(a=11, b=2, c=33, d=4, e=55, f=6) +f4(f=6, e=55, d=4, c=33, b=2, a=11) + +# positional with default, then keyword only +def f5(a, b=4, *c, d=8): + print(a, b, c, d) +f5(1) +f5(1, d=9) +f5(1, b=44, d=9) diff --git a/tests/basics/getattr1.py b/tests/basics/getattr1.py index 9a96154ca5..59cb7e7f7a 100644 --- a/tests/basics/getattr1.py +++ b/tests/basics/getattr1.py @@ -15,3 +15,4 @@ print(getattr(a, "var2")) print(getattr(a, "meth")(5)) print(getattr(a, "_none_such", 123)) print(getattr(list, "foo", 456)) +print(getattr(a, "va" + "r2")) diff --git a/unix/Makefile b/unix/Makefile index ffc3391a14..485009135f 100644 --- a/unix/Makefile +++ b/unix/Makefile @@ -48,7 +48,10 @@ ifeq ($(MICROPY_PY_FFI),1) LIBFFI_LDFLAGS_MOD := $(shell pkg-config --libs libffi) LIBFFI_CFLAGS_MOD := $(shell pkg-config --cflags libffi) CFLAGS_MOD += $(LIBFFI_CFLAGS_MOD) -DMICROPY_PY_FFI=1 -LDFLAGS_MOD += -ldl $(LIBFFI_LDFLAGS_MOD) +ifeq ($(UNAME_S),Linux) +LDFLAGS_MOD += -ldl +endif +LDFLAGS_MOD += $(LIBFFI_LDFLAGS_MOD) SRC_MOD += modffi.c endif diff --git a/unix/modos.c b/unix/modos.c index a6be4bfff0..657958d04c 100644 --- a/unix/modos.c +++ b/unix/modos.c @@ -52,7 +52,7 @@ STATIC mp_obj_t mod_os_stat(mp_obj_t path_in) { mp_obj_tuple_t *t = mp_obj_new_tuple(10, NULL); t->items[0] = MP_OBJ_NEW_SMALL_INT((machine_int_t)sb.st_mode); - t->items[1] = MP_OBJ_NEW_SMALL_INT(sb.st_ino); + t->items[1] = MP_OBJ_NEW_SMALL_INT((machine_int_t)sb.st_ino); t->items[2] = MP_OBJ_NEW_SMALL_INT((machine_int_t)sb.st_dev); t->items[3] = MP_OBJ_NEW_SMALL_INT((machine_int_t)sb.st_nlink); t->items[4] = MP_OBJ_NEW_SMALL_INT((machine_int_t)sb.st_uid); diff --git a/unix/modtime.c b/unix/modtime.c index f957b96784..3cc09e3cd8 100644 --- a/unix/modtime.c +++ b/unix/modtime.c @@ -55,10 +55,8 @@ void msec_sleep_tv(struct timeval *tv) { #define MP_CLOCKS_PER_SEC CLOCKS_PER_SEC #endif -#if defined(MP_CLOCKS_PER_SEC) && (MP_CLOCKS_PER_SEC == 1000000) // POSIX -#define CLOCK_DIV 1000.0 -#elif defined(MP_CLOCKS_PER_SEC) && (MP_CLOCKS_PER_SEC == 1000) // WIN32 -#define CLOCK_DIV 1.0 +#if defined(MP_CLOCKS_PER_SEC) +#define CLOCK_DIV (MP_CLOCKS_PER_SEC / 1000.0) #else #error Unsupported clock() implementation #endif diff --git a/unix/mpconfigport.h b/unix/mpconfigport.h index fe68b99341..f6cef8578c 100644 --- a/unix/mpconfigport.h +++ b/unix/mpconfigport.h @@ -43,6 +43,7 @@ #define MICROPY_OPT_COMPUTED_GOTO (1) #define MICROPY_PY_BUILTINS_FROZENSET (1) #define MICROPY_PY_SYS_EXIT (1) +#define MICROPY_PY_SYS_PLATFORM "linux" #define MICROPY_PY_SYS_STDFILES (1) #define MICROPY_PY_CMATH (1) #define MICROPY_PY_IO_FILEIO (1) |