From 9ecbcfff994621aa17fdbb5bf2f0c6710e54ea4c Mon Sep 17 00:00:00 2001
From: Damien <damien.p.george@gmail.com>
Date: Wed, 11 Dec 2013 00:41:43 +0000
Subject: [PATCH] py: work towards working closures.

---
 py/bc.h         | 29 ++++++++++++++------------
 py/compile.c    | 43 +++++++++++++++++++++++++++++++-------
 py/emit.h       |  8 +++----
 py/emitbc.c     | 55 +++++++++++++++++++++++--------------------------
 py/emitcpy.c    | 48 +++++++++++++++++++++---------------------
 py/emitnative.c | 50 ++++++++++++++++++++++----------------------
 py/runtime.c    | 53 +++++++++++++++++++++++++++++++++++++++++++----
 py/runtime.h    |  8 ++++++-
 py/scope.c      |  1 +
 py/scope.h      |  6 +++++-
 py/vm.c         | 21 +++++++++++++++++++
 11 files changed, 214 insertions(+), 108 deletions(-)

diff --git a/py/bc.h b/py/bc.h
index 455d5054f..6dc1dbbd4 100644
--- a/py/bc.h
+++ b/py/bc.h
@@ -12,25 +12,28 @@
 #define PYBC_LOAD_FAST_1        (0x21)
 #define PYBC_LOAD_FAST_2        (0x22)
 #define PYBC_LOAD_FAST_N        (0x23) // uint
-#define PYBC_LOAD_NAME          (0x24) // qstr
-#define PYBC_LOAD_GLOBAL        (0x25) // qstr
-#define PYBC_LOAD_ATTR          (0x26) // qstr
-#define PYBC_LOAD_METHOD        (0x27) // qstr
-#define PYBC_LOAD_BUILD_CLASS   (0x28)
+#define PYBC_LOAD_DEREF         (0x24) // uint
+#define PYBC_LOAD_CLOSURE       (0x25) // uint
+#define PYBC_LOAD_NAME          (0x26) // qstr
+#define PYBC_LOAD_GLOBAL        (0x27) // qstr
+#define PYBC_LOAD_ATTR          (0x28) // qstr
+#define PYBC_LOAD_METHOD        (0x29) // qstr
+#define PYBC_LOAD_BUILD_CLASS   (0x2a)
 
 #define PYBC_STORE_FAST_0       (0x30)
 #define PYBC_STORE_FAST_1       (0x31)
 #define PYBC_STORE_FAST_2       (0x32)
 #define PYBC_STORE_FAST_N       (0x33) // uint
-#define PYBC_STORE_NAME         (0x34) // qstr
-#define PYBC_STORE_GLOBAL       (0x35) // qstr
-#define PYBC_STORE_ATTR         (0x36) // qstr
-#define PYBC_STORE_SUBSCR       (0x37)
+#define PYBC_STORE_DEREF        (0x34) // uint
+#define PYBC_STORE_NAME         (0x35) // qstr
+#define PYBC_STORE_GLOBAL       (0x36) // qstr
+#define PYBC_STORE_ATTR         (0x37) // qstr
+#define PYBC_STORE_SUBSCR       (0x38)
 
 #define PYBC_DELETE_FAST_N      (0x39) // uint
-#define PYBC_DELETE_NAME        (0x3a) // qstr
-#define PYBC_DELETE_GLOBAL      (0x3b) // qstr
-#define PYBC_DELETE_DEREF       (0x3c) // qstr
+#define PYBC_DELETE_DEREF       (0x3a) // uint
+#define PYBC_DELETE_NAME        (0x3b) // qstr
+#define PYBC_DELETE_GLOBAL      (0x3c) // qstr
 #define PYBC_DELETE_ATTR        (0x3d) // qstr
 #define PYBC_DELETE_SUBSCR      (0x3e)
 
@@ -80,7 +83,7 @@
 #define PYBC_YIELD_FROM         (0x83)
 
 #define PYBC_MAKE_FUNCTION      (0x90) // uint
-#define PYBC_MAKE_CLOSURE       (0x91) // uint?
+#define PYBC_MAKE_CLOSURE       (0x91) // uint
 #define PYBC_CALL_FUNCTION      (0x92) // uint
 #define PYBC_CALL_FUNCTION_VAR  (0x93) // uint
 #define PYBC_CALL_FUNCTION_KW   (0x94) // uint
diff --git a/py/compile.c b/py/compile.c
index 17aa20d24..2fd0d6ed1 100644
--- a/py/compile.c
+++ b/py/compile.c
@@ -2842,9 +2842,7 @@ void compile_scope_inline_asm(compiler_t *comp, scope_t *scope, pass_kind_t pass
 void compile_scope_compute_things(compiler_t *comp, scope_t *scope) {
     // in functions, turn implicit globals into explicit globals
     // compute num_locals, and the index of each local
-    // compute the index of free and cell vars (freevars[idx] in CPython)
     scope->num_locals = 0;
-    int num_closed = 0;
     for (int i = 0; i < scope->id_info_len; i++) {
         id_info_t *id = &scope->id_info[i];
         if (scope->kind == SCOPE_CLASS && id->qstr == comp->qstr___class__) {
@@ -2854,16 +2852,47 @@ void compile_scope_compute_things(compiler_t *comp, scope_t *scope) {
         if (scope->kind >= SCOPE_FUNCTION && scope->kind <= SCOPE_GEN_EXPR && id->kind == ID_INFO_KIND_GLOBAL_IMPLICIT) {
             id->kind = ID_INFO_KIND_GLOBAL_EXPLICIT;
         }
+        // note: params always count for 1 local, even if they are a cell
         if (id->param || id->kind == ID_INFO_KIND_LOCAL) {
             id->local_num = scope->num_locals;
             scope->num_locals += 1;
-        } else if (id->kind == ID_INFO_KIND_CELL) {
+        }
+    }
+
+    // compute the index of cell vars (freevars[idx] in CPython)
+    int num_closed = 0;
+    for (int i = 0; i < scope->id_info_len; i++) {
+        id_info_t *id = &scope->id_info[i];
+        if (id->kind == ID_INFO_KIND_CELL) {
             id->local_num = num_closed;
+#if !MICROPY_EMIT_CPYTHON
+            // the cells come right after the fast locals (CPython doesn't add this offset)
+            id->local_num += scope->num_locals;
+#endif
             num_closed += 1;
-        } else if (id->kind == ID_INFO_KIND_FREE) {
-            id_info_t *id_parent = scope_find_local_in_parent(scope, id->qstr);
-            assert(id_parent != NULL); // should never be NULL
-            id->local_num = id_parent->local_num;
+        }
+    }
+    scope->num_cells = num_closed;
+
+    // compute the index of free vars (freevars[idx] in CPython)
+    // make sure they are in the order of the parent scope
+    if (scope->parent != NULL) {
+        int num_free = 0;
+        for (int i = 0; i < scope->parent->id_info_len; i++) {
+            id_info_t *id = &scope->parent->id_info[i];
+            if (id->kind == ID_INFO_KIND_CELL || id->kind == ID_INFO_KIND_FREE) {
+                for (int j = 0; j < scope->id_info_len; j++) {
+                    id_info_t *id2 = &scope->id_info[j];
+                    if (id2->kind == ID_INFO_KIND_FREE && id->qstr == id2->qstr) {
+                        id2->local_num = num_closed + num_free;
+#if !MICROPY_EMIT_CPYTHON
+                        // the frees come right after the cells (CPython doesn't add this offset)
+                        id2->local_num += scope->num_locals;
+#endif
+                        num_free += 1;
+                    }
+                }
+            }
         }
     }
 
diff --git a/py/emit.h b/py/emit.h
index a04acf537..fdfe76437 100644
--- a/py/emit.h
+++ b/py/emit.h
@@ -45,24 +45,24 @@ typedef struct _emit_method_table_t {
     void (*load_const_verbatim_quoted_str)(emit_t *emit, qstr qstr, bool bytes);
     void (*load_const_verbatim_end)(emit_t *emit);
     void (*load_fast)(emit_t *emit, qstr qstr, int local_num);
-    void (*load_name)(emit_t *emit, qstr qstr);
-    void (*load_global)(emit_t *emit, qstr qstr);
     void (*load_deref)(emit_t *emit, qstr qstr, int local_num);
     void (*load_closure)(emit_t *emit, qstr qstr, int local_num);
+    void (*load_name)(emit_t *emit, qstr qstr);
+    void (*load_global)(emit_t *emit, qstr qstr);
     void (*load_attr)(emit_t *emit, qstr qstr);
     void (*load_method)(emit_t *emit, qstr qstr);
     void (*load_build_class)(emit_t *emit);
     void (*store_fast)(emit_t *emit, qstr qstr, int local_num);
+    void (*store_deref)(emit_t *emit, qstr qstr, int local_num);
     void (*store_name)(emit_t *emit, qstr qstr);
     void (*store_global)(emit_t *emit, qstr qstr);
-    void (*store_deref)(emit_t *emit, qstr qstr, int local_num);
     void (*store_attr)(emit_t *emit, qstr qstr);
     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_deref)(emit_t *emit, qstr qstr, int local_num);
     void (*delete_name)(emit_t *emit, qstr qstr);
     void (*delete_global)(emit_t *emit, qstr qstr);
-    void (*delete_deref)(emit_t *emit, qstr qstr, int local_num);
     void (*delete_attr)(emit_t *emit, qstr qstr);
     void (*delete_subscr)(emit_t *emit);
     void (*dup_top)(emit_t *emit);
diff --git a/py/emitbc.c b/py/emitbc.c
index 7d02e439e..880c10807 100644
--- a/py/emitbc.c
+++ b/py/emitbc.c
@@ -75,7 +75,7 @@ static void emit_bc_end_pass(emit_t *emit) {
         emit->code_base = m_new(byte, emit->code_size);
 
     } else if (emit->pass == PASS_3) {
-        rt_assign_byte_code(emit->scope->unique_code_id, emit->code_base, emit->code_size, emit->scope->num_params, emit->scope->num_locals, emit->scope->stack_size, (emit->scope->flags & SCOPE_FLAG_GENERATOR) != 0);
+        rt_assign_byte_code(emit->scope->unique_code_id, emit->code_base, emit->code_size, emit->scope->num_params, emit->scope->num_locals, emit->scope->num_cells, emit->scope->stack_size, (emit->scope->flags & SCOPE_FLAG_GENERATOR) != 0);
     }
 }
 
@@ -302,24 +302,24 @@ static void emit_bc_load_fast(emit_t *emit, qstr qstr, int local_num) {
     }
 }
 
-static void emit_bc_load_name(emit_t *emit, qstr qstr) {
+static void emit_bc_load_deref(emit_t *emit, qstr qstr, int local_num) {
     emit_pre(emit, 1);
-    emit_write_byte_1_qstr(emit, PYBC_LOAD_NAME, qstr);
+    emit_write_byte_1_uint(emit, PYBC_LOAD_DEREF, local_num);
 }
 
-static void emit_bc_load_global(emit_t *emit, qstr qstr) {
+static void emit_bc_load_closure(emit_t *emit, qstr qstr, int local_num) {
     emit_pre(emit, 1);
-    emit_write_byte_1_qstr(emit, PYBC_LOAD_GLOBAL, qstr);
+    emit_write_byte_1_uint(emit, PYBC_LOAD_CLOSURE, local_num);
 }
 
-static void emit_bc_load_deref(emit_t *emit, qstr qstr, int local_num) {
+static void emit_bc_load_name(emit_t *emit, qstr qstr) {
     emit_pre(emit, 1);
-    assert(0);
+    emit_write_byte_1_qstr(emit, PYBC_LOAD_NAME, qstr);
 }
 
-static void emit_bc_load_closure(emit_t *emit, qstr qstr, int local_num) {
+static void emit_bc_load_global(emit_t *emit, qstr qstr) {
     emit_pre(emit, 1);
-    assert(0);
+    emit_write_byte_1_qstr(emit, PYBC_LOAD_GLOBAL, qstr);
 }
 
 static void emit_bc_load_attr(emit_t *emit, qstr qstr) {
@@ -348,6 +348,11 @@ static void emit_bc_store_fast(emit_t *emit, qstr qstr, int local_num) {
     }
 }
 
+static void emit_bc_store_deref(emit_t *emit, qstr qstr, int local_num) {
+    emit_pre(emit, -1);
+    emit_write_byte_1_uint(emit, PYBC_STORE_DEREF, local_num);
+}
+
 static void emit_bc_store_name(emit_t *emit, qstr qstr) {
     emit_pre(emit, -1);
     emit_write_byte_1_qstr(emit, PYBC_STORE_NAME, qstr);
@@ -358,11 +363,6 @@ static void emit_bc_store_global(emit_t *emit, qstr qstr) {
     emit_write_byte_1_qstr(emit, PYBC_STORE_GLOBAL, qstr);
 }
 
-static void emit_bc_store_deref(emit_t *emit, qstr qstr, int local_num) {
-    emit_pre(emit, -1);
-    assert(0);
-}
-
 static void emit_bc_store_attr(emit_t *emit, qstr qstr) {
     emit_pre(emit, -2);
     emit_write_byte_1_qstr(emit, PYBC_STORE_ATTR, qstr);
@@ -385,6 +385,11 @@ static void emit_bc_delete_fast(emit_t *emit, qstr qstr, int local_num) {
     emit_write_byte_1_uint(emit, PYBC_DELETE_FAST_N, local_num);
 }
 
+static void emit_bc_delete_deref(emit_t *emit, qstr qstr, int local_num) {
+    emit_pre(emit, 0);
+    emit_write_byte_1_qstr(emit, PYBC_DELETE_DEREF, local_num);
+}
+
 static void emit_bc_delete_name(emit_t *emit, qstr qstr) {
     emit_pre(emit, 0);
     emit_write_byte_1_qstr(emit, PYBC_DELETE_NAME, qstr);
@@ -395,12 +400,6 @@ static void emit_bc_delete_global(emit_t *emit, qstr qstr) {
     emit_write_byte_1_qstr(emit, PYBC_DELETE_GLOBAL, qstr);
 }
 
-static void emit_bc_delete_deref(emit_t *emit, qstr qstr, int local_num) {
-    emit_pre(emit, 0);
-    assert(0);
-    //emit_write_byte_1_qstr(emit, PYBC_DELETE_DEREF, qstr);
-}
-
 static void emit_bc_delete_attr(emit_t *emit, qstr qstr) {
     emit_pre(emit, -1);
     emit_write_byte_1_qstr(emit, PYBC_DELETE_ATTR, qstr);
@@ -612,11 +611,9 @@ static void emit_bc_make_function(emit_t *emit, scope_t *scope, int n_dict_param
 }
 
 static void emit_bc_make_closure(emit_t *emit, scope_t *scope, int n_dict_params, int n_default_params) {
-    assert(0);
-    emit_pre(emit, -2 - n_default_params - 2 * n_dict_params);
-    if (emit->pass == PASS_3) {
-        printf("MAKE_CLOSURE %d\n", (n_dict_params << 8) | n_default_params);
-    }
+    assert(n_default_params == 0 && n_dict_params == 0);
+    emit_pre(emit, 0);
+    emit_write_byte_1_uint(emit, PYBC_MAKE_CLOSURE, scope->unique_code_id);
 }
 
 static void emit_bc_call_function(emit_t *emit, int n_positional, int n_keyword, bool have_star_arg, bool have_dbl_star_arg) {
@@ -728,24 +725,24 @@ const emit_method_table_t emit_bc_method_table = {
     emit_bc_load_const_verbatim_quoted_str,
     emit_bc_load_const_verbatim_end,
     emit_bc_load_fast,
-    emit_bc_load_name,
-    emit_bc_load_global,
     emit_bc_load_deref,
     emit_bc_load_closure,
+    emit_bc_load_name,
+    emit_bc_load_global,
     emit_bc_load_attr,
     emit_bc_load_method,
     emit_bc_load_build_class,
     emit_bc_store_fast,
+    emit_bc_store_deref,
     emit_bc_store_name,
     emit_bc_store_global,
-    emit_bc_store_deref,
     emit_bc_store_attr,
     emit_bc_store_subscr,
     emit_bc_store_locals,
     emit_bc_delete_fast,
+    emit_bc_delete_deref,
     emit_bc_delete_name,
     emit_bc_delete_global,
-    emit_bc_delete_deref,
     emit_bc_delete_attr,
     emit_bc_delete_subscr,
     emit_bc_dup_top,
diff --git a/py/emitcpy.c b/py/emitcpy.c
index 428e6fd00..a1d03ec4c 100644
--- a/py/emitcpy.c
+++ b/py/emitcpy.c
@@ -265,31 +265,31 @@ static void emit_cpy_load_fast(emit_t *emit, qstr qstr, int local_num) {
     }
 }
 
-static void emit_cpy_load_name(emit_t *emit, qstr qstr) {
+static void emit_cpy_load_deref(emit_t *emit, qstr qstr, int local_num) {
     emit_pre(emit, 1, 3);
     if (emit->pass == PASS_3) {
-        printf("LOAD_NAME %s\n", qstr_str(qstr));
+        printf("LOAD_DEREF %d %s\n", local_num, qstr_str(qstr));
     }
 }
 
-static void emit_cpy_load_global(emit_t *emit, qstr qstr) {
+static void emit_cpy_load_closure(emit_t *emit, qstr qstr, int local_num) {
     emit_pre(emit, 1, 3);
     if (emit->pass == PASS_3) {
-        printf("LOAD_GLOBAL %s\n", qstr_str(qstr));
+        printf("LOAD_CLOSURE %d %s\n", local_num, qstr_str(qstr));
     }
 }
 
-static void emit_cpy_load_deref(emit_t *emit, qstr qstr, int local_num) {
+static void emit_cpy_load_name(emit_t *emit, qstr qstr) {
     emit_pre(emit, 1, 3);
     if (emit->pass == PASS_3) {
-        printf("LOAD_DEREF %d %s\n", local_num, qstr_str(qstr));
+        printf("LOAD_NAME %s\n", qstr_str(qstr));
     }
 }
 
-static void emit_cpy_load_closure(emit_t *emit, qstr qstr, int local_num) {
+static void emit_cpy_load_global(emit_t *emit, qstr qstr) {
     emit_pre(emit, 1, 3);
     if (emit->pass == PASS_3) {
-        printf("LOAD_CLOSURE %d %s\n", local_num, qstr_str(qstr));
+        printf("LOAD_GLOBAL %s\n", qstr_str(qstr));
     }
 }
 
@@ -318,24 +318,24 @@ static void emit_cpy_store_fast(emit_t *emit, qstr qstr, int local_num) {
     }
 }
 
-static void emit_cpy_store_name(emit_t *emit, qstr qstr) {
+static void emit_cpy_store_deref(emit_t *emit, qstr qstr, int local_num) {
     emit_pre(emit, -1, 3);
     if (emit->pass == PASS_3) {
-        printf("STORE_NAME %s\n", qstr_str(qstr));
+        printf("STORE_DEREF %d %s\n", local_num, qstr_str(qstr));
     }
 }
 
-static void emit_cpy_store_global(emit_t *emit, qstr qstr) {
+static void emit_cpy_store_name(emit_t *emit, qstr qstr) {
     emit_pre(emit, -1, 3);
     if (emit->pass == PASS_3) {
-        printf("STORE_GLOBAL %s\n", qstr_str(qstr));
+        printf("STORE_NAME %s\n", qstr_str(qstr));
     }
 }
 
-static void emit_cpy_store_deref(emit_t *emit, qstr qstr, int local_num) {
+static void emit_cpy_store_global(emit_t *emit, qstr qstr) {
     emit_pre(emit, -1, 3);
     if (emit->pass == PASS_3) {
-        printf("STORE_DEREF %d %s\n", local_num, qstr_str(qstr));
+        printf("STORE_GLOBAL %s\n", qstr_str(qstr));
     }
 }
 
@@ -367,24 +367,24 @@ static void emit_cpy_delete_fast(emit_t *emit, qstr qstr, int local_num) {
     }
 }
 
-static void emit_cpy_delete_name(emit_t *emit, qstr qstr) {
+static void emit_cpy_delete_deref(emit_t *emit, qstr qstr, int local_num) {
     emit_pre(emit, 0, 3);
     if (emit->pass == PASS_3) {
-        printf("DELETE_NAME %s\n", qstr_str(qstr));
+        printf("DELETE_DEREF %d %s\n", local_num, qstr_str(qstr));
     }
 }
 
-static void emit_cpy_delete_global(emit_t *emit, qstr qstr) {
+static void emit_cpy_delete_name(emit_t *emit, qstr qstr) {
     emit_pre(emit, 0, 3);
     if (emit->pass == PASS_3) {
-        printf("DELETE_GLOBAL %s\n", qstr_str(qstr));
+        printf("DELETE_NAME %s\n", qstr_str(qstr));
     }
 }
 
-static void emit_cpy_delete_deref(emit_t *emit, qstr qstr, int local_num) {
+static void emit_cpy_delete_global(emit_t *emit, qstr qstr) {
     emit_pre(emit, 0, 3);
     if (emit->pass == PASS_3) {
-        printf("DELETE_DEREF %d %s\n", local_num, qstr_str(qstr));
+        printf("DELETE_GLOBAL %s\n", qstr_str(qstr));
     }
 }
 
@@ -852,24 +852,24 @@ const emit_method_table_t emit_cpython_method_table = {
     emit_cpy_load_const_verbatim_quoted_str,
     emit_cpy_load_const_verbatim_end,
     emit_cpy_load_fast,
-    emit_cpy_load_name,
-    emit_cpy_load_global,
     emit_cpy_load_deref,
     emit_cpy_load_closure,
+    emit_cpy_load_name,
+    emit_cpy_load_global,
     emit_cpy_load_attr,
     emit_cpy_load_method,
     emit_cpy_load_build_class,
     emit_cpy_store_fast,
+    emit_cpy_store_deref,
     emit_cpy_store_name,
     emit_cpy_store_global,
-    emit_cpy_store_deref,
     emit_cpy_store_attr,
     emit_cpy_store_subscr,
     emit_cpy_store_locals,
     emit_cpy_delete_fast,
+    emit_cpy_delete_deref,
     emit_cpy_delete_name,
     emit_cpy_delete_global,
-    emit_cpy_delete_deref,
     emit_cpy_delete_attr,
     emit_cpy_delete_subscr,
     emit_cpy_dup_top,
diff --git a/py/emitnative.c b/py/emitnative.c
index 52805d90c..29acee9e8 100644
--- a/py/emitnative.c
+++ b/py/emitnative.c
@@ -688,6 +688,17 @@ static void emit_native_load_fast(emit_t *emit, qstr qstr, int local_num) {
 #endif
 }
 
+static void emit_native_load_deref(emit_t *emit, qstr qstr, int local_num) {
+    // not implemented
+    // in principle could support this quite easily (ldr r0, [r0, #0]) and then get closed over variables!
+    assert(0);
+}
+
+static void emit_native_load_closure(emit_t *emit, qstr qstr, int local_num) {
+    // not implemented
+    assert(0);
+}
+
 static void emit_native_load_name(emit_t *emit, qstr qstr) {
     emit_pre(emit);
     emit_call_with_imm_arg(emit, RT_F_LOAD_NAME, rt_load_name, qstr, REG_ARG_1);
@@ -700,17 +711,6 @@ static void emit_native_load_global(emit_t *emit, qstr qstr) {
     emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET);
 }
 
-static void emit_native_load_deref(emit_t *emit, qstr qstr, int local_num) {
-    // not implemented
-    // in principle could support this quite easily (ldr r0, [r0, #0]) and then get closed over variables!
-    assert(0);
-}
-
-static void emit_native_load_closure(emit_t *emit, qstr qstr, int local_num) {
-    // not implemented
-    assert(0);
-}
-
 static void emit_native_load_attr(emit_t *emit, qstr qstr) {
     // depends on type of subject:
     //  - integer, function, pointer to integers: error
@@ -771,6 +771,11 @@ static void emit_native_store_fast(emit_t *emit, qstr qstr, int local_num) {
     }
 }
 
+static void emit_native_store_deref(emit_t *emit, qstr qstr, int local_num) {
+    // not implemented
+    assert(0);
+}
+
 static void emit_native_store_name(emit_t *emit, qstr qstr) {
     // rt_store_name, but needs conversion of object (maybe have rt_viper_store_name(obj, type))
     vtype_kind_t vtype;
@@ -785,11 +790,6 @@ static void emit_native_store_global(emit_t *emit, qstr qstr) {
     assert(0);
 }
 
-static void emit_native_store_deref(emit_t *emit, qstr qstr, int local_num) {
-    // not implemented
-    assert(0);
-}
-
 static void emit_native_store_attr(emit_t *emit, qstr qstr) {
     vtype_kind_t vtype_base, vtype_val;
     emit_pre_pop_reg_reg(emit, &vtype_base, REG_ARG_1, &vtype_val, REG_ARG_3); // arg1 = base, arg3 = value
@@ -825,6 +825,11 @@ static void emit_native_delete_fast(emit_t *emit, qstr qstr, int local_num) {
     assert(0);
 }
 
+static void emit_native_delete_deref(emit_t *emit, qstr qstr, int local_num) {
+    // not supported
+    assert(0);
+}
+
 static void emit_native_delete_name(emit_t *emit, qstr qstr) {
     // not implemented
     // use rt_delete_name
@@ -837,11 +842,6 @@ static void emit_native_delete_global(emit_t *emit, qstr qstr) {
     assert(0);
 }
 
-static void emit_native_delete_deref(emit_t *emit, qstr qstr, int local_num) {
-    // not supported
-    assert(0);
-}
-
 static void emit_native_delete_attr(emit_t *emit, qstr qstr) {
     // not supported
     assert(0);
@@ -1280,24 +1280,24 @@ const emit_method_table_t EXPORT_FUN(method_table) = {
     emit_native_load_const_verbatim_quoted_str,
     emit_native_load_const_verbatim_end,
     emit_native_load_fast,
-    emit_native_load_name,
-    emit_native_load_global,
     emit_native_load_deref,
     emit_native_load_closure,
+    emit_native_load_name,
+    emit_native_load_global,
     emit_native_load_attr,
     emit_native_load_method,
     emit_native_load_build_class,
     emit_native_store_fast,
+    emit_native_store_deref,
     emit_native_store_name,
     emit_native_store_global,
-    emit_native_store_deref,
     emit_native_store_attr,
     emit_native_store_subscr,
     emit_native_store_locals,
     emit_native_delete_fast,
+    emit_native_delete_deref,
     emit_native_delete_name,
     emit_native_delete_global,
-    emit_native_delete_deref,
     emit_native_delete_attr,
     emit_native_delete_subscr,
     emit_native_dup_top,
diff --git a/py/runtime.c b/py/runtime.c
index df69ddf0d..189d29fa2 100644
--- a/py/runtime.c
+++ b/py/runtime.c
@@ -43,6 +43,7 @@ typedef enum {
     O_FLOAT,
     O_COMPLEX,
 #endif
+    O_CELL,
     O_EXCEPTION_0,
     O_EXCEPTION_N,
     O_RANGE,
@@ -57,6 +58,7 @@ typedef enum {
     O_GEN_WRAP,
     O_GEN_INSTANCE,
     O_BOUND_METH,
+    O_CLOSURE,
     O_TUPLE,
     O_LIST,
     O_TUPLE_IT,
@@ -101,6 +103,7 @@ struct _py_obj_base_t {
             py_float_t imag;
         } u_complex;
 #endif
+        py_obj_t u_cell; // for O_CELL
         struct { // for O_EXCEPTION_0
             qstr id;
         } u_exc0;
@@ -148,6 +151,10 @@ struct _py_obj_base_t {
             py_obj_t meth;
             py_obj_t self;
         } u_bound_meth;
+        struct { // for O_CLOSURE
+            py_obj_t fun;
+            py_obj_t vars;
+        } u_closure;
         struct { // for O_TUPLE, O_LIST
             machine_uint_t alloc;
             machine_uint_t len;
@@ -382,6 +389,13 @@ py_obj_t py_obj_new_complex(py_float_t real, py_float_t imag) {
 }
 #endif
 
+py_obj_t py_obj_new_cell(py_obj_t val) {
+    py_obj_base_t *o = m_new(py_obj_base_t, 1);
+    o->kind = O_CELL;
+    o->u_cell = val;
+    return (py_obj_t)o;
+}
+
 py_obj_t py_obj_new_exception_0(qstr id) {
     py_obj_base_t *o = m_new(py_obj_base_t, 1);
     o->kind = O_EXCEPTION_0;
@@ -585,6 +599,23 @@ qstr py_obj_get_qstr(py_obj_t arg) {
     }
 }
 
+py_obj_t py_obj_get_cell(py_obj_t cell) {
+    if (IS_O(cell, O_CELL)) {
+        return ((py_obj_base_t*)cell)->u_cell;
+    } else {
+        assert(0);
+        return py_const_none;
+    }
+}
+
+void py_obj_set_cell(py_obj_t cell, py_obj_t val) {
+    if (IS_O(cell, O_CELL)) {
+        ((py_obj_base_t*)cell)->u_cell = val;
+    } else {
+        assert(0);
+    }
+}
+
 py_obj_t *py_obj_get_array_fixed_n(py_obj_t o_in, machine_int_t n) {
     if (IS_O(o_in, O_TUPLE) || IS_O(o_in, O_LIST)) {
         py_obj_base_t *o = o_in;
@@ -879,6 +910,7 @@ typedef struct _py_code_t {
     py_code_kind_t kind;
     int n_args;
     int n_locals;
+    int n_cells;
     int n_stack;
     bool is_generator;
     union {
@@ -1078,19 +1110,20 @@ static void alloc_unique_codes(void) {
     }
 }
 
-void rt_assign_byte_code(int unique_code_id, byte *code, uint len, int n_args, int n_locals, int n_stack, bool is_generator) {
+void rt_assign_byte_code(int unique_code_id, byte *code, uint len, int n_args, int n_locals, int n_cells, int n_stack, bool is_generator) {
     alloc_unique_codes();
 
     assert(unique_code_id < next_unique_code_id);
     unique_codes[unique_code_id].kind = PY_CODE_BYTE;
     unique_codes[unique_code_id].n_args = n_args;
     unique_codes[unique_code_id].n_locals = n_locals;
+    unique_codes[unique_code_id].n_cells = n_cells;
     unique_codes[unique_code_id].n_stack = n_stack;
     unique_codes[unique_code_id].is_generator = is_generator;
     unique_codes[unique_code_id].u_byte.code = code;
     unique_codes[unique_code_id].u_byte.len = len;
 
-    printf("byte code: %d bytes\n", len);
+    //printf("byte code: %d bytes\n", len);
 
 #ifdef DEBUG_PRINT
     DEBUG_printf("assign byte code: id=%d code=%p len=%u n_args=%d\n", unique_code_id, code, len, n_args);
@@ -1120,6 +1153,7 @@ void rt_assign_native_code(int unique_code_id, py_fun_t fun, uint len, int n_arg
     unique_codes[unique_code_id].kind = PY_CODE_NATIVE;
     unique_codes[unique_code_id].n_args = n_args;
     unique_codes[unique_code_id].n_locals = 0;
+    unique_codes[unique_code_id].n_cells = 0;
     unique_codes[unique_code_id].n_stack = 0;
     unique_codes[unique_code_id].is_generator = false;
     unique_codes[unique_code_id].u_native.fun = fun;
@@ -1153,6 +1187,7 @@ void rt_assign_inline_asm_code(int unique_code_id, py_fun_t fun, uint len, int n
     unique_codes[unique_code_id].kind = PY_CODE_INLINE_ASM;
     unique_codes[unique_code_id].n_args = n_args;
     unique_codes[unique_code_id].n_locals = 0;
+    unique_codes[unique_code_id].n_cells = 0;
     unique_codes[unique_code_id].n_stack = 0;
     unique_codes[unique_code_id].is_generator = false;
     unique_codes[unique_code_id].u_inline_asm.fun = fun;
@@ -1562,7 +1597,7 @@ py_obj_t rt_make_function_from_id(int unique_code_id) {
         case PY_CODE_BYTE:
             o->kind = O_FUN_BC;
             o->u_fun_bc.n_args = c->n_args;
-            o->u_fun_bc.n_state = c->n_locals + c->n_stack;
+            o->u_fun_bc.n_state = c->n_locals + c->n_cells + c->n_stack;
             o->u_fun_bc.code = c->u_byte.code;
             break;
         case PY_CODE_NATIVE:
@@ -1588,7 +1623,7 @@ py_obj_t rt_make_function_from_id(int unique_code_id) {
         py_obj_base_t *o2 = m_new(py_obj_base_t, 1);
         o2->kind = O_GEN_WRAP;
         // we have at least 3 locals so the bc can write back fast[0,1,2] safely; should improve how this is done
-        o2->u_gen_wrap.n_state = (c->n_locals < 3 ? 3 : c->n_locals) + c->n_stack;
+        o2->u_gen_wrap.n_state = ((c->n_locals + c->n_cells) < 3 ? 3 : (c->n_locals + c->n_cells)) + c->n_stack;
         o2->u_gen_wrap.fun = o;
         o = o2;
     }
@@ -1634,6 +1669,16 @@ py_obj_t rt_make_function_var(int n_fixed_args, py_fun_var_t f) {
     return o;
 }
 
+py_obj_t rt_make_closure_from_id(int unique_code_id, py_obj_t closure_tuple) {
+    py_obj_t f = rt_make_function_from_id(unique_code_id);
+    // wrap function in closure object
+    py_obj_base_t *f2 = m_new(py_obj_base_t, 1);
+    f2->kind = O_CLOSURE;
+    f2->u_closure.fun = f;
+    f2->u_closure.vars = closure_tuple;
+    return f2;
+}
+
 py_obj_t rt_call_function_0(py_obj_t fun) {
     return rt_call_function_n(fun, 0, NULL);
 }
diff --git a/py/runtime.h b/py/runtime.h
index c1d1936bf..c9b8be500 100644
--- a/py/runtime.h
+++ b/py/runtime.h
@@ -94,18 +94,23 @@ extern py_obj_t py_const_stop_iteration; // special object indicating end of ite
 void rt_init(void);
 void rt_deinit(void);
 int rt_get_unique_code_id(bool is_main_module);
-void rt_assign_byte_code(int unique_code_id, byte *code, uint len, int n_args, int n_locals, int n_stack, bool is_generator);
+void rt_assign_byte_code(int unique_code_id, byte *code, uint len, int n_args, int n_locals, int n_cells, int n_stack, bool is_generator);
 void rt_assign_native_code(int unique_code_id, py_fun_t f, uint len, int n_args);
 void rt_assign_inline_asm_code(int unique_code_id, py_fun_t f, uint len, int n_args);
 void py_obj_print(py_obj_t o);
 
 py_obj_t py_obj_new_int(machine_int_t value);
 py_obj_t py_obj_new_str(qstr qstr);
+py_obj_t py_obj_new_cell(py_obj_t val);
 
 int rt_is_true(py_obj_t arg);
 machine_int_t py_obj_get_int(py_obj_t arg);
 machine_float_t py_obj_get_float(py_obj_t arg);
 qstr py_obj_get_qstr(py_obj_t arg);
+
+py_obj_t py_obj_get_cell(py_obj_t cell);
+void py_obj_set_cell(py_obj_t cell, py_obj_t val);
+
 py_obj_t *py_obj_get_array_fixed_n(py_obj_t o, machine_int_t n);
 
 py_obj_t rt_load_const_dec(qstr qstr);
@@ -124,6 +129,7 @@ py_obj_t rt_make_function_1(py_fun_1_t f);
 py_obj_t rt_make_function_2(py_fun_2_t f);
 py_obj_t rt_make_function(int n_args, py_fun_t code);
 py_obj_t rt_make_function_var(int n_fixed_args, py_fun_var_t f);
+py_obj_t rt_make_closure_from_id(int unique_code_id, py_obj_t closure_tuple);
 py_obj_t rt_call_function_0(py_obj_t fun);
 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);
diff --git a/py/scope.c b/py/scope.c
index 7f844108e..d72d130f2 100644
--- a/py/scope.c
+++ b/py/scope.c
@@ -52,6 +52,7 @@ scope_t *scope_new(scope_kind_t kind, py_parse_node_t pn, uint unique_code_id, u
     scope->num_dict_params = 0;
     */
     scope->num_locals = 0;
+    scope->num_cells = 0;
     scope->unique_code_id = unique_code_id;
     scope->emit_options = emit_options;
 
diff --git a/py/scope.h b/py/scope.h
index 9a04c56f6..f560e3d21 100644
--- a/py/scope.h
+++ b/py/scope.h
@@ -11,7 +11,10 @@ typedef struct _id_info_t {
     bool param;
     int kind;
     qstr qstr;
-    int local_num; // when it's an ID_INFO_KIND_LOCAL this is the unique number of the local
+
+    // when it's an ID_INFO_KIND_LOCAL this is the unique number of the local
+    // whet it's an ID_INFO_KIND_CELL/FREE this is the unique number of the closed over variable
+    int local_num;
 } id_info_t;
 
 // taken from python source, Include/code.h
@@ -46,6 +49,7 @@ typedef struct _scope_t {
     int num_dict_params;
     */
     int num_locals;
+    int num_cells;
     int stack_size;
     uint unique_code_id;
     uint emit_options;
diff --git a/py/vm.c b/py/vm.c
index fabed0f02..55c7bd30c 100644
--- a/py/vm.c
+++ b/py/vm.c
@@ -120,6 +120,16 @@ bool py_execute_byte_code_2(const byte **ip_in_out, py_obj_t *fastn, py_obj_t **
                         PUSH(fastn[unum]);
                         break;
 
+                    case PYBC_LOAD_DEREF:
+                        DECODE_UINT;
+                        PUSH(py_obj_get_cell(fastn[unum]));
+                        break;
+
+                    case PYBC_LOAD_CLOSURE:
+                        DECODE_UINT;
+                        PUSH(fastn[unum]);
+                        break;
+
                     case PYBC_LOAD_NAME:
                         DECODE_QSTR;
                         PUSH(rt_load_name(qstr));
@@ -162,6 +172,11 @@ bool py_execute_byte_code_2(const byte **ip_in_out, py_obj_t *fastn, py_obj_t **
                         fastn[unum] = POP();
                         break;
 
+                    case PYBC_STORE_DEREF:
+                        DECODE_UINT;
+                        py_obj_set_cell(fastn[unum], POP());
+                        break;
+
                     case PYBC_STORE_NAME:
                         DECODE_QSTR;
                         rt_store_name(qstr, POP());
@@ -382,6 +397,12 @@ bool py_execute_byte_code_2(const byte **ip_in_out, py_obj_t *fastn, py_obj_t **
                         PUSH(rt_make_function_from_id(unum));
                         break;
 
+                    case PYBC_MAKE_CLOSURE:
+                        DECODE_UINT;
+                        obj1 = POP();
+                        PUSH(rt_make_closure_from_id(unum, obj1));
+                        break;
+
                     case PYBC_CALL_FUNCTION:
                         DECODE_UINT;
                         assert((unum & 0xff00) == 0); // n_keyword
-- 
GitLab