فهرست منبع

【更新】MicroPython 至最新版本。

Signed-off-by: armink <armink.ztl@gmail.com>
armink 8 سال پیش
والد
کامیت
c4df871327
70فایلهای تغییر یافته به همراه1304 افزوده شده و 614 حذف شده
  1. 4 3
      extmod/machine_signal.c
  2. 30 86
      extmod/machine_spi.c
  3. 3 8
      extmod/machine_spi.h
  4. 377 0
      extmod/modbtree.c
  5. 54 0
      extmod/modframebuf.c
  6. 55 0
      extmod/moduhashlib.c
  7. 11 0
      extmod/modujson.c
  8. 3 1
      extmod/modure.c
  9. 26 1
      extmod/modussl_axtls.c
  10. 20 7
      extmod/modussl_mbedtls.c
  11. 0 3
      extmod/modutimeq.c
  12. 1 1
      extmod/modwebrepl.c
  13. 3 5
      extmod/vfs.c
  14. 98 11
      extmod/vfs_fat.c
  15. 2 4
      extmod/vfs_fat.h
  16. 6 2
      extmod/vfs_fat_diskio.c
  17. 3 6
      extmod/vfs_fat_file.c
  18. 0 108
      extmod/vfs_fat_misc.c
  19. 1 1
      extmod/vfs_reader.c
  20. 2 2
      lib/utils/pyexec.c
  21. 2 0
      lib/utils/pyexec.h
  22. 3 3
      port/genhdr/mpversion.h
  23. 1 0
      port/mpconfigport.h
  24. 1 4
      py/asmarm.c
  25. 2 3
      py/asmthumb.c
  26. 1 3
      py/asmx64.c
  27. 2 1
      py/asmx86.c
  28. 1 1
      py/asmx86.h
  29. 0 6
      py/bc.c
  30. 40 13
      py/builtinimport.c
  31. 2 7
      py/compile.c
  32. 2 0
      py/emitbc.c
  33. 4 1
      py/emitglue.c
  34. 4 1
      py/emitglue.h
  35. 7 3
      py/formatfloat.c
  36. 40 30
      py/gc.c
  37. 7 3
      py/makeqstrdefs.py
  38. 1 1
      py/makeversionhdr.py
  39. 7 15
      py/misc.h
  40. 2 2
      py/mkrules.mk
  41. 16 38
      py/modbuiltins.c
  42. 3 0
      py/modcollections.c
  43. 5 5
      py/modmicropython.c
  44. 25 0
      py/mpconfig.h
  45. 4 8
      py/mpstate.h
  46. 6 6
      py/mpz.c
  47. 2 2
      py/nlrthumb.c
  48. 4 14
      py/obj.h
  49. 166 0
      py/objdeque.c
  50. 13 0
      py/objdict.c
  51. 3 16
      py/objexcept.c
  52. 13 0
      py/objexcept.h
  53. 1 1
      py/objfloat.c
  54. 11 12
      py/objint.c
  55. 2 0
      py/objlist.h
  56. 17 11
      py/objmodule.c
  57. 9 0
      py/objmodule.h
  58. 21 0
      py/objrange.c
  59. 6 5
      py/objstr.c
  60. 1 1
      py/objstrunicode.c
  61. 12 2
      py/parsenum.c
  62. 5 1
      py/persistentcode.c
  63. 43 36
      py/py.mk
  64. 2 1
      py/pystack.c
  65. 1 0
      py/qstr.h
  66. 4 0
      py/qstrdefs.h
  67. 32 46
      py/repl.c
  68. 10 2
      py/runtime.c
  69. 11 18
      py/unicode.c
  70. 28 43
      py/vm.c

+ 4 - 3
extmod/machine_signal.c

@@ -58,7 +58,7 @@ STATIC mp_obj_t signal_make_new(const mp_obj_type_t *type, size_t n_args, size_t
         // If first argument isn't a Pin-like object, we filter out "invert"
         // from keyword arguments and pass them all to the exported Pin
         // constructor to create one.
-        mp_obj_t *pin_args = alloca(n_args + n_kw * 2);
+        mp_obj_t *pin_args = mp_local_alloc((n_args + n_kw * 2) * sizeof(mp_obj_t));
         memcpy(pin_args, args, n_args * sizeof(mp_obj_t));
         const mp_obj_t *src = args + n_args;
         mp_obj_t *dst = pin_args + n_args;
@@ -83,12 +83,13 @@ STATIC mp_obj_t signal_make_new(const mp_obj_type_t *type, size_t n_args, size_t
         if (invert && sig_value != NULL) {
             *sig_value = mp_obj_is_true(*sig_value) ? MP_OBJ_NEW_SMALL_INT(0) : MP_OBJ_NEW_SMALL_INT(1);
         }
-        #if MICROPY_PY_PIN
+
         // Here we pass NULL as a type, hoping that mp_pin_make_new()
         // will just ignore it as set a concrete type. If not, we'd need
         // to expose port's "default" pin type too.
         pin = MICROPY_PY_MACHINE_PIN_MAKE_NEW(NULL, n_args, n_kw, pin_args);
-        #endif
+
+        mp_local_free(pin_args);
     }
     else
     #endif

+ 30 - 86
extmod/machine_spi.c

@@ -38,61 +38,6 @@
 #define MICROPY_PY_MACHINE_SPI_LSB (1)
 #endif
 
-void mp_machine_soft_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest) {
-    mp_machine_soft_spi_obj_t *self = (mp_machine_soft_spi_obj_t*)self_in;
-    uint32_t delay_half = self->delay_half;
-
-    // only MSB transfer is implemented
-
-    // If a port defines MICROPY_PY_MACHINE_SPI_MIN_DELAY, and the configured
-    // delay_half is equal to this value, then the software SPI implementation
-    // will run as fast as possible, limited only by CPU speed and GPIO time.
-    #ifdef MICROPY_PY_MACHINE_SPI_MIN_DELAY
-    if (delay_half == MICROPY_PY_MACHINE_SPI_MIN_DELAY) {
-        for (size_t i = 0; i < len; ++i) {
-            uint8_t data_out = src[i];
-            uint8_t data_in = 0;
-            for (int j = 0; j < 8; ++j, data_out <<= 1) {
-                mp_hal_pin_write(self->mosi, (data_out >> 7) & 1);
-                mp_hal_pin_write(self->sck, 1 - self->polarity);
-                data_in = (data_in << 1) | mp_hal_pin_read(self->miso);
-                mp_hal_pin_write(self->sck, self->polarity);
-            }
-            if (dest != NULL) {
-                dest[i] = data_in;
-            }
-        }
-        return;
-    }
-    #endif
-
-    for (size_t i = 0; i < len; ++i) {
-        uint8_t data_out = src[i];
-        uint8_t data_in = 0;
-        for (int j = 0; j < 8; ++j, data_out <<= 1) {
-            mp_hal_pin_write(self->mosi, (data_out >> 7) & 1);
-            if (self->phase == 0) {
-                mp_hal_delay_us_fast(delay_half);
-                mp_hal_pin_write(self->sck, 1 - self->polarity);
-            } else {
-                mp_hal_pin_write(self->sck, 1 - self->polarity);
-                mp_hal_delay_us_fast(delay_half);
-            }
-            data_in = (data_in << 1) | mp_hal_pin_read(self->miso);
-            if (self->phase == 0) {
-                mp_hal_delay_us_fast(delay_half);
-                mp_hal_pin_write(self->sck, self->polarity);
-            } else {
-                mp_hal_pin_write(self->sck, self->polarity);
-                mp_hal_delay_us_fast(delay_half);
-            }
-        }
-        if (dest != NULL) {
-            dest[i] = data_in;
-        }
-    }
-}
-
 /******************************************************************************/
 // MicroPython bindings for generic machine.SPI
 
@@ -199,9 +144,9 @@ MP_DEFINE_CONST_DICT(mp_machine_spi_locals_dict, machine_spi_locals_dict_table);
 // Implementation of soft SPI
 
 STATIC uint32_t baudrate_from_delay_half(uint32_t delay_half) {
-    #ifdef MICROPY_PY_MACHINE_SPI_MIN_DELAY
-    if (delay_half == MICROPY_PY_MACHINE_SPI_MIN_DELAY) {
-        return MICROPY_PY_MACHINE_SPI_MAX_BAUDRATE;
+    #ifdef MICROPY_HW_SOFTSPI_MIN_DELAY
+    if (delay_half == MICROPY_HW_SOFTSPI_MIN_DELAY) {
+        return MICROPY_HW_SOFTSPI_MAX_BAUDRATE;
     } else
     #endif
     {
@@ -210,9 +155,9 @@ STATIC uint32_t baudrate_from_delay_half(uint32_t delay_half) {
 }
 
 STATIC uint32_t baudrate_to_delay_half(uint32_t baudrate) {
-    #ifdef MICROPY_PY_MACHINE_SPI_MIN_DELAY
-    if (baudrate >= MICROPY_PY_MACHINE_SPI_MAX_BAUDRATE) {
-        return MICROPY_PY_MACHINE_SPI_MIN_DELAY;
+    #ifdef MICROPY_HW_SOFTSPI_MIN_DELAY
+    if (baudrate >= MICROPY_HW_SOFTSPI_MAX_BAUDRATE) {
+        return MICROPY_HW_SOFTSPI_MIN_DELAY;
     } else
     #endif
     {
@@ -229,8 +174,8 @@ STATIC void mp_machine_soft_spi_print(const mp_print_t *print, mp_obj_t self_in,
     mp_machine_soft_spi_obj_t *self = MP_OBJ_TO_PTR(self_in);
     mp_printf(print, "SoftSPI(baudrate=%u, polarity=%u, phase=%u,"
         " sck=" MP_HAL_PIN_FMT ", mosi=" MP_HAL_PIN_FMT ", miso=" MP_HAL_PIN_FMT ")",
-        baudrate_from_delay_half(self->delay_half), self->polarity, self->phase,
-        mp_hal_pin_name(self->sck), mp_hal_pin_name(self->mosi), mp_hal_pin_name(self->miso));
+        baudrate_from_delay_half(self->spi.delay_half), self->spi.polarity, self->spi.phase,
+        mp_hal_pin_name(self->spi.sck), mp_hal_pin_name(self->spi.mosi), mp_hal_pin_name(self->spi.miso));
 }
 
 STATIC mp_obj_t mp_machine_soft_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
@@ -253,9 +198,9 @@ STATIC mp_obj_t mp_machine_soft_spi_make_new(const mp_obj_type_t *type, size_t n
     self->base.type = &mp_machine_soft_spi_type;
 
     // set parameters
-    self->delay_half = baudrate_to_delay_half(args[ARG_baudrate].u_int);
-    self->polarity = args[ARG_polarity].u_int;
-    self->phase = args[ARG_phase].u_int;
+    self->spi.delay_half = baudrate_to_delay_half(args[ARG_baudrate].u_int);
+    self->spi.polarity = args[ARG_polarity].u_int;
+    self->spi.phase = args[ARG_phase].u_int;
     if (args[ARG_bits].u_int != 8) {
         mp_raise_ValueError("bits must be 8");
     }
@@ -267,15 +212,12 @@ STATIC mp_obj_t mp_machine_soft_spi_make_new(const mp_obj_type_t *type, size_t n
         || args[ARG_miso].u_obj == MP_OBJ_NULL) {
         mp_raise_ValueError("must specify all of sck/mosi/miso");
     }
-    self->sck = mp_hal_get_pin_obj(args[ARG_sck].u_obj);
-    self->mosi = mp_hal_get_pin_obj(args[ARG_mosi].u_obj);
-    self->miso = mp_hal_get_pin_obj(args[ARG_miso].u_obj);
+    self->spi.sck = mp_hal_get_pin_obj(args[ARG_sck].u_obj);
+    self->spi.mosi = mp_hal_get_pin_obj(args[ARG_mosi].u_obj);
+    self->spi.miso = mp_hal_get_pin_obj(args[ARG_miso].u_obj);
 
-    // configure pins
-    mp_hal_pin_write(self->sck, self->polarity);
-    mp_hal_pin_output(self->sck);
-    mp_hal_pin_output(self->mosi);
-    mp_hal_pin_input(self->miso);
+    // configure bus
+    mp_soft_spi_ioctl(&self->spi, MP_SPI_IOCTL_INIT);
 
     return MP_OBJ_FROM_PTR(self);
 }
@@ -296,32 +238,34 @@ STATIC void mp_machine_soft_spi_init(mp_obj_base_t *self_in, size_t n_args, cons
     mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
 
     if (args[ARG_baudrate].u_int != -1) {
-        self->delay_half = baudrate_to_delay_half(args[ARG_baudrate].u_int);
+        self->spi.delay_half = baudrate_to_delay_half(args[ARG_baudrate].u_int);
     }
     if (args[ARG_polarity].u_int != -1) {
-        self->polarity = args[ARG_polarity].u_int;
+        self->spi.polarity = args[ARG_polarity].u_int;
     }
     if (args[ARG_phase].u_int != -1) {
-        self->phase = args[ARG_phase].u_int;
+        self->spi.phase = args[ARG_phase].u_int;
     }
     if (args[ARG_sck].u_obj != MP_OBJ_NULL) {
-        self->sck = mp_hal_get_pin_obj(args[ARG_sck].u_obj);
+        self->spi.sck = mp_hal_get_pin_obj(args[ARG_sck].u_obj);
     }
     if (args[ARG_mosi].u_obj != MP_OBJ_NULL) {
-        self->mosi = mp_hal_get_pin_obj(args[ARG_mosi].u_obj);
+        self->spi.mosi = mp_hal_get_pin_obj(args[ARG_mosi].u_obj);
     }
     if (args[ARG_miso].u_obj != MP_OBJ_NULL) {
-        self->miso = mp_hal_get_pin_obj(args[ARG_miso].u_obj);
+        self->spi.miso = mp_hal_get_pin_obj(args[ARG_miso].u_obj);
     }
 
-    // configure pins
-    mp_hal_pin_write(self->sck, self->polarity);
-    mp_hal_pin_output(self->sck);
-    mp_hal_pin_output(self->mosi);
-    mp_hal_pin_input(self->miso);
+    // configure bus
+    mp_soft_spi_ioctl(&self->spi, MP_SPI_IOCTL_INIT);
+}
+
+STATIC void mp_machine_soft_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest) {
+    mp_machine_soft_spi_obj_t *self = (mp_machine_soft_spi_obj_t*)self_in;
+    mp_soft_spi_transfer(&self->spi, len, src, dest);
 }
 
-STATIC const mp_machine_spi_p_t mp_machine_soft_spi_p = {
+const mp_machine_spi_p_t mp_machine_soft_spi_p = {
     .init = mp_machine_soft_spi_init,
     .deinit = NULL,
     .transfer = mp_machine_soft_spi_transfer,

+ 3 - 8
extmod/machine_spi.h

@@ -28,6 +28,7 @@
 
 #include "py/obj.h"
 #include "py/mphal.h"
+#include "drivers/bus/spi.h"
 
 // SPI protocol
 typedef struct _mp_machine_spi_p_t {
@@ -38,19 +39,13 @@ typedef struct _mp_machine_spi_p_t {
 
 typedef struct _mp_machine_soft_spi_obj_t {
     mp_obj_base_t base;
-    uint32_t delay_half; // microsecond delay for half SCK period
-    uint8_t polarity;
-    uint8_t phase;
-    mp_hal_pin_obj_t sck;
-    mp_hal_pin_obj_t mosi;
-    mp_hal_pin_obj_t miso;
+    mp_soft_spi_obj_t spi;
 } mp_machine_soft_spi_obj_t;
 
+extern const mp_machine_spi_p_t mp_machine_soft_spi_p;
 extern const mp_obj_type_t mp_machine_soft_spi_type;
 extern const mp_obj_dict_t mp_machine_spi_locals_dict;
 
-void mp_machine_soft_spi_transfer(mp_obj_base_t *self, size_t len, const uint8_t *src, uint8_t *dest);
-
 mp_obj_t mp_machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args);
 
 MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_read_obj);

+ 377 - 0
extmod/modbtree.c

@@ -0,0 +1,377 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Paul Sokolovsky
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h> // for declaration of global errno variable
+#include <fcntl.h>
+
+#include "py/runtime.h"
+#include "py/stream.h"
+
+#if MICROPY_PY_BTREE
+
+#include <db.h>
+#include <../../btree/btree.h>
+
+typedef struct _mp_obj_btree_t {
+    mp_obj_base_t base;
+    DB *db;
+    mp_obj_t start_key;
+    mp_obj_t end_key;
+    #define FLAG_END_KEY_INCL 1
+    #define FLAG_DESC 2
+    #define FLAG_ITER_TYPE_MASK 0xc0
+    #define FLAG_ITER_KEYS   0x40
+    #define FLAG_ITER_VALUES 0x80
+    #define FLAG_ITER_ITEMS  0xc0
+    byte flags;
+    byte next_flags;
+} mp_obj_btree_t;
+
+STATIC const mp_obj_type_t btree_type;
+
+#define CHECK_ERROR(res) \
+        if (res == RET_ERROR) { \
+            mp_raise_OSError(errno); \
+        }
+
+void __dbpanic(DB *db) {
+    printf("__dbpanic(%p)\n", db);
+}
+
+STATIC mp_obj_btree_t *btree_new(DB *db) {
+    mp_obj_btree_t *o = m_new_obj(mp_obj_btree_t);
+    o->base.type = &btree_type;
+    o->db = db;
+    o->start_key = mp_const_none;
+    o->end_key = mp_const_none;
+    o->next_flags = 0;
+    return o;
+}
+
+STATIC void btree_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
+    (void)kind;
+    mp_obj_btree_t *self = MP_OBJ_TO_PTR(self_in);
+    mp_printf(print, "<btree %p>", self->db);
+}
+
+STATIC mp_obj_t btree_flush(mp_obj_t self_in) {
+    mp_obj_btree_t *self = MP_OBJ_TO_PTR(self_in);
+    return MP_OBJ_NEW_SMALL_INT(__bt_sync(self->db, 0));
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(btree_flush_obj, btree_flush);
+
+STATIC mp_obj_t btree_close(mp_obj_t self_in) {
+    mp_obj_btree_t *self = MP_OBJ_TO_PTR(self_in);
+    return MP_OBJ_NEW_SMALL_INT(__bt_close(self->db));
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(btree_close_obj, btree_close);
+
+STATIC mp_obj_t btree_put(size_t n_args, const mp_obj_t *args) {
+    (void)n_args;
+    mp_obj_btree_t *self = MP_OBJ_TO_PTR(args[0]);
+    DBT key, val;
+    key.data = (void*)mp_obj_str_get_data(args[1], &key.size);
+    val.data = (void*)mp_obj_str_get_data(args[2], &val.size);
+    return MP_OBJ_NEW_SMALL_INT(__bt_put(self->db, &key, &val, 0));
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(btree_put_obj, 3, 4, btree_put);
+
+STATIC mp_obj_t btree_get(size_t n_args, const mp_obj_t *args) {
+    mp_obj_btree_t *self = MP_OBJ_TO_PTR(args[0]);
+    DBT key, val;
+    key.data = (void*)mp_obj_str_get_data(args[1], &key.size);
+    int res = __bt_get(self->db, &key, &val, 0);
+    if (res == RET_SPECIAL) {
+        if (n_args > 2) {
+            return args[2];
+        } else {
+            return mp_const_none;
+        }
+    }
+    CHECK_ERROR(res);
+    return mp_obj_new_bytes(val.data, val.size);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(btree_get_obj, 2, 3, btree_get);
+
+STATIC mp_obj_t btree_seq(size_t n_args, const mp_obj_t *args) {
+    mp_obj_btree_t *self = MP_OBJ_TO_PTR(args[0]);
+    int flags = MP_OBJ_SMALL_INT_VALUE(args[1]);
+    DBT key, val;
+    if (n_args > 2) {
+        key.data = (void*)mp_obj_str_get_data(args[2], &key.size);
+    }
+
+    int res = __bt_seq(self->db, &key, &val, flags);
+    CHECK_ERROR(res);
+    if (res == RET_SPECIAL) {
+        return mp_const_none;
+    }
+
+    mp_obj_t pair_o = mp_obj_new_tuple(2, NULL);
+    mp_obj_tuple_t *pair = MP_OBJ_TO_PTR(pair_o);
+    pair->items[0] = mp_obj_new_bytes(key.data, key.size);
+    pair->items[1] = mp_obj_new_bytes(val.data, val.size);
+    return pair_o;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(btree_seq_obj, 2, 4, btree_seq);
+
+STATIC mp_obj_t btree_init_iter(size_t n_args, const mp_obj_t *args, byte type) {
+    mp_obj_btree_t *self = MP_OBJ_TO_PTR(args[0]);
+    self->next_flags = type;
+    self->start_key = mp_const_none;
+    self->end_key = mp_const_none;
+    if (n_args > 1) {
+        self->start_key = args[1];
+        if (n_args > 2) {
+            self->end_key = args[2];
+            if (n_args > 3) {
+                self->next_flags = type | MP_OBJ_SMALL_INT_VALUE(args[3]);
+            }
+        }
+    }
+    return args[0];
+}
+
+STATIC mp_obj_t btree_keys(size_t n_args, const mp_obj_t *args) {
+    return btree_init_iter(n_args, args, FLAG_ITER_KEYS);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(btree_keys_obj, 1, 4, btree_keys);
+
+STATIC mp_obj_t btree_values(size_t n_args, const mp_obj_t *args) {
+    return btree_init_iter(n_args, args, FLAG_ITER_VALUES);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(btree_values_obj, 1, 4, btree_values);
+
+STATIC mp_obj_t btree_items(size_t n_args, const mp_obj_t *args) {
+    return btree_init_iter(n_args, args, FLAG_ITER_ITEMS);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(btree_items_obj, 1, 4, btree_items);
+
+STATIC mp_obj_t btree_getiter(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) {
+    (void)iter_buf;
+    mp_obj_btree_t *self = MP_OBJ_TO_PTR(self_in);
+    if (self->next_flags != 0) {
+        // If we're called immediately after keys(), values(), or items(),
+        // use their setup for iteration.
+        self->flags = self->next_flags;
+        self->next_flags = 0;
+    } else {
+        // Otherwise, iterate over all keys.
+        self->flags = FLAG_ITER_KEYS;
+        self->start_key = mp_const_none;
+        self->end_key = mp_const_none;
+    }
+
+    return self_in;
+}
+
+STATIC mp_obj_t btree_iternext(mp_obj_t self_in) {
+    mp_obj_btree_t *self = MP_OBJ_TO_PTR(self_in);
+    DBT key, val;
+    int res;
+    bool desc = self->flags & FLAG_DESC;
+    if (self->start_key != MP_OBJ_NULL) {
+        int flags = R_FIRST;
+        if (self->start_key != mp_const_none) {
+            key.data = (void*)mp_obj_str_get_data(self->start_key, &key.size);
+            flags = R_CURSOR;
+        } else if (desc) {
+            flags = R_LAST;
+        }
+        res = __bt_seq(self->db, &key, &val, flags);
+        self->start_key = MP_OBJ_NULL;
+    } else {
+        res = __bt_seq(self->db, &key, &val, desc ? R_PREV : R_NEXT);
+    }
+
+    if (res == RET_SPECIAL) {
+        return MP_OBJ_STOP_ITERATION;
+    }
+    CHECK_ERROR(res);
+
+    if (self->end_key != mp_const_none) {
+        DBT end_key;
+        end_key.data = (void*)mp_obj_str_get_data(self->end_key, &end_key.size);
+        BTREE *t = self->db->internal;
+        int cmp = t->bt_cmp(&key, &end_key);
+        if (desc) {
+            cmp = -cmp;
+        }
+        if (self->flags & FLAG_END_KEY_INCL) {
+            cmp--;
+        }
+        if (cmp >= 0) {
+            self->end_key = MP_OBJ_NULL;
+            return MP_OBJ_STOP_ITERATION;
+        }
+    }
+
+    switch (self->flags & FLAG_ITER_TYPE_MASK) {
+        case FLAG_ITER_KEYS:
+            return mp_obj_new_bytes(key.data, key.size);
+        case FLAG_ITER_VALUES:
+            return mp_obj_new_bytes(val.data, val.size);
+        default: {
+            mp_obj_t pair_o = mp_obj_new_tuple(2, NULL);
+            mp_obj_tuple_t *pair = MP_OBJ_TO_PTR(pair_o);
+            pair->items[0] = mp_obj_new_bytes(key.data, key.size);
+            pair->items[1] = mp_obj_new_bytes(val.data, val.size);
+            return pair_o;
+        }
+    }
+}
+
+STATIC mp_obj_t btree_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) {
+    mp_obj_btree_t *self = MP_OBJ_TO_PTR(self_in);
+    if (value == MP_OBJ_NULL) {
+        // delete
+        DBT key;
+        key.data = (void*)mp_obj_str_get_data(index, &key.size);
+        int res = __bt_delete(self->db, &key, 0);
+        if (res == RET_SPECIAL) {
+            nlr_raise(mp_obj_new_exception(&mp_type_KeyError));
+        }
+        CHECK_ERROR(res);
+        return mp_const_none;
+    } else if (value == MP_OBJ_SENTINEL) {
+        // load
+        DBT key, val;
+        key.data = (void*)mp_obj_str_get_data(index, &key.size);
+        int res = __bt_get(self->db, &key, &val, 0);
+        if (res == RET_SPECIAL) {
+            nlr_raise(mp_obj_new_exception(&mp_type_KeyError));
+        }
+        CHECK_ERROR(res);
+        return mp_obj_new_bytes(val.data, val.size);
+    } else {
+        // store
+        DBT key, val;
+        key.data = (void*)mp_obj_str_get_data(index, &key.size);
+        val.data = (void*)mp_obj_str_get_data(value, &val.size);
+        int res = __bt_put(self->db, &key, &val, 0);
+        CHECK_ERROR(res);
+        return mp_const_none;
+    }
+}
+
+STATIC mp_obj_t btree_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
+    mp_obj_btree_t *self = MP_OBJ_TO_PTR(lhs_in);
+    switch (op) {
+        case MP_BINARY_OP_CONTAINS: {
+            DBT key, val;
+            key.data = (void*)mp_obj_str_get_data(rhs_in, &key.size);
+            int res = __bt_get(self->db, &key, &val, 0);
+            CHECK_ERROR(res);
+            return mp_obj_new_bool(res != RET_SPECIAL);
+        }
+        default:
+            // op not supported
+            return MP_OBJ_NULL;
+    }
+}
+
+STATIC const mp_rom_map_elem_t btree_locals_dict_table[] = {
+    { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&btree_close_obj) },
+    { MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&btree_flush_obj) },
+    { MP_ROM_QSTR(MP_QSTR_get), MP_ROM_PTR(&btree_get_obj) },
+    { MP_ROM_QSTR(MP_QSTR_put), MP_ROM_PTR(&btree_put_obj) },
+    { MP_ROM_QSTR(MP_QSTR_seq), MP_ROM_PTR(&btree_seq_obj) },
+    { MP_ROM_QSTR(MP_QSTR_keys), MP_ROM_PTR(&btree_keys_obj) },
+    { MP_ROM_QSTR(MP_QSTR_values), MP_ROM_PTR(&btree_values_obj) },
+    { MP_ROM_QSTR(MP_QSTR_items), MP_ROM_PTR(&btree_items_obj) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(btree_locals_dict, btree_locals_dict_table);
+
+STATIC const mp_obj_type_t btree_type = {
+    { &mp_type_type },
+    // Save on qstr's, reuse same as for module
+    .name = MP_QSTR_btree,
+    .print = btree_print,
+    .getiter = btree_getiter,
+    .iternext = btree_iternext,
+    .binary_op = btree_binary_op,
+    .subscr = btree_subscr,
+    .locals_dict = (void*)&btree_locals_dict,
+};
+
+STATIC FILEVTABLE btree_stream_fvtable = {
+    mp_stream_posix_read,
+    mp_stream_posix_write,
+    mp_stream_posix_lseek,
+    mp_stream_posix_fsync
+};
+
+STATIC mp_obj_t mod_btree_open(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+    static const mp_arg_t allowed_args[] = {
+        { MP_QSTR_flags, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
+        { MP_QSTR_cachesize, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
+        { MP_QSTR_pagesize, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
+        { MP_QSTR_minkeypage, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
+    };
+
+    // Make sure we got a stream object
+    mp_get_stream_raise(pos_args[0], MP_STREAM_OP_READ | MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL);
+
+    struct {
+        mp_arg_val_t flags;
+        mp_arg_val_t cachesize;
+        mp_arg_val_t pagesize;
+        mp_arg_val_t minkeypage;
+    } args;
+    mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args,
+        MP_ARRAY_SIZE(allowed_args), allowed_args, (mp_arg_val_t*)&args);
+    BTREEINFO openinfo = {0};
+    openinfo.flags = args.flags.u_int;
+    openinfo.cachesize = args.cachesize.u_int;
+    openinfo.psize = args.pagesize.u_int;
+    openinfo.minkeypage = args.minkeypage.u_int;
+
+    DB *db = __bt_open(pos_args[0], &btree_stream_fvtable, &openinfo, /*dflags*/0);
+    if (db == NULL) {
+        mp_raise_OSError(errno);
+    }
+    return MP_OBJ_FROM_PTR(btree_new(db));
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_btree_open_obj, 1, mod_btree_open);
+
+STATIC const mp_rom_map_elem_t mp_module_btree_globals_table[] = {
+    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_btree) },
+    { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mod_btree_open_obj) },
+    { MP_ROM_QSTR(MP_QSTR_INCL), MP_ROM_INT(FLAG_END_KEY_INCL) },
+    { MP_ROM_QSTR(MP_QSTR_DESC), MP_ROM_INT(FLAG_DESC) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(mp_module_btree_globals, mp_module_btree_globals_table);
+
+const mp_obj_module_t mp_module_btree = {
+    .base = { &mp_type_module },
+    .globals = (mp_obj_dict_t*)&mp_module_btree_globals,
+};
+
+#endif // MICROPY_PY_BTREE

+ 54 - 0
extmod/modframebuf.c

@@ -54,7 +54,9 @@ typedef struct _mp_framebuf_p_t {
 // constants for formats
 #define FRAMEBUF_MVLSB    (0)
 #define FRAMEBUF_RGB565   (1)
+#define FRAMEBUF_GS2_HMSB (5)
 #define FRAMEBUF_GS4_HMSB (2)
+#define FRAMEBUF_GS8      (6)
 #define FRAMEBUF_MHLSB    (3)
 #define FRAMEBUF_MHMSB    (4)
 
@@ -130,6 +132,30 @@ STATIC void rgb565_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, i
     }
 }
 
+// Functions for GS2_HMSB format
+
+STATIC void gs2_hmsb_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) {
+    uint8_t *pixel = &((uint8_t*)fb->buf)[(x + y * fb->stride) >> 2];
+    uint8_t shift = (x & 0x3) << 1;
+    uint8_t mask = 0x3 << shift;
+    uint8_t color = (col & 0x3) << shift;
+    *pixel = color | (*pixel & (~mask));
+}
+
+STATIC uint32_t gs2_hmsb_getpixel(const mp_obj_framebuf_t *fb, int x, int y) {
+    uint8_t pixel = ((uint8_t*)fb->buf)[(x + y * fb->stride) >> 2];
+    uint8_t shift = (x & 0x3) << 1;
+    return (pixel >> shift) & 0x3;
+}
+
+STATIC void gs2_hmsb_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) {
+    for (int xx=x; xx < x+w; xx++) {
+        for (int yy=y; yy < y+h; yy++) {
+            gs2_hmsb_setpixel(fb, xx, yy, col);
+        }
+    }
+}
+
 // Functions for GS4_HMSB format
 
 STATIC void gs4_hmsb_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) {
@@ -181,10 +207,31 @@ STATIC void gs4_hmsb_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w,
     }
 }
 
+// Functions for GS8 format
+
+STATIC void gs8_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) {
+    uint8_t *pixel = &((uint8_t*)fb->buf)[(x + y * fb->stride)];
+    *pixel = col & 0xff;
+}
+
+STATIC uint32_t gs8_getpixel(const mp_obj_framebuf_t *fb, int x, int y) {
+    return ((uint8_t*)fb->buf)[(x + y * fb->stride)];
+}
+
+STATIC void gs8_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) {
+    uint8_t *pixel = &((uint8_t*)fb->buf)[(x + y * fb->stride)];
+    while (h--) {
+        memset(pixel, col, w);
+        pixel += fb->stride;
+    }
+}
+
 STATIC mp_framebuf_p_t formats[] = {
     [FRAMEBUF_MVLSB] = {mvlsb_setpixel, mvlsb_getpixel, mvlsb_fill_rect},
     [FRAMEBUF_RGB565] = {rgb565_setpixel, rgb565_getpixel, rgb565_fill_rect},
+    [FRAMEBUF_GS2_HMSB] = {gs2_hmsb_setpixel, gs2_hmsb_getpixel, gs2_hmsb_fill_rect},
     [FRAMEBUF_GS4_HMSB] = {gs4_hmsb_setpixel, gs4_hmsb_getpixel, gs4_hmsb_fill_rect},
+    [FRAMEBUF_GS8] = {gs8_setpixel, gs8_getpixel, gs8_fill_rect},
     [FRAMEBUF_MHLSB] = {mono_horiz_setpixel, mono_horiz_getpixel, mono_horiz_fill_rect},
     [FRAMEBUF_MHMSB] = {mono_horiz_setpixel, mono_horiz_getpixel, mono_horiz_fill_rect},
 };
@@ -240,9 +287,14 @@ STATIC mp_obj_t framebuf_make_new(const mp_obj_type_t *type, size_t n_args, size
         case FRAMEBUF_MHMSB:
             o->stride = (o->stride + 7) & ~7;
             break;
+        case FRAMEBUF_GS2_HMSB:
+            o->stride = (o->stride + 3) & ~3;
+            break;
         case FRAMEBUF_GS4_HMSB:
             o->stride = (o->stride + 1) & ~1;
             break;
+        case FRAMEBUF_GS8:
+            break;
         default:
             mp_raise_ValueError("invalid format");
     }
@@ -579,7 +631,9 @@ STATIC const mp_rom_map_elem_t framebuf_module_globals_table[] = {
     { MP_ROM_QSTR(MP_QSTR_MVLSB), MP_ROM_INT(FRAMEBUF_MVLSB) },
     { MP_ROM_QSTR(MP_QSTR_MONO_VLSB), MP_ROM_INT(FRAMEBUF_MVLSB) },
     { MP_ROM_QSTR(MP_QSTR_RGB565), MP_ROM_INT(FRAMEBUF_RGB565) },
+    { MP_ROM_QSTR(MP_QSTR_GS2_HMSB), MP_ROM_INT(FRAMEBUF_GS2_HMSB) },
     { MP_ROM_QSTR(MP_QSTR_GS4_HMSB), MP_ROM_INT(FRAMEBUF_GS4_HMSB) },
+    { MP_ROM_QSTR(MP_QSTR_GS8), MP_ROM_INT(FRAMEBUF_GS8) },
     { MP_ROM_QSTR(MP_QSTR_MONO_HLSB), MP_ROM_INT(FRAMEBUF_MHLSB) },
     { MP_ROM_QSTR(MP_QSTR_MONO_HMSB), MP_ROM_INT(FRAMEBUF_MHMSB) },
 };

+ 55 - 0
extmod/moduhashlib.c

@@ -32,10 +32,19 @@
 #if MICROPY_PY_UHASHLIB
 
 #include "crypto-algorithms/sha256.h"
+
 #if MICROPY_PY_UHASHLIB_SHA1
+
+#if MICROPY_SSL_AXTLS
 #include "lib/axtls/crypto/crypto.h"
 #endif
 
+#if MICROPY_SSL_MBEDTLS
+#include "mbedtls/sha1.h"
+#endif
+
+#endif
+
 typedef struct _mp_obj_hash_t {
     mp_obj_base_t base;
     char state[0];
@@ -57,6 +66,7 @@ STATIC mp_obj_t hash_make_new(const mp_obj_type_t *type, size_t n_args, size_t n
 #if MICROPY_PY_UHASHLIB_SHA1
 STATIC mp_obj_t sha1_update(mp_obj_t self_in, mp_obj_t arg);
 
+#if MICROPY_SSL_AXTLS
 STATIC mp_obj_t sha1_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
     mp_arg_check_num(n_args, n_kw, 0, 1, false);
     mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(SHA1_CTX));
@@ -69,6 +79,22 @@ STATIC mp_obj_t sha1_make_new(const mp_obj_type_t *type, size_t n_args, size_t n
 }
 #endif
 
+#if MICROPY_SSL_MBEDTLS
+STATIC mp_obj_t sha1_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+    mp_arg_check_num(n_args, n_kw, 0, 1, false);
+    mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(mbedtls_sha1_context));
+    o->base.type = type;
+    mbedtls_sha1_init((mbedtls_sha1_context*)o->state);
+    mbedtls_sha1_starts((mbedtls_sha1_context*)o->state);
+    if (n_args == 1) {
+        sha1_update(MP_OBJ_FROM_PTR(o), args[0]);
+    }
+    return MP_OBJ_FROM_PTR(o);
+}
+#endif
+
+#endif
+
 STATIC mp_obj_t hash_update(mp_obj_t self_in, mp_obj_t arg) {
     mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in);
     mp_buffer_info_t bufinfo;
@@ -79,6 +105,8 @@ STATIC mp_obj_t hash_update(mp_obj_t self_in, mp_obj_t arg) {
 MP_DEFINE_CONST_FUN_OBJ_2(hash_update_obj, hash_update);
 
 #if MICROPY_PY_UHASHLIB_SHA1
+
+#if MICROPY_SSL_AXTLS
 STATIC mp_obj_t sha1_update(mp_obj_t self_in, mp_obj_t arg) {
     mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in);
     mp_buffer_info_t bufinfo;
@@ -86,6 +114,18 @@ STATIC mp_obj_t sha1_update(mp_obj_t self_in, mp_obj_t arg) {
     SHA1_Update((SHA1_CTX*)self->state, bufinfo.buf, bufinfo.len);
     return mp_const_none;
 }
+#endif
+
+#if MICROPY_SSL_MBEDTLS
+STATIC mp_obj_t sha1_update(mp_obj_t self_in, mp_obj_t arg) {
+    mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in);
+    mp_buffer_info_t bufinfo;
+    mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ);
+    mbedtls_sha1_update((mbedtls_sha1_context*)self->state, bufinfo.buf, bufinfo.len);
+    return mp_const_none;
+}
+#endif
+
 MP_DEFINE_CONST_FUN_OBJ_2(sha1_update_obj, sha1_update);
 #endif
 
@@ -99,6 +139,8 @@ STATIC mp_obj_t hash_digest(mp_obj_t self_in) {
 MP_DEFINE_CONST_FUN_OBJ_1(hash_digest_obj, hash_digest);
 
 #if MICROPY_PY_UHASHLIB_SHA1
+
+#if MICROPY_SSL_AXTLS
 STATIC mp_obj_t sha1_digest(mp_obj_t self_in) {
     mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in);
     vstr_t vstr;
@@ -106,6 +148,19 @@ STATIC mp_obj_t sha1_digest(mp_obj_t self_in) {
     SHA1_Final((byte*)vstr.buf, (SHA1_CTX*)self->state);
     return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
 }
+#endif
+
+#if MICROPY_SSL_MBEDTLS
+STATIC mp_obj_t sha1_digest(mp_obj_t self_in) {
+    mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in);
+    vstr_t vstr;
+    vstr_init_len(&vstr, 20);
+    mbedtls_sha1_finish((mbedtls_sha1_context*)self->state, (byte*)vstr.buf);
+    mbedtls_sha1_free((mbedtls_sha1_context*)self->state);
+    return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
+}
+#endif
+
 MP_DEFINE_CONST_FUN_OBJ_1(sha1_digest_obj, sha1_digest);
 #endif
 

+ 11 - 0
extmod/modujson.c

@@ -34,6 +34,16 @@
 
 #if MICROPY_PY_UJSON
 
+STATIC mp_obj_t mod_ujson_dump(mp_obj_t obj, mp_obj_t stream) {
+    if (!MP_OBJ_IS_OBJ(stream)) {
+        mp_raise_TypeError(NULL);
+    }
+    mp_print_t print = {MP_OBJ_TO_PTR(stream), mp_stream_write_adaptor};
+    mp_obj_print_helper(&print, obj, PRINT_JSON);
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_ujson_dump_obj, mod_ujson_dump);
+
 STATIC mp_obj_t mod_ujson_dumps(mp_obj_t obj) {
     vstr_t vstr;
     mp_print_t print;
@@ -283,6 +293,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_ujson_loads_obj, mod_ujson_loads);
 
 STATIC const mp_rom_map_elem_t mp_module_ujson_globals_table[] = {
     { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ujson) },
+    { MP_ROM_QSTR(MP_QSTR_dump), MP_ROM_PTR(&mod_ujson_dump_obj) },
     { MP_ROM_QSTR(MP_QSTR_dumps), MP_ROM_PTR(&mod_ujson_dumps_obj) },
     { MP_ROM_QSTR(MP_QSTR_load), MP_ROM_PTR(&mod_ujson_load_obj) },
     { MP_ROM_QSTR(MP_QSTR_loads), MP_ROM_PTR(&mod_ujson_loads_obj) },

+ 3 - 1
extmod/modure.c

@@ -144,7 +144,7 @@ STATIC mp_obj_t re_split(size_t n_args, const mp_obj_t *args) {
     }
 
     mp_obj_t retval = mp_obj_new_list(0, NULL);
-    const char **caps = alloca(caps_num * sizeof(char*));
+    const char **caps = mp_local_alloc(caps_num * sizeof(char*));
     while (true) {
         // cast is a workaround for a bug in msvc: it treats const char** as a const pointer instead of a pointer to pointer to const char
         memset((char**)caps, 0, caps_num * sizeof(char*));
@@ -165,6 +165,8 @@ STATIC mp_obj_t re_split(size_t n_args, const mp_obj_t *args) {
             break;
         }
     }
+    // cast is a workaround for a bug in msvc (see above)
+    mp_local_free((char**)caps);
 
     mp_obj_t s = mp_obj_new_str_of_type(str_type, (const byte*)subj.begin, subj.end - subj.begin);
     mp_obj_list_append(retval, s);

+ 26 - 1
extmod/modussl_axtls.c

@@ -44,6 +44,8 @@ typedef struct _mp_obj_ssl_socket_t {
 } mp_obj_ssl_socket_t;
 
 struct ssl_args {
+    mp_arg_val_t key;
+    mp_arg_val_t cert;
     mp_arg_val_t server_side;
     mp_arg_val_t server_hostname;
 };
@@ -62,10 +64,28 @@ STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) {
     o->sock = sock;
 
     uint32_t options = SSL_SERVER_VERIFY_LATER;
+    if (args->key.u_obj != mp_const_none) {
+        options |= SSL_NO_DEFAULT_KEY;
+    }
     if ((o->ssl_ctx = ssl_ctx_new(options, SSL_DEFAULT_CLNT_SESS)) == NULL) {
         mp_raise_OSError(MP_EINVAL);
     }
 
+    if (args->key.u_obj != mp_const_none) {
+        size_t len;
+        const byte *data = (const byte*)mp_obj_str_get_data(args->key.u_obj, &len);
+        int res = ssl_obj_memory_load(o->ssl_ctx, SSL_OBJ_RSA_KEY, data, len, NULL);
+        if (res != SSL_OK) {
+            mp_raise_ValueError("invalid key");
+        }
+
+        data = (const byte*)mp_obj_str_get_data(args->cert.u_obj, &len);
+        res = ssl_obj_memory_load(o->ssl_ctx, SSL_OBJ_X509_CERT, data, len, NULL);
+        if (res != SSL_OK) {
+            mp_raise_ValueError("invalid cert");
+        }
+    }
+
     if (args->server_side.u_bool) {
         o->ssl_sock = ssl_server_new(o->ssl_ctx, (long)sock);
     } else {
@@ -113,7 +133,7 @@ STATIC mp_uint_t socket_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errc
         mp_int_t r = ssl_read(o->ssl_sock, &o->buf);
         if (r == SSL_OK) {
             // SSL_OK from ssl_read() means "everything is ok, but there's
-            // not user data yet. So, we just keep reading.
+            // no user data yet". So, we just keep reading.
             continue;
         }
         if (r < 0) {
@@ -121,6 +141,9 @@ STATIC mp_uint_t socket_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errc
                 // EOF
                 return 0;
             }
+            if (r == SSL_EAGAIN) {
+                r = MP_EAGAIN;
+            }
             *errcode = r;
             return MP_STREAM_ERROR;
         }
@@ -208,6 +231,8 @@ STATIC const mp_obj_type_t ussl_socket_type = {
 STATIC mp_obj_t mod_ssl_wrap_socket(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
     // TODO: Implement more args
     static const mp_arg_t allowed_args[] = {
+        { MP_QSTR_key, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
+        { MP_QSTR_cert, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
         { MP_QSTR_server_side, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
         { MP_QSTR_server_hostname, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
     };

+ 20 - 7
extmod/modussl_mbedtls.c

@@ -141,8 +141,7 @@ STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) {
     const byte seed[] = "upy";
     ret = mbedtls_ctr_drbg_seed(&o->ctr_drbg, null_entropy_func/*mbedtls_entropy_func*/, &o->entropy, seed, sizeof(seed));
     if (ret != 0) {
-        printf("ret=%d\n", ret);
-        assert(0);
+        goto cleanup;
     }
 
     ret = mbedtls_ssl_config_defaults(&o->conf,
@@ -150,7 +149,7 @@ STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) {
                     MBEDTLS_SSL_TRANSPORT_STREAM,
                     MBEDTLS_SSL_PRESET_DEFAULT);
     if (ret != 0) {
-        assert(0);
+        goto cleanup;
     }
 
     mbedtls_ssl_conf_authmode(&o->conf, MBEDTLS_SSL_VERIFY_NONE);
@@ -161,14 +160,14 @@ STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) {
 
     ret = mbedtls_ssl_setup(&o->ssl, &o->conf);
     if (ret != 0) {
-        assert(0);
+        goto cleanup;
     }
 
     if (args->server_hostname.u_obj != mp_const_none) {
         const char *sni = mp_obj_str_get_str(args->server_hostname.u_obj);
         ret = mbedtls_ssl_set_hostname(&o->ssl, sni);
         if (ret != 0) {
-            assert(0);
+            goto cleanup;
         }
     }
 
@@ -194,13 +193,27 @@ STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) {
 
     while ((ret = mbedtls_ssl_handshake(&o->ssl)) != 0) {
         if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
-            //assert(0);
             printf("mbedtls_ssl_handshake error: -%x\n", -ret);
-            mp_raise_OSError(MP_EIO);
+            goto cleanup;
         }
     }
 
     return o;
+
+cleanup:
+    mbedtls_pk_free(&o->pkey);
+    mbedtls_x509_crt_free(&o->cert);
+    mbedtls_x509_crt_free(&o->cacert);
+    mbedtls_ssl_free(&o->ssl);
+    mbedtls_ssl_config_free(&o->conf);
+    mbedtls_ctr_drbg_free(&o->ctr_drbg);
+    mbedtls_entropy_free(&o->entropy);
+
+    if (ret == MBEDTLS_ERR_SSL_ALLOC_FAILED) {
+        mp_raise_OSError(MP_ENOMEM);
+    } else {
+        mp_raise_OSError(MP_EIO);
+    }
 }
 
 STATIC mp_obj_t mod_ssl_getpeercert(mp_obj_t o_in, mp_obj_t binary_form) {

+ 0 - 3
extmod/modutimeq.c

@@ -26,7 +26,6 @@
  */
 
 #include <string.h>
-#include <stdio.h>
 
 #include "py/objlist.h"
 #include "py/runtime.h"
@@ -36,9 +35,7 @@
 
 #define MODULO MICROPY_PY_UTIME_TICKS_PERIOD
 
-#ifndef DEBUG
 #define DEBUG 0
-#endif
 
 // the algorithm here is modelled on CPython's heapq.py
 

+ 1 - 1
extmod/modwebrepl.c

@@ -141,7 +141,7 @@ STATIC void handle_op(mp_obj_webrepl_t *self) {
     // Handle operations requiring opened file
 
     mp_obj_t open_args[2] = {
-        mp_obj_new_str(self->hdr.fname, strlen(self->hdr.fname), false),
+        mp_obj_new_str(self->hdr.fname, strlen(self->hdr.fname)),
         MP_OBJ_NEW_QSTR(MP_QSTR_rb)
     };
 

+ 3 - 5
extmod/vfs.c

@@ -261,7 +261,7 @@ mp_obj_t mp_vfs_chdir(mp_obj_t path_in) {
         // subsequent relative paths begin at the root of that VFS.
         for (vfs = MP_STATE_VM(vfs_mount_table); vfs != NULL; vfs = vfs->next) {
             if (vfs->len == 1) {
-                mp_obj_t root = mp_obj_new_str("/", 1, false);
+                mp_obj_t root = MP_OBJ_NEW_QSTR(MP_QSTR__slash_);
                 mp_vfs_proxy_call(vfs, MP_QSTR_chdir, 1, &root);
                 break;
             }
@@ -318,7 +318,7 @@ STATIC mp_obj_t mp_vfs_ilistdir_it_iternext(mp_obj_t self_in) {
         self->cur.vfs = vfs->next;
         if (vfs->len == 1) {
             // vfs is mounted at root dir, delegate to it
-            mp_obj_t root = mp_obj_new_str("/", 1, false);
+            mp_obj_t root = MP_OBJ_NEW_QSTR(MP_QSTR__slash_);
             self->is_iter = true;
             self->cur.iter = mp_vfs_proxy_call(vfs, MP_QSTR_ilistdir, 1, &root);
             return mp_iternext(self->cur.iter);
@@ -366,9 +366,7 @@ mp_obj_t mp_vfs_listdir(size_t n_args, const mp_obj_t *args) {
     mp_obj_t dir_list = mp_obj_new_list(0, NULL);
     mp_obj_t next;
     while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) {
-        mp_obj_t *items;
-        mp_obj_get_array_fixed_n(next, 3, &items);
-        mp_obj_list_append(dir_list, items[0]);
+        mp_obj_list_append(dir_list, mp_obj_subscr(next, MP_OBJ_NEW_SMALL_INT(0), MP_OBJ_SENTINEL));
     }
     return dir_list;
 }

+ 98 - 11
extmod/vfs_fat.c

@@ -47,6 +47,20 @@
 
 #define mp_obj_fat_vfs_t fs_user_mount_t
 
+mp_import_stat_t fat_vfs_import_stat(fs_user_mount_t *vfs, const char *path) {
+    FILINFO fno;
+    assert(vfs != NULL);
+    FRESULT res = f_stat(&vfs->fatfs, path, &fno);
+    if (res == FR_OK) {
+        if ((fno.fattrib & AM_DIR) != 0) {
+            return MP_IMPORT_STAT_DIR;
+        } else {
+            return MP_IMPORT_STAT_FILE;
+        }
+    }
+    return MP_IMPORT_STAT_NO_EXIST;
+}
+
 STATIC mp_obj_t fat_vfs_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
     mp_arg_check_num(n_args, n_kw, 1, 1, false);
 
@@ -69,9 +83,28 @@ STATIC mp_obj_t fat_vfs_make_new(const mp_obj_type_t *type, size_t n_args, size_
         mp_load_method(args[0], MP_QSTR_count, vfs->u.old.count);
     }
 
+    // mount the block device so the VFS methods can be used
+    FRESULT res = f_mount(&vfs->fatfs);
+    if (res == FR_NO_FILESYSTEM) {
+        // don't error out if no filesystem, to let mkfs()/mount() create one if wanted
+        vfs->flags |= FSUSER_NO_FILESYSTEM;
+    } else if (res != FR_OK) {
+        mp_raise_OSError(fresult_to_errno_table[res]);
+    }
+
     return MP_OBJ_FROM_PTR(vfs);
 }
 
+#if _FS_REENTRANT
+STATIC mp_obj_t fat_vfs_del(mp_obj_t self_in) {
+    mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(self_in);
+    // f_umount only needs to be called to release the sync object
+    f_umount(&self->fatfs);
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_del_obj, fat_vfs_del);
+#endif
+
 STATIC mp_obj_t fat_vfs_mkfs(mp_obj_t bdev_in) {
     // create new object
     fs_user_mount_t *vfs = MP_OBJ_TO_PTR(fat_vfs_make_new(&mp_fat_vfs_type, 1, 0, &bdev_in));
@@ -88,7 +121,52 @@ STATIC mp_obj_t fat_vfs_mkfs(mp_obj_t bdev_in) {
 STATIC MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_mkfs_fun_obj, fat_vfs_mkfs);
 STATIC MP_DEFINE_CONST_STATICMETHOD_OBJ(fat_vfs_mkfs_obj, MP_ROM_PTR(&fat_vfs_mkfs_fun_obj));
 
-STATIC MP_DEFINE_CONST_FUN_OBJ_3(fat_vfs_open_obj, fatfs_builtin_open_self);
+typedef struct _mp_vfs_fat_ilistdir_it_t {
+    mp_obj_base_t base;
+    mp_fun_1_t iternext;
+    bool is_str;
+    FF_DIR dir;
+} mp_vfs_fat_ilistdir_it_t;
+
+STATIC mp_obj_t mp_vfs_fat_ilistdir_it_iternext(mp_obj_t self_in) {
+    mp_vfs_fat_ilistdir_it_t *self = MP_OBJ_TO_PTR(self_in);
+
+    for (;;) {
+        FILINFO fno;
+        FRESULT res = f_readdir(&self->dir, &fno);
+        char *fn = fno.fname;
+        if (res != FR_OK || fn[0] == 0) {
+            // stop on error or end of dir
+            break;
+        }
+
+        // Note that FatFS already filters . and .., so we don't need to
+
+        // make 4-tuple with info about this entry
+        mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(4, NULL));
+        if (self->is_str) {
+            t->items[0] = mp_obj_new_str(fn, strlen(fn));
+        } else {
+            t->items[0] = mp_obj_new_bytes((const byte*)fn, strlen(fn));
+        }
+        if (fno.fattrib & AM_DIR) {
+            // dir
+            t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFDIR);
+        } else {
+            // file
+            t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFREG);
+        }
+        t->items[2] = MP_OBJ_NEW_SMALL_INT(0); // no inode number
+        t->items[3] = mp_obj_new_int_from_uint(fno.fsize);
+
+        return MP_OBJ_FROM_PTR(t);
+    }
+
+    // ignore error because we may be closing a second time
+    f_closedir(&self->dir);
+
+    return MP_OBJ_STOP_ITERATION;
+}
 
 STATIC mp_obj_t fat_vfs_ilistdir_func(size_t n_args, const mp_obj_t *args) {
     mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(args[0]);
@@ -103,7 +181,17 @@ STATIC mp_obj_t fat_vfs_ilistdir_func(size_t n_args, const mp_obj_t *args) {
         path = "";
     }
 
-    return fat_vfs_ilistdir2(self, path, is_str_type);
+    // Create a new iterator object to list the dir
+    mp_vfs_fat_ilistdir_it_t *iter = m_new_obj(mp_vfs_fat_ilistdir_it_t);
+    iter->base.type = &mp_type_polymorph_iter;
+    iter->iternext = mp_vfs_fat_ilistdir_it_iternext;
+    iter->is_str = is_str_type;
+    FRESULT res = f_opendir(&self->fatfs, &iter->dir, path);
+    if (res != FR_OK) {
+        mp_raise_OSError(fresult_to_errno_table[res]);
+    }
+
+    return MP_OBJ_FROM_PTR(iter);
 }
 STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fat_vfs_ilistdir_obj, 1, 2, fat_vfs_ilistdir_func);
 
@@ -197,7 +285,7 @@ STATIC mp_obj_t fat_vfs_getcwd(mp_obj_t vfs_in) {
     if (res != FR_OK) {
         mp_raise_OSError(fresult_to_errno_table[res]);
     }
-    return mp_obj_new_str(buf, strlen(buf), false);
+    return mp_obj_new_str(buf, strlen(buf));
 }
 STATIC MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_getcwd_obj, fat_vfs_getcwd);
 
@@ -291,10 +379,8 @@ STATIC mp_obj_t vfs_fat_mount(mp_obj_t self_in, mp_obj_t readonly, mp_obj_t mkfs
         self->writeblocks[0] = MP_OBJ_NULL;
     }
 
-    // mount the block device
-    FRESULT res = f_mount(&self->fatfs);
-
     // check if we need to make the filesystem
+    FRESULT res = (self->flags & FSUSER_NO_FILESYSTEM) ? FR_NO_FILESYSTEM : FR_OK;
     if (res == FR_NO_FILESYSTEM && mp_obj_is_true(mkfs)) {
         uint8_t working_buf[_MAX_SS];
         res = f_mkfs(&self->fatfs, FM_FAT | FM_SFD, 0, working_buf, sizeof(working_buf));
@@ -302,22 +388,23 @@ STATIC mp_obj_t vfs_fat_mount(mp_obj_t self_in, mp_obj_t readonly, mp_obj_t mkfs
     if (res != FR_OK) {
         mp_raise_OSError(fresult_to_errno_table[res]);
     }
+    self->flags &= ~FSUSER_NO_FILESYSTEM;
 
     return mp_const_none;
 }
 STATIC MP_DEFINE_CONST_FUN_OBJ_3(vfs_fat_mount_obj, vfs_fat_mount);
 
 STATIC mp_obj_t vfs_fat_umount(mp_obj_t self_in) {
-    fs_user_mount_t *self = MP_OBJ_TO_PTR(self_in);
-    FRESULT res = f_umount(&self->fatfs);
-    if (res != FR_OK) {
-        mp_raise_OSError(fresult_to_errno_table[res]);
-    }
+    (void)self_in;
+    // keep the FAT filesystem mounted internally so the VFS methods can still be used
     return mp_const_none;
 }
 STATIC MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_umount_obj, vfs_fat_umount);
 
 STATIC const mp_rom_map_elem_t fat_vfs_locals_dict_table[] = {
+    #if _FS_REENTRANT
+    { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&fat_vfs_del_obj) },
+    #endif
     { MP_ROM_QSTR(MP_QSTR_mkfs), MP_ROM_PTR(&fat_vfs_mkfs_obj) },
     { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&fat_vfs_open_obj) },
     { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&fat_vfs_ilistdir_obj) },

+ 2 - 4
extmod/vfs_fat.h

@@ -35,6 +35,7 @@
 #define FSUSER_NATIVE       (0x0001) // readblocks[2]/writeblocks[2] contain native func
 #define FSUSER_FREE_OBJ     (0x0002) // fs_user_mount_t obj should be freed on umount
 #define FSUSER_HAVE_IOCTL   (0x0004) // new protocol with ioctl
+#define FSUSER_NO_FILESYSTEM (0x0008) // the block device has no filesystem on it
 
 typedef struct _fs_user_mount_t {
     mp_obj_base_t base;
@@ -56,9 +57,6 @@ extern const byte fresult_to_errno_table[20];
 extern const mp_obj_type_t mp_fat_vfs_type;
 
 mp_import_stat_t fat_vfs_import_stat(struct _fs_user_mount_t *vfs, const char *path);
-mp_obj_t fatfs_builtin_open_self(mp_obj_t self_in, mp_obj_t path, mp_obj_t mode);
-MP_DECLARE_CONST_FUN_OBJ_KW(mp_builtin_open_obj);
-
-mp_obj_t fat_vfs_ilistdir2(struct _fs_user_mount_t *vfs, const char *path, bool is_str_type);
+MP_DECLARE_CONST_FUN_OBJ_3(fat_vfs_open_obj);
 
 #endif // MICROPY_INCLUDED_EXTMOD_VFS_FAT_H

+ 6 - 2
extmod/vfs_fat_diskio.c

@@ -36,6 +36,8 @@
 #include "py/mphal.h"
 
 #include "py/runtime.h"
+#include "py/binary.h"
+#include "py/objarray.h"
 #include "lib/oofatfs/ff.h"
 #include "lib/oofatfs/diskio.h"
 #include "extmod/vfs_fat.h"
@@ -126,8 +128,9 @@ DRESULT disk_read (
             return RES_ERROR;
         }
     } else {
+        mp_obj_array_t ar = {{&mp_type_bytearray}, BYTEARRAY_TYPECODE, 0, count * SECSIZE(&vfs->fatfs), buff};
         vfs->readblocks[2] = MP_OBJ_NEW_SMALL_INT(sector);
-        vfs->readblocks[3] = mp_obj_new_bytearray_by_ref(count * SECSIZE(&vfs->fatfs), buff);
+        vfs->readblocks[3] = MP_OBJ_FROM_PTR(&ar);
         mp_call_method_n_kw(2, 0, vfs->readblocks);
         // TODO handle error return
     }
@@ -162,8 +165,9 @@ DRESULT disk_write (
             return RES_ERROR;
         }
     } else {
+        mp_obj_array_t ar = {{&mp_type_bytearray}, BYTEARRAY_TYPECODE, 0, count * SECSIZE(&vfs->fatfs), (void*)buff};
         vfs->writeblocks[2] = MP_OBJ_NEW_SMALL_INT(sector);
-        vfs->writeblocks[3] = mp_obj_new_bytearray_by_ref(count * SECSIZE(&vfs->fatfs), (void*)buff);
+        vfs->writeblocks[3] = MP_OBJ_FROM_PTR(&ar);
         mp_call_method_n_kw(2, 0, vfs->writeblocks);
         // TODO handle error return
     }

+ 3 - 6
extmod/vfs_fat_file.c

@@ -134,11 +134,7 @@ STATIC mp_uint_t file_obj_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg,
                 break;
 
             case 1: // SEEK_CUR
-                if (s->offset != 0) {
-                    *errcode = MP_EOPNOTSUPP;
-                    return MP_STREAM_ERROR;
-                }
-                // no-operation
+                f_lseek(&self->fp, f_tell(&self->fp) + s->offset);
                 break;
 
             case 2: // SEEK_END
@@ -286,7 +282,7 @@ const mp_obj_type_t mp_type_textio = {
 };
 
 // Factory function for I/O stream classes
-mp_obj_t fatfs_builtin_open_self(mp_obj_t self_in, mp_obj_t path, mp_obj_t mode) {
+STATIC mp_obj_t fatfs_builtin_open_self(mp_obj_t self_in, mp_obj_t path, mp_obj_t mode) {
     // TODO: analyze buffering args and instantiate appropriate type
     fs_user_mount_t *self = MP_OBJ_TO_PTR(self_in);
     mp_arg_val_t arg_vals[FILE_OPEN_NUM_ARGS];
@@ -295,5 +291,6 @@ mp_obj_t fatfs_builtin_open_self(mp_obj_t self_in, mp_obj_t path, mp_obj_t mode)
     arg_vals[2].u_obj = mp_const_none;
     return file_open(self, &mp_type_textio, arg_vals);
 }
+MP_DEFINE_CONST_FUN_OBJ_3(fat_vfs_open_obj, fatfs_builtin_open_self);
 
 #endif // MICROPY_VFS && MICROPY_VFS_FAT

+ 0 - 108
extmod/vfs_fat_misc.c

@@ -1,108 +0,0 @@
-/*
- * This file is part of the MicroPython project, http://micropython.org/
- *
- * The MIT License (MIT)
- *
- * Copyright (c) 2014 Damien P. George
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "py/mpconfig.h"
-#if MICROPY_VFS_FAT
-
-#include <string.h>
-#include "py/runtime.h"
-#include "lib/oofatfs/ff.h"
-#include "extmod/vfs_fat.h"
-#include "py/lexer.h"
-
-typedef struct _mp_vfs_fat_ilistdir_it_t {
-    mp_obj_base_t base;
-    mp_fun_1_t iternext;
-    bool is_str;
-    FF_DIR dir;
-} mp_vfs_fat_ilistdir_it_t;
-
-STATIC mp_obj_t mp_vfs_fat_ilistdir_it_iternext(mp_obj_t self_in) {
-    mp_vfs_fat_ilistdir_it_t *self = MP_OBJ_TO_PTR(self_in);
-
-    for (;;) {
-        FILINFO fno;
-        FRESULT res = f_readdir(&self->dir, &fno);
-        char *fn = fno.fname;
-        if (res != FR_OK || fn[0] == 0) {
-            // stop on error or end of dir
-            break;
-        }
-
-        // Note that FatFS already filters . and .., so we don't need to
-
-        // make 3-tuple with info about this entry
-        mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL));
-        if (self->is_str) {
-            t->items[0] = mp_obj_new_str(fn, strlen(fn), false);
-        } else {
-            t->items[0] = mp_obj_new_bytes((const byte*)fn, strlen(fn));
-        }
-        if (fno.fattrib & AM_DIR) {
-            // dir
-            t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFDIR);
-        } else {
-            // file
-            t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFREG);
-        }
-        t->items[2] = MP_OBJ_NEW_SMALL_INT(0); // no inode number
-
-        return MP_OBJ_FROM_PTR(t);
-    }
-
-    // ignore error because we may be closing a second time
-    f_closedir(&self->dir);
-
-    return MP_OBJ_STOP_ITERATION;
-}
-
-mp_obj_t fat_vfs_ilistdir2(fs_user_mount_t *vfs, const char *path, bool is_str_type) {
-    mp_vfs_fat_ilistdir_it_t *iter = m_new_obj(mp_vfs_fat_ilistdir_it_t);
-    iter->base.type = &mp_type_polymorph_iter;
-    iter->iternext = mp_vfs_fat_ilistdir_it_iternext;
-    iter->is_str = is_str_type;
-    FRESULT res = f_opendir(&vfs->fatfs, &iter->dir, path);
-    if (res != FR_OK) {
-        mp_raise_OSError(fresult_to_errno_table[res]);
-    }
-    return MP_OBJ_FROM_PTR(iter);
-}
-
-mp_import_stat_t fat_vfs_import_stat(fs_user_mount_t *vfs, const char *path) {
-    FILINFO fno;
-    assert(vfs != NULL);
-    FRESULT res = f_stat(&vfs->fatfs, path, &fno);
-    if (res == FR_OK) {
-        if ((fno.fattrib & AM_DIR) != 0) {
-            return MP_IMPORT_STAT_DIR;
-        } else {
-            return MP_IMPORT_STAT_FILE;
-        }
-    }
-    return MP_IMPORT_STAT_NO_EXIST;
-}
-
-#endif // MICROPY_VFS_FAT

+ 1 - 1
extmod/vfs_reader.c

@@ -71,7 +71,7 @@ STATIC void mp_reader_vfs_close(void *data) {
 
 void mp_reader_new_file(mp_reader_t *reader, const char *filename) {
     mp_reader_vfs_t *rf = m_new_obj(mp_reader_vfs_t);
-    mp_obj_t arg = mp_obj_new_str(filename, strlen(filename), false);
+    mp_obj_t arg = mp_obj_new_str(filename, strlen(filename));
     rf->file = mp_vfs_open(1, &arg, (mp_map_t*)&mp_const_empty_map);
     int errcode;
     rf->len = mp_stream_rw(rf->file, rf->buf, sizeof(rf->buf), &errcode, MP_STREAM_RW_READ | MP_STREAM_RW_ONCE);

+ 2 - 2
lib/utils/pyexec.c

@@ -35,7 +35,7 @@
 #include "py/gc.h"
 #include "py/frozenmod.h"
 #include "py/mphal.h"
-#if defined(USE_DEVICE_MODE)
+#if MICROPY_HW_ENABLE_USB
 #include "irq.h"
 #include "usb.h"
 #endif
@@ -406,7 +406,7 @@ friendly_repl_reset:
     for (;;) {
     input_restart:
 
-        #if defined(USE_DEVICE_MODE)
+        #if MICROPY_HW_ENABLE_USB
         if (usb_vcp_is_enabled()) {
             // If the user gets to here and interrupts are disabled then
             // they'll never see the prompt, traceback etc. The USB REPL needs

+ 2 - 0
lib/utils/pyexec.h

@@ -26,6 +26,8 @@
 #ifndef MICROPY_INCLUDED_LIB_UTILS_PYEXEC_H
 #define MICROPY_INCLUDED_LIB_UTILS_PYEXEC_H
 
+#include "py/obj.h"
+
 typedef enum {
     PYEXEC_MODE_RAW_REPL,
     PYEXEC_MODE_FRIENDLY_REPL,

+ 3 - 3
port/genhdr/mpversion.h

@@ -1,7 +1,7 @@
 // This file was generated by py/makeversionhdr.py
-#define MICROPY_GIT_TAG "v1.9.3-286-gbbb0843-dirty"
-#define MICROPY_GIT_HASH "bbb0843-dirty"
-#define MICROPY_BUILD_DATE "2018-02-08"
+#define MICROPY_GIT_TAG "v1.9.3-477-g7b0a020-dirty"
+#define MICROPY_GIT_HASH "7b0a020-dirty"
+#define MICROPY_BUILD_DATE "2018-03-21"
 #define MICROPY_VERSION_MAJOR (1)
 #define MICROPY_VERSION_MINOR (9)
 #define MICROPY_VERSION_MICRO (3)

+ 1 - 0
port/mpconfigport.h

@@ -42,6 +42,7 @@
 #endif
 
 #define MICROPY_STACK_CHECK         (1)
+#define MICROPY_PY_MICROPYTHON_STACK_USE (1)
 #define MICROPY_QSTR_BYTES_IN_HASH  (1)
 #define MICROPY_QSTR_EXTRA_POOL     mp_qstr_frozen_const_pool
 #define MICROPY_ALLOC_PATH_MAX      (256)

+ 1 - 4
py/asmarm.c

@@ -150,10 +150,7 @@ void asm_arm_bkpt(asm_arm_t *as) {
 //  | low address    | high address in RAM
 
 void asm_arm_entry(asm_arm_t *as, int num_locals) {
-
-    if (num_locals < 0) {
-        num_locals = 0;
-    }
+    assert(num_locals >= 0);
 
     as->stack_adjust = 0;
     as->push_reglist = 1 << ASM_ARM_REG_R1

+ 2 - 3
py/asmthumb.c

@@ -104,6 +104,8 @@ STATIC void asm_thumb_write_word32(asm_thumb_t *as, int w32) {
 //  | low address    | high address in RAM
 
 void asm_thumb_entry(asm_thumb_t *as, int num_locals) {
+    assert(num_locals >= 0);
+
     // work out what to push and how many extra spaces to reserve on stack
     // so that we have enough for all locals and it's aligned an 8-byte boundary
     // we push extra regs (r1, r2, r3) to help do the stack adjustment
@@ -111,9 +113,6 @@ void asm_thumb_entry(asm_thumb_t *as, int num_locals) {
     // for push rlist, lowest numbered register at the lowest address
     uint reglist;
     uint stack_adjust;
-    if (num_locals < 0) {
-        num_locals = 0;
-    }
     // don't pop r0 because it's used for return value
     switch (num_locals) {
         case 0:

+ 1 - 3
py/asmx64.c

@@ -526,11 +526,9 @@ void asm_x64_jcc_label(asm_x64_t *as, int jcc_type, mp_uint_t label) {
 }
 
 void asm_x64_entry(asm_x64_t *as, int num_locals) {
+    assert(num_locals >= 0);
     asm_x64_push_r64(as, ASM_X64_REG_RBP);
     asm_x64_mov_r64_r64(as, ASM_X64_REG_RBP, ASM_X64_REG_RSP);
-    if (num_locals < 0) {
-        num_locals = 0;
-    }
     num_locals |= 1; // make it odd so stack is aligned on 16 byte boundary
     asm_x64_sub_r64_i32(as, ASM_X64_REG_RSP, num_locals * WORD_SIZE);
     asm_x64_push_r64(as, ASM_X64_REG_RBX);

+ 2 - 1
py/asmx86.c

@@ -387,7 +387,8 @@ void asm_x86_jcc_label(asm_x86_t *as, mp_uint_t jcc_type, mp_uint_t label) {
     }
 }
 
-void asm_x86_entry(asm_x86_t *as, mp_uint_t num_locals) {
+void asm_x86_entry(asm_x86_t *as, int num_locals) {
+    assert(num_locals >= 0);
     asm_x86_push_r32(as, ASM_X86_REG_EBP);
     asm_x86_mov_r32_r32(as, ASM_X86_REG_EBP, ASM_X86_REG_ESP);
     if (num_locals > 0) {

+ 1 - 1
py/asmx86.h

@@ -104,7 +104,7 @@ void asm_x86_test_r8_with_r8(asm_x86_t* as, int src_r32_a, int src_r32_b);
 void asm_x86_setcc_r8(asm_x86_t* as, mp_uint_t jcc_type, int dest_r8);
 void asm_x86_jmp_label(asm_x86_t* as, mp_uint_t label);
 void asm_x86_jcc_label(asm_x86_t* as, mp_uint_t jcc_type, mp_uint_t label);
-void asm_x86_entry(asm_x86_t* as, mp_uint_t num_locals);
+void asm_x86_entry(asm_x86_t* as, int num_locals);
 void asm_x86_exit(asm_x86_t* as);
 void asm_x86_mov_arg_to_r32(asm_x86_t *as, int src_arg_num, int dest_r32);
 void asm_x86_mov_local_to_r32(asm_x86_t* as, int src_local_num, int dest_r32);

+ 0 - 6
py/bc.c

@@ -26,21 +26,15 @@
  */
 
 #include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
 #include <assert.h>
 
-#include "py/mpconfig.h"
-#include "py/misc.h"
-#include "py/mpstate.h"
 #include "py/runtime.h"
 #include "py/bc0.h"
 #include "py/bc.h"
 
 #if MICROPY_DEBUG_VERBOSE // print debugging info
 #define DEBUG_PRINT (1)
-#define DEBUG_printf DEBUG_printf
 #else // don't print debugging info
 #define DEBUG_PRINT (0)
 #define DEBUG_printf(...) (void)0

+ 40 - 13
py/builtinimport.c

@@ -44,6 +44,8 @@
 #define DEBUG_printf(...) (void)0
 #endif
 
+#if MICROPY_ENABLE_EXTERNAL_IMPORT
+
 #define PATH_SEP_CHAR '/'
 
 bool mp_obj_is_package(mp_obj_t module) {
@@ -389,19 +391,7 @@ mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) {
                     }
                     // found weak linked module
                     module_obj = el->value;
-                    if (MICROPY_MODULE_BUILTIN_INIT) {
-                        // look for __init__ and call it if it exists
-                        // Note: this code doesn't work fully correctly because it allows the
-                        // __init__ function to be called twice if the module is imported by its
-                        // non-weak-link name.  Also, this code is duplicated in objmodule.c.
-                        mp_obj_t dest[2];
-                        mp_load_method_maybe(el->value, MP_QSTR___init__, dest);
-                        if (dest[0] != MP_OBJ_NULL) {
-                            mp_call_method_n_kw(0, 0, dest);
-                            // register module so __init__ is not called again
-                            mp_module_register(mod_name, el->value);
-                        }
-                    }
+                    mp_module_call_init(mod_name, module_obj);
                 } else {
                     no_exist:
                 #else
@@ -485,4 +475,41 @@ mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) {
     // Otherwise, we need to return top-level package
     return top_module_obj;
 }
+
+#else // MICROPY_ENABLE_EXTERNAL_IMPORT
+
+mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) {
+    // Check that it's not a relative import
+    if (n_args >= 5 && MP_OBJ_SMALL_INT_VALUE(args[4]) != 0) {
+        mp_raise_NotImplementedError("relative import");
+    }
+
+    // Check if module already exists, and return it if it does
+    qstr module_name_qstr = mp_obj_str_get_qstr(args[0]);
+    mp_obj_t module_obj = mp_module_get(module_name_qstr);
+    if (module_obj != MP_OBJ_NULL) {
+        return module_obj;
+    }
+
+    #if MICROPY_MODULE_WEAK_LINKS
+    // Check if there is a weak link to this module
+    mp_map_elem_t *el = mp_map_lookup((mp_map_t*)&mp_builtin_module_weak_links_map, MP_OBJ_NEW_QSTR(module_name_qstr), MP_MAP_LOOKUP);
+    if (el != NULL) {
+        // Found weak-linked module
+        mp_module_call_init(module_name_qstr, el->value);
+        return el->value;
+    }
+    #endif
+
+    // Couldn't find the module, so fail
+    if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) {
+        mp_raise_msg(&mp_type_ImportError, "module not found");
+    } else {
+        nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ImportError,
+            "no module named '%q'", module_name_qstr));
+    }
+}
+
+#endif // MICROPY_ENABLE_EXTERNAL_IMPORT
+
 MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin___import___obj, 1, 5, mp_builtin___import__);

+ 2 - 7
py/compile.c

@@ -376,6 +376,7 @@ STATIC void c_assign_atom_expr(compiler_t *comp, mp_parse_node_struct_t *pns, as
                     EMIT(store_subscr);
                 }
             }
+            return;
         } else if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_trailer_period) {
             assert(MP_PARSE_NODE_IS_ID(pns1->nodes[0]));
             if (assign_kind == ASSIGN_AUG_LOAD) {
@@ -387,16 +388,10 @@ STATIC void c_assign_atom_expr(compiler_t *comp, mp_parse_node_struct_t *pns, as
                 }
                 EMIT_ARG(store_attr, MP_PARSE_NODE_LEAF_ARG(pns1->nodes[0]));
             }
-        } else {
-            goto cannot_assign;
+            return;
         }
-    } else {
-        goto cannot_assign;
     }
 
-    return;
-
-cannot_assign:
     compile_syntax_error(comp, (mp_parse_node_t)pns, "can't assign to expression");
 }
 

+ 2 - 0
py/emitbc.c

@@ -437,7 +437,9 @@ void mp_emit_bc_end_pass(emit_t *emit) {
 
     } else if (emit->pass == MP_PASS_EMIT) {
         mp_emit_glue_assign_bytecode(emit->scope->raw_code, emit->code_base,
+            #if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS
             emit->code_info_size + emit->bytecode_size,
+            #endif
             emit->const_table,
             #if MICROPY_PERSISTENT_CODE_SAVE
             emit->ct_cur_obj, emit->ct_cur_raw_code,

+ 4 - 1
py/emitglue.c

@@ -55,7 +55,10 @@ mp_raw_code_t *mp_emit_glue_new_raw_code(void) {
     return rc;
 }
 
-void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, const byte *code, mp_uint_t len,
+void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, const byte *code,
+    #if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS
+    size_t len,
+    #endif
     const mp_uint_t *const_table,
     #if MICROPY_PERSISTENT_CODE_SAVE
     uint16_t n_obj, uint16_t n_raw_code,

+ 4 - 1
py/emitglue.h

@@ -63,7 +63,10 @@ typedef struct _mp_raw_code_t {
 
 mp_raw_code_t *mp_emit_glue_new_raw_code(void);
 
-void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, const byte *code, mp_uint_t len,
+void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, const byte *code,
+    #if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS
+    size_t len,
+    #endif
     const mp_uint_t *const_table,
     #if MICROPY_PERSISTENT_CODE_SAVE
     uint16_t n_obj, uint16_t n_raw_code,

+ 7 - 3
py/formatfloat.c

@@ -258,7 +258,7 @@ int mp_format_float(FPTYPE f, char *buf, size_t buf_size, char fmt, int prec, ch
         }
 
         // It can be that f was right on the edge of an entry in pos_pow needs to be reduced
-        if (f >= FPCONST(10.0)) {
+        if ((int)f >= 10) {
             e += 1;
             f *= FPCONST(0.1);
         }
@@ -330,7 +330,11 @@ int mp_format_float(FPTYPE f, char *buf, size_t buf_size, char fmt, int prec, ch
     // Print the digits of the mantissa
     for (int i = 0; i < num_digits; ++i, --dec) {
         int32_t d = (int32_t)f;
-        *s++ = '0' + d;
+        if (d < 0) {
+            *s++ = '0';
+        } else {
+            *s++ = '0' + d;
+        }
         if (dec == 0 && prec > 0) {
             *s++ = '.';
         }
@@ -341,7 +345,7 @@ int mp_format_float(FPTYPE f, char *buf, size_t buf_size, char fmt, int prec, ch
     // Round
     // If we print non-exponential format (i.e. 'f'), but a digit we're going
     // to round by (e) is too far away, then there's nothing to round.
-    if ((org_fmt != 'f' || e <= 1) && f >= FPCONST(5.0)) {
+    if ((org_fmt != 'f' || e <= num_digits) && f >= FPCONST(5.0)) {
         char *rs = s;
         rs--;
         while (1) {

+ 40 - 30
py/gc.c

@@ -203,29 +203,14 @@ bool gc_is_locked(void) {
 #endif
 #endif
 
-// ptr should be of type void*
-#define VERIFY_MARK_AND_PUSH(ptr) \
-    do { \
-        if (VERIFY_PTR(ptr)) { \
-            size_t _block = BLOCK_FROM_PTR(ptr); \
-            if (ATB_GET_KIND(_block) == AT_HEAD) { \
-                /* an unmarked head, mark it, and push it on gc stack */ \
-                TRACE_MARK(_block, ptr); \
-                ATB_HEAD_TO_MARK(_block); \
-                if (MP_STATE_MEM(gc_sp) < &MP_STATE_MEM(gc_stack)[MICROPY_ALLOC_GC_STACK_SIZE]) { \
-                    *MP_STATE_MEM(gc_sp)++ = _block; \
-                } else { \
-                    MP_STATE_MEM(gc_stack_overflow) = 1; \
-                } \
-            } \
-        } \
-    } while (0)
-
-STATIC void gc_drain_stack(void) {
-    while (MP_STATE_MEM(gc_sp) > MP_STATE_MEM(gc_stack)) {
-        // pop the next block off the stack
-        size_t block = *--MP_STATE_MEM(gc_sp);
-
+// Take the given block as the topmost block on the stack. Check all it's
+// children: mark the unmarked child blocks and put those newly marked
+// blocks on the stack. When all children have been checked, pop off the
+// topmost block on the stack and repeat with that one.
+STATIC void gc_mark_subtree(size_t block) {
+    // Start with the block passed in the argument.
+    size_t sp = 0;
+    for (;;) {
         // work out number of consecutive blocks in the chain starting with this one
         size_t n_blocks = 0;
         do {
@@ -236,22 +221,41 @@ STATIC void gc_drain_stack(void) {
         void **ptrs = (void**)PTR_FROM_BLOCK(block);
         for (size_t i = n_blocks * BYTES_PER_BLOCK / sizeof(void*); i > 0; i--, ptrs++) {
             void *ptr = *ptrs;
-            VERIFY_MARK_AND_PUSH(ptr);
+            if (VERIFY_PTR(ptr)) {
+                // Mark and push this pointer
+                size_t childblock = BLOCK_FROM_PTR(ptr);
+                if (ATB_GET_KIND(childblock) == AT_HEAD) {
+                    // an unmarked head, mark it, and push it on gc stack
+                    TRACE_MARK(childblock, ptr);
+                    ATB_HEAD_TO_MARK(childblock);
+                    if (sp < MICROPY_ALLOC_GC_STACK_SIZE) {
+                        MP_STATE_MEM(gc_stack)[sp++] = childblock;
+                    } else {
+                        MP_STATE_MEM(gc_stack_overflow) = 1;
+                    }
+                }
+            }
         }
+
+        // Are there any blocks on the stack?
+        if (sp == 0) {
+            break; // No, stack is empty, we're done.
+        }
+
+        // pop the next block off the stack
+        block = MP_STATE_MEM(gc_stack)[--sp];
     }
 }
 
 STATIC void gc_deal_with_stack_overflow(void) {
     while (MP_STATE_MEM(gc_stack_overflow)) {
         MP_STATE_MEM(gc_stack_overflow) = 0;
-        MP_STATE_MEM(gc_sp) = MP_STATE_MEM(gc_stack);
 
         // scan entire memory looking for blocks which have been marked but not their children
         for (size_t block = 0; block < MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB; block++) {
             // trace (again) if mark bit set
             if (ATB_GET_KIND(block) == AT_MARK) {
-                *MP_STATE_MEM(gc_sp)++ = block;
-                gc_drain_stack();
+                gc_mark_subtree(block);
             }
         }
     }
@@ -319,7 +323,6 @@ void gc_collect_start(void) {
     MP_STATE_MEM(gc_alloc_amount) = 0;
     #endif
     MP_STATE_MEM(gc_stack_overflow) = 0;
-    MP_STATE_MEM(gc_sp) = MP_STATE_MEM(gc_stack);
 
     // Trace root pointers.  This relies on the root pointers being organised
     // correctly in the mp_state_ctx structure.  We scan nlr_top, dict_locals,
@@ -337,8 +340,15 @@ void gc_collect_start(void) {
 void gc_collect_root(void **ptrs, size_t len) {
     for (size_t i = 0; i < len; i++) {
         void *ptr = ptrs[i];
-        VERIFY_MARK_AND_PUSH(ptr);
-        gc_drain_stack();
+        if (VERIFY_PTR(ptr)) {
+            size_t block = BLOCK_FROM_PTR(ptr);
+            if (ATB_GET_KIND(block) == AT_HEAD) {
+                // An unmarked head: mark it, and mark all its children
+                TRACE_MARK(block, ptr);
+                ATB_HEAD_TO_MARK(block);
+                gc_mark_subtree(block);
+            }
+        }
     }
 }
 

+ 7 - 3
py/makeqstrdefs.py

@@ -24,12 +24,16 @@ def write_out(fname, output):
             f.write("\n".join(output) + "\n")
 
 def process_file(f):
+    re_line = re.compile(r"#[line]*\s\d+\s\"([^\"]+)\"")
+    re_qstr = re.compile(r'MP_QSTR_[_a-zA-Z0-9]+')
     output = []
     last_fname = None
     for line in f:
+        if line.isspace():
+            continue
         # match gcc-like output (# n "file") and msvc-like output (#line n "file")
-        if line and (line[0:2] == "# " or line[0:5] == "#line"):
-            m = re.match(r"#[line]*\s\d+\s\"([^\"]+)\"", line)
+        if line.startswith(('# ', '#line')):
+            m = re_line.match(line)
             assert m is not None
             fname = m.group(1)
             if not fname.endswith(".c"):
@@ -39,7 +43,7 @@ def process_file(f):
                 output = []
                 last_fname = fname
             continue
-        for match in re.findall(r'MP_QSTR_[_a-zA-Z0-9]+', line):
+        for match in re_qstr.findall(line):
             name = match.replace('MP_QSTR_', '')
             if name not in QSTRING_BLACK_LIST:
                 output.append('Q(' + name + ')')

+ 1 - 1
py/makeversionhdr.py

@@ -99,7 +99,7 @@ def make_version_header(filename):
 
     # Only write the file if we need to
     if write_file:
-        print("Generating %s" % filename)
+        print("GEN %s" % filename)
         with open(filename, 'w') as f:
             f.write(file_data)
 

+ 7 - 15
py/misc.h

@@ -121,8 +121,15 @@ typedef uint32_t unichar;
 typedef uint unichar;
 #endif
 
+#if MICROPY_PY_BUILTINS_STR_UNICODE
 unichar utf8_get_char(const byte *s);
 const byte *utf8_next_char(const byte *s);
+size_t utf8_charlen(const byte *str, size_t len);
+#else
+static inline unichar utf8_get_char(const byte *s) { return *s; }
+static inline const byte *utf8_next_char(const byte *s) { return s + 1; }
+static inline size_t utf8_charlen(const byte *str, size_t len) { (void)str; return len; }
+#endif
 
 bool unichar_isspace(unichar c);
 bool unichar_isalpha(unichar c);
@@ -135,7 +142,6 @@ bool unichar_islower(unichar c);
 unichar unichar_tolower(unichar c);
 unichar unichar_toupper(unichar c);
 mp_uint_t unichar_xdigit_value(unichar c);
-mp_uint_t unichar_charlen(const char *str, mp_uint_t len);
 #define UTF8_IS_NONASCII(ch) ((ch) & 0x80)
 #define UTF8_IS_CONT(ch) (((ch) & 0xC0) == 0x80)
 
@@ -198,20 +204,6 @@ int DEBUG_printf(const char *fmt, ...);
 
 extern mp_uint_t mp_verbose_flag;
 
-// This is useful for unicode handling. Some CPU archs has
-// special instructions for efficient implementation of this
-// function (e.g. CLZ on ARM).
-// NOTE: this function is unused at the moment
-#ifndef count_lead_ones
-static inline mp_uint_t count_lead_ones(byte val) {
-    mp_uint_t c = 0;
-    for (byte mask = 0x80; val & mask; mask >>= 1) {
-        c++;
-    }
-    return c;
-}
-#endif
-
 /** float internals *************/
 
 #if MICROPY_PY_BUILTINS_FLOAT

+ 2 - 2
py/mkrules.mk

@@ -97,7 +97,7 @@ $(HEADER_BUILD):
 
 ifneq ($(FROZEN_DIR),)
 $(BUILD)/frozen.c: $(wildcard $(FROZEN_DIR)/*) $(HEADER_BUILD) $(FROZEN_EXTRA_DEPS)
-	$(ECHO) "Generating $@"
+	$(ECHO) "GEN $@"
 	$(Q)$(MAKE_FROZEN) $(FROZEN_DIR) > $@
 endif
 
@@ -118,7 +118,7 @@ $(BUILD)/frozen_mpy/%.mpy: $(FROZEN_MPY_DIR)/%.py $(TOP)/mpy-cross/mpy-cross
 
 # to build frozen_mpy.c from all .mpy files
 $(BUILD)/frozen_mpy.c: $(FROZEN_MPY_MPY_FILES) $(BUILD)/genhdr/qstrdefs.generated.h
-	@$(ECHO) "Creating $@"
+	@$(ECHO) "GEN $@"
 	$(Q)$(MPY_TOOL) -f -q $(BUILD)/genhdr/qstrdefs.preprocessed.h $(FROZEN_MPY_MPY_FILES) > $@
 endif
 

+ 16 - 38
py/modbuiltins.c

@@ -173,46 +173,24 @@ STATIC mp_obj_t mp_builtin_chr(mp_obj_t o_in) {
 MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_chr_obj, mp_builtin_chr);
 
 STATIC mp_obj_t mp_builtin_dir(size_t n_args, const mp_obj_t *args) {
-    // TODO make this function more general and less of a hack
-
-    mp_obj_dict_t *dict = NULL;
-    mp_map_t *members = NULL;
-    if (n_args == 0) {
-        // make a list of names in the local name space
-        dict = mp_locals_get();
-    } else { // n_args == 1
-        // make a list of names in the given object
-        if (MP_OBJ_IS_TYPE(args[0], &mp_type_module)) {
-            dict = mp_obj_module_get_globals(args[0]);
-        } else {
-            mp_obj_type_t *type;
-            if (MP_OBJ_IS_TYPE(args[0], &mp_type_type)) {
-                type = MP_OBJ_TO_PTR(args[0]);
-            } else {
-                type = mp_obj_get_type(args[0]);
-            }
-            if (type->locals_dict != NULL && type->locals_dict->base.type == &mp_type_dict) {
-                dict = type->locals_dict;
-            }
-        }
-        if (mp_obj_is_instance_type(mp_obj_get_type(args[0]))) {
-            mp_obj_instance_t *inst = MP_OBJ_TO_PTR(args[0]);
-            members = &inst->members;
-        }
-    }
-
     mp_obj_t dir = mp_obj_new_list(0, NULL);
-    if (dict != NULL) {
+    if (n_args == 0) {
+        // Make a list of names in the local namespace
+        mp_obj_dict_t *dict = mp_locals_get();
         for (size_t i = 0; i < dict->map.alloc; i++) {
             if (MP_MAP_SLOT_IS_FILLED(&dict->map, i)) {
                 mp_obj_list_append(dir, dict->map.table[i].key);
             }
         }
-    }
-    if (members != NULL) {
-        for (size_t i = 0; i < members->alloc; i++) {
-            if (MP_MAP_SLOT_IS_FILLED(members, i)) {
-                mp_obj_list_append(dir, members->table[i].key);
+    } else { // n_args == 1
+        // Make a list of names in the given object
+        // Implemented by probing all possible qstrs with mp_load_method_maybe
+        size_t nqstr = QSTR_TOTAL();
+        for (size_t i = 1; i < nqstr; ++i) {
+            mp_obj_t dest[2];
+            mp_load_method_maybe(args[0], i, dest);
+            if (dest[0] != MP_OBJ_NULL) {
+                mp_obj_list_append(dir, MP_OBJ_NEW_QSTR(i));
             }
         }
     }
@@ -343,19 +321,19 @@ MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_oct_obj, mp_builtin_oct);
 
 STATIC mp_obj_t mp_builtin_ord(mp_obj_t o_in) {
     size_t len;
-    const char *str = mp_obj_str_get_data(o_in, &len);
+    const byte *str = (const byte*)mp_obj_str_get_data(o_in, &len);
     #if MICROPY_PY_BUILTINS_STR_UNICODE
     if (MP_OBJ_IS_STR(o_in)) {
-        len = unichar_charlen(str, len);
+        len = utf8_charlen(str, len);
         if (len == 1) {
-            return mp_obj_new_int(utf8_get_char((const byte*)str));
+            return mp_obj_new_int(utf8_get_char(str));
         }
     } else
     #endif
     {
         // a bytes object, or a str without unicode support (don't sign extend the char)
         if (len == 1) {
-            return MP_OBJ_NEW_SMALL_INT(((const byte*)str)[0]);
+            return MP_OBJ_NEW_SMALL_INT(str[0]);
         }
     }
 

+ 3 - 0
py/modcollections.c

@@ -30,6 +30,9 @@
 
 STATIC const mp_rom_map_elem_t mp_module_collections_globals_table[] = {
     { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ucollections) },
+    #if MICROPY_PY_COLLECTIONS_DEQUE
+    { MP_ROM_QSTR(MP_QSTR_deque), MP_ROM_PTR(&mp_type_deque) },
+    #endif
     { MP_ROM_QSTR(MP_QSTR_namedtuple), MP_ROM_PTR(&mp_namedtuple_obj) },
     #if MICROPY_PY_COLLECTIONS_ORDEREDDICT
     { MP_ROM_QSTR(MP_QSTR_OrderedDict), MP_ROM_PTR(&mp_type_ordereddict) },

+ 5 - 5
py/modmicropython.c

@@ -103,15 +103,15 @@ STATIC mp_obj_t mp_micropython_qstr_info(size_t n_args, const mp_obj_t *args) {
 }
 STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_micropython_qstr_info_obj, 0, 1, mp_micropython_qstr_info);
 
-#if MICROPY_STACK_CHECK
+#endif // MICROPY_PY_MICROPYTHON_MEM_INFO
+
+#if MICROPY_PY_MICROPYTHON_STACK_USE
 STATIC mp_obj_t mp_micropython_stack_use(void) {
     return MP_OBJ_NEW_SMALL_INT(mp_stack_usage());
 }
 STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_stack_use_obj, mp_micropython_stack_use);
 #endif
 
-#endif // MICROPY_PY_MICROPYTHON_MEM_INFO
-
 #if MICROPY_ENABLE_PYSTACK
 STATIC mp_obj_t mp_micropython_pystack_use(void) {
     return MP_OBJ_NEW_SMALL_INT(mp_pystack_usage());
@@ -167,10 +167,10 @@ STATIC const mp_rom_map_elem_t mp_module_micropython_globals_table[] = {
 #endif
     { MP_ROM_QSTR(MP_QSTR_mem_info), MP_ROM_PTR(&mp_micropython_mem_info_obj) },
     { MP_ROM_QSTR(MP_QSTR_qstr_info), MP_ROM_PTR(&mp_micropython_qstr_info_obj) },
-    #if MICROPY_STACK_CHECK
+#endif
+    #if MICROPY_PY_MICROPYTHON_STACK_USE
     { MP_ROM_QSTR(MP_QSTR_stack_use), MP_ROM_PTR(&mp_micropython_stack_use_obj) },
     #endif
-#endif
 #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF && (MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE == 0)
     { MP_ROM_QSTR(MP_QSTR_alloc_emergency_exception_buf), MP_ROM_PTR(&mp_alloc_emergency_exception_buf_obj) },
 #endif

+ 25 - 0
py/mpconfig.h

@@ -405,6 +405,13 @@
 /*****************************************************************************/
 /* Python internal features                                                  */
 
+// Whether to enable import of external modules
+// When disabled, only importing of built-in modules is supported
+// When enabled, a port must implement mp_import_stat (among other things)
+#ifndef MICROPY_ENABLE_EXTERNAL_IMPORT
+#define MICROPY_ENABLE_EXTERNAL_IMPORT (1)
+#endif
+
 // Whether to use the POSIX reader for importing files
 #ifndef MICROPY_READER_POSIX
 #define MICROPY_READER_POSIX (0)
@@ -787,6 +794,14 @@ typedef double mp_float_t;
 #define MICROPY_PY_BUILTINS_RANGE_ATTRS (1)
 #endif
 
+// Whether to support binary ops [only (in)equality is defined] between range
+// objects.  With this option disabled all range objects that are not exactly
+// the same object will compare as not-equal.  With it enabled the semantics
+// match CPython and ranges are equal if they yield the same sequence of items.
+#ifndef MICROPY_PY_BUILTINS_RANGE_BINOP
+#define MICROPY_PY_BUILTINS_RANGE_BINOP (0)
+#endif
+
 // Whether to support timeout exceptions (like socket.timeout)
 #ifndef MICROPY_PY_BUILTINS_TIMEOUTERROR
 #define MICROPY_PY_BUILTINS_TIMEOUTERROR (0)
@@ -892,6 +907,11 @@ typedef double mp_float_t;
 #define MICROPY_PY_MICROPYTHON_MEM_INFO (0)
 #endif
 
+// Whether to provide "micropython.stack_use" function
+#ifndef MICROPY_PY_MICROPYTHON_STACK_USE
+#define MICROPY_PY_MICROPYTHON_STACK_USE (MICROPY_PY_MICROPYTHON_MEM_INFO)
+#endif
+
 // Whether to provide "array" module. Note that large chunk of the
 // underlying code is shared with "bytearray" builtin type, so to
 // get real savings, it should be disabled too.
@@ -916,6 +936,11 @@ typedef double mp_float_t;
 #define MICROPY_PY_COLLECTIONS (1)
 #endif
 
+// Whether to provide "ucollections.deque" type
+#ifndef MICROPY_PY_COLLECTIONS_DEQUE
+#define MICROPY_PY_COLLECTIONS_DEQUE (0)
+#endif
+
 // Whether to provide "collections.OrderedDict" type
 #ifndef MICROPY_PY_COLLECTIONS_ORDEREDDICT
 #define MICROPY_PY_COLLECTIONS_ORDEREDDICT (0)

+ 4 - 8
py/mpstate.h

@@ -78,7 +78,6 @@ typedef struct _mp_state_mem_t {
 
     int gc_stack_overflow;
     size_t gc_stack[MICROPY_ALLOC_GC_STACK_SIZE];
-    size_t *gc_sp;
     uint16_t gc_lock_depth;
 
     // This variable controls auto garbage collection.  If set to 0 then the
@@ -167,6 +166,10 @@ typedef struct _mp_state_vm_t {
 
     // root pointers for extmod
 
+    #if MICROPY_REPL_EVENT_DRIVEN
+    vstr_t *repl_line;
+    #endif
+
     #if MICROPY_PY_OS_DUPTERM
     mp_obj_t dupterm_objs[MICROPY_PY_OS_DUPTERM];
     mp_obj_t dupterm_arr_obj;
@@ -176,13 +179,6 @@ typedef struct _mp_state_vm_t {
     mp_obj_t lwip_slip_stream;
     #endif
 
-    #if !MICROPY_VFS  //If not difined MICROPY_VFS,only define MICROPY_MODUOS_FILE
-    #if MICROPY_MODUOS_FILE
-    struct _mp_vfs_mount_t *vfs_cur;
-    struct _mp_vfs_mount_t *vfs_mount_table;
-    #endif
-    #endif
-
     #if MICROPY_VFS
     struct _mp_vfs_mount_t *vfs_cur;
     struct _mp_vfs_mount_t *vfs_mount_table;

+ 6 - 6
py/mpz.c

@@ -705,17 +705,14 @@ STATIC void mpz_need_dig(mpz_t *z, size_t need) {
 }
 
 STATIC mpz_t *mpz_clone(const mpz_t *src) {
+    assert(src->alloc != 0);
     mpz_t *z = m_new_obj(mpz_t);
     z->neg = src->neg;
     z->fixed_dig = 0;
     z->alloc = src->alloc;
     z->len = src->len;
-    if (src->dig == NULL) {
-        z->dig = NULL;
-    } else {
-        z->dig = m_new(mpz_dig_t, z->alloc);
-        memcpy(z->dig, src->dig, src->alloc * sizeof(mpz_dig_t));
-    }
+    z->dig = m_new(mpz_dig_t, z->alloc);
+    memcpy(z->dig, src->dig, src->alloc * sizeof(mpz_dig_t));
     return z;
 }
 
@@ -983,6 +980,7 @@ these functions are unused
 /* returns abs(z)
 */
 mpz_t *mpz_abs(const mpz_t *z) {
+    // TODO: handle case of z->alloc=0
     mpz_t *z2 = mpz_clone(z);
     z2->neg = 0;
     return z2;
@@ -991,6 +989,7 @@ mpz_t *mpz_abs(const mpz_t *z) {
 /* returns -z
 */
 mpz_t *mpz_neg(const mpz_t *z) {
+    // TODO: handle case of z->alloc=0
     mpz_t *z2 = mpz_clone(z);
     z2->neg = 1 - z2->neg;
     return z2;
@@ -1408,6 +1407,7 @@ these functions are unused
 */
 mpz_t *mpz_gcd(const mpz_t *z1, const mpz_t *z2) {
     if (z1->len == 0) {
+        // TODO: handle case of z2->alloc=0
         mpz_t *a = mpz_clone(z2);
         a->neg = 0;
         return a;

+ 2 - 2
py/nlrthumb.c

@@ -76,9 +76,9 @@ __attribute__((naked)) unsigned int nlr_push(nlr_buf_t *nlr) {
 #endif
     );
 
-    #if defined(__GNUC__)
+    #if defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8))
     // Older versions of gcc give an error when naked functions don't return a value
-    __builtin_unreachable();
+    return 0;
     #endif
 }
 

+ 4 - 14
py/obj.h

@@ -250,6 +250,8 @@ typedef struct _mp_rom_obj_t { mp_const_obj_t o; } mp_rom_obj_t;
 
 // The macros below are derived from the ones above and are used to
 // check for more specific object types.
+// Note: these are kept as macros because inline functions sometimes use much
+// more code space than the equivalent macros, depending on the compiler.
 
 #define MP_OBJ_IS_TYPE(o, t) (MP_OBJ_IS_OBJ(o) && (((mp_obj_base_t*)MP_OBJ_TO_PTR(o))->type == (t))) // this does not work for checking int, str or fun; use below macros for that
 #define MP_OBJ_IS_INT(o) (MP_OBJ_IS_SMALL_INT(o) || MP_OBJ_IS_TYPE(o, &mp_type_int))
@@ -257,17 +259,6 @@ typedef struct _mp_rom_obj_t { mp_const_obj_t o; } mp_rom_obj_t;
 #define MP_OBJ_IS_STR_OR_BYTES(o) (MP_OBJ_IS_QSTR(o) || (MP_OBJ_IS_OBJ(o) && ((mp_obj_base_t*)MP_OBJ_TO_PTR(o))->type->binary_op == mp_obj_str_binary_op))
 #define MP_OBJ_IS_FUN(o) (MP_OBJ_IS_OBJ(o) && (((mp_obj_base_t*)MP_OBJ_TO_PTR(o))->type->name == MP_QSTR_function))
 
-// Note: inline functions sometimes use much more code space than the
-// equivalent macros, depending on the compiler.
-//static inline bool MP_OBJ_IS_TYPE(mp_const_obj_t o, const mp_obj_type_t *t) { return (MP_OBJ_IS_OBJ(o) && (((mp_obj_base_t*)(o))->type == (t))); } // this does not work for checking a string, use below macro for that
-//static inline bool MP_OBJ_IS_INT(mp_const_obj_t o) { return (MP_OBJ_IS_SMALL_INT(o) || MP_OBJ_IS_TYPE(o, &mp_type_int)); } // returns true if o is a small int or long int
-// Need to forward declare these for the inline function to compile.
-extern const mp_obj_type_t mp_type_int;
-extern const mp_obj_type_t mp_type_bool;
-static inline bool mp_obj_is_integer(mp_const_obj_t o) { return MP_OBJ_IS_INT(o) || MP_OBJ_IS_TYPE(o, &mp_type_bool); } // returns true if o is bool, small int or long int
-//static inline bool MP_OBJ_IS_STR(mp_const_obj_t o) { return (MP_OBJ_IS_QSTR(o) || MP_OBJ_IS_TYPE(o, &mp_type_str)); }
-
-
 // These macros are used to declare and define constant function objects
 // You can put "static" in front of the definitions to make them local
 
@@ -553,6 +544,7 @@ extern const mp_obj_type_t mp_type_list;
 extern const mp_obj_type_t mp_type_map; // map (the python builtin, not the dict implementation detail)
 extern const mp_obj_type_t mp_type_enumerate;
 extern const mp_obj_type_t mp_type_filter;
+extern const mp_obj_type_t mp_type_deque;
 extern const mp_obj_type_t mp_type_dict;
 extern const mp_obj_type_t mp_type_ordereddict;
 extern const mp_obj_type_t mp_type_range;
@@ -624,7 +616,6 @@ extern const struct _mp_obj_str_t mp_const_empty_bytes_obj;
 extern const struct _mp_obj_tuple_t mp_const_empty_tuple_obj;
 extern const struct _mp_obj_singleton_t mp_const_ellipsis_obj;
 extern const struct _mp_obj_singleton_t mp_const_notimplemented_obj;
-extern const struct _mp_obj_exception_t mp_const_MemoryError_obj;
 extern const struct _mp_obj_exception_t mp_const_GeneratorExit_obj;
 
 // General API for objects
@@ -681,6 +672,7 @@ bool mp_obj_is_true(mp_obj_t arg);
 bool mp_obj_is_callable(mp_obj_t o_in);
 bool mp_obj_equal(mp_obj_t o1, mp_obj_t o2);
 
+static inline bool mp_obj_is_integer(mp_const_obj_t o) { return MP_OBJ_IS_INT(o) || MP_OBJ_IS_TYPE(o, &mp_type_bool); } // returns true if o is bool, small int or long int
 mp_int_t mp_obj_get_int(mp_const_obj_t arg);
 mp_int_t mp_obj_get_int_truncated(mp_const_obj_t arg);
 bool mp_obj_get_int_maybe(mp_const_obj_t arg, mp_int_t *value);
@@ -752,8 +744,6 @@ void mp_obj_tuple_del(mp_obj_t self_in);
 mp_int_t mp_obj_tuple_hash(mp_obj_t self_in);
 
 // list
-struct _mp_obj_list_t;
-void mp_obj_list_init(struct _mp_obj_list_t *o, size_t n);
 mp_obj_t mp_obj_list_append(mp_obj_t self_in, mp_obj_t arg);
 mp_obj_t mp_obj_list_remove(mp_obj_t self_in, mp_obj_t value);
 void mp_obj_list_get(mp_obj_t self_in, size_t *len, mp_obj_t **items);

+ 166 - 0
py/objdeque.c

@@ -0,0 +1,166 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Paul Sokolovsky
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <string.h>
+
+#include "py/mpconfig.h"
+#if MICROPY_PY_COLLECTIONS_DEQUE
+
+#include "py/runtime.h"
+
+typedef struct _mp_obj_deque_t {
+    mp_obj_base_t base;
+    size_t alloc;
+    size_t i_get;
+    size_t i_put;
+    mp_obj_t *items;
+    uint32_t flags;
+    #define FLAG_CHECK_OVERFLOW 1
+} mp_obj_deque_t;
+
+STATIC mp_obj_t deque_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+    mp_arg_check_num(n_args, n_kw, 2, 3, false);
+
+    /* Initialization from existing sequence is not supported, so an empty
+       tuple must be passed as such. */
+    if (args[0] != mp_const_empty_tuple) {
+        mp_raise_ValueError(NULL);
+    }
+
+    // Protect against -1 leading to zero-length allocation and bad array access
+    mp_int_t maxlen = mp_obj_get_int(args[1]);
+    if (maxlen < 0) {
+        mp_raise_ValueError(NULL);
+    }
+
+    mp_obj_deque_t *o = m_new_obj(mp_obj_deque_t);
+    o->base.type = type;
+    o->alloc = maxlen + 1;
+    o->i_get = o->i_put = 0;
+    o->items = m_new0(mp_obj_t, o->alloc);
+
+    if (n_args > 2) {
+        o->flags = mp_obj_get_int(args[2]);
+    }
+
+    return MP_OBJ_FROM_PTR(o);
+}
+
+STATIC mp_obj_t deque_unary_op(mp_unary_op_t op, mp_obj_t self_in) {
+    mp_obj_deque_t *self = MP_OBJ_TO_PTR(self_in);
+    switch (op) {
+        case MP_UNARY_OP_BOOL:
+            return mp_obj_new_bool(self->i_get != self->i_put);
+        case MP_UNARY_OP_LEN: {
+            mp_int_t len = self->i_put - self->i_get;
+            if (len < 0) {
+                len += self->alloc;
+            }
+            return MP_OBJ_NEW_SMALL_INT(len);
+        }
+        #if MICROPY_PY_SYS_GETSIZEOF
+        case MP_UNARY_OP_SIZEOF: {
+            size_t sz = sizeof(*self) + sizeof(mp_obj_t) * self->alloc;
+            return MP_OBJ_NEW_SMALL_INT(sz);
+        }
+        #endif
+        default:
+            return MP_OBJ_NULL; // op not supported
+    }
+}
+
+STATIC mp_obj_t mp_obj_deque_append(mp_obj_t self_in, mp_obj_t arg) {
+    mp_obj_deque_t *self = MP_OBJ_TO_PTR(self_in);
+
+    size_t new_i_put = self->i_put + 1;
+    if (new_i_put == self->alloc) {
+        new_i_put = 0;
+    }
+
+    if (self->flags & FLAG_CHECK_OVERFLOW && new_i_put == self->i_get) {
+        mp_raise_msg(&mp_type_IndexError, "full");
+    }
+
+    self->items[self->i_put] = arg;
+    self->i_put = new_i_put;
+
+    if (self->i_get == new_i_put) {
+        if (++self->i_get == self->alloc) {
+            self->i_get = 0;
+        }
+    }
+
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(deque_append_obj, mp_obj_deque_append);
+
+STATIC mp_obj_t deque_popleft(mp_obj_t self_in) {
+    mp_obj_deque_t *self = MP_OBJ_TO_PTR(self_in);
+
+    if (self->i_get == self->i_put) {
+        mp_raise_msg(&mp_type_IndexError, "empty");
+    }
+
+    mp_obj_t ret = self->items[self->i_get];
+    self->items[self->i_get] = MP_OBJ_NULL;
+
+    if (++self->i_get == self->alloc) {
+        self->i_get = 0;
+    }
+
+    return ret;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(deque_popleft_obj, deque_popleft);
+
+#if 0
+STATIC mp_obj_t deque_clear(mp_obj_t self_in) {
+    mp_obj_deque_t *self = MP_OBJ_TO_PTR(self_in);
+    self->i_get = self->i_put = 0;
+    mp_seq_clear(self->items, 0, self->alloc, sizeof(*self->items));
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(deque_clear_obj, deque_clear);
+#endif
+
+STATIC const mp_rom_map_elem_t deque_locals_dict_table[] = {
+    { MP_ROM_QSTR(MP_QSTR_append), MP_ROM_PTR(&deque_append_obj) },
+    #if 0
+    { MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&deque_clear_obj) },
+    #endif
+    { MP_ROM_QSTR(MP_QSTR_popleft), MP_ROM_PTR(&deque_popleft_obj) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(deque_locals_dict, deque_locals_dict_table);
+
+const mp_obj_type_t mp_type_deque = {
+    { &mp_type_type },
+    .name = MP_QSTR_deque,
+    .make_new = deque_make_new,
+    .unary_op = deque_unary_op,
+    .locals_dict = (mp_obj_dict_t*)&deque_locals_dict,
+};
+
+#endif // MICROPY_PY_COLLECTIONS_DEQUE

+ 13 - 0
py/objdict.c

@@ -195,9 +195,16 @@ STATIC mp_obj_t dict_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) {
 /******************************************************************************/
 /* dict methods                                                               */
 
+STATIC void mp_ensure_not_fixed(const mp_obj_dict_t *dict) {
+    if (dict->map.is_fixed) {
+        mp_raise_TypeError(NULL);
+    }
+}
+
 STATIC mp_obj_t dict_clear(mp_obj_t self_in) {
     mp_check_self(MP_OBJ_IS_DICT_TYPE(self_in));
     mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_in);
+    mp_ensure_not_fixed(self);
 
     mp_map_clear(&self->map);
 
@@ -253,6 +260,9 @@ STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(dict_fromkeys_obj, MP_ROM_PTR(&dict_fromk
 STATIC mp_obj_t dict_get_helper(size_t n_args, const mp_obj_t *args, mp_map_lookup_kind_t lookup_kind) {
     mp_check_self(MP_OBJ_IS_DICT_TYPE(args[0]));
     mp_obj_dict_t *self = MP_OBJ_TO_PTR(args[0]);
+    if (lookup_kind != MP_MAP_LOOKUP) {
+        mp_ensure_not_fixed(self);
+    }
     mp_map_elem_t *elem = mp_map_lookup(&self->map, args[1], lookup_kind);
     mp_obj_t value;
     if (elem == NULL || elem->value == MP_OBJ_NULL) {
@@ -295,6 +305,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dict_setdefault_obj, 2, 3, dict_setde
 STATIC mp_obj_t dict_popitem(mp_obj_t self_in) {
     mp_check_self(MP_OBJ_IS_DICT_TYPE(self_in));
     mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_in);
+    mp_ensure_not_fixed(self);
     size_t cur = 0;
     mp_map_elem_t *next = dict_iter_next(self, &cur);
     if (next == NULL) {
@@ -313,6 +324,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(dict_popitem_obj, dict_popitem);
 STATIC mp_obj_t dict_update(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
     mp_check_self(MP_OBJ_IS_DICT_TYPE(args[0]));
     mp_obj_dict_t *self = MP_OBJ_TO_PTR(args[0]);
+    mp_ensure_not_fixed(self);
 
     mp_arg_check_num(n_args, kwargs->used, 1, 2, true);
 
@@ -578,6 +590,7 @@ size_t mp_obj_dict_len(mp_obj_t self_in) {
 mp_obj_t mp_obj_dict_store(mp_obj_t self_in, mp_obj_t key, mp_obj_t value) {
     mp_check_self(MP_OBJ_IS_DICT_TYPE(self_in));
     mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_in);
+    mp_ensure_not_fixed(self);
     mp_map_lookup(&self->map, key, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value;
     return self_in;
 }

+ 3 - 16
py/objexcept.c

@@ -43,9 +43,6 @@
 // Number of traceback entries to reserve in the emergency exception buffer
 #define EMG_TRACEBACK_ALLOC (2 * TRACEBACK_ENTRY_LEN)
 
-// Instance of MemoryError exception - needed by mp_malloc_fail
-const mp_obj_exception_t mp_const_MemoryError_obj = {{&mp_type_MemoryError}, 0, 0, NULL, (mp_obj_tuple_t*)&mp_const_empty_tuple_obj};
-
 // Optionally allocated buffer for storing the first argument of an exception
 // allocated when the heap is locked.
 #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
@@ -96,7 +93,7 @@ mp_obj_t mp_alloc_emergency_exception_buf(mp_obj_t size_in) {
 // definition module-private so far, have it here.
 const mp_obj_exception_t mp_const_GeneratorExit_obj = {{&mp_type_GeneratorExit}, 0, 0, NULL, (mp_obj_tuple_t*)&mp_const_empty_tuple_obj};
 
-STATIC void mp_obj_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) {
+void mp_obj_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) {
     mp_obj_exception_t *o = MP_OBJ_TO_PTR(o_in);
     mp_print_kind_t k = kind & ~PRINT_EXC_SUBCLASS;
     bool is_subclass = kind & PRINT_EXC_SUBCLASS;
@@ -189,7 +186,7 @@ mp_obj_t mp_obj_exception_get_value(mp_obj_t self_in) {
     }
 }
 
-STATIC void exception_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
+void mp_obj_exception_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
     mp_obj_exception_t *self = MP_OBJ_TO_PTR(self_in);
     if (dest[0] != MP_OBJ_NULL) {
         // store/delete attribute
@@ -217,17 +214,7 @@ const mp_obj_type_t mp_type_BaseException = {
     .name = MP_QSTR_BaseException,
     .print = mp_obj_exception_print,
     .make_new = mp_obj_exception_make_new,
-    .attr = exception_attr,
-};
-
-#define MP_DEFINE_EXCEPTION(exc_name, base_name) \
-const mp_obj_type_t mp_type_ ## exc_name = { \
-    { &mp_type_type }, \
-    .name = MP_QSTR_ ## exc_name, \
-    .print = mp_obj_exception_print, \
-    .make_new = mp_obj_exception_make_new, \
-    .attr = exception_attr, \
-    .parent = &mp_type_ ## base_name, \
+    .attr = mp_obj_exception_attr,
 };
 
 // List of all exceptions, arranged as in the table at:

+ 13 - 0
py/objexcept.h

@@ -37,4 +37,17 @@ typedef struct _mp_obj_exception_t {
     mp_obj_tuple_t *args;
 } mp_obj_exception_t;
 
+void mp_obj_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind);
+void mp_obj_exception_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest);
+
+#define MP_DEFINE_EXCEPTION(exc_name, base_name) \
+const mp_obj_type_t mp_type_ ## exc_name = { \
+    { &mp_type_type }, \
+    .name = MP_QSTR_ ## exc_name, \
+    .print = mp_obj_exception_print, \
+    .make_new = mp_obj_exception_make_new, \
+    .attr = mp_obj_exception_attr, \
+    .parent = &mp_type_ ## base_name, \
+};
+
 #endif // MICROPY_INCLUDED_PY_OBJEXCEPT_H

+ 1 - 1
py/objfloat.c

@@ -293,7 +293,7 @@ mp_obj_t mp_obj_float_binary_op(mp_binary_op_t op, mp_float_t lhs_val, mp_obj_t
             break;
         case MP_BINARY_OP_POWER:
         case MP_BINARY_OP_INPLACE_POWER:
-            if (lhs_val == 0 && rhs_val < 0) {
+            if (lhs_val == 0 && rhs_val < 0 && !isinf(rhs_val)) {
                 goto zero_division_error;
             }
             if (lhs_val < 0 && rhs_val != MICROPY_FLOAT_C_FUN(floor)(rhs_val)) {

+ 11 - 12
py/objint.c

@@ -222,27 +222,26 @@ size_t mp_int_format_size(size_t num_bits, int base, const char *prefix, char co
 char *mp_obj_int_formatted(char **buf, size_t *buf_size, size_t *fmt_size, mp_const_obj_t self_in,
                            int base, const char *prefix, char base_char, char comma) {
     fmt_int_t num;
+    #if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_NONE
+    // Only have small ints; get the integer value to format.
+    num = MP_OBJ_SMALL_INT_VALUE(self_in);
+    #else
     if (MP_OBJ_IS_SMALL_INT(self_in)) {
         // A small int; get the integer value to format.
         num = MP_OBJ_SMALL_INT_VALUE(self_in);
-#if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE
-    } else if (MP_OBJ_IS_TYPE(self_in, &mp_type_int)) {
+    } else {
+        assert(MP_OBJ_IS_TYPE(self_in, &mp_type_int));
         // Not a small int.
-#if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_LONGLONG
+        #if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_LONGLONG
         const mp_obj_int_t *self = self_in;
         // Get the value to format; mp_obj_get_int truncates to mp_int_t.
         num = self->val;
-#else
+        #else
         // Delegate to the implementation for the long int.
         return mp_obj_int_formatted_impl(buf, buf_size, fmt_size, self_in, base, prefix, base_char, comma);
-#endif
-#endif
-    } else {
-        // Not an int.
-        **buf = '\0';
-        *fmt_size = 0;
-        return *buf;
+        #endif
     }
+    #endif
 
     char sign = '\0';
     if (num < 0) {
@@ -378,7 +377,7 @@ mp_obj_t mp_obj_int_binary_op_extra_cases(mp_binary_op_t op, mp_obj_t lhs_in, mp
         // true acts as 0
         return mp_binary_op(op, lhs_in, MP_OBJ_NEW_SMALL_INT(1));
     } else if (op == MP_BINARY_OP_MULTIPLY) {
-        if (MP_OBJ_IS_STR(rhs_in) || MP_OBJ_IS_TYPE(rhs_in, &mp_type_bytes) || MP_OBJ_IS_TYPE(rhs_in, &mp_type_tuple) || MP_OBJ_IS_TYPE(rhs_in, &mp_type_list)) {
+        if (MP_OBJ_IS_STR_OR_BYTES(rhs_in) || MP_OBJ_IS_TYPE(rhs_in, &mp_type_tuple) || MP_OBJ_IS_TYPE(rhs_in, &mp_type_list)) {
             // multiply is commutative for these types, so delegate to them
             return mp_binary_op(op, rhs_in, lhs_in);
         }

+ 2 - 0
py/objlist.h

@@ -35,4 +35,6 @@ typedef struct _mp_obj_list_t {
     mp_obj_t *items;
 } mp_obj_list_t;
 
+void mp_obj_list_init(mp_obj_list_t *o, size_t n);
+
 #endif // MICROPY_INCLUDED_PY_OBJLIST_H

+ 17 - 11
py/objmodule.c

@@ -247,17 +247,7 @@ mp_obj_t mp_module_get(qstr module_name) {
         if (el == NULL) {
             return MP_OBJ_NULL;
         }
-
-        if (MICROPY_MODULE_BUILTIN_INIT) {
-            // look for __init__ and call it if it exists
-            mp_obj_t dest[2];
-            mp_load_method_maybe(el->value, MP_QSTR___init__, dest);
-            if (dest[0] != MP_OBJ_NULL) {
-                mp_call_method_n_kw(0, 0, dest);
-                // register module so __init__ is not called again
-                mp_module_register(module_name, el->value);
-            }
-        }
+        mp_module_call_init(module_name, el->value);
     }
 
     // module found, return it
@@ -268,3 +258,19 @@ void mp_module_register(qstr qst, mp_obj_t module) {
     mp_map_t *mp_loaded_modules_map = &MP_STATE_VM(mp_loaded_modules_dict).map;
     mp_map_lookup(mp_loaded_modules_map, MP_OBJ_NEW_QSTR(qst), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = module;
 }
+
+#if MICROPY_MODULE_BUILTIN_INIT
+void mp_module_call_init(qstr module_name, mp_obj_t module_obj) {
+    // Look for __init__ and call it if it exists
+    mp_obj_t dest[2];
+    mp_load_method_maybe(module_obj, MP_QSTR___init__, dest);
+    if (dest[0] != MP_OBJ_NULL) {
+        mp_call_method_n_kw(0, 0, dest);
+        // Register module so __init__ is not called again.
+        // If a module can be referenced by more than one name (eg due to weak links)
+        // then __init__ will still be called for each distinct import, and it's then
+        // up to the particular module to make sure it's __init__ code only runs once.
+        mp_module_register(module_name, module_obj);
+    }
+}
+#endif

+ 9 - 0
py/objmodule.h

@@ -34,4 +34,13 @@ extern const mp_map_t mp_builtin_module_weak_links_map;
 mp_obj_t mp_module_get(qstr module_name);
 void mp_module_register(qstr qstr, mp_obj_t module);
 
+#if MICROPY_MODULE_BUILTIN_INIT
+void mp_module_call_init(qstr module_name, mp_obj_t module_obj);
+#else
+static inline void mp_module_call_init(qstr module_name, mp_obj_t module_obj) {
+    (void)module_name;
+    (void)module_obj;
+}
+#endif
+
 #endif // MICROPY_INCLUDED_PY_OBJMODULE_H

+ 21 - 0
py/objrange.c

@@ -138,6 +138,24 @@ STATIC mp_obj_t range_unary_op(mp_unary_op_t op, mp_obj_t self_in) {
     }
 }
 
+#if MICROPY_PY_BUILTINS_RANGE_BINOP
+STATIC mp_obj_t range_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
+    if (!MP_OBJ_IS_TYPE(rhs_in, &mp_type_range) || op != MP_BINARY_OP_EQUAL) {
+        return MP_OBJ_NULL; // op not supported
+    }
+    mp_obj_range_t *lhs = MP_OBJ_TO_PTR(lhs_in);
+    mp_obj_range_t *rhs = MP_OBJ_TO_PTR(rhs_in);
+    mp_int_t lhs_len = range_len(lhs);
+    mp_int_t rhs_len = range_len(rhs);
+    return mp_obj_new_bool(
+        lhs_len == rhs_len
+        && (lhs_len == 0
+            || (lhs->start == rhs->start
+                && (lhs_len == 1 || lhs->step == rhs->step)))
+    );
+}
+#endif
+
 STATIC mp_obj_t range_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) {
     if (value == MP_OBJ_SENTINEL) {
         // load
@@ -195,6 +213,9 @@ const mp_obj_type_t mp_type_range = {
     .print = range_print,
     .make_new = range_make_new,
     .unary_op = range_unary_op,
+    #if MICROPY_PY_BUILTINS_RANGE_BINOP
+    .binary_op = range_binary_op,
+    #endif
     .subscr = range_subscr,
     .getiter = range_getiter,
 #if MICROPY_PY_BUILTINS_RANGE_ATTRS

+ 6 - 5
py/objstr.c

@@ -223,7 +223,10 @@ STATIC mp_obj_t bytes_make_new(const mp_obj_type_t *type_in, size_t n_args, size
     }
 
     if (MP_OBJ_IS_SMALL_INT(args[0])) {
-        uint len = MP_OBJ_SMALL_INT_VALUE(args[0]);
+        mp_int_t len = MP_OBJ_SMALL_INT_VALUE(args[0]);
+        if (len < 0) {
+            mp_raise_ValueError(NULL);
+        }
         vstr_t vstr;
         vstr_init_len(&vstr, len);
         memset(vstr.buf, 0, len);
@@ -660,9 +663,7 @@ STATIC mp_obj_t str_rsplit(size_t n_args, const mp_obj_t *args) {
             }
             res->items[idx--] = mp_obj_new_str_of_type(self_type, s + sep_len, last - s - sep_len);
             last = s;
-            if (splits > 0) {
-                splits--;
-            }
+            splits--;
         }
         if (idx != 0) {
             // We split less parts than split limit, now go cleanup surplus
@@ -1704,7 +1705,7 @@ STATIC mp_obj_t str_count(size_t n_args, const mp_obj_t *args) {
 
     // if needle_len is zero then we count each gap between characters as an occurrence
     if (needle_len == 0) {
-        return MP_OBJ_NEW_SMALL_INT(unichar_charlen((const char*)start, end - start) + 1);
+        return MP_OBJ_NEW_SMALL_INT(utf8_charlen(start, end - start) + 1);
     }
 
     // count the occurrences

+ 1 - 1
py/objstrunicode.c

@@ -104,7 +104,7 @@ STATIC mp_obj_t uni_unary_op(mp_unary_op_t op, mp_obj_t self_in) {
         case MP_UNARY_OP_BOOL:
             return mp_obj_new_bool(str_len != 0);
         case MP_UNARY_OP_LEN:
-            return MP_OBJ_NEW_SMALL_INT(unichar_charlen((const char *)str_data, str_len));
+            return MP_OBJ_NEW_SMALL_INT(utf8_charlen(str_data, str_len));
         default:
             return MP_OBJ_NULL; // op not supported
     }

+ 12 - 2
py/parsenum.c

@@ -172,10 +172,15 @@ mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool
 #if MICROPY_PY_BUILTINS_FLOAT
 
 // DEC_VAL_MAX only needs to be rough and is used to retain precision while not overflowing
+// SMALL_NORMAL_VAL is the smallest power of 10 that is still a normal float
 #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
 #define DEC_VAL_MAX 1e20F
+#define SMALL_NORMAL_VAL (1e-37F)
+#define SMALL_NORMAL_EXP (-37)
 #elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
 #define DEC_VAL_MAX 1e200
+#define SMALL_NORMAL_VAL (1e-307)
+#define SMALL_NORMAL_EXP (-307)
 #endif
 
     const char *top = str + len;
@@ -275,8 +280,13 @@ mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool
             exp_val = -exp_val;
         }
 
-        // apply the exponent
-        dec_val *= MICROPY_FLOAT_C_FUN(pow)(10, exp_val + exp_extra);
+        // apply the exponent, making sure it's not a subnormal value
+        exp_val += exp_extra;
+        if (exp_val < SMALL_NORMAL_EXP) {
+            exp_val -= SMALL_NORMAL_EXP;
+            dec_val *= SMALL_NORMAL_VAL;
+        }
+        dec_val *= MICROPY_FLOAT_C_FUN(pow)(10, exp_val);
     }
 
     // negate value if needed

+ 5 - 1
py/persistentcode.c

@@ -200,7 +200,11 @@ STATIC mp_raw_code_t *load_raw_code(mp_reader_t *reader) {
 
     // create raw_code and return it
     mp_raw_code_t *rc = mp_emit_glue_new_raw_code();
-    mp_emit_glue_assign_bytecode(rc, bytecode, bc_len, const_table,
+    mp_emit_glue_assign_bytecode(rc, bytecode,
+        #if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS
+        bc_len,
+        #endif
+        const_table,
         #if MICROPY_PERSISTENT_CODE_SAVE
         n_obj, n_raw_code,
         #endif

+ 43 - 36
py/py.mk

@@ -101,7 +101,7 @@ $(BUILD)/extmod/modbtree.o: CFLAGS += $(BTREE_DEFS)
 endif
 
 # py object files
-PY_O_BASENAME = \
+PY_CORE_O_BASENAME = $(addprefix py/,\
 	mpstate.o \
 	nlr.o \
 	nlrx86.o \
@@ -158,6 +158,7 @@ PY_O_BASENAME = \
 	objcell.o \
 	objclosure.o \
 	objcomplex.o \
+	objdeque.o \
 	objdict.o \
 	objenumerate.o \
 	objexcept.o \
@@ -213,41 +214,47 @@ PY_O_BASENAME = \
 	repl.o \
 	smallint.o \
 	frozenmod.o \
-	../extmod/moductypes.o \
-	../extmod/modujson.o \
-	../extmod/modure.o \
-	../extmod/moduzlib.o \
-	../extmod/moduheapq.o \
-	../extmod/modutimeq.o \
-	../extmod/moduhashlib.o \
-	../extmod/modubinascii.o \
-	../extmod/virtpin.o \
-	../extmod/machine_mem.o \
-	../extmod/machine_pinbase.o \
-	../extmod/machine_signal.o \
-	../extmod/machine_pulse.o \
-	../extmod/machine_i2c.o \
-	../extmod/machine_spi.o \
-	../extmod/modussl_axtls.o \
-	../extmod/modussl_mbedtls.o \
-	../extmod/modurandom.o \
-	../extmod/moduselect.o \
-	../extmod/modwebsocket.o \
-	../extmod/modwebrepl.o \
-	../extmod/modframebuf.o \
-	../extmod/vfs.o \
-	../extmod/vfs_reader.o \
-	../extmod/vfs_fat.o \
-	../extmod/vfs_fat_diskio.o \
-	../extmod/vfs_fat_file.o \
-	../extmod/vfs_fat_misc.o \
-	../extmod/utime_mphal.o \
-	../extmod/uos_dupterm.o \
-	../lib/embed/abort_.o \
-	../lib/utils/printf.o \
+	)
+
+PY_EXTMOD_O_BASENAME = \
+	extmod/moductypes.o \
+	extmod/modujson.o \
+	extmod/modure.o \
+	extmod/moduzlib.o \
+	extmod/moduheapq.o \
+	extmod/modutimeq.o \
+	extmod/moduhashlib.o \
+	extmod/modubinascii.o \
+	extmod/virtpin.o \
+	extmod/machine_mem.o \
+	extmod/machine_pinbase.o \
+	extmod/machine_signal.o \
+	extmod/machine_pulse.o \
+	extmod/machine_i2c.o \
+	extmod/machine_spi.o \
+	extmod/modussl_axtls.o \
+	extmod/modussl_mbedtls.o \
+	extmod/modurandom.o \
+	extmod/moduselect.o \
+	extmod/modwebsocket.o \
+	extmod/modwebrepl.o \
+	extmod/modframebuf.o \
+	extmod/vfs.o \
+	extmod/vfs_reader.o \
+	extmod/vfs_fat.o \
+	extmod/vfs_fat_diskio.o \
+	extmod/vfs_fat_file.o \
+	extmod/utime_mphal.o \
+	extmod/uos_dupterm.o \
+	lib/embed/abort_.o \
+	lib/utils/printf.o \
 
 # prepend the build destination prefix to the py object files
-PY_O = $(addprefix $(PY_BUILD)/, $(PY_O_BASENAME))
+PY_CORE_O = $(addprefix $(BUILD)/, $(PY_CORE_O_BASENAME))
+PY_EXTMOD_O = $(addprefix $(BUILD)/, $(PY_EXTMOD_O_BASENAME))
+
+# this is a convenience variable for ports that want core, extmod and frozen code
+PY_O = $(PY_CORE_O) $(PY_EXTMOD_O)
 
 # object file for frozen files
 ifneq ($(FROZEN_DIR),)
@@ -260,8 +267,8 @@ PY_O += $(BUILD)/$(BUILD)/frozen_mpy.o
 endif
 
 # Sources that may contain qstrings
-SRC_QSTR_IGNORE = nlr% emitnx86% emitnx64% emitnthumb% emitnarm% emitnxtensa%
-SRC_QSTR = $(SRC_MOD) $(addprefix py/,$(filter-out $(SRC_QSTR_IGNORE),$(PY_O_BASENAME:.o=.c)) emitnative.c)
+SRC_QSTR_IGNORE = py/nlr% py/emitnx86% py/emitnx64% py/emitnthumb% py/emitnarm% py/emitnxtensa%
+SRC_QSTR = $(SRC_MOD) $(filter-out $(SRC_QSTR_IGNORE),$(PY_CORE_O_BASENAME:.o=.c)) py/emitnative.c $(PY_EXTMOD_O_BASENAME:.o=.c)
 
 # Anything that depends on FORCE will be considered out-of-date
 FORCE:

+ 2 - 1
py/pystack.c

@@ -43,7 +43,8 @@ void *mp_pystack_alloc(size_t n_bytes) {
     #endif
     if (MP_STATE_THREAD(pystack_cur) + n_bytes > MP_STATE_THREAD(pystack_end)) {
         // out of memory in the pystack
-        mp_raise_recursion_depth();
+        nlr_raise(mp_obj_new_exception_arg1(&mp_type_RuntimeError,
+            MP_OBJ_NEW_QSTR(MP_QSTR_pystack_space_exhausted)));
     }
     void *ptr = MP_STATE_THREAD(pystack_cur);
     MP_STATE_THREAD(pystack_cur) += n_bytes;

+ 1 - 0
py/qstr.h

@@ -56,6 +56,7 @@ typedef struct _qstr_pool_t {
 } qstr_pool_t;
 
 #define QSTR_FROM_STR_STATIC(s) (qstr_from_strn((s), strlen(s)))
+#define QSTR_TOTAL() (MP_STATE_VM(last_pool)->total_prev_len + MP_STATE_VM(last_pool)->len)
 
 void qstr_init(void);
 

+ 4 - 0
py/qstrdefs.h

@@ -52,3 +52,7 @@ Q(<genexpr>)
 Q(<string>)
 Q(<stdin>)
 Q(utf-8)
+
+#if MICROPY_ENABLE_PYSTACK
+Q(pystack exhausted)
+#endif

+ 32 - 46
py/repl.c

@@ -27,6 +27,7 @@
 #include <string.h>
 #include "py/obj.h"
 #include "py/runtime.h"
+#include "py/builtin.h"
 #include "py/repl.h"
 
 #if MICROPY_HELPER_REPL
@@ -136,8 +137,11 @@ size_t mp_repl_autocomplete(const char *str, size_t len, const mp_print_t *print
         }
     }
 
-    // begin search in locals dict
-    mp_obj_dict_t *dict = mp_locals_get();
+    size_t nqstr = QSTR_TOTAL();
+
+    // begin search in outer global dict which is accessed from __main__
+    mp_obj_t obj = MP_OBJ_FROM_PTR(&mp_module___main__);
+    mp_obj_t dest[2];
 
     for (;;) {
         // get next word in string to complete
@@ -148,43 +152,20 @@ size_t mp_repl_autocomplete(const char *str, size_t len, const mp_print_t *print
         size_t s_len = str - s_start;
 
         if (str < top) {
-            // a complete word, lookup in current dict
-
-            mp_obj_t obj = MP_OBJ_NULL;
-            for (size_t i = 0; i < dict->map.alloc; i++) {
-                if (MP_MAP_SLOT_IS_FILLED(&dict->map, i)) {
-                    size_t d_len;
-                    const char *d_str = mp_obj_str_get_data(dict->map.table[i].key, &d_len);
-                    if (s_len == d_len && strncmp(s_start, d_str, d_len) == 0) {
-                        obj = dict->map.table[i].value;
-                        break;
-                    }
-                }
+            // a complete word, lookup in current object
+            qstr q = qstr_find_strn(s_start, s_len);
+            if (q == MP_QSTR_NULL) {
+                // lookup will fail
+                return 0;
             }
+            mp_load_method_maybe(obj, q, dest);
+            obj = dest[0]; // attribute, method, or MP_OBJ_NULL if nothing found
 
             if (obj == MP_OBJ_NULL) {
                 // lookup failed
                 return 0;
             }
 
-            // found an object of this name; try to get its dict
-            if (MP_OBJ_IS_TYPE(obj, &mp_type_module)) {
-                dict = mp_obj_module_get_globals(obj);
-            } else {
-                mp_obj_type_t *type;
-                if (MP_OBJ_IS_TYPE(obj, &mp_type_type)) {
-                    type = MP_OBJ_TO_PTR(obj);
-                } else {
-                    type = mp_obj_get_type(obj);
-                }
-                if (type->locals_dict != NULL && type->locals_dict->base.type == &mp_type_dict) {
-                    dict = type->locals_dict;
-                } else {
-                    // obj has no dict
-                    return 0;
-                }
-            }
-
             // skip '.' to move to next word
             ++str;
 
@@ -192,14 +173,15 @@ size_t mp_repl_autocomplete(const char *str, size_t len, const mp_print_t *print
             // end of string, do completion on this partial name
 
             // look for matches
-            int n_found = 0;
             const char *match_str = NULL;
             size_t match_len = 0;
-            for (size_t i = 0; i < dict->map.alloc; i++) {
-                if (MP_MAP_SLOT_IS_FILLED(&dict->map, i)) {
-                    size_t d_len;
-                    const char *d_str = mp_obj_str_get_data(dict->map.table[i].key, &d_len);
-                    if (s_len <= d_len && strncmp(s_start, d_str, s_len) == 0) {
+            qstr q_first = 0, q_last;
+            for (qstr q = 1; q < nqstr; ++q) {
+                size_t d_len;
+                const char *d_str = (const char*)qstr_data(q, &d_len);
+                if (s_len <= d_len && strncmp(s_start, d_str, s_len) == 0) {
+                    mp_load_method_maybe(obj, q, dest);
+                    if (dest[0] != MP_OBJ_NULL) {
                         if (match_str == NULL) {
                             match_str = d_str;
                             match_len = d_len;
@@ -213,13 +195,16 @@ size_t mp_repl_autocomplete(const char *str, size_t len, const mp_print_t *print
                                 }
                             }
                         }
-                        ++n_found;
+                        if (q_first == 0) {
+                            q_first = q;
+                        }
+                        q_last = q;
                     }
                 }
             }
 
             // nothing found
-            if (n_found == 0) {
+            if (q_first == 0) {
                 // If there're no better alternatives, and if it's first word
                 // in the line, try to complete "import".
                 if (s_start == org_str) {
@@ -234,7 +219,7 @@ size_t mp_repl_autocomplete(const char *str, size_t len, const mp_print_t *print
             }
 
             // 1 match found, or multiple matches with a common prefix
-            if (n_found == 1 || match_len > s_len) {
+            if (q_first == q_last || match_len > s_len) {
                 *compl_str = match_str + s_len;
                 return match_len - s_len;
             }
@@ -245,11 +230,12 @@ size_t mp_repl_autocomplete(const char *str, size_t len, const mp_print_t *print
             #define MAX_LINE_LEN  (4 * WORD_SLOT_LEN)
 
             int line_len = MAX_LINE_LEN; // force a newline for first word
-            for (size_t i = 0; i < dict->map.alloc; i++) {
-                if (MP_MAP_SLOT_IS_FILLED(&dict->map, i)) {
-                    size_t d_len;
-                    const char *d_str = mp_obj_str_get_data(dict->map.table[i].key, &d_len);
-                    if (s_len <= d_len && strncmp(s_start, d_str, s_len) == 0) {
+            for (qstr q = q_first; q <= q_last; ++q) {
+                size_t d_len;
+                const char *d_str = (const char*)qstr_data(q, &d_len);
+                if (s_len <= d_len && strncmp(s_start, d_str, s_len) == 0) {
+                    mp_load_method_maybe(obj, q, dest);
+                    if (dest[0] != MP_OBJ_NULL) {
                         int gap = (line_len + WORD_SLOT_LEN - 1) / WORD_SLOT_LEN * WORD_SLOT_LEN - line_len;
                         if (gap < 2) {
                             gap += WORD_SLOT_LEN;

+ 10 - 2
py/runtime.c

@@ -1215,13 +1215,12 @@ mp_vm_return_kind_t mp_resume(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t th
 
     if (type->iternext != NULL && send_value == mp_const_none) {
         mp_obj_t ret = type->iternext(self_in);
+        *ret_val = ret;
         if (ret != MP_OBJ_STOP_ITERATION) {
-            *ret_val = ret;
             return MP_VM_RETURN_YIELD;
         } else {
             // Emulate raise StopIteration()
             // Special case, handled in vm.c
-            *ret_val = MP_OBJ_NULL;
             return MP_VM_RETURN_NORMAL;
         }
     }
@@ -1337,6 +1336,8 @@ import_error:
         return dest[0];
     }
 
+    #if MICROPY_ENABLE_EXTERNAL_IMPORT
+
     // See if it's a package, then can try FS import
     if (!mp_obj_is_package(module)) {
         goto import_error;
@@ -1363,6 +1364,13 @@ import_error:
 
     // TODO lookup __import__ and call that instead of going straight to builtin implementation
     return mp_builtin___import__(5, args);
+
+    #else
+
+    // Package import not supported with external imports disabled
+    goto import_error;
+
+    #endif
 }
 
 void mp_import_all(mp_obj_t module) {

+ 11 - 18
py/unicode.c

@@ -67,9 +67,9 @@ STATIC const uint8_t attr[] = {
     AT_LO, AT_LO, AT_LO, AT_PR, AT_PR, AT_PR, AT_PR, 0
 };
 
-// TODO: Rename to str_get_char
-unichar utf8_get_char(const byte *s) {
 #if MICROPY_PY_BUILTINS_STR_UNICODE
+
+unichar utf8_get_char(const byte *s) {
     unichar ord = *s++;
     if (!UTF8_IS_NONASCII(ord)) return ord;
     ord &= 0x7F;
@@ -80,22 +80,14 @@ unichar utf8_get_char(const byte *s) {
         ord = (ord << 6) | (*s++ & 0x3F);
     }
     return ord;
-#else
-    return *s;
-#endif
 }
 
-// TODO: Rename to str_next_char
 const byte *utf8_next_char(const byte *s) {
-#if MICROPY_PY_BUILTINS_STR_UNICODE
     ++s;
     while (UTF8_IS_CONT(*s)) {
         ++s;
     }
     return s;
-#else
-    return s + 1;
-#endif
 }
 
 mp_uint_t utf8_ptr_to_index(const byte *s, const byte *ptr) {
@@ -109,21 +101,18 @@ mp_uint_t utf8_ptr_to_index(const byte *s, const byte *ptr) {
     return i;
 }
 
-// TODO: Rename to str_charlen
-mp_uint_t unichar_charlen(const char *str, mp_uint_t len) {
-#if MICROPY_PY_BUILTINS_STR_UNICODE
-    mp_uint_t charlen = 0;
-    for (const char *top = str + len; str < top; ++str) {
+size_t utf8_charlen(const byte *str, size_t len) {
+    size_t charlen = 0;
+    for (const byte *top = str + len; str < top; ++str) {
         if (!UTF8_IS_CONT(*str)) {
             ++charlen;
         }
     }
     return charlen;
-#else
-    return len;
-#endif
 }
 
+#endif
+
 // Be aware: These unichar_is* functions are actually ASCII-only!
 bool unichar_isspace(unichar c) {
     return c < 128 && (attr[c] & FL_SPACE) != 0;
@@ -183,6 +172,8 @@ mp_uint_t unichar_xdigit_value(unichar c) {
     return n;
 }
 
+#if MICROPY_PY_BUILTINS_STR_UNICODE
+
 bool utf8_check(const byte *p, size_t len) {
     uint8_t need = 0;
     const byte *end = p + len;
@@ -210,3 +201,5 @@ bool utf8_check(const byte *p, size_t len) {
     }
     return need == 0; // no pending fragments allowed
 }
+
+#endif

+ 28 - 43
py/vm.c

@@ -48,14 +48,6 @@
 // top element.
 // Exception stack also grows up, top element is also pointed at.
 
-// Exception stack unwind reasons (WHY_* in CPython-speak)
-// TODO perhaps compress this to RETURN=0, JUMP>0, with number of unwinds
-// left to do encoded in the JUMP number
-typedef enum {
-    UNWIND_RETURN = 1,
-    UNWIND_JUMP,
-} mp_unwind_reason_t;
-
 #define DECODE_UINT \
     mp_uint_t unum = 0; \
     do { \
@@ -613,29 +605,18 @@ dispatch_loop:
                         mp_call_method_n_kw(3, 0, sp);
                         SET_TOP(mp_const_none);
                     } else if (MP_OBJ_IS_SMALL_INT(TOP())) {
-                        mp_int_t cause_val = MP_OBJ_SMALL_INT_VALUE(TOP());
-                        if (cause_val == UNWIND_RETURN) {
-                            // stack: (..., __exit__, ctx_mgr, ret_val, UNWIND_RETURN)
-                            mp_obj_t ret_val = sp[-1];
-                            sp[-1] = mp_const_none;
-                            sp[0] = mp_const_none;
-                            sp[1] = mp_const_none;
-                            mp_call_method_n_kw(3, 0, sp - 3);
-                            sp[-3] = ret_val;
-                            sp[-2] = MP_OBJ_NEW_SMALL_INT(UNWIND_RETURN);
-                        } else {
-                            assert(cause_val == UNWIND_JUMP);
-                            // stack: (..., __exit__, ctx_mgr, dest_ip, num_exc, UNWIND_JUMP)
-                            mp_obj_t dest_ip = sp[-2];
-                            mp_obj_t num_exc = sp[-1];
-                            sp[-2] = mp_const_none;
-                            sp[-1] = mp_const_none;
-                            sp[0] = mp_const_none;
-                            mp_call_method_n_kw(3, 0, sp - 4);
-                            sp[-4] = dest_ip;
-                            sp[-3] = num_exc;
-                            sp[-2] = MP_OBJ_NEW_SMALL_INT(UNWIND_JUMP);
-                        }
+                        // Getting here there are two distinct cases:
+                        //  - unwind return, stack: (..., __exit__, ctx_mgr, ret_val, SMALL_INT(-1))
+                        //  - unwind jump, stack:   (..., __exit__, ctx_mgr, dest_ip, SMALL_INT(num_exc))
+                        // For both cases we do exactly the same thing.
+                        mp_obj_t data = sp[-1];
+                        mp_obj_t cause = sp[0];
+                        sp[-1] = mp_const_none;
+                        sp[0] = mp_const_none;
+                        sp[1] = mp_const_none;
+                        mp_call_method_n_kw(3, 0, sp - 3);
+                        sp[-3] = data;
+                        sp[-2] = cause;
                         sp -= 2; // we removed (__exit__, ctx_mgr)
                     } else {
                         assert(mp_obj_is_exception_instance(TOP()));
@@ -680,10 +661,11 @@ unwind_jump:;
                             // of a "with" block contains the context manager info.
                             // We're going to run "finally" code as a coroutine
                             // (not calling it recursively). Set up a sentinel
-                            // on a stack so it can return back to us when it is
+                            // on the stack so it can return back to us when it is
                             // done (when WITH_CLEANUP or END_FINALLY reached).
-                            PUSH((mp_obj_t)unum); // push number of exception handlers left to unwind
-                            PUSH(MP_OBJ_NEW_SMALL_INT(UNWIND_JUMP)); // push sentinel
+                            // The sentinel is the number of exception handlers left to
+                            // unwind, which is a non-negative integer.
+                            PUSH(MP_OBJ_NEW_SMALL_INT(unum));
                             ip = exc_sp->handler; // get exception handler byte code address
                             exc_sp--; // pop exception handler
                             goto dispatch_loop; // run the exception handler
@@ -720,11 +702,14 @@ unwind_jump:;
                     } else if (MP_OBJ_IS_SMALL_INT(TOP())) {
                         // We finished "finally" coroutine and now dispatch back
                         // to our caller, based on TOS value
-                        mp_unwind_reason_t reason = MP_OBJ_SMALL_INT_VALUE(POP());
-                        if (reason == UNWIND_RETURN) {
+                        mp_int_t cause = MP_OBJ_SMALL_INT_VALUE(POP());
+                        if (cause < 0) {
+                            // A negative cause indicates unwind return
                             goto unwind_return;
                         } else {
-                            assert(reason == UNWIND_JUMP);
+                            // Otherwise it's an unwind jump and we must push as a raw
+                            // number the number of exception handlers to unwind
+                            PUSH((mp_obj_t)cause);
                             goto unwind_jump;
                         }
                     } else {
@@ -1101,7 +1086,7 @@ unwind_return:
                             // (not calling it recursively). Set up a sentinel
                             // on a stack so it can return back to us when it is
                             // done (when WITH_CLEANUP or END_FINALLY reached).
-                            PUSH(MP_OBJ_NEW_SMALL_INT(UNWIND_RETURN));
+                            PUSH(MP_OBJ_NEW_SMALL_INT(-1));
                             ip = exc_sp->handler;
                             exc_sp--;
                             goto dispatch_loop;
@@ -1177,6 +1162,7 @@ yield:
                     mp_obj_t send_value = POP();
                     mp_obj_t t_exc = MP_OBJ_NULL;
                     mp_obj_t ret_value;
+                    code_state->sp = sp; // Save sp because it's needed if mp_resume raises StopIteration
                     if (inject_exc != MP_OBJ_NULL) {
                         t_exc = inject_exc;
                         inject_exc = MP_OBJ_NULL;
@@ -1192,8 +1178,7 @@ yield:
                     } else if (ret_kind == MP_VM_RETURN_NORMAL) {
                         // Pop exhausted gen
                         sp--;
-                        // TODO: When ret_value can be MP_OBJ_NULL here??
-                        if (ret_value == MP_OBJ_NULL || ret_value == MP_OBJ_STOP_ITERATION) {
+                        if (ret_value == MP_OBJ_STOP_ITERATION) {
                             // Optimize StopIteration
                             // TODO: get StopIteration's value
                             PUSH(mp_const_none);
@@ -1376,7 +1361,8 @@ exception_handler:
                     } else if (*code_state->ip == MP_BC_YIELD_FROM) {
                         // StopIteration inside yield from call means return a value of
                         // yield from, so inject exception's value as yield from's result
-                        *++code_state->sp = mp_obj_exception_get_value(MP_OBJ_FROM_PTR(nlr.ret_val));
+                        // (Instead of stack pop then push we just replace exhausted gen with value)
+                        *code_state->sp = mp_obj_exception_get_value(MP_OBJ_FROM_PTR(nlr.ret_val));
                         code_state->ip++; // yield from is over, move to next instruction
                         goto outer_dispatch_loop; // continue with dispatch loop
                     }
@@ -1389,8 +1375,7 @@ unwind_loop:
             // set file and line number that the exception occurred at
             // TODO: don't set traceback for exceptions re-raised by END_FINALLY.
             // But consider how to handle nested exceptions.
-            // TODO need a better way of not adding traceback to constant objects (right now, just GeneratorExit_obj and MemoryError_obj)
-            if (nlr.ret_val != &mp_const_GeneratorExit_obj && nlr.ret_val != &mp_const_MemoryError_obj) {
+            if (nlr.ret_val != &mp_const_GeneratorExit_obj) {
                 const byte *ip = code_state->fun_bc->bytecode;
                 ip = mp_decode_uint_skip(ip); // skip n_state
                 ip = mp_decode_uint_skip(ip); // skip n_exc_stack