From a397776d6bf1a9d0b07d7138b289cd661c5e1b99 Mon Sep 17 00:00:00 2001
From: Damien <damien.p.george@gmail.com>
Date: Wed, 9 Oct 2013 23:10:10 +0100
Subject: [PATCH] Implement basic class/object functionality in runtime.

---
 py/emit.h       |   2 +-
 py/emitbc.c     |  12 +--
 py/emitcpy.c    |  14 +--
 py/emitnative.c |  14 +--
 py/main.c       |   2 +-
 py/runtime.c    | 277 +++++++++++++++++++++++++++++++++++++-----------
 py/runtime.h    |   5 +-
 py/vm.c         |  26 +++--
 py/vm.h         |   5 +-
 9 files changed, 265 insertions(+), 92 deletions(-)

diff --git a/py/emit.h b/py/emit.h
index 93c7298d37..a8bc1fca24 100644
--- a/py/emit.h
+++ b/py/emit.h
@@ -57,8 +57,8 @@ typedef struct _emit_method_table_t {
     void (*store_global)(emit_t *emit, qstr qstr);
     void (*store_deref)(emit_t *emit, qstr qstr);
     void (*store_attr)(emit_t *emit, qstr qstr);
-    void (*store_locals)(emit_t *emit);
     void (*store_subscr)(emit_t *emit);
+    void (*store_locals)(emit_t *emit);
     void (*delete_fast)(emit_t *emit, qstr qstr, int local_num);
     void (*delete_name)(emit_t *emit, qstr qstr);
     void (*delete_global)(emit_t *emit, qstr qstr);
diff --git a/py/emitbc.c b/py/emitbc.c
index 81b059d1cb..d1ac74f0e9 100644
--- a/py/emitbc.c
+++ b/py/emitbc.c
@@ -348,16 +348,16 @@ static void emit_bc_store_attr(emit_t *emit, qstr qstr) {
     emit_write_byte_1_qstr(emit, PYBC_STORE_ATTR, qstr);
 }
 
-static void emit_bc_store_locals(emit_t *emit) {
-    emit_pre(emit, -1);
-    emit_write_byte_1(emit, PYBC_STORE_LOCALS);
-}
-
 static void emit_bc_store_subscr(emit_t *emit) {
     emit_pre(emit, -3);
     emit_write_byte_1(emit, PYBC_STORE_SUBSCR);
 }
 
+static void emit_bc_store_locals(emit_t *emit) {
+    // not needed for byte code
+    emit_pre(emit, -1);
+}
+
 static void emit_bc_delete_fast(emit_t *emit, qstr qstr, int local_num) {
     assert(local_num >= 0);
     emit_pre(emit, 0);
@@ -718,8 +718,8 @@ const emit_method_table_t emit_bc_method_table = {
     emit_bc_store_global,
     emit_bc_store_deref,
     emit_bc_store_attr,
-    emit_bc_store_locals,
     emit_bc_store_subscr,
+    emit_bc_store_locals,
     emit_bc_delete_fast,
     emit_bc_delete_name,
     emit_bc_delete_global,
diff --git a/py/emitcpy.c b/py/emitcpy.c
index 203fce70a1..7c9615c09a 100644
--- a/py/emitcpy.c
+++ b/py/emitcpy.c
@@ -352,17 +352,17 @@ static void emit_cpy_store_attr(emit_t *emit, qstr qstr) {
     }
 }
 
-static void emit_cpy_store_locals(emit_t *emit) {
-    emit_pre(emit, -1, 1);
+static void emit_cpy_store_subscr(emit_t *emit) {
+    emit_pre(emit, -3, 1);
     if (emit->pass == PASS_3) {
-        printf("STORE_LOCALS\n");
+        printf("STORE_SUBSCR\n");
     }
 }
 
-static void emit_cpy_store_subscr(emit_t *emit) {
-    emit_pre(emit, -3, 1);
+static void emit_cpy_store_locals(emit_t *emit) {
+    emit_pre(emit, -1, 1);
     if (emit->pass == PASS_3) {
-        printf("STORE_SUBSCR\n");
+        printf("STORE_LOCALS\n");
     }
 }
 
@@ -870,8 +870,8 @@ const emit_method_table_t emit_cpython_method_table = {
     emit_cpy_store_global,
     emit_cpy_store_deref,
     emit_cpy_store_attr,
-    emit_cpy_store_locals,
     emit_cpy_store_subscr,
+    emit_cpy_store_locals,
     emit_cpy_delete_fast,
     emit_cpy_delete_name,
     emit_cpy_delete_global,
diff --git a/py/emitnative.c b/py/emitnative.c
index 69022e142c..4c2f941e03 100644
--- a/py/emitnative.c
+++ b/py/emitnative.c
@@ -731,11 +731,6 @@ static void emit_native_store_attr(emit_t *emit, qstr qstr) {
     assert(0);
 }
 
-static void emit_native_store_locals(emit_t *emit) {
-    // not supported
-    assert(0);
-}
-
 static void emit_native_store_subscr(emit_t *emit) {
     // depends on type of subject:
     //  - integer, function, pointer to structure: error
@@ -749,6 +744,13 @@ static void emit_native_store_subscr(emit_t *emit) {
     emit_call(emit, RT_F_STORE_SUBSCR, rt_store_subscr);
 }
 
+static void emit_native_store_locals(emit_t *emit) {
+    // not needed
+    vtype_kind_t vtype;
+    emit_pre_pop_reg(emit, &vtype, REG_TEMP0);
+    emit_post(emit);
+}
+
 static void emit_native_delete_fast(emit_t *emit, qstr qstr, int local_num) {
     // not implemented
     // could support for Python types, just set to None (so GC can reclaim it)
@@ -1146,8 +1148,8 @@ const emit_method_table_t EXPORT_FUN(method_table) = {
     emit_native_store_global,
     emit_native_store_deref,
     emit_native_store_attr,
-    emit_native_store_locals,
     emit_native_store_subscr,
+    emit_native_store_locals,
     emit_native_delete_fast,
     emit_native_delete_name,
     emit_native_delete_global,
diff --git a/py/main.c b/py/main.c
index cca52c4e73..2059a8fc48 100644
--- a/py/main.c
+++ b/py/main.c
@@ -33,7 +33,7 @@ int main(int argc, char **argv) {
         py_parse_node_t pn = py_parse(lex, 0);
         if (pn != PY_PARSE_NODE_NULL) {
             //printf("----------------\n");
-            parse_node_show(pn, 0);
+            //parse_node_show(pn, 0);
             //printf("----------------\n");
             py_compile(pn);
             //printf("----------------\n");
diff --git a/py/runtime.c b/py/runtime.c
index 8403a75272..7e7164d4f0 100644
--- a/py/runtime.c
+++ b/py/runtime.c
@@ -9,8 +9,8 @@
 #include "runtime.h"
 #include "vm.h"
 
-//#define DEBUG_printf(args...) (void)0
-#define DEBUG_printf(args...) printf(args)
+#define DEBUG_printf(args...) (void)0
+//#define DEBUG_printf(args...) printf(args)
 
 #define DEBUG_OP_printf(args...) (void)0
 //#define DEBUG_OP_printf(args...) printf(args)
@@ -46,6 +46,7 @@ typedef enum {
     O_SET,
     O_MAP,
     O_CLASS,
+    O_OBJ,
 } py_obj_kind_t;
 
 typedef enum {
@@ -59,13 +60,15 @@ typedef struct _py_map_elem_t {
 } py_map_elem_t;
 
 typedef struct _py_map_t {
-    py_map_kind_t kind;
+    py_map_kind_t kind; // TODO merge this 1-bit field into alloc or used
     machine_uint_t alloc;
     machine_uint_t used;
     py_map_elem_t *table;
 } py_map_t;
 
-typedef struct _py_obj_base_t {
+typedef struct _py_obj_base_t py_obj_base_t;
+
+struct _py_obj_base_t {
     py_obj_kind_t kind;
     union {
         const char *id;
@@ -101,24 +104,22 @@ typedef struct _py_obj_base_t {
             py_obj_t *table;
         } u_set;
         py_map_t u_map; // for O_MAP
-        /*
-        struct { // for O_MAP
-            int alloc;
-            int used;
-            py_map_elem_t *table;
-        } u_map;
-        */
         struct { // for O_CLASS
-            py_map_t *map;
+            py_map_t *locals;
         } u_class;
+        struct { // for O_OBJ
+            py_obj_base_t *class; // points to a O_CLASS object
+            py_map_t *members;
+        } u_obj;
     };
-} py_obj_base_t;
+};
 
 py_obj_t py_const_none;
 py_obj_t py_const_false;
 py_obj_t py_const_true;
 
-py_map_t map_name;
+py_map_t map_locals;
+py_map_t map_globals;
 py_map_t map_builtins;
 
 // approximatelly doubling primes; made with Mathematica command: Table[Prime[Floor[(1.7)^n]], {n, 3, 24}]
@@ -334,10 +335,22 @@ py_obj_t py_builtin_len(py_obj_t o_in) {
     return TO_SMALL_INT(len);
 }
 
-py_obj_t py_builtin___build_class__(py_obj_t o1, py_obj_t o2) {
+py_obj_t py_builtin___build_class__(py_obj_t o_class_fun, py_obj_t o_class_name) {
+    // we differ from CPython: we set the new __locals__ object here
+    py_map_t old_locals = map_locals;
+    py_map_t *class_locals = py_map_new(MAP_QSTR, 0);
+    map_locals = *class_locals;
+
+    // call the class code
+    rt_call_function_1(o_class_fun, (py_obj_t)0xdeadbeef);
+
+    // restore old __locals__ object
+    map_locals = old_locals;
+
+    // create and return the new class
     py_obj_base_t *o = m_new(py_obj_base_t, 1);
     o->kind = O_CLASS;
-    o->u_class.map = py_map_new(MAP_QSTR, 0);
+    o->u_class.locals = class_locals;
     return o;
 }
 
@@ -353,7 +366,10 @@ void rt_init() {
     py_const_false = py_obj_new_const("False");
     py_const_true = py_obj_new_const("True");
 
-    py_map_init(&map_name, MAP_QSTR, 0);
+    py_map_init(&map_locals, MAP_QSTR, 0);
+
+    py_map_init(&map_globals, MAP_QSTR, 1);
+    py_qstr_map_lookup(&map_globals, qstr_from_str_static("__name__"), true)->value = py_obj_new_str(qstr_from_str_static("__main__"));
 
     py_map_init(&map_builtins, MAP_QSTR, 3);
     py_qstr_map_lookup(&map_builtins, q_print, true)->value = rt_make_function_1(py_builtin_print);
@@ -446,6 +462,25 @@ void rt_assign_inline_asm_code(int unique_code_id, py_fun_t fun, uint len, int n
     }
 }
 
+bool py_obj_is_callable(py_obj_t o_in) {
+    if (IS_SMALL_INT(o_in)) {
+        return false;
+    } else {
+        py_obj_base_t *o = o_in;
+        switch (o->kind) {
+            case O_FUN_0:
+            case O_FUN_1:
+            case O_FUN_2:
+            case O_FUN_N:
+            case O_FUN_BC:
+            case O_FUN_ASM:
+                return true;
+            default:
+                return false;
+        }
+    }
+}
+
 const char *py_obj_get_type_str(py_obj_t o_in) {
     if (IS_SMALL_INT(o_in)) {
         return "int";
@@ -476,6 +511,13 @@ const char *py_obj_get_type_str(py_obj_t o_in) {
                 return "set";
             case O_MAP:
                 return "dict";
+            case O_OBJ:
+            {
+                py_map_elem_t *qn = py_qstr_map_lookup(o->u_obj.class->u_class.locals, qstr_from_str_static("__qualname__"), false);
+                assert(qn != NULL);
+                assert(IS_O(qn->value, O_STR));
+                return qstr_str(((py_obj_base_t*)qn->value)->u_str);
+            }
             default:
                 assert(0);
                 return "UnknownType";
@@ -546,6 +588,7 @@ void py_obj_print(py_obj_t o_in) {
                 break;
             }
             default:
+                printf("<? %d>", o->kind);
                 assert(0);
         }
     }
@@ -587,20 +630,33 @@ py_obj_t rt_load_const_str(qstr qstr) {
 
 py_obj_t rt_load_name(qstr qstr) {
     // logic: search locals, globals, builtins
-    DEBUG_OP_printf("load %s\n", qstr_str(qstr));
-    py_map_elem_t *elem = py_qstr_map_lookup(&map_name, qstr, false);
+    DEBUG_OP_printf("load name %s\n", qstr_str(qstr));
+    py_map_elem_t *elem = py_qstr_map_lookup(&map_locals, qstr, false);
     if (elem == NULL) {
-        elem = py_qstr_map_lookup(&map_builtins, qstr, false);
+        elem = py_qstr_map_lookup(&map_globals, qstr, false);
         if (elem == NULL) {
-            printf("name doesn't exist: %s\n", qstr_str(qstr));
-            assert(0);
+            elem = py_qstr_map_lookup(&map_builtins, qstr, false);
+            if (elem == NULL) {
+                printf("name doesn't exist: %s\n", qstr_str(qstr));
+                assert(0);
+            }
         }
     }
     return elem->value;
 }
 
 py_obj_t rt_load_global(qstr qstr) {
-    return rt_load_name(qstr); // TODO
+    // logic: search globals, builtins
+    DEBUG_OP_printf("load global %s\n", qstr_str(qstr));
+    py_map_elem_t *elem = py_qstr_map_lookup(&map_globals, qstr, false);
+    if (elem == NULL) {
+        elem = py_qstr_map_lookup(&map_builtins, qstr, false);
+        if (elem == NULL) {
+            printf("name doesn't exist: %s\n", qstr_str(qstr));
+            assert(0);
+        }
+    }
+    return elem->value;
 }
 
 py_obj_t rt_load_build_class() {
@@ -614,8 +670,13 @@ py_obj_t rt_load_build_class() {
 }
 
 void rt_store_name(qstr qstr, py_obj_t obj) {
-    DEBUG_OP_printf("store %s <- %p\n", qstr_str(qstr), obj);
-    py_qstr_map_lookup(&map_name, qstr, true)->value = obj;
+    DEBUG_OP_printf("store name %s <- %p\n", qstr_str(qstr), obj);
+    py_qstr_map_lookup(&map_locals, qstr, true)->value = obj;
+}
+
+void rt_store_global(qstr qstr, py_obj_t obj) {
+    DEBUG_OP_printf("store global %s <- %p\n", qstr_str(qstr), obj);
+    py_qstr_map_lookup(&map_globals, qstr, true)->value = obj;
 }
 
 py_obj_t rt_unary_op(int op, py_obj_t arg) {
@@ -803,6 +864,18 @@ py_obj_t rt_call_function_0(py_obj_t fun) {
         assert(o->u_fun_asm.n_args == 0);
         DEBUG_OP_printf("calling inline asm %p with no args\n", o->u_fun_asm.fun);
         return rt_convert_val_from_inline_asm(((inline_asm_fun_0_t)o->u_fun_asm.fun)());
+    } else if (IS_O(fun, O_BOUND_METH)) {
+        py_obj_base_t *o = fun;
+        DEBUG_OP_printf("calling bound method %p with self and no args\n", o->u_bound_meth.meth);
+        return rt_call_function_1(o->u_bound_meth.meth, o->u_bound_meth.self);
+    } else if (IS_O(fun, O_CLASS)) {
+        // instantiate an instance of a class
+        DEBUG_OP_printf("instantiate object of class %p with no args\n", fun);
+        py_obj_base_t *o = m_new(py_obj_base_t, 1);
+        o->kind = O_OBJ;
+        o->u_obj.class = fun;
+        o->u_obj.members = py_map_new(MAP_QSTR, 0);
+        return o;
     } else {
         printf("fun0:%p\n", fun);
         assert(0);
@@ -817,7 +890,10 @@ py_obj_t rt_call_function_1(py_obj_t fun, py_obj_t arg) {
         return ((py_fun_1_t)o->u_fun.fun)(arg);
     } else if (IS_O(fun, O_FUN_BC)) {
         py_obj_base_t *o = fun;
-        assert(o->u_fun_bc.n_args == 1);
+        if (o->u_fun_bc.n_args != 1) {
+            printf("rt_call_function_1: trying to pass 1 argument to a function that takes %d arguments\n", o->u_fun_bc.n_args);
+            assert(0);
+        }
         DEBUG_OP_printf("calling byte code %p with 1 arg\n", o->u_fun_bc.code);
         return py_execute_byte_code(o->u_fun_bc.code, o->u_fun_bc.len, &arg, 1);
     } else if (IS_O(fun, O_FUN_ASM)) {
@@ -845,8 +921,8 @@ py_obj_t rt_call_function_2(py_obj_t fun, py_obj_t arg1, py_obj_t arg2) {
         assert(o->u_fun_bc.n_args == 2);
         DEBUG_OP_printf("calling byte code %p with 2 args\n", o->u_fun_bc.code);
         py_obj_t args[2];
-        args[0] = arg1;
-        args[1] = arg2;
+        args[1] = arg1;
+        args[0] = arg2;
         return py_execute_byte_code(o->u_fun_bc.code, o->u_fun_bc.len, &args[0], 2);
     } else if (IS_O(fun, O_FUN_ASM)) {
         py_obj_base_t *o = fun;
@@ -859,6 +935,19 @@ py_obj_t rt_call_function_2(py_obj_t fun, py_obj_t arg1, py_obj_t arg2) {
     }
 }
 
+// args are in reverse order in the array
+py_obj_t rt_call_function_n(py_obj_t fun, int n_args, const py_obj_t *args) {
+    if (IS_O(fun, O_FUN_BC)) {
+        py_obj_base_t *o = fun;
+        assert(o->u_fun_bc.n_args == n_args);
+        DEBUG_OP_printf("calling byte code %p with %d args\n", o->u_fun_bc.code, n_args);
+        return py_execute_byte_code(o->u_fun_bc.code, o->u_fun_bc.len, args, n_args);
+    } else {
+        assert(0);
+        return py_const_none;
+    }
+}
+
 py_obj_t rt_call_method_1(py_obj_t fun, py_obj_t self) {
     DEBUG_OP_printf("call method %p %p\n", fun, self);
     if (self == NULL) {
@@ -877,6 +966,13 @@ py_obj_t rt_call_method_2(py_obj_t fun, py_obj_t self, py_obj_t arg) {
     }
 }
 
+// args contains: arg(n_args-1)  arg(n_args-2)  ...  arg(0)  self/NULL  fun
+// if n_args==0 then there are only self/NULL and fun
+py_obj_t rt_call_method_n(int n_args, const py_obj_t *args) {
+    DEBUG_OP_printf("call method %p %p %d args\n", args[n_args + 1], args[n_args] , n_args);
+    return rt_call_function_n(args[n_args + 1], n_args + ((args[n_args] == NULL) ? 0 : 1), args);
+}
+
 // items are in reverse order
 py_obj_t rt_build_list(int n_args, py_obj_t *items) {
     py_obj_base_t *o = m_new(py_obj_base_t, 1);
@@ -964,27 +1060,6 @@ py_obj_t rt_store_map(py_obj_t map, py_obj_t key, py_obj_t value) {
     return map;
 }
 
-void rt_store_subscr(py_obj_t base, py_obj_t index, py_obj_t value) {
-    if (IS_O(base, O_LIST) && IS_SMALL_INT(index)) {
-        // list store
-        py_obj_base_t *o = base;
-        int idx = FROM_SMALL_INT(index);
-        if (idx < 0) {
-            idx += o->u_list.len;
-        }
-        if (0 <= idx && idx < o->u_list.len) {
-            o->u_list.items[idx] = value;
-        } else {
-            assert(0);
-        }
-    } else if (IS_O(base, O_MAP)) {
-        // map store
-        py_map_lookup(base, index, true)->value = value;
-    } else {
-        assert(0);
-    }
-}
-
 py_obj_t build_bound_method(py_obj_t self, py_obj_t meth) {
     py_obj_base_t *o = m_new(py_obj_base_t, 1);
     o->kind = O_BOUND_METH;
@@ -994,22 +1069,41 @@ py_obj_t build_bound_method(py_obj_t self, py_obj_t meth) {
 }
 
 py_obj_t rt_load_attr(py_obj_t base, qstr attr) {
-    DEBUG_OP_printf("load %s\n", qstr_str(attr));
+    DEBUG_OP_printf("load attr %s\n", qstr_str(attr));
     if (IS_O(base, O_LIST) && attr == q_append) {
         return build_bound_method(base, fun_list_append);
     } else if (IS_O(base, O_CLASS)) {
         py_obj_base_t *o = base;
-        py_map_elem_t *elem = py_qstr_map_lookup(o->u_class.map, attr, false);
+        py_map_elem_t *elem = py_qstr_map_lookup(o->u_class.locals, attr, false);
         if (elem == NULL) {
-            printf("Nope! %s\n", qstr_str(attr));
-            assert(0);
+            goto no_attr;
         }
         return elem->value;
-    } else {
-        printf("AttributeError: '%s' object has no attribute '%s'\n", py_obj_get_type_str(base), qstr_str(attr));
-        assert(0);
-        return py_const_none;
+    } else if (IS_O(base, O_OBJ)) {
+        // logic: look in obj members then class locals (TODO check this against CPython)
+        py_obj_base_t *o = base;
+        py_map_elem_t *elem = py_qstr_map_lookup(o->u_obj.members, attr, false);
+        if (elem != NULL) {
+            // object member, always treated as a value
+            return elem->value;
+        }
+        elem = py_qstr_map_lookup(o->u_obj.class->u_class.locals, attr, false);
+        if (elem != NULL) {
+            if (py_obj_is_callable(elem->value)) {
+                // class member is callable so build a bound method
+                return build_bound_method(base, elem->value);
+            } else {
+                // class member is a value, so just return that value
+                return elem->value;
+            }
+        }
+        goto no_attr;
     }
+
+no_attr:
+    printf("AttributeError: '%s' object has no attribute '%s'\n", py_obj_get_type_str(base), qstr_str(attr));
+    assert(0);
+    return py_const_none;
 }
 
 void rt_load_method(py_obj_t base, qstr attr, py_obj_t *dest) {
@@ -1017,9 +1111,74 @@ void rt_load_method(py_obj_t base, qstr attr, py_obj_t *dest) {
     if (IS_O(base, O_LIST) && attr == q_append) {
         dest[1] = fun_list_append;
         dest[0] = base;
+        return;
+    } else if (IS_O(base, O_OBJ)) {
+        // logic: look in obj members then class locals (TODO check this against CPython)
+        py_obj_base_t *o = base;
+        py_map_elem_t *elem = py_qstr_map_lookup(o->u_obj.members, attr, false);
+        if (elem != NULL) {
+            // object member, always treated as a value
+            dest[1] = elem->value;
+            dest[0] = NULL;
+            return;
+        }
+        elem = py_qstr_map_lookup(o->u_obj.class->u_class.locals, attr, false);
+        if (elem != NULL) {
+            if (py_obj_is_callable(elem->value)) {
+                // class member is callable so build a bound method
+                dest[1] = elem->value;
+                dest[0] = base;
+                return;
+            } else {
+                // class member is a value, so just return that value
+                dest[1] = elem->value;
+                dest[0] = NULL;
+                return;
+            }
+        }
+        goto no_attr;
+    }
+
+no_attr:
+    dest[1] = rt_load_attr(base, attr);
+    dest[0] = NULL;
+}
+
+void rt_store_attr(py_obj_t base, qstr attr, py_obj_t val) {
+    DEBUG_OP_printf("store attr %p.%s <- %p\n", base, qstr_str(attr), val);
+    if (IS_O(base, O_OBJ)) {
+        // logic: look in class locals (no add) then obj members (add) (TODO check this against CPython)
+        py_obj_base_t *o = base;
+        py_map_elem_t *elem = py_qstr_map_lookup(o->u_obj.class->u_class.locals, attr, false);
+        if (elem != NULL) {
+            elem->value = val;
+        } else {
+            elem = py_qstr_map_lookup(o->u_obj.class->u_class.locals, attr, true)->value = val;
+        }
+    } else {
+        printf("?AttributeError: '%s' object has no attribute '%s'\n", py_obj_get_type_str(base), qstr_str(attr));
+        assert(0);
+    }
+}
+
+void rt_store_subscr(py_obj_t base, py_obj_t index, py_obj_t value) {
+    if (IS_O(base, O_LIST) && IS_SMALL_INT(index)) {
+        // list store
+        py_obj_base_t *o = base;
+        int idx = FROM_SMALL_INT(index);
+        if (idx < 0) {
+            idx += o->u_list.len;
+        }
+        if (0 <= idx && idx < o->u_list.len) {
+            o->u_list.items[idx] = value;
+        } else {
+            assert(0);
+        }
+    } else if (IS_O(base, O_MAP)) {
+        // map store
+        py_map_lookup(base, index, true)->value = value;
     } else {
-        dest[1] = rt_load_attr(base, attr);
-        dest[0] = NULL;
+        assert(0);
     }
 }
 
diff --git a/py/runtime.h b/py/runtime.h
index 72d589231c..d72182cb39 100644
--- a/py/runtime.h
+++ b/py/runtime.h
@@ -100,6 +100,7 @@ py_obj_t rt_load_name(qstr qstr);
 py_obj_t rt_load_global(qstr qstr);
 py_obj_t rt_load_build_class();
 void rt_store_name(qstr qstr, py_obj_t obj);
+void rt_store_global(qstr qstr, py_obj_t obj);
 py_obj_t rt_unary_op(int op, py_obj_t arg);
 py_obj_t rt_binary_op(int op, py_obj_t lhs, py_obj_t rhs);
 py_obj_t rt_compare_op(int op, py_obj_t lhs, py_obj_t rhs);
@@ -113,10 +114,12 @@ py_obj_t rt_call_function_1(py_obj_t fun, py_obj_t arg);
 py_obj_t rt_call_function_2(py_obj_t fun, py_obj_t arg1, py_obj_t arg2);
 py_obj_t rt_call_method_1(py_obj_t fun, py_obj_t self);
 py_obj_t rt_call_method_2(py_obj_t fun, py_obj_t self, py_obj_t arg);
+py_obj_t rt_call_method_n(int n_args, const py_obj_t *args);
 py_obj_t rt_build_list(int n_args, py_obj_t *items);
 py_obj_t rt_build_map(int n_args);
 py_obj_t rt_store_map(py_obj_t map, py_obj_t key, py_obj_t value);
 py_obj_t rt_build_set(int n_args, py_obj_t *items);
-void rt_store_subscr(py_obj_t base, py_obj_t index, py_obj_t val);
 py_obj_t rt_load_attr(py_obj_t base, qstr attr);
 void rt_load_method(py_obj_t base, qstr attr, py_obj_t *dest);
+void rt_store_attr(py_obj_t base, qstr attr, py_obj_t val);
+void rt_store_subscr(py_obj_t base, py_obj_t index, py_obj_t val);
diff --git a/py/vm.c b/py/vm.c
index 63a478fcf3..ac76089a16 100644
--- a/py/vm.c
+++ b/py/vm.c
@@ -14,8 +14,9 @@
 #define PUSH(val) *--sp = (val)
 #define POP() (*sp++)
 
-py_obj_t py_execute_byte_code(byte *code, uint len, py_obj_t *args, uint n_args) {
-    byte *ip = code;
+// args are in reverse order in array
+py_obj_t py_execute_byte_code(const byte *code, uint len, const py_obj_t *args, uint n_args) {
+    const byte *ip = code;
     py_obj_t stack[10];
     py_obj_t *sp = &stack[10]; // stack grows down, sp points to top of stack
     machine_uint_t unum;
@@ -27,14 +28,14 @@ py_obj_t py_execute_byte_code(byte *code, uint len, py_obj_t *args, uint n_args)
     // init args
     for (int i = 0; i < n_args; i++) {
         if (i == 0) {
-            fast0 = args[0];
+            fast0 = args[n_args - 1];
         } else if (i == 1) {
-            fast1 = args[1];
+            fast1 = args[n_args - 2];
         } else if (i == 2) {
-            fast2 = args[2];
+            fast2 = args[n_args - 3];
         } else {
             assert(i - 3 < 4);
-            fastn[i - 3] = args[i];
+            fastn[i - 3] = args[n_args - 1 - i];
         }
     }
 
@@ -137,6 +138,12 @@ py_obj_t py_execute_byte_code(byte *code, uint len, py_obj_t *args, uint n_args)
                 rt_store_name(qstr, POP());
                 break;
 
+            case PYBC_STORE_ATTR:
+                DECODE_QSTR;
+                rt_store_attr(sp[0], qstr, sp[1]);
+                sp += 2;
+                break;
+
             case PYBC_STORE_SUBSCR:
                 rt_store_subscr(sp[1], sp[0], sp[2]);
                 sp += 3;
@@ -254,9 +261,12 @@ py_obj_t py_execute_byte_code(byte *code, uint len, py_obj_t *args, uint n_args)
                 } else if ((unum & 0xff) == 1) {
                     obj2 = *sp++; // the first argument
                     obj1 = *sp++; // the self object (or NULL)
-                    *sp = rt_call_function_2(*sp, obj1, obj2);
+                    *sp = rt_call_method_2(*sp, obj1, obj2);
                 } else {
-                    assert(0);
+                    unum = unum & 0xff;
+                    obj1 = rt_call_method_n(unum, sp);
+                    sp += unum + 1;
+                    *sp = obj1;
                 }
                 break;
 
diff --git a/py/vm.h b/py/vm.h
index f09843a960..1a5bcd9b35 100644
--- a/py/vm.h
+++ b/py/vm.h
@@ -25,8 +25,7 @@
 #define PYBC_STORE_NAME         (0x34) // qstr
 #define PYBC_STORE_GLOBAL       (0x35) // qstr
 #define PYBC_STORE_ATTR         (0x36) // qstr
-#define PYBC_STORE_LOCALS       (0x37)
-#define PYBC_STORE_SUBSCR       (0x38)
+#define PYBC_STORE_SUBSCR       (0x37)
 
 #define PYBC_DELETE_FAST_N      (0x39) // uint
 #define PYBC_DELETE_NAME        (0x3a) // qstr
@@ -94,4 +93,4 @@
 #define PYBC_IMPORT_FROM (0xe1)
 #define PYBC_IMPORT_STAR (0xe2)
 
-py_obj_t py_execute_byte_code(byte *code, uint len, py_obj_t *args, uint n_args);
+py_obj_t py_execute_byte_code(const byte *code, uint len, const py_obj_t *args, uint n_args);
-- 
GitLab