py: Put all bytecode state (arg count, etc) in bytecode.
diff --git a/py/bc.c b/py/bc.c
index a4ee847..da0ea78 100644
--- a/py/bc.c
+++ b/py/bc.c
@@ -31,6 +31,7 @@
 
 #include "py/nlr.h"
 #include "py/objfun.h"
+#include "py/runtime0.h"
 #include "py/bc.h"
 
 #if 0 // print debugging info
@@ -100,6 +101,12 @@
     code_state->prev = NULL;
     #endif
 
+    // get params
+    mp_uint_t scope_flags = *code_state->ip++;
+    mp_uint_t n_pos_args = *code_state->ip++;
+    mp_uint_t n_kwonly_args = *code_state->ip++;
+    mp_uint_t n_def_pos_args = *code_state->ip++;
+
     // align ip
     code_state->ip = MP_ALIGN(code_state->ip, sizeof(mp_uint_t));
 
@@ -112,33 +119,33 @@
     const mp_obj_t *kwargs = args + n_args;
 
     // 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];
+    mp_obj_t *var_pos_kw_args = &code_state->state[n_state - 1 - n_pos_args - n_kwonly_args];
 
     // check positional arguments
 
-    if (n_args > self->n_pos_args) {
+    if (n_args > n_pos_args) {
         // given more than enough arguments
-        if (!self->takes_var_args) {
-            fun_pos_args_mismatch(self, self->n_pos_args, n_args);
+        if ((scope_flags & MP_SCOPE_FLAG_VARARGS) == 0) {
+            fun_pos_args_mismatch(self, n_pos_args, n_args);
         }
         // put extra arguments in varargs tuple
-        *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;
+        *var_pos_kw_args-- = mp_obj_new_tuple(n_args - n_pos_args, args + n_pos_args);
+        n_args = n_pos_args;
     } else {
-        if (self->takes_var_args) {
+        if ((scope_flags & MP_SCOPE_FLAG_VARARGS) != 0) {
             DEBUG_printf("passing empty tuple as *args\n");
             *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 && !self->has_def_kw_args) {
-            if (n_args >= (mp_uint_t)(self->n_pos_args - self->n_def_args)) {
+        if (n_kw == 0 && (scope_flags & MP_SCOPE_FLAG_DEFKWARGS) == 0) {
+            if (n_args >= (mp_uint_t)(n_pos_args - n_def_pos_args)) {
                 // given enough arguments, but may need to use some default arguments
-                for (mp_uint_t 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)];
+                for (mp_uint_t i = n_args; i < n_pos_args; i++) {
+                    code_state->state[n_state - 1 - i] = self->extra_args[i - (n_pos_args - n_def_pos_args)];
                 }
             } else {
-                fun_pos_args_mismatch(self, self->n_pos_args - self->n_def_args, n_args);
+                fun_pos_args_mismatch(self, n_pos_args - n_def_pos_args, n_args);
             }
         }
     }
@@ -150,12 +157,12 @@
 
     // check keyword arguments
 
-    if (n_kw != 0 || self->has_def_kw_args) {
+    if (n_kw != 0 || (scope_flags & MP_SCOPE_FLAG_DEFKWARGS) != 0) {
         DEBUG_printf("Initial 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 - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args);
 
         mp_obj_t dict = MP_OBJ_NULL;
-        if (self->takes_kw_args) {
+        if ((scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) != 0) {
             dict = mp_obj_new_dict(n_kw); // TODO: better go conservative with 0?
             *var_pos_kw_args = dict;
         }
@@ -165,7 +172,7 @@
 
         for (mp_uint_t i = 0; i < n_kw; i++) {
             mp_obj_t wanted_arg_name = kwargs[2 * i];
-            for (mp_uint_t j = 0; j < self->n_pos_args + self->n_kwonly_args; j++) {
+            for (mp_uint_t j = 0; j < n_pos_args + n_kwonly_args; j++) {
                 if (wanted_arg_name == arg_names[j]) {
                     if (code_state->state[n_state - 1 - j] != MP_OBJ_NULL) {
                         nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
@@ -176,7 +183,7 @@
                 }
             }
             // Didn't find name match with positional args
-            if (!self->takes_kw_args) {
+            if ((scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) == 0) {
                 nlr_raise(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]);
@@ -184,19 +191,19 @@
         }
 
         DEBUG_printf("Args with kws flattened: ");
-        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 - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args);
 
         // 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 (mp_uint_t i = self->n_def_args; i > 0; i--, d++, s--) {
+        mp_obj_t *d = &code_state->state[n_state - n_pos_args];
+        mp_obj_t *s = &self->extra_args[n_def_pos_args - 1];
+        for (mp_uint_t i = n_def_pos_args; i > 0; i--, d++, s--) {
             if (*d == MP_OBJ_NULL) {
                 *d = *s;
             }
         }
 
         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);
+        dump_args(code_state->state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args);
 
         // Check that all mandatory positional args are specified
         while (d < &code_state->state[n_state]) {
@@ -208,35 +215,35 @@
 
         // Check that all mandatory keyword args are specified
         // Fill in default kw args if we have them
-        for (mp_uint_t i = 0; i < self->n_kwonly_args; i++) {
-            if (code_state->state[n_state - 1 - self->n_pos_args - i] == MP_OBJ_NULL) {
+        for (mp_uint_t i = 0; i < n_kwonly_args; i++) {
+            if (code_state->state[n_state - 1 - 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, arg_names[self->n_pos_args + i], MP_MAP_LOOKUP);
+                if ((scope_flags & MP_SCOPE_FLAG_DEFKWARGS) != 0) {
+                    elem = mp_map_lookup(&((mp_obj_dict_t*)self->extra_args[n_def_pos_args])->map, arg_names[n_pos_args + i], MP_MAP_LOOKUP);
                 }
                 if (elem != NULL) {
-                    code_state->state[n_state - 1 - self->n_pos_args - i] = elem->value;
+                    code_state->state[n_state - 1 - n_pos_args - i] = elem->value;
                 } else {
                     nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
-                        "function missing required keyword argument '%q'", MP_OBJ_QSTR_VALUE(arg_names[self->n_pos_args + i])));
+                        "function missing required keyword argument '%q'", MP_OBJ_QSTR_VALUE(arg_names[n_pos_args + i])));
                 }
             }
         }
 
     } else {
         // no keyword arguments given
-        if (self->n_kwonly_args != 0) {
+        if (n_kwonly_args != 0) {
             nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError,
                 "function missing keyword-only argument"));
         }
-        if (self->takes_kw_args) {
+        if ((scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) != 0) {
             *var_pos_kw_args = mp_obj_new_dict(0);
         }
     }
 
     // get the ip and skip argument names
     const byte *ip = code_state->ip;
-    ip += (self->n_pos_args + self->n_kwonly_args) * sizeof(mp_uint_t);
+    ip += (n_pos_args + n_kwonly_args) * sizeof(mp_uint_t);
 
     // store pointer to code_info and jump over it
     {
@@ -256,7 +263,7 @@
     // 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);
+    DEBUG_printf("Calling: n_pos_args=%d, n_kwonly_args=%d\n", n_pos_args, n_kwonly_args);
+    dump_args(code_state->state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args);
     dump_args(code_state->state, n_state);
 }
diff --git a/py/bc.h b/py/bc.h
index 73b67bc..5824688 100644
--- a/py/bc.h
+++ b/py/bc.h
@@ -33,6 +33,10 @@
 //
 //  n_state         : var uint
 //  n_exc_stack     : var uint
+//  scope_flags     : byte
+//  n_pos_args      : byte          number of arguments this function takes
+//  n_kwonly_args   : byte          number of keyword-only arguments this function takes
+//  n_def_pos_args  : byte          number of default positional arguments
 //
 //  <word alignment padding>
 //
@@ -85,7 +89,7 @@
 mp_vm_return_kind_t mp_execute_bytecode(mp_code_state *code_state, volatile mp_obj_t inject_exc);
 mp_code_state *mp_obj_fun_bc_prepare_codestate(mp_obj_t func, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args);
 void mp_setup_code_state(mp_code_state *code_state, mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args);
-void mp_bytecode_print(const void *descr, mp_uint_t n_total_args, const byte *code, mp_uint_t len);
+void mp_bytecode_print(const void *descr, const byte *code, mp_uint_t len);
 void mp_bytecode_print2(const byte *code, mp_uint_t len);
 const byte *mp_bytecode_print_str(const byte *ip);
 #define mp_bytecode_print_inst(code) mp_bytecode_print2(code, 1)
diff --git a/py/compile.c b/py/compile.c
index f10b378..e0561e6 100644
--- a/py/compile.c
+++ b/py/compile.c
@@ -539,6 +539,12 @@
     assert(n_pos_defaults >= 0);
     assert(n_kw_defaults >= 0);
 
+    // set flags
+    if (n_kw_defaults > 0) {
+        this_scope->scope_flags |= MP_SCOPE_FLAG_DEFKWARGS;
+    }
+    this_scope->num_def_pos_args = n_pos_defaults;
+
     // make closed over variables, if any
     // ensure they are closed over in the order defined in the outer scope (mainly to agree with CPython)
     int nfree = 0;
diff --git a/py/emitbc.c b/py/emitbc.c
index 0ed7828..6b45019 100644
--- a/py/emitbc.c
+++ b/py/emitbc.c
@@ -294,6 +294,13 @@
         emit_write_code_info_uint(emit, scope->exc_stack_size);
     }
 
+    // Write scope flags and number of arguments.
+    // TODO check that num args all fit in a byte
+    emit_write_code_info_byte(emit, emit->scope->scope_flags);
+    emit_write_code_info_byte(emit, emit->scope->num_pos_args);
+    emit_write_code_info_byte(emit, emit->scope->num_kwonly_args);
+    emit_write_code_info_byte(emit, emit->scope->num_def_pos_args);
+
     // Align code-info so that following pointers are aligned on a machine word.
     emit_align_code_info_to_machine_word(emit);
 
@@ -372,9 +379,7 @@
 
     } else if (emit->pass == MP_PASS_EMIT) {
         mp_emit_glue_assign_bytecode(emit->scope->raw_code, emit->code_base,
-            emit->code_info_size + emit->bytecode_size,
-            emit->scope->num_pos_args, emit->scope->num_kwonly_args,
-            emit->scope->scope_flags);
+            emit->code_info_size + emit->bytecode_size, emit->scope->scope_flags);
     }
 }
 
diff --git a/py/emitglue.c b/py/emitglue.c
index 610e76d..83fe420 100644
--- a/py/emitglue.c
+++ b/py/emitglue.c
@@ -48,11 +48,9 @@
     mp_raw_code_kind_t kind : 3;
     mp_uint_t scope_flags : 7;
     mp_uint_t n_pos_args : 11;
-    mp_uint_t n_kwonly_args : 11;
     union {
         struct {
-            byte *code;
-            mp_uint_t len;
+            const byte *code;
         } u_byte;
         struct {
             void *fun_data;
@@ -67,36 +65,32 @@
     return rc;
 }
 
-void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, byte *code, mp_uint_t len, mp_uint_t n_pos_args, mp_uint_t n_kwonly_args, mp_uint_t scope_flags) {
+void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, byte *code, mp_uint_t len, mp_uint_t scope_flags) {
     rc->kind = MP_CODE_BYTECODE;
     rc->scope_flags = scope_flags;
-    rc->n_pos_args = n_pos_args;
-    rc->n_kwonly_args = n_kwonly_args;
     rc->data.u_byte.code = code;
-    rc->data.u_byte.len = len;
 
 #ifdef DEBUG_PRINT
-    DEBUG_printf("assign byte code: code=%p len=" UINT_FMT " n_pos_args=" UINT_FMT " n_kwonly_args=" UINT_FMT " flags=%x\n", code, len, n_pos_args, n_kwonly_args, (uint)scope_flags);
+    DEBUG_printf("assign byte code: code=%p len=" UINT_FMT " flags=%x\n", code, len, (uint)scope_flags);
 #endif
 #if MICROPY_DEBUG_PRINTERS
     if (mp_verbose_flag >= 2) {
-        mp_bytecode_print(rc, n_pos_args + n_kwonly_args, code, len);
+        mp_bytecode_print(rc, code, len);
     }
 #endif
 }
 
 #if MICROPY_EMIT_NATIVE || MICROPY_EMIT_INLINE_THUMB
-void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void *fun_data, mp_uint_t fun_len, mp_uint_t n_pos_args, mp_uint_t n_kwonly_args, mp_uint_t scope_flags, mp_uint_t type_sig) {
+void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void *fun_data, mp_uint_t fun_len, mp_uint_t n_pos_args, mp_uint_t scope_flags, mp_uint_t type_sig) {
     assert(kind == MP_CODE_NATIVE_PY || kind == MP_CODE_NATIVE_VIPER || kind == MP_CODE_NATIVE_ASM);
     rc->kind = kind;
     rc->scope_flags = scope_flags;
     rc->n_pos_args = n_pos_args;
-    rc->n_kwonly_args = n_kwonly_args;
     rc->data.u_native.fun_data = fun_data;
     rc->data.u_native.type_sig = type_sig;
 
 #ifdef DEBUG_PRINT
-    DEBUG_printf("assign native: kind=%d fun=%p len=" UINT_FMT " n_pos_args=" UINT_FMT " n_kwonly_args=" UINT_FMT " flags=%x\n", kind, fun_data, fun_len, n_pos_args, n_kwonly_args, (uint)scope_flags);
+    DEBUG_printf("assign native: kind=%d fun=%p len=" UINT_FMT " n_pos_args=" UINT_FMT " flags=%x\n", kind, fun_data, fun_len, n_pos_args, (uint)scope_flags);
     for (mp_uint_t i = 0; i < fun_len; i++) {
         if (i > 0 && i % 16 == 0) {
             DEBUG_printf("\n");
@@ -131,11 +125,11 @@
     switch (rc->kind) {
         case MP_CODE_BYTECODE:
         no_other_choice:
-            fun = mp_obj_new_fun_bc(rc->scope_flags, rc->n_pos_args, rc->n_kwonly_args, def_args, def_kw_args, rc->data.u_byte.code);
+            fun = mp_obj_new_fun_bc(def_args, def_kw_args, rc->data.u_byte.code);
             break;
         #if MICROPY_EMIT_NATIVE
         case MP_CODE_NATIVE_PY:
-            fun = mp_obj_new_fun_native(rc->scope_flags, rc->n_pos_args, rc->n_kwonly_args, def_args, def_kw_args, rc->data.u_native.fun_data);
+            fun = mp_obj_new_fun_native(def_args, def_kw_args, rc->data.u_native.fun_data);
             break;
         case MP_CODE_NATIVE_VIPER:
             fun = mp_obj_new_fun_viper(rc->n_pos_args, rc->data.u_native.fun_data, rc->data.u_native.type_sig);
diff --git a/py/emitglue.h b/py/emitglue.h
index 97e680b..56029b3 100644
--- a/py/emitglue.h
+++ b/py/emitglue.h
@@ -43,8 +43,8 @@
 
 mp_raw_code_t *mp_emit_glue_new_raw_code(void);
 
-void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, byte *code, mp_uint_t len, mp_uint_t n_pos_args, mp_uint_t n_kwonly_args, mp_uint_t scope_flags);
-void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void *fun_data, mp_uint_t fun_len, mp_uint_t n_pos_args, mp_uint_t n_kwonly_args, mp_uint_t scope_flags, mp_uint_t type_sig);
+void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, byte *code, mp_uint_t len, mp_uint_t scope_flags);
+void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void *fun_data, mp_uint_t fun_len, mp_uint_t n_pos_args, mp_uint_t scope_flags, mp_uint_t type_sig);
 
 mp_obj_t mp_make_function_from_raw_code(mp_raw_code_t *rc, mp_obj_t def_args, mp_obj_t def_kw_args);
 mp_obj_t mp_make_closure_from_raw_code(mp_raw_code_t *rc, mp_uint_t n_closed_over, const mp_obj_t *args);
diff --git a/py/emitinlinethumb.c b/py/emitinlinethumb.c
index 9f4ef12..14cbf57 100644
--- a/py/emitinlinethumb.c
+++ b/py/emitinlinethumb.c
@@ -90,7 +90,7 @@
 
     if (emit->pass == MP_PASS_EMIT) {
         void *f = asm_thumb_get_code(emit->as);
-        mp_emit_glue_assign_native(emit->scope->raw_code, MP_CODE_NATIVE_ASM, f, asm_thumb_get_code_size(emit->as), emit->scope->num_pos_args, 0, 0, 0);
+        mp_emit_glue_assign_native(emit->scope->raw_code, MP_CODE_NATIVE_ASM, f, asm_thumb_get_code_size(emit->as), emit->scope->num_pos_args, 0, 0);
     }
 }
 
diff --git a/py/emitnative.c b/py/emitnative.c
index 1fcc843..99eac79 100644
--- a/py/emitnative.c
+++ b/py/emitnative.c
@@ -824,6 +824,10 @@
 
     if (!emit->do_viper_types) {
         emit->prelude_offset = ASM_GET_CODE_POS(emit->as);
+        ASM_DATA(emit->as, 1, emit->scope->scope_flags);
+        ASM_DATA(emit->as, 1, emit->scope->num_pos_args);
+        ASM_DATA(emit->as, 1, emit->scope->num_kwonly_args);
+        ASM_DATA(emit->as, 1, emit->scope->num_def_pos_args);
         ASM_ALIGN(emit->as, ASM_WORD_SIZE);
 
         // write argument names as qstr objects
@@ -874,8 +878,7 @@
 
         mp_emit_glue_assign_native(emit->scope->raw_code,
             emit->do_viper_types ? MP_CODE_NATIVE_VIPER : MP_CODE_NATIVE_PY,
-            f, f_len, emit->scope->num_pos_args, emit->scope->num_kwonly_args,
-            emit->scope->scope_flags, type_sig);
+            f, f_len, emit->scope->num_pos_args, emit->scope->scope_flags, type_sig);
     }
 }
 
diff --git a/py/obj.h b/py/obj.h
index 6439508..a627a41 100644
--- a/py/obj.h
+++ b/py/obj.h
@@ -537,8 +537,8 @@
 mp_obj_t mp_obj_new_exception_args(const mp_obj_type_t *exc_type, mp_uint_t 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(mp_uint_t scope_flags, mp_uint_t n_pos_args, mp_uint_t n_kwonly_args, mp_obj_t def_args, mp_obj_t def_kw_args, const byte *code);
-mp_obj_t mp_obj_new_fun_native(mp_uint_t scope_flags, mp_uint_t n_pos_args, mp_uint_t n_kwonly_args, mp_obj_t def_args_in, mp_obj_t def_kw_args, const void *fun_data);
+mp_obj_t mp_obj_new_fun_bc(mp_obj_t def_args, mp_obj_t def_kw_args, const byte *code);
+mp_obj_t mp_obj_new_fun_native(mp_obj_t def_args_in, mp_obj_t def_kw_args, const void *fun_data);
 mp_obj_t mp_obj_new_fun_viper(mp_uint_t n_args, void *fun_data, mp_uint_t type_sig);
 mp_obj_t mp_obj_new_fun_asm(mp_uint_t n_args, void *fun_data);
 mp_obj_t mp_obj_new_gen_wrap(mp_obj_t fun);
diff --git a/py/objfun.c b/py/objfun.c
index 53ddb0a..a54e50d 100644
--- a/py/objfun.c
+++ b/py/objfun.c
@@ -125,8 +125,12 @@
     const byte *bc = fun->bytecode;
     mp_decode_uint(&bc); // skip n_state
     mp_decode_uint(&bc); // skip n_exc_stack
+    bc++; // skip scope_params
+    mp_uint_t n_pos_args = *bc++;
+    mp_uint_t n_kwonly_args = *bc++;
+    bc++; // skip n_def_pos_args
     bc = MP_ALIGN(bc, sizeof(mp_uint_t)); // align
-    bc += (fun->n_pos_args + fun->n_kwonly_args) * sizeof(mp_uint_t); // skip arg names
+    bc += (n_pos_args + n_kwonly_args) * sizeof(mp_uint_t); // skip arg names
     return mp_obj_code_get_name(bc);
 }
 
@@ -316,7 +320,7 @@
 #endif
 };
 
-mp_obj_t mp_obj_new_fun_bc(mp_uint_t scope_flags, mp_uint_t n_pos_args, mp_uint_t n_kwonly_args, mp_obj_t def_args_in, mp_obj_t def_kw_args, const byte *code) {
+mp_obj_t mp_obj_new_fun_bc(mp_obj_t def_args_in, mp_obj_t def_kw_args, const byte *code) {
     mp_uint_t n_def_args = 0;
     mp_uint_t n_extra_args = 0;
     mp_obj_tuple_t *def_args = def_args_in;
@@ -331,12 +335,6 @@
     mp_obj_fun_bc_t *o = m_new_obj_var(mp_obj_fun_bc_t, mp_obj_t, n_extra_args);
     o->base.type = &mp_type_fun_bc;
     o->globals = mp_globals_get();
-    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;
     if (def_args != MP_OBJ_NULL) {
         memcpy(o->extra_args, def_args->items, n_def_args * sizeof(mp_obj_t));
@@ -366,8 +364,8 @@
     .unary_op = mp_generic_unary_op,
 };
 
-mp_obj_t mp_obj_new_fun_native(mp_uint_t scope_flags, mp_uint_t n_pos_args, mp_uint_t n_kwonly_args, mp_obj_t def_args_in, mp_obj_t def_kw_args, const void *fun_data) {
-    mp_obj_fun_bc_t *o = mp_obj_new_fun_bc(scope_flags, n_pos_args, n_kwonly_args, def_args_in, def_kw_args, (const byte*)fun_data);
+mp_obj_t mp_obj_new_fun_native(mp_obj_t def_args_in, mp_obj_t def_kw_args, const void *fun_data) {
+    mp_obj_fun_bc_t *o = mp_obj_new_fun_bc(def_args_in, def_kw_args, (const byte*)fun_data);
     o->base.type = &mp_type_fun_native;
     return o;
 }
diff --git a/py/objfun.h b/py/objfun.h
index a5b9f2b..cdc495e 100644
--- a/py/objfun.h
+++ b/py/objfun.h
@@ -31,12 +31,6 @@
 typedef struct _mp_obj_fun_bc_t {
     mp_obj_base_t base;
     mp_obj_dict_t *globals;         // the context within which this function was defined
-    mp_uint_t n_pos_args : 8;       // number of arguments this function takes
-    mp_uint_t n_kwonly_args : 8;    // number of keyword-only arguments this function takes
-    mp_uint_t n_def_args : 8;       // number of default arguments
-    mp_uint_t has_def_kw_args : 1;  // set if this function has default keyword args
-    mp_uint_t takes_var_args : 1;   // set if this function takes variable args
-    mp_uint_t takes_kw_args : 1;    // set if this function takes keyword args
     const byte *bytecode;           // bytecode for the function
     // the following extra_args array is allocated space to take (in order):
     //  - values of positional default args (if any)
diff --git a/py/runtime0.h b/py/runtime0.h
index 1d7d64b..d00949f 100644
--- a/py/runtime0.h
+++ b/py/runtime0.h
@@ -30,6 +30,7 @@
 #define MP_SCOPE_FLAG_VARARGS      (0x01)
 #define MP_SCOPE_FLAG_VARKEYWORDS  (0x02)
 #define MP_SCOPE_FLAG_GENERATOR    (0x04)
+#define MP_SCOPE_FLAG_DEFKWARGS    (0x08)
 
 // types for native (viper) function signature
 #define MP_NATIVE_TYPE_OBJ  (0x00)
diff --git a/py/scope.h b/py/scope.h
index 0ea0035..fac936a 100644
--- a/py/scope.h
+++ b/py/scope.h
@@ -66,6 +66,7 @@
     uint8_t emit_options; // see compile.h
     uint16_t num_pos_args;
     uint16_t num_kwonly_args;
+    uint16_t num_def_pos_args;
     uint16_t num_locals;
     uint16_t stack_size;     // maximum size of the locals stack
     uint16_t exc_stack_size; // maximum size of the exception stack
diff --git a/py/showbc.c b/py/showbc.c
index 87e7c6a..538eddc 100644
--- a/py/showbc.c
+++ b/py/showbc.c
@@ -54,18 +54,22 @@
 
 const byte *mp_showbc_code_start;
 
-void mp_bytecode_print(const void *descr, mp_uint_t n_total_args, const byte *ip, mp_uint_t len) {
+void mp_bytecode_print(const void *descr, const byte *ip, mp_uint_t len) {
     mp_showbc_code_start = ip;
 
-    // get state size and exception stack size
+    // get bytecode parameters
     mp_uint_t n_state = mp_decode_uint(&ip);
     mp_uint_t n_exc_stack = mp_decode_uint(&ip);
+    /*mp_uint_t scope_flags =*/ ip++;
+    mp_uint_t n_pos_args = *ip++;
+    mp_uint_t n_kwonly_args = *ip++;
+    /*mp_uint_t n_def_pos_args =*/ ip++;
 
     ip = MP_ALIGN(ip, sizeof(mp_uint_t));
 
     // get and skip arg names
     const mp_obj_t *arg_names = (const mp_obj_t*)ip;
-    ip += n_total_args * sizeof(mp_uint_t);
+    ip += (n_pos_args + n_kwonly_args) * sizeof(mp_uint_t);
 
     const byte *code_info = ip;
     mp_uint_t code_info_size = mp_decode_uint(&code_info);
@@ -88,7 +92,7 @@
 
     // bytecode prelude: arg names (as qstr objects)
     printf("arg names:");
-    for (mp_uint_t i = 0; i < n_total_args; i++) {
+    for (mp_uint_t i = 0; i < n_pos_args + n_kwonly_args; i++) {
         printf(" %s", qstr_str(MP_OBJ_QSTR_VALUE(arg_names[i])));
     }
     printf("\n");