summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--py/bc.h3
-rw-r--r--py/builtin.c9
-rw-r--r--py/compile.c12
-rw-r--r--py/emitglue.c6
-rw-r--r--py/modsys.c6
-rw-r--r--py/obj.h3
-rw-r--r--py/objfun.c219
-rw-r--r--py/objfun.h7
-rw-r--r--py/objgenerator.c4
-rw-r--r--py/objstr.c5
-rw-r--r--py/objtype.c6
-rwxr-xr-xpy/py-version.sh2
-rw-r--r--py/qstrdefs.h4
-rw-r--r--py/runtime.c4
-rw-r--r--py/runtime.h3
-rw-r--r--py/vm.c120
-rw-r--r--tests/basics/fun-defargs.py2
-rw-r--r--tests/basics/fun-kwonly.py9
-rw-r--r--tests/basics/fun-kwonlydef.py36
-rw-r--r--tests/basics/getattr1.py1
-rw-r--r--unix/Makefile5
-rw-r--r--unix/modos.c2
-rw-r--r--unix/modtime.c6
-rw-r--r--unix/mpconfigport.h1
24 files changed, 258 insertions, 217 deletions
diff --git a/py/bc.h b/py/bc.h
index 915a0f269e..2b9793236f 100644
--- a/py/bc.h
+++ b/py/bc.h
@@ -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
diff --git a/py/obj.h b/py/obj.h
index aa78b2a22d..d5ea40901c 100644
--- a/py/obj.h
+++ b/py/obj.h
@@ -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)
diff --git a/py/vm.c b/py/vm.c
index b24c85a491..42709889cd 100644
--- a/py/vm.c
+++ b/py/vm.c
@@ -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)