diff --git a/.codespell/exclude-file.txt b/.codespell/exclude-file.txt index 9f49d9ae0cc2..08e1a2b9059e 100644 --- a/.codespell/exclude-file.txt +++ b/.codespell/exclude-file.txt @@ -5,3 +5,6 @@ USB_PRODUCT = "BLOK" print(binascii.b2a_base64(b"fo")) # again, neither will "there" or "wither", since they have "the" i1Qb$TE"rl +ZEN = "the zen of python beautiful is better than ugly explicit is better than implicit simple is better than complex complex is better than complicated flat is better than nested sparse is better than dense readability counts special cases arent special enough to break the rules although practicality beats purity errors should never pass silently unless explicitly silenced in the face of ambiguity refuse the temptation to guess there should be one and preferably only one obvious way to do it although that way may not be obvious at first unless youre dutch now is better than never although never is often better than right now if the implementation is hard to explain its a bad idea if the implementation is easy to explain it may be a good idea namespaces are one honking great idea lets do more of those" + "arent", + "youre", diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 637f2ec3d1eb..690e5f46a4fc 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -1,3 +1,6 @@ +# all: Update Python formatting to ruff-format. +bbd8760bd9a2302e5abee29db279102bb11d7732 + # all: Fix various spelling mistakes found by codespell 2.2.6. cf490a70917a1b2d38ba9b58e763e0837d0f7ca7 diff --git a/.gitattributes b/.gitattributes index db6c4e338655..38bba729fc19 100644 --- a/.gitattributes +++ b/.gitattributes @@ -10,12 +10,15 @@ *.props text eol=crlf *.bat text eol=crlf +# CIRCUITPY-CHANGE: add some more binary types. # These are binary so should never be modified by git. *.a binary +*.ico binary *.png binary *.jpg binary *.dxf binary *.mpy binary +*.der binary *.deb binary *.zip binary *.pdf binary @@ -24,11 +27,4 @@ # These should also not be modified by git. tests/basics/string_cr_conversion.py -text tests/basics/string_crlf_conversion.py -text -ports/stm32/pybcdc.inf_template -text -ports/stm32/usbhost/** -text -ports/cc3200/hal/aes.c -text -ports/cc3200/hal/aes.h -text -ports/cc3200/hal/des.c -text -ports/cc3200/hal/i2s.c -text -ports/cc3200/hal/i2s.h -text -ports/cc3200/version.h -text +# CIRCUITPY-CHANGE: remove non-CircuitPython tests diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dfea63a9707a..f9026830bb05 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -255,10 +255,13 @@ jobs: wget --no-verbose -O gcc-arm.zip https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-mingw-w64-i686-arm-none-eabi.zip unzip -q -d /tmp gcc-arm.zip tar -C /tmp/arm-gnu-toolchain* -cf - . | tar -C /usr/local -xf - - pip install wheel - # requirements_dev.txt doesn't install on windows. (with msys2 python) + # We could use a venv instead, but that requires entering the venv on each run step + # that runs in its own shell. There are some actions that help with that, but not for msys2 + # that I can find. (dhalbert) + pip install --break-system-packages wheel + # requirements-dev.txt doesn't install on windows. (with msys2 python) # instead, pick a subset for what we want to do - pip install cascadetoml jinja2 typer click intelhex + pip install --break-system-packages cascadetoml jinja2 typer click intelhex # check that installed packages work....? which python; python --version; python -c "import cascadetoml" which python3; python3 --version; python3 -c "import cascadetoml" diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 2cfb61baf1eb..729be77979de 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -9,14 +9,14 @@ on: jobs: run: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: test: [all, mpy, native, native_mpy] env: CP_VERSION: ${{ inputs.cp-version }} - MICROPY_CPYTHON3: python3.8 + MICROPY_CPYTHON3: python3.12 MICROPY_MICROPYTHON: ../ports/unix/build-coverage/micropython TEST_all: TEST_mpy: --via-mpy -d basics float micropython @@ -32,7 +32,7 @@ jobs: - name: Set up python uses: actions/setup-python@v5 with: - python-version: 3.8 + python-version: 3.12 - name: Set up submodules uses: ./.github/actions/deps/submodules with: diff --git a/.gitignore b/.gitignore index 70f05586f1a3..9679ed5e0e2b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ # # SPDX-License-Identifier: MIT +# CIRCUITPY-CHANGES: many additions + # Compiled Sources ################### *.o diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index dc8382f2205f..7ae1048a16c8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,6 +2,8 @@ # # SPDX-License-Identifier: Unlicense +# CIRCUITPY-CHANGE: CircuitPython-specific. + repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.0.1 diff --git a/LICENSE b/LICENSE index aec1da60178f..4338915beb14 100644 --- a/LICENSE +++ b/LICENSE @@ -17,5 +17,15 @@ 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. +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +-------------------------------------------------------------------------------- + +#CIRCUITPY-CHANGE: + +Unless specified otherwise (see below), the above license and copyright applies +to all files derived from MicroPython in this repository. + +Individual files may include additional copyright holders and specify other licenses. +See the comments and SPDX headers. diff --git a/Makefile b/Makefile index 4e1b38c0407c..30966918c365 100644 --- a/Makefile +++ b/Makefile @@ -282,6 +282,7 @@ check-stubs: stubs @(cd $(STUBDIR) && set -- */__init__.pyi && mypy "$${@%/*}") @tools/test-stubs.sh +.PHONY: update-frozen-libraries update-frozen-libraries: @echo "Updating all frozen libraries to latest tagged version." cd frozen; for library in *; do cd $$library; ../../tools/git-checkout-latest-tag.sh; cd ..; done @@ -350,3 +351,16 @@ remove-all-submodules: .PHONY: fetch-tags fetch-tags: git fetch --tags --recurse-submodules=no --shallow-since="2023-02-01" https://github.com/adafruit/circuitpython HEAD + +.PHONY: coverage +coverage: + make -j -C ports/unix VARIANT=coverage + +.PHONY: coverage-clean +coverage-fresh: + make -C ports/unix VARIANT=coverage clean + make -j -C ports/unix VARIANT=coverage + +.PHONY: run-tests +run-tests: + cd tests; MICROPY_MICROPYTHON=../ports/unix/build-coverage/micropython ./run-tests.py diff --git a/docs/library/sys.rst b/docs/library/sys.rst index fd37f2abd27d..4e8c889eb017 100644 --- a/docs/library/sys.rst +++ b/docs/library/sys.rst @@ -66,7 +66,7 @@ Constants .. data:: maxsize Maximum value which a native integer type can hold on the current platform, - or maximum value representable by CircuitPython integer type, if it's smaller + or maximum value representable by the CircuitPython integer type, if it's smaller than platform max value (that is the case for CircuitPython ports without long int support). diff --git a/docs/readthedocs/settings/local_settings.py b/docs/readthedocs/settings/local_settings.py index 8d2bac7a7690..c6145dcc52ad 100644 --- a/docs/readthedocs/settings/local_settings.py +++ b/docs/readthedocs/settings/local_settings.py @@ -1,9 +1,10 @@ import os # Directory that the project lives in, aka ../.. -SITE_ROOT = '/'.join(os.path.dirname(__file__).split('/')[0:-2]) +SITE_ROOT = "/".join(os.path.dirname(__file__).split("/")[0:-2]) TEMPLATE_DIRS = ( - "%s/templates/" % SITE_ROOT, # Your custom template directory, before the RTD one to override it. - "%s/readthedocs/templates/" % SITE_ROOT, # Default RTD template dir + "%s/templates/" + % SITE_ROOT, # Your custom template directory, before the RTD one to override it. + "%s/readthedocs/templates/" % SITE_ROOT, # Default RTD template dir ) diff --git a/examples/usercmodule/cppexample/example.cpp b/examples/usercmodule/cppexample/example.cpp index 06809732a4d7..2df832baa76f 100644 --- a/examples/usercmodule/cppexample/example.cpp +++ b/examples/usercmodule/cppexample/example.cpp @@ -1,9 +1,16 @@ extern "C" { #include +#include // Here we implement the function using C++ code, but since it's // declaration has to be compatible with C everything goes in extern "C" scope. mp_obj_t cppfunc(mp_obj_t a_obj, mp_obj_t b_obj) { + // The following no-ops are just here to verify the static assertions used in + // the public API all compile with C++. + MP_STATIC_ASSERT_STR_ARRAY_COMPATIBLE; + if (mp_obj_is_type(a_obj, &mp_type_BaseException)) { + } + // Prove we have (at least) C++11 features. const auto a = mp_obj_get_int(a_obj); const auto b = mp_obj_get_int(b_obj); diff --git a/extmod/extmod.mk b/extmod/extmod.mk index 3f66b5f6d380..a72d473bb485 100644 --- a/extmod/extmod.mk +++ b/extmod/extmod.mk @@ -2,6 +2,7 @@ # and provides rules to build 3rd-party components for extmod modules. # CIRCUITPY-CHANGE: many extmod modules removed +# CIRCUITPY-CHANGE: modzlib.c still used SRC_EXTMOD_C += \ extmod/modasyncio.c \ extmod/modbinascii.c \ @@ -37,6 +38,96 @@ SRC_QSTR += $(SRC_EXTMOD_C) CFLAGS += $(CFLAGS_EXTMOD) $(CFLAGS_THIRDPARTY) LDFLAGS += $(LDFLAGS_EXTMOD) $(LDFLAGS_THIRDPARTY) +################################################################################ +# libm/libm_dbl math library + +# Single-precision math library. +SRC_LIB_LIBM_C += $(addprefix lib/libm/,\ + acoshf.c \ + asinfacosf.c \ + asinhf.c \ + atan2f.c \ + atanf.c \ + atanhf.c \ + ef_rem_pio2.c \ + erf_lgamma.c \ + fmodf.c \ + kf_cos.c \ + kf_rem_pio2.c \ + kf_sin.c \ + kf_tan.c \ + log1pf.c \ + math.c \ + nearbyintf.c \ + roundf.c \ + sf_cos.c \ + sf_erf.c \ + sf_frexp.c \ + sf_ldexp.c \ + sf_modf.c \ + sf_sin.c \ + sf_tan.c \ + wf_lgamma.c \ + wf_tgamma.c \ + ) + +# Choose only one of these sqrt implementations, software or hardware. +SRC_LIB_LIBM_SQRT_SW_C += lib/libm/ef_sqrt.c +SRC_LIB_LIBM_SQRT_HW_C += lib/libm/thumb_vfp_sqrtf.c + +# Double-precision math library. +SRC_LIB_LIBM_DBL_C += $(addprefix lib/libm_dbl/,\ + __cos.c \ + __expo2.c \ + __fpclassify.c \ + __rem_pio2.c \ + __rem_pio2_large.c \ + __signbit.c \ + __sin.c \ + __tan.c \ + acos.c \ + acosh.c \ + asin.c \ + asinh.c \ + atan.c \ + atan2.c \ + atanh.c \ + ceil.c \ + cos.c \ + cosh.c \ + copysign.c \ + erf.c \ + exp.c \ + expm1.c \ + floor.c \ + fmod.c \ + frexp.c \ + ldexp.c \ + lgamma.c \ + log.c \ + log10.c \ + log1p.c \ + modf.c \ + nearbyint.c \ + pow.c \ + rint.c \ + round.c \ + scalbn.c \ + sin.c \ + sinh.c \ + tan.c \ + tanh.c \ + tgamma.c \ + trunc.c \ + ) + +# Choose only one of these sqrt implementations, software or hardware. +SRC_LIB_LIBM_DBL_SQRT_SW_C += lib/libm_dbl/sqrt.c +SRC_LIB_LIBM_DBL_SQRT_HW_C += lib/libm_dbl/thumb_vfp_sqrt.c + +# Too many warnings in libm_dbl, disable for now. +$(BUILD)/lib/libm_dbl/%.o: CFLAGS += -Wno-double-promotion -Wno-float-conversion + ################################################################################ # VFS FAT FS @@ -75,6 +166,7 @@ SRC_THIRDPARTY_C += $(addprefix $(LITTLEFS_DIR)/,\ lfs2_util.c \ ) +# CIRCUITPY-CHANGE: -Wno-missing-field-initializers instead of -Wno-shadow $(BUILD)/$(LITTLEFS_DIR)/lfs2.o: CFLAGS += -Wno-missing-field-initializers endif @@ -235,6 +327,9 @@ SRC_THIRDPARTY_C += $(addprefix $(LWIP_DIR)/,\ core/ipv6/nd6.c \ netif/ethernet.c \ ) +ifeq ($(MICROPY_PY_LWIP_LOOPBACK),1) +CFLAGS_EXTMOD += -DLWIP_NETIF_LOOPBACK=1 +endif ifeq ($(MICROPY_PY_LWIP_SLIP),1) CFLAGS_EXTMOD += -DMICROPY_PY_LWIP_SLIP=1 SRC_THIRDPARTY_C += $(LWIP_DIR)/netif/slipif.c diff --git a/extmod/lwip-include/lwipopts.h b/extmod/lwip-include/lwipopts.h index 805bec2230de..584decfe85f8 100644 --- a/extmod/lwip-include/lwipopts.h +++ b/extmod/lwip-include/lwipopts.h @@ -23,6 +23,7 @@ typedef uint32_t sys_prot_t; #define LWIP_NETCONN 0 #define LWIP_SOCKET 0 +// CIRCUITPY-CHANGE: #if instead of #ifdef #if MICROPY_PY_LWIP_SLIP #define LWIP_HAVE_SLIPIF 1 #endif diff --git a/extmod/misc.h b/extmod/misc.h index 80e5b59a08ee..8ea51a981ed6 100644 --- a/extmod/misc.h +++ b/extmod/misc.h @@ -39,10 +39,12 @@ bool mp_os_dupterm_is_builtin_stream(mp_const_obj_t stream); void mp_os_dupterm_stream_detached_attached(mp_obj_t stream_detached, mp_obj_t stream_attached); uintptr_t mp_os_dupterm_poll(uintptr_t poll_flags); int mp_os_dupterm_rx_chr(void); -void mp_os_dupterm_tx_strn(const char *str, size_t len); +int mp_os_dupterm_tx_strn(const char *str, size_t len); void mp_os_deactivate(size_t dupterm_idx, const char *msg, mp_obj_t exc); #else -#define mp_os_dupterm_tx_strn(s, l) +static inline int mp_os_dupterm_tx_strn(const char *s, size_t l) { + return -1; +} #endif #endif // MICROPY_INCLUDED_EXTMOD_MISC_H diff --git a/extmod/modasyncio.c b/extmod/modasyncio.c index 4667e3de5332..8d7b52bc227e 100644 --- a/extmod/modasyncio.c +++ b/extmod/modasyncio.c @@ -31,6 +31,7 @@ #if MICROPY_PY_ASYNCIO +// CIRCUITPY-CHANGE #if CIRCUITPY && !(defined(__unix__) || defined(__APPLE__)) #include "shared-bindings/supervisor/__init__.h" #endif @@ -308,6 +309,7 @@ STATIC mp_obj_t task_getiter(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) { STATIC mp_obj_t task_iternext(mp_obj_t self_in) { mp_obj_task_t *self = MP_OBJ_TO_PTR(self_in); if (TASK_IS_DONE(self)) { + // CIRCUITPY-CHANGE if (self->data == mp_const_none) { // Task finished but has already been sent to the loop's exception handler. mp_raise_StopIteration(MP_OBJ_NULL); diff --git a/extmod/modbinascii.c b/extmod/modbinascii.c index 78d4c891f85c..90fa7bbf906f 100644 --- a/extmod/modbinascii.c +++ b/extmod/modbinascii.c @@ -34,6 +34,7 @@ #if MICROPY_PY_BINASCII +// CIRCUITPY-CHANGE: added static void check_not_unicode(const mp_obj_t arg) { #if MICROPY_CPYTHON_COMPAT if (mp_obj_is_str(arg)) { diff --git a/extmod/modhashlib.c b/extmod/modhashlib.c index 86a1a2987e8b..fc9c87320004 100644 --- a/extmod/modhashlib.c +++ b/extmod/modhashlib.c @@ -114,6 +114,7 @@ STATIC mp_obj_t hashlib_sha256_digest(mp_obj_t self_in) { #else +// CIRCUITPY-CHANGE static void check_not_unicode(const mp_obj_t arg) { #if MICROPY_CPYTHON_COMPAT if (mp_obj_is_str(arg)) { @@ -136,6 +137,7 @@ STATIC mp_obj_t hashlib_sha256_make_new(const mp_obj_type_t *type, size_t n_args } STATIC mp_obj_t hashlib_sha256_update(mp_obj_t self_in, mp_obj_t arg) { + // CIRCUITPY-CHANGE check_not_unicode(arg); mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); hashlib_ensure_not_final(self); @@ -306,7 +308,7 @@ STATIC mp_obj_t hashlib_md5_digest(mp_obj_t self_in) { #if MICROPY_SSL_MBEDTLS -#if MBEDTLS_VERSION_NUMBER < 0x02070000 +#if MBEDTLS_VERSION_NUMBER < 0x02070000 || MBEDTLS_VERSION_NUMBER >= 0x03000000 #define mbedtls_md5_starts_ret mbedtls_md5_starts #define mbedtls_md5_update_ret mbedtls_md5_update #define mbedtls_md5_finish_ret mbedtls_md5_finish diff --git a/extmod/modjson.c b/extmod/modjson.c index 9c0e6b97b675..12dc70632594 100644 --- a/extmod/modjson.c +++ b/extmod/modjson.c @@ -26,6 +26,7 @@ #include +// CIRCUITPY-CHANGE #include "py/binary.h" #include "py/objarray.h" #include "py/objlist.h" @@ -111,6 +112,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_json_dumps_obj, mod_json_dumps); #endif +// CIRCUITPY-CHANGE #define JSON_DEBUG(...) (void)0 // #define JSON_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__) @@ -147,6 +149,7 @@ typedef struct _json_stream_t { STATIC byte json_stream_next(json_stream_t *s) { mp_uint_t ret = s->read(s->stream_obj, &s->cur, 1, &s->errcode); + // CIRCUITPY-CHANGE JSON_DEBUG(" usjon_stream_next err:%2d cur: %c \n", s->errcode, s->cur); if (s->errcode != 0) { mp_raise_OSError(s->errcode); @@ -427,6 +430,7 @@ STATIC mp_obj_t _mod_json_load(mp_obj_t stream_obj, bool return_first_json) { mp_raise_ValueError(MP_ERROR_TEXT("syntax error in JSON")); } +// CIRCUITPY-CHANGE STATIC mp_obj_t mod_json_load(mp_obj_t stream_obj) { return _mod_json_load(stream_obj, true); } @@ -437,6 +441,7 @@ STATIC mp_obj_t mod_json_loads(mp_obj_t obj) { mp_get_buffer_raise(obj, &bufinfo, MP_BUFFER_READ); vstr_t vstr = {bufinfo.len, bufinfo.len, (char *)bufinfo.buf, true}; mp_obj_stringio_t sio = {{&mp_type_stringio}, &vstr, 0, MP_OBJ_NULL}; + // CIRCUITPY-CHANGE return _mod_json_load(MP_OBJ_FROM_PTR(&sio), false); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_json_loads_obj, mod_json_loads); diff --git a/extmod/modmachine.c b/extmod/modmachine.c new file mode 100644 index 000000000000..a64b8ba79d43 --- /dev/null +++ b/extmod/modmachine.c @@ -0,0 +1,242 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023 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/runtime.h" + +#if MICROPY_PY_MACHINE + +#include "extmod/modmachine.h" +#include "shared/runtime/pyexec.h" + +#if MICROPY_PY_MACHINE_DHT_READINTO +#include "drivers/dht/dht.h" +#endif + +// The port must provide implementations of these low-level machine functions. + +STATIC void mp_machine_idle(void); + +#if MICROPY_PY_MACHINE_BOOTLOADER +NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args); +#endif + +#if MICROPY_PY_MACHINE_BARE_METAL_FUNCS +STATIC mp_obj_t mp_machine_unique_id(void); +NORETURN STATIC void mp_machine_reset(void); +STATIC mp_int_t mp_machine_reset_cause(void); +STATIC mp_obj_t mp_machine_get_freq(void); +STATIC void mp_machine_set_freq(size_t n_args, const mp_obj_t *args); +STATIC void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args); +NORETURN STATIC void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args); +#endif + +// The port can provide additional machine-module implementation in this file. +#ifdef MICROPY_PY_MACHINE_INCLUDEFILE +#include MICROPY_PY_MACHINE_INCLUDEFILE +#endif + +STATIC mp_obj_t machine_soft_reset(void) { + pyexec_system_exit = PYEXEC_FORCED_EXIT; + mp_raise_type(&mp_type_SystemExit); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_soft_reset_obj, machine_soft_reset); + +#if MICROPY_PY_MACHINE_BOOTLOADER +NORETURN mp_obj_t machine_bootloader(size_t n_args, const mp_obj_t *args) { + mp_machine_bootloader(n_args, args); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_bootloader_obj, 0, 1, machine_bootloader); +#endif + +STATIC mp_obj_t machine_idle(void) { + mp_machine_idle(); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_idle_obj, machine_idle); + +#if MICROPY_PY_MACHINE_BARE_METAL_FUNCS + +STATIC mp_obj_t machine_unique_id(void) { + return mp_machine_unique_id(); +} +MP_DEFINE_CONST_FUN_OBJ_0(machine_unique_id_obj, machine_unique_id); + +NORETURN STATIC mp_obj_t machine_reset(void) { + mp_machine_reset(); +} +MP_DEFINE_CONST_FUN_OBJ_0(machine_reset_obj, machine_reset); + +STATIC mp_obj_t machine_reset_cause(void) { + return MP_OBJ_NEW_SMALL_INT(mp_machine_reset_cause()); +} +MP_DEFINE_CONST_FUN_OBJ_0(machine_reset_cause_obj, machine_reset_cause); + +STATIC mp_obj_t machine_freq(size_t n_args, const mp_obj_t *args) { + if (n_args == 0) { + return mp_machine_get_freq(); + } else { + mp_machine_set_freq(n_args, args); + return mp_const_none; + } +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_freq_obj, 0, 1, machine_freq); + +STATIC mp_obj_t machine_lightsleep(size_t n_args, const mp_obj_t *args) { + mp_machine_lightsleep(n_args, args); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_lightsleep_obj, 0, 1, machine_lightsleep); + +NORETURN STATIC mp_obj_t machine_deepsleep(size_t n_args, const mp_obj_t *args) { + mp_machine_deepsleep(n_args, args); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_deepsleep_obj, 0, 1, machine_deepsleep); + +#endif + +#if MICROPY_PY_MACHINE_DISABLE_IRQ_ENABLE_IRQ + +STATIC mp_obj_t machine_disable_irq(void) { + uint32_t state = MICROPY_BEGIN_ATOMIC_SECTION(); + return mp_obj_new_int(state); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_disable_irq_obj, machine_disable_irq); + +STATIC mp_obj_t machine_enable_irq(mp_obj_t state_in) { + uint32_t state = mp_obj_get_int(state_in); + MICROPY_END_ATOMIC_SECTION(state); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_enable_irq_obj, machine_enable_irq); + +#endif + +STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_machine) }, + + // Memory access objects. + { MP_ROM_QSTR(MP_QSTR_mem8), MP_ROM_PTR(&machine_mem8_obj) }, + { MP_ROM_QSTR(MP_QSTR_mem16), MP_ROM_PTR(&machine_mem16_obj) }, + { MP_ROM_QSTR(MP_QSTR_mem32), MP_ROM_PTR(&machine_mem32_obj) }, + + // Miscellaneous functions. + #if MICROPY_PY_MACHINE_BARE_METAL_FUNCS + { MP_ROM_QSTR(MP_QSTR_unique_id), MP_ROM_PTR(&machine_unique_id_obj) }, + #endif + + // Reset related functions. + { MP_ROM_QSTR(MP_QSTR_soft_reset), MP_ROM_PTR(&machine_soft_reset_obj) }, + #if MICROPY_PY_MACHINE_BOOTLOADER + { MP_ROM_QSTR(MP_QSTR_bootloader), MP_ROM_PTR(&machine_bootloader_obj) }, + #endif + #if MICROPY_PY_MACHINE_BARE_METAL_FUNCS + { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&machine_reset_obj) }, + { MP_ROM_QSTR(MP_QSTR_reset_cause), MP_ROM_PTR(&machine_reset_cause_obj) }, + #endif + + // Power related functions. + { MP_ROM_QSTR(MP_QSTR_idle), MP_ROM_PTR(&machine_idle_obj) }, + #if MICROPY_PY_MACHINE_BARE_METAL_FUNCS + { MP_ROM_QSTR(MP_QSTR_freq), MP_ROM_PTR(&machine_freq_obj) }, + { MP_ROM_QSTR(MP_QSTR_lightsleep), MP_ROM_PTR(&machine_lightsleep_obj) }, + { MP_ROM_QSTR(MP_QSTR_deepsleep), MP_ROM_PTR(&machine_deepsleep_obj) }, + #endif + + // Interrupt related functions. + #if MICROPY_PY_MACHINE_DISABLE_IRQ_ENABLE_IRQ + { MP_ROM_QSTR(MP_QSTR_disable_irq), MP_ROM_PTR(&machine_disable_irq_obj) }, + { MP_ROM_QSTR(MP_QSTR_enable_irq), MP_ROM_PTR(&machine_enable_irq_obj) }, + #endif + + // Functions for bit protocols. + #if MICROPY_PY_MACHINE_BITSTREAM + { MP_ROM_QSTR(MP_QSTR_bitstream), MP_ROM_PTR(&machine_bitstream_obj) }, + #endif + #if MICROPY_PY_MACHINE_DHT_READINTO + { MP_ROM_QSTR(MP_QSTR_dht_readinto), MP_ROM_PTR(&dht_readinto_obj) }, + #endif + #if MICROPY_PY_MACHINE_PULSE + { MP_ROM_QSTR(MP_QSTR_time_pulse_us), MP_ROM_PTR(&machine_time_pulse_us_obj) }, + #endif + + // Classes for PinBase and Signal. + #if MICROPY_PY_MACHINE_PIN_BASE + { MP_ROM_QSTR(MP_QSTR_PinBase), MP_ROM_PTR(&machine_pinbase_type) }, + #endif + { MP_ROM_QSTR(MP_QSTR_Signal), MP_ROM_PTR(&machine_signal_type) }, + + // Classes for software bus protocols. + #if MICROPY_PY_MACHINE_SOFTI2C + { MP_ROM_QSTR(MP_QSTR_SoftI2C), MP_ROM_PTR(&mp_machine_soft_i2c_type) }, + #endif + #if MICROPY_PY_MACHINE_SOFTSPI + { MP_ROM_QSTR(MP_QSTR_SoftSPI), MP_ROM_PTR(&mp_machine_soft_spi_type) }, + #endif + + // Classes for hardware peripherals. + #if MICROPY_PY_MACHINE_ADC + { MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&machine_adc_type) }, + #endif + #if MICROPY_PY_MACHINE_ADC_BLOCK + { MP_ROM_QSTR(MP_QSTR_ADCBlock), MP_ROM_PTR(&machine_adc_block_type) }, + #endif + #if MICROPY_PY_MACHINE_DAC + { MP_ROM_QSTR(MP_QSTR_DAC), MP_ROM_PTR(&machine_dac_type) }, + #endif + #if MICROPY_PY_MACHINE_I2C + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&machine_i2c_type) }, + #endif + #if MICROPY_PY_MACHINE_I2S + { MP_ROM_QSTR(MP_QSTR_I2S), MP_ROM_PTR(&machine_i2s_type) }, + #endif + #if MICROPY_PY_MACHINE_PWM + { MP_ROM_QSTR(MP_QSTR_PWM), MP_ROM_PTR(&machine_pwm_type) }, + #endif + #if MICROPY_PY_MACHINE_SPI + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&machine_spi_type) }, + #endif + #if MICROPY_PY_MACHINE_UART + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&machine_uart_type) }, + #endif + #if MICROPY_PY_MACHINE_WDT + { MP_ROM_QSTR(MP_QSTR_WDT), MP_ROM_PTR(&machine_wdt_type) }, + #endif + + // A port can add extra entries to the module by defining the following macro. + #ifdef MICROPY_PY_MACHINE_EXTRA_GLOBALS + MICROPY_PY_MACHINE_EXTRA_GLOBALS + #endif +}; +STATIC MP_DEFINE_CONST_DICT(machine_module_globals, machine_module_globals_table); + +const mp_obj_module_t mp_module_machine = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&machine_module_globals, +}; + +MP_REGISTER_EXTENSIBLE_MODULE(MP_QSTR_machine, mp_module_machine); + +#endif // MICROPY_PY_MACHINE diff --git a/extmod/modmachine.h b/extmod/modmachine.h new file mode 100644 index 000000000000..d186ce661510 --- /dev/null +++ b/extmod/modmachine.h @@ -0,0 +1,269 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023 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. + */ + +#ifndef MICROPY_INCLUDED_EXTMOD_MODMACHINE_H +#define MICROPY_INCLUDED_EXTMOD_MODMACHINE_H + +#include "py/mphal.h" +#include "py/obj.h" + +#if MICROPY_PY_MACHINE + +#include "drivers/bus/spi.h" + +// Whether to enable the ADC.init() method. +// Requires a port to implement mp_machine_adc_init_helper(). +#ifndef MICROPY_PY_MACHINE_ADC_INIT +#define MICROPY_PY_MACHINE_ADC_INIT (0) +#endif + +// Whether to enable the ADC.deinit() method. +// Requires a port to implement mp_machine_adc_deinit(). +#ifndef MICROPY_PY_MACHINE_ADC_DEINIT +#define MICROPY_PY_MACHINE_ADC_DEINIT (0) +#endif + +// Whether to enable the ADC.block() method. +// Requires a port to implement mp_machine_adc_block(). +#ifndef MICROPY_PY_MACHINE_ADC_BLOCK +#define MICROPY_PY_MACHINE_ADC_BLOCK (0) +#endif + +// Whether to enable the ADC.read_uv() method. +// Requires a port to implement mp_machine_adc_read_uv(). +#ifndef MICROPY_PY_MACHINE_ADC_READ_UV +#define MICROPY_PY_MACHINE_ADC_READ_UV (0) +#endif + +// Whether to enable the ADC.atten() and ADC.width() methods. +// Note: these are legacy and should not be used on new ports. +#ifndef MICROPY_PY_MACHINE_ADC_ATTEN_WIDTH +#define MICROPY_PY_MACHINE_ADC_ATTEN_WIDTH (0) +#endif + +// Whether to enable the ADC.read() method. +// Note: this is legacy and should not be used on new ports. +#ifndef MICROPY_PY_MACHINE_ADC_READ +#define MICROPY_PY_MACHINE_ADC_READ (0) +#endif + +// Whether to enable the UART.sendbreak() method. +// Requires a port to implement mp_machine_uart_sendbreak(). +#ifndef MICROPY_PY_MACHINE_UART_SENDBREAK +#define MICROPY_PY_MACHINE_UART_SENDBREAK (0) +#endif + +// Whether to enable the UART.readchar() and UART.writechar() methods. +// Requires a port to implement mp_machine_uart_readchar() and mp_machine_uart_writechar(). +#ifndef MICROPY_PY_MACHINE_UART_READCHAR_WRITECHAR +#define MICROPY_PY_MACHINE_UART_READCHAR_WRITECHAR (0) +#endif + +// Whether to enable the UART.irq() method. +// Requires a port to implement mp_machine_uart_irq(). +#ifndef MICROPY_PY_MACHINE_UART_IRQ +#define MICROPY_PY_MACHINE_UART_IRQ (0) +#endif + +// Temporary support for legacy construction of SoftI2C via I2C type. +#define MP_MACHINE_I2C_CHECK_FOR_LEGACY_SOFTI2C_CONSTRUCTION(n_args, n_kw, all_args) \ + do { \ + if (n_args == 0 || all_args[0] == MP_OBJ_NEW_SMALL_INT(-1)) { \ + mp_print_str(MICROPY_ERROR_PRINTER, "Warning: I2C(-1, ...) is deprecated, use SoftI2C(...) instead\n"); \ + if (n_args != 0) { \ + --n_args; \ + ++all_args; \ + } \ + return MP_OBJ_TYPE_GET_SLOT(&mp_machine_soft_i2c_type, make_new)(&mp_machine_soft_i2c_type, n_args, n_kw, all_args); \ + } \ + } while (0) + +// Temporary support for legacy construction of SoftSPI via SPI type. +#define MP_MACHINE_SPI_CHECK_FOR_LEGACY_SOFTSPI_CONSTRUCTION(n_args, n_kw, all_args) \ + do { \ + if (n_args == 0 || all_args[0] == MP_OBJ_NEW_SMALL_INT(-1)) { \ + mp_print_str(MICROPY_ERROR_PRINTER, "Warning: SPI(-1, ...) is deprecated, use SoftSPI(...) instead\n"); \ + if (n_args != 0) { \ + --n_args; \ + ++all_args; \ + } \ + return MP_OBJ_TYPE_GET_SLOT(&mp_machine_soft_spi_type, make_new)(&mp_machine_soft_spi_type, n_args, n_kw, all_args); \ + } \ + } while (0) + +#if MICROPY_PY_MACHINE_I2C || MICROPY_PY_MACHINE_SOFTI2C + +#define MP_MACHINE_I2C_FLAG_READ (0x01) // if not set then it's a write +#define MP_MACHINE_I2C_FLAG_STOP (0x02) + +#if MICROPY_PY_MACHINE_I2C_TRANSFER_WRITE1 +// If set, the first mp_machine_i2c_buf_t in a transfer is a write. +#define MP_MACHINE_I2C_FLAG_WRITE1 (0x04) +#endif + +#endif + +// A port must provide these types, but they are otherwise opaque. +typedef struct _machine_adc_obj_t machine_adc_obj_t; +typedef struct _machine_adc_block_obj_t machine_adc_block_obj_t; +typedef struct _machine_i2s_obj_t machine_i2s_obj_t; +typedef struct _machine_pwm_obj_t machine_pwm_obj_t; +typedef struct _machine_uart_obj_t machine_uart_obj_t; +typedef struct _machine_wdt_obj_t machine_wdt_obj_t; + +typedef struct _machine_mem_obj_t { + mp_obj_base_t base; + unsigned elem_size; // in bytes +} machine_mem_obj_t; + +#if MICROPY_PY_MACHINE_I2C || MICROPY_PY_MACHINE_SOFTI2C + +typedef struct _mp_machine_i2c_buf_t { + size_t len; + uint8_t *buf; +} mp_machine_i2c_buf_t; + +// I2C protocol: +// - init must be non-NULL +// - start/stop/read/write can be NULL, meaning operation is not supported +// - transfer must be non-NULL +// - transfer_single only needs to be set if transfer=mp_machine_i2c_transfer_adaptor +typedef struct _mp_machine_i2c_p_t { + #if MICROPY_PY_MACHINE_I2C_TRANSFER_WRITE1 + bool transfer_supports_write1; + #endif + void (*init)(mp_obj_base_t *obj, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); + int (*start)(mp_obj_base_t *obj); + int (*stop)(mp_obj_base_t *obj); + int (*read)(mp_obj_base_t *obj, uint8_t *dest, size_t len, bool nack); + int (*write)(mp_obj_base_t *obj, const uint8_t *src, size_t len); + int (*transfer)(mp_obj_base_t *obj, uint16_t addr, size_t n, mp_machine_i2c_buf_t *bufs, unsigned int flags); + int (*transfer_single)(mp_obj_base_t *obj, uint16_t addr, size_t len, uint8_t *buf, unsigned int flags); +} mp_machine_i2c_p_t; + +// SoftI2C object. +typedef struct _mp_machine_soft_i2c_obj_t { + mp_obj_base_t base; + uint32_t us_delay; + uint32_t us_timeout; + mp_hal_pin_obj_t scl; + mp_hal_pin_obj_t sda; +} mp_machine_soft_i2c_obj_t; + +#endif + +#if MICROPY_PY_MACHINE_SPI || MICROPY_PY_MACHINE_SOFTSPI + +// SPI protocol. +typedef struct _mp_machine_spi_p_t { + void (*init)(mp_obj_base_t *obj, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); + void (*deinit)(mp_obj_base_t *obj); // can be NULL + void (*transfer)(mp_obj_base_t *obj, size_t len, const uint8_t *src, uint8_t *dest); +} mp_machine_spi_p_t; + +// SoftSPI object. +typedef struct _mp_machine_soft_spi_obj_t { + mp_obj_base_t base; + mp_soft_spi_obj_t spi; +} mp_machine_soft_spi_obj_t; + +#endif + +// Objects for machine.mem8, machine.mem16 and machine.mem32. +extern const machine_mem_obj_t machine_mem8_obj; +extern const machine_mem_obj_t machine_mem16_obj; +extern const machine_mem_obj_t machine_mem32_obj; + +// These classes correspond to machine.Type entries in the machine module. +// Their Python bindings are implemented in extmod, and their implementation +// is provided by a port. +extern const mp_obj_type_t machine_adc_type; +extern const mp_obj_type_t machine_adc_block_type; +extern const mp_obj_type_t machine_i2c_type; +extern const mp_obj_type_t machine_i2s_type; +extern const mp_obj_type_t machine_mem_type; +extern const mp_obj_type_t machine_pin_type; +extern const mp_obj_type_t machine_pinbase_type; +extern const mp_obj_type_t machine_pwm_type; +extern const mp_obj_type_t machine_rtc_type; +extern const mp_obj_type_t machine_signal_type; +extern const mp_obj_type_t machine_spi_type; +extern const mp_obj_type_t machine_timer_type; +extern const mp_obj_type_t machine_uart_type; +extern const mp_obj_type_t machine_wdt_type; + +#if MICROPY_PY_MACHINE_SOFTI2C +extern const mp_obj_type_t mp_machine_soft_i2c_type; +#endif +#if MICROPY_PY_MACHINE_I2C || MICROPY_PY_MACHINE_SOFTI2C +extern const mp_obj_dict_t mp_machine_i2c_locals_dict; +#endif + +#if MICROPY_PY_MACHINE_SOFTSPI +extern const mp_obj_type_t mp_machine_soft_spi_type; +extern const mp_machine_spi_p_t mp_machine_soft_spi_p; +#endif +#if MICROPY_PY_MACHINE_SPI || MICROPY_PY_MACHINE_SOFTSPI +extern const mp_obj_dict_t mp_machine_spi_locals_dict; +#endif + +#if defined(MICROPY_MACHINE_MEM_GET_READ_ADDR) +uintptr_t MICROPY_MACHINE_MEM_GET_READ_ADDR(mp_obj_t addr_o, uint align); +#endif +#if defined(MICROPY_MACHINE_MEM_GET_WRITE_ADDR) +uintptr_t MICROPY_MACHINE_MEM_GET_WRITE_ADDR(mp_obj_t addr_o, uint align); +#endif + +NORETURN mp_obj_t machine_bootloader(size_t n_args, const mp_obj_t *args); +void machine_bitstream_high_low(mp_hal_pin_obj_t pin, uint32_t *timing_ns, const uint8_t *buf, size_t len); +mp_uint_t machine_time_pulse_us(mp_hal_pin_obj_t pin, int pulse_level, mp_uint_t timeout_us); + +MP_DECLARE_CONST_FUN_OBJ_0(machine_unique_id_obj); +MP_DECLARE_CONST_FUN_OBJ_0(machine_reset_obj); +MP_DECLARE_CONST_FUN_OBJ_0(machine_reset_cause_obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(machine_freq_obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(machine_lightsleep_obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(machine_deepsleep_obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(machine_bootloader_obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(machine_bitstream_obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(machine_time_pulse_us_obj); + +#if MICROPY_PY_MACHINE_I2C +int mp_machine_i2c_transfer_adaptor(mp_obj_base_t *self, uint16_t addr, size_t n, mp_machine_i2c_buf_t *bufs, unsigned int flags); +int mp_machine_soft_i2c_transfer(mp_obj_base_t *self, uint16_t addr, size_t n, mp_machine_i2c_buf_t *bufs, unsigned int flags); +#endif + +#if MICROPY_PY_MACHINE_SPI +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); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_machine_spi_readinto_obj); +MP_DECLARE_CONST_FUN_OBJ_2(mp_machine_spi_write_obj); +MP_DECLARE_CONST_FUN_OBJ_3(mp_machine_spi_write_readinto_obj); +#endif + +#endif // MICROPY_PY_MACHINE + +#endif // MICROPY_INCLUDED_EXTMOD_MODMACHINE_H diff --git a/extmod/modos.c b/extmod/modos.c index 6df49d7f8497..5a088b520298 100644 --- a/extmod/modos.c +++ b/extmod/modos.c @@ -24,6 +24,7 @@ * THE SOFTWARE. */ +#include "py/mphal.h" #include "py/objstr.h" #include "py/runtime.h" @@ -48,6 +49,13 @@ #include "extmod/vfs_posix.h" #endif +#if MICROPY_MBFS +#if MICROPY_VFS +#error "MICROPY_MBFS requires MICROPY_VFS to be disabled" +#endif +#include "ports/nrf/modules/os/microbitfs.h" +#endif + #if MICROPY_PY_OS_UNAME #include "genhdr/mpversion.h" #endif @@ -121,6 +129,21 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_os_uname_obj, mp_os_uname); #endif +#if MICROPY_PY_OS_DUPTERM_NOTIFY +STATIC mp_obj_t mp_os_dupterm_notify(mp_obj_t obj_in) { + (void)obj_in; + for (;;) { + int c = mp_os_dupterm_rx_chr(); + if (c < 0) { + break; + } + ringbuf_put(&stdin_ringbuf, c); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_os_dupterm_notify_obj, mp_os_dupterm_notify); +#endif + STATIC const mp_rom_map_elem_t os_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_os) }, @@ -187,6 +210,14 @@ STATIC const mp_rom_map_elem_t os_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_VfsPosix), MP_ROM_PTR(&mp_type_vfs_posix) }, #endif #endif + + #if MICROPY_MBFS + // For special micro:bit filesystem only. + { MP_ROM_QSTR(MP_QSTR_listdir), MP_ROM_PTR(&os_mbfs_listdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&os_mbfs_ilistdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&os_mbfs_stat_obj) }, + { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&os_mbfs_remove_obj) }, + #endif }; STATIC MP_DEFINE_CONST_DICT(os_module_globals, os_module_globals_table); diff --git a/extmod/modre.c b/extmod/modre.c index 467371b73065..d6ea1b65b7c7 100644 --- a/extmod/modre.c +++ b/extmod/modre.c @@ -238,12 +238,12 @@ STATIC mp_obj_t re_exec(bool is_anchored, uint n_args, const mp_obj_t *args) { } #endif int caps_num = (self->re.sub + 1) * 2; - mp_obj_match_t *match = m_new_obj_var(mp_obj_match_t, char *, caps_num); + mp_obj_match_t *match = m_new_obj_var(mp_obj_match_t, caps, char *, caps_num); // 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 *)match->caps, 0, caps_num * sizeof(char *)); int res = re1_5_recursiveloopprog(&self->re, &subj, match->caps, caps_num, is_anchored); if (res == 0) { - m_del_var(mp_obj_match_t, char *, caps_num, match); + m_del_var(mp_obj_match_t, caps, char *, caps_num, match); return mp_const_none; } diff --git a/extmod/modselect.c b/extmod/modselect.c index 63797e0b8952..7d86de3cfac3 100644 --- a/extmod/modselect.c +++ b/extmod/modselect.c @@ -32,6 +32,7 @@ #include "py/stream.h" #include "py/mperrno.h" #include "py/mphal.h" +// CIRCUITPY-CHANGE #include "shared/runtime/interrupt_char.h" #if MICROPY_PY_SELECT @@ -42,6 +43,7 @@ #if MICROPY_PY_SELECT_POSIX_OPTIMISATIONS +#include #include #if !((MP_STREAM_POLL_RD) == (POLLIN) && \ @@ -143,14 +145,47 @@ STATIC void poll_obj_set_revents(poll_obj_t *poll_obj, mp_uint_t revents) { } } +// How much (in pollfds) to grow the allocation for poll_set->pollfds by. +#define POLL_SET_ALLOC_INCREMENT (4) + STATIC struct pollfd *poll_set_add_fd(poll_set_t *poll_set, int fd) { struct pollfd *free_slot = NULL; if (poll_set->used == poll_set->max_used) { // No free slots below max_used, so expand max_used (and possibly allocate). if (poll_set->max_used >= poll_set->alloc) { - poll_set->pollfds = m_renew(struct pollfd, poll_set->pollfds, poll_set->alloc, poll_set->alloc + 4); - poll_set->alloc += 4; + size_t new_alloc = poll_set->alloc + POLL_SET_ALLOC_INCREMENT; + // Try to grow in-place. + struct pollfd *new_fds = m_renew_maybe(struct pollfd, poll_set->pollfds, poll_set->alloc, new_alloc, false); + if (!new_fds) { + // Failed to grow in-place. Do a new allocation and copy over the pollfd values. + new_fds = m_new(struct pollfd, new_alloc); + memcpy(new_fds, poll_set->pollfds, sizeof(struct pollfd) * poll_set->alloc); + + // Update existing poll_obj_t to update their pollfd field to + // point to the same offset inside the new allocation. + for (mp_uint_t i = 0; i < poll_set->map.alloc; ++i) { + if (!mp_map_slot_is_filled(&poll_set->map, i)) { + continue; + } + + poll_obj_t *poll_obj = MP_OBJ_TO_PTR(poll_set->map.table[i].value); + if (!poll_obj) { + // This is the one we're currently adding, + // poll_set_add_obj doesn't assign elem->value until + // afterwards. + continue; + } + + poll_obj->pollfd = new_fds + (poll_obj->pollfd - poll_set->pollfds); + } + + // Delete the old allocation. + m_del(struct pollfd, poll_set->pollfds, poll_set->alloc); + } + + poll_set->pollfds = new_fds; + poll_set->alloc = new_alloc; } free_slot = &poll_set->pollfds[poll_set->max_used++]; } else { @@ -307,6 +342,7 @@ STATIC mp_uint_t poll_set_poll_once(poll_set_t *poll_set, size_t *rwx_num) { STATIC mp_uint_t poll_set_poll_until_ready_or_timeout(poll_set_t *poll_set, size_t *rwx_num, mp_uint_t timeout) { mp_uint_t start_ticks = mp_hal_ticks_ms(); + bool has_timeout = timeout != (mp_uint_t)-1; #if MICROPY_PY_SELECT_POSIX_OPTIMISATIONS @@ -351,12 +387,12 @@ STATIC mp_uint_t poll_set_poll_until_ready_or_timeout(poll_set_t *poll_set, size } // Return if an object is ready, or if the timeout expired. - if (n_ready > 0 || (timeout != (mp_uint_t)-1 && mp_hal_ticks_ms() - start_ticks >= timeout)) { + if (n_ready > 0 || (has_timeout && mp_hal_ticks_ms() - start_ticks >= timeout)) { return n_ready; } - // This would be MICROPY_EVENT_POLL_HOOK but the call to poll() above already includes a delay. - mp_handle_pending(true); + // This would be mp_event_wait_ms() but the call to poll() above already includes a delay. + mp_event_handle_nowait(); } #else @@ -364,17 +400,20 @@ STATIC mp_uint_t poll_set_poll_until_ready_or_timeout(poll_set_t *poll_set, size for (;;) { // poll the objects mp_uint_t n_ready = poll_set_poll_once(poll_set, rwx_num); - if (n_ready > 0 || (timeout != (mp_uint_t)-1 && mp_hal_ticks_ms() - start_ticks >= timeout)) { + uint32_t elapsed = mp_hal_ticks_ms() - start_ticks; + if (n_ready > 0 || (has_timeout && elapsed >= timeout)) { return n_ready; } - // CIRCUITPY-CHANGE - RUN_BACKGROUND_TASKS; + // CIRCUITPY-CHANGE: check for ctrl-C interrupt if (mp_hal_is_interrupted()) { return 0; } - #ifdef MICROPY_EVENT_POLL_HOOK - MICROPY_EVENT_POLL_HOOK; - #endif + // CIRCUITPY-CHANGE: mp_event_wait_ms() and mp_event_wait_indefinite() will do RUN_BACKGROUND_TASKS + if (has_timeout) { + mp_event_wait_ms(timeout - elapsed); + } else { + mp_event_wait_indefinite(); + } } #endif diff --git a/extmod/moductypes.c b/extmod/moductypes.c index 63be621fbcbe..f56567107df7 100644 --- a/extmod/moductypes.c +++ b/extmod/moductypes.c @@ -96,7 +96,7 @@ STATIC NORETURN void syntax_error(void) { STATIC mp_obj_t uctypes_struct_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); mp_obj_uctypes_struct_t *o = mp_obj_malloc(mp_obj_uctypes_struct_t, type); - o->addr = (void *)(uintptr_t)mp_obj_int_get_truncated(args[0]); + o->addr = (void *)(uintptr_t)mp_obj_get_int_truncated(args[0]); o->desc = args[1]; o->flags = LAYOUT_NATIVE; if (n_args == 3) { diff --git a/extmod/vfs.c b/extmod/vfs.c index f7973b6cc5f0..21dbac5c2bfc 100644 --- a/extmod/vfs.c +++ b/extmod/vfs.c @@ -42,6 +42,7 @@ #include "extmod/vfs_lfs.h" #endif +// CIRCUITPY-CHANGE: handle undefined without a warning #if defined(MICROPY_VFS_POSIX) && MICROPY_VFS_POSIX #include "extmod/vfs_posix.h" #endif @@ -310,6 +311,7 @@ mp_obj_t mp_vfs_open(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + // CIRCUITPY-CHANGE: handle undefined without a warning #if defined(MICROPY_VFS_POSIX) && MICROPY_VFS_POSIX // If the file is an integer then delegate straight to the POSIX handler if (mp_obj_is_small_int(args[ARG_file].u_obj)) { @@ -434,6 +436,7 @@ mp_obj_t mp_vfs_listdir(size_t n_args, const mp_obj_t *args) { MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_vfs_listdir_obj, 0, 1, mp_vfs_listdir); mp_obj_t mp_vfs_mkdir(mp_obj_t path_in) { + // CIRCUITPY-CHANGE: initialize mp_obj_t path_out = mp_const_none; mp_vfs_mount_t *vfs = lookup_path(path_in, &path_out); if (vfs == MP_VFS_ROOT || (vfs != MP_VFS_NONE && !strcmp(mp_obj_str_get_str(path_out), "/"))) { diff --git a/extmod/vfs.h b/extmod/vfs.h index 4e6af208e4d0..c6c471d476c0 100644 --- a/extmod/vfs.h +++ b/extmod/vfs.h @@ -28,6 +28,7 @@ #include "py/builtin.h" #include "py/obj.h" +// CIRCUITPY-CHANGE #include "py/proto.h" // return values of mp_vfs_lookup_path @@ -44,6 +45,7 @@ #define MP_BLOCKDEV_FLAG_FREE_OBJ (0x0002) // fs_user_mount_t obj should be freed on umount #define MP_BLOCKDEV_FLAG_HAVE_IOCTL (0x0004) // new protocol with ioctl #define MP_BLOCKDEV_FLAG_NO_FILESYSTEM (0x0008) // the block device has no filesystem on it +// CIRCUITPY-CHANGE: additional flags // Device is writable over USB and read-only to MicroPython. #define MP_BLOCKDEV_FLAG_USB_WRITABLE (0x0010) // Bit set when the above flag is checked before opening a file for write. @@ -59,9 +61,9 @@ #define MP_BLOCKDEV_IOCTL_BLOCK_SIZE (5) #define MP_BLOCKDEV_IOCTL_BLOCK_ERASE (6) - // At the moment the VFS protocol just has import_stat, but could be extended to other methods typedef struct _mp_vfs_proto_t { + // CIRCUITPY-CHANGE MP_PROTOCOL_HEAD mp_import_stat_t (*import_stat)(void *self, const char *path); } mp_vfs_proto_t; @@ -88,6 +90,7 @@ typedef struct _mp_vfs_mount_t { struct _mp_vfs_mount_t *next; } mp_vfs_mount_t; +// CIRCUITPY-CHANGE: allow outside use of ilistdir_it_iternext typedef struct _mp_vfs_ilistdir_it_t { mp_obj_base_t base; mp_fun_1_t iternext; @@ -124,7 +127,6 @@ mp_obj_t mp_vfs_stat(mp_obj_t path_in); mp_obj_t mp_vfs_statvfs(mp_obj_t path_in); int mp_vfs_mount_and_chdir_protected(mp_obj_t bdev, mp_obj_t mount_point); -mp_obj_t mp_vfs_ilistdir_it_iternext(mp_obj_t self_in); MP_DECLARE_CONST_FUN_OBJ_KW(mp_vfs_mount_obj); MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_umount_obj); diff --git a/extmod/vfs_blockdev.c b/extmod/vfs_blockdev.c index 796f13517416..eda5cecc147a 100644 --- a/extmod/vfs_blockdev.c +++ b/extmod/vfs_blockdev.c @@ -30,6 +30,7 @@ #include "py/mperrno.h" #include "extmod/vfs.h" +// CIRCUITPY-CHANGE #if CIRCUITPY_SDCARDIO #include "shared-bindings/sdcardio/SDCard.h" #endif diff --git a/extmod/vfs_fat.c b/extmod/vfs_fat.c index 27d8187e718d..ee0b8eb735bd 100644 --- a/extmod/vfs_fat.c +++ b/extmod/vfs_fat.c @@ -36,6 +36,7 @@ #error "with MICROPY_VFS_FAT enabled, must also enable MICROPY_VFS" #endif +// CIRCUITPY-CHANGE: extra includes #include #include "py/obj.h" #include "py/objproperty.h" @@ -54,6 +55,7 @@ #define mp_obj_fat_vfs_t fs_user_mount_t +// CIRCUITPY-CHANGE // Factoring this common call saves about 90 bytes. STATIC NORETURN void mp_raise_OSError_fresult(FRESULT res) { mp_raise_OSError(fresult_to_errno_table[res]); @@ -92,12 +94,14 @@ STATIC mp_obj_t fat_vfs_make_new(const mp_obj_type_t *type, size_t n_args, size_ // don't error out if no filesystem, to let mkfs()/mount() create one if wanted vfs->blockdev.flags |= MP_BLOCKDEV_FLAG_NO_FILESYSTEM; } else if (res != FR_OK) { + // CIRCUITPY-CHANGE mp_raise_OSError_fresult(res); } return MP_OBJ_FROM_PTR(vfs); } +// CIRCUITPY-CHANGE STATIC void verify_fs_writable(fs_user_mount_t *vfs) { if (!filesystem_is_writable_by_python(vfs)) { mp_raise_OSError(MP_EROFS); @@ -125,6 +129,7 @@ STATIC mp_obj_t fat_vfs_mkfs(mp_obj_t bdev_in) { res = f_mkfs(&vfs->fatfs, FM_FAT32, 0, working_buf, sizeof(working_buf)); } if (res != FR_OK) { + // CIRCUITPY-CHANGE mp_raise_OSError_fresult(res); } @@ -209,6 +214,7 @@ STATIC mp_obj_t fat_vfs_ilistdir_func(size_t n_args, const mp_obj_t *args) { iter->is_str = is_str_type; FRESULT res = f_opendir(&self->fatfs, &iter->dir, path); if (res != FR_OK) { + // CIRCUITPY-CHANGE mp_raise_OSError_fresult(res); } @@ -218,6 +224,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fat_vfs_ilistdir_obj, 1, 2, fat_vfs_i STATIC mp_obj_t fat_vfs_remove_internal(mp_obj_t vfs_in, mp_obj_t path_in, mp_int_t attr) { mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in); + // CIRCUITPY-CHANGE verify_fs_writable(self); const char *path = mp_obj_str_get_str(path_in); @@ -225,6 +232,7 @@ STATIC mp_obj_t fat_vfs_remove_internal(mp_obj_t vfs_in, mp_obj_t path_in, mp_in FRESULT res = f_stat(&self->fatfs, path, &fno); if (res != FR_OK) { + // CIRCUITPY-CHANGE mp_raise_OSError_fresult(res); } @@ -233,6 +241,7 @@ STATIC mp_obj_t fat_vfs_remove_internal(mp_obj_t vfs_in, mp_obj_t path_in, mp_in res = f_unlink(&self->fatfs, path); if (res != FR_OK) { + // CIRCUITPY-CHANGE mp_raise_OSError_fresult(res); } return mp_const_none; @@ -253,10 +262,10 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(fat_vfs_rmdir_obj, fat_vfs_rmdir); STATIC mp_obj_t fat_vfs_rename(mp_obj_t vfs_in, mp_obj_t path_in, mp_obj_t path_out) { mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in); + // CIRCUITPY-CHANGE verify_fs_writable(self); const char *old_path = mp_obj_str_get_str(path_in); const char *new_path = mp_obj_str_get_str(path_out); - FRESULT res = f_rename(&self->fatfs, old_path, new_path); if (res == FR_EXIST) { // if new_path exists then try removing it (but only if it's a file) @@ -267,6 +276,7 @@ STATIC mp_obj_t fat_vfs_rename(mp_obj_t vfs_in, mp_obj_t path_in, mp_obj_t path_ if (res == FR_OK) { return mp_const_none; } else { + // CIRCUITPY-CHANGE mp_raise_OSError_fresult(res); } @@ -281,6 +291,7 @@ STATIC mp_obj_t fat_vfs_mkdir(mp_obj_t vfs_in, mp_obj_t path_o) { if (res == FR_OK) { return mp_const_none; } else { + // CIRCUITPY-CHANGE mp_raise_OSError_fresult(res); } } @@ -295,6 +306,7 @@ STATIC mp_obj_t fat_vfs_chdir(mp_obj_t vfs_in, mp_obj_t path_in) { FRESULT res = f_chdir(&self->fatfs, path); if (res != FR_OK) { + // CIRCUITPY-CHANGE mp_raise_OSError_fresult(res); } @@ -308,6 +320,7 @@ STATIC mp_obj_t fat_vfs_getcwd(mp_obj_t vfs_in) { char buf[MICROPY_ALLOC_PATH_MAX + 1]; FRESULT res = f_getcwd(&self->fatfs, buf, sizeof(buf)); if (res != FR_OK) { + // CIRCUITPY-CHANGE mp_raise_OSError_fresult(res); } return mp_obj_new_str(buf, strlen(buf)); @@ -329,6 +342,7 @@ STATIC mp_obj_t fat_vfs_stat(mp_obj_t vfs_in, mp_obj_t path_in) { } else { FRESULT res = f_stat(&self->fatfs, path, &fno); if (res != FR_OK) { + // CIRCUITPY-CHANGE mp_raise_OSError_fresult(res); } } @@ -340,12 +354,13 @@ STATIC mp_obj_t fat_vfs_stat(mp_obj_t vfs_in, mp_obj_t path_in) { } else { mode |= MP_S_IFREG; } + // CIRCUITPY-CHANGE #if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_NONE // On non-longint builds, the number of seconds since 1970 (epoch) is too // large to fit in a smallint, so just return 31-DEC-1999 (0). - mp_obj_t seconds = MP_OBJ_NEW_SMALL_INT(946684800); + mp_obj_t seconds_obj = MP_OBJ_NEW_SMALL_INT(946684800); #else - mp_obj_t seconds = mp_obj_new_int_from_uint( + mp_obj_t seconds_obj = mp_obj_new_int_from_uint( timeutils_seconds_since_epoch( 1980 + ((fno.fdate >> 9) & 0x7f), (fno.fdate >> 5) & 0x0f, @@ -362,9 +377,10 @@ STATIC mp_obj_t fat_vfs_stat(mp_obj_t vfs_in, mp_obj_t path_in) { t->items[4] = MP_OBJ_NEW_SMALL_INT(0); // st_uid t->items[5] = MP_OBJ_NEW_SMALL_INT(0); // st_gid t->items[6] = mp_obj_new_int_from_uint(fno.fsize); // st_size - t->items[7] = seconds; // st_atime - t->items[8] = seconds; // st_mtime - t->items[9] = seconds; // st_ctime + // CIRCUITPY-CHANGE: already converted to obj + t->items[7] = seconds_obj; // st_atime + t->items[8] = seconds_obj; // st_mtime + t->items[9] = seconds_obj; // st_ctime return MP_OBJ_FROM_PTR(t); } @@ -379,6 +395,7 @@ STATIC mp_obj_t fat_vfs_statvfs(mp_obj_t vfs_in, mp_obj_t path_in) { FATFS *fatfs = &self->fatfs; FRESULT res = f_getfree(fatfs, &nclst); if (FR_OK != res) { + // CIRCUITPY-CHANGE mp_raise_OSError_fresult(res); } @@ -417,6 +434,7 @@ STATIC mp_obj_t vfs_fat_mount(mp_obj_t self_in, mp_obj_t readonly, mp_obj_t mkfs res = f_mkfs(&self->fatfs, FM_FAT | FM_SFD, 0, working_buf, sizeof(working_buf)); } if (res != FR_OK) { + // CIRCUITPY-CHANGE mp_raise_OSError_fresult(res); } self->blockdev.flags &= ~MP_BLOCKDEV_FLAG_NO_FILESYSTEM; @@ -432,6 +450,7 @@ STATIC mp_obj_t vfs_fat_umount(mp_obj_t self_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_umount_obj, vfs_fat_umount); +// CIRCUITPY-CHANGE STATIC mp_obj_t vfs_fat_utime(mp_obj_t vfs_in, mp_obj_t path_in, mp_obj_t times_in) { mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in); const char *path = mp_obj_str_get_str(path_in); @@ -524,6 +543,7 @@ STATIC const mp_rom_map_elem_t fat_vfs_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&fat_vfs_statvfs_obj) }, { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&vfs_fat_mount_obj) }, { MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&fat_vfs_umount_obj) }, + // CIRCUITPY-CHANGE: added { MP_ROM_QSTR(MP_QSTR_utime), MP_ROM_PTR(&fat_vfs_utime_obj) }, { MP_ROM_QSTR(MP_QSTR_readonly), MP_ROM_PTR(&fat_vfs_readonly_obj) }, #if MICROPY_FATFS_USE_LABEL @@ -533,6 +553,7 @@ STATIC const mp_rom_map_elem_t fat_vfs_locals_dict_table[] = { STATIC MP_DEFINE_CONST_DICT(fat_vfs_locals_dict, fat_vfs_locals_dict_table); STATIC const mp_vfs_proto_t fat_vfs_proto = { + // CIRCUITPY-CHANGE MP_PROTO_IMPLEMENT(MP_QSTR_protocol_vfs) .import_stat = fat_vfs_import_stat, }; @@ -540,6 +561,7 @@ STATIC const mp_vfs_proto_t fat_vfs_proto = { MP_DEFINE_CONST_OBJ_TYPE( mp_fat_vfs_type, MP_QSTR_VfsFat, + // CIRCUITPY-CHANGE: has properties MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, make_new, fat_vfs_make_new, protocol, &fat_vfs_proto, diff --git a/extmod/vfs_fat.h b/extmod/vfs_fat.h index 68a882a95205..b25950100e67 100644 --- a/extmod/vfs_fat.h +++ b/extmod/vfs_fat.h @@ -47,6 +47,7 @@ extern const mp_obj_type_t mp_type_vfs_fat_textio; MP_DECLARE_CONST_FUN_OBJ_3(fat_vfs_open_obj); +// CIRCUITPY-CHANGE typedef struct _pyb_file_obj_t { mp_obj_base_t base; FIL fp; diff --git a/extmod/vfs_fat_file.c b/extmod/vfs_fat_file.c index b9c89c46f365..74dabce99eb2 100644 --- a/extmod/vfs_fat_file.c +++ b/extmod/vfs_fat_file.c @@ -27,6 +27,7 @@ #include "py/mpconfig.h" #if MICROPY_VFS && MICROPY_VFS_FAT +// CIRCUITPY-CHANGE: more includes #include #include @@ -63,6 +64,7 @@ const byte fresult_to_errno_table[20] = { STATIC void file_obj_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { (void)kind; + // CIRCUITPY-CHANGE mp_printf(print, "", mp_obj_get_type_qstr(self_in), MP_OBJ_TO_PTR(self_in)); } @@ -198,6 +200,7 @@ STATIC mp_obj_t fat_vfs_open(mp_obj_t self_in, mp_obj_t path_in, mp_obj_t mode_i const mp_obj_type_t *type = &mp_type_vfs_fat_textio; int mode = 0; const char *mode_s = mp_obj_str_get_str(mode_in); + // CIRCUITPY-CHANGE: validate mode uint32_t rwxa_count = 0; uint32_t bt_count = 0; uint32_t plus_count = 0; diff --git a/extmod/vfs_lfsx.c b/extmod/vfs_lfsx.c index fe0731eced2a..e78c154d1fe8 100644 --- a/extmod/vfs_lfsx.c +++ b/extmod/vfs_lfsx.c @@ -99,7 +99,7 @@ STATIC void MP_VFS_LFSx(init_config)(MP_OBJ_VFS_LFSx * self, mp_obj_t bdev, size config->lookahead_buffer = m_new(uint8_t, config->lookahead / 8); #else config->block_cycles = 100; - config->cache_size = 4 * MAX(read_size, prog_size); + config->cache_size = MIN(config->block_size, (4 * MAX(read_size, prog_size))); config->lookahead_size = lookahead; config->read_buffer = m_new(uint8_t, config->cache_size); config->prog_buffer = m_new(uint8_t, config->cache_size); diff --git a/extmod/vfs_lfsx_file.c b/extmod/vfs_lfsx_file.c index 879ba78973db..b9f332339733 100644 --- a/extmod/vfs_lfsx_file.c +++ b/extmod/vfs_lfsx_file.c @@ -90,9 +90,9 @@ mp_obj_t MP_VFS_LFSx(file_open)(mp_obj_t self_in, mp_obj_t path_in, mp_obj_t mod } #if LFS_BUILD_VERSION == 1 - MP_OBJ_VFS_LFSx_FILE *o = m_new_obj_var_with_finaliser(MP_OBJ_VFS_LFSx_FILE, uint8_t, self->lfs.cfg->prog_size); + MP_OBJ_VFS_LFSx_FILE *o = m_new_obj_var_with_finaliser(MP_OBJ_VFS_LFSx_FILE, file_buffer, uint8_t, self->lfs.cfg->prog_size); #else - MP_OBJ_VFS_LFSx_FILE *o = m_new_obj_var_with_finaliser(MP_OBJ_VFS_LFSx_FILE, uint8_t, self->lfs.cfg->cache_size); + MP_OBJ_VFS_LFSx_FILE *o = m_new_obj_var_with_finaliser(MP_OBJ_VFS_LFSx_FILE, file_buffer, uint8_t, self->lfs.cfg->cache_size); #endif o->base.type = type; o->vfs = self; diff --git a/extmod/vfs_posix.c b/extmod/vfs_posix.c index d63bb5be7bff..e29b47cccda8 100644 --- a/extmod/vfs_posix.c +++ b/extmod/vfs_posix.c @@ -46,6 +46,9 @@ #ifdef _MSC_VER #include // For mkdir etc. #endif +#ifdef _WIN32 +#include +#endif typedef struct _mp_obj_vfs_posix_t { mp_obj_base_t base; @@ -55,21 +58,23 @@ typedef struct _mp_obj_vfs_posix_t { } mp_obj_vfs_posix_t; STATIC const char *vfs_posix_get_path_str(mp_obj_vfs_posix_t *self, mp_obj_t path) { - if (self->root_len == 0) { - return mp_obj_str_get_str(path); + const char *path_str = mp_obj_str_get_str(path); + if (self->root_len == 0 || path_str[0] != '/') { + return path_str; } else { - self->root.len = self->root_len; - vstr_add_str(&self->root, mp_obj_str_get_str(path)); + self->root.len = self->root_len - 1; + vstr_add_str(&self->root, path_str); return vstr_null_terminated_str(&self->root); } } STATIC mp_obj_t vfs_posix_get_path_obj(mp_obj_vfs_posix_t *self, mp_obj_t path) { - if (self->root_len == 0) { + const char *path_str = mp_obj_str_get_str(path); + if (self->root_len == 0 || path_str[0] != '/') { return path; } else { - self->root.len = self->root_len; - vstr_add_str(&self->root, mp_obj_str_get_str(path)); + self->root.len = self->root_len - 1; + vstr_add_str(&self->root, path_str); return mp_obj_new_str(self->root.buf, self->root.len); } } @@ -107,7 +112,28 @@ STATIC mp_obj_t vfs_posix_make_new(const mp_obj_type_t *type, size_t n_args, siz mp_obj_vfs_posix_t *vfs = mp_obj_malloc(mp_obj_vfs_posix_t, type); vstr_init(&vfs->root, 0); if (n_args == 1) { - vstr_add_str(&vfs->root, mp_obj_str_get_str(args[0])); + const char *root = mp_obj_str_get_str(args[0]); + // if the root is relative, make it absolute, otherwise we'll get confused by chdir + #ifdef _WIN32 + char buf[MICROPY_ALLOC_PATH_MAX + 1]; + DWORD result = GetFullPathNameA(root, sizeof(buf), buf, NULL); + if (result > 0 && result < sizeof(buf)) { + vstr_add_str(&vfs->root, buf); + } else { + mp_raise_OSError(GetLastError()); + } + #else + if (root[0] != '\0' && root[0] != '/') { + char buf[MICROPY_ALLOC_PATH_MAX + 1]; + const char *cwd = getcwd(buf, sizeof(buf)); + if (cwd == NULL) { + mp_raise_OSError(errno); + } + vstr_add_str(&vfs->root, cwd); + vstr_add_char(&vfs->root, '/'); + } + vstr_add_str(&vfs->root, root); + #endif vstr_add_char(&vfs->root, '/'); } vfs->root_len = vfs->root.len; @@ -160,7 +186,14 @@ STATIC mp_obj_t vfs_posix_getcwd(mp_obj_t self_in) { if (ret == NULL) { mp_raise_OSError(errno); } - ret += self->root_len; + if (self->root_len > 0) { + ret += self->root_len - 1; + #ifdef _WIN32 + if (*ret == '\\') { + *(char *)ret = '/'; + } + #endif + } return mp_obj_new_str(ret, strlen(ret)); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(vfs_posix_getcwd_obj, vfs_posix_getcwd); diff --git a/extmod/vfs_posix.h b/extmod/vfs_posix.h index 32299b8269f0..00756b4c9d6f 100644 --- a/extmod/vfs_posix.h +++ b/extmod/vfs_posix.h @@ -27,7 +27,6 @@ #define MICROPY_INCLUDED_EXTMOD_VFS_POSIX_H #include "py/lexer.h" -#include "py/mpconfig.h" #include "py/obj.h" extern const mp_obj_type_t mp_type_vfs_posix; diff --git a/extmod/vfs_posix_file.c b/extmod/vfs_posix_file.c index 81a608d2b66c..2f7078954391 100644 --- a/extmod/vfs_posix_file.c +++ b/extmod/vfs_posix_file.c @@ -279,14 +279,14 @@ STATIC const mp_stream_p_t vfs_posix_textio_stream_p = { #if MICROPY_PY_SYS_STDIO_BUFFER -const mp_obj_vfs_posix_file_t mp_sys_stdin_buffer_obj = {{&mp_type_vfs_posix_fileio}, STDIN_FILENO}; -const mp_obj_vfs_posix_file_t mp_sys_stdout_buffer_obj = {{&mp_type_vfs_posix_fileio}, STDOUT_FILENO}; -const mp_obj_vfs_posix_file_t mp_sys_stderr_buffer_obj = {{&mp_type_vfs_posix_fileio}, STDERR_FILENO}; +mp_obj_vfs_posix_file_t mp_sys_stdin_buffer_obj = {{&mp_type_vfs_posix_fileio}, STDIN_FILENO}; +mp_obj_vfs_posix_file_t mp_sys_stdout_buffer_obj = {{&mp_type_vfs_posix_fileio}, STDOUT_FILENO}; +mp_obj_vfs_posix_file_t mp_sys_stderr_buffer_obj = {{&mp_type_vfs_posix_fileio}, STDERR_FILENO}; // Forward declarations. -const mp_obj_vfs_posix_file_t mp_sys_stdin_obj; -const mp_obj_vfs_posix_file_t mp_sys_stdout_obj; -const mp_obj_vfs_posix_file_t mp_sys_stderr_obj; +mp_obj_vfs_posix_file_t mp_sys_stdin_obj; +mp_obj_vfs_posix_file_t mp_sys_stdout_obj; +mp_obj_vfs_posix_file_t mp_sys_stderr_obj; STATIC void vfs_posix_textio_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { if (dest[0] != MP_OBJ_NULL) { @@ -332,8 +332,8 @@ MP_DEFINE_CONST_OBJ_TYPE( locals_dict, &vfs_posix_rawfile_locals_dict ); -const mp_obj_vfs_posix_file_t mp_sys_stdin_obj = {{&mp_type_vfs_posix_textio}, STDIN_FILENO}; -const mp_obj_vfs_posix_file_t mp_sys_stdout_obj = {{&mp_type_vfs_posix_textio}, STDOUT_FILENO}; -const mp_obj_vfs_posix_file_t mp_sys_stderr_obj = {{&mp_type_vfs_posix_textio}, STDERR_FILENO}; +mp_obj_vfs_posix_file_t mp_sys_stdin_obj = {{&mp_type_vfs_posix_textio}, STDIN_FILENO}; +mp_obj_vfs_posix_file_t mp_sys_stdout_obj = {{&mp_type_vfs_posix_textio}, STDOUT_FILENO}; +mp_obj_vfs_posix_file_t mp_sys_stderr_obj = {{&mp_type_vfs_posix_textio}, STDERR_FILENO}; #endif // MICROPY_VFS_POSIX diff --git a/extmod/vfs_reader.c b/extmod/vfs_reader.c index d3904c5c5069..13fc31bee508 100644 --- a/extmod/vfs_reader.c +++ b/extmod/vfs_reader.c @@ -26,6 +26,7 @@ #include #include +#include #include "py/runtime.h" #include "py/stream.h" @@ -34,33 +35,39 @@ #if MICROPY_READER_VFS +#ifndef MICROPY_READER_VFS_DEFAULT_BUFFER_SIZE +#define MICROPY_READER_VFS_DEFAULT_BUFFER_SIZE (2 * MICROPY_BYTES_PER_GC_BLOCK - offsetof(mp_reader_vfs_t, buf)) +#endif +#define MICROPY_READER_VFS_MIN_BUFFER_SIZE (MICROPY_BYTES_PER_GC_BLOCK - offsetof(mp_reader_vfs_t, buf)) +#define MICROPY_READER_VFS_MAX_BUFFER_SIZE (255) + typedef struct _mp_reader_vfs_t { mp_obj_t file; - uint16_t len; - uint16_t pos; - byte buf[24]; + uint8_t bufpos; + uint8_t buflen; + uint8_t bufsize; + byte buf[]; } mp_reader_vfs_t; STATIC mp_uint_t mp_reader_vfs_readbyte(void *data) { mp_reader_vfs_t *reader = (mp_reader_vfs_t *)data; - if (reader->pos >= reader->len) { - if (reader->len < sizeof(reader->buf)) { + if (reader->bufpos >= reader->buflen) { + if (reader->buflen < reader->bufsize) { return MP_READER_EOF; } else { int errcode; - reader->len = mp_stream_rw(reader->file, reader->buf, sizeof(reader->buf), - &errcode, MP_STREAM_RW_READ | MP_STREAM_RW_ONCE); + reader->buflen = mp_stream_rw(reader->file, reader->buf, reader->bufsize, &errcode, MP_STREAM_RW_READ | MP_STREAM_RW_ONCE); if (errcode != 0) { // TODO handle errors properly return MP_READER_EOF; } - if (reader->len == 0) { + if (reader->buflen == 0) { return MP_READER_EOF; } - reader->pos = 0; + reader->bufpos = 0; } } - return reader->buf[reader->pos++]; + return reader->buf[reader->bufpos++]; } STATIC void mp_reader_vfs_close(void *data) { @@ -69,19 +76,32 @@ STATIC void mp_reader_vfs_close(void *data) { m_del_obj(mp_reader_vfs_t, reader); } -void mp_reader_new_file(mp_reader_t *reader, const char *filename) { - mp_reader_vfs_t *rf = m_new_obj(mp_reader_vfs_t); +void mp_reader_new_file(mp_reader_t *reader, qstr filename) { mp_obj_t args[2] = { - mp_obj_new_str(filename, strlen(filename)), + MP_OBJ_NEW_QSTR(filename), MP_OBJ_NEW_QSTR(MP_QSTR_rb), }; - rf->file = mp_vfs_open(MP_ARRAY_SIZE(args), &args[0], (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); + mp_obj_t file = mp_vfs_open(MP_ARRAY_SIZE(args), &args[0], (mp_map_t *)&mp_const_empty_map); + + const mp_stream_p_t *stream_p = mp_get_stream(file); + int errcode = 0; + mp_uint_t bufsize = stream_p->ioctl(file, MP_STREAM_GET_BUFFER_SIZE, 0, &errcode); + if (bufsize == MP_STREAM_ERROR || bufsize == 0) { + // bufsize == 0 is included here to support mpremote v1.21 and older where mount file ioctl + // returned 0 by default. + bufsize = MICROPY_READER_VFS_DEFAULT_BUFFER_SIZE; + } else { + bufsize = MIN(MICROPY_READER_VFS_MAX_BUFFER_SIZE, MAX(MICROPY_READER_VFS_MIN_BUFFER_SIZE, bufsize)); + } + + mp_reader_vfs_t *rf = m_new_obj_var(mp_reader_vfs_t, buf, byte, bufsize); + rf->file = file; + rf->bufsize = bufsize; + rf->buflen = mp_stream_rw(rf->file, rf->buf, rf->bufsize, &errcode, MP_STREAM_RW_READ | MP_STREAM_RW_ONCE); if (errcode != 0) { mp_raise_OSError(errcode); } - rf->pos = 0; + rf->bufpos = 0; reader->data = rf; reader->readbyte = mp_reader_vfs_readbyte; reader->close = mp_reader_vfs_close; diff --git a/extmod/virtpin.h b/extmod/virtpin.h index 591249e48d67..81094f21cf16 100644 --- a/extmod/virtpin.h +++ b/extmod/virtpin.h @@ -27,6 +27,7 @@ #define MICROPY_INCLUDED_EXTMOD_VIRTPIN_H #include "py/obj.h" +// CIRCUITPY-CHANGE #include "py/proto.h" #define MP_PIN_READ (1) @@ -36,6 +37,7 @@ // Pin protocol typedef struct _mp_pin_p_t { + // CIRCUITPY-CHANGE MP_PROTOCOL_HEAD mp_uint_t (*ioctl)(mp_obj_t obj, mp_uint_t request, uintptr_t arg, int *errcode); } mp_pin_p_t; diff --git a/frozen/Adafruit_CircuitPython_asyncio b/frozen/Adafruit_CircuitPython_asyncio index 2032ac10a967..2ea2b06c7dcc 160000 --- a/frozen/Adafruit_CircuitPython_asyncio +++ b/frozen/Adafruit_CircuitPython_asyncio @@ -1 +1 @@ -Subproject commit 2032ac10a967b10b3e1c8f72fa1423db6cc1b246 +Subproject commit 2ea2b06c7dcc41538790cc4cef5bc3e709b80762 diff --git a/lib/littlefs/lfs2.c b/lib/littlefs/lfs2.c index 534e4a1a6551..613213669c8f 100644 --- a/lib/littlefs/lfs2.c +++ b/lib/littlefs/lfs2.c @@ -46,8 +46,8 @@ static int lfs2_bd_read(lfs2_t *lfs2, lfs2_block_t block, lfs2_off_t off, void *buffer, lfs2_size_t size) { uint8_t *data = buffer; - if (block >= lfs2->cfg->block_count || - off+size > lfs2->cfg->block_size) { + if (off+size > lfs2->cfg->block_size + || (lfs2->block_count && block >= lfs2->block_count)) { return LFS2_ERR_CORRUPT; } @@ -104,7 +104,7 @@ static int lfs2_bd_read(lfs2_t *lfs2, } // load to cache, first condition can no longer fail - LFS2_ASSERT(block < lfs2->cfg->block_count); + LFS2_ASSERT(!lfs2->block_count || block < lfs2->block_count); rcache->block = block; rcache->off = lfs2_aligndown(off, lfs2->cfg->read_size); rcache->size = lfs2_min( @@ -135,14 +135,14 @@ static int lfs2_bd_cmp(lfs2_t *lfs2, uint8_t dat[8]; diff = lfs2_min(size-i, sizeof(dat)); - int res = lfs2_bd_read(lfs2, + int err = lfs2_bd_read(lfs2, pcache, rcache, hint-i, block, off+i, &dat, diff); - if (res) { - return res; + if (err) { + return err; } - res = memcmp(dat, data + i, diff); + int res = memcmp(dat, data + i, diff); if (res) { return res < 0 ? LFS2_CMP_LT : LFS2_CMP_GT; } @@ -151,11 +151,32 @@ static int lfs2_bd_cmp(lfs2_t *lfs2, return LFS2_CMP_EQ; } +static int lfs2_bd_crc(lfs2_t *lfs2, + const lfs2_cache_t *pcache, lfs2_cache_t *rcache, lfs2_size_t hint, + lfs2_block_t block, lfs2_off_t off, lfs2_size_t size, uint32_t *crc) { + lfs2_size_t diff = 0; + + for (lfs2_off_t i = 0; i < size; i += diff) { + uint8_t dat[8]; + diff = lfs2_min(size-i, sizeof(dat)); + int err = lfs2_bd_read(lfs2, + pcache, rcache, hint-i, + block, off+i, &dat, diff); + if (err) { + return err; + } + + *crc = lfs2_crc(*crc, &dat, diff); + } + + return 0; +} + #ifndef LFS2_READONLY static int lfs2_bd_flush(lfs2_t *lfs2, lfs2_cache_t *pcache, lfs2_cache_t *rcache, bool validate) { if (pcache->block != LFS2_BLOCK_NULL && pcache->block != LFS2_BLOCK_INLINE) { - LFS2_ASSERT(pcache->block < lfs2->cfg->block_count); + LFS2_ASSERT(pcache->block < lfs2->block_count); lfs2_size_t diff = lfs2_alignup(pcache->size, lfs2->cfg->prog_size); int err = lfs2->cfg->prog(lfs2->cfg, pcache->block, pcache->off, pcache->buffer, diff); @@ -208,7 +229,7 @@ static int lfs2_bd_prog(lfs2_t *lfs2, lfs2_block_t block, lfs2_off_t off, const void *buffer, lfs2_size_t size) { const uint8_t *data = buffer; - LFS2_ASSERT(block == LFS2_BLOCK_INLINE || block < lfs2->cfg->block_count); + LFS2_ASSERT(block == LFS2_BLOCK_INLINE || block < lfs2->block_count); LFS2_ASSERT(off + size <= lfs2->cfg->block_size); while (size > 0) { @@ -252,7 +273,7 @@ static int lfs2_bd_prog(lfs2_t *lfs2, #ifndef LFS2_READONLY static int lfs2_bd_erase(lfs2_t *lfs2, lfs2_block_t block) { - LFS2_ASSERT(block < lfs2->cfg->block_count); + LFS2_ASSERT(block < lfs2->block_count); int err = lfs2->cfg->erase(lfs2->cfg, block); LFS2_ASSERT(err <= 0); return err; @@ -279,14 +300,12 @@ static inline int lfs2_pair_cmp( paira[0] == pairb[1] || paira[1] == pairb[0]); } -#ifndef LFS2_READONLY -static inline bool lfs2_pair_sync( +static inline bool lfs2_pair_issync( const lfs2_block_t paira[2], const lfs2_block_t pairb[2]) { return (paira[0] == pairb[0] && paira[1] == pairb[1]) || (paira[0] == pairb[1] && paira[1] == pairb[0]); } -#endif static inline void lfs2_pair_fromle32(lfs2_block_t pair[2]) { pair[0] = lfs2_fromle32(pair[0]); @@ -325,6 +344,10 @@ static inline uint16_t lfs2_tag_type1(lfs2_tag_t tag) { return (tag & 0x70000000) >> 20; } +static inline uint16_t lfs2_tag_type2(lfs2_tag_t tag) { + return (tag & 0x78000000) >> 20; +} + static inline uint16_t lfs2_tag_type3(lfs2_tag_t tag) { return (tag & 0x7ff00000) >> 20; } @@ -386,7 +409,7 @@ static inline bool lfs2_gstate_hasorphans(const lfs2_gstate_t *a) { } static inline uint8_t lfs2_gstate_getorphans(const lfs2_gstate_t *a) { - return lfs2_tag_size(a->tag); + return lfs2_tag_size(a->tag) & 0x1ff; } static inline bool lfs2_gstate_hasmove(const lfs2_gstate_t *a) { @@ -394,6 +417,10 @@ static inline bool lfs2_gstate_hasmove(const lfs2_gstate_t *a) { } #endif +static inline bool lfs2_gstate_needssuperblock(const lfs2_gstate_t *a) { + return lfs2_tag_size(a->tag) >> 9; +} + static inline bool lfs2_gstate_hasmovehere(const lfs2_gstate_t *a, const lfs2_block_t *pair) { return lfs2_tag_type1(a->tag) && lfs2_pair_cmp(a->pair, pair) == 0; @@ -413,6 +440,24 @@ static inline void lfs2_gstate_tole32(lfs2_gstate_t *a) { } #endif +// operations on forward-CRCs used to track erased state +struct lfs2_fcrc { + lfs2_size_t size; + uint32_t crc; +}; + +static void lfs2_fcrc_fromle32(struct lfs2_fcrc *fcrc) { + fcrc->size = lfs2_fromle32(fcrc->size); + fcrc->crc = lfs2_fromle32(fcrc->crc); +} + +#ifndef LFS2_READONLY +static void lfs2_fcrc_tole32(struct lfs2_fcrc *fcrc) { + fcrc->size = lfs2_tole32(fcrc->size); + fcrc->crc = lfs2_tole32(fcrc->crc); +} +#endif + // other endianness operations static void lfs2_ctz_fromle32(struct lfs2_ctz *ctz) { ctz->head = lfs2_fromle32(ctz->head); @@ -473,6 +518,28 @@ static void lfs2_mlist_append(lfs2_t *lfs2, struct lfs2_mlist *mlist) { lfs2->mlist = mlist; } +// some other filesystem operations +static uint32_t lfs2_fs_disk_version(lfs2_t *lfs2) { + (void)lfs2; +#ifdef LFS2_MULTIVERSION + if (lfs2->cfg->disk_version) { + return lfs2->cfg->disk_version; + } else +#endif + { + return LFS2_DISK_VERSION; + } +} + +static uint16_t lfs2_fs_disk_version_major(lfs2_t *lfs2) { + return 0xffff & (lfs2_fs_disk_version(lfs2) >> 16); + +} + +static uint16_t lfs2_fs_disk_version_minor(lfs2_t *lfs2) { + return 0xffff & (lfs2_fs_disk_version(lfs2) >> 0); +} + /// Internal operations predeclared here /// #ifndef LFS2_READONLY @@ -500,6 +567,8 @@ static lfs2_stag_t lfs2_fs_parent(lfs2_t *lfs2, const lfs2_block_t dir[2], static int lfs2_fs_forceconsistency(lfs2_t *lfs2); #endif +static void lfs2_fs_prepsuperblock(lfs2_t *lfs2, bool needssuperblock); + #ifdef LFS2_MIGRATE static int lfs21_traverse(lfs2_t *lfs2, int (*cb)(void*, lfs2_block_t), void *data); @@ -528,7 +597,7 @@ static int lfs2_rawunmount(lfs2_t *lfs2); static int lfs2_alloc_lookahead(void *p, lfs2_block_t block) { lfs2_t *lfs2 = (lfs2_t*)p; lfs2_block_t off = ((block - lfs2->free.off) - + lfs2->cfg->block_count) % lfs2->cfg->block_count; + + lfs2->block_count) % lfs2->block_count; if (off < lfs2->free.size) { lfs2->free.buffer[off / 32] |= 1U << (off % 32); @@ -542,7 +611,7 @@ static int lfs2_alloc_lookahead(void *p, lfs2_block_t block) { // is to prevent blocks from being garbage collected in the middle of a // commit operation static void lfs2_alloc_ack(lfs2_t *lfs2) { - lfs2->free.ack = lfs2->cfg->block_count; + lfs2->free.ack = lfs2->block_count; } // drop the lookahead buffer, this is done during mounting and failed @@ -553,6 +622,26 @@ static void lfs2_alloc_drop(lfs2_t *lfs2) { lfs2_alloc_ack(lfs2); } +#ifndef LFS2_READONLY +static int lfs2_fs_rawgc(lfs2_t *lfs2) { + // Move free offset at the first unused block (lfs2->free.i) + // lfs2->free.i is equal lfs2->free.size when all blocks are used + lfs2->free.off = (lfs2->free.off + lfs2->free.i) % lfs2->block_count; + lfs2->free.size = lfs2_min(8*lfs2->cfg->lookahead_size, lfs2->free.ack); + lfs2->free.i = 0; + + // find mask of free blocks from tree + memset(lfs2->free.buffer, 0, lfs2->cfg->lookahead_size); + int err = lfs2_fs_rawtraverse(lfs2, lfs2_alloc_lookahead, lfs2, true); + if (err) { + lfs2_alloc_drop(lfs2); + return err; + } + + return 0; +} +#endif + #ifndef LFS2_READONLY static int lfs2_alloc(lfs2_t *lfs2, lfs2_block_t *block) { while (true) { @@ -563,7 +652,7 @@ static int lfs2_alloc(lfs2_t *lfs2, lfs2_block_t *block) { if (!(lfs2->free.buffer[off / 32] & (1U << (off % 32)))) { // found a free block - *block = (lfs2->free.off + off) % lfs2->cfg->block_count; + *block = (lfs2->free.off + off) % lfs2->block_count; // eagerly find next off so an alloc ack can // discredit old lookahead blocks @@ -585,16 +674,8 @@ static int lfs2_alloc(lfs2_t *lfs2, lfs2_block_t *block) { return LFS2_ERR_NOSPC; } - lfs2->free.off = (lfs2->free.off + lfs2->free.size) - % lfs2->cfg->block_count; - lfs2->free.size = lfs2_min(8*lfs2->cfg->lookahead_size, lfs2->free.ack); - lfs2->free.i = 0; - - // find mask of free blocks from tree - memset(lfs2->free.buffer, 0, lfs2->cfg->lookahead_size); - int err = lfs2_fs_rawtraverse(lfs2, lfs2_alloc_lookahead, lfs2, true); - if (err) { - lfs2_alloc_drop(lfs2); + int err = lfs2_fs_rawgc(lfs2); + if(err) { return err; } } @@ -808,7 +889,7 @@ static int lfs2_dir_traverse(lfs2_t *lfs2, // iterate over directory and attrs lfs2_tag_t tag; const void *buffer; - struct lfs2_diskoff disk; + struct lfs2_diskoff disk = {0}; while (true) { { if (off+lfs2_tag_dsize(ptag) < dir->off) { @@ -998,7 +1079,8 @@ static lfs2_stag_t lfs2_dir_fetchmatch(lfs2_t *lfs2, // if either block address is invalid we return LFS2_ERR_CORRUPT here, // otherwise later writes to the pair could fail - if (pair[0] >= lfs2->cfg->block_count || pair[1] >= lfs2->cfg->block_count) { + if (lfs2->block_count + && (pair[0] >= lfs2->block_count || pair[1] >= lfs2->block_count)) { return LFS2_ERR_CORRUPT; } @@ -1035,6 +1117,11 @@ static lfs2_stag_t lfs2_dir_fetchmatch(lfs2_t *lfs2, bool tempsplit = false; lfs2_stag_t tempbesttag = besttag; + // assume not erased until proven otherwise + bool maybeerased = false; + bool hasfcrc = false; + struct lfs2_fcrc fcrc; + dir->rev = lfs2_tole32(dir->rev); uint32_t crc = lfs2_crc(0xffffffff, &dir->rev, sizeof(dir->rev)); dir->rev = lfs2_fromle32(dir->rev); @@ -1049,7 +1136,6 @@ static lfs2_stag_t lfs2_dir_fetchmatch(lfs2_t *lfs2, if (err) { if (err == LFS2_ERR_CORRUPT) { // can't continue? - dir->erased = false; break; } return err; @@ -1058,19 +1144,19 @@ static lfs2_stag_t lfs2_dir_fetchmatch(lfs2_t *lfs2, crc = lfs2_crc(crc, &tag, sizeof(tag)); tag = lfs2_frombe32(tag) ^ ptag; - // next commit not yet programmed or we're not in valid range + // next commit not yet programmed? if (!lfs2_tag_isvalid(tag)) { - dir->erased = (lfs2_tag_type1(ptag) == LFS2_TYPE_CRC && - dir->off % lfs2->cfg->prog_size == 0); + // we only might be erased if the last tag was a crc + maybeerased = (lfs2_tag_type2(ptag) == LFS2_TYPE_CCRC); break; + // out of range? } else if (off + lfs2_tag_dsize(tag) > lfs2->cfg->block_size) { - dir->erased = false; break; } ptag = tag; - if (lfs2_tag_type1(tag) == LFS2_TYPE_CRC) { + if (lfs2_tag_type2(tag) == LFS2_TYPE_CCRC) { // check the crc attr uint32_t dcrc; err = lfs2_bd_read(lfs2, @@ -1078,7 +1164,6 @@ static lfs2_stag_t lfs2_dir_fetchmatch(lfs2_t *lfs2, dir->pair[0], off+sizeof(tag), &dcrc, sizeof(dcrc)); if (err) { if (err == LFS2_ERR_CORRUPT) { - dir->erased = false; break; } return err; @@ -1086,7 +1171,6 @@ static lfs2_stag_t lfs2_dir_fetchmatch(lfs2_t *lfs2, dcrc = lfs2_fromle32(dcrc); if (crc != dcrc) { - dir->erased = false; break; } @@ -1108,26 +1192,21 @@ static lfs2_stag_t lfs2_dir_fetchmatch(lfs2_t *lfs2, dir->tail[1] = temptail[1]; dir->split = tempsplit; - // reset crc + // reset crc, hasfcrc crc = 0xffffffff; continue; } // crc the entry first, hopefully leaving it in the cache - for (lfs2_off_t j = sizeof(tag); j < lfs2_tag_dsize(tag); j++) { - uint8_t dat; - err = lfs2_bd_read(lfs2, - NULL, &lfs2->rcache, lfs2->cfg->block_size, - dir->pair[0], off+j, &dat, 1); - if (err) { - if (err == LFS2_ERR_CORRUPT) { - dir->erased = false; - break; - } - return err; + err = lfs2_bd_crc(lfs2, + NULL, &lfs2->rcache, lfs2->cfg->block_size, + dir->pair[0], off+sizeof(tag), + lfs2_tag_dsize(tag)-sizeof(tag), &crc); + if (err) { + if (err == LFS2_ERR_CORRUPT) { + break; } - - crc = lfs2_crc(crc, &dat, 1); + return err; } // directory modification tags? @@ -1154,11 +1233,24 @@ static lfs2_stag_t lfs2_dir_fetchmatch(lfs2_t *lfs2, dir->pair[0], off+sizeof(tag), &temptail, 8); if (err) { if (err == LFS2_ERR_CORRUPT) { - dir->erased = false; break; } + return err; } lfs2_pair_fromle32(temptail); + } else if (lfs2_tag_type3(tag) == LFS2_TYPE_FCRC) { + err = lfs2_bd_read(lfs2, + NULL, &lfs2->rcache, lfs2->cfg->block_size, + dir->pair[0], off+sizeof(tag), + &fcrc, sizeof(fcrc)); + if (err) { + if (err == LFS2_ERR_CORRUPT) { + break; + } + } + + lfs2_fcrc_fromle32(&fcrc); + hasfcrc = true; } // found a match for our fetcher? @@ -1167,7 +1259,6 @@ static lfs2_stag_t lfs2_dir_fetchmatch(lfs2_t *lfs2, dir->pair[0], off+sizeof(tag)}); if (res < 0) { if (res == LFS2_ERR_CORRUPT) { - dir->erased = false; break; } return res; @@ -1189,35 +1280,67 @@ static lfs2_stag_t lfs2_dir_fetchmatch(lfs2_t *lfs2, } } - // consider what we have good enough - if (dir->off > 0) { - // synthetic move - if (lfs2_gstate_hasmovehere(&lfs2->gdisk, dir->pair)) { - if (lfs2_tag_id(lfs2->gdisk.tag) == lfs2_tag_id(besttag)) { - besttag |= 0x80000000; - } else if (besttag != -1 && - lfs2_tag_id(lfs2->gdisk.tag) < lfs2_tag_id(besttag)) { - besttag -= LFS2_MKTAG(0, 1, 0); + // found no valid commits? + if (dir->off == 0) { + // try the other block? + lfs2_pair_swap(dir->pair); + dir->rev = revs[(r+1)%2]; + continue; + } + + // did we end on a valid commit? we may have an erased block + dir->erased = false; + if (maybeerased && dir->off % lfs2->cfg->prog_size == 0) { + #ifdef LFS2_MULTIVERSION + // note versions < lfs22.1 did not have fcrc tags, if + // we're < lfs22.1 treat missing fcrc as erased data + // + // we don't strictly need to do this, but otherwise writing + // to lfs22.0 disks becomes very inefficient + if (lfs2_fs_disk_version(lfs2) < 0x00020001) { + dir->erased = true; + + } else + #endif + if (hasfcrc) { + // check for an fcrc matching the next prog's erased state, if + // this failed most likely a previous prog was interrupted, we + // need a new erase + uint32_t fcrc_ = 0xffffffff; + int err = lfs2_bd_crc(lfs2, + NULL, &lfs2->rcache, lfs2->cfg->block_size, + dir->pair[0], dir->off, fcrc.size, &fcrc_); + if (err && err != LFS2_ERR_CORRUPT) { + return err; } - } - // found tag? or found best id? - if (id) { - *id = lfs2_min(lfs2_tag_id(besttag), dir->count); + // found beginning of erased part? + dir->erased = (fcrc_ == fcrc.crc); } + } - if (lfs2_tag_isvalid(besttag)) { - return besttag; - } else if (lfs2_tag_id(besttag) < dir->count) { - return LFS2_ERR_NOENT; - } else { - return 0; + // synthetic move + if (lfs2_gstate_hasmovehere(&lfs2->gdisk, dir->pair)) { + if (lfs2_tag_id(lfs2->gdisk.tag) == lfs2_tag_id(besttag)) { + besttag |= 0x80000000; + } else if (besttag != -1 && + lfs2_tag_id(lfs2->gdisk.tag) < lfs2_tag_id(besttag)) { + besttag -= LFS2_MKTAG(0, 1, 0); } } - // failed, try the other block? - lfs2_pair_swap(dir->pair); - dir->rev = revs[(r+1)%2]; + // found tag? or found best id? + if (id) { + *id = lfs2_min(lfs2_tag_id(besttag), dir->count); + } + + if (lfs2_tag_isvalid(besttag)) { + return besttag; + } else if (lfs2_tag_id(besttag) < dir->count) { + return LFS2_ERR_NOENT; + } else { + return 0; + } } LFS2_ERROR("Corrupted dir pair at {0x%"PRIx32", 0x%"PRIx32"}", @@ -1491,9 +1614,15 @@ static int lfs2_dir_commitattr(lfs2_t *lfs2, struct lfs2_commit *commit, #endif #ifndef LFS2_READONLY + static int lfs2_dir_commitcrc(lfs2_t *lfs2, struct lfs2_commit *commit) { // align to program units - const lfs2_off_t end = lfs2_alignup(commit->off + 2*sizeof(uint32_t), + // + // this gets a bit complex as we have two types of crcs: + // - 5-word crc with fcrc to check following prog (middle of block) + // - 2-word crc with no following prog (end of block) + const lfs2_off_t end = lfs2_alignup( + lfs2_min(commit->off + 5*sizeof(uint32_t), lfs2->cfg->block_size), lfs2->cfg->prog_size); lfs2_off_t off1 = 0; @@ -1503,89 +1632,128 @@ static int lfs2_dir_commitcrc(lfs2_t *lfs2, struct lfs2_commit *commit) { // padding is not crced, which lets fetches skip padding but // makes committing a bit more complicated while (commit->off < end) { - lfs2_off_t off = commit->off + sizeof(lfs2_tag_t); - lfs2_off_t noff = lfs2_min(end - off, 0x3fe) + off; + lfs2_off_t noff = ( + lfs2_min(end - (commit->off+sizeof(lfs2_tag_t)), 0x3fe) + + (commit->off+sizeof(lfs2_tag_t))); + // too large for crc tag? need padding commits if (noff < end) { - noff = lfs2_min(noff, end - 2*sizeof(uint32_t)); + noff = lfs2_min(noff, end - 5*sizeof(uint32_t)); } - // read erased state from next program unit - lfs2_tag_t tag = 0xffffffff; - int err = lfs2_bd_read(lfs2, - NULL, &lfs2->rcache, sizeof(tag), - commit->block, noff, &tag, sizeof(tag)); - if (err && err != LFS2_ERR_CORRUPT) { - return err; - } + // space for fcrc? + uint8_t eperturb = (uint8_t)-1; + if (noff >= end && noff <= lfs2->cfg->block_size - lfs2->cfg->prog_size) { + // first read the leading byte, this always contains a bit + // we can perturb to avoid writes that don't change the fcrc + int err = lfs2_bd_read(lfs2, + NULL, &lfs2->rcache, lfs2->cfg->prog_size, + commit->block, noff, &eperturb, 1); + if (err && err != LFS2_ERR_CORRUPT) { + return err; + } - // build crc tag - bool reset = ~lfs2_frombe32(tag) >> 31; - tag = LFS2_MKTAG(LFS2_TYPE_CRC + reset, 0x3ff, noff - off); + #ifdef LFS2_MULTIVERSION + // unfortunately fcrcs break mdir fetching < lfs22.1, so only write + // these if we're a >= lfs22.1 filesystem + if (lfs2_fs_disk_version(lfs2) <= 0x00020000) { + // don't write fcrc + } else + #endif + { + // find the expected fcrc, don't bother avoiding a reread + // of the eperturb, it should still be in our cache + struct lfs2_fcrc fcrc = { + .size = lfs2->cfg->prog_size, + .crc = 0xffffffff + }; + err = lfs2_bd_crc(lfs2, + NULL, &lfs2->rcache, lfs2->cfg->prog_size, + commit->block, noff, fcrc.size, &fcrc.crc); + if (err && err != LFS2_ERR_CORRUPT) { + return err; + } + + lfs2_fcrc_tole32(&fcrc); + err = lfs2_dir_commitattr(lfs2, commit, + LFS2_MKTAG(LFS2_TYPE_FCRC, 0x3ff, sizeof(struct lfs2_fcrc)), + &fcrc); + if (err) { + return err; + } + } + } - // write out crc - uint32_t footer[2]; - footer[0] = lfs2_tobe32(tag ^ commit->ptag); - commit->crc = lfs2_crc(commit->crc, &footer[0], sizeof(footer[0])); - footer[1] = lfs2_tole32(commit->crc); - err = lfs2_bd_prog(lfs2, + // build commit crc + struct { + lfs2_tag_t tag; + uint32_t crc; + } ccrc; + lfs2_tag_t ntag = LFS2_MKTAG( + LFS2_TYPE_CCRC + (((uint8_t)~eperturb) >> 7), 0x3ff, + noff - (commit->off+sizeof(lfs2_tag_t))); + ccrc.tag = lfs2_tobe32(ntag ^ commit->ptag); + commit->crc = lfs2_crc(commit->crc, &ccrc.tag, sizeof(lfs2_tag_t)); + ccrc.crc = lfs2_tole32(commit->crc); + + int err = lfs2_bd_prog(lfs2, &lfs2->pcache, &lfs2->rcache, false, - commit->block, commit->off, &footer, sizeof(footer)); + commit->block, commit->off, &ccrc, sizeof(ccrc)); if (err) { return err; } // keep track of non-padding checksum to verify if (off1 == 0) { - off1 = commit->off + sizeof(uint32_t); + off1 = commit->off + sizeof(lfs2_tag_t); crc1 = commit->crc; } - commit->off += sizeof(tag)+lfs2_tag_size(tag); - commit->ptag = tag ^ ((lfs2_tag_t)reset << 31); - commit->crc = 0xffffffff; // reset crc for next "commit" - } + commit->off = noff; + // perturb valid bit? + commit->ptag = ntag ^ ((0x80UL & ~eperturb) << 24); + // reset crc for next commit + commit->crc = 0xffffffff; - // flush buffers - int err = lfs2_bd_sync(lfs2, &lfs2->pcache, &lfs2->rcache, false); - if (err) { - return err; + // manually flush here since we don't prog the padding, this confuses + // the caching layer + if (noff >= end || noff >= lfs2->pcache.off + lfs2->cfg->cache_size) { + // flush buffers + int err = lfs2_bd_sync(lfs2, &lfs2->pcache, &lfs2->rcache, false); + if (err) { + return err; + } + } } // successful commit, check checksums to make sure + // + // note that we don't need to check padding commits, worst + // case if they are corrupted we would have had to compact anyways lfs2_off_t off = commit->begin; - lfs2_off_t noff = off1; - while (off < end) { - uint32_t crc = 0xffffffff; - for (lfs2_off_t i = off; i < noff+sizeof(uint32_t); i++) { - // check against written crc, may catch blocks that - // become readonly and match our commit size exactly - if (i == off1 && crc != crc1) { - return LFS2_ERR_CORRUPT; - } - - // leave it up to caching to make this efficient - uint8_t dat; - err = lfs2_bd_read(lfs2, - NULL, &lfs2->rcache, noff+sizeof(uint32_t)-i, - commit->block, i, &dat, 1); - if (err) { - return err; - } + uint32_t crc = 0xffffffff; + int err = lfs2_bd_crc(lfs2, + NULL, &lfs2->rcache, off1+sizeof(uint32_t), + commit->block, off, off1-off, &crc); + if (err) { + return err; + } - crc = lfs2_crc(crc, &dat, 1); - } + // check non-padding commits against known crc + if (crc != crc1) { + return LFS2_ERR_CORRUPT; + } - // detected write error? - if (crc != 0) { - return LFS2_ERR_CORRUPT; - } + // make sure to check crc in case we happen to pick + // up an unrelated crc (frozen block?) + err = lfs2_bd_crc(lfs2, + NULL, &lfs2->rcache, sizeof(uint32_t), + commit->block, off1, sizeof(uint32_t), &crc); + if (err) { + return err; + } - // skip padding - off = lfs2_min(end - noff, 0x3fe) + noff; - if (off < end) { - off = lfs2_min(off, end - 2*sizeof(uint32_t)); - } - noff = off + sizeof(uint32_t); + if (crc != 0) { + return LFS2_ERR_CORRUPT; } return 0; @@ -1926,11 +2094,20 @@ static int lfs2_dir_splittingcompact(lfs2_t *lfs2, lfs2_mdir_t *dir, return err; } - // space is complicated, we need room for tail, crc, gstate, - // cleanup delete, and we cap at half a block to give room - // for metadata updates. + // space is complicated, we need room for: + // + // - tail: 4+2*4 = 12 bytes + // - gstate: 4+3*4 = 16 bytes + // - move delete: 4 = 4 bytes + // - crc: 4+4 = 8 bytes + // total = 40 bytes + // + // And we cap at half a block to avoid degenerate cases with + // nearly-full metadata blocks. + // if (end - split < 0xff - && size <= lfs2_min(lfs2->cfg->block_size - 36, + && size <= lfs2_min( + lfs2->cfg->block_size - 40, lfs2_alignup( (lfs2->cfg->metadata_max ? lfs2->cfg->metadata_max @@ -1976,7 +2153,7 @@ static int lfs2_dir_splittingcompact(lfs2_t *lfs2, lfs2_mdir_t *dir, // do we have extra space? littlefs can't reclaim this space // by itself, so expand cautiously - if ((lfs2_size_t)size < lfs2->cfg->block_count/2) { + if ((lfs2_size_t)size < lfs2->block_count/2) { LFS2_DEBUG("Expanding superblock at rev %"PRIu32, dir->rev); int err = lfs2_dir_split(lfs2, dir, attrs, attrcount, source, begin, end); @@ -2594,11 +2771,6 @@ static int lfs2_dir_rawseek(lfs2_t *lfs2, lfs2_dir_t *dir, lfs2_off_t off) { dir->id = (off > 0 && lfs2_pair_cmp(dir->head, lfs2->root) == 0); while (off > 0) { - int diff = lfs2_min(dir->m.count - dir->id, off); - dir->id += diff; - dir->pos += diff; - off -= diff; - if (dir->id == dir->m.count) { if (!dir->m.split) { return LFS2_ERR_INVAL; @@ -2611,6 +2783,11 @@ static int lfs2_dir_rawseek(lfs2_t *lfs2, lfs2_dir_t *dir, lfs2_off_t off) { dir->id = 0; } + + int diff = lfs2_min(dir->m.count - dir->id, off); + dir->id += diff; + dir->pos += diff; + off -= diff; } return 0; @@ -3347,7 +3524,7 @@ static lfs2_ssize_t lfs2_file_flushedwrite(lfs2_t *lfs2, lfs2_file_t *file, // find out which block we're extending from int err = lfs2_ctz_find(lfs2, NULL, &file->cache, file->ctz.head, file->ctz.size, - file->pos-1, &file->block, &file->off); + file->pos-1, &file->block, &(lfs2_off_t){0}); if (err) { file->flags |= LFS2_F_ERRED; return err; @@ -3525,26 +3702,55 @@ static int lfs2_file_rawtruncate(lfs2_t *lfs2, lfs2_file_t *file, lfs2_off_t siz lfs2_off_t pos = file->pos; lfs2_off_t oldsize = lfs2_file_rawsize(lfs2, file); if (size < oldsize) { - // need to flush since directly changing metadata - int err = lfs2_file_flush(lfs2, file); - if (err) { - return err; - } + // revert to inline file? + if (size <= lfs2_min(0x3fe, lfs2_min( + lfs2->cfg->cache_size, + (lfs2->cfg->metadata_max ? + lfs2->cfg->metadata_max : lfs2->cfg->block_size) / 8))) { + // flush+seek to head + lfs2_soff_t res = lfs2_file_rawseek(lfs2, file, 0, LFS2_SEEK_SET); + if (res < 0) { + return (int)res; + } - // lookup new head in ctz skip list - err = lfs2_ctz_find(lfs2, NULL, &file->cache, - file->ctz.head, file->ctz.size, - size, &file->block, &file->off); - if (err) { - return err; - } + // read our data into rcache temporarily + lfs2_cache_drop(lfs2, &lfs2->rcache); + res = lfs2_file_flushedread(lfs2, file, + lfs2->rcache.buffer, size); + if (res < 0) { + return (int)res; + } - // need to set pos/block/off consistently so seeking back to - // the old position does not get confused - file->pos = size; - file->ctz.head = file->block; - file->ctz.size = size; - file->flags |= LFS2_F_DIRTY | LFS2_F_READING; + file->ctz.head = LFS2_BLOCK_INLINE; + file->ctz.size = size; + file->flags |= LFS2_F_DIRTY | LFS2_F_READING | LFS2_F_INLINE; + file->cache.block = file->ctz.head; + file->cache.off = 0; + file->cache.size = lfs2->cfg->cache_size; + memcpy(file->cache.buffer, lfs2->rcache.buffer, size); + + } else { + // need to flush since directly changing metadata + int err = lfs2_file_flush(lfs2, file); + if (err) { + return err; + } + + // lookup new head in ctz skip list + err = lfs2_ctz_find(lfs2, NULL, &file->cache, + file->ctz.head, file->ctz.size, + size-1, &file->block, &(lfs2_off_t){0}); + if (err) { + return err; + } + + // need to set pos/block/off consistently so seeking back to + // the old position does not get confused + file->pos = size; + file->ctz.head = file->block; + file->ctz.size = size; + file->flags |= LFS2_F_DIRTY | LFS2_F_READING; + } } else if (size > oldsize) { // flush+seek if not already at end lfs2_soff_t res = lfs2_file_rawseek(lfs2, file, 0, LFS2_SEEK_END); @@ -3902,8 +4108,24 @@ static int lfs2_rawremoveattr(lfs2_t *lfs2, const char *path, uint8_t type) { /// Filesystem operations /// static int lfs2_init(lfs2_t *lfs2, const struct lfs2_config *cfg) { lfs2->cfg = cfg; + lfs2->block_count = cfg->block_count; // May be 0 int err = 0; +#ifdef LFS2_MULTIVERSION + // this driver only supports minor version < current minor version + LFS2_ASSERT(!lfs2->cfg->disk_version || ( + (0xffff & (lfs2->cfg->disk_version >> 16)) + == LFS2_DISK_VERSION_MAJOR + && (0xffff & (lfs2->cfg->disk_version >> 0)) + <= LFS2_DISK_VERSION_MINOR)); +#endif + + // check that bool is a truthy-preserving type + // + // note the most common reason for this failure is a before-c99 compiler, + // which littlefs currently does not support + LFS2_ASSERT((bool)0x80000000); + // validate that the lfs2-cfg sizes were initiated properly before // performing any arithmetic logics with them LFS2_ASSERT(lfs2->cfg->read_size != 0); @@ -3916,7 +4138,10 @@ static int lfs2_init(lfs2_t *lfs2, const struct lfs2_config *cfg) { LFS2_ASSERT(lfs2->cfg->cache_size % lfs2->cfg->prog_size == 0); LFS2_ASSERT(lfs2->cfg->block_size % lfs2->cfg->cache_size == 0); - // check that the block size is large enough to fit ctz pointers + // check that the block size is large enough to fit all ctz pointers + LFS2_ASSERT(lfs2->cfg->block_size >= 128); + // this is the exact calculation for all ctz pointers, if this fails + // and the simpler assert above does not, math must be broken LFS2_ASSERT(4*lfs2_npw2(0xffffffff / (lfs2->cfg->block_size-2*4)) <= lfs2->cfg->block_size); @@ -4026,6 +4251,8 @@ static int lfs2_deinit(lfs2_t *lfs2) { return 0; } + + #ifndef LFS2_READONLY static int lfs2_rawformat(lfs2_t *lfs2, const struct lfs2_config *cfg) { int err = 0; @@ -4035,11 +4262,13 @@ static int lfs2_rawformat(lfs2_t *lfs2, const struct lfs2_config *cfg) { return err; } + LFS2_ASSERT(cfg->block_count != 0); + // create free lookahead memset(lfs2->free.buffer, 0, lfs2->cfg->lookahead_size); lfs2->free.off = 0; lfs2->free.size = lfs2_min(8*lfs2->cfg->lookahead_size, - lfs2->cfg->block_count); + lfs2->block_count); lfs2->free.i = 0; lfs2_alloc_ack(lfs2); @@ -4052,9 +4281,9 @@ static int lfs2_rawformat(lfs2_t *lfs2, const struct lfs2_config *cfg) { // write one superblock lfs2_superblock_t superblock = { - .version = LFS2_DISK_VERSION, + .version = lfs2_fs_disk_version(lfs2), .block_size = lfs2->cfg->block_size, - .block_count = lfs2->cfg->block_count, + .block_count = lfs2->block_count, .name_max = lfs2->name_max, .file_max = lfs2->file_max, .attr_max = lfs2->attr_max, @@ -4100,14 +4329,23 @@ static int lfs2_rawmount(lfs2_t *lfs2, const struct lfs2_config *cfg) { // scan directory blocks for superblock and any global updates lfs2_mdir_t dir = {.tail = {0, 1}}; - lfs2_block_t cycle = 0; + lfs2_block_t tortoise[2] = {LFS2_BLOCK_NULL, LFS2_BLOCK_NULL}; + lfs2_size_t tortoise_i = 1; + lfs2_size_t tortoise_period = 1; while (!lfs2_pair_isnull(dir.tail)) { - if (cycle >= lfs2->cfg->block_count/2) { - // loop detected + // detect cycles with Brent's algorithm + if (lfs2_pair_issync(dir.tail, tortoise)) { + LFS2_WARN("Cycle detected in tail list"); err = LFS2_ERR_CORRUPT; goto cleanup; } - cycle += 1; + if (tortoise_i == tortoise_period) { + tortoise[0] = dir.tail[0]; + tortoise[1] = dir.tail[1]; + tortoise_i = 0; + tortoise_period *= 2; + } + tortoise_i += 1; // fetch next block in tail list lfs2_stag_t tag = lfs2_dir_fetchmatch(lfs2, &dir, dir.tail, @@ -4141,14 +4379,33 @@ static int lfs2_rawmount(lfs2_t *lfs2, const struct lfs2_config *cfg) { // check version uint16_t major_version = (0xffff & (superblock.version >> 16)); uint16_t minor_version = (0xffff & (superblock.version >> 0)); - if ((major_version != LFS2_DISK_VERSION_MAJOR || - minor_version > LFS2_DISK_VERSION_MINOR)) { - LFS2_ERROR("Invalid version v%"PRIu16".%"PRIu16, - major_version, minor_version); + if (major_version != lfs2_fs_disk_version_major(lfs2) + || minor_version > lfs2_fs_disk_version_minor(lfs2)) { + LFS2_ERROR("Invalid version " + "v%"PRIu16".%"PRIu16" != v%"PRIu16".%"PRIu16, + major_version, + minor_version, + lfs2_fs_disk_version_major(lfs2), + lfs2_fs_disk_version_minor(lfs2)); err = LFS2_ERR_INVAL; goto cleanup; } + // found older minor version? set an in-device only bit in the + // gstate so we know we need to rewrite the superblock before + // the first write + if (minor_version < lfs2_fs_disk_version_minor(lfs2)) { + LFS2_DEBUG("Found older minor version " + "v%"PRIu16".%"PRIu16" < v%"PRIu16".%"PRIu16, + major_version, + minor_version, + lfs2_fs_disk_version_major(lfs2), + lfs2_fs_disk_version_minor(lfs2)); + // note this bit is reserved on disk, so fetching more gstate + // will not interfere here + lfs2_fs_prepsuperblock(lfs2, true); + } + // check superblock configuration if (superblock.name_max) { if (superblock.name_max > lfs2->name_max) { @@ -4183,16 +4440,20 @@ static int lfs2_rawmount(lfs2_t *lfs2, const struct lfs2_config *cfg) { lfs2->attr_max = superblock.attr_max; } - if (superblock.block_count != lfs2->cfg->block_count) { + // this is where we get the block_count from disk if block_count=0 + if (lfs2->cfg->block_count + && superblock.block_count != lfs2->cfg->block_count) { LFS2_ERROR("Invalid block count (%"PRIu32" != %"PRIu32")", superblock.block_count, lfs2->cfg->block_count); err = LFS2_ERR_INVAL; goto cleanup; } + lfs2->block_count = superblock.block_count; + if (superblock.block_size != lfs2->cfg->block_size) { LFS2_ERROR("Invalid block size (%"PRIu32" != %"PRIu32")", - superblock.block_count, lfs2->cfg->block_count); + superblock.block_size, lfs2->cfg->block_size); err = LFS2_ERR_INVAL; goto cleanup; } @@ -4205,12 +4466,6 @@ static int lfs2_rawmount(lfs2_t *lfs2, const struct lfs2_config *cfg) { } } - // found superblock? - if (lfs2_pair_isnull(lfs2->root)) { - err = LFS2_ERR_INVAL; - goto cleanup; - } - // update littlefs with gstate if (!lfs2_gstate_iszero(&lfs2->gstate)) { LFS2_DEBUG("Found pending gstate 0x%08"PRIx32"%08"PRIx32"%08"PRIx32, @@ -4223,7 +4478,7 @@ static int lfs2_rawmount(lfs2_t *lfs2, const struct lfs2_config *cfg) { // setup free lookahead, to distribute allocations uniformly across // boots, we start the allocator at a random location - lfs2->free.off = lfs2->seed % lfs2->cfg->block_count; + lfs2->free.off = lfs2->seed % lfs2->block_count; lfs2_alloc_drop(lfs2); return 0; @@ -4239,6 +4494,46 @@ static int lfs2_rawunmount(lfs2_t *lfs2) { /// Filesystem filesystem operations /// +static int lfs2_fs_rawstat(lfs2_t *lfs2, struct lfs2_fsinfo *fsinfo) { + // if the superblock is up-to-date, we must be on the most recent + // minor version of littlefs + if (!lfs2_gstate_needssuperblock(&lfs2->gstate)) { + fsinfo->disk_version = lfs2_fs_disk_version(lfs2); + + // otherwise we need to read the minor version on disk + } else { + // fetch the superblock + lfs2_mdir_t dir; + int err = lfs2_dir_fetch(lfs2, &dir, lfs2->root); + if (err) { + return err; + } + + lfs2_superblock_t superblock; + lfs2_stag_t tag = lfs2_dir_get(lfs2, &dir, LFS2_MKTAG(0x7ff, 0x3ff, 0), + LFS2_MKTAG(LFS2_TYPE_INLINESTRUCT, 0, sizeof(superblock)), + &superblock); + if (tag < 0) { + return tag; + } + lfs2_superblock_fromle32(&superblock); + + // read the on-disk version + fsinfo->disk_version = superblock.version; + } + + // filesystem geometry + fsinfo->block_size = lfs2->cfg->block_size; + fsinfo->block_count = lfs2->block_count; + + // other on-disk configuration, we cache all of these for internal use + fsinfo->name_max = lfs2->name_max; + fsinfo->file_max = lfs2->file_max; + fsinfo->attr_max = lfs2->attr_max; + + return 0; +} + int lfs2_fs_rawtraverse(lfs2_t *lfs2, int (*cb)(void *data, lfs2_block_t block), void *data, bool includeorphans) { @@ -4258,13 +4553,22 @@ int lfs2_fs_rawtraverse(lfs2_t *lfs2, } #endif - lfs2_block_t cycle = 0; + lfs2_block_t tortoise[2] = {LFS2_BLOCK_NULL, LFS2_BLOCK_NULL}; + lfs2_size_t tortoise_i = 1; + lfs2_size_t tortoise_period = 1; while (!lfs2_pair_isnull(dir.tail)) { - if (cycle >= lfs2->cfg->block_count/2) { - // loop detected + // detect cycles with Brent's algorithm + if (lfs2_pair_issync(dir.tail, tortoise)) { + LFS2_WARN("Cycle detected in tail list"); return LFS2_ERR_CORRUPT; } - cycle += 1; + if (tortoise_i == tortoise_period) { + tortoise[0] = dir.tail[0]; + tortoise[1] = dir.tail[1]; + tortoise_i = 0; + tortoise_period *= 2; + } + tortoise_i += 1; for (int i = 0; i < 2; i++) { int err = cb(data, dir.tail[i]); @@ -4343,13 +4647,22 @@ static int lfs2_fs_pred(lfs2_t *lfs2, // iterate over all directory directory entries pdir->tail[0] = 0; pdir->tail[1] = 1; - lfs2_block_t cycle = 0; + lfs2_block_t tortoise[2] = {LFS2_BLOCK_NULL, LFS2_BLOCK_NULL}; + lfs2_size_t tortoise_i = 1; + lfs2_size_t tortoise_period = 1; while (!lfs2_pair_isnull(pdir->tail)) { - if (cycle >= lfs2->cfg->block_count/2) { - // loop detected + // detect cycles with Brent's algorithm + if (lfs2_pair_issync(pdir->tail, tortoise)) { + LFS2_WARN("Cycle detected in tail list"); return LFS2_ERR_CORRUPT; } - cycle += 1; + if (tortoise_i == tortoise_period) { + tortoise[0] = pdir->tail[0]; + tortoise[1] = pdir->tail[1]; + tortoise_i = 0; + tortoise_period *= 2; + } + tortoise_i += 1; if (lfs2_pair_cmp(pdir->tail, pair) == 0) { return 0; @@ -4399,13 +4712,22 @@ static lfs2_stag_t lfs2_fs_parent(lfs2_t *lfs2, const lfs2_block_t pair[2], // use fetchmatch with callback to find pairs parent->tail[0] = 0; parent->tail[1] = 1; - lfs2_block_t cycle = 0; + lfs2_block_t tortoise[2] = {LFS2_BLOCK_NULL, LFS2_BLOCK_NULL}; + lfs2_size_t tortoise_i = 1; + lfs2_size_t tortoise_period = 1; while (!lfs2_pair_isnull(parent->tail)) { - if (cycle >= lfs2->cfg->block_count/2) { - // loop detected + // detect cycles with Brent's algorithm + if (lfs2_pair_issync(parent->tail, tortoise)) { + LFS2_WARN("Cycle detected in tail list"); return LFS2_ERR_CORRUPT; } - cycle += 1; + if (tortoise_i == tortoise_period) { + tortoise[0] = parent->tail[0]; + tortoise[1] = parent->tail[1]; + tortoise_i = 0; + tortoise_period *= 2; + } + tortoise_i += 1; lfs2_stag_t tag = lfs2_dir_fetchmatch(lfs2, parent, parent->tail, LFS2_MKTAG(0x7ff, 0, 0x3ff), @@ -4422,9 +4744,15 @@ static lfs2_stag_t lfs2_fs_parent(lfs2_t *lfs2, const lfs2_block_t pair[2], } #endif +static void lfs2_fs_prepsuperblock(lfs2_t *lfs2, bool needssuperblock) { + lfs2->gstate.tag = (lfs2->gstate.tag & ~LFS2_MKTAG(0, 0, 0x200)) + | (uint32_t)needssuperblock << 9; +} + #ifndef LFS2_READONLY static int lfs2_fs_preporphans(lfs2_t *lfs2, int8_t orphans) { - LFS2_ASSERT(lfs2_tag_size(lfs2->gstate.tag) > 0 || orphans >= 0); + LFS2_ASSERT(lfs2_tag_size(lfs2->gstate.tag) > 0x000 || orphans >= 0); + LFS2_ASSERT(lfs2_tag_size(lfs2->gstate.tag) < 0x1ff || orphans <= 0); lfs2->gstate.tag += orphans; lfs2->gstate.tag = ((lfs2->gstate.tag & ~LFS2_MKTAG(0x800, 0, 0)) | ((uint32_t)lfs2_gstate_hasorphans(&lfs2->gstate) << 31)); @@ -4443,6 +4771,45 @@ static void lfs2_fs_prepmove(lfs2_t *lfs2, } #endif +#ifndef LFS2_READONLY +static int lfs2_fs_desuperblock(lfs2_t *lfs2) { + if (!lfs2_gstate_needssuperblock(&lfs2->gstate)) { + return 0; + } + + LFS2_DEBUG("Rewriting superblock {0x%"PRIx32", 0x%"PRIx32"}", + lfs2->root[0], + lfs2->root[1]); + + lfs2_mdir_t root; + int err = lfs2_dir_fetch(lfs2, &root, lfs2->root); + if (err) { + return err; + } + + // write a new superblock + lfs2_superblock_t superblock = { + .version = lfs2_fs_disk_version(lfs2), + .block_size = lfs2->cfg->block_size, + .block_count = lfs2->block_count, + .name_max = lfs2->name_max, + .file_max = lfs2->file_max, + .attr_max = lfs2->attr_max, + }; + + lfs2_superblock_tole32(&superblock); + err = lfs2_dir_commit(lfs2, &root, LFS2_MKATTRS( + {LFS2_MKTAG(LFS2_TYPE_INLINESTRUCT, 0, sizeof(superblock)), + &superblock})); + if (err) { + return err; + } + + lfs2_fs_prepsuperblock(lfs2, false); + return 0; +} +#endif + #ifndef LFS2_READONLY static int lfs2_fs_demove(lfs2_t *lfs2) { if (!lfs2_gstate_hasmove(&lfs2->gdisk)) { @@ -4455,6 +4822,10 @@ static int lfs2_fs_demove(lfs2_t *lfs2) { lfs2->gdisk.pair[1], lfs2_tag_id(lfs2->gdisk.tag)); + // no other gstate is supported at this time, so if we found something else + // something most likely went wrong in gstate calculation + LFS2_ASSERT(lfs2_tag_type3(lfs2->gdisk.tag) == LFS2_TYPE_DELETE); + // fetch and delete the moved entry lfs2_mdir_t movedir; int err = lfs2_dir_fetch(lfs2, &movedir, lfs2->gdisk.pair); @@ -4481,12 +4852,20 @@ static int lfs2_fs_deorphan(lfs2_t *lfs2, bool powerloss) { return 0; } - int8_t found = 0; -restart: - { + // Check for orphans in two separate passes: + // - 1 for half-orphans (relocations) + // - 2 for full-orphans (removes/renames) + // + // Two separate passes are needed as half-orphans can contain outdated + // references to full-orphans, effectively hiding them from the deorphan + // search. + // + int pass = 0; + while (pass < 2) { // Fix any orphans lfs2_mdir_t pdir = {.split = true, .tail = {0, 1}}; lfs2_mdir_t dir; + bool moreorphans = false; // iterate over all directory directory entries while (!lfs2_pair_isnull(pdir.tail)) { @@ -4504,42 +4883,7 @@ static int lfs2_fs_deorphan(lfs2_t *lfs2, bool powerloss) { return tag; } - // note we only check for full orphans if we may have had a - // power-loss, otherwise orphans are created intentionally - // during operations such as lfs2_mkdir - if (tag == LFS2_ERR_NOENT && powerloss) { - // we are an orphan - LFS2_DEBUG("Fixing orphan {0x%"PRIx32", 0x%"PRIx32"}", - pdir.tail[0], pdir.tail[1]); - - // steal state - err = lfs2_dir_getgstate(lfs2, &dir, &lfs2->gdelta); - if (err) { - return err; - } - - // steal tail - lfs2_pair_tole32(dir.tail); - int state = lfs2_dir_orphaningcommit(lfs2, &pdir, LFS2_MKATTRS( - {LFS2_MKTAG(LFS2_TYPE_TAIL + dir.split, 0x3ff, 8), - dir.tail})); - lfs2_pair_fromle32(dir.tail); - if (state < 0) { - return state; - } - - found += 1; - - // did our commit create more orphans? - if (state == LFS2_OK_ORPHANED) { - goto restart; - } - - // refetch tail - continue; - } - - if (tag != LFS2_ERR_NOENT) { + if (pass == 0 && tag != LFS2_ERR_NOENT) { lfs2_block_t pair[2]; lfs2_stag_t state = lfs2_dir_get(lfs2, &parent, LFS2_MKTAG(0x7ff, 0x3ff, 0), tag, pair); @@ -4548,7 +4892,7 @@ static int lfs2_fs_deorphan(lfs2_t *lfs2, bool powerloss) { } lfs2_pair_fromle32(pair); - if (!lfs2_pair_sync(pair, pdir.tail)) { + if (!lfs2_pair_issync(pair, pdir.tail)) { // we have desynced LFS2_DEBUG("Fixing half-orphan " "{0x%"PRIx32", 0x%"PRIx32"} " @@ -4578,33 +4922,69 @@ static int lfs2_fs_deorphan(lfs2_t *lfs2, bool powerloss) { return state; } - found += 1; - // did our commit create more orphans? if (state == LFS2_OK_ORPHANED) { - goto restart; + moreorphans = true; } // refetch tail continue; } } + + // note we only check for full orphans if we may have had a + // power-loss, otherwise orphans are created intentionally + // during operations such as lfs2_mkdir + if (pass == 1 && tag == LFS2_ERR_NOENT && powerloss) { + // we are an orphan + LFS2_DEBUG("Fixing orphan {0x%"PRIx32", 0x%"PRIx32"}", + pdir.tail[0], pdir.tail[1]); + + // steal state + err = lfs2_dir_getgstate(lfs2, &dir, &lfs2->gdelta); + if (err) { + return err; + } + + // steal tail + lfs2_pair_tole32(dir.tail); + int state = lfs2_dir_orphaningcommit(lfs2, &pdir, LFS2_MKATTRS( + {LFS2_MKTAG(LFS2_TYPE_TAIL + dir.split, 0x3ff, 8), + dir.tail})); + lfs2_pair_fromle32(dir.tail); + if (state < 0) { + return state; + } + + // did our commit create more orphans? + if (state == LFS2_OK_ORPHANED) { + moreorphans = true; + } + + // refetch tail + continue; + } } pdir = dir; } + + pass = moreorphans ? 0 : pass+1; } // mark orphans as fixed - return lfs2_fs_preporphans(lfs2, -lfs2_min( - lfs2_gstate_getorphans(&lfs2->gstate), - found)); + return lfs2_fs_preporphans(lfs2, -lfs2_gstate_getorphans(&lfs2->gstate)); } #endif #ifndef LFS2_READONLY static int lfs2_fs_forceconsistency(lfs2_t *lfs2) { - int err = lfs2_fs_demove(lfs2); + int err = lfs2_fs_desuperblock(lfs2); + if (err) { + return err; + } + + err = lfs2_fs_demove(lfs2); if (err) { return err; } @@ -4618,6 +4998,36 @@ static int lfs2_fs_forceconsistency(lfs2_t *lfs2) { } #endif +#ifndef LFS2_READONLY +static int lfs2_fs_rawmkconsistent(lfs2_t *lfs2) { + // lfs2_fs_forceconsistency does most of the work here + int err = lfs2_fs_forceconsistency(lfs2); + if (err) { + return err; + } + + // do we have any pending gstate? + lfs2_gstate_t delta = {0}; + lfs2_gstate_xor(&delta, &lfs2->gdisk); + lfs2_gstate_xor(&delta, &lfs2->gstate); + if (!lfs2_gstate_iszero(&delta)) { + // lfs2_dir_commit will implicitly write out any pending gstate + lfs2_mdir_t root; + err = lfs2_dir_fetch(lfs2, &root, lfs2->root); + if (err) { + return err; + } + + err = lfs2_dir_commit(lfs2, &root, NULL, 0); + if (err) { + return err; + } + } + + return 0; +} +#endif + static int lfs2_fs_size_count(void *p, lfs2_block_t block) { (void)block; lfs2_size_t *size = p; @@ -4635,6 +5045,45 @@ static lfs2_ssize_t lfs2_fs_rawsize(lfs2_t *lfs2) { return size; } +#ifndef LFS2_READONLY +static int lfs2_fs_rawgrow(lfs2_t *lfs2, lfs2_size_t block_count) { + // shrinking is not supported + LFS2_ASSERT(block_count >= lfs2->block_count); + + if (block_count > lfs2->block_count) { + lfs2->block_count = block_count; + + // fetch the root + lfs2_mdir_t root; + int err = lfs2_dir_fetch(lfs2, &root, lfs2->root); + if (err) { + return err; + } + + // update the superblock + lfs2_superblock_t superblock; + lfs2_stag_t tag = lfs2_dir_get(lfs2, &root, LFS2_MKTAG(0x7ff, 0x3ff, 0), + LFS2_MKTAG(LFS2_TYPE_INLINESTRUCT, 0, sizeof(superblock)), + &superblock); + if (tag < 0) { + return tag; + } + lfs2_superblock_fromle32(&superblock); + + superblock.block_count = lfs2->block_count; + + lfs2_superblock_tole32(&superblock); + err = lfs2_dir_commit(lfs2, &root, LFS2_MKATTRS( + {tag, &superblock})); + if (err) { + return err; + } + } + + return 0; +} +#endif + #ifdef LFS2_MIGRATE ////// Migration from littelfs v1 below this ////// @@ -5058,6 +5507,10 @@ static int lfs21_unmount(lfs2_t *lfs2) { /// v1 migration /// static int lfs2_rawmigrate(lfs2_t *lfs2, const struct lfs2_config *cfg) { struct lfs21 lfs21; + + // Indeterminate filesystem size not allowed for migration. + LFS2_ASSERT(cfg->block_count != 0); + int err = lfs21_mount(lfs2, &lfs21, cfg); if (err) { return err; @@ -5754,6 +6207,20 @@ int lfs2_dir_rewind(lfs2_t *lfs2, lfs2_dir_t *dir) { return err; } +int lfs2_fs_stat(lfs2_t *lfs2, struct lfs2_fsinfo *fsinfo) { + int err = LFS2_LOCK(lfs2->cfg); + if (err) { + return err; + } + LFS2_TRACE("lfs2_fs_stat(%p, %p)", (void*)lfs2, (void*)fsinfo); + + err = lfs2_fs_rawstat(lfs2, fsinfo); + + LFS2_TRACE("lfs2_fs_stat -> %d", err); + LFS2_UNLOCK(lfs2->cfg); + return err; +} + lfs2_ssize_t lfs2_fs_size(lfs2_t *lfs2) { int err = LFS2_LOCK(lfs2->cfg); if (err) { @@ -5783,6 +6250,54 @@ int lfs2_fs_traverse(lfs2_t *lfs2, int (*cb)(void *, lfs2_block_t), void *data) return err; } +#ifndef LFS2_READONLY +int lfs2_fs_gc(lfs2_t *lfs2) { + int err = LFS2_LOCK(lfs2->cfg); + if (err) { + return err; + } + LFS2_TRACE("lfs2_fs_gc(%p)", (void*)lfs2); + + err = lfs2_fs_rawgc(lfs2); + + LFS2_TRACE("lfs2_fs_gc -> %d", err); + LFS2_UNLOCK(lfs2->cfg); + return err; +} +#endif + +#ifndef LFS2_READONLY +int lfs2_fs_mkconsistent(lfs2_t *lfs2) { + int err = LFS2_LOCK(lfs2->cfg); + if (err) { + return err; + } + LFS2_TRACE("lfs2_fs_mkconsistent(%p)", (void*)lfs2); + + err = lfs2_fs_rawmkconsistent(lfs2); + + LFS2_TRACE("lfs2_fs_mkconsistent -> %d", err); + LFS2_UNLOCK(lfs2->cfg); + return err; +} +#endif + +#ifndef LFS2_READONLY +int lfs2_fs_grow(lfs2_t *lfs2, lfs2_size_t block_count) { + int err = LFS2_LOCK(lfs2->cfg); + if (err) { + return err; + } + LFS2_TRACE("lfs2_fs_grow(%p, %"PRIu32")", (void*)lfs2, block_count); + + err = lfs2_fs_rawgrow(lfs2, block_count); + + LFS2_TRACE("lfs2_fs_grow -> %d", err); + LFS2_UNLOCK(lfs2->cfg); + return err; +} +#endif + #ifdef LFS2_MIGRATE int lfs2_migrate(lfs2_t *lfs2, const struct lfs2_config *cfg) { int err = LFS2_LOCK(cfg); diff --git a/lib/littlefs/lfs2.h b/lib/littlefs/lfs2.h index 715764f7ce98..559ccebac926 100644 --- a/lib/littlefs/lfs2.h +++ b/lib/littlefs/lfs2.h @@ -8,8 +8,6 @@ #ifndef LFS2_H #define LFS2_H -#include -#include #include "lfs2_util.h" #ifdef __cplusplus @@ -23,14 +21,14 @@ extern "C" // Software library version // Major (top-nibble), incremented on backwards incompatible changes // Minor (bottom-nibble), incremented on feature additions -#define LFS2_VERSION 0x00020005 +#define LFS2_VERSION 0x00020008 #define LFS2_VERSION_MAJOR (0xffff & (LFS2_VERSION >> 16)) #define LFS2_VERSION_MINOR (0xffff & (LFS2_VERSION >> 0)) // Version of On-disk data structures // Major (top-nibble), incremented on backwards incompatible changes // Minor (bottom-nibble), incremented on feature additions -#define LFS2_DISK_VERSION 0x00020000 +#define LFS2_DISK_VERSION 0x00020001 #define LFS2_DISK_VERSION_MAJOR (0xffff & (LFS2_DISK_VERSION >> 16)) #define LFS2_DISK_VERSION_MINOR (0xffff & (LFS2_DISK_VERSION >> 0)) @@ -114,6 +112,8 @@ enum lfs2_type { LFS2_TYPE_SOFTTAIL = 0x600, LFS2_TYPE_HARDTAIL = 0x601, LFS2_TYPE_MOVESTATE = 0x7ff, + LFS2_TYPE_CCRC = 0x500, + LFS2_TYPE_FCRC = 0x5ff, // internal chip sources LFS2_FROM_NOOP = 0x000, @@ -263,6 +263,14 @@ struct lfs2_config { // can help bound the metadata compaction time. Must be <= block_size. // Defaults to block_size when zero. lfs2_size_t metadata_max; + +#ifdef LFS2_MULTIVERSION + // On-disk version to use when writing in the form of 16-bit major version + // + 16-bit minor version. This limiting metadata to what is supported by + // older minor versions. Note that some features will be lost. Defaults to + // to the most recent minor version when zero. + uint32_t disk_version; +#endif }; // File info structure @@ -280,6 +288,27 @@ struct lfs2_info { char name[LFS2_NAME_MAX+1]; }; +// Filesystem info structure +struct lfs2_fsinfo { + // On-disk version. + uint32_t disk_version; + + // Size of a logical block in bytes. + lfs2_size_t block_size; + + // Number of logical blocks in filesystem. + lfs2_size_t block_count; + + // Upper limit on the length of file names in bytes. + lfs2_size_t name_max; + + // Upper limit on the size of files in bytes. + lfs2_size_t file_max; + + // Upper limit on the size of custom attributes in bytes. + lfs2_size_t attr_max; +}; + // Custom attribute structure, used to describe custom attributes // committed atomically during file writes. struct lfs2_attr { @@ -410,6 +439,7 @@ typedef struct lfs2 { } free; const struct lfs2_config *cfg; + lfs2_size_t block_count; lfs2_size_t name_max; lfs2_size_t file_max; lfs2_size_t attr_max; @@ -534,8 +564,8 @@ int lfs2_file_open(lfs2_t *lfs2, lfs2_file_t *file, // are values from the enum lfs2_open_flags that are bitwise-ored together. // // The config struct provides additional config options per file as described -// above. The config struct must be allocated while the file is open, and the -// config struct must be zeroed for defaults and backwards compatibility. +// above. The config struct must remain allocated while the file is open, and +// the config struct must be zeroed for defaults and backwards compatibility. // // Returns a negative error code on failure. int lfs2_file_opencfg(lfs2_t *lfs2, lfs2_file_t *file, @@ -659,6 +689,12 @@ int lfs2_dir_rewind(lfs2_t *lfs2, lfs2_dir_t *dir); /// Filesystem-level filesystem operations +// Find on-disk info about the filesystem +// +// Fills out the fsinfo structure based on the filesystem found on-disk. +// Returns a negative error code on failure. +int lfs2_fs_stat(lfs2_t *lfs2, struct lfs2_fsinfo *fsinfo); + // Finds the current size of the filesystem // // Note: Result is best effort. If files share COW structures, the returned @@ -676,6 +712,40 @@ lfs2_ssize_t lfs2_fs_size(lfs2_t *lfs2); // Returns a negative error code on failure. int lfs2_fs_traverse(lfs2_t *lfs2, int (*cb)(void*, lfs2_block_t), void *data); +// Attempt to proactively find free blocks +// +// Calling this function is not required, but may allowing the offloading of +// the expensive block allocation scan to a less time-critical code path. +// +// Note: littlefs currently does not persist any found free blocks to disk. +// This may change in the future. +// +// Returns a negative error code on failure. Finding no free blocks is +// not an error. +int lfs2_fs_gc(lfs2_t *lfs2); + +#ifndef LFS2_READONLY +// Attempt to make the filesystem consistent and ready for writing +// +// Calling this function is not required, consistency will be implicitly +// enforced on the first operation that writes to the filesystem, but this +// function allows the work to be performed earlier and without other +// filesystem changes. +// +// Returns a negative error code on failure. +int lfs2_fs_mkconsistent(lfs2_t *lfs2); +#endif + +#ifndef LFS2_READONLY +// Grows the filesystem to a new size, updating the superblock with the new +// block count. +// +// Note: This is irreversible. +// +// Returns a negative error code on failure. +int lfs2_fs_grow(lfs2_t *lfs2, lfs2_size_t block_count); +#endif + #ifndef LFS2_READONLY #ifdef LFS2_MIGRATE // Attempts to migrate a previous version of littlefs diff --git a/lib/littlefs/lfs2_util.h b/lib/littlefs/lfs2_util.h index 6a4c8ffb5047..dd2cbcc106d8 100644 --- a/lib/littlefs/lfs2_util.h +++ b/lib/littlefs/lfs2_util.h @@ -167,10 +167,9 @@ static inline int lfs2_scmp(uint32_t a, uint32_t b) { // Convert between 32-bit little-endian and native order static inline uint32_t lfs2_fromle32(uint32_t a) { -#if !defined(LFS2_NO_INTRINSICS) && ( \ - (defined( BYTE_ORDER ) && defined( ORDER_LITTLE_ENDIAN ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \ +#if (defined( BYTE_ORDER ) && defined( ORDER_LITTLE_ENDIAN ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \ (defined(__BYTE_ORDER ) && defined(__ORDER_LITTLE_ENDIAN ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \ - (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) + (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) return a; #elif !defined(LFS2_NO_INTRINSICS) && ( \ (defined( BYTE_ORDER ) && defined( ORDER_BIG_ENDIAN ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \ @@ -196,10 +195,9 @@ static inline uint32_t lfs2_frombe32(uint32_t a) { (defined(__BYTE_ORDER ) && defined(__ORDER_LITTLE_ENDIAN ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \ (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) return __builtin_bswap32(a); -#elif !defined(LFS2_NO_INTRINSICS) && ( \ - (defined( BYTE_ORDER ) && defined( ORDER_BIG_ENDIAN ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \ +#elif (defined( BYTE_ORDER ) && defined( ORDER_BIG_ENDIAN ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \ (defined(__BYTE_ORDER ) && defined(__ORDER_BIG_ENDIAN ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \ - (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) + (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) return a; #else return (((uint8_t*)&a)[0] << 24) | diff --git a/lib/mbedtls_errors/esp32_mbedtls_errors.c b/lib/mbedtls_errors/esp32_mbedtls_errors.c index c56f8a19ffb1..5d89d15b4dae 100644 --- a/lib/mbedtls_errors/esp32_mbedtls_errors.c +++ b/lib/mbedtls_errors/esp32_mbedtls_errors.c @@ -45,10 +45,6 @@ #include "mbedtls/aes.h" #endif -#if defined(MBEDTLS_ARC4_C) -#include "mbedtls/arc4.h" -#endif - #if defined(MBEDTLS_ARIA_C) #include "mbedtls/aria.h" #endif @@ -65,10 +61,6 @@ #include "mbedtls/bignum.h" #endif -#if defined(MBEDTLS_BLOWFISH_C) -#include "mbedtls/blowfish.h" -#endif - #if defined(MBEDTLS_CAMELLIA_C) #include "mbedtls/camellia.h" #endif @@ -89,10 +81,6 @@ #include "mbedtls/cipher.h" #endif -#if defined(MBEDTLS_CMAC_C) -#include "mbedtls/cmac.h" -#endif - #if defined(MBEDTLS_CTR_DRBG_C) #include "mbedtls/ctr_drbg.h" #endif @@ -113,6 +101,14 @@ #include "mbedtls/entropy.h" #endif +#if defined(MBEDTLS_ERROR_C) +#include "mbedtls/error.h" +#endif + +#if defined(MBEDTLS_PLATFORM_C) +#include "mbedtls/platform.h" +#endif + #if defined(MBEDTLS_GCM_C) #include "mbedtls/gcm.h" #endif @@ -125,20 +121,12 @@ #include "mbedtls/hmac_drbg.h" #endif -#if defined(MBEDTLS_MD_C) -#include "mbedtls/md.h" -#endif - -#if defined(MBEDTLS_MD2_C) -#include "mbedtls/md2.h" +#if defined(MBEDTLS_LMS_C) +#include "mbedtls/lms.h" #endif -#if defined(MBEDTLS_MD4_C) -#include "mbedtls/md4.h" -#endif - -#if defined(MBEDTLS_MD5_C) -#include "mbedtls/md5.h" +#if defined(MBEDTLS_MD_C) +#include "mbedtls/md.h" #endif #if defined(MBEDTLS_NET_C) @@ -149,10 +137,6 @@ #include "mbedtls/oid.h" #endif -#if defined(MBEDTLS_PADLOCK_C) -#include "mbedtls/padlock.h" -#endif - #if defined(MBEDTLS_PEM_PARSE_C) || defined(MBEDTLS_PEM_WRITE_C) #include "mbedtls/pem.h" #endif @@ -169,18 +153,14 @@ #include "mbedtls/pkcs5.h" #endif -#if defined(MBEDTLS_PLATFORM_C) -#include "mbedtls/platform.h" +#if defined(MBEDTLS_PKCS7_C) +#include "mbedtls/pkcs7.h" #endif #if defined(MBEDTLS_POLY1305_C) #include "mbedtls/poly1305.h" #endif -#if defined(MBEDTLS_RIPEMD160_C) -#include "mbedtls/ripemd160.h" -#endif - #if defined(MBEDTLS_RSA_C) #include "mbedtls/rsa.h" #endif @@ -209,10 +189,6 @@ #include "mbedtls/x509.h" #endif -#if defined(MBEDTLS_XTEA_C) -#include "mbedtls/xtea.h" -#endif - // Error code table type struct ssl_errs { @@ -231,7 +207,6 @@ static const struct ssl_errs mbedtls_high_level_error_tab[] = { { -(MBEDTLS_ERR_CIPHER_FULL_BLOCK_EXPECTED), "CIPHER_FULL_BLOCK_EXPECTED" }, { -(MBEDTLS_ERR_CIPHER_AUTH_FAILED), "CIPHER_AUTH_FAILED" }, { -(MBEDTLS_ERR_CIPHER_INVALID_CONTEXT), "CIPHER_INVALID_CONTEXT" }, - { -(MBEDTLS_ERR_CIPHER_HW_ACCEL_FAILED), "CIPHER_HW_ACCEL_FAILED" }, #endif /* MBEDTLS_CIPHER_C */ #if defined(MBEDTLS_DHM_C) @@ -244,7 +219,6 @@ static const struct ssl_errs mbedtls_high_level_error_tab[] = { { -(MBEDTLS_ERR_DHM_INVALID_FORMAT), "DHM_INVALID_FORMAT" }, { -(MBEDTLS_ERR_DHM_ALLOC_FAILED), "DHM_ALLOC_FAILED" }, { -(MBEDTLS_ERR_DHM_FILE_IO_ERROR), "DHM_FILE_IO_ERROR" }, - { -(MBEDTLS_ERR_DHM_HW_ACCEL_FAILED), "DHM_HW_ACCEL_FAILED" }, { -(MBEDTLS_ERR_DHM_SET_GROUP_FAILED), "DHM_SET_GROUP_FAILED" }, #endif /* MBEDTLS_DHM_C */ @@ -257,7 +231,6 @@ static const struct ssl_errs mbedtls_high_level_error_tab[] = { { -(MBEDTLS_ERR_ECP_RANDOM_FAILED), "ECP_RANDOM_FAILED" }, { -(MBEDTLS_ERR_ECP_INVALID_KEY), "ECP_INVALID_KEY" }, { -(MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH), "ECP_SIG_LEN_MISMATCH" }, - { -(MBEDTLS_ERR_ECP_HW_ACCEL_FAILED), "ECP_HW_ACCEL_FAILED" }, { -(MBEDTLS_ERR_ECP_IN_PROGRESS), "ECP_IN_PROGRESS" }, #endif /* MBEDTLS_ECP_C */ @@ -266,7 +239,6 @@ static const struct ssl_errs mbedtls_high_level_error_tab[] = { { -(MBEDTLS_ERR_MD_BAD_INPUT_DATA), "MD_BAD_INPUT_DATA" }, { -(MBEDTLS_ERR_MD_ALLOC_FAILED), "MD_ALLOC_FAILED" }, { -(MBEDTLS_ERR_MD_FILE_IO_ERROR), "MD_FILE_IO_ERROR" }, - { -(MBEDTLS_ERR_MD_HW_ACCEL_FAILED), "MD_HW_ACCEL_FAILED" }, #endif /* MBEDTLS_MD_C */ #if defined(MBEDTLS_PEM_PARSE_C) || defined(MBEDTLS_PEM_WRITE_C) @@ -296,7 +268,7 @@ static const struct ssl_errs mbedtls_high_level_error_tab[] = { { -(MBEDTLS_ERR_PK_UNKNOWN_NAMED_CURVE), "PK_UNKNOWN_NAMED_CURVE" }, { -(MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE), "PK_FEATURE_UNAVAILABLE" }, { -(MBEDTLS_ERR_PK_SIG_LEN_MISMATCH), "PK_SIG_LEN_MISMATCH" }, - { -(MBEDTLS_ERR_PK_HW_ACCEL_FAILED), "PK_HW_ACCEL_FAILED" }, + { -(MBEDTLS_ERR_PK_BUFFER_TOO_SMALL), "PK_BUFFER_TOO_SMALL" }, #endif /* MBEDTLS_PK_C */ #if defined(MBEDTLS_PKCS12_C) @@ -313,6 +285,21 @@ static const struct ssl_errs mbedtls_high_level_error_tab[] = { { -(MBEDTLS_ERR_PKCS5_PASSWORD_MISMATCH), "PKCS5_PASSWORD_MISMATCH" }, #endif /* MBEDTLS_PKCS5_C */ +#if defined(MBEDTLS_PKCS7_C) + { -(MBEDTLS_ERR_PKCS7_INVALID_FORMAT), "PKCS7_INVALID_FORMAT" }, + { -(MBEDTLS_ERR_PKCS7_FEATURE_UNAVAILABLE), "PKCS7_FEATURE_UNAVAILABLE" }, + { -(MBEDTLS_ERR_PKCS7_INVALID_VERSION), "PKCS7_INVALID_VERSION" }, + { -(MBEDTLS_ERR_PKCS7_INVALID_CONTENT_INFO), "PKCS7_INVALID_CONTENT_INFO" }, + { -(MBEDTLS_ERR_PKCS7_INVALID_ALG), "PKCS7_INVALID_ALG" }, + { -(MBEDTLS_ERR_PKCS7_INVALID_CERT), "PKCS7_INVALID_CERT" }, + { -(MBEDTLS_ERR_PKCS7_INVALID_SIGNATURE), "PKCS7_INVALID_SIGNATURE" }, + { -(MBEDTLS_ERR_PKCS7_INVALID_SIGNER_INFO), "PKCS7_INVALID_SIGNER_INFO" }, + { -(MBEDTLS_ERR_PKCS7_BAD_INPUT_DATA), "PKCS7_BAD_INPUT_DATA" }, + { -(MBEDTLS_ERR_PKCS7_ALLOC_FAILED), "PKCS7_ALLOC_FAILED" }, + { -(MBEDTLS_ERR_PKCS7_VERIFY_FAIL), "PKCS7_VERIFY_FAIL" }, + { -(MBEDTLS_ERR_PKCS7_CERT_DATE_INVALID), "PKCS7_CERT_DATE_INVALID" }, +#endif /* MBEDTLS_PKCS7_C */ + #if defined(MBEDTLS_RSA_C) { -(MBEDTLS_ERR_RSA_BAD_INPUT_DATA), "RSA_BAD_INPUT_DATA" }, { -(MBEDTLS_ERR_RSA_INVALID_PADDING), "RSA_INVALID_PADDING" }, @@ -323,45 +310,34 @@ static const struct ssl_errs mbedtls_high_level_error_tab[] = { { -(MBEDTLS_ERR_RSA_VERIFY_FAILED), "RSA_VERIFY_FAILED" }, { -(MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE), "RSA_OUTPUT_TOO_LARGE" }, { -(MBEDTLS_ERR_RSA_RNG_FAILED), "RSA_RNG_FAILED" }, - { -(MBEDTLS_ERR_RSA_UNSUPPORTED_OPERATION), "RSA_UNSUPPORTED_OPERATION" }, - { -(MBEDTLS_ERR_RSA_HW_ACCEL_FAILED), "RSA_HW_ACCEL_FAILED" }, #endif /* MBEDTLS_RSA_C */ #if defined(MBEDTLS_SSL_TLS_C) + { -(MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS), "SSL_CRYPTO_IN_PROGRESS" }, { -(MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE), "SSL_FEATURE_UNAVAILABLE" }, { -(MBEDTLS_ERR_SSL_BAD_INPUT_DATA), "SSL_BAD_INPUT_DATA" }, { -(MBEDTLS_ERR_SSL_INVALID_MAC), "SSL_INVALID_MAC" }, { -(MBEDTLS_ERR_SSL_INVALID_RECORD), "SSL_INVALID_RECORD" }, { -(MBEDTLS_ERR_SSL_CONN_EOF), "SSL_CONN_EOF" }, - { -(MBEDTLS_ERR_SSL_UNKNOWN_CIPHER), "SSL_UNKNOWN_CIPHER" }, - { -(MBEDTLS_ERR_SSL_NO_CIPHER_CHOSEN), "SSL_NO_CIPHER_CHOSEN" }, + { -(MBEDTLS_ERR_SSL_DECODE_ERROR), "SSL_DECODE_ERROR" }, { -(MBEDTLS_ERR_SSL_NO_RNG), "SSL_NO_RNG" }, { -(MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE), "SSL_NO_CLIENT_CERTIFICATE" }, - { -(MBEDTLS_ERR_SSL_CERTIFICATE_TOO_LARGE), "SSL_CERTIFICATE_TOO_LARGE" }, - { -(MBEDTLS_ERR_SSL_CERTIFICATE_REQUIRED), "SSL_CERTIFICATE_REQUIRED" }, + { -(MBEDTLS_ERR_SSL_UNSUPPORTED_EXTENSION), "SSL_UNSUPPORTED_EXTENSION" }, + { -(MBEDTLS_ERR_SSL_NO_APPLICATION_PROTOCOL), "SSL_NO_APPLICATION_PROTOCOL" }, { -(MBEDTLS_ERR_SSL_PRIVATE_KEY_REQUIRED), "SSL_PRIVATE_KEY_REQUIRED" }, { -(MBEDTLS_ERR_SSL_CA_CHAIN_REQUIRED), "SSL_CA_CHAIN_REQUIRED" }, { -(MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE), "SSL_UNEXPECTED_MESSAGE" }, - { -(MBEDTLS_ERR_SSL_PEER_VERIFY_FAILED), "SSL_PEER_VERIFY_FAILED" }, + { -(MBEDTLS_ERR_SSL_UNRECOGNIZED_NAME), "SSL_UNRECOGNIZED_NAME" }, { -(MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY), "SSL_PEER_CLOSE_NOTIFY" }, - { -(MBEDTLS_ERR_SSL_BAD_HS_CLIENT_HELLO), "SSL_BAD_HS_CLIENT_HELLO" }, - { -(MBEDTLS_ERR_SSL_BAD_HS_SERVER_HELLO), "SSL_BAD_HS_SERVER_HELLO" }, - { -(MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE), "SSL_BAD_HS_CERTIFICATE" }, - { -(MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_REQUEST), "SSL_BAD_HS_CERTIFICATE_REQUEST" }, - { -(MBEDTLS_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE), "SSL_BAD_HS_SERVER_KEY_EXCHANGE" }, - { -(MBEDTLS_ERR_SSL_BAD_HS_SERVER_HELLO_DONE), "SSL_BAD_HS_SERVER_HELLO_DONE" }, - { -(MBEDTLS_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE), "SSL_BAD_HS_CLIENT_KEY_EXCHANGE" }, - { -(MBEDTLS_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE_RP), "SSL_BAD_HS_CLIENT_KEY_EXCHANGE_RP" }, - { -(MBEDTLS_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE_CS), "SSL_BAD_HS_CLIENT_KEY_EXCHANGE_CS" }, - { -(MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY), "SSL_BAD_HS_CERTIFICATE_VERIFY" }, - { -(MBEDTLS_ERR_SSL_BAD_HS_CHANGE_CIPHER_SPEC), "SSL_BAD_HS_CHANGE_CIPHER_SPEC" }, - { -(MBEDTLS_ERR_SSL_BAD_HS_FINISHED), "SSL_BAD_HS_FINISHED" }, + { -(MBEDTLS_ERR_SSL_BAD_CERTIFICATE), "SSL_BAD_CERTIFICATE" }, + { -(MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET), "SSL_RECEIVED_NEW_SESSION_TICKET" }, + { -(MBEDTLS_ERR_SSL_CANNOT_READ_EARLY_DATA), "SSL_CANNOT_READ_EARLY_DATA" }, + { -(MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA), "SSL_CANNOT_WRITE_EARLY_DATA" }, { -(MBEDTLS_ERR_SSL_ALLOC_FAILED), "SSL_ALLOC_FAILED" }, { -(MBEDTLS_ERR_SSL_HW_ACCEL_FAILED), "SSL_HW_ACCEL_FAILED" }, { -(MBEDTLS_ERR_SSL_HW_ACCEL_FALLTHROUGH), "SSL_HW_ACCEL_FALLTHROUGH" }, - { -(MBEDTLS_ERR_SSL_COMPRESSION_FAILED), "SSL_COMPRESSION_FAILED" }, - { -(MBEDTLS_ERR_SSL_BAD_HS_PROTOCOL_VERSION), "SSL_BAD_HS_PROTOCOL_VERSION" }, - { -(MBEDTLS_ERR_SSL_BAD_HS_NEW_SESSION_TICKET), "SSL_BAD_HS_NEW_SESSION_TICKET" }, + { -(MBEDTLS_ERR_SSL_BAD_PROTOCOL_VERSION), "SSL_BAD_PROTOCOL_VERSION" }, + { -(MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE), "SSL_HANDSHAKE_FAILURE" }, { -(MBEDTLS_ERR_SSL_SESSION_TICKET_EXPIRED), "SSL_SESSION_TICKET_EXPIRED" }, { -(MBEDTLS_ERR_SSL_PK_TYPE_MISMATCH), "SSL_PK_TYPE_MISMATCH" }, { -(MBEDTLS_ERR_SSL_UNKNOWN_IDENTITY), "SSL_UNKNOWN_IDENTITY" }, @@ -370,18 +346,18 @@ static const struct ssl_errs mbedtls_high_level_error_tab[] = { { -(MBEDTLS_ERR_SSL_WAITING_SERVER_HELLO_RENEGO), "SSL_WAITING_SERVER_HELLO_RENEGO" }, { -(MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED), "SSL_HELLO_VERIFY_REQUIRED" }, { -(MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL), "SSL_BUFFER_TOO_SMALL" }, - { -(MBEDTLS_ERR_SSL_NO_USABLE_CIPHERSUITE), "SSL_NO_USABLE_CIPHERSUITE" }, { -(MBEDTLS_ERR_SSL_WANT_READ), "SSL_WANT_READ" }, { -(MBEDTLS_ERR_SSL_WANT_WRITE), "SSL_WANT_WRITE" }, { -(MBEDTLS_ERR_SSL_TIMEOUT), "SSL_TIMEOUT" }, { -(MBEDTLS_ERR_SSL_CLIENT_RECONNECT), "SSL_CLIENT_RECONNECT" }, { -(MBEDTLS_ERR_SSL_UNEXPECTED_RECORD), "SSL_UNEXPECTED_RECORD" }, { -(MBEDTLS_ERR_SSL_NON_FATAL), "SSL_NON_FATAL" }, - { -(MBEDTLS_ERR_SSL_INVALID_VERIFY_HASH), "SSL_INVALID_VERIFY_HASH" }, + { -(MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER), "SSL_ILLEGAL_PARAMETER" }, { -(MBEDTLS_ERR_SSL_CONTINUE_PROCESSING), "SSL_CONTINUE_PROCESSING" }, { -(MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS), "SSL_ASYNC_IN_PROGRESS" }, { -(MBEDTLS_ERR_SSL_EARLY_MESSAGE), "SSL_EARLY_MESSAGE" }, - { -(MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS), "SSL_CRYPTO_IN_PROGRESS" }, + { -(MBEDTLS_ERR_SSL_UNEXPECTED_CID), "SSL_UNEXPECTED_CID" }, + { -(MBEDTLS_ERR_SSL_VERSION_MISMATCH), "SSL_VERSION_MISMATCH" }, { -(MBEDTLS_ERR_SSL_BAD_CONFIG), "SSL_BAD_CONFIG" }, #endif /* MBEDTLS_SSL_TLS_C */ @@ -418,19 +394,11 @@ static const struct ssl_errs mbedtls_low_level_error_tab[] = { { -(MBEDTLS_ERR_AES_INVALID_KEY_LENGTH), "AES_INVALID_KEY_LENGTH" }, { -(MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH), "AES_INVALID_INPUT_LENGTH" }, { -(MBEDTLS_ERR_AES_BAD_INPUT_DATA), "AES_BAD_INPUT_DATA" }, - { -(MBEDTLS_ERR_AES_FEATURE_UNAVAILABLE), "AES_FEATURE_UNAVAILABLE" }, - { -(MBEDTLS_ERR_AES_HW_ACCEL_FAILED), "AES_HW_ACCEL_FAILED" }, #endif /* MBEDTLS_AES_C */ -#if defined(MBEDTLS_ARC4_C) - { -(MBEDTLS_ERR_ARC4_HW_ACCEL_FAILED), "ARC4_HW_ACCEL_FAILED" }, -#endif /* MBEDTLS_ARC4_C */ - #if defined(MBEDTLS_ARIA_C) { -(MBEDTLS_ERR_ARIA_BAD_INPUT_DATA), "ARIA_BAD_INPUT_DATA" }, { -(MBEDTLS_ERR_ARIA_INVALID_INPUT_LENGTH), "ARIA_INVALID_INPUT_LENGTH" }, - { -(MBEDTLS_ERR_ARIA_FEATURE_UNAVAILABLE), "ARIA_FEATURE_UNAVAILABLE" }, - { -(MBEDTLS_ERR_ARIA_HW_ACCEL_FAILED), "ARIA_HW_ACCEL_FAILED" }, #endif /* MBEDTLS_ARIA_C */ #if defined(MBEDTLS_ASN1_PARSE_C) @@ -459,28 +427,18 @@ static const struct ssl_errs mbedtls_low_level_error_tab[] = { { -(MBEDTLS_ERR_MPI_ALLOC_FAILED), "MPI_ALLOC_FAILED" }, #endif /* MBEDTLS_BIGNUM_C */ -#if defined(MBEDTLS_BLOWFISH_C) - { -(MBEDTLS_ERR_BLOWFISH_BAD_INPUT_DATA), "BLOWFISH_BAD_INPUT_DATA" }, - { -(MBEDTLS_ERR_BLOWFISH_INVALID_INPUT_LENGTH), "BLOWFISH_INVALID_INPUT_LENGTH" }, - { -(MBEDTLS_ERR_BLOWFISH_HW_ACCEL_FAILED), "BLOWFISH_HW_ACCEL_FAILED" }, -#endif /* MBEDTLS_BLOWFISH_C */ - #if defined(MBEDTLS_CAMELLIA_C) { -(MBEDTLS_ERR_CAMELLIA_BAD_INPUT_DATA), "CAMELLIA_BAD_INPUT_DATA" }, { -(MBEDTLS_ERR_CAMELLIA_INVALID_INPUT_LENGTH), "CAMELLIA_INVALID_INPUT_LENGTH" }, - { -(MBEDTLS_ERR_CAMELLIA_HW_ACCEL_FAILED), "CAMELLIA_HW_ACCEL_FAILED" }, #endif /* MBEDTLS_CAMELLIA_C */ #if defined(MBEDTLS_CCM_C) { -(MBEDTLS_ERR_CCM_BAD_INPUT), "CCM_BAD_INPUT" }, { -(MBEDTLS_ERR_CCM_AUTH_FAILED), "CCM_AUTH_FAILED" }, - { -(MBEDTLS_ERR_CCM_HW_ACCEL_FAILED), "CCM_HW_ACCEL_FAILED" }, #endif /* MBEDTLS_CCM_C */ #if defined(MBEDTLS_CHACHA20_C) { -(MBEDTLS_ERR_CHACHA20_BAD_INPUT_DATA), "CHACHA20_BAD_INPUT_DATA" }, - { -(MBEDTLS_ERR_CHACHA20_FEATURE_UNAVAILABLE), "CHACHA20_FEATURE_UNAVAILABLE" }, - { -(MBEDTLS_ERR_CHACHA20_HW_ACCEL_FAILED), "CHACHA20_HW_ACCEL_FAILED" }, #endif /* MBEDTLS_CHACHA20_C */ #if defined(MBEDTLS_CHACHAPOLY_C) @@ -488,10 +446,6 @@ static const struct ssl_errs mbedtls_low_level_error_tab[] = { { -(MBEDTLS_ERR_CHACHAPOLY_AUTH_FAILED), "CHACHAPOLY_AUTH_FAILED" }, #endif /* MBEDTLS_CHACHAPOLY_C */ -#if defined(MBEDTLS_CMAC_C) - { -(MBEDTLS_ERR_CMAC_HW_ACCEL_FAILED), "CMAC_HW_ACCEL_FAILED" }, -#endif /* MBEDTLS_CMAC_C */ - #if defined(MBEDTLS_CTR_DRBG_C) { -(MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED), "CTR_DRBG_ENTROPY_SOURCE_FAILED" }, { -(MBEDTLS_ERR_CTR_DRBG_REQUEST_TOO_BIG), "CTR_DRBG_REQUEST_TOO_BIG" }, @@ -501,7 +455,6 @@ static const struct ssl_errs mbedtls_low_level_error_tab[] = { #if defined(MBEDTLS_DES_C) { -(MBEDTLS_ERR_DES_INVALID_INPUT_LENGTH), "DES_INVALID_INPUT_LENGTH" }, - { -(MBEDTLS_ERR_DES_HW_ACCEL_FAILED), "DES_HW_ACCEL_FAILED" }, #endif /* MBEDTLS_DES_C */ #if defined(MBEDTLS_ENTROPY_C) @@ -512,10 +465,20 @@ static const struct ssl_errs mbedtls_low_level_error_tab[] = { { -(MBEDTLS_ERR_ENTROPY_FILE_IO_ERROR), "ENTROPY_FILE_IO_ERROR" }, #endif /* MBEDTLS_ENTROPY_C */ +#if defined(MBEDTLS_ERROR_C) + { -(MBEDTLS_ERR_ERROR_GENERIC_ERROR), "ERROR_GENERIC_ERROR" }, + { -(MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED), "ERROR_CORRUPTION_DETECTED" }, +#endif /* MBEDTLS_ERROR_C */ + +#if defined(MBEDTLS_PLATFORM_C) + { -(MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED), "PLATFORM_HW_ACCEL_FAILED" }, + { -(MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED), "PLATFORM_FEATURE_UNSUPPORTED" }, +#endif /* MBEDTLS_PLATFORM_C */ + #if defined(MBEDTLS_GCM_C) { -(MBEDTLS_ERR_GCM_AUTH_FAILED), "GCM_AUTH_FAILED" }, - { -(MBEDTLS_ERR_GCM_HW_ACCEL_FAILED), "GCM_HW_ACCEL_FAILED" }, { -(MBEDTLS_ERR_GCM_BAD_INPUT), "GCM_BAD_INPUT" }, + { -(MBEDTLS_ERR_GCM_BUFFER_TOO_SMALL), "GCM_BUFFER_TOO_SMALL" }, #endif /* MBEDTLS_GCM_C */ #if defined(MBEDTLS_HKDF_C) @@ -529,17 +492,13 @@ static const struct ssl_errs mbedtls_low_level_error_tab[] = { { -(MBEDTLS_ERR_HMAC_DRBG_ENTROPY_SOURCE_FAILED), "HMAC_DRBG_ENTROPY_SOURCE_FAILED" }, #endif /* MBEDTLS_HMAC_DRBG_C */ -#if defined(MBEDTLS_MD2_C) - { -(MBEDTLS_ERR_MD2_HW_ACCEL_FAILED), "MD2_HW_ACCEL_FAILED" }, -#endif /* MBEDTLS_MD2_C */ - -#if defined(MBEDTLS_MD4_C) - { -(MBEDTLS_ERR_MD4_HW_ACCEL_FAILED), "MD4_HW_ACCEL_FAILED" }, -#endif /* MBEDTLS_MD4_C */ - -#if defined(MBEDTLS_MD5_C) - { -(MBEDTLS_ERR_MD5_HW_ACCEL_FAILED), "MD5_HW_ACCEL_FAILED" }, -#endif /* MBEDTLS_MD5_C */ +#if defined(MBEDTLS_LMS_C) + { -(MBEDTLS_ERR_LMS_BAD_INPUT_DATA), "LMS_BAD_INPUT_DATA" }, + { -(MBEDTLS_ERR_LMS_OUT_OF_PRIVATE_KEYS), "LMS_OUT_OF_PRIVATE_KEYS" }, + { -(MBEDTLS_ERR_LMS_VERIFY_FAILED), "LMS_VERIFY_FAILED" }, + { -(MBEDTLS_ERR_LMS_ALLOC_FAILED), "LMS_ALLOC_FAILED" }, + { -(MBEDTLS_ERR_LMS_BUFFER_TOO_SMALL), "LMS_BUFFER_TOO_SMALL" }, +#endif /* MBEDTLS_LMS_C */ #if defined(MBEDTLS_NET_C) { -(MBEDTLS_ERR_NET_SOCKET_FAILED), "NET_SOCKET_FAILED" }, @@ -562,50 +521,26 @@ static const struct ssl_errs mbedtls_low_level_error_tab[] = { { -(MBEDTLS_ERR_OID_BUF_TOO_SMALL), "OID_BUF_TOO_SMALL" }, #endif /* MBEDTLS_OID_C */ -#if defined(MBEDTLS_PADLOCK_C) - { -(MBEDTLS_ERR_PADLOCK_DATA_MISALIGNED), "PADLOCK_DATA_MISALIGNED" }, -#endif /* MBEDTLS_PADLOCK_C */ - -#if defined(MBEDTLS_PLATFORM_C) - { -(MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED), "PLATFORM_HW_ACCEL_FAILED" }, - { -(MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED), "PLATFORM_FEATURE_UNSUPPORTED" }, -#endif /* MBEDTLS_PLATFORM_C */ - #if defined(MBEDTLS_POLY1305_C) { -(MBEDTLS_ERR_POLY1305_BAD_INPUT_DATA), "POLY1305_BAD_INPUT_DATA" }, - { -(MBEDTLS_ERR_POLY1305_FEATURE_UNAVAILABLE), "POLY1305_FEATURE_UNAVAILABLE" }, - { -(MBEDTLS_ERR_POLY1305_HW_ACCEL_FAILED), "POLY1305_HW_ACCEL_FAILED" }, #endif /* MBEDTLS_POLY1305_C */ -#if defined(MBEDTLS_RIPEMD160_C) - { -(MBEDTLS_ERR_RIPEMD160_HW_ACCEL_FAILED), "RIPEMD160_HW_ACCEL_FAILED" }, -#endif /* MBEDTLS_RIPEMD160_C */ - #if defined(MBEDTLS_SHA1_C) - { -(MBEDTLS_ERR_SHA1_HW_ACCEL_FAILED), "SHA1_HW_ACCEL_FAILED" }, { -(MBEDTLS_ERR_SHA1_BAD_INPUT_DATA), "SHA1_BAD_INPUT_DATA" }, #endif /* MBEDTLS_SHA1_C */ #if defined(MBEDTLS_SHA256_C) - { -(MBEDTLS_ERR_SHA256_HW_ACCEL_FAILED), "SHA256_HW_ACCEL_FAILED" }, { -(MBEDTLS_ERR_SHA256_BAD_INPUT_DATA), "SHA256_BAD_INPUT_DATA" }, #endif /* MBEDTLS_SHA256_C */ #if defined(MBEDTLS_SHA512_C) - { -(MBEDTLS_ERR_SHA512_HW_ACCEL_FAILED), "SHA512_HW_ACCEL_FAILED" }, { -(MBEDTLS_ERR_SHA512_BAD_INPUT_DATA), "SHA512_BAD_INPUT_DATA" }, #endif /* MBEDTLS_SHA512_C */ #if defined(MBEDTLS_THREADING_C) - { -(MBEDTLS_ERR_THREADING_FEATURE_UNAVAILABLE), "THREADING_FEATURE_UNAVAILABLE" }, { -(MBEDTLS_ERR_THREADING_BAD_INPUT_DATA), "THREADING_BAD_INPUT_DATA" }, { -(MBEDTLS_ERR_THREADING_MUTEX_ERROR), "THREADING_MUTEX_ERROR" }, #endif /* MBEDTLS_THREADING_C */ - -#if defined(MBEDTLS_XTEA_C) - { -(MBEDTLS_ERR_XTEA_INVALID_INPUT_LENGTH), "XTEA_INVALID_INPUT_LENGTH" }, - { -(MBEDTLS_ERR_XTEA_HW_ACCEL_FAILED), "XTEA_HW_ACCEL_FAILED" }, -#endif /* MBEDTLS_XTEA_C */ // END generated code }; @@ -655,7 +590,7 @@ void mbedtls_strerror(int ret, char *buf, size_t buflen) { if (got_hl) { use_ret = ret & 0xFF80; - // special case + // special case, don't try to translate low level code #if defined(MBEDTLS_SSL_TLS_C) if (use_ret == -(MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE)) { strncpy(buf, "MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE", buflen); diff --git a/lib/micropython-lib b/lib/micropython-lib index e025c843b60e..7cdf70881519 160000 --- a/lib/micropython-lib +++ b/lib/micropython-lib @@ -1 +1 @@ -Subproject commit e025c843b60e93689f0f991d753010bb5bd6a722 +Subproject commit 7cdf70881519c73667efbc4a61a04d9c1a49babb diff --git a/lib/oofatfs/ff.c b/lib/oofatfs/ff.c index bc301f3ed4f6..8a5dcf2221ca 100644 --- a/lib/oofatfs/ff.c +++ b/lib/oofatfs/ff.c @@ -278,6 +278,7 @@ typedef struct { /* SBCS up-case tables (\x80-\xFF) */ +// CIRCUITPY-CHANGE // Optimize the 437-only case with a truncated lookup table. #if FF_CODE_PAGE == 437 #define TBL_CT437 {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \ @@ -1187,6 +1188,7 @@ static DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FF break; } } + // CIRCUITPY-CHANGE: explicit fallthrough MP_FALLTHROUGH /* go to default */ #endif @@ -1997,6 +1999,7 @@ static void gen_numname ( if (c > '9') c += 7; ns[i--] = c; seq /= 16; + // CIRCUITPY-CHANGE: limit to 8 digits } while (seq && i > 0); ns[i] = '~'; @@ -2894,6 +2897,7 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not } #elif FF_CODE_PAGE < 900 /* SBCS cfg */ wc = ff_uni2oem(wc, CODEPAGE); /* Unicode ==> ANSI/OEM code */ + // CIRCUITPY-CHANGE // Optimize the 437-only case with a truncated lookup table. #if FF_CODE_PAGE == 437 if (wc & 0x80 && wc < (0xA5 - 0x80)) wc = ExCvt[wc & 0x7F]; /* Convert extended character to upper (SBCS) */ @@ -4832,7 +4836,7 @@ FRESULT f_rename ( DWORD dw; DEF_NAMBUF - + // CIRCUITPY-CHANGE // Check to see if we're moving a directory into itself. This occurs when we're moving a // directory where the old path is a prefix of the new and the next character is a "/" and thus // preserves the original directory name. @@ -5425,6 +5429,7 @@ FRESULT f_mkfs ( // CIRCUITPY-CHANGE: Make number of root directory entries changeable. See below. UINT n_rootdir = 512; /* Default number of root directory entries for FAT volume */ static const WORD cst[] = {1, 4, 16, 64, 256, 512, 0}; /* Cluster size boundary for FAT volume (4Ks unit) */ +// CIRCUITPY-CHANGE: optional FAT32 support #if FF_MKFS_FAT32 static const WORD cst32[] = {1, 2, 4, 8, 16, 32, 0}; /* Cluster size boundary for FAT32 volume (128Ks unit) */ #endif @@ -5439,6 +5444,7 @@ FRESULT f_mkfs ( DWORD tbl[3]; #endif + // CIRCUITPY-CHANGE: random volids DWORD volid = MAKE_VOLID(); /* Check mounted drive and clear work area */ @@ -5500,6 +5506,7 @@ FRESULT f_mkfs ( } } if (au > 128) LEAVE_MKFS(FR_INVALID_PARAMETER); /* Too large au for FAT/FAT32 */ + // CIRCUITPY-CHANGE: optional FAT32 support if (FF_MKFS_FAT32 && (opt & FM_FAT32)) { /* FAT32 possible? */ if ((opt & FM_ANY) == FM_FAT32 || !(opt & FM_FAT)) { /* FAT32 only or no-FAT? */ fmt = FS_FAT32; break; @@ -5555,6 +5562,7 @@ FRESULT f_mkfs ( } st = 1; /* Do not compress short run */ /* go to next case */ + // CIRCUITPY-CHANGE: explicit fallthrough MP_FALLTHROUGH case 1: ch = si++; /* Fill the short run */ @@ -5641,6 +5649,7 @@ FRESULT f_mkfs ( st_dword(buf + BPB_DataOfsEx, b_data - b_vol); /* Data offset [sector] */ st_dword(buf + BPB_NumClusEx, n_clst); /* Number of clusters */ st_dword(buf + BPB_RootClusEx, 2 + tbl[0] + tbl[1]); /* Root dir cluster # */ + // CIRCUITPY-CHANGE: random volids st_dword(buf + BPB_VolIDEx, volid); /* VSN */ st_word(buf + BPB_FSVerEx, 0x100); /* Filesystem version (1.00) */ for (buf[BPB_BytsPerSecEx] = 0, i = ss; i >>= 1; buf[BPB_BytsPerSecEx]++) ; /* Log2 of sector size [byte] */ @@ -5677,6 +5686,7 @@ FRESULT f_mkfs ( do { pau = au; /* Pre-determine number of clusters and FAT sub-type */ +// CIRCUITPY-CHANGE: optional FAT32 support #if FF_MKFS_FAT32 if (fmt == FS_FAT32) { /* FAT32 volume */ if (pau == 0) { /* au auto-selection */ @@ -5725,6 +5735,7 @@ FRESULT f_mkfs ( /* Determine number of clusters and final check of validity of the FAT sub-type */ if (sz_vol < b_data + pau * 16 - b_vol) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too small volume */ n_clst = (sz_vol - sz_rsv - sz_fat * n_fats - sz_dir) / pau; +// CIRCUITPY-CHANGE: optional FAT32 support #if FF_MKFS_FAT32 if (fmt == FS_FAT32) { if (n_clst <= MAX_FAT16) { /* Too few clusters for FAT32 */ @@ -5766,6 +5777,7 @@ FRESULT f_mkfs ( buf[BPB_SecPerClus] = (BYTE)pau; /* Cluster size [sector] */ st_word(buf + BPB_RsvdSecCnt, (WORD)sz_rsv); /* Size of reserved area */ buf[BPB_NumFATs] = (BYTE)n_fats; /* Number of FATs */ +// CIRCUITPY-CHANGE: optional FAT32 support #if FF_MKFS_FAT32 st_word(buf + BPB_RootEntCnt, (WORD)((fmt == FS_FAT32) ? 0 : n_rootdir)); /* Number of root directory entries */ #else @@ -5780,6 +5792,7 @@ FRESULT f_mkfs ( st_word(buf + BPB_SecPerTrk, 63); /* Number of sectors per track (for int13) */ st_word(buf + BPB_NumHeads, 255); /* Number of heads (for int13) */ st_dword(buf + BPB_HiddSec, b_vol); /* Volume offset in the physical drive [sector] */ +// CIRCUITPY-CHANGE: optional FAT32 support #if FF_MKFS_FAT32 if (fmt == FS_FAT32) { st_dword(buf + BS_VolID32, volid); /* VSN */ @@ -5803,6 +5816,7 @@ FRESULT f_mkfs ( if (disk_write(pdrv, buf, b_vol, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Write it to the VBR sector */ /* Create FSINFO record if needed */ +// CIRCUITPY-CHANGE: optional FAT32 support #if FF_MKFS_FAT32 if (fmt == FS_FAT32) { disk_write(pdrv, buf, b_vol + 6, 1); /* Write backup VBR (VBR + 6) */ @@ -5821,6 +5835,7 @@ FRESULT f_mkfs ( mem_set(buf, 0, (UINT)szb_buf); sect = b_fat; /* FAT start sector */ for (i = 0; i < n_fats; i++) { /* Initialize FATs each */ +// CIRCUITPY-CHANGE: optional FAT32 support #if FF_MKFS_FAT32 if (fmt == FS_FAT32) { st_dword(buf + 0, 0xFFFFFFF8); /* Entry 0 */ @@ -5841,6 +5856,7 @@ FRESULT f_mkfs ( } /* Initialize root directory (fill with zero) */ +// CIRCUITPY-CHANGE: optional FAT32 support #if FF_MKFS_FAT32 nsect = (fmt == FS_FAT32) ? pau : sz_dir; /* Number of root directory sectors */ #else @@ -5857,6 +5873,7 @@ FRESULT f_mkfs ( if (FF_FS_EXFAT && fmt == FS_EXFAT) { sys = 0x07; /* HPFS/NTFS/exFAT */ } else { + // CIRCUITPY-CHANGE: optional FAT32 support if (FF_MKFS_FAT32 && fmt == FS_FAT32) { sys = 0x0C; /* FAT32X */ } else { diff --git a/lib/oofatfs/ff.h b/lib/oofatfs/ff.h index d77ddf57611d..2d3a0e6a789f 100644 --- a/lib/oofatfs/ff.h +++ b/lib/oofatfs/ff.h @@ -162,6 +162,7 @@ typedef struct { DWORD bitbase; /* Allocation bitmap base sector */ #endif DWORD winsect; /* Current sector appearing in the win[] */ + // CIRCUITPY-CHANGE: ensure alignment __attribute__((aligned(FF_WINDOW_ALIGNMENT),)) BYTE win[FF_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg). */ } FATFS; @@ -334,6 +335,7 @@ FRESULT f_setcp (WORD cp); /* Set curre DWORD get_fattime (void); #endif +// CIRCUITPY-CHANGE: random volids #if FF_FS_MAKE_VOLID DWORD make_volid (void); #endif diff --git a/lib/oofatfs/ffconf.h b/lib/oofatfs/ffconf.h index f22fbee9323c..7e85ed8186f6 100644 --- a/lib/oofatfs/ffconf.h +++ b/lib/oofatfs/ffconf.h @@ -72,6 +72,7 @@ #define FF_USE_MKFS 1 /* This option switches f_mkfs() function. (0:Disable or 1:Enable) */ +// CIRCUITPY-CHANGE: optional FAT32 support #ifdef MICROPY_FATFS_MKFS_FAT32 #define FF_MKFS_FAT32 MICROPY_FATFS_MKFS_FAT32 #else @@ -79,6 +80,7 @@ #endif /* This option switches off FAT32 support in f_mkfs() */ +// CIRCUITPY-CHANGE: enable fast seek #define FF_USE_FASTSEEK 1 /* This option switches fast seek function. (0:Disable or 1:Enable) */ @@ -169,6 +171,7 @@ / memory for the working buffer, memory management functions, ff_memalloc() and / ff_memfree() in ffsystem.c, need to be added to the project. */ +// CIRCUITPY-CHANGE: unicode filenames for FAT #ifdef MICROPY_FATFS_LFN_UNICODE #define FF_LFN_UNICODE (MICROPY_FATFS_LFN_UNICODE) #else @@ -267,6 +270,7 @@ / for variable sector size mode and disk_ioctl() function needs to implement / GET_SECTOR_SIZE command. */ +// CIRCUITPY-CHANGE: align FATFS window buffer for tinyusb #ifdef MICROPY_FATFS_WINDOW_ALIGNMENT #define FF_WINDOW_ALIGNMENT (MICROPY_FATFS_WINDOW_ALIGNMENT) #else @@ -379,6 +383,7 @@ / SemaphoreHandle_t and etc. A header file for O/S definitions needs to be / included somewhere in the scope of ff.h. */ +// CIRCUITPY-CHANGE: random volids #ifndef FF_FS_MAKE_VOLID #define FF_FS_MAKE_VOLID (0) #endif diff --git a/lib/oofatfs/ffunicode.c b/lib/oofatfs/ffunicode.c index a04577616a6c..34dde8bee2c0 100644 --- a/lib/oofatfs/ffunicode.c +++ b/lib/oofatfs/ffunicode.c @@ -499,6 +499,7 @@ DWORD ff_wtoupper ( /* Returns up-converted code point */ DWORD uni /* Unicode code point to be up-converted */ ) { + // CIRCUITPY-CHANGE #if FF_FS_CASE_INSENSITIVE_COMPARISON_ASCII_ONLY // Only uppercase ASCII characters. Everything else will require the user to // pass in an uppercase version. diff --git a/lib/uzlib/lz77.c b/lib/uzlib/lz77.c new file mode 100644 index 000000000000..8d6920665e54 --- /dev/null +++ b/lib/uzlib/lz77.c @@ -0,0 +1,91 @@ +/* + * Simple LZ77 streaming compressor. + * + * The scheme implemented here doesn't use a hash table and instead does a brute + * force search in the history for a previous string. It is relatively slow + * (but still O(N)) but gives good compression and minimal memory usage. For a + * small history window (eg 256 bytes) it's not too slow and compresses well. + * + * MIT license; Copyright (c) 2021 Damien P. George + */ + +#include "uzlib.h" + +#include "defl_static.c" + +#define MATCH_LEN_MIN (3) +#define MATCH_LEN_MAX (258) + +// hist should be a preallocated buffer of hist_max size bytes. +// hist_max should be greater than 0 a power of 2 (ie 1, 2, 4, 8, ...). +// It's possible to pass in hist=NULL, and then the history window will be taken from the +// src passed in to uzlib_lz77_compress (this is useful when not doing streaming compression). +void uzlib_lz77_init(uzlib_lz77_state_t *state, uint8_t *hist, size_t hist_max) { + memset(state, 0, sizeof(uzlib_lz77_state_t)); + state->hist_buf = hist; + state->hist_max = hist_max; + state->hist_start = 0; + state->hist_len = 0; +} + +// Search back in the history for the maximum match of the given src data, +// with support for searching beyond the end of the history and into the src buffer +// (effectively the history and src buffer are concatenated). +static size_t uzlib_lz77_search_max_match(uzlib_lz77_state_t *state, const uint8_t *src, size_t len, size_t *longest_offset) { + size_t longest_len = 0; + for (size_t hist_search = 0; hist_search < state->hist_len; ++hist_search) { + // Search for a match. + size_t match_len; + for (match_len = 0; match_len <= MATCH_LEN_MAX && match_len < len; ++match_len) { + uint8_t hist; + if (hist_search + match_len < state->hist_len) { + hist = state->hist_buf[(state->hist_start + hist_search + match_len) & (state->hist_max - 1)]; + } else { + hist = src[hist_search + match_len - state->hist_len]; + } + if (src[match_len] != hist) { + break; + } + } + + // Take this match if its length is at least the minimum, and larger than previous matches. + // If the length is the same as the previous longest then take this match as well, because + // this match will be closer (more recent in the history) and take less bits to encode. + if (match_len >= MATCH_LEN_MIN && match_len >= longest_len) { + longest_len = match_len; + *longest_offset = state->hist_len - hist_search; + } + } + + return longest_len; +} + +// Compress the given chunk of data. +void uzlib_lz77_compress(uzlib_lz77_state_t *state, const uint8_t *src, unsigned len) { + const uint8_t *top = src + len; + while (src < top) { + // Look for a match in the history window. + size_t match_offset = 0; + size_t match_len = uzlib_lz77_search_max_match(state, src, top - src, &match_offset); + + // Encode the literal byte or the match. + if (match_len == 0) { + uzlib_literal(state, *src); + match_len = 1; + } else { + uzlib_match(state, match_offset, match_len); + } + + // Push the bytes into the history buffer. + size_t mask = state->hist_max - 1; + while (match_len--) { + uint8_t b = *src++; + state->hist_buf[(state->hist_start + state->hist_len) & mask] = b; + if (state->hist_len == state->hist_max) { + state->hist_start = (state->hist_start + 1) & mask; + } else { + ++state->hist_len; + } + } + } +} diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index ed8975e5c0b8..b98a916a4573 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -153,7 +153,7 @@ msgstr "" msgid "%q length must be >= %d" msgstr "" -#: py/objmodule.c py/runtime.c +#: py/modsys.c py/objmodule.c py/runtime.c msgid "%q moved from %q to %q" msgstr "" @@ -1371,10 +1371,6 @@ msgstr "" msgid "Name or service not known" msgstr "" -#: py/qstr.c -msgid "Name too long" -msgstr "" - #: shared-bindings/displayio/TileGrid.c msgid "New bitmap must be same size as old bitmap" msgstr "" @@ -2329,6 +2325,7 @@ msgid "You pressed the SW38 button at start up." msgstr "" #: ports/espressif/boards/hardkernel_odroid_go/mpconfigboard.h +#: ports/espressif/boards/vidi_x/mpconfigboard.h msgid "You pressed the VOLUME button at start up." msgstr "" @@ -3507,6 +3504,10 @@ msgstr "" msgid "name not defined" msgstr "" +#: py/qstr.c +msgid "name too long" +msgstr "" + #: py/persistentcode.c msgid "native code in .mpy unsupported" msgstr "" diff --git a/logo/vector-logo-2.ico b/logo/vector-logo-2.ico new file mode 100644 index 000000000000..24b12812cf49 Binary files /dev/null and b/logo/vector-logo-2.ico differ diff --git a/mpy-cross/Makefile b/mpy-cross/Makefile index 14e8913f137a..9962e9bcbb24 100644 --- a/mpy-cross/Makefile +++ b/mpy-cross/Makefile @@ -1,6 +1,7 @@ include ../py/mkenv.mk # define main target +# CIRCUITPY-CHANGE PROG ?= mpy-cross # qstr definitions (must come before including py.mk) @@ -21,6 +22,7 @@ CWARN = -Wall -Werror CWARN += -Wextra -Wno-unused-parameter -Wpointer-arith CFLAGS += $(INC) $(CWARN) -std=gnu99 $(COPT) $(CFLAGS_EXTRA) CFLAGS += -fdata-sections -ffunction-sections -fno-asynchronous-unwind-tables +# CIRCUITPY-CHANGE CFLAGS += -DCIRCUITPY # Debugging/Optimization @@ -46,6 +48,7 @@ endif LDFLAGS += $(LDFLAGS_MOD) $(LDFLAGS_ARCH) -lm $(LDFLAGS_EXTRA) # source files +# CIRCUITPY-CHANGE: extra files SRC_C = \ main.c \ gccollect.c \ diff --git a/mpy-cross/README.md b/mpy-cross/README.md index 3d88814d649a..83f6d6fd8d92 100644 --- a/mpy-cross/README.md +++ b/mpy-cross/README.md @@ -1,9 +1,3 @@ - - MicroPython cross compiler ========================== diff --git a/mpy-cross/main.c b/mpy-cross/main.c index 78135574c44a..acbd30b64bbd 100644 --- a/mpy-cross/main.c +++ b/mpy-cross/main.c @@ -37,6 +37,7 @@ #include "py/stackctrl.h" #include "genhdr/mpversion.h" #ifdef _WIN32 +// CIRCUITPY-CHANGE #include "fmode.h" #endif @@ -71,7 +72,7 @@ STATIC int compile_and_save(const char *file, const char *output_file, const cha if (strcmp(file, "-") == 0) { lex = mp_lexer_new_from_fd(MP_QSTR__lt_stdin_gt_, STDIN_FILENO, false); } else { - lex = mp_lexer_new_from_file(file); + lex = mp_lexer_new_from_file(qstr_from_str(file)); } qstr source_name; @@ -104,7 +105,7 @@ STATIC int compile_and_save(const char *file, const char *output_file, const cha vstr_add_str(&vstr, output_file); } - mp_raw_code_save_file(&cm, vstr_null_terminated_str(&vstr)); + mp_raw_code_save_file(&cm, qstr_from_strn(vstr.buf, vstr.len)); vstr_clear(&vstr); } @@ -247,6 +248,7 @@ MP_NOINLINE int main_(int argc, char **argv) { if (strcmp(argv[a], "-X") == 0) { a += 1; } else if (strcmp(argv[a], "--version") == 0) { + // CIRCUITPY-CHANGE printf("CircuitPython " MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE "; mpy-cross emitting mpy v" MP_STRINGIFY(MPY_VERSION) "." MP_STRINGIFY(MPY_SUB_VERSION) "\n"); return 0; diff --git a/mpy-cross/micropython.rc b/mpy-cross/micropython.rc new file mode 100644 index 000000000000..8d92bb0d81ea --- /dev/null +++ b/mpy-cross/micropython.rc @@ -0,0 +1 @@ +app ICON "../../logo/vector-logo-2.ico" diff --git a/mpy-cross/mpconfigport.h b/mpy-cross/mpconfigport.h index e0772470110a..bdd10efdaea0 100644 --- a/mpy-cross/mpconfigport.h +++ b/mpy-cross/mpconfigport.h @@ -26,6 +26,10 @@ // options to control how MicroPython is built +// CIRCUITPY-CHANGE: mpy-cross doesn't have background tasks +#define RUN_BACKGROUND_TASKS ((void)0) + + #define MICROPY_ALLOC_PATH_MAX (PATH_MAX) #define MICROPY_PERSISTENT_CODE_LOAD (0) #define MICROPY_PERSISTENT_CODE_SAVE (1) @@ -42,8 +46,6 @@ #define MICROPY_EMIT_X86 (1) #define MICROPY_EMIT_THUMB (1) #define MICROPY_EMIT_INLINE_THUMB (1) -#define MICROPY_EMIT_INLINE_THUMB_ARMV7M (1) -#define MICROPY_EMIT_INLINE_THUMB_FLOAT (1) #define MICROPY_EMIT_ARM (1) #define MICROPY_EMIT_XTENSA (1) #define MICROPY_EMIT_INLINE_XTENSA (1) @@ -72,6 +74,7 @@ #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_DOUBLE) #define MICROPY_CPYTHON_COMPAT (1) +// CIRCUITPY-CHANGE #define MICROPY_PY_ASYNC_AWAIT (1) #define MICROPY_USE_INTERNAL_PRINTF (0) @@ -93,50 +96,6 @@ #define MICROPY_PY_IO (0) #define MICROPY_PY_SYS (0) -// MINGW only handles these errno names. -#ifdef __MINGW32__ -#define MICROPY_PY_ERRNO_LIST \ - X(EPERM) \ - X(ENOENT) \ - X(ESRCH) \ - X(EINTR) \ - X(EIO) \ - X(ENXIO) \ - X(E2BIG) \ - X(ENOEXEC) \ - X(EBADF) \ - X(ECHILD) \ - X(EAGAIN) \ - X(ENOMEM) \ - X(EACCES) \ - X(EFAULT) \ - X(EBUSY) \ - X(EEXIST) \ - X(EXDEV) \ - X(ENODEV) \ - X(ENOTDIR) \ - X(EISDIR) \ - X(EINVAL) \ - X(ENFILE) \ - X(EMFILE) \ - X(ENOTTY) \ - X(EFBIG) \ - X(ENOSPC) \ - X(ESPIPE) \ - X(EROFS) \ - X(EMLINK) \ - X(EPIPE) \ - X(EDOM) \ - X(ERANGE) \ - X(EDEADLOCK) \ - X(EDEADLK) \ - X(ENAMETOOLONG) \ - X(ENOLCK) \ - X(ENOSYS) \ - X(ENOTEMPTY) \ - X(EILSEQ) -#endif - // type definitions for the specific machine #ifdef __LP64__ diff --git a/mpy-cross/mphalport.h b/mpy-cross/mphalport.h index b45ff339bad8..38359283157c 100644 --- a/mpy-cross/mphalport.h +++ b/mpy-cross/mphalport.h @@ -1,6 +1,2 @@ -// SPDX-FileCopyrightText: 2014 MicroPython & CircuitPython contributors (https://github.com/adafruit/circuitpython/graphs/contributors) -// -// SPDX-License-Identifier: MIT - // prevent including extmod/virtpin.h #define mp_hal_pin_obj_t diff --git a/mpy-cross/qstrdefsport.h b/mpy-cross/qstrdefsport.h index 36d4b9ccf4fa..3ba897069bf7 100644 --- a/mpy-cross/qstrdefsport.h +++ b/mpy-cross/qstrdefsport.h @@ -1,5 +1 @@ -// SPDX-FileCopyrightText: 2014 MicroPython & CircuitPython contributors (https://github.com/adafruit/circuitpython/graphs/contributors) -// -// SPDX-License-Identifier: MIT - // qstrs specific to this port diff --git a/ports/atmel-samd/boards/mini_sam_m4/mpconfigboard.mk b/ports/atmel-samd/boards/mini_sam_m4/mpconfigboard.mk index 933a0ee673ac..73c56fb5fbab 100644 --- a/ports/atmel-samd/boards/mini_sam_m4/mpconfigboard.mk +++ b/ports/atmel-samd/boards/mini_sam_m4/mpconfigboard.mk @@ -10,8 +10,9 @@ QSPI_FLASH_FILESYSTEM = 1 EXTERNAL_FLASH_DEVICES = "W25Q16JVxM, W25Q16JVxQ" LONGINT_IMPL = MPZ -CIRCUITPY_SYNTHIO = 0 CIRCUITPY_FLOPPYIO = 0 +CIRCUITPY_JPEGIO = 0 +CIRCUITPY_SYNTHIO = 0 CIRCUITPY_BITBANG_APA102 = 1 diff --git a/ports/atmel-samd/boards/pewpew_lcd/mpconfigboard.mk b/ports/atmel-samd/boards/pewpew_lcd/mpconfigboard.mk index 929a971d003f..823f5698156c 100644 --- a/ports/atmel-samd/boards/pewpew_lcd/mpconfigboard.mk +++ b/ports/atmel-samd/boards/pewpew_lcd/mpconfigboard.mk @@ -23,8 +23,8 @@ CIRCUITPY_KEYPAD_KEYMATRIX = 0 CIRCUITPY_MATH = 1 CIRCUITPY_ANALOGIO = 1 CIRCUITPY_NEOPIXEL_WRITE = 1 -CIRCUITPY_SAMD = 1 +CIRCUITPY_SAMD = 0 CIRCUITPY_EPAPERDISPLAY = 0 CIRCUITPY_I2CDISPLAYBUS = 0 CIRCUITPY_STAGE = 0 diff --git a/ports/atmel-samd/common-hal/rtc/RTC.h b/ports/atmel-samd/common-hal/rtc/RTC.h index daf215164610..c91422887e5b 100644 --- a/ports/atmel-samd/common-hal/rtc/RTC.h +++ b/ports/atmel-samd/common-hal/rtc/RTC.h @@ -5,8 +5,3 @@ // SPDX-License-Identifier: MIT #pragma once - -#ifndef MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_RTC_RTC_H -#define MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_RTC_RTC_H - -#endif // MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_RTC_RTC_H diff --git a/ports/atmel-samd/mphalport.c b/ports/atmel-samd/mphalport.c index 6815fefbaa4f..aa0473f04dd7 100644 --- a/ports/atmel-samd/mphalport.c +++ b/ports/atmel-samd/mphalport.c @@ -6,6 +6,11 @@ #include +#include "mpconfigboard.h" +#include "mphalport.h" +#include "reset.h" +#include "supervisor/shared/tick.h" + #include "shared/readline/readline.h" #include "shared/runtime/interrupt_char.h" #include "py/mphal.h" @@ -21,10 +26,6 @@ #include "hal/include/hal_sleep.h" #include "sam.h" -#include "mpconfigboard.h" -#include "mphalport.h" -#include "reset.h" -#include "supervisor/shared/tick.h" extern uint32_t common_hal_mcu_processor_get_frequency(void); diff --git a/ports/broadcom/mphalport.h b/ports/broadcom/mphalport.h index 6a7cdb99db59..d42915aa8bd0 100644 --- a/ports/broadcom/mphalport.h +++ b/ports/broadcom/mphalport.h @@ -19,7 +19,7 @@ void mp_hal_delay_us(mp_uint_t us); void mp_hal_set_interrupt_char(int c); int mp_hal_stdin_rx_chr(void); -void mp_hal_stdout_tx_strn(const char *str, size_t len); +mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len); #ifdef MICROPY_HW_USBHOST #include "usbkbd.h" diff --git a/ports/cxd56/common-hal/rtc/RTC.h b/ports/cxd56/common-hal/rtc/RTC.h index e9bf05160416..9f868e5c5305 100644 --- a/ports/cxd56/common-hal/rtc/RTC.h +++ b/ports/cxd56/common-hal/rtc/RTC.h @@ -5,8 +5,3 @@ // SPDX-License-Identifier: MIT #pragma once - -#ifndef MICROPY_INCLUDED_CXD56_COMMON_HAL_RTC_RTC_H -#define MICROPY_INCLUDED_CXD56_COMMON_HAL_RTC_RTC_H - -#endif // MICROPY_INCLUDED_CXD56_COMMON_HAL_RTC_RTC_H diff --git a/ports/espressif/common-hal/_bleio/ble_events.c b/ports/espressif/common-hal/_bleio/ble_events.c index cc93cfc53947..6a616819a8bd 100644 --- a/ports/espressif/common-hal/_bleio/ble_events.c +++ b/ports/espressif/common-hal/_bleio/ble_events.c @@ -64,7 +64,7 @@ void ble_event_add_handler(ble_gap_event_fn *func, void *param) { } // Add a new handler to the front of the list - ble_event_handler_entry_t *handler = m_new_ll(ble_event_handler_entry_t, 1); + ble_event_handler_entry_t *handler = m_new(ble_event_handler_entry_t, 1); ble_event_add_handler_entry(handler, func, param); } diff --git a/ports/mimxrt10xx/linking/common.ld b/ports/mimxrt10xx/linking/common.ld index 6e674a49f240..2c6be0dd1292 100644 --- a/ports/mimxrt10xx/linking/common.ld +++ b/ports/mimxrt10xx/linking/common.ld @@ -96,7 +96,6 @@ SECTIONS /* Less critical portions of the runtime. */ *runtime.o(.text.mp_import* .text.mp_resume* .text.mp_make_raise* .text.mp_init) - *gc.o(.text.gc_never_free .text.gc_make_long_lived) /* Anything marked cold/unlikely should be in flash. */ *(.text.unlikely.*) diff --git a/ports/nordic/common-hal/watchdog/__init__.h b/ports/nordic/common-hal/watchdog/__init__.h index 3df378fbadcf..c91422887e5b 100644 --- a/ports/nordic/common-hal/watchdog/__init__.h +++ b/ports/nordic/common-hal/watchdog/__init__.h @@ -5,8 +5,3 @@ // SPDX-License-Identifier: MIT #pragma once - -#ifndef MICROPY_INCLUDED_NRF_COMMON_HAL_WATCHDOG___INIT___H -#define MICROPY_INCLUDED_NRF_COMMON_HAL_WATCHDOG___INIT___H - -#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_WATCHDOG___INIT___H diff --git a/ports/raspberrypi/common-hal/i2ctarget/I2CTarget.h b/ports/raspberrypi/common-hal/i2ctarget/I2CTarget.h index 9293ecbdf227..0b6c2875e3f4 100644 --- a/ports/raspberrypi/common-hal/i2ctarget/I2CTarget.h +++ b/ports/raspberrypi/common-hal/i2ctarget/I2CTarget.h @@ -6,9 +6,6 @@ #pragma once -#ifndef MICROPY_INCLUDED_RPI_COMMON_HAL_I2C_TARGET_H -#define MICROPY_INCLUDED_RPI_COMMON_HAL_I2C_TARGET_H - #include "py/obj.h" #include "common-hal/microcontroller/Pin.h" #include "src/rp2_common/hardware_i2c/include/hardware/i2c.h" @@ -24,5 +21,3 @@ typedef struct { uint8_t scl_pin; uint8_t sda_pin; } i2ctarget_i2c_target_obj_t; - -#endif MICROPY_INCLUDED_RPI_COMMON_HAL_BUSIO_I2C_TARGET_H diff --git a/ports/raspberrypi/common-hal/socketpool/Socket.c b/ports/raspberrypi/common-hal/socketpool/Socket.c index d2626456f950..84d8bbd5149e 100644 --- a/ports/raspberrypi/common-hal/socketpool/Socket.c +++ b/ports/raspberrypi/common-hal/socketpool/Socket.c @@ -849,7 +849,7 @@ socketpool_socket_obj_t *common_hal_socketpool_socket_accept(socketpool_socket_o mp_obj_t *peer_out) { // Create new socket object, do it here because we must not raise an out-of-memory // exception when the LWIP concurrency lock is held - socketpool_socket_obj_t *accepted = m_new_ll_obj_with_finaliser(socketpool_socket_obj_t); + socketpool_socket_obj_t *accepted = m_new_obj_with_finaliser(socketpool_socket_obj_t); socketpool_socket_reset(accepted); int ret = socketpool_socket_accept(socket, peer_out, accepted); diff --git a/ports/unix/Makefile b/ports/unix/Makefile index 60bc5ae98c10..526955b1ea5c 100644 --- a/ports/unix/Makefile +++ b/ports/unix/Makefile @@ -193,6 +193,7 @@ ifeq ($(MICROPY_PY_JNI),1) CFLAGS += -I/usr/lib/jvm/java-7-openjdk-amd64/include -DMICROPY_PY_JNI=1 endif +# CIRCUITPY-CHANGE: CircuitPython-specific files. # source files SRC_C += \ main.c \ @@ -230,11 +231,9 @@ OBJ += $(addprefix $(BUILD)/, $(SHARED_SRC_C:.c=.o)) # List of sources for qstr extraction SRC_QSTR += $(SRC_C) $(SRC_CXX) $(SHARED_SRC_C) -# Append any auto-generated sources that are needed by sources listed in -# SRC_QSTR -SRC_QSTR_AUTO_DEPS += ifneq ($(FROZEN_MANIFEST),) +# CIRCUITPY-CHANGE # To use frozen code create a manifest.py file with a description of files to # freeze, then invoke make with FROZEN_MANIFEST=manifest.py (be sure to build from scratch). CFLAGS += -DMICROPY_QSTR_EXTRA_POOL=mp_qstr_frozen_const_pool diff --git a/ports/unix/coverage.c b/ports/unix/coverage.c index 8ac1d3d2982e..2670db3402dd 100644 --- a/ports/unix/coverage.c +++ b/ports/unix/coverage.c @@ -356,19 +356,20 @@ STATIC mp_obj_t extra_coverage(void) { mp_printf(&mp_plat_print, "# repl\n"); const char *str; - size_t len = mp_repl_autocomplete("__n", 3, &mp_plat_print, &str); + size_t len = mp_repl_autocomplete("__n", 3, &mp_plat_print, &str); // expect "ame__" mp_printf(&mp_plat_print, "%.*s\n", (int)len, str); - len = mp_repl_autocomplete("im", 2, &mp_plat_print, &str); + len = mp_repl_autocomplete("im", 2, &mp_plat_print, &str); // expect "port" mp_printf(&mp_plat_print, "%.*s\n", (int)len, str); - mp_repl_autocomplete("import ", 7, &mp_plat_print, &str); - len = mp_repl_autocomplete("import ti", 9, &mp_plat_print, &str); + mp_repl_autocomplete("import ", 7, &mp_plat_print, &str); // expect the list of builtins + len = mp_repl_autocomplete("import ti", 9, &mp_plat_print, &str); // expect "me" mp_printf(&mp_plat_print, "%.*s\n", (int)len, str); + // CIRCUITPY-CHANGE mp_repl_autocomplete("import ra", 9, &mp_plat_print, &str); mp_store_global(MP_QSTR_sys, mp_import_name(MP_QSTR_sys, mp_const_none, MP_OBJ_NEW_SMALL_INT(0))); - mp_repl_autocomplete("sys.", 4, &mp_plat_print, &str); - len = mp_repl_autocomplete("sys.impl", 8, &mp_plat_print, &str); + mp_repl_autocomplete("sys.", 4, &mp_plat_print, &str); // expect dir(sys) + len = mp_repl_autocomplete("sys.impl", 8, &mp_plat_print, &str); // expect "ementation" mp_printf(&mp_plat_print, "%.*s\n", (int)len, str); } @@ -545,7 +546,7 @@ STATIC mp_obj_t extra_coverage(void) { fun_bc.context = &context; fun_bc.child_table = NULL; fun_bc.bytecode = (const byte *)"\x01"; // just needed for n_state - mp_code_state_t *code_state = m_new_obj_var(mp_code_state_t, mp_obj_t, 1); + mp_code_state_t *code_state = m_new_obj_var(mp_code_state_t, state, mp_obj_t, 1); code_state->fun_bc = &fun_bc; code_state->ip = (const byte *)"\x00"; // just needed for an invalid opcode code_state->sp = &code_state->state[0]; @@ -578,9 +579,10 @@ STATIC mp_obj_t extra_coverage(void) { mp_sched_unlock(); mp_printf(&mp_plat_print, "unlocked\n"); - // drain pending callbacks + // drain pending callbacks, and test mp_event_wait_indefinite(), mp_event_wait_ms() + mp_event_wait_indefinite(); // the unix port only waits 500us in this call while (mp_sched_num_pending()) { - mp_handle_pending(true); + mp_event_wait_ms(1); } // setting the keyboard interrupt and raising it during mp_handle_pending @@ -610,6 +612,7 @@ STATIC mp_obj_t extra_coverage(void) { mp_handle_pending(true); } + // CIRCUITPY-CHANGE: ringbuf is different // ringbuf { #define RINGBUF_SIZE 99 diff --git a/ports/unix/main.c b/ports/unix/main.c index c24be9624b7e..c7e85a3c63cb 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -54,7 +54,8 @@ #include "genhdr/mpversion.h" #include "input.h" -#if defined(MICROPY_UNIX_COVERAGE) // CIRCUITPY-CHANGE +// CIRCUITPY-CHANGE +#if defined(MICROPY_UNIX_COVERAGE) #include "py/objstr.h" typedef int os_getenv_err_t; mp_obj_t common_hal_os_getenv(const char *key, mp_obj_t default_); @@ -109,6 +110,7 @@ STATIC void stderr_print_strn(void *env, const char *str, size_t len) { (void)env; ssize_t ret; MP_HAL_RETRY_SYSCALL(ret, write(STDERR_FILENO, str, len), {}); + // CIRCUITPY-CHANGE: This should have been conditionalized. #if MICROPY_PY_OS_DUPTERM mp_os_dupterm_tx_strn(str, len); #endif @@ -159,7 +161,8 @@ STATIC int execute_from_lexer(int source_kind, const void *source, mp_parse_inpu const vstr_t *vstr = source; lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, vstr->buf, vstr->len, false); } else if (source_kind == LEX_SRC_FILENAME) { - lex = mp_lexer_new_from_file((const char *)source); + const char *filename = (const char *)source; + lex = mp_lexer_new_from_file(qstr_from_str(filename)); } else { // LEX_SRC_STDIN lex = mp_lexer_new_from_fd(MP_QSTR__lt_stdin_gt_, 0, false); } @@ -326,10 +329,10 @@ STATIC int do_repl(void) { } int ret = execute_from_lexer(LEX_SRC_STR, line, MP_PARSE_SINGLE_INPUT, true); + free(line); if (ret & FORCED_EXIT) { return ret; } - free(line); } #endif diff --git a/ports/unix/modmachine.c b/ports/unix/modmachine.c index dd3cbf96c04c..40d08d911142 100644 --- a/ports/unix/modmachine.c +++ b/ports/unix/modmachine.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2013-2023 Damien P. George * Copyright (c) 2015 Paul Sokolovsky * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -25,16 +25,8 @@ * THE SOFTWARE. */ -#include -#include - -#include "py/runtime.h" -#include "py/obj.h" - -#include "extmod/machine_mem.h" -#include "extmod/machine_pinbase.h" -#include "extmod/machine_signal.h" -#include "extmod/machine_pulse.h" +// This file is never compiled standalone, it's included directly from +// extmod/modmachine.c via MICROPY_PY_MACHINE_INCLUDEFILE. #if MICROPY_PLAT_DEV_MEM #include @@ -44,7 +36,8 @@ #define MICROPY_PAGE_MASK (MICROPY_PAGE_SIZE - 1) #endif -#if MICROPY_PY_MACHINE +// This variable is needed for machine.soft_reset(), but the variable is otherwise unused. +int pyexec_system_exit = 0; uintptr_t mod_machine_mem_get_addr(mp_obj_t addr_o, uint align) { uintptr_t addr = mp_obj_get_int_truncated(addr_o); @@ -77,39 +70,10 @@ uintptr_t mod_machine_mem_get_addr(mp_obj_t addr_o, uint align) { return addr; } -#ifdef MICROPY_UNIX_MACHINE_IDLE -STATIC mp_obj_t machine_idle(void) { - MICROPY_UNIX_MACHINE_IDLE - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_0(machine_idle_obj, machine_idle); -#endif - -STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_machine) }, - - { MP_ROM_QSTR(MP_QSTR_mem8), MP_ROM_PTR(&machine_mem8_obj) }, - { MP_ROM_QSTR(MP_QSTR_mem16), MP_ROM_PTR(&machine_mem16_obj) }, - { MP_ROM_QSTR(MP_QSTR_mem32), MP_ROM_PTR(&machine_mem32_obj) }, - +STATIC void mp_machine_idle(void) { #ifdef MICROPY_UNIX_MACHINE_IDLE - { MP_ROM_QSTR(MP_QSTR_idle), MP_ROM_PTR(&machine_idle_obj) }, - #endif - - { MP_ROM_QSTR(MP_QSTR_PinBase), MP_ROM_PTR(&machine_pinbase_type) }, - { MP_ROM_QSTR(MP_QSTR_Signal), MP_ROM_PTR(&machine_signal_type) }, - #if MICROPY_PY_MACHINE_PULSE - { MP_ROM_QSTR(MP_QSTR_time_pulse_us), MP_ROM_PTR(&machine_time_pulse_us_obj) }, + MICROPY_UNIX_MACHINE_IDLE + #else + // Do nothing. #endif -}; - -STATIC MP_DEFINE_CONST_DICT(machine_module_globals, machine_module_globals_table); - -const mp_obj_module_t mp_module_machine = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t *)&machine_module_globals, -}; - -MP_REGISTER_EXTENSIBLE_MODULE(MP_QSTR_machine, mp_module_machine); - -#endif // MICROPY_PY_MACHINE +} diff --git a/ports/unix/modos.c b/ports/unix/modos.c index 3c7f87f614c0..4a821a4b417a 100644 --- a/ports/unix/modos.c +++ b/ports/unix/modos.c @@ -32,6 +32,7 @@ #include "py/runtime.h" #include "py/mphal.h" +// CIRCUITPY-CHANGE: enhanced getenv #if defined(MICROPY_UNIX_COVERAGE) #include "py/objstr.h" typedef int os_getenv_err_t; diff --git a/ports/unix/mpconfigport.h b/ports/unix/mpconfigport.h index b3630153cb14..3e7ea77f71a1 100644 --- a/ports/unix/mpconfigport.h +++ b/ports/unix/mpconfigport.h @@ -24,6 +24,7 @@ * THE SOFTWARE. */ +// CIRCUITPY-CHANGE #pragma once // Options to control how MicroPython is built for this port, overriding @@ -140,6 +141,9 @@ typedef long mp_off_t; #define MICROPY_STACKLESS_STRICT (0) #endif +// Implementation of the machine module. +#define MICROPY_PY_MACHINE_INCLUDEFILE "ports/unix/modmachine.c" + // Unix-specific configuration of machine.mem*. #define MICROPY_MACHINE_MEM_GET_READ_ADDR mod_machine_mem_get_addr #define MICROPY_MACHINE_MEM_GET_WRITE_ADDR mod_machine_mem_get_addr @@ -148,7 +152,9 @@ typedef long mp_off_t; #define MICROPY_FATFS_RPATH (2) #define MICROPY_FATFS_MAX_SS (4096) #define MICROPY_FATFS_LFN_CODE_PAGE 437 /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */ +// CIRCUITPY-CHANGE: enable FAT32 support #define MICROPY_FATFS_MKFS_FAT32 (1) +// CIRCUITPY-CHANGE: allow FAT label access #define MICROPY_FATFS_USE_LABEL (1) #define MICROPY_ALLOC_PATH_MAX (PATH_MAX) @@ -226,21 +232,11 @@ static inline unsigned long mp_random_seed_init(void) { #include #endif -// If threading is enabled, configure the atomic section. -#if MICROPY_PY_THREAD -#define MICROPY_BEGIN_ATOMIC_SECTION() (mp_thread_unix_begin_atomic_section(), 0xffffffff) -#define MICROPY_END_ATOMIC_SECTION(x) (void)x; mp_thread_unix_end_atomic_section() -#endif - // In lieu of a WFI(), slow down polling from being a tight loop. -#ifndef MICROPY_EVENT_POLL_HOOK -#define MICROPY_EVENT_POLL_HOOK \ - do { \ - extern void mp_handle_pending(bool); \ - mp_handle_pending(true); \ - usleep(500); /* equivalent to mp_hal_delay_us(500) */ \ - } while (0); -#endif +// +// Note that we don't delay for the full TIMEOUT_MS, as execution +// can't be woken from the delay. +#define MICROPY_INTERNAL_WFE(TIMEOUT_MS) mp_hal_delay_us(500) // Configure the implementation of machine.idle(). #include diff --git a/ports/unix/mphalport.h b/ports/unix/mphalport.h index d7827417506c..7f71217632a8 100644 --- a/ports/unix/mphalport.h +++ b/ports/unix/mphalport.h @@ -25,12 +25,19 @@ */ #include #include +// CIRCUITPY-CHANGE: extra include #include #ifndef CHAR_CTRL_C #define CHAR_CTRL_C (3) #endif +// If threading is enabled, configure the atomic section. +#if MICROPY_PY_THREAD +#define MICROPY_BEGIN_ATOMIC_SECTION() (mp_thread_unix_begin_atomic_section(), 0xffffffff) +#define MICROPY_END_ATOMIC_SECTION(x) (void)x; mp_thread_unix_end_atomic_section() +#endif + // CIRCUITPY-CHANGE: mp_hal_set_interrupt_char(int) instead of char void mp_hal_set_interrupt_char(int c); bool mp_hal_is_interrupted(void); diff --git a/ports/unix/unix_mphal.c b/ports/unix/unix_mphal.c index 35c8c8b3f7b3..6e9ad5705368 100644 --- a/ports/unix/unix_mphal.c +++ b/ports/unix/unix_mphal.c @@ -190,13 +190,18 @@ main_term:; return c; } -void mp_hal_stdout_tx_strn(const char *str, size_t len) { +mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) { ssize_t ret; MP_HAL_RETRY_SYSCALL(ret, write(STDOUT_FILENO, str, len), {}); + mp_uint_t written = ret < 0 ? 0 : ret; // CIRCUITPY-CHANGE: need to conditionalize MICROPY_PY_OS_DUPTERM #if MICROPY_PY_OS_DUPTERM - mp_os_dupterm_tx_strn(str, len); + int dupterm_res = mp_os_dupterm_tx_strn(str, len); + if (dupterm_res >= 0) { + written = MIN((mp_uint_t)dupterm_res, written); + } #endif + return written; } // cooked is same as uncooked because the terminal does some postprocessing @@ -246,17 +251,10 @@ uint64_t mp_hal_time_ns(void) { #ifndef mp_hal_delay_ms void mp_hal_delay_ms(mp_uint_t ms) { - #ifdef MICROPY_EVENT_POLL_HOOK mp_uint_t start = mp_hal_ticks_ms(); while (mp_hal_ticks_ms() - start < ms) { - // MICROPY_EVENT_POLL_HOOK does usleep(500). - MICROPY_EVENT_POLL_HOOK + mp_event_wait_ms(1); } - #else - // TODO: POSIX et al. define usleep() as guaranteedly capable only of 1s sleep: - // "The useconds argument shall be less than one million." - usleep(ms * 1000); - #endif } #endif diff --git a/ports/unix/variants/coverage/mpconfigvariant.mk b/ports/unix/variants/coverage/mpconfigvariant.mk index 6b6778c322fc..9721c2803e25 100644 --- a/ports/unix/variants/coverage/mpconfigvariant.mk +++ b/ports/unix/variants/coverage/mpconfigvariant.mk @@ -1,6 +1,7 @@ # Disable optimisations and enable assert() on coverage builds. DEBUG ?= 1 +# CIRCUITPY-CHANGE: add exception chaining CFLAGS += \ -fprofile-arcs -ftest-coverage \ -Wformat -Wmissing-declarations -Wmissing-prototypes \ @@ -13,6 +14,7 @@ LDFLAGS += -fprofile-arcs -ftest-coverage FROZEN_MANIFEST ?= $(VARIANT_DIR)/manifest.py USER_C_MODULES = $(TOP)/examples/usercmodule +# CIRCUITPY-CHANGE: use CircuitPython bindings and implementations SRC_QRIO := $(patsubst ../../%,%,$(wildcard ../../shared-bindings/qrio/*.c ../../shared-module/qrio/*.c ../../lib/quirc/lib/*.c)) SRC_C += $(SRC_QRIO) diff --git a/ports/unix/variants/minimal/mpconfigvariant.h b/ports/unix/variants/minimal/mpconfigvariant.h index 2e2bea58a034..0dbfbb3d1cd4 100644 --- a/ports/unix/variants/minimal/mpconfigvariant.h +++ b/ports/unix/variants/minimal/mpconfigvariant.h @@ -47,6 +47,7 @@ #define MICROPY_COMP_CONST_LITERAL (1) #define MICROPY_COMP_CONST_TUPLE (1) #define MICROPY_COMP_DOUBLE_TUPLE_ASSIGN (1) +#define MICROPY_ENABLE_COMPILER (1) #define MICROPY_ENABLE_EXTERNAL_IMPORT (1) #define MICROPY_FULL_CHECKS (1) #define MICROPY_HELPER_REPL (1) diff --git a/ports/unix/variants/mpconfigvariant_common.h b/ports/unix/variants/mpconfigvariant_common.h index 082938ed5f4f..981babca9f10 100644 --- a/ports/unix/variants/mpconfigvariant_common.h +++ b/ports/unix/variants/mpconfigvariant_common.h @@ -116,3 +116,4 @@ // Enable the "machine" module, mostly for machine.mem*. #define MICROPY_PY_MACHINE (1) #define MICROPY_PY_MACHINE_PULSE (1) +#define MICROPY_PY_MACHINE_PIN_BASE (1) diff --git a/ports/unix/variants/standard/manifest.py b/ports/unix/variants/standard/manifest.py index 08295fc678d0..5d8c670e9a1e 100644 --- a/ports/unix/variants/standard/manifest.py +++ b/ports/unix/variants/standard/manifest.py @@ -1 +1,3 @@ include("$(PORT_DIR)/variants/manifest.py") + +# CIRCUITPY-CHANGE: Do not include extmod/aysncio diff --git a/py/argcheck.c b/py/argcheck.c index ad39b2fcd8f9..9302dec96c39 100644 --- a/py/argcheck.c +++ b/py/argcheck.c @@ -29,6 +29,7 @@ #include "py/runtime.h" +// CIRCUITPY-CHANGE: PLACE_IN_ITCM void PLACE_IN_ITCM(mp_arg_check_num_sig)(size_t n_args, size_t n_kw, uint32_t sig) { // TODO maybe take the function name as an argument so we can print nicer error messages @@ -50,6 +51,7 @@ void PLACE_IN_ITCM(mp_arg_check_num_sig)(size_t n_args, size_t n_kw, uint32_t si #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_arg_error_terse_mismatch(); #else + // CIRCUITPY-CHANGE: specific mp_raise routine mp_raise_TypeError_varg(MP_ERROR_TEXT("function takes %d positional arguments but %d were given"), n_args_min, n_args); #endif @@ -59,6 +61,7 @@ void PLACE_IN_ITCM(mp_arg_check_num_sig)(size_t n_args, size_t n_kw, uint32_t si #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_arg_error_terse_mismatch(); #else + // CIRCUITPY-CHANGE: specific mp_raise routine mp_raise_TypeError_varg( MP_ERROR_TEXT("function missing %d required positional arguments"), n_args_min - n_args); @@ -67,6 +70,7 @@ void PLACE_IN_ITCM(mp_arg_check_num_sig)(size_t n_args, size_t n_kw, uint32_t si #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_arg_error_terse_mismatch(); #else + // CIRCUITPY-CHANGE: specific mp_raise routine mp_raise_TypeError_varg( MP_ERROR_TEXT("function expected at most %d arguments, got %d"), n_args_max, n_args); @@ -75,6 +79,7 @@ void PLACE_IN_ITCM(mp_arg_check_num_sig)(size_t n_args, size_t n_kw, uint32_t si } } +// CIRCUITPY-CHANGE: better keyword arg checking in next two routines inline void mp_arg_check_num_kw_array(size_t n_args, size_t n_kw, size_t n_args_min, size_t n_args_max, bool takes_kw) { mp_arg_check_num_sig(n_args, n_kw, MP_OBJ_FUN_MAKE_SIG(n_args_min, n_args_max, takes_kw)); } @@ -176,7 +181,7 @@ NORETURN void mp_arg_error_unimpl_kw(void) { } #endif - +// CIRCUITPY-CHANGE: more specific mp_raise routines mp_int_t mp_arg_validate_int(mp_int_t i, mp_int_t required_i, qstr arg_name) { if (i != required_i) { mp_raise_ValueError_varg(MP_ERROR_TEXT("%q must be %d"), arg_name, required_i); diff --git a/py/asmarm.h b/py/asmarm.h index ed8dc5f03921..0fc1efe5ecd8 100644 --- a/py/asmarm.h +++ b/py/asmarm.h @@ -132,6 +132,7 @@ void asm_arm_bx_reg(asm_arm_t *as, uint reg_src); // Holds a pointer to mp_fun_table #define ASM_ARM_REG_FUN_TABLE ASM_ARM_REG_R7 +// CIRCUITPY-CHANGE: prevent #if warning #if defined(GENERIC_ASM_API) && GENERIC_ASM_API // The following macros provide a (mostly) arch-independent API to @@ -183,8 +184,6 @@ void asm_arm_bx_reg(asm_arm_t *as, uint reg_src); #define ASM_MOV_LOCAL_REG(as, local_num, reg_src) asm_arm_mov_local_reg((as), (local_num), (reg_src)) #define ASM_MOV_REG_IMM(as, reg_dest, imm) asm_arm_mov_reg_i32_optimised((as), (reg_dest), (imm)) -#define ASM_MOV_REG_IMM_FIX_U16(as, reg_dest, imm) asm_arm_mov_reg_i32((as), (reg_dest), (imm)) -#define ASM_MOV_REG_IMM_FIX_WORD(as, reg_dest, imm) asm_arm_mov_reg_i32((as), (reg_dest), (imm)) #define ASM_MOV_REG_LOCAL(as, reg_dest, local_num) asm_arm_mov_reg_local((as), (reg_dest), (local_num)) #define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_arm_mov_reg_reg((as), (reg_dest), (reg_src)) #define ASM_MOV_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_arm_mov_reg_local_addr((as), (reg_dest), (local_num)) diff --git a/py/asmthumb.h b/py/asmthumb.h index 8961b0f79610..61b4928d8d9c 100644 --- a/py/asmthumb.h +++ b/py/asmthumb.h @@ -350,6 +350,7 @@ void asm_thumb_b_rel12(asm_thumb_t *as, int rel); // Holds a pointer to mp_fun_table #define ASM_THUMB_REG_FUN_TABLE ASM_THUMB_REG_R7 +// CIRCUITPY-CHANGE: prevent #if warning #if defined(GENERIC_ASM_API) && GENERIC_ASM_API // The following macros provide a (mostly) arch-independent API to @@ -401,7 +402,6 @@ void asm_thumb_b_rel12(asm_thumb_t *as, int rel); #define ASM_MOV_LOCAL_REG(as, local_num, reg) asm_thumb_mov_local_reg((as), (local_num), (reg)) #define ASM_MOV_REG_IMM(as, reg_dest, imm) asm_thumb_mov_reg_i32_optimised((as), (reg_dest), (imm)) -#define ASM_MOV_REG_IMM_FIX_WORD(as, reg_dest, imm) asm_thumb_mov_reg_i32((as), (reg_dest), (imm)) #define ASM_MOV_REG_LOCAL(as, reg_dest, local_num) asm_thumb_mov_reg_local((as), (reg_dest), (local_num)) #define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_thumb_mov_reg_reg((as), (reg_dest), (reg_src)) #define ASM_MOV_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_thumb_mov_reg_local_addr((as), (reg_dest), (local_num)) diff --git a/py/asmx64.h b/py/asmx64.h index a4eaa12984b9..2d4bf8d33ced 100644 --- a/py/asmx64.h +++ b/py/asmx64.h @@ -124,6 +124,7 @@ void asm_x64_call_ind(asm_x64_t *as, size_t fun_id, int temp_r32); // Holds a pointer to mp_fun_table #define ASM_X64_REG_FUN_TABLE ASM_X64_REG_RBP +// CIRCUITPY-CHANGE: prevent #if warning #if defined(GENERIC_ASM_API) && GENERIC_ASM_API // The following macros provide a (mostly) arch-independent API to @@ -186,8 +187,6 @@ void asm_x64_call_ind(asm_x64_t *as, size_t fun_id, int temp_r32); #define ASM_MOV_LOCAL_REG(as, local_num, reg_src) asm_x64_mov_r64_to_local((as), (reg_src), (local_num)) #define ASM_MOV_REG_IMM(as, reg_dest, imm) asm_x64_mov_i64_to_r64_optimised((as), (imm), (reg_dest)) -#define ASM_MOV_REG_IMM_FIX_U16(as, reg_dest, imm) asm_x64_mov_i32_to_r64((as), (imm), (reg_dest)) -#define ASM_MOV_REG_IMM_FIX_WORD(as, reg_dest, imm) asm_x64_mov_i32_to_r64((as), (imm), (reg_dest)) #define ASM_MOV_REG_LOCAL(as, reg_dest, local_num) asm_x64_mov_local_to_r64((as), (local_num), (reg_dest)) #define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_x64_mov_r64_r64((as), (reg_dest), (reg_src)) #define ASM_MOV_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_x64_mov_local_addr_to_r64((as), (local_num), (reg_dest)) diff --git a/py/asmx86.h b/py/asmx86.h index f344c78f0930..10532265c28e 100644 --- a/py/asmx86.h +++ b/py/asmx86.h @@ -120,6 +120,7 @@ void asm_x86_call_ind(asm_x86_t *as, size_t fun_id, mp_uint_t n_args, int temp_r // Holds a pointer to mp_fun_table #define ASM_X86_REG_FUN_TABLE ASM_X86_REG_EBP +// CIRCUITPY-CHANGE: prevent #if warning #if defined(GENERIC_ASM_API) && GENERIC_ASM_API // The following macros provide a (mostly) arch-independent API to @@ -181,8 +182,6 @@ void asm_x86_call_ind(asm_x86_t *as, size_t fun_id, mp_uint_t n_args, int temp_r #define ASM_MOV_LOCAL_REG(as, local_num, reg_src) asm_x86_mov_r32_to_local((as), (reg_src), (local_num)) #define ASM_MOV_REG_IMM(as, reg_dest, imm) asm_x86_mov_i32_to_r32((as), (imm), (reg_dest)) -#define ASM_MOV_REG_IMM_FIX_U16(as, reg_dest, imm) asm_x86_mov_i32_to_r32((as), (imm), (reg_dest)) -#define ASM_MOV_REG_IMM_FIX_WORD(as, reg_dest, imm) asm_x86_mov_i32_to_r32((as), (imm), (reg_dest)) #define ASM_MOV_REG_LOCAL(as, reg_dest, local_num) asm_x86_mov_local_to_r32((as), (local_num), (reg_dest)) #define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_x86_mov_r32_r32((as), (reg_dest), (reg_src)) #define ASM_MOV_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_x86_mov_local_addr_to_r32((as), (local_num), (reg_dest)) diff --git a/py/asmxtensa.h b/py/asmxtensa.h index 60205f8f9c70..62fdb5df89a8 100644 --- a/py/asmxtensa.h +++ b/py/asmxtensa.h @@ -287,6 +287,7 @@ void asm_xtensa_call_ind_win(asm_xtensa_t *as, uint idx); #define ASM_XTENSA_REG_FUN_TABLE ASM_XTENSA_REG_A15 #define ASM_XTENSA_REG_FUN_TABLE_WIN ASM_XTENSA_REG_A7 +// CIRCUITPY-CHANGE: prevent #if warning #if defined(GENERIC_ASM_API) && GENERIC_ASM_API // The following macros provide a (mostly) arch-independent API to @@ -366,8 +367,6 @@ void asm_xtensa_call_ind_win(asm_xtensa_t *as, uint idx); #define ASM_MOV_LOCAL_REG(as, local_num, reg_src) asm_xtensa_mov_local_reg((as), ASM_NUM_REGS_SAVED + (local_num), (reg_src)) #define ASM_MOV_REG_IMM(as, reg_dest, imm) asm_xtensa_mov_reg_i32_optimised((as), (reg_dest), (imm)) -#define ASM_MOV_REG_IMM_FIX_U16(as, reg_dest, imm) asm_xtensa_mov_reg_i32((as), (reg_dest), (imm)) -#define ASM_MOV_REG_IMM_FIX_WORD(as, reg_dest, imm) asm_xtensa_mov_reg_i32((as), (reg_dest), (imm)) #define ASM_MOV_REG_LOCAL(as, reg_dest, local_num) asm_xtensa_mov_reg_local((as), (reg_dest), ASM_NUM_REGS_SAVED + (local_num)) #define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_mov_n((as), (reg_dest), (reg_src)) #define ASM_MOV_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_xtensa_mov_reg_local_addr((as), (reg_dest), ASM_NUM_REGS_SAVED + (local_num)) diff --git a/py/bc.c b/py/bc.c index 0dc7229a2c4b..752bd085372a 100644 --- a/py/bc.c +++ b/py/bc.c @@ -97,9 +97,11 @@ STATIC NORETURN void fun_pos_args_mismatch(mp_obj_fun_bc_t *f, size_t expected, mp_arg_error_terse_mismatch(); #elif MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_NORMAL (void)f; + // CIRCUITPY-CHANGE: more specific mp_raise routine mp_raise_TypeError_varg( MP_ERROR_TEXT("function takes %d positional arguments but %d were given"), expected, given); #elif MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED + // CIRCUITPY-CHANGE: more specific mp_raise routine mp_raise_TypeError_varg( MP_ERROR_TEXT("%q() takes %d positional arguments but %d were given"), mp_obj_fun_get_name(MP_OBJ_FROM_PTR(f)), expected, given); @@ -281,6 +283,7 @@ STATIC void mp_setup_code_state_helper(mp_code_state_t *code_state, size_t n_arg if (elem != NULL) { code_state_state[n_state - 1 - n_pos_args - i] = elem->value; } else { + // CIRCUITPY-CHANGE: more specific mp_raise routine mp_raise_TypeError_varg( MP_ERROR_TEXT("function missing required keyword argument '%q'"), MP_OBJ_QSTR_VALUE(arg_names[n_pos_args + i])); diff --git a/py/bc.h b/py/bc.h index b732028dbfcb..f2d4c5ee67ce 100644 --- a/py/bc.h +++ b/py/bc.h @@ -101,6 +101,7 @@ out_byte(out_env, z); \ } while (0) +// CIRCUITPY-CHANGE: avoid warnings #define MP_BC_PRELUDE_SIG_DECODE_INTO(ip, S, E, F, A, K, D) \ do { \ uint8_t z = *(ip)++; \ diff --git a/py/binary.c b/py/binary.c index 3157f7fe0b55..d80d8e6e583a 100644 --- a/py/binary.c +++ b/py/binary.c @@ -51,6 +51,7 @@ size_t mp_binary_get_size(char struct_type, char val_type, size_t *palign) { switch (val_type) { case 'b': case 'B': + // CIRCUITPY-CHANGE: x code: padding case 'x': size = 1; break; @@ -70,6 +71,7 @@ size_t mp_binary_get_size(char struct_type, char val_type, size_t *palign) { case 'Q': size = 8; break; + // CIRCUITPY-CHANGE: non-standard typecodes can be turned off #if MICROPY_NONSTANDARD_TYPECODES case 'P': case 'O': @@ -97,6 +99,7 @@ size_t mp_binary_get_size(char struct_type, char val_type, size_t *palign) { case BYTEARRAY_TYPECODE: case 'b': case 'B': + // CIRCUITPY-CHANGE: x code: padding case 'x': align = size = 1; break; @@ -120,6 +123,7 @@ size_t mp_binary_get_size(char struct_type, char val_type, size_t *palign) { align = alignof(long long); size = sizeof(long long); break; + // CIRCUITPY-CHANGE: non-standard typecodes can be turned off #if MICROPY_NONSTANDARD_TYPECODES case 'P': case 'O': @@ -186,6 +190,7 @@ mp_obj_t mp_binary_get_val_array(char typecode, void *p, size_t index) { case 'd': return mp_obj_new_float_from_d(((double *)p)[index]); #endif + // CIRCUITPY-CHANGE: non-standard typecodes can be turned off #if MICROPY_NONSTANDARD_TYPECODES // Extension to CPython: array of objects case 'O': @@ -243,6 +248,7 @@ mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte *p_base, byte * long long val = mp_binary_get_int(size, is_signed(val_type), (struct_type == '>'), p); + // CIRCUITPY-CHANGE: non-standard typecodes can be turned off if (MICROPY_NONSTANDARD_TYPECODES && (val_type == 'O')) { return (mp_obj_t)(mp_uint_t)val; #if MICROPY_NONSTANDARD_TYPECODES @@ -316,6 +322,7 @@ void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte *p mp_uint_t val; switch (val_type) { + // CIRCUITPY-CHANGE: non-standard typecodes can be turned off #if MICROPY_NONSTANDARD_TYPECODES case 'O': val = (mp_uint_t)val_in; @@ -350,6 +357,7 @@ void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte *p } #endif default: { + // CIRCUITPY-CHANGE: add overflow checks bool signed_type = is_signed(val_type); #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE if (mp_obj_is_exact_type(val_in, &mp_type_int)) { @@ -389,6 +397,7 @@ void mp_binary_set_val_array(char typecode, void *p, size_t index, mp_obj_t val_ ((double *)p)[index] = mp_obj_get_float_to_d(val_in); break; #endif + // CIRCUITPY-CHANGE: non-standard typecodes can be turned off #if MICROPY_NONSTANDARD_TYPECODES // Extension to CPython: array of objects case 'O': @@ -396,6 +405,7 @@ void mp_binary_set_val_array(char typecode, void *p, size_t index, mp_obj_t val_ break; #endif default: { + // CIRCUITPY-CHANGE: add overflow checks size_t size = mp_binary_get_size('@', typecode, NULL); bool signed_type = is_signed(typecode); @@ -459,6 +469,7 @@ void mp_binary_set_val_array_from_int(char typecode, void *p, size_t index, mp_i ((double *)p)[index] = (double)val; break; #endif + // CIRCUITPY-CHANGE: non-standard typecodes can be turned off #if MICROPY_NONSTANDARD_TYPECODES // Extension to CPython: array of pointers case 'P': diff --git a/py/builtinevex.c b/py/builtinevex.c index 97eab7fad94d..73fca5d39bf5 100644 --- a/py/builtinevex.c +++ b/py/builtinevex.c @@ -136,17 +136,18 @@ STATIC mp_obj_t eval_exec_helper(size_t n_args, const mp_obj_t *args, mp_parse_i } #endif - // Extract the source code. - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ); // create the lexer // MP_PARSE_SINGLE_INPUT is used to indicate a file input mp_lexer_t *lex; if (MICROPY_PY_BUILTINS_EXECFILE && parse_input_kind == MP_PARSE_SINGLE_INPUT) { - lex = mp_lexer_new_from_file(bufinfo.buf); + lex = mp_lexer_new_from_file(mp_obj_str_get_qstr(args[0])); parse_input_kind = MP_PARSE_FILE_INPUT; } else { + // Extract the source code. + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ); + lex = mp_lexer_new_from_str_len(MP_QSTR__lt_string_gt_, bufinfo.buf, bufinfo.len, 0); } diff --git a/py/builtinhelp.c b/py/builtinhelp.c index fe7653b9a26f..7f145a9ea596 100644 --- a/py/builtinhelp.c +++ b/py/builtinhelp.c @@ -77,6 +77,7 @@ STATIC void mp_help_add_from_names(mp_obj_t list, const char *name) { } #endif +// CIRCUITPY-CHANGE: move extern to top level to prevent warnings #if MICROPY_MODULE_FROZEN extern const char mp_frozen_names[]; #endif @@ -122,6 +123,7 @@ STATIC void mp_help_print_modules(void) { #if MICROPY_ENABLE_EXTERNAL_IMPORT // let the user know there may be other modules available from the filesystem + // CIRCUITPY-CHANGE: make translatable serial_write_compressed(MP_ERROR_TEXT("Plus any modules on the filesystem\n")); #endif } @@ -138,9 +140,11 @@ STATIC void mp_help_print_obj(const mp_obj_t obj) { const mp_obj_type_t *type = mp_obj_get_type(obj); // try to print something sensible about the given object + // CIRCUITPY-CHANGE: make translatable mp_cprintf(MP_PYTHON_PRINTER, MP_ERROR_TEXT("object ")); mp_obj_print(obj, PRINT_STR); + // CIRCUITPY-CHANGE: make translatable mp_cprintf(MP_PYTHON_PRINTER, MP_ERROR_TEXT(" is of type %q\n"), type->name); mp_map_t *map = NULL; @@ -166,6 +170,7 @@ STATIC void mp_help_print_obj(const mp_obj_t obj) { STATIC mp_obj_t mp_builtin_help(size_t n_args, const mp_obj_t *args) { if (n_args == 0) { + // CIRCUITPY-CHANGE: make translatable // print a general help message. Translate only works on single strings on one line. mp_cprintf(MP_PYTHON_PRINTER, MP_ERROR_TEXT("Welcome to Adafruit CircuitPython %s!\n\nVisit circuitpython.org for more information.\n\nTo list built-in modules type `help(\"modules\")`.\n"), diff --git a/py/builtinimport.c b/py/builtinimport.c index 9bc8b2b107b4..2e25c390a209 100644 --- a/py/builtinimport.c +++ b/py/builtinimport.c @@ -166,11 +166,11 @@ STATIC void do_load_from_lexer(mp_module_context_t *context, mp_lexer_t *lex) { #endif #if (MICROPY_HAS_FILE_READER && MICROPY_PERSISTENT_CODE_LOAD) || MICROPY_MODULE_FROZEN_MPY -STATIC void do_execute_raw_code(const mp_module_context_t *context, const mp_raw_code_t *rc, const char *source_name) { - (void)source_name; - +STATIC void do_execute_raw_code(const mp_module_context_t *context, const mp_raw_code_t *rc, qstr source_name) { #if MICROPY_PY___FILE__ - mp_store_attr(MP_OBJ_FROM_PTR(&context->module), MP_QSTR___file__, MP_OBJ_NEW_QSTR(qstr_from_str(source_name))); + mp_store_attr(MP_OBJ_FROM_PTR(&context->module), MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name)); + #else + (void)source_name; #endif // execute the module in its context @@ -226,7 +226,12 @@ STATIC void do_load(mp_module_context_t *module_obj, vstr_t *file) { if (frozen_type == MP_FROZEN_MPY) { const mp_frozen_module_t *frozen = modref; module_obj->constants = frozen->constants; - do_execute_raw_code(module_obj, frozen->rc, file_str + frozen_path_prefix_len); + #if MICROPY_PY___FILE__ + qstr frozen_file_qstr = qstr_from_str(file_str + frozen_path_prefix_len); + #else + qstr frozen_file_qstr = MP_QSTRnull; + #endif + do_execute_raw_code(module_obj, frozen->rc, frozen_file_qstr); return; } #endif @@ -234,14 +239,16 @@ STATIC void do_load(mp_module_context_t *module_obj, vstr_t *file) { #endif // MICROPY_MODULE_FROZEN + qstr file_qstr = qstr_from_str(file_str); + // If we support loading .mpy files then check if the file extension is of // the correct format and, if so, load and execute the file. #if MICROPY_HAS_FILE_READER && MICROPY_PERSISTENT_CODE_LOAD if (file_str[file->len - 3] == 'm') { mp_compiled_module_t cm; cm.context = module_obj; - mp_raw_code_load_file(file_str, &cm); - do_execute_raw_code(cm.context, cm.rc, file_str); + mp_raw_code_load_file(file_qstr, &cm); + do_execute_raw_code(cm.context, cm.rc, file_qstr); return; } #endif @@ -249,12 +256,13 @@ STATIC void do_load(mp_module_context_t *module_obj, vstr_t *file) { // If we can compile scripts then load the file and compile and execute it. #if MICROPY_ENABLE_COMPILER { - mp_lexer_t *lex = mp_lexer_new_from_file(file_str); + mp_lexer_t *lex = mp_lexer_new_from_file(file_qstr); do_load_from_lexer(module_obj, lex); return; } #else // If we get here then the file was not frozen and we can't compile scripts. + // CIRCUITPY-CHANGE: use more specific mp_raise mp_raise_ImportError(MP_ERROR_TEXT("script compilation not supported")); #endif } diff --git a/py/compile.c b/py/compile.c index 4752d508c8b7..c73fa500469c 100644 --- a/py/compile.c +++ b/py/compile.c @@ -923,6 +923,7 @@ STATIC void compile_decorated(compiler_t *comp, mp_parse_node_struct_t *pns) { mp_parse_node_struct_t *pns0 = (mp_parse_node_struct_t *)pns_body->nodes[0]; body_name = compile_funcdef_helper(comp, pns0, emit_options); scope_t *fscope = (scope_t *)pns0->nodes[4]; + // CIRCUITPY-CHANGE: distinguish generators and async routines fscope->scope_flags |= MP_SCOPE_FLAG_GENERATOR | MP_SCOPE_FLAG_ASYNC; #endif } else { @@ -1650,9 +1651,11 @@ STATIC void compile_try_except(compiler_t *comp, mp_parse_node_t pn_body, int n_ if (qstr_exception_local != 0) { EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE); EMIT_ARG(label_assign, l3); + EMIT_ARG(adjust_stack_size, 1); // stack adjust for possible return value EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE); compile_store_id(comp, qstr_exception_local); compile_delete_id(comp, qstr_exception_local); + EMIT_ARG(adjust_stack_size, -1); compile_decrease_except_level(comp); } @@ -1682,9 +1685,18 @@ STATIC void compile_try_finally(compiler_t *comp, mp_parse_node_t pn_body, int n } else { compile_try_except(comp, pn_body, n_except, pn_except, pn_else); } + + // If the code reaches this point then the try part of the try-finally exited normally. + // This is indicated to the runtime by None sitting on the stack. EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE); + + // Compile the finally block. + // The stack needs to be adjusted by 1 to account for the possibility that the finally is + // being executed as part of a return, and the return value is on the top of the stack. EMIT_ARG(label_assign, l_finally_block); + EMIT_ARG(adjust_stack_size, 1); compile_node(comp, pn_finally); + EMIT_ARG(adjust_stack_size, -1); compile_decrease_except_level(comp); } @@ -1963,6 +1975,7 @@ STATIC void compile_async_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { // async def compile_funcdef(comp, pns0); scope_t *fscope = (scope_t *)pns0->nodes[4]; + // CIRCUITPY-CHANGE: distinguish generators and async routines fscope->scope_flags |= MP_SCOPE_FLAG_GENERATOR | MP_SCOPE_FLAG_ASYNC; } else { // async for/with; first verify the scope is a generator diff --git a/py/dynruntime.h b/py/dynruntime.h index 9be2d5b64328..d80ce10f577b 100644 --- a/py/dynruntime.h +++ b/py/dynruntime.h @@ -125,7 +125,8 @@ static inline void *m_realloc_dyn(void *ptr, size_t new_num_bytes) { #define mp_obj_get_int_truncated(o) (mp_fun_table.native_from_obj(o, MP_NATIVE_TYPE_UINT)) #define mp_obj_str_get_str(s) (mp_obj_str_get_data_dyn((s), NULL)) #define mp_obj_str_get_data(o, len) (mp_obj_str_get_data_dyn((o), (len))) -#define mp_get_buffer_raise(o, bufinfo, fl) (mp_fun_table.get_buffer_raise((o), (bufinfo), (fl))) +#define mp_get_buffer(o, bufinfo, fl) (mp_fun_table.get_buffer((o), (bufinfo), (fl))) +#define mp_get_buffer_raise(o, bufinfo, fl) (mp_fun_table.get_buffer((o), (bufinfo), (fl) | MP_BUFFER_RAISE_IF_UNSUPPORTED)) #define mp_get_stream_raise(s, flags) (mp_fun_table.get_stream_raise((s), (flags))) #define mp_obj_is_true(o) (mp_fun_table.native_from_obj(o, MP_NATIVE_TYPE_BOOL)) @@ -137,6 +138,7 @@ static inline void *m_realloc_dyn(void *ptr, size_t new_num_bytes) { #define mp_obj_malloc_helper(n, t) (mp_obj_malloc_helper_dyn(n, t)) +// CIRCUITPY-CHANGE: new routine #define mp_obj_assert_native_inited(o) (mp_fun_table.assert_native_inited((o))) static inline mp_obj_t mp_obj_new_str_of_type_dyn(const mp_obj_type_t *type, const byte *data, size_t len) { @@ -235,6 +237,7 @@ static inline void *mp_obj_malloc_helper_dyn(size_t num_bytes, const mp_obj_type #define nlr_raise(o) (mp_raise_dyn(o)) #define mp_raise_type_arg(type, arg) (mp_raise_dyn(mp_obj_new_exception_arg1_dyn((type), (arg)))) +// CIRCUITPY-CHANGE: use str #define mp_raise_msg(type, msg) (mp_fun_table.raise_msg_str((type), (msg))) #define mp_raise_OSError(er) (mp_raise_OSError_dyn(er)) #define mp_raise_NotImplementedError(msg) (mp_raise_msg(&mp_type_NotImplementedError, (msg))) @@ -252,6 +255,7 @@ static NORETURN inline void mp_raise_dyn(mp_obj_t o) { } } +// CIRCUITPY-CHANGE: new routine static NORETURN inline void mp_raise_arg1(const mp_obj_type_t *exc_type, mp_obj_t arg) { mp_fun_table.raise(mp_obj_new_exception_arg1_dyn(exc_type, arg)); for (;;) { diff --git a/py/emitglue.c b/py/emitglue.c index 9d6e59349f6a..d78ef4831596 100644 --- a/py/emitglue.c +++ b/py/emitglue.c @@ -43,6 +43,7 @@ #define DEBUG_printf DEBUG_printf #define DEBUG_OP_printf(...) DEBUG_printf(__VA_ARGS__) #else // don't print debugging info +// CIRCUITPY-CHANGE: prevent warnings #define DEBUG_PRINT (0) #define DEBUG_printf(...) (void)0 #define DEBUG_OP_printf(...) (void)0 @@ -88,6 +89,7 @@ void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, const byte *code, mp_prof_extract_prelude(code, prelude); #endif + // CIRCUITPY-CHANGE: prevent warning #if defined(DEBUG_PRINT) && DEBUG_PRINT #if !(MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS) const size_t len = 0; @@ -111,6 +113,7 @@ void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void // so that the generated native code which was created in data RAM will // be available for execution from instruction RAM. #if MICROPY_EMIT_THUMB || MICROPY_EMIT_INLINE_THUMB + // CIRCUITPY-CHANGE: prevent warning #if defined(__ICACHE_PRESENT) && __ICACHE_PRESENT == 1 // Flush D-cache, so the code emitted is stored in RAM. MP_HAL_CLEAN_DCACHE(fun_data, fun_len); @@ -189,6 +192,7 @@ mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, const mp_module case MP_CODE_NATIVE_VIPER: fun = mp_obj_new_fun_native(def_args, rc->fun_data, context, rc->children); // Check for a generator function, and if so change the type of the object + // CIRCUITPY-CHANGE: distinguish generators and async if ((rc->scope_flags & MP_SCOPE_FLAG_ASYNC) != 0) { ((mp_obj_base_t *)MP_OBJ_TO_PTR(fun))->type = &mp_type_native_coro_wrap; } else if ((rc->scope_flags & MP_SCOPE_FLAG_GENERATOR) != 0) { @@ -206,6 +210,7 @@ mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, const mp_module assert(rc->kind == MP_CODE_BYTECODE); fun = mp_obj_new_fun_bc(def_args, rc->fun_data, context, rc->children); // check for generator functions and if so change the type of the object + // CIRCUITPY-CHANGE: distinguish generators and async // A generator is MP_SCOPE_FLAG_ASYNC | MP_SCOPE_FLAG_GENERATOR, // so check for ASYNC first. #if MICROPY_PY_ASYNC_AWAIT diff --git a/py/formatfloat.c b/py/formatfloat.c index a2855b8afc3c..b4348122ff42 100644 --- a/py/formatfloat.c +++ b/py/formatfloat.c @@ -80,6 +80,7 @@ static inline int fp_isless1(float x) { #elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE +// CIRCUITPY-CHANGE: prevent warnings #pragma GCC diagnostic ignored "-Wfloat-equal" #define FPTYPE double #define FPCONST(x) x diff --git a/py/gc.c b/py/gc.c index dcf07c6467e7..7c60ab79627e 100644 --- a/py/gc.c +++ b/py/gc.c @@ -305,14 +305,28 @@ STATIC bool gc_try_add_heap(size_t failed_alloc) { // - If the new heap won't fit in the available free space, add the largest // new heap that will fit (this may lead to failed system heap allocations // elsewhere, but some allocation will likely fail in this circumstance!) - size_t total_heap = 0; + + // Compute total number of blocks in the current heap. + size_t total_blocks = 0; for (mp_state_mem_area_t *area = &MP_STATE_MEM(area); area != NULL; area = NEXT_AREA(area)) { - total_heap += area->gc_pool_end - area->gc_alloc_table_start; - total_heap += ALLOC_TABLE_GAP_BYTE + sizeof(mp_state_mem_area_t); + total_blocks += area->gc_alloc_table_byte_len * BLOCKS_PER_ATB; } + // Compute bytes needed to build a heap with total_blocks blocks. + size_t total_heap = + total_blocks / BLOCKS_PER_ATB + #if MICROPY_ENABLE_FINALISER + + total_blocks / BLOCKS_PER_FTB + #endif + + total_blocks * BYTES_PER_BLOCK + + ALLOC_TABLE_GAP_BYTE + + sizeof(mp_state_mem_area_t); + + // Round up size to the nearest multiple of BYTES_PER_BLOCK. + total_heap = (total_heap + BYTES_PER_BLOCK - 1) & (~(BYTES_PER_BLOCK - 1)); + DEBUG_printf("total_heap " UINT_FMT " bytes\n", total_heap); size_t to_alloc = MIN(avail, MAX(total_heap, needed)); @@ -851,6 +865,7 @@ void *gc_alloc(size_t n_bytes, unsigned int alloc_flags) { } #endif + // CIRCUITPY-CHANGE #if CIRCUITPY_DEBUG gc_dump_alloc_table(&mp_plat_print); #endif @@ -936,6 +951,7 @@ void *gc_alloc(size_t n_bytes, unsigned int alloc_flags) { gc_dump_alloc_table(&mp_plat_print); #endif + // CIRCUITPY-CHANGE #if CIRCUITPY_MEMORYMONITOR memorymonitor_track_allocation(end_block - start_block + 1); #endif diff --git a/py/gc.h b/py/gc.h index 22b958980fdb..5f4b18f7e64b 100644 --- a/py/gc.h +++ b/py/gc.h @@ -30,6 +30,7 @@ #include #include "py/mpprint.h" +// CIRCUITPY-CHANGE #include "py/mpconfig.h" #include "py/mpstate.h" #include "py/misc.h" @@ -77,14 +78,8 @@ enum { void *gc_alloc(size_t n_bytes, unsigned int alloc_flags); void gc_free(void *ptr); // does not call finaliser size_t gc_nbytes(const void *ptr); -bool gc_has_finaliser(const void *ptr); void *gc_realloc(void *ptr, size_t n_bytes, bool allow_move); -// CIRCUITPY-CHANGE -// Prevents a pointer from ever being freed because it establishes a permanent reference to it. Use -// very sparingly because it can leak memory. -bool gc_never_free(void *ptr); - // CIRCUITPY-CHANGE // True if the pointer is on the MP heap. Doesn't require that it is the start // of a block. diff --git a/py/lexer.c b/py/lexer.c index 6b28f2215227..5e911a1a2333 100644 --- a/py/lexer.c +++ b/py/lexer.c @@ -527,14 +527,14 @@ STATIC void parse_string_literal(mp_lexer_t *lex, bool is_raw, bool is_fstring) vstr_cut_tail_bytes(&lex->vstr, n_closing); } +// This function returns whether it has crossed a newline or not. +// It therefore always return true if stop_at_newline is true STATIC bool skip_whitespace(mp_lexer_t *lex, bool stop_at_newline) { - bool had_physical_newline = false; while (!is_end(lex)) { if (is_physical_newline(lex)) { if (stop_at_newline && lex->nested_bracket_level == 0) { - break; + return true; } - had_physical_newline = true; next_char(lex); } else if (is_whitespace(lex)) { next_char(lex); @@ -543,16 +543,16 @@ STATIC bool skip_whitespace(mp_lexer_t *lex, bool stop_at_newline) { while (!is_end(lex) && !is_physical_newline(lex)) { next_char(lex); } - // had_physical_newline will be set on next loop + // will return true on next loop } else if (is_char_and(lex, '\\', '\n')) { - // line-continuation, so don't set had_physical_newline + // line-continuation, so don't return true next_char(lex); next_char(lex); } else { break; } } - return had_physical_newline; + return false; } void mp_lexer_to_next(mp_lexer_t *lex) { @@ -577,7 +577,10 @@ void mp_lexer_to_next(mp_lexer_t *lex) { vstr_reset(&lex->vstr); // skip white space and comments - bool had_physical_newline = skip_whitespace(lex, false); + // set the newline tokens at the line and column of the preceding line: + // only advance on the pointer until a new line is crossed, save the + // line and column, and then readvance it + bool had_physical_newline = skip_whitespace(lex, true); // set token source information lex->tok_line = lex->line; @@ -591,7 +594,12 @@ void mp_lexer_to_next(mp_lexer_t *lex) { lex->tok_kind = MP_TOKEN_INDENT; lex->emit_dent -= 1; - } else if (had_physical_newline && lex->nested_bracket_level == 0) { + } else if (had_physical_newline) { + // The cursor is at the end of the previous line, pointing to a + // physical newline. Skip any remaining whitespace, comments, and + // newlines. + skip_whitespace(lex, false); + lex->tok_kind = MP_TOKEN_NEWLINE; size_t num_spaces = lex->column - 1; @@ -862,9 +870,10 @@ mp_lexer_t *mp_lexer_new(qstr src_name, mp_reader_t reader) { // preload first token mp_lexer_to_next(lex); - // Check that the first token is in the first column. If it's not then we - // convert the token kind to INDENT so that the parser gives a syntax error. - if (lex->tok_column != 1) { + // Check that the first token is in the first column unless it is a + // newline. Otherwise we convert the token kind to INDENT so that + // the parser gives a syntax error. + if (lex->tok_column != 1 && lex->tok_kind != MP_TOKEN_NEWLINE) { lex->tok_kind = MP_TOKEN_INDENT; } @@ -879,10 +888,10 @@ mp_lexer_t *mp_lexer_new_from_str_len(qstr src_name, const char *str, size_t len #if MICROPY_READER_POSIX || MICROPY_READER_VFS -mp_lexer_t *mp_lexer_new_from_file(const char *filename) { +mp_lexer_t *mp_lexer_new_from_file(qstr filename) { mp_reader_t reader; mp_reader_new_file(&reader, filename); - return mp_lexer_new(qstr_from_str(filename), reader); + return mp_lexer_new(filename, reader); } #if MICROPY_HELPER_LEXER_UNIX diff --git a/py/lexer.h b/py/lexer.h index 8295dec0f715..2d9d0447b8ba 100644 --- a/py/lexer.h +++ b/py/lexer.h @@ -191,7 +191,7 @@ mp_lexer_t *mp_lexer_new_from_str_len(qstr src_name, const char *str, size_t len // If MICROPY_READER_POSIX or MICROPY_READER_VFS aren't enabled then // this function must be implemented by the port. -mp_lexer_t *mp_lexer_new_from_file(const char *filename); +mp_lexer_t *mp_lexer_new_from_file(qstr filename); #if MICROPY_HELPER_LEXER_UNIX mp_lexer_t *mp_lexer_new_from_fd(qstr filename, int fd, bool close_fd); diff --git a/py/makeqstrdata.py b/py/makeqstrdata.py index 5c08cbac5066..9e84330a347c 100644 --- a/py/makeqstrdata.py +++ b/py/makeqstrdata.py @@ -10,21 +10,15 @@ from __future__ import print_function -import bisect + import re import sys -import collections -import gettext -import os.path - +# CIRCUITPY-CHANGE if hasattr(sys.stdout, "reconfigure"): sys.stdout.reconfigure(encoding="utf-8") sys.stderr.reconfigure(errors="backslashreplace") -py = os.path.dirname(sys.argv[0]) -top = os.path.dirname(py) - # Python 2/3 compatibility: # - iterating through bytes is different # - codepoint2name lives in a different module @@ -68,20 +62,9 @@ codepoint2name[ord("|")] = "pipe" codepoint2name[ord("~")] = "tilde" -C_ESCAPES = { - "\a": "\\a", - "\b": "\\b", - "\f": "\\f", - "\n": "\\n", - "\r": "\\r", - "\t": "\\t", - "\v": "\\v", - "'": "\\'", - '"': '\\"', -} +# static qstrs, these must maintain a specific order for .mpy compatibility +# See QSTR_LAST_STATIC at the top of py/persistentcode.c -# static qstrs, should be sorted -# These are qstrs that are always included and always have the same number. It allows mpy files to omit them. static_qstr_list = [ "", "__dir__", # Put __dir__ after empty qstr for builtin dir() to work @@ -250,74 +233,70 @@ "zip", ] -# CIRCUITPY-CHANGE -# When taking the next merge from Micropython, prefer upstream's way of ensuring these appear in the "QSTR0" pool. -# These qstrs have to be sorted early (preferably right after static_qstr_list) because they are required to fit in 8-bit values -# however they should never be *forced* to appear -# repeats len, hash, int from the static qstr list, but this doesn't hurt anything. -eightbit_qstr_list = [ - "__abs__", - "__add__", - "__and__", +# Additional QSTRs that must have index <255 because they are stored in +# `mp_binary_op_method_name` and `mp_unary_op_method_name` (see py/objtype.c). +# These are not part of the .mpy compatibility list, but we place them in the +# fixed unsorted pool (i.e. QDEF0) to ensure their indices are small. +operator_qstr_list = { "__bool__", + "__pos__", + "__neg__", + "__invert__", + "__abs__", + "__float__", "__complex__", - "__contains__", - "__delete__", - "__divmod__", + "__sizeof__", + "__lt__", + "__gt__", "__eq__", - "__float__", - "__floordiv__", + "__le__", "__ge__", - "__get__", - "__gt__", - "__hash__", + "__ne__", + "__contains__", "__iadd__", - "__iand__", - "__ifloordiv__", - "__ilshift__", + "__isub__", + "__imul__", "__imatmul__", + "__ifloordiv__", + "__itruediv__", "__imod__", - "__imul__", - "__int__", - "__invert__", - "__ior__", "__ipow__", - "__irshift__", - "__isub__", - "__itruediv__", + "__ior__", "__ixor__", - "__le__", - "__len__", - "__lshift__", - "__lt__", + "__iand__", + "__ilshift__", + "__irshift__", + "__add__", + "__sub__", + "__mul__", "__matmul__", + "__floordiv__", + "__truediv__", "__mod__", - "__mul__", - "__ne__", - "__neg__", - "__or__", - "__pos__", + "__divmod__", "__pow__", + "__or__", + "__xor__", + "__and__", + "__lshift__", + "__rshift__", "__radd__", - "__rand__", - "__rfloordiv__", - "__rlshift__", + "__rsub__", + "__rmul__", "__rmatmul__", + "__rfloordiv__", + "__rtruediv__", "__rmod__", - "__rmul__", - "__ror__", "__rpow__", - "__rrshift__", - "__rshift__", - "__rsub__", - "__rtruediv__", + "__ror__", "__rxor__", + "__rand__", + "__rlshift__", + "__rrshift__", + "__get__", "__set__", - "__sizeof__", - "__sub__", - "__truediv__", - "__xor__", -] + "__delete__", +} # this must match the equivalent function in qstr.c @@ -341,24 +320,16 @@ def esc_char(m): return re.sub(r"[^A-Za-z0-9_]", esc_char, qst) +static_qstr_list_ident = list(map(qstr_escape, static_qstr_list)) + + # CIRCUITPY-CHANGE: add translations handling def parse_input_headers_with_translations(infiles): qcfgs = {} qstrs = {} + # CIRCUITPY-CHANGE: add translations translations = set() - # add static qstrs - for qstr in static_qstr_list: - # work out the corresponding qstr name - ident = qstr_escape(qstr) - - # don't add duplicates - assert ident not in qstrs - - # add the qstr to the list, with order number to retain original order in file - order = len(qstrs) - 300000 - qstrs[ident] = (order, ident, qstr) - # read the qstrs in from the input files for infile in infiles: with open(infile, "rt") as f: @@ -399,21 +370,18 @@ def parse_input_headers_with_translations(infiles): ident = qstr_escape(qstr) # don't add duplicates + if ident in static_qstr_list_ident: + continue if ident in qstrs: continue - # add the qstr to the list, with order number to retain original order in file - order = len(qstrs) - # but put special method names like __add__ at the top of list, so - # that their id's fit into a byte - if ident in eightbit_qstr_list: - order -= 100000 - qstrs[ident] = (order, ident, qstr) + qstrs[ident] = (ident, qstr) - if not qcfgs and qstrs: + if not qcfgs: sys.stderr.write("ERROR: Empty preprocessor output - check for errors above\n") sys.exit(1) + # CIRCUITPY-CHANGE return qcfgs, qstrs, translations @@ -454,16 +422,27 @@ def print_qstr_data(qcfgs, qstrs, translations): print("") # add NULL qstr with no hash or data - print('QDEF(MP_QSTRnull, 0, 0, "")') + print('QDEF0(MP_QSTRnull, 0, 0, "")') + + # add static qstrs to the first unsorted pool + for qstr in static_qstr_list: + qbytes = make_bytes(cfg_bytes_len, cfg_bytes_hash, qstr) + print("QDEF0(MP_QSTR_%s, %s)" % (qstr_escape(qstr), qbytes)) + # CIRCUITPY-CHANGE: track total qstr size total_qstr_size = 0 - # go through each qstr and print it out - for order, ident, qstr in sorted(qstrs.values(), key=lambda x: x[0]): + + # add remaining qstrs to the sorted (by value) pool (unless they're in + # operator_qstr_list, in which case add them to the unsorted pool) + for ident, qstr in sorted(qstrs.values(), key=lambda x: x[1]): qbytes = make_bytes(cfg_bytes_len, cfg_bytes_hash, qstr) - print("QDEF(MP_QSTR_%s, %s)" % (ident, qbytes)) + pool = 0 if qstr in operator_qstr_list else 1 + print("QDEF%d(MP_QSTR_%s, %s)" % (pool, ident, qbytes)) + # CIRCUITPY-CHANGE: track total qstr size total_qstr_size += len(qstr) + # CIRCUITPY-CHANGE: translations print( "// Enumerate translated texts but don't actually include translations. Instead, the linker will link them in." ) @@ -475,20 +454,10 @@ def print_qstr_data(qcfgs, qstrs, translations): def do_work(infiles): + # CIRCUITPY-CHANGE: include translations qcfgs, qstrs, translations = parse_input_headers_with_translations(infiles) print_qstr_data(qcfgs, qstrs, translations) if __name__ == "__main__": - import argparse - - parser = argparse.ArgumentParser( - description="Process QSTR definitions into headers for compilation" - ) - parser.add_argument( - "infiles", metavar="N", type=str, nargs="+", help="an integer for the accumulator" - ) - - args = parser.parse_args() - - do_work(args.infiles) + do_work(sys.argv[1:]) diff --git a/py/makeqstrdefs.py b/py/makeqstrdefs.py index 9839f7460186..21300efb2c8a 100644 --- a/py/makeqstrdefs.py +++ b/py/makeqstrdefs.py @@ -15,6 +15,7 @@ import multiprocessing, multiprocessing.dummy +# CIRCUITPY-CHANGE from html.entities import name2codepoint # add some custom names to map characters that aren't in HTML @@ -64,6 +65,10 @@ _MODE_ROOT_POINTER = "root_pointer" +class PreprocessorError(Exception): + pass + + def is_c_source(fname): return os.path.splitext(fname)[1] in [".c"] @@ -93,10 +98,10 @@ def preprocess(): def pp(flags): def run(files): - completed = subprocess.run(args.pp + flags + files, stdout=subprocess.PIPE) - if completed.returncode != 0: - raise RuntimeError(" ".join(args.pp + flags + files)) - return completed.stdout + try: + return subprocess.check_output(args.pp + flags + files) + except subprocess.CalledProcessError as er: + raise PreprocessorError(str(er)) return run @@ -124,6 +129,7 @@ def write_out(fname, output): f.write("\n".join(output) + "\n") +# CIRCUITPY-CHANGE: added def qstr_unescape(qstr): for name in name2codepoint: if "__" + name + "__" in qstr: @@ -146,6 +152,7 @@ def process_file(f): ) elif args.mode == _MODE_ROOT_POINTER: re_match = re.compile(r"MP_REGISTER_ROOT_POINTER\(.*?\);") + # CIRCUITPY-CHANGE: added re_translate = re.compile(r"MP_COMPRESSED_ROM_TEXT\(\"((?:(?=(\\?))\2.)*?)\"\)") output = [] last_fname = None @@ -170,6 +177,7 @@ def process_file(f): elif args.mode in (_MODE_COMPRESS, _MODE_MODULE, _MODE_ROOT_POINTER): output.append(match) + # CIRCUITPY-CHANGE: added for match in re_translate.findall(line): output.append('TRANSLATE("' + match[0] + '")') @@ -184,6 +192,7 @@ def cat_together(): hasher = hashlib.md5() all_lines = [] + # CIRCUITPY-CHANGE: added outf = open(args.output_dir + "/out", "wb") for fname in glob.glob(args.output_dir + "/*." + args.mode): with open(fname, "rb") as f: @@ -191,6 +200,7 @@ def cat_together(): all_lines += lines all_lines.sort() all_lines = b"\n".join(all_lines) + # CIRCUITPY-CHANGE: added outf.write(all_lines) outf.close() hasher.update(all_lines) @@ -209,6 +219,7 @@ def cat_together(): mode_full = "Module registrations" elif args.mode == _MODE_ROOT_POINTER: mode_full = "Root pointer registrations" + # CIRCUITPY-CHANGE if old_hash != new_hash: print(mode_full, "updated") try: @@ -261,7 +272,12 @@ class Args: for k, v in named_args.items(): setattr(args, k, v) - preprocess() + try: + preprocess() + except PreprocessorError as er: + print(er) + sys.exit(1) + sys.exit(0) args.mode = sys.argv[2] diff --git a/py/maketranslationdata.py b/py/maketranslationdata.py index 28a868ae0dc8..0c264f31d68f 100644 --- a/py/maketranslationdata.py +++ b/py/maketranslationdata.py @@ -527,7 +527,7 @@ def esc_char(m): def parse_qstrs(infile): r = {} - rx = re.compile(r'QDEF\([A-Za-z0-9_]+,\s*\d+,\s*\d+,\s*(?P"(?:[^"\\\\]*|\\.)")\)') + rx = re.compile(r'QDEF[01]\([A-Za-z0-9_]+,\s*\d+,\s*\d+,\s*(?P"(?:[^"\\\\]*|\\.)")\)') content = infile.read() for i, mat in enumerate(rx.findall(content, re.M)): mat = eval(mat) diff --git a/py/makeversionhdr.py b/py/makeversionhdr.py index edcb994b4f0e..72d6f3237876 100644 --- a/py/makeversionhdr.py +++ b/py/makeversionhdr.py @@ -1,6 +1,7 @@ """ Generate header file with macros defining MicroPython version info. +# CIRCUITPY-CHANGE: This script is thoroughly reworked for use with CircuitPython. This script works with Python 3.7 and newer """ @@ -116,6 +117,7 @@ def make_version_header(repo_path, filename): #define MICROPY_VERSION_MAJOR (%s) #define MICROPY_VERSION_MINOR (%s) #define MICROPY_VERSION_MICRO (%s) +#define MICROPY_VERSION_PRERELEASE 0 #define MICROPY_VERSION_STRING "%s" // Combined version as a 32-bit number for convenience #define MICROPY_VERSION (MICROPY_VERSION_MAJOR << 16 | MICROPY_VERSION_MINOR << 8 | MICROPY_VERSION_MICRO) diff --git a/py/malloc.c b/py/malloc.c index ee2a48da0b5f..0dbe66ea5585 100644 --- a/py/malloc.c +++ b/py/malloc.c @@ -267,6 +267,7 @@ void m_tracked_free(void *ptr_in) { if (ptr_in == NULL) { return; } + // CIRCUITPY-CHANGE: cast to avoid compiler warning m_tracked_node_t *node = (m_tracked_node_t *)(void *)((uint8_t *)ptr_in - sizeof(m_tracked_node_t)); #if MICROPY_DEBUG_VERBOSE size_t data_bytes; diff --git a/py/misc.h b/py/misc.h index e17616b16a82..dc43e613155f 100644 --- a/py/misc.h +++ b/py/misc.h @@ -34,6 +34,7 @@ #include #include +// CIRCUITPY-CHANGE: include directly instead of depending on previous includes #include "mpconfig.h" typedef unsigned char byte; @@ -54,10 +55,15 @@ typedef unsigned int uint; // Static assertion macro #define MP_STATIC_ASSERT(cond) ((void)sizeof(char[1 - 2 * !(cond)])) -#if defined(_MSC_VER) -#define MP_STATIC_ASSERT_NOT_MSC(cond) (1) +// In C++ things like comparing extern const pointers are not constant-expressions so cannot be used +// in MP_STATIC_ASSERT. Note that not all possible compiler versions will reject this. Some gcc versions +// do, others only with -Werror=vla, msvc always does. +// The (void) is needed to avoid "left operand of comma operator has no effect [-Werror=unused-value]" +// when using this macro on the left-hand side of a comma. +#if defined(_MSC_VER) || defined(__cplusplus) +#define MP_STATIC_ASSERT_NONCONSTEXPR(cond) ((void)1) #else -#define MP_STATIC_ASSERT_NOT_MSC(cond) MP_STATIC_ASSERT(cond) +#define MP_STATIC_ASSERT_NONCONSTEXPR(cond) MP_STATIC_ASSERT(cond) #endif // Round-up integer division @@ -69,32 +75,30 @@ typedef unsigned int uint; // TODO make a lazy m_renew that can increase by a smaller amount than requested (but by at least 1 more element) #define m_new(type, num) ((type *)(m_malloc(sizeof(type) * (num)))) -#define m_new_ll(type, num) m_new(type, num) // CIRCUITPY-CHANGE: clue to long-lived allocator #define m_new_maybe(type, num) ((type *)(m_malloc_maybe(sizeof(type) * (num)))) #define m_new0(type, num) ((type *)(m_malloc0(sizeof(type) * (num)))) #define m_new_obj(type) (m_new(type, 1)) #define m_new_obj_maybe(type) (m_new_maybe(type, 1)) -#define m_new_obj_var(obj_type, var_type, var_num) ((obj_type *)m_malloc(sizeof(obj_type) + sizeof(var_type) * (var_num))) -#define m_new_obj_var0(obj_type, var_type, var_num) ((obj_type *)m_malloc0(sizeof(obj_type) + sizeof(var_type) * (var_num))) -#define m_new_obj_var_maybe(obj_type, var_type, var_num) ((obj_type *)m_malloc_maybe(sizeof(obj_type) + sizeof(var_type) * (var_num))) +#define m_new_obj_var(obj_type, var_field, var_type, var_num) ((obj_type *)m_malloc(offsetof(obj_type, var_field) + sizeof(var_type) * (var_num))) +#define m_new_obj_var0(obj_type, var_field, var_type, var_num) ((obj_type *)m_malloc0(offsetof(obj_type, var_field) + sizeof(var_type) * (var_num))) +#define m_new_obj_var_maybe(obj_type, var_field, var_type, var_num) ((obj_type *)m_malloc_maybe(offsetof(obj_type, var_field) + sizeof(var_type) * (var_num))) #if MICROPY_ENABLE_FINALISER #define m_new_obj_with_finaliser(type) ((type *)(m_malloc_with_finaliser(sizeof(type)))) -#define m_new_ll_obj_with_finaliser(type) m_new_obj_with_finaliser(type) // CIRCUITPY-CHANGE: clue to long-lived allocator -#define m_new_obj_var_with_finaliser(type, var_type, var_num) ((type *)m_malloc_with_finaliser(sizeof(type) + sizeof(var_type) * (var_num))) +#define m_new_obj_var_with_finaliser(type, var_field, var_type, var_num) ((type *)m_malloc_with_finaliser(offsetof(type, var_field) + sizeof(var_type) * (var_num))) #else #define m_new_obj_with_finaliser(type) m_new_obj(type) -#define m_new_obj_var_with_finaliser(type, var_type, var_num) m_new_obj_var(type, var_type, var_num) +#define m_new_obj_var_with_finaliser(type, var_field, var_type, var_num) m_new_obj_var(type, var_field, var_type, var_num) #endif #if MICROPY_MALLOC_USES_ALLOCATED_SIZE #define m_renew(type, ptr, old_num, new_num) ((type *)(m_realloc((ptr), sizeof(type) * (old_num), sizeof(type) * (new_num)))) #define m_renew_maybe(type, ptr, old_num, new_num, allow_move) ((type *)(m_realloc_maybe((ptr), sizeof(type) * (old_num), sizeof(type) * (new_num), (allow_move)))) #define m_del(type, ptr, num) m_free(ptr, sizeof(type) * (num)) -#define m_del_var(obj_type, var_type, var_num, ptr) (m_free(ptr, sizeof(obj_type) + sizeof(var_type) * (var_num))) +#define m_del_var(obj_type, var_field, var_type, var_num, ptr) (m_free(ptr, offsetof(obj_type, var_field) + sizeof(var_type) * (var_num))) #else #define m_renew(type, ptr, old_num, new_num) ((type *)(m_realloc((ptr), sizeof(type) * (new_num)))) #define m_renew_maybe(type, ptr, old_num, new_num, allow_move) ((type *)(m_realloc_maybe((ptr), sizeof(type) * (new_num), (allow_move)))) #define m_del(type, ptr, num) ((void)(num), m_free(ptr)) -#define m_del_var(obj_type, var_type, var_num, ptr) ((void)(var_num), m_free(ptr)) +#define m_del_var(obj_type, var_field, var_type, var_num, ptr) ((void)(var_num), m_free(ptr)) #endif #define m_del_obj(type, ptr) (m_del(type, ptr, 1)) @@ -297,6 +301,7 @@ typedef union _mp_float_union_t { // So leave MP_COMPRESSED_ROM_TEXT in place for makeqstrdefs.py / makecompresseddata.py to find them. #else + // Compression enabled and doing a regular build. // Map MP_COMPRESSED_ROM_TEXT to the compressed strings. @@ -326,8 +331,10 @@ inline MP_ALWAYSINLINE const char *MP_COMPRESSED_ROM_TEXT(const char *msg) { return msg; } + #endif +// CIRCUITPY-CHANGE #elif defined(CIRCUITPY) #include "supervisor/shared/translate/translate.h" #else diff --git a/py/mkenv.mk b/py/mkenv.mk index a794504413ea..7161ea89de30 100644 --- a/py/mkenv.mk +++ b/py/mkenv.mk @@ -37,6 +37,7 @@ else Q = @ endif +# CIRCUITPY-CHANGE ifneq ($(filter rules,$(BUILD_VERBOSE)),) # This clever shell redefinition will print out the makefile line that is causing an action. # Note that -j can cause the order to be confusing. @@ -70,6 +71,7 @@ OBJCOPY = $(CROSS_COMPILE)objcopy SIZE = $(CROSS_COMPILE)size STRIP = $(CROSS_COMPILE)strip AR = $(CROSS_COMPILE)ar +WINDRES = $(CROSS_COMPILE)windres MAKE_MANIFEST = $(PYTHON) $(TOP)/tools/makemanifest.py MAKE_FROZEN = $(PYTHON) $(TOP)/tools/make-frozen.py diff --git a/py/mkrules.cmake b/py/mkrules.cmake index 02e3148f2be5..bfc56abfe80b 100644 --- a/py/mkrules.cmake +++ b/py/mkrules.cmake @@ -15,6 +15,10 @@ set(MICROPY_ROOT_POINTERS_SPLIT "${MICROPY_GENHDR_DIR}/root_pointers.split") set(MICROPY_ROOT_POINTERS_COLLECTED "${MICROPY_GENHDR_DIR}/root_pointers.collected") set(MICROPY_ROOT_POINTERS "${MICROPY_GENHDR_DIR}/root_pointers.h") +if(NOT MICROPY_PREVIEW_VERSION_2) + set(MICROPY_PREVIEW_VERSION_2 0) +endif() + # Need to do this before extracting MICROPY_CPP_DEF below. Rest of frozen # manifest handling is at the end of this file. if(MICROPY_FROZEN_MANIFEST) @@ -24,6 +28,12 @@ if(MICROPY_FROZEN_MANIFEST) ) endif() +if(MICROPY_PREVIEW_VERSION_2) + target_compile_definitions(${MICROPY_TARGET} PUBLIC + MICROPY_PREVIEW_VERSION_2=\(1\) + ) +endif() + # Provide defaults for preprocessor flags if not already defined if(NOT MICROPY_CPP_FLAGS) get_target_property(MICROPY_CPP_INC ${MICROPY_TARGET} INCLUDE_DIRECTORIES) @@ -89,6 +99,7 @@ add_custom_command( add_custom_command( OUTPUT ${MICROPY_QSTRDEFS_COLLECTED} COMMAND ${Python3_EXECUTABLE} ${MICROPY_PY_DIR}/makeqstrdefs.py cat qstr _ ${MICROPY_GENHDR_DIR}/qstr ${MICROPY_QSTRDEFS_COLLECTED} + BYPRODUCTS "${MICROPY_QSTRDEFS_COLLECTED}.hash" DEPENDS ${MICROPY_QSTRDEFS_SPLIT} VERBATIM COMMAND_EXPAND_LISTS @@ -126,6 +137,7 @@ add_custom_command( add_custom_command( OUTPUT ${MICROPY_MODULEDEFS_COLLECTED} COMMAND ${Python3_EXECUTABLE} ${MICROPY_PY_DIR}/makeqstrdefs.py cat module _ ${MICROPY_GENHDR_DIR}/module ${MICROPY_MODULEDEFS_COLLECTED} + BYPRODUCTS "${MICROPY_MODULEDEFS_COLLECTED}.hash" DEPENDS ${MICROPY_MODULEDEFS_SPLIT} VERBATIM COMMAND_EXPAND_LISTS @@ -151,6 +163,7 @@ add_custom_command( add_custom_command( OUTPUT ${MICROPY_ROOT_POINTERS_COLLECTED} COMMAND ${Python3_EXECUTABLE} ${MICROPY_PY_DIR}/makeqstrdefs.py cat root_pointer _ ${MICROPY_GENHDR_DIR}/root_pointer ${MICROPY_ROOT_POINTERS_COLLECTED} + BYPRODUCTS "${MICROPY_ROOT_POINTERS_COLLECTED}.hash" DEPENDS ${MICROPY_ROOT_POINTERS_SPLIT} VERBATIM COMMAND_EXPAND_LISTS @@ -202,10 +215,33 @@ if(MICROPY_FROZEN_MANIFEST) ) endif() + if(NOT MICROPY_CROSS_FLAGS) + set(MICROPY_CROSS_FLAGS "") + else() + set(MICROPY_CROSS_FLAGS "-f${MICROPY_CROSS_FLAGS}") + endif() + + # Set default path variables to be passed to makemanifest.py. These will + # be available in path substitutions. Additional variables can be set + # per-board in mpconfigboard.cmake. + set(MICROPY_MANIFEST_PORT_DIR ${MICROPY_PORT_DIR}) + set(MICROPY_MANIFEST_BOARD_DIR ${MICROPY_BOARD_DIR}) + set(MICROPY_MANIFEST_MPY_DIR ${MICROPY_DIR}) + set(MICROPY_MANIFEST_MPY_LIB_DIR ${MICROPY_LIB_DIR}) + + # Find all MICROPY_MANIFEST_* variables and turn them into command line arguments. + get_cmake_property(_manifest_vars VARIABLES) + list(FILTER _manifest_vars INCLUDE REGEX "MICROPY_MANIFEST_.*") + foreach(_manifest_var IN LISTS _manifest_vars) + list(APPEND _manifest_var_args "-v") + string(REGEX REPLACE "MICROPY_MANIFEST_(.*)" "\\1" _manifest_var_name ${_manifest_var}) + list(APPEND _manifest_var_args "${_manifest_var_name}=${${_manifest_var}}") + endforeach() + add_custom_target( BUILD_FROZEN_CONTENT ALL BYPRODUCTS ${MICROPY_FROZEN_CONTENT} - COMMAND ${Python3_EXECUTABLE} ${MICROPY_DIR}/tools/makemanifest.py -o ${MICROPY_FROZEN_CONTENT} -v "MPY_DIR=${MICROPY_DIR}" -v "MPY_LIB_DIR=${MICROPY_LIB_DIR}" -v "PORT_DIR=${MICROPY_PORT_DIR}" -v "BOARD_DIR=${MICROPY_BOARD_DIR}" -b "${CMAKE_BINARY_DIR}" -f${MICROPY_CROSS_FLAGS} --mpy-tool-flags=${MICROPY_MPY_TOOL_FLAGS} ${MICROPY_FROZEN_MANIFEST} + COMMAND ${Python3_EXECUTABLE} ${MICROPY_DIR}/tools/makemanifest.py -o ${MICROPY_FROZEN_CONTENT} ${_manifest_var_args} -b "${CMAKE_BINARY_DIR}" ${MICROPY_CROSS_FLAGS} --mpy-tool-flags=${MICROPY_MPY_TOOL_FLAGS} ${MICROPY_FROZEN_MANIFEST} DEPENDS ${MICROPY_QSTRDEFS_GENERATED} ${MICROPY_ROOT_POINTERS} diff --git a/py/mkrules.mk b/py/mkrules.mk index 16fa05309d1e..4a72ced53603 100644 --- a/py/mkrules.mk +++ b/py/mkrules.mk @@ -4,6 +4,13 @@ THIS_MAKEFILE = $(lastword $(MAKEFILE_LIST)) include $(dir $(THIS_MAKEFILE))mkenv.mk endif +# Enable in-progress/breaking changes that are slated for MicroPython 2.x. +MICROPY_PREVIEW_VERSION_2 ?= 0 + +ifeq ($(MICROPY_PREVIEW_VERSION_2),1) +CFLAGS += -DMICROPY_PREVIEW_VERSION_2=1 +endif + HELP_BUILD_ERROR ?= "See \033[1;31mhttps://github.com/micropython/micropython/wiki/Build-Troubleshooting\033[0m" HELP_MPY_LIB_SUBMODULE ?= "\033[1;31mError: micropython-lib submodule is not initialized.\033[0m Run 'make submodules'" @@ -44,7 +51,7 @@ QSTR_GEN_CXXFLAGS += $(QSTR_GEN_FLAGS) # can be located. By following this scheme, it allows a single build rule # to be used to compile all .c files. -# CIRCUITPY-CHANGE: adds STEPECHO +# CIRCUITPY-CHANGE: use STEPECHO vpath %.S . $(TOP) $(USER_C_MODULES) $(BUILD)/%.o: %.S $(STEPECHO) "CC $<" @@ -53,11 +60,12 @@ $(BUILD)/%.o: %.S vpath %.s . $(TOP) $(USER_C_MODULES) $(BUILD)/%.o: %.s $(STEPECHO) "AS $<" - $(Q)$(AS) -o $@ $< + $(Q)$(AS) $(AFLAGS) -o $@ $< +# CIRCUITPY-CHANGE: use STEPECHO define compile_c $(STEPECHO) "CC $<" -$(Q)$(CC) $(CFLAGS) -c -MD -o $@ $< +$(Q)$(CC) $(CFLAGS) -c -MD -MF $(@:.o=.d) -o $@ $< || (echo -e $(HELP_BUILD_ERROR); false) @# The following fixes the dependency file. @# See http://make.paulandlesley.org/autodep.html for details. @# Regex adjusted from the above to play better with Windows paths, etc. @@ -69,7 +77,7 @@ endef define compile_cxx $(ECHO) "CXX $<" -$(Q)$(CXX) $(CXXFLAGS) -c -MD -o $@ $< || (echo -e $(HELP_BUILD_ERROR); false) +$(Q)$(CXX) $(CXXFLAGS) -c -MD -MF $(@:.o=.d) -o $@ $< || (echo -e $(HELP_BUILD_ERROR); false) @# The following fixes the dependency file. @# See http://make.paulandlesley.org/autodep.html for details. @# Regex adjusted from the above to play better with Windows paths, etc. @@ -88,6 +96,7 @@ vpath %.cpp . $(TOP) $(USER_C_MODULES) $(BUILD)/%.o: %.cpp $(call compile_cxx) +# CIRCUITPY-CHANGE: use STEPECHO $(BUILD)/%.pp: %.c $(STEPECHO) "PreProcess $<" $(Q)$(CPP) $(CFLAGS) -Wp,-C,-dD,-dI -o $@ $< @@ -108,6 +117,7 @@ $(OBJ): | $(HEADER_BUILD)/qstrdefs.generated.h $(HEADER_BUILD)/mpversion.h $(OBJ # - else, if list of newer prerequisites ($?) is not empty, then process just these ($?) # - else, process all source files ($^) [this covers "make -B" which can set $? to empty] # See more information about this process in docs/develop/qstr.rst. +# CIRCUITPY-CHANGE: use STEPECHO in multiple rules below $(HEADER_BUILD)/qstr.i.last: $(SRC_QSTR) $(QSTR_GLOBAL_DEPENDENCIES) | $(QSTR_GLOBAL_REQUIREMENTS) $(STEPECHO) "GEN $@" $(Q)$(PYTHON) $(PY_SRC)/makeqstrdefs.py pp $(CPP) output $(HEADER_BUILD)/qstr.i.last cflags $(QSTR_GEN_CFLAGS) cxxflags $(QSTR_GEN_CXXFLAGS) sources $^ dependencies $(QSTR_GLOBAL_DEPENDENCIES) changed_sources $? @@ -158,6 +168,7 @@ $(HEADER_BUILD)/compressed.collected: $(HEADER_BUILD)/compressed.split # will be created if they don't exist. OBJ_DIRS = $(sort $(dir $(OBJ))) $(OBJ): | $(OBJ_DIRS) +// CIRCUITPY-CHANGE: use $(Q) $(OBJ_DIRS): $(Q)$(MKDIR) -p $@ @@ -190,8 +201,8 @@ CFLAGS += -DMICROPY_QSTR_EXTRA_POOL=mp_qstr_frozen_const_pool CFLAGS += -DMICROPY_MODULE_FROZEN_MPY CFLAGS += -DMICROPY_MODULE_FROZEN_STR -# to build frozen_content.c from a manifest # CIRCUITPY-CHANGE: FROZEN_MANIFEST is constructed at build time +# to build frozen_content.c from a manifest $(BUILD)/frozen_content.c: FORCE $(BUILD)/genhdr/qstrdefs.generated.h $(BUILD)/genhdr/root_pointers.h $(FROZEN_MANIFEST) | $(MICROPY_MPYCROSS_DEPENDENCY) $(Q)test -e "$(MPY_LIB_DIR)/README.md" || (echo -e $(HELP_MPY_LIB_SUBMODULE); false) $(Q)$(MAKE_MANIFEST) -o $@ -v "MPY_DIR=$(TOP)" -v "MPY_LIB_DIR=$(MPY_LIB_DIR)" -v "PORT_DIR=$(shell pwd)" -v "BOARD_DIR=$(BOARD_DIR)" -b "$(BUILD)" $(if $(MPY_CROSS_FLAGS),-f"$(MPY_CROSS_FLAGS)",) --mpy-tool-flags="$(MPY_TOOL_FLAGS)" $(FROZEN_MANIFEST) diff --git a/py/modbuiltins.c b/py/modbuiltins.c index f7294d9038c7..8aacafb796a3 100644 --- a/py/modbuiltins.c +++ b/py/modbuiltins.c @@ -170,7 +170,7 @@ STATIC mp_obj_t mp_builtin_dir(size_t n_args, const mp_obj_t *args) { // Implemented by probing all possible qstrs with mp_load_method_maybe size_t nqstr = QSTR_TOTAL(); for (size_t i = MP_QSTR_ + 1; i < nqstr; ++i) { - // CIRCUITPY-CHANGE: changes PR #6539 + // CIRCUITPY-CHANGE: changes PR #6539: catch dir() exceptions mp_obj_t dest[2] = {}; mp_load_method_protected(args[0], i, dest, true); if (dest[0] != MP_OBJ_NULL) { @@ -740,6 +740,7 @@ STATIC const mp_rom_map_elem_t mp_module_builtins_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_IndentationError), MP_ROM_PTR(&mp_type_IndentationError) }, { MP_ROM_QSTR(MP_QSTR_IndexError), MP_ROM_PTR(&mp_type_IndexError) }, { MP_ROM_QSTR(MP_QSTR_KeyboardInterrupt), MP_ROM_PTR(&mp_type_KeyboardInterrupt) }, + // CIRCUITPY-CHANGE: add ReloadException { MP_ROM_QSTR(MP_QSTR_ReloadException), MP_ROM_PTR(&mp_type_ReloadException) }, { MP_ROM_QSTR(MP_QSTR_KeyError), MP_ROM_PTR(&mp_type_KeyError) }, { MP_ROM_QSTR(MP_QSTR_LookupError), MP_ROM_PTR(&mp_type_LookupError) }, @@ -747,6 +748,7 @@ STATIC const mp_rom_map_elem_t mp_module_builtins_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_NameError), MP_ROM_PTR(&mp_type_NameError) }, { MP_ROM_QSTR(MP_QSTR_NotImplementedError), MP_ROM_PTR(&mp_type_NotImplementedError) }, { MP_ROM_QSTR(MP_QSTR_OSError), MP_ROM_PTR(&mp_type_OSError) }, + // CIRCUITPY-CHANGE: add TimeoutEror ConnectionError, BrokenPipeError { MP_ROM_QSTR(MP_QSTR_TimeoutError), MP_ROM_PTR(&mp_type_TimeoutError) }, { MP_ROM_QSTR(MP_QSTR_ConnectionError), MP_ROM_PTR(&mp_type_ConnectionError) }, { MP_ROM_QSTR(MP_QSTR_BrokenPipeError), MP_ROM_PTR(&mp_type_BrokenPipeError) }, @@ -767,6 +769,7 @@ STATIC const mp_rom_map_elem_t mp_module_builtins_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_ViperTypeError), MP_ROM_PTR(&mp_type_ViperTypeError) }, #endif { MP_ROM_QSTR(MP_QSTR_ZeroDivisionError), MP_ROM_PTR(&mp_type_ZeroDivisionError) }, + // CIRCUITYPY-CHANGE #if CIRCUITPY_WARNINGS { MP_ROM_QSTR(MP_QSTR_Warning), MP_ROM_PTR(&mp_type_Warning) }, { MP_ROM_QSTR(MP_QSTR_FutureWarning), MP_ROM_PTR(&mp_type_FutureWarning) }, diff --git a/py/moderrno.c b/py/moderrno.c index e8057ff00287..b1002f177da5 100644 --- a/py/moderrno.c +++ b/py/moderrno.c @@ -121,6 +121,7 @@ qstr mp_errno_to_str(mp_obj_t errno_val) { #endif } +// CIRCUITPY-CHANGE // For commonly encountered errors, return human readable strings, otherwise try errno name const char *mp_common_errno_to_str(mp_obj_t errno_val, char *buf, size_t len) { if (!mp_obj_is_small_int(errno_val)) { diff --git a/py/modmicropython.c b/py/modmicropython.c index 7c097ff48701..17985b3e2b94 100644 --- a/py/modmicropython.c +++ b/py/modmicropython.c @@ -37,6 +37,7 @@ // Various builtins specific to MicroPython runtime, // living in micropython module +// CIRCUITPY-CHANGE: avoid warning #if CIRCUITPY_MICROPYTHON_ADVANCED && MICROPY_ENABLE_COMPILER STATIC mp_obj_t mp_micropython_opt_level(size_t n_args, const mp_obj_t *args) { if (n_args == 0) { @@ -49,6 +50,7 @@ STATIC mp_obj_t mp_micropython_opt_level(size_t n_args, const mp_obj_t *args) { STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_micropython_opt_level_obj, 0, 1, mp_micropython_opt_level); #endif +// CIRCUITPY-CHANGE: avoid warning #if CIRCUITPY_MICROPYTHON_ADVANCED && MICROPY_PY_MICROPYTHON_MEM_INFO #if MICROPY_MEM_STATS @@ -109,6 +111,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_micropython_qstr_info_obj, 0, 1, m #endif // MICROPY_PY_MICROPYTHON_MEM_INFO +// CIRCUITPY-CHANGE: avoid warning #if CIRCUITPY_MICROPYTHON_ADVANCED && MICROPY_PY_MICROPYTHON_STACK_USE STATIC mp_obj_t mp_micropython_stack_use(void) { return MP_OBJ_NEW_SMALL_INT(mp_stack_usage()); @@ -116,6 +119,7 @@ STATIC mp_obj_t mp_micropython_stack_use(void) { STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_stack_use_obj, mp_micropython_stack_use); #endif +// CIRCUITPY-CHANGE: avoid warning #if CIRCUITPY_MICROPYTHON_ADVANCED && MICROPY_ENABLE_PYSTACK STATIC mp_obj_t mp_micropython_pystack_use(void) { return MP_OBJ_NEW_SMALL_INT(mp_pystack_usage()); @@ -123,6 +127,7 @@ STATIC mp_obj_t mp_micropython_pystack_use(void) { STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_pystack_use_obj, mp_micropython_pystack_use); #endif +// CIRCUITPY-CHANGE: avoid warning #if CIRCUITPY_MICROPYTHON_ADVANCED && MICROPY_ENABLE_GC STATIC mp_obj_t mp_micropython_heap_lock(void) { gc_lock(); @@ -144,10 +149,12 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_heap_locked_obj, mp_micropython_ #endif #endif +// CIRCUITPY-CHANGE: avoid warning #if CIRCUITPY_MICROPYTHON_ADVANCED && MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF && (MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE == 0) STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_alloc_emergency_exception_buf_obj, mp_alloc_emergency_exception_buf); #endif +// CIRCUITPY-CHANGE: avoid warning #if CIRCUITPY_MICROPYTHON_ADVANCED && MICROPY_KBD_EXCEPTION STATIC mp_obj_t mp_micropython_kbd_intr(mp_obj_t int_chr_in) { mp_hal_set_interrupt_char(mp_obj_get_int(int_chr_in)); @@ -169,9 +176,11 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(mp_micropython_schedule_obj, mp_micropython_sch STATIC const mp_rom_map_elem_t mp_module_micropython_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_micropython) }, { MP_ROM_QSTR(MP_QSTR_const), MP_ROM_PTR(&mp_identity_obj) }, + // CIRCUITPY-CHANGE: avoid warning #if CIRCUITPY_MICROPYTHON_ADVANCED && MICROPY_ENABLE_COMPILER { MP_ROM_QSTR(MP_QSTR_opt_level), MP_ROM_PTR(&mp_micropython_opt_level_obj) }, #endif + // CIRCUITPY-CHANGE: avoid warning #if CIRCUITPY_MICROPYTHON_ADVANCED && MICROPY_PY_MICROPYTHON_MEM_INFO #if MICROPY_MEM_STATS { MP_ROM_QSTR(MP_QSTR_mem_total), MP_ROM_PTR(&mp_micropython_mem_total_obj) }, @@ -181,15 +190,19 @@ STATIC const mp_rom_map_elem_t mp_module_micropython_globals_table[] = { { 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) }, #endif + // CIRCUITPY-CHANGE: avoid warning #if CIRCUITPY_MICROPYTHON_ADVANCED && MICROPY_PY_MICROPYTHON_STACK_USE { MP_ROM_QSTR(MP_QSTR_stack_use), MP_ROM_PTR(&mp_micropython_stack_use_obj) }, #endif + // CIRCUITPY-CHANGE: avoid warning #if CIRCUITPY_MICROPYTHON_ADVANCED && 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 + // CIRCUITPY-CHANGE: avoid warning #if CIRCUITPY_MICROPYTHON_ADVANCED && MICROPY_ENABLE_PYSTACK { MP_ROM_QSTR(MP_QSTR_pystack_use), MP_ROM_PTR(&mp_micropython_pystack_use_obj) }, #endif + // CIRCUITPY-CHANGE: avoid warning #if CIRCUITPY_MICROPYTHON_ADVANCED && MICROPY_ENABLE_GC { MP_ROM_QSTR(MP_QSTR_heap_lock), MP_ROM_PTR(&mp_micropython_heap_lock_obj) }, { MP_ROM_QSTR(MP_QSTR_heap_unlock), MP_ROM_PTR(&mp_micropython_heap_unlock_obj) }, @@ -197,6 +210,7 @@ STATIC const mp_rom_map_elem_t mp_module_micropython_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_heap_locked), MP_ROM_PTR(&mp_micropython_heap_locked_obj) }, #endif #endif + // CIRCUITPY-CHANGE: avoid warning #if CIRCUITPY_MICROPYTHON_ADVANCED && MICROPY_KBD_EXCEPTION { MP_ROM_QSTR(MP_QSTR_kbd_intr), MP_ROM_PTR(&mp_micropython_kbd_intr_obj) }, #endif diff --git a/py/modsys.c b/py/modsys.c index 3ea57ee9a5d8..6397467c2282 100644 --- a/py/modsys.c +++ b/py/modsys.c @@ -51,6 +51,7 @@ #if MICROPY_PY_SYS +// CIRCUITPY-CHANGE #include "genhdr/mpversion.h" // defined per port; type of these is irrelevant, just need pointer @@ -66,31 +67,47 @@ const mp_print_t mp_sys_stdout_print = {&mp_sys_stdout_obj, mp_stream_write_adap STATIC const MP_DEFINE_STR_OBJ(mp_sys_version_obj, "3.4.0; " MICROPY_BANNER_NAME_AND_VERSION); // version_info - Python language version that this implementation conforms to, as a tuple of ints -#define I(n) MP_OBJ_NEW_SMALL_INT(n) -// TODO: CPython is now at 5-element array, but save 2 els so far... -STATIC const mp_obj_tuple_t mp_sys_version_info_obj = {{&mp_type_tuple}, 3, {I(3), I(4), I(0)}}; +// TODO: CPython is now at 5-element array (major, minor, micro, releaselevel, serial), but save 2 els so far... +STATIC const mp_rom_obj_tuple_t mp_sys_version_info_obj = {{&mp_type_tuple}, 3, {MP_ROM_INT(3), MP_ROM_INT(4), MP_ROM_INT(0)}}; // sys.implementation object // this holds the MicroPython version -STATIC const mp_obj_tuple_t mp_sys_implementation_version_info_obj = { +STATIC const mp_rom_obj_tuple_t mp_sys_implementation_version_info_obj = { {&mp_type_tuple}, - 3, - { I(MICROPY_VERSION_MAJOR), I(MICROPY_VERSION_MINOR), I(MICROPY_VERSION_MICRO) } + 4, + { + MP_ROM_INT(MICROPY_VERSION_MAJOR), + MP_ROM_INT(MICROPY_VERSION_MINOR), + MP_ROM_INT(MICROPY_VERSION_MICRO), + #if MICROPY_VERSION_PRERELEASE + MP_ROM_QSTR(MP_QSTR_preview), + #else + MP_ROM_QSTR(MP_QSTR_), + #endif + } }; STATIC const MP_DEFINE_STR_OBJ(mp_sys_implementation_machine_obj, MICROPY_BANNER_MACHINE); -#if MICROPY_PERSISTENT_CODE_LOAD -#define SYS_IMPLEMENTATION_ELEMS \ - MP_ROM_QSTR(MP_QSTR_circuitpython), \ - MP_ROM_PTR(&mp_sys_implementation_version_info_obj), \ - MP_ROM_PTR(&mp_sys_implementation_machine_obj), \ - MP_ROM_INT(MPY_FILE_HEADER_INT) -#else -#define SYS_IMPLEMENTATION_ELEMS \ +// CIRCUITPY-CHANGE: MP_QSTR_circuitpython +#define SYS_IMPLEMENTATION_ELEMS_BASE \ MP_ROM_QSTR(MP_QSTR_circuitpython), \ MP_ROM_PTR(&mp_sys_implementation_version_info_obj), \ MP_ROM_PTR(&mp_sys_implementation_machine_obj) + +#if MICROPY_PERSISTENT_CODE_LOAD +#define SYS_IMPLEMENTATION_ELEMS__MPY \ + , MP_ROM_INT(MPY_FILE_HEADER_INT) +#else +#define SYS_IMPLEMENTATION_ELEMS__MPY #endif + #if MICROPY_PY_ATTRTUPLE +#if MICROPY_PREVIEW_VERSION_2 +#define SYS_IMPLEMENTATION_ELEMS__V2 \ + , MP_ROM_TRUE +#else +#define SYS_IMPLEMENTATION_ELEMS__V2 +#endif + STATIC const qstr impl_fields[] = { MP_QSTR_name, MP_QSTR_version, @@ -98,19 +115,30 @@ STATIC const qstr impl_fields[] = { #if MICROPY_PERSISTENT_CODE_LOAD MP_QSTR__mpy, #endif + #if MICROPY_PREVIEW_VERSION_2 + MP_QSTR__v2, + #endif }; STATIC MP_DEFINE_ATTRTUPLE( mp_sys_implementation_obj, impl_fields, - 3 + MICROPY_PERSISTENT_CODE_LOAD, - SYS_IMPLEMENTATION_ELEMS + 3 + MICROPY_PERSISTENT_CODE_LOAD + MICROPY_PREVIEW_VERSION_2, + SYS_IMPLEMENTATION_ELEMS_BASE + SYS_IMPLEMENTATION_ELEMS__MPY + SYS_IMPLEMENTATION_ELEMS__V2 ); #else STATIC const mp_rom_obj_tuple_t mp_sys_implementation_obj = { {&mp_type_tuple}, 3 + MICROPY_PERSISTENT_CODE_LOAD, + // Do not include SYS_IMPLEMENTATION_ELEMS__V2 because + // SYS_IMPLEMENTATION_ELEMS__MPY may be empty if + // MICROPY_PERSISTENT_CODE_LOAD is disabled, which means they'll share + // the same index. Cannot query _v2 if MICROPY_PY_ATTRTUPLE is + // disabled. { - SYS_IMPLEMENTATION_ELEMS + SYS_IMPLEMENTATION_ELEMS_BASE + SYS_IMPLEMENTATION_ELEMS__MPY } }; #endif @@ -128,6 +156,10 @@ STATIC const MP_DEFINE_STR_OBJ(mp_sys_platform_obj, MICROPY_PY_SYS_PLATFORM); MP_DEFINE_STR_OBJ(mp_sys_executable_obj, ""); #endif +#if MICROPY_PY_SYS_INTERN +MP_DEFINE_CONST_FUN_OBJ_1(mp_sys_intern_obj, mp_obj_str_intern_checked); +#endif + // exit([retval]): raise SystemExit, with optional argument given to the exception STATIC mp_obj_t mp_sys_exit(size_t n_args, const mp_obj_t *args) { if (n_args == 0) { @@ -280,6 +312,10 @@ STATIC const mp_rom_map_elem_t mp_module_sys_globals_table[] = { #endif #endif + #if MICROPY_PY_SYS_INTERN + { MP_ROM_QSTR(MP_QSTR_intern), MP_ROM_PTR(&mp_sys_intern_obj) }, + #endif + #if MICROPY_PY_SYS_EXIT { MP_ROM_QSTR(MP_QSTR_exit), MP_ROM_PTR(&mp_sys_exit_obj) }, #endif diff --git a/py/modthread.c b/py/modthread.c index 3c3656c74215..aac58076f302 100644 --- a/py/modthread.c +++ b/py/modthread.c @@ -175,6 +175,7 @@ STATIC void *thread_entry(void *args_in) { // The GC starts off unlocked on this thread. ts.gc_lock_depth = 0; + ts.nlr_jump_callback_top = NULL; ts.mp_pending_exception = MP_OBJ_NULL; // set locals and globals from the calling context @@ -235,7 +236,7 @@ STATIC mp_obj_t mod_thread_start_new_thread(size_t n_args, const mp_obj_t *args) // check for keyword arguments if (n_args == 2) { // just position arguments - th_args = m_new_obj_var(thread_entry_args_t, mp_obj_t, pos_args_len); + th_args = m_new_obj_var(thread_entry_args_t, args, mp_obj_t, pos_args_len); th_args->n_kw = 0; } else { // positional and keyword arguments @@ -243,7 +244,7 @@ STATIC mp_obj_t mod_thread_start_new_thread(size_t n_args, const mp_obj_t *args) mp_raise_TypeError(MP_ERROR_TEXT("expecting a dict for keyword args")); } mp_map_t *map = &((mp_obj_dict_t *)MP_OBJ_TO_PTR(args[2]))->map; - th_args = m_new_obj_var(thread_entry_args_t, mp_obj_t, pos_args_len + 2 * map->used); + th_args = m_new_obj_var(thread_entry_args_t, args, mp_obj_t, pos_args_len + 2 * map->used); th_args->n_kw = map->used; // copy across the keyword arguments for (size_t i = 0, n = pos_args_len; i < map->alloc; ++i) { diff --git a/py/mpconfig.h b/py/mpconfig.h index 016f9104f785..0a846bcd7cb3 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -26,6 +26,7 @@ #ifndef MICROPY_INCLUDED_PY_MPCONFIG_H #define MICROPY_INCLUDED_PY_MPCONFIG_H +// CIRCUITPY-CHANGE // Is this a CircuitPython build? #ifndef CIRCUITPY #define CIRCUITPY 0 @@ -37,22 +38,36 @@ #else // Current version of MicroPython #define MICROPY_VERSION_MAJOR 1 -#define MICROPY_VERSION_MINOR 21 -#define MICROPY_VERSION_MICRO 0 - -// Combined version as a 32-bit number for convenience -#define MICROPY_VERSION ( \ - MICROPY_VERSION_MAJOR << 16 \ - | MICROPY_VERSION_MINOR << 8 \ - | MICROPY_VERSION_MICRO) - -// String version -#define MICROPY_VERSION_STRING \ +#define MICROPY_VERSION_MINOR 22 +#define MICROPY_VERSION_MICRO 2 +#define MICROPY_VERSION_PRERELEASE 0 + +// Combined version as a 32-bit number for convenience to allow version +// comparison. Doesn't include prerelease state. +// e.g. #if MICROPY_VERSION < MICROPY_MAKE_VERSION(1, 22, 0) +#define MICROPY_MAKE_VERSION(major, minor, patch) (major << 16 | minor << 8 | patch) +#define MICROPY_VERSION MICROPY_MAKE_VERSION(MICROPY_VERSION_MAJOR, MICROPY_VERSION_MINOR, MICROPY_VERSION_MICRO) + +// String version. This is only used directly for platform.platform and +// os.uname().release. All other version info available in the firmware (e.g. +// the REPL banner) comes from MICROPY_GIT_TAG. +#define MICROPY_VERSION_STRING_BASE \ MP_STRINGIFY(MICROPY_VERSION_MAJOR) "." \ MP_STRINGIFY(MICROPY_VERSION_MINOR) "." \ MP_STRINGIFY(MICROPY_VERSION_MICRO) +#if MICROPY_VERSION_PRERELEASE +#define MICROPY_VERSION_STRING MICROPY_VERSION_STRING_BASE "-preview" +#else +#define MICROPY_VERSION_STRING MICROPY_VERSION_STRING_BASE #endif +#endif // CIRCUITPY + +// If this is enabled, then in-progress/breaking changes slated for the 2.x +// release will be enabled. +#ifndef MICROPY_PREVIEW_VERSION_2 +#define MICROPY_PREVIEW_VERSION_2 (0) +#endif // This file contains default configuration settings for MicroPython. // You can override any of the options below using mpconfigport.h file @@ -1324,11 +1339,13 @@ typedef double mp_float_t; #define MICROPY_PY_COLLECTIONS_DEQUE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) #endif +// CIRCUITPY-CHANGE // Whether "collections.deque" supports iteration #ifndef MICROPY_PY_COLLECTIONS_DEQUE_ITER #define MICROPY_PY_COLLECTIONS_DEQUE_ITER (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) #endif +// CIRCUITPY-CHANGE // Whether "collections.deque" supports subscription #ifndef MICROPY_PY_COLLECTIONS_DEQUE_SUBSCR #define MICROPY_PY_COLLECTIONS_DEQUE_SUBSCR (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) @@ -1473,6 +1490,11 @@ typedef double mp_float_t; #define MICROPY_PY_SYS_EXECUTABLE (0) #endif +// Whether to provide "sys.intern" +#ifndef MICROPY_PY_SYS_INTERN +#define MICROPY_PY_SYS_INTERN (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) +#endif + // Whether to provide "sys.exit" function #ifndef MICROPY_PY_SYS_EXIT #define MICROPY_PY_SYS_EXIT (1) @@ -1879,8 +1901,12 @@ typedef double mp_float_t; // String used for the banner, and sys.version additional information #ifndef MICROPY_BANNER_NAME_AND_VERSION +#if MICROPY_PREVIEW_VERSION_2 +#define MICROPY_BANNER_NAME_AND_VERSION "MicroPython (with v2.0 preview) " MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE +#else #define MICROPY_BANNER_NAME_AND_VERSION "CircuitPython " MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE #endif +#endif // String used for the second part of the banner, and sys.implementation._machine #ifndef MICROPY_BANNER_MACHINE @@ -1891,14 +1917,6 @@ typedef double mp_float_t; #endif #endif -// On embedded platforms, these will typically enable/disable irqs. -#ifndef MICROPY_BEGIN_ATOMIC_SECTION -#define MICROPY_BEGIN_ATOMIC_SECTION() (0) -#endif -#ifndef MICROPY_END_ATOMIC_SECTION -#define MICROPY_END_ATOMIC_SECTION(state) (void)(state) -#endif - // Allow to override static modifier for global objects, e.g. to use with // object code analysis tools which don't support static symbols. #ifndef STATIC diff --git a/py/mperrno.h b/py/mperrno.h index a449ae6d4bd8..9e4ecd9419c6 100644 --- a/py/mperrno.h +++ b/py/mperrno.h @@ -27,6 +27,7 @@ #define MICROPY_INCLUDED_PY_MPERRNO_H #include "py/mpconfig.h" +// CIRCUITPY-CHANGE #include "py/obj.h" #if MICROPY_USE_INTERNAL_ERRNO diff --git a/py/mphal.h b/py/mphal.h index 0d4b1224e5ce..a4f222d0b1e1 100644 --- a/py/mphal.h +++ b/py/mphal.h @@ -35,6 +35,14 @@ #include #endif +// On embedded platforms, these will typically enable/disable irqs. +#ifndef MICROPY_BEGIN_ATOMIC_SECTION +#define MICROPY_BEGIN_ATOMIC_SECTION() (0) +#endif +#ifndef MICROPY_END_ATOMIC_SECTION +#define MICROPY_END_ATOMIC_SECTION(state) (void)(state) +#endif + #ifndef mp_hal_stdio_poll uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags); #endif @@ -48,7 +56,7 @@ void mp_hal_stdout_tx_str(const char *str); #endif #ifndef mp_hal_stdout_tx_strn -void mp_hal_stdout_tx_strn(const char *str, size_t len); +mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len); #endif #ifndef mp_hal_stdout_tx_strn_cooked @@ -90,4 +98,17 @@ uint64_t mp_hal_time_ns(void); #include "extmod/virtpin.h" #endif +// Event handling and wait-for-event functions. + +#ifndef MICROPY_INTERNAL_WFE +// Fallback definition for ports that don't need to suspend the CPU. +#define MICROPY_INTERNAL_WFE(TIMEOUT_MS) (void)0 +#endif + +#ifndef MICROPY_INTERNAL_EVENT_HOOK +// Fallback definition for ports that don't need any port-specific +// non-blocking event processing. +#define MICROPY_INTERNAL_EVENT_HOOK (void)0 +#endif + #endif // MICROPY_INCLUDED_PY_MPHAL_H diff --git a/py/mpprint.c b/py/mpprint.c index 467371bec247..18bc1e5b9b79 100644 --- a/py/mpprint.c +++ b/py/mpprint.c @@ -492,9 +492,11 @@ int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args) { qstr qst = va_arg(args, qstr); size_t len; const char *str = (const char *)qstr_data(qst, &len); + // CIRCUITPY-CHANGE chrs += print_str_common(print, str, prec, len, flags, fill, width); break; } + // CIRCUITPY-CHANGE: new code to print compressed strings case 'S': { mp_rom_error_text_t arg = va_arg(args, mp_rom_error_text_t); size_t len_with_nul = decompress_length(arg); @@ -509,6 +511,7 @@ int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args) { #ifndef NDEBUG // With debugging enabled, catch printing of null string pointers if (str == NULL) { + // CIRCUITPY-CHANGE str = "(null)"; } #endif @@ -546,6 +549,7 @@ int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args) { case 'p': case 'P': // don't bother to handle upcase for 'P' // Use unsigned long int to work on both ILP32 and LP64 systems + // CIRCUITPY-CHANGE: print 0x prefix #if SUPPORT_INT_BASE_PREFIX chrs += mp_print_int(print, va_arg(args, unsigned long int), 0, 16, 'a', flags | PF_FLAG_SHOW_PREFIX, fill, width); #else @@ -593,6 +597,7 @@ int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args) { return chrs; } +// CIRCUITPY-CHANGE: compressed string printer int mp_cprintf(const mp_print_t *print, mp_rom_error_text_t compressed_fmt, ...) { va_list ap; va_start(ap, compressed_fmt); @@ -601,6 +606,7 @@ int mp_cprintf(const mp_print_t *print, mp_rom_error_text_t compressed_fmt, ...) return ret; } +// CIRCUITPY-CHANGE: compressed string printer int mp_vcprintf(const mp_print_t *print, mp_rom_error_text_t compressed_fmt, va_list args) { char fmt[decompress_length(compressed_fmt)]; // TODO: Optimise this to format-while-decompressing (and not require the temp stack space). diff --git a/py/mpprint.h b/py/mpprint.h index ccf794ab5b06..e883cc27047f 100644 --- a/py/mpprint.h +++ b/py/mpprint.h @@ -79,7 +79,7 @@ int mp_printf(const mp_print_t *print, const char *fmt, ...); int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args); #endif -// CIRCUITPY-CHANGE +// CIRCUITPY-CHANGE: compressed string printers struct compressed_string; int mp_cprintf(const mp_print_t *print, const struct compressed_string *compressed_fmt, ...); #ifdef va_start diff --git a/py/mpstate.c b/py/mpstate.c index 491a66a045dd..06f3feb0baa6 100644 --- a/py/mpstate.c +++ b/py/mpstate.c @@ -32,4 +32,5 @@ mp_dynamic_compiler_t mp_dynamic_compiler = {0}; #endif +// CIRCUITPY-CHANGE: PLACE_IN_DTCM_BSS mp_state_ctx_t PLACE_IN_DTCM_BSS(mp_state_ctx); diff --git a/py/mpstate.h b/py/mpstate.h index 3717cc15c2ee..1898982e1c1e 100644 --- a/py/mpstate.h +++ b/py/mpstate.h @@ -36,6 +36,7 @@ #include "py/objlist.h" #include "py/objexcept.h" +// CIRCUITPY-CHANGE #if CIRCUITPY_WARNINGS #include "shared-bindings/warnings/__init__.h" #endif @@ -166,6 +167,7 @@ typedef struct _mp_state_vm_t { struct _m_tracked_node_t *m_tracked_head; #endif + // CIRCUITPY-CHANGE: trackback // non-heap memory for creating a traceback if we can't allocate RAM mp_obj_traceback_t mp_emergency_traceback_obj; @@ -188,6 +190,7 @@ typedef struct _mp_state_vm_t { mp_obj_exception_t mp_kbd_exception; #endif + // CIRCUITPY-CHANGE // exception object of type ReloadException mp_obj_exception_t mp_reload_exception; @@ -268,8 +271,10 @@ typedef struct _mp_state_vm_t { #endif } mp_state_vm_t; -// This structure holds state that is specific to a given thread. -// Everything in this structure is scanned for root pointers. +// This structure holds state that is specific to a given thread. Everything +// in this structure is scanned for root pointers. Anything added to this +// structure must have corresponding initialisation added to thread_entry (in +// py/modthread.c). typedef struct _mp_state_thread_t { // Stack top at the start of program char *stack_top; @@ -316,6 +321,7 @@ typedef struct _mp_state_thread_t { struct _mp_code_state_t *current_code_state; #endif + // CIRCUITPY-CHANGE #if CIRCUITPY_WARNINGS warnings_action_t warnings_action; #endif diff --git a/py/mpz.h b/py/mpz.h index d2825df3b646..f205e3cd158a 100644 --- a/py/mpz.h +++ b/py/mpz.h @@ -142,7 +142,7 @@ void mpz_divmod_inpl(mpz_t *dest_quo, mpz_t *dest_rem, const mpz_t *lhs, const m static inline size_t mpz_max_num_bits(const mpz_t *z) { return z->len * MPZ_DIG_SIZE; } -// CIRCUITPY-CHANGE +// CIRCUITPY-CHANGE: add num_bits function static inline size_t mpz_num_bits(const mpz_t *z) { if (mpz_is_zero(z)) { return 0; diff --git a/py/nativeglue.c b/py/nativeglue.c index ef75d248c597..4df7fbcd0729 100644 --- a/py/nativeglue.c +++ b/py/nativeglue.c @@ -32,6 +32,7 @@ #include "py/runtime.h" #include "py/smallint.h" #include "py/nativeglue.h" +// CIRCUITPY-CHANGE #include "py/objtype.h" #include "py/gc.h" @@ -320,7 +321,8 @@ const mp_fun_table_t mp_fun_table = { gc_realloc, mp_printf, mp_vprintf, - mp_raise_msg_str, // CIRCUITPY-CHANGE + // CIRCUITPY-CHANGE + mp_raise_msg_str, mp_obj_get_type, mp_obj_new_str, mp_obj_new_bytes, @@ -329,8 +331,9 @@ const mp_fun_table_t mp_fun_table = { mp_obj_new_float_from_d, mp_obj_get_float_to_f, mp_obj_get_float_to_d, - mp_get_buffer_raise, + mp_get_buffer, mp_get_stream_raise, + // CIRCUITPY-CHANGE mp_obj_assert_native_inited, &mp_plat_print, &mp_type_type, diff --git a/py/nativeglue.h b/py/nativeglue.h index 5f7bfe01100c..407fd4c637ae 100644 --- a/py/nativeglue.h +++ b/py/nativeglue.h @@ -145,6 +145,7 @@ typedef struct _mp_fun_table_t { #if defined(__GNUC__) NORETURN // Only certain compilers support no-return attributes in function pointer declarations #endif + // CIRCUITPY-CHANGE void (*raise_msg_str)(const mp_obj_type_t *exc_type, const char *msg); const mp_obj_type_t *(*obj_get_type)(mp_const_obj_t o_in); mp_obj_t (*obj_new_str)(const char *data, size_t len); @@ -154,8 +155,9 @@ typedef struct _mp_fun_table_t { mp_obj_t (*obj_new_float_from_d)(double d); float (*obj_get_float_to_f)(mp_obj_t o); double (*obj_get_float_to_d)(mp_obj_t o); - void (*get_buffer_raise)(mp_obj_t obj, mp_buffer_info_t *bufinfo, mp_uint_t flags); + bool (*get_buffer)(mp_obj_t obj, mp_buffer_info_t *bufinfo, mp_uint_t flags); const mp_stream_p_t *(*get_stream_raise)(mp_obj_t self_in, int flags); + // CIRCUITPY-CHANGE void (*assert_native_inited)(mp_obj_t native_object); const mp_print_t *plat_print; const mp_obj_type_t *type_type; diff --git a/py/nlr.c b/py/nlr.c index 69d252617829..516b8b86276b 100644 --- a/py/nlr.c +++ b/py/nlr.c @@ -28,6 +28,7 @@ #if !MICROPY_NLR_SETJMP // When not using setjmp, nlr_push_tail is called from inline asm so needs special care +// CIRCUITPY-CHANGE: avoid warning #if defined(MICROPY_NLR_X86) && MICROPY_NLR_X86 && defined(MICROPY_NLR_OS_WINDOWS) && MICROPY_NLR_OS_WINDOWS // On these 32-bit platforms make sure nlr_push_tail doesn't have a leading underscore unsigned int nlr_push_tail(nlr_buf_t *nlr) asm ("nlr_push_tail"); diff --git a/py/nlr.h b/py/nlr.h index 827979f34f49..15f883422f5f 100644 --- a/py/nlr.h +++ b/py/nlr.h @@ -48,6 +48,7 @@ // *FORMAT-OFF* // If MICROPY_NLR_SETJMP is not enabled then auto-detect the machine arch +// CIRCUITPY-CHANGE: avoid warning #if !defined(MICROPY_NLR_SETJMP) || !MICROPY_NLR_SETJMP // A lot of nlr-related things need different treatment on Windows #if defined(_WIN32) || defined(__CYGWIN__) @@ -56,10 +57,12 @@ #define MICROPY_NLR_OS_WINDOWS 0 #endif #if defined(__i386__) + // CIRCUITPY-CHANGE: turn off explicitly to avoid warnings #define MICROPY_NLR_SETJMP (0) #define MICROPY_NLR_X86 (1) #define MICROPY_NLR_NUM_REGS (MICROPY_NLR_NUM_REGS_X86) #elif defined(__x86_64__) + // CIRCUITPY-CHANGE: turn off explicitly to avoid warnings #define MICROPY_NLR_SETJMP (0) #define MICROPY_NLR_X64 (1) #if MICROPY_NLR_OS_WINDOWS @@ -68,6 +71,7 @@ #define MICROPY_NLR_NUM_REGS (MICROPY_NLR_NUM_REGS_X64) #endif #elif defined(__thumb2__) || defined(__thumb__) || defined(__arm__) + // CIRCUITPY-CHANGE: turn off explicitly to avoid warnings #define MICROPY_NLR_SETJMP (0) #define MICROPY_NLR_THUMB (1) #if defined(__SOFTFP__) @@ -82,10 +86,12 @@ #define MICROPY_NLR_AARCH64 (1) #define MICROPY_NLR_NUM_REGS (MICROPY_NLR_NUM_REGS_AARCH64) #elif defined(__xtensa__) + // CIRCUITPY-CHANGE: turn off explicitly to avoid warnings #define MICROPY_NLR_SETJMP (0) #define MICROPY_NLR_XTENSA (1) #define MICROPY_NLR_NUM_REGS (MICROPY_NLR_NUM_REGS_XTENSA) #elif defined(__powerpc__) + // CIRCUITPY-CHANGE: turn off explicitly to avoid warnings #define MICROPY_NLR_SETJMP (0) #define MICROPY_NLR_POWERPC (1) // this could be less but using 128 for safety @@ -95,10 +101,11 @@ #define MICROPY_NLR_NUM_REGS (MICROPY_NLR_NUM_REGS_MIPS) #else #define MICROPY_NLR_SETJMP (1) -// #warning "No native NLR support for this arch, using setjmp implementation" + //#warning "No native NLR support for this arch, using setjmp implementation" #endif #endif +// CIRCUITPY-CHANGE // If MICROPY_NLR_SETJMP is not defined above - define/disable it here #if !defined(MICROPY_NLR_SETJMP) #define MICROPY_NLR_SETJMP (0) diff --git a/py/nlraarch64.c b/py/nlraarch64.c index 3c2674621bf2..f6fee5b63abd 100644 --- a/py/nlraarch64.c +++ b/py/nlraarch64.c @@ -26,6 +26,7 @@ #include "py/mpstate.h" // needed for NLR defs +// CIRCUITPY-CHANGE: avoid warnings #if defined(MICROPY_NLR_AARCH64) && MICROPY_NLR_AARCH64 // AArch64 callee-saved registers are x19-x29. @@ -75,6 +76,7 @@ NORETURN void nlr_jump(void *val) { "ret \n" : : "r" (top) + // CIRCUITPY-CHANGE: MicroPython caught up with this change in https://github.com/micropython/micropython/pull/14126 : "memory" ); diff --git a/py/nlrmips.c b/py/nlrmips.c index 81a8306333f9..22690d19dc80 100644 --- a/py/nlrmips.c +++ b/py/nlrmips.c @@ -78,6 +78,7 @@ NORETURN void nlr_jump(void *val) { "nop \n" : : "r" (top) + // CIRCUITPY-CHANGE: MicroPython caught up with this change in https://github.com/micropython/micropython/pull/14126 : "memory" ); MP_UNREACHABLE diff --git a/py/nlrpowerpc.c b/py/nlrpowerpc.c index edcea0d6f2f2..f516383f776e 100644 --- a/py/nlrpowerpc.c +++ b/py/nlrpowerpc.c @@ -114,6 +114,7 @@ NORETURN void nlr_jump(void *val) { "blr ;" : : "r" (&top->regs) + // CIRCUITPY-CHANGE: MicroPython caught up with this change in https://github.com/micropython/micropython/pull/14126 : "memory" ); diff --git a/py/nlrthumb.c b/py/nlrthumb.c index 6cf6e8c487e4..06a713a213b3 100644 --- a/py/nlrthumb.c +++ b/py/nlrthumb.c @@ -26,6 +26,7 @@ #include "py/mpstate.h" +// CIRCUITPY-CHANGE: avoid warning #if defined(MICROPY_NLR_THUMB) && MICROPY_NLR_THUMB #undef nlr_push @@ -133,6 +134,7 @@ NORETURN void nlr_jump(void *val) { "bx lr \n" // return : // output operands : "r" (top) // input operands + // CIRCUITPY-CHANGE: MicroPython makes this change in https://github.com/micropython/micropython/pull/14126 (later than v1.22) : "memory" // clobbered registers ); diff --git a/py/nlrx64.c b/py/nlrx64.c index 000d858cf733..4db637cbe8fb 100644 --- a/py/nlrx64.c +++ b/py/nlrx64.c @@ -26,6 +26,7 @@ #include "py/mpstate.h" +// CIRCUITPY-CHANGE: avoid warning #if defined(MICROPY_NLR_X64) && MICROPY_NLR_X64 #undef nlr_push @@ -123,6 +124,7 @@ NORETURN void nlr_jump(void *val) { "ret \n" // return : // output operands : "r" (top) // input operands + // CIRCUITPY-CHANGE: MicroPython caught up with this change in https://github.com/micropython/micropython/pull/14126 : "memory" // clobbered registers ); diff --git a/py/nlrx86.c b/py/nlrx86.c index 0946f9284356..83b2f0f3ae03 100644 --- a/py/nlrx86.c +++ b/py/nlrx86.c @@ -26,6 +26,7 @@ #include "py/mpstate.h" +// CIRCUITPY-CHANGE: avoid warning #if defined(MICROPY_NLR_X86) && MICROPY_NLR_X86 #undef nlr_push @@ -33,6 +34,7 @@ // For reference, x86 callee save regs are: // ebx, esi, edi, ebp, esp, eip +// CIRCUITPY-CHANGE: avoid warning #if defined(MICROPY_NLR_OS_WINDOWS) && MICROPY_NLR_OS_WINDOWS unsigned int nlr_push_tail(nlr_buf_t *nlr) asm ("nlr_push_tail"); #else @@ -95,6 +97,7 @@ NORETURN void nlr_jump(void *val) { "ret \n" // return : // output operands : "r" (top) // input operands + // CIRCUITPY-CHANGE: MicroPython caught up with this change in https://github.com/micropython/micropython/pull/14126 : "memory" // clobbered registers ); diff --git a/py/nlrxtensa.c b/py/nlrxtensa.c index 78c6a937b1e6..01828ae5f46c 100644 --- a/py/nlrxtensa.c +++ b/py/nlrxtensa.c @@ -26,6 +26,7 @@ #include "py/mpstate.h" +// CIRCUITPY-CHANGE: avoid warning #if defined(MICROPY_NLR_XTENSA) && MICROPY_NLR_XTENSA #undef nlr_push @@ -74,6 +75,7 @@ NORETURN void nlr_jump(void *val) { "ret.n \n" // return : // output operands : "r" (top) // input operands + // CIRCUITPY-CHANGE: MicroPython caught up with this change in https://github.com/micropython/micropython/pull/14126 : "memory" // clobbered registers ); diff --git a/py/obj.c b/py/obj.c index 85deaff8e680..94544cc2ea2c 100644 --- a/py/obj.c +++ b/py/obj.c @@ -29,16 +29,19 @@ #include #include +// CIRCUITPY-CHANGE #include "shared/runtime/interrupt_char.h" #include "py/obj.h" #include "py/objtype.h" #include "py/objint.h" #include "py/objstr.h" +// CIRCUITPY-CHANGE #include "py/qstr.h" #include "py/runtime.h" #include "py/stackctrl.h" #include "py/stream.h" // for mp_obj_print +// CIRCUITPY-CHANGE #include "supervisor/shared/stack.h" // Allocates an object and also sets type, for mp_obj_malloc{,_var} macros. @@ -244,6 +247,7 @@ void mp_obj_print_exception(const mp_print_t *print, mp_obj_t exc) { mp_obj_print_exception_with_limit(print, exc, 0); } +// CIRCUITPY-CHANGE bool PLACE_IN_ITCM(mp_obj_is_true)(mp_obj_t arg) { if (arg == mp_const_false) { return 0; @@ -490,6 +494,7 @@ void mp_obj_get_complex(mp_obj_t arg, mp_float_t *real, mp_float_t *imag) { #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("can't convert to complex")); #else + // CIRCUITPY-CHANGE: more specific mp_raise mp_raise_TypeError_varg( MP_ERROR_TEXT("can't convert %s to complex"), mp_obj_get_type_str(arg)); #endif @@ -509,6 +514,7 @@ void mp_obj_get_array(mp_obj_t o, size_t *len, mp_obj_t **items) { #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("expected tuple/list")); #else + // CIRCUITPY-CHANGE: more specific mp_raise mp_raise_TypeError_varg( MP_ERROR_TEXT("object '%s' isn't a tuple or list"), mp_obj_get_type_str(o)); #endif @@ -587,6 +593,7 @@ mp_obj_t mp_obj_len(mp_obj_t o_in) { #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("object has no len")); #else + // CIRCUITPY-CHANGE: more specific mp_raise mp_raise_TypeError_varg( MP_ERROR_TEXT("object of type '%s' has no len()"), mp_obj_get_type_str(o_in)); #endif @@ -619,6 +626,7 @@ mp_obj_t mp_obj_subscr(mp_obj_t base, mp_obj_t index, mp_obj_t value) { const mp_obj_type_t *type = mp_obj_get_type(base); if (MP_OBJ_TYPE_HAS_SLOT(type, subscr)) { mp_obj_t ret = MP_OBJ_TYPE_GET_SLOT(type, subscr)(base, index, value); + // CIRCUITPY-CHANGE // May have called port specific C code. Make sure it didn't mess up the heap. assert_heap_ok(); if (ret != MP_OBJ_NULL) { @@ -630,6 +638,7 @@ mp_obj_t mp_obj_subscr(mp_obj_t base, mp_obj_t index, mp_obj_t value) { #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("object doesn't support item deletion")); #else + // CIRCUITPY-CHANGE: more specific mp_raise mp_raise_TypeError_varg( MP_ERROR_TEXT("'%s' object doesn't support item deletion"), mp_obj_get_type_str(base)); #endif @@ -637,6 +646,7 @@ mp_obj_t mp_obj_subscr(mp_obj_t base, mp_obj_t index, mp_obj_t value) { #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("object isn't subscriptable")); #else + // CIRCUITPY-CHANGE: more specific mp_raise mp_raise_TypeError_varg( MP_ERROR_TEXT("'%s' object isn't subscriptable"), mp_obj_get_type_str(base)); #endif @@ -644,6 +654,7 @@ mp_obj_t mp_obj_subscr(mp_obj_t base, mp_obj_t index, mp_obj_t value) { #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("object doesn't support item assignment")); #else + // CIRCUITPY-CHANGE: more specific mp_raise mp_raise_TypeError_varg( MP_ERROR_TEXT("'%s' object doesn't support item assignment"), mp_obj_get_type_str(base)); #endif diff --git a/py/obj.h b/py/obj.h index 4ddf30115dc3..2d1637dafa29 100644 --- a/py/obj.h +++ b/py/obj.h @@ -390,12 +390,13 @@ typedef struct _mp_rom_obj_t { mp_const_obj_t o; } mp_rom_obj_t; const mp_obj_fun_builtin_var_t obj_name = \ {{&mp_type_fun_builtin_var}, MP_OBJ_FUN_MAKE_SIG(n_args_min, MP_OBJ_FUN_ARGS_MAX, true), .fun.kw = fun_name} +// CIRCUITPY-CHANGE #define MP_DEFINE_CONST_PROP_GET(obj_name, fun_name) \ const mp_obj_fun_builtin_fixed_t fun_name##_obj = {{&mp_type_fun_builtin_1}, .fun._1 = fun_name}; \ MP_PROPERTY_GETTER(obj_name, (mp_obj_t)&fun_name##_obj); +// CIRCUITPY-CHANGE // These macros are used to define constant or mutable map/dict objects -// These macros are used to define constant map/dict objects // You can put "static" in front of the definition to make it local #define MP_DEFINE_CONST_MAP(map_name, table_name) \ @@ -423,6 +424,7 @@ typedef struct _mp_rom_obj_t { mp_const_obj_t o; } mp_rom_obj_t; #define MP_DEFINE_CONST_DICT(dict_name, table_name) MP_DEFINE_CONST_DICT_WITH_SIZE(dict_name, table_name, MP_ARRAY_SIZE(table_name)) +// CIRCUITPY-CHANGE: mutable version #define MP_DEFINE_MUTABLE_MAP(map_name, table_name) \ mp_map_t map_name = { \ .all_keys_are_qstrs = 1, \ @@ -433,6 +435,7 @@ typedef struct _mp_rom_obj_t { mp_const_obj_t o; } mp_rom_obj_t; .table = table_name, \ } +// CIRCUITPY-CHANGE: mutable version #define MP_DEFINE_MUTABLE_DICT(dict_name, table_name) \ mp_obj_dict_t dict_name = { \ .base = {&mp_type_dict}, \ @@ -873,6 +876,7 @@ extern const mp_obj_type_t mp_type_fun_bc; extern const mp_obj_type_t mp_type_module; extern const mp_obj_type_t mp_type_staticmethod; extern const mp_obj_type_t mp_type_classmethod; +extern const mp_obj_type_t mp_type_bound_meth; extern const mp_obj_type_t mp_type_property; extern const mp_obj_type_t mp_type_stringio; extern const mp_obj_type_t mp_type_bytesio; @@ -918,9 +922,11 @@ extern const mp_obj_type_t mp_type_UnicodeError; extern const mp_obj_type_t mp_type_ValueError; extern const mp_obj_type_t mp_type_ViperTypeError; extern const mp_obj_type_t mp_type_ZeroDivisionError; +// CIRCUITPY-CHANGE #if CIRCUITPY_ALARM extern const mp_obj_type_t mp_type_DeepSleepRequest; #endif +// CIRCUITPY-CHANGE #if CIRCUITPY_WARNINGS extern const mp_obj_type_t mp_type_Warning; extern const mp_obj_type_t mp_type_FutureWarning; @@ -983,10 +989,10 @@ void *mp_obj_malloc_helper(size_t num_bytes, const mp_obj_type_t *type); // optimizations (other tricks like using ({ expr; exper; }) or (exp, expr, expr) in mp_obj_is_type() result // in missed optimizations) #define mp_type_assert_not_bool_int_str_nonetype(t) ( \ - MP_STATIC_ASSERT_NOT_MSC((t) != &mp_type_bool), assert((t) != &mp_type_bool), \ - MP_STATIC_ASSERT_NOT_MSC((t) != &mp_type_int), assert((t) != &mp_type_int), \ - MP_STATIC_ASSERT_NOT_MSC((t) != &mp_type_str), assert((t) != &mp_type_str), \ - MP_STATIC_ASSERT_NOT_MSC((t) != &mp_type_NoneType), assert((t) != &mp_type_NoneType), \ + MP_STATIC_ASSERT_NONCONSTEXPR((t) != &mp_type_bool), assert((t) != &mp_type_bool), \ + MP_STATIC_ASSERT_NONCONSTEXPR((t) != &mp_type_int), assert((t) != &mp_type_int), \ + MP_STATIC_ASSERT_NONCONSTEXPR((t) != &mp_type_str), assert((t) != &mp_type_str), \ + MP_STATIC_ASSERT_NONCONSTEXPR((t) != &mp_type_NoneType), assert((t) != &mp_type_NoneType), \ 1) #define mp_obj_is_type(o, t) (mp_type_assert_not_bool_int_str_nonetype(t) && mp_obj_is_exact_type(o, t)) @@ -1034,7 +1040,6 @@ mp_obj_t mp_obj_new_bytearray_by_ref(size_t n, void *items); #if MICROPY_PY_BUILTINS_FLOAT mp_obj_t mp_obj_new_int_from_float(mp_float_t val); mp_obj_t mp_obj_new_complex(mp_float_t real, mp_float_t imag); - // CIRCUITPY-CHANGE: our own conversion routines that don't bring double routines extern mp_float_t uint64_to_float(uint64_t ui64); extern uint64_t float_to_uint64(float f); diff --git a/py/objarray.c b/py/objarray.c index 4e986e2378f3..bf12b7e5ef6f 100644 --- a/py/objarray.c +++ b/py/objarray.c @@ -293,6 +293,7 @@ STATIC void memoryview_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { mp_obj_array_t *self = MP_OBJ_TO_PTR(self_in); dest[0] = MP_OBJ_NEW_SMALL_INT(mp_binary_get_size('@', self->typecode & TYPECODE_MASK, NULL)); } + // CIRCUITPY-CHANGE #if MICROPY_PY_BUILTINS_BYTES_HEX || MICROPY_CPYTHON_COMPAT else { // Need to forward to locals dict. @@ -518,6 +519,7 @@ STATIC mp_obj_t array_extend(mp_obj_t self_in, mp_obj_t arg_in) { MP_DEFINE_CONST_FUN_OBJ_2(mp_obj_array_extend_obj, array_extend); #endif +// CIRCUITPY-CHANGE: buffer_finder used belo #if MICROPY_PY_BUILTINS_BYTEARRAY && MICROPY_CPYTHON_COMPAT STATIC mp_obj_t buffer_finder(size_t n_args, const mp_obj_t *args, int direction, bool is_index) { mp_check_self(mp_obj_is_type(args[0], &mp_type_bytearray)); @@ -653,6 +655,7 @@ STATIC mp_obj_t array_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value } else { mp_seq_replace_slice_no_grow(dest_items, o->len, slice.start, slice.stop, src_items, src_len, item_sz); + // CIRCUITPY-CHANGE #if MICROPY_NONSTANDARD_TYPECODES // Clear "freed" elements at the end of list // TODO: This is actually only needed for typecode=='O' @@ -732,7 +735,6 @@ STATIC mp_int_t array_get_buffer(mp_obj_t o_in, mp_buffer_info_t *bufinfo, mp_ui return 0; } - // CIRCUITPY-CHANGE #if MICROPY_CPYTHON_COMPAT && MICROPY_PY_BUILTINS_BYTEARRAY // Directly lifted from objstr.c @@ -808,6 +810,7 @@ MP_DEFINE_CONST_OBJ_TYPE( #define MEMORYVIEW_TYPE_ATTR #endif +// CIRCUITPY-CHANGE: provides cast #if MICROPY_CPYTHON_COMPAT // CIRCUITPY-CHANGE: provides cast STATIC const mp_rom_map_elem_t memoryview_locals_dict_table[] = { diff --git a/py/objboundmeth.c b/py/objboundmeth.c index 8486f876f9a5..b0be810c5c15 100644 --- a/py/objboundmeth.c +++ b/py/objboundmeth.c @@ -83,6 +83,28 @@ STATIC mp_obj_t bound_meth_call(mp_obj_t self_in, size_t n_args, size_t n_kw, co return mp_call_method_self_n_kw(self->meth, self->self, n_args, n_kw, args); } +STATIC mp_obj_t bound_meth_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + mp_obj_bound_meth_t *self = MP_OBJ_TO_PTR(self_in); + switch (op) { + case MP_UNARY_OP_HASH: + return MP_OBJ_NEW_SMALL_INT((mp_uint_t)self->self ^ (mp_uint_t)self->meth); + default: + return MP_OBJ_NULL; // op not supported + } +} + +STATIC mp_obj_t bound_meth_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { + // The MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE flag is clear for this type, so if this + // function is called with MP_BINARY_OP_EQUAL then lhs_in and rhs_in must have the + // same type, which is mp_type_bound_meth. + if (op != MP_BINARY_OP_EQUAL) { + return MP_OBJ_NULL; // op not supported + } + mp_obj_bound_meth_t *lhs = MP_OBJ_TO_PTR(lhs_in); + mp_obj_bound_meth_t *rhs = MP_OBJ_TO_PTR(rhs_in); + return mp_obj_new_bool(lhs->self == rhs->self && lhs->meth == rhs->meth); +} + #if MICROPY_PY_FUNCTION_ATTRS STATIC void bound_meth_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { if (dest[0] != MP_OBJ_NULL) { @@ -107,13 +129,15 @@ STATIC void bound_meth_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { #define BOUND_METH_TYPE_ATTR #endif -STATIC MP_DEFINE_CONST_OBJ_TYPE( +MP_DEFINE_CONST_OBJ_TYPE( mp_type_bound_meth, MP_QSTR_bound_method, MP_TYPE_FLAG_NONE, BOUND_METH_TYPE_PRINT BOUND_METH_TYPE_ATTR - call, bound_meth_call + call, bound_meth_call, + unary_op, bound_meth_unary_op, + binary_op, bound_meth_binary_op ); mp_obj_t mp_obj_new_bound_meth(mp_obj_t meth, mp_obj_t self) { diff --git a/py/objdeque.c b/py/objdeque.c index 583537017fdb..5f22bb33f96e 100644 --- a/py/objdeque.c +++ b/py/objdeque.c @@ -30,6 +30,9 @@ #if MICROPY_PY_COLLECTIONS_DEQUE +// CIRCUITPY-CHANGE: Upcoming https://github.com/micropython/micropython/pull/10724 +// will incporate some or all of the changes here. + typedef struct _mp_obj_deque_t { mp_obj_base_t base; size_t alloc; @@ -40,6 +43,7 @@ typedef struct _mp_obj_deque_t { #define FLAG_CHECK_OVERFLOW 1 } mp_obj_deque_t; +// CIRCUITPY-CHANGE: add operations static mp_obj_t mp_obj_deque_append(mp_obj_t self_in, mp_obj_t arg); static mp_obj_t mp_obj_deque_extend(mp_obj_t self_in, mp_obj_t arg_in); #if MICROPY_PY_COLLECTIONS_DEQUE_ITER @@ -64,6 +68,7 @@ static mp_obj_t deque_make_new(const mp_obj_type_t *type, size_t n_args, size_t o->flags = mp_obj_get_int(args[2]); } + // CIRCUITPY-CHANGE: allow non-empty initialization mp_obj_deque_extend(MP_OBJ_FROM_PTR(o), args[0]); return MP_OBJ_FROM_PTR(o); diff --git a/py/objdict.c b/py/objdict.c index c168f480112c..18862decb7f6 100644 --- a/py/objdict.c +++ b/py/objdict.c @@ -87,6 +87,7 @@ STATIC mp_map_elem_t *dict_iter_next(mp_obj_dict_t *dict, size_t *cur) { } STATIC void dict_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + // CIRCUITPY-CHANGE mp_obj_dict_t *self = native_dict(self_in); bool first = true; const char *item_separator = ", "; @@ -127,6 +128,7 @@ STATIC void dict_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_ } } +// CIRCUITPY-CHANGE // This is a helper function to initialize an empty, but typed dictionary with // a given number of slots. STATIC mp_obj_t dict_new_typed(const mp_obj_type_t *type, const size_t n) { @@ -160,6 +162,7 @@ mp_obj_t mp_obj_dict_make_new(const mp_obj_type_t *type, size_t n_args, size_t n } STATIC mp_obj_t dict_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + // CIRCUITPY-CHANGE mp_obj_dict_t *self = native_dict(self_in); switch (op) { case MP_UNARY_OP_BOOL: @@ -178,6 +181,7 @@ STATIC mp_obj_t dict_unary_op(mp_unary_op_t op, mp_obj_t self_in) { } STATIC mp_obj_t dict_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { + // CIRCUITPY-CHANGE mp_obj_dict_t *o = native_dict(lhs_in); switch (op) { case MP_BINARY_OP_CONTAINS: { @@ -239,6 +243,7 @@ STATIC mp_obj_t dict_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_ // Note: Make sure this is inlined in load part of dict_subscr() below. mp_obj_t mp_obj_dict_get(mp_obj_t self_in, mp_obj_t index) { + // CIRCUITPY-CHANGE mp_obj_dict_t *self = native_dict(self_in); mp_map_elem_t *elem = mp_map_lookup(&self->map, index, MP_MAP_LOOKUP); if (elem == NULL) { @@ -255,6 +260,7 @@ STATIC mp_obj_t dict_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { return mp_const_none; } else if (value == MP_OBJ_SENTINEL) { // load + // CIRCUITPY-CHANGE mp_obj_dict_t *self = native_dict(self_in); mp_map_elem_t *elem = mp_map_lookup(&self->map, index, MP_MAP_LOOKUP); if (elem == NULL) { @@ -272,6 +278,7 @@ STATIC mp_obj_t dict_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { /******************************************************************************/ /* dict methods */ +// CIRCUITPY-CHANGE STATIC void PLACE_IN_ITCM(mp_ensure_not_fixed)(const mp_obj_dict_t * dict) { if (dict->map.is_fixed) { mp_raise_TypeError(NULL); @@ -280,6 +287,7 @@ STATIC void PLACE_IN_ITCM(mp_ensure_not_fixed)(const mp_obj_dict_t * dict) { STATIC mp_obj_t dict_clear(mp_obj_t self_in) { mp_check_self(mp_obj_is_dict_or_ordereddict(self_in)); + // CIRCUITPY-CHANGE mp_obj_dict_t *self = native_dict(self_in); mp_ensure_not_fixed(self); @@ -291,8 +299,10 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(dict_clear_obj, dict_clear); mp_obj_t mp_obj_dict_copy(mp_obj_t self_in) { mp_check_self(mp_obj_is_dict_or_ordereddict(self_in)); + // CIRCUITPY-CHANGE mp_obj_dict_t *self = native_dict(self_in); mp_obj_t other_out = mp_obj_new_dict(self->map.alloc); + // CIRCUITPY-CHANGE mp_obj_dict_t *other = native_dict(other_out); other->base.type = self->base.type; other->map.used = self->map.used; @@ -307,6 +317,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(dict_copy_obj, mp_obj_dict_copy); #if MICROPY_PY_BUILTINS_DICT_FROMKEYS // this is a classmethod STATIC mp_obj_t dict_fromkeys(size_t n_args, const mp_obj_t *args) { + // CIRCUITPY-CHANGE mp_obj_type_t *type = MP_OBJ_TO_PTR(args[0]); mp_obj_t iter = mp_getiter(args[1], NULL); mp_obj_t value = mp_const_none; @@ -340,6 +351,7 @@ 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_or_ordereddict(args[0])); + // CIRCUITPY-CHANGE mp_obj_dict_t *self = native_dict(args[0]); if (lookup_kind != MP_MAP_LOOKUP) { mp_ensure_not_fixed(self); @@ -385,9 +397,11 @@ 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_or_ordereddict(self_in)); + // CIRCUITPY-CHANGE mp_obj_dict_t *self = native_dict(self_in); mp_ensure_not_fixed(self); if (self->map.used == 0) { + // CIRCUITPY-CHANGE mp_raise_msg_varg(&mp_type_KeyError, MP_ERROR_TEXT("pop from empty %q"), MP_QSTR_dict); } size_t cur = 0; @@ -410,6 +424,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_or_ordereddict(args[0])); + // CIRCUITPY-CHANGE mp_obj_dict_t *self = native_dict(args[0]); mp_ensure_not_fixed(self); @@ -536,6 +551,7 @@ typedef struct _mp_obj_dict_view_t { STATIC mp_obj_t dict_view_it_iternext(mp_obj_t self_in) { mp_check_self(mp_obj_is_type(self_in, &mp_type_dict_view_it)); mp_obj_dict_view_it_t *self = MP_OBJ_TO_PTR(self_in); + // CIRCUITPY-CHANGE mp_map_elem_t *next = dict_iter_next(native_dict(self->dict), &self->cur); if (next == NULL) { @@ -594,7 +610,6 @@ STATIC void dict_view_print(const mp_print_t *print, mp_obj_t self_in, mp_print_ mp_print_str(print, "])"); } -// CIRCUITPY-CHANGE STATIC mp_obj_t dict_view_unary_op(mp_unary_op_t op, mp_obj_t o_in) { mp_obj_dict_view_t *o = MP_OBJ_TO_PTR(o_in); // only dict.values() supports __hash__. @@ -742,6 +757,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_or_ordereddict(self_in)); + // CIRCUITPY-CHANGE mp_obj_dict_t *self = native_dict(self_in); mp_ensure_not_fixed(self); mp_map_lookup(&self->map, key, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value; diff --git a/py/objexcept.c b/py/objexcept.c index 07727a2c0502..03dbff334e4b 100644 --- a/py/objexcept.c +++ b/py/objexcept.c @@ -31,6 +31,7 @@ #include #include "py/objlist.h" +// CIRCUITPY-CHANGE #include "py/objnamedtuple.h" #include "py/objstr.h" #include "py/objtuple.h" @@ -82,6 +83,8 @@ void mp_init_emergency_exception_buf(void) { #else #define mp_emergency_exception_buf_size MP_STATE_VM(mp_emergency_exception_buf_size) +#include "py/mphal.h" // for MICROPY_BEGIN_ATOMIC_SECTION/MICROPY_END_ATOMIC_SECTION + void mp_init_emergency_exception_buf(void) { mp_emergency_exception_buf_size = 0; MP_STATE_VM(mp_emergency_exception_buf) = NULL; @@ -233,7 +236,7 @@ mp_obj_t mp_obj_exception_make_new(const mp_obj_type_t *type, size_t n_args, siz o_tuple = (mp_obj_tuple_t *)&mp_const_empty_tuple_obj; } else { // Try to allocate memory for the tuple containing the args - o_tuple = m_new_obj_var_maybe(mp_obj_tuple_t, mp_obj_t, n_args); + o_tuple = m_new_obj_var_maybe(mp_obj_tuple_t, items, mp_obj_t, n_args); #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF // If we are called by mp_obj_new_exception_msg_varg then it will have @@ -462,6 +465,7 @@ MP_DEFINE_EXCEPTION(DeepSleepRequest, BaseException) MP_DEFINE_EXCEPTION(RuntimeWarning, Warning) MP_DEFINE_EXCEPTION(SyntaxWarning, Warning) MP_DEFINE_EXCEPTION(UserWarning, Warning) + MP_DEFINE_EXCEPTION(FutureWarning, Warning) MP_DEFINE_EXCEPTION(ImportWarning, Warning) MP_DEFINE_EXCEPTION(UnicodeWarning, Warning) MP_DEFINE_EXCEPTION(BytesWarning, Warning) @@ -527,7 +531,7 @@ mp_obj_t mp_obj_new_exception_msg_varg(const mp_obj_type_t *exc_type, mp_rom_err return exc; } -mp_obj_t mp_obj_new_exception_msg_vlist(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, va_list ap) { +mp_obj_t mp_obj_new_exception_msg_vlist(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, va_list args) { assert(fmt != NULL); // Check that the given type is an exception type @@ -535,6 +539,7 @@ mp_obj_t mp_obj_new_exception_msg_vlist(const mp_obj_type_t *exc_type, mp_rom_er // Try to allocate memory for the message mp_obj_str_t *o_str = m_new_obj_maybe(mp_obj_str_t); + // CIRCUITPY-CHANGE: here and more below size_t o_str_alloc = decompress_length(fmt); byte *o_str_buf = m_new_maybe(byte, o_str_alloc); @@ -568,7 +573,7 @@ mp_obj_t mp_obj_new_exception_msg_vlist(const mp_obj_type_t *exc_type, mp_rom_er // We have some memory to format the string. struct _exc_printer_t exc_pr = {!used_emg_buf, o_str_alloc, 0, o_str_buf}; mp_print_t print = {&exc_pr, exc_add_strn}; - mp_vcprintf(&print, fmt, ap); + mp_vcprintf(&print, fmt, args); exc_pr.buf[exc_pr.len] = '\0'; o_str->len = exc_pr.len; o_str->data = exc_pr.buf; @@ -584,6 +589,7 @@ mp_obj_t mp_obj_new_exception_msg_vlist(const mp_obj_type_t *exc_type, mp_rom_er mp_obj_t arg = MP_OBJ_FROM_PTR(o_str); return mp_obj_exception_make_new(exc_type, 1, 0, &arg); } + #endif // return true if the given object is an exception type @@ -617,6 +623,7 @@ bool mp_obj_exception_match(mp_obj_t exc, mp_const_obj_t exc_type) { // traceback handling functions void mp_obj_exception_clear_traceback(mp_obj_t self_in) { + // CIRCUITPY-CHANGE mp_obj_exception_t *self = mp_obj_exception_get_native(self_in); // just set the traceback to the empty traceback object // we don't want to call any memory management functions here @@ -629,6 +636,7 @@ void mp_obj_exception_clear_traceback(mp_obj_t self_in) { #endif } +// CIRCUITPY-CHANGE: many changes for tracebacks void mp_obj_exception_add_traceback(mp_obj_t self_in, qstr file, size_t line, qstr block) { mp_obj_exception_t *self = mp_obj_exception_get_native(self_in); @@ -716,7 +724,7 @@ void mp_obj_exception_get_traceback(mp_obj_t self_in, size_t *n, size_t **values } } -// CIRCUITPY-CHANGE +// CIRCUITPY-CHANGE: here until end #if MICROPY_PY_SYS_EXC_INFO STATIC const mp_obj_namedtuple_type_t code_type_obj = { NAMEDTUPLE_TYPE_BASE_AND_SLOTS(MP_QSTR_code), diff --git a/py/objexcept.h b/py/objexcept.h index 1311effde850..3c1278736a55 100644 --- a/py/objexcept.h +++ b/py/objexcept.h @@ -34,6 +34,7 @@ typedef struct _mp_obj_exception_t { mp_obj_base_t base; mp_obj_tuple_t *args; + // CIRCUITPY-CHANGE mp_obj_traceback_t *traceback; #if MICROPY_CPYTHON_EXCEPTION_CHAIN struct _mp_obj_exception_t *cause, *context; @@ -44,6 +45,7 @@ typedef struct _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); +// CIRCUITPY-CHANGE: new routines void mp_obj_exception_initialize0(mp_obj_exception_t *o_exc, const mp_obj_type_t *type); mp_obj_exception_t *mp_obj_exception_get_native(mp_obj_t self_in); diff --git a/py/objfloat.c b/py/objfloat.c index 66de3a0b74f4..9129c364032d 100644 --- a/py/objfloat.c +++ b/py/objfloat.c @@ -270,6 +270,7 @@ mp_obj_t mp_obj_float_binary_op(mp_binary_op_t op, mp_float_t lhs_val, mp_obj_t case MP_BINARY_OP_INPLACE_FLOOR_DIVIDE: if (rhs_val == 0) { zero_division_error: + // CIRCUITPY-CHANGE: a message here is redundant mp_raise_ZeroDivisionError(); } // Python specs require that x == (x//y)*y + (x%y) so we must diff --git a/py/objfun.c b/py/objfun.c index df12f0b401c8..46145129eb4e 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -48,6 +48,7 @@ /******************************************************************************/ /* builtin functions */ +// CIRCUITPY-CHANGE: PLACE_IN_ITCM STATIC mp_obj_t PLACE_IN_ITCM(fun_builtin_0_call)(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { (void)args; assert(mp_obj_is_type(self_in, &mp_type_fun_builtin_0)); @@ -61,6 +62,7 @@ MP_DEFINE_CONST_OBJ_TYPE( call, fun_builtin_0_call ); +// CIRCUITPY-CHANGE: PLACE_IN_ITCM STATIC mp_obj_t PLACE_IN_ITCM(fun_builtin_1_call)(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { assert(mp_obj_is_type(self_in, &mp_type_fun_builtin_1)); mp_obj_fun_builtin_fixed_t *self = MP_OBJ_TO_PTR(self_in); @@ -85,6 +87,7 @@ MP_DEFINE_CONST_OBJ_TYPE( call, fun_builtin_2_call ); +// CIRCUITPY-CHANGE: PLACE_IN_ITCM STATIC mp_obj_t PLACE_IN_ITCM(fun_builtin_3_call)(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { assert(mp_obj_is_type(self_in, &mp_type_fun_builtin_3)); mp_obj_fun_builtin_fixed_t *self = MP_OBJ_TO_PTR(self_in); @@ -215,7 +218,7 @@ mp_code_state_t *mp_obj_fun_bc_prepare_codestate(mp_obj_t self_in, size_t n_args // RuntimeError should be raised instead. So, we use m_new_obj_var_maybe(), // return NULL, then vm.c takes the needed action (either raise // RuntimeError or fallback to stack allocation). - code_state = m_new_obj_var_maybe(mp_code_state_t, byte, state_size); + code_state = m_new_obj_var_maybe(mp_code_state_t, state, byte, state_size); if (!code_state) { return NULL; } @@ -230,6 +233,7 @@ mp_code_state_t *mp_obj_fun_bc_prepare_codestate(mp_obj_t self_in, size_t n_args } #endif +// CIRCUITPY-CHANGE: PLACE_IN_ITCM STATIC mp_obj_t PLACE_IN_ITCM(fun_bc_call)(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { MP_STACK_CHECK(); @@ -247,10 +251,10 @@ STATIC mp_obj_t PLACE_IN_ITCM(fun_bc_call)(mp_obj_t self_in, size_t n_args, size // allocate state for locals and stack mp_code_state_t *code_state = NULL; #if MICROPY_ENABLE_PYSTACK - code_state = mp_pystack_alloc(sizeof(mp_code_state_t) + state_size); + code_state = mp_pystack_alloc(offsetof(mp_code_state_t, state) + state_size); #else if (state_size > VM_MAX_STATE_ON_STACK) { - code_state = m_new_obj_var_maybe(mp_code_state_t, byte, state_size); + code_state = m_new_obj_var_maybe(mp_code_state_t, state, byte, state_size); #if MICROPY_DEBUG_VM_STACK_OVERFLOW if (code_state != NULL) { memset(code_state->state, 0, state_size); @@ -258,7 +262,7 @@ STATIC mp_obj_t PLACE_IN_ITCM(fun_bc_call)(mp_obj_t self_in, size_t n_args, size #endif } if (code_state == NULL) { - code_state = alloca(sizeof(mp_code_state_t) + state_size); + code_state = alloca(offsetof(mp_code_state_t, state) + state_size); #if MICROPY_DEBUG_VM_STACK_OVERFLOW memset(code_state->state, 0, state_size); #endif @@ -320,7 +324,7 @@ STATIC mp_obj_t PLACE_IN_ITCM(fun_bc_call)(mp_obj_t self_in, size_t n_args, size #else // free the state if it was allocated on the heap if (state_size != 0) { - m_del_var(mp_code_state_t, byte, state_size, code_state); + m_del_var(mp_code_state_t, state, byte, state_size, code_state); } #endif @@ -402,6 +406,7 @@ mp_obj_t mp_obj_new_fun_bc(const mp_obj_t *def_args, const byte *code, const mp_ #if MICROPY_EMIT_NATIVE +// CIRCUITPY-CHANGE: PLACE_IN_ITCM STATIC mp_obj_t PLACE_IN_ITCM(fun_native_call)(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { MP_STACK_CHECK(); mp_obj_fun_bc_t *self = MP_OBJ_TO_PTR(self_in); @@ -499,6 +504,7 @@ STATIC mp_uint_t convert_obj_for_inline_asm(mp_obj_t obj) { } } +// CIRCUITPY-CHANGE: PLACE_IN_ITCM STATIC mp_obj_t PLACE_IN_ITCM(fun_asm_call)(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_obj_fun_asm_t *self = self_in; diff --git a/py/objgenerator.c b/py/objgenerator.c index 67e88fb6c7ca..6dc1f17aa803 100644 --- a/py/objgenerator.c +++ b/py/objgenerator.c @@ -58,6 +58,7 @@ typedef struct _mp_obj_gen_instance_t { } mp_obj_gen_instance_t; STATIC mp_obj_t gen_wrap_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + // CIRCUITPY-CHANGE // A generating or coroutine function is just a bytecode function // with type mp_type_gen_wrap or mp_type_coro_wrap. mp_obj_fun_bc_t *self_fun = MP_OBJ_TO_PTR(self_in); @@ -66,9 +67,11 @@ STATIC mp_obj_t gen_wrap_call(mp_obj_t self_in, size_t n_args, size_t n_kw, cons const uint8_t *ip = self_fun->bytecode; MP_BC_PRELUDE_SIG_DECODE(ip); + // CIRCUITPY-CHANGE // allocate the generator or coroutine object, with room for local stack and exception stack mp_obj_gen_instance_t *o = mp_obj_malloc_var(mp_obj_gen_instance_t, byte, n_state * sizeof(mp_obj_t) + n_exc_stack * sizeof(mp_exc_stack_t), + // CIRCUITPY-CHANGE #if MICROPY_PY_ASYNC_AWAIT self_fun->base.type == &mp_type_gen_wrap ? &mp_type_gen_instance : &mp_type_coro_instance #else @@ -97,6 +100,7 @@ MP_DEFINE_CONST_OBJ_TYPE( call, gen_wrap_call ); +// CIRCUITPY-CHANGE #if MICROPY_PY_ASYNC_AWAIT MP_DEFINE_CONST_OBJ_TYPE( mp_type_coro_wrap, @@ -124,6 +128,7 @@ STATIC mp_obj_t native_gen_wrap_call(mp_obj_t self_in, size_t n_args, size_t n_k mp_obj_fun_bc_t *self_fun = MP_OBJ_TO_PTR(self_in); // Determine start of prelude. + // CIRCUITPY-CHANGE: prevent compiler warning #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" uintptr_t prelude_ptr_index = ((uintptr_t *)self_fun->bytecode)[0]; @@ -141,6 +146,7 @@ STATIC mp_obj_t native_gen_wrap_call(mp_obj_t self_in, size_t n_args, size_t n_k // Allocate the generator object, with room for local stack (exception stack not needed). mp_obj_gen_instance_native_t *o = mp_obj_malloc_var(mp_obj_gen_instance_native_t, byte, n_state * sizeof(mp_obj_t), + // CIRCUITPY-CHANGE #if MICROPY_PY_ASYNC_AWAIT (self_fun->base.type == &mp_type_native_gen_wrap) ? &mp_type_gen_instance : &mp_type_coro_instance #else @@ -160,6 +166,7 @@ STATIC mp_obj_t native_gen_wrap_call(mp_obj_t self_in, size_t n_args, size_t n_k o->code_state.exc_sp_idx = MP_CODE_STATE_EXC_SP_IDX_SENTINEL; // Prepare the generator instance for execution + // CIRCUITPY-CHANGE: prevent compiler warning #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" uintptr_t start_offset = ((uintptr_t *)self_fun->bytecode)[1]; @@ -183,7 +190,7 @@ MP_DEFINE_CONST_OBJ_TYPE( NATIVE_GEN_WRAP_TYPE_ATTR ); - +// CIRCUITPY-CHANGE #if MICROPY_PY_ASYNC_AWAIT MP_DEFINE_CONST_OBJ_TYPE( mp_type_native_coro_wrap, @@ -359,6 +366,7 @@ STATIC mp_obj_t gen_instance_send(mp_obj_t self_in, mp_obj_t send_value) { } STATIC MP_DEFINE_CONST_FUN_OBJ_2(gen_instance_send_obj, gen_instance_send); +// CIRCUITPY-CHANGE #if MICROPY_PY_ASYNC_AWAIT STATIC mp_obj_t coro_instance_await(mp_obj_t self_in) { return self_in; @@ -388,6 +396,7 @@ STATIC mp_obj_t gen_instance_throw(size_t n_args, const mp_obj_t *args) { } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(gen_instance_throw_obj, 2, 4, gen_instance_throw); +// CIRCUITPY-CHANGE static mp_obj_t generatorexit(void) { #if MICROPY_CPYTHON_EXCEPTION_CHAIN MP_STATIC_ASSERT(!MICROPY_CONST_GENERATOREXIT_OBJ); @@ -400,6 +409,7 @@ static mp_obj_t generatorexit(void) { STATIC mp_obj_t gen_instance_close(mp_obj_t self_in) { mp_obj_t ret; + // CIRCUITPY-CHANGE switch (mp_obj_gen_resume(self_in, mp_const_none, generatorexit(), &ret)) { case MP_VM_RETURN_YIELD: mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("generator ignored GeneratorExit")); @@ -452,8 +462,8 @@ MP_DEFINE_CONST_OBJ_TYPE( locals_dict, &gen_instance_locals_dict ); +// CIRCUITPY-CHANGE: additions for coroutines #if MICROPY_PY_ASYNC_AWAIT -// CIRCUITPY-CHANGE // coroutine instance locals dict and type // same as generator, but with addition of __await()__. STATIC const mp_rom_map_elem_t coro_instance_locals_dict_table[] = { diff --git a/py/objint.c b/py/objint.c index ed686bd218f4..f8fb9b511375 100644 --- a/py/objint.c +++ b/py/objint.c @@ -140,8 +140,10 @@ mp_obj_t mp_obj_new_int_from_float(mp_float_t val) { if (u.p.exp == ((1 << MP_FLOAT_EXP_BITS) - 1)) { // ...then number is Inf (positive or negative) if fraction is 0, else NaN. if (u.p.frc == 0) { + // CIRCUITPY-CHANGE mp_raise_msg_varg(&mp_type_OverflowError, MP_ERROR_TEXT("can't convert %s to int"), "inf"); } else { + // CIRCUITPY-CHANGE mp_raise_ValueError_varg(MP_ERROR_TEXT("can't convert %s to int"), "NaN"); } } else { @@ -300,7 +302,7 @@ char *mp_obj_int_formatted(char **buf, size_t *buf_size, size_t *fmt_size, mp_co return b; } -// CIRCUITPY-CHANGE +// CIRCUITPY-CHANGE: more thorough checking #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE void mp_obj_int_buffer_overflow_check(mp_obj_t self_in, size_t nbytes, bool is_signed) { @@ -528,6 +530,7 @@ STATIC mp_obj_t int_from_bytes(size_t n_args, const mp_obj_t *pos_args, mp_map_t STATIC MP_DEFINE_CONST_FUN_OBJ_KW(int_from_bytes_fun_obj, 3, int_from_bytes); STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(int_from_bytes_obj, MP_ROM_PTR(&int_from_bytes_fun_obj)); +// CIRCUITPY-CHANGE: supports signed STATIC mp_obj_t int_to_bytes(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_length, ARG_byteorder, ARG_signed }; static const mp_arg_t allowed_args[] = { diff --git a/py/objint_longlong.c b/py/objint_longlong.c index 4a5ef05fcfbb..70d7e2873c55 100644 --- a/py/objint_longlong.c +++ b/py/objint_longlong.c @@ -43,7 +43,7 @@ const mp_obj_int_t mp_sys_maxsize_obj = {{&mp_type_int}, MP_SSIZE_MAX}; #endif -// CIRCUITPY-CHANGE +// CIRCUITPY-CHANGE: bit_length mp_obj_t mp_obj_int_bit_length_impl(mp_obj_t self_in) { assert(mp_obj_is_type(self_in, &mp_type_int)); mp_obj_int_t *self = self_in; diff --git a/py/objint_mpz.c b/py/objint_mpz.c index 847653b25fe7..71e6d51f3a41 100644 --- a/py/objint_mpz.c +++ b/py/objint_mpz.c @@ -47,22 +47,22 @@ STATIC const mpz_dig_t maxsize_dig[] = { #define NUM_DIG 1 (MP_SSIZE_MAX >> MPZ_DIG_SIZE * 0) & DIG_MASK, #if (MP_SSIZE_MAX >> MPZ_DIG_SIZE * 0) > DIG_MASK -#undef NUM_DIG + #undef NUM_DIG #define NUM_DIG 2 - (MP_SSIZE_MAX >> MPZ_DIG_SIZE * 1) & DIG_MASK, - #if (MP_SSIZE_MAX >> MPZ_DIG_SIZE * 1) > DIG_MASK -#undef NUM_DIG + (MP_SSIZE_MAX >> MPZ_DIG_SIZE * 1) & DIG_MASK, + #if (MP_SSIZE_MAX >> MPZ_DIG_SIZE * 1) > DIG_MASK + #undef NUM_DIG #define NUM_DIG 3 - (MP_SSIZE_MAX >> MPZ_DIG_SIZE * 2) & DIG_MASK, - #if (MP_SSIZE_MAX >> MPZ_DIG_SIZE * 2) > DIG_MASK -#undef NUM_DIG + (MP_SSIZE_MAX >> MPZ_DIG_SIZE * 2) & DIG_MASK, + #if (MP_SSIZE_MAX >> MPZ_DIG_SIZE * 2) > DIG_MASK + #undef NUM_DIG #define NUM_DIG 4 - (MP_SSIZE_MAX >> MPZ_DIG_SIZE * 3) & DIG_MASK, - #if (MP_SSIZE_MAX >> MPZ_DIG_SIZE * 3) > DIG_MASK - #error cannot encode MP_SSIZE_MAX as mpz - #endif - #endif - #endif + (MP_SSIZE_MAX >> MPZ_DIG_SIZE * 3) & DIG_MASK, + #if (MP_SSIZE_MAX >> MPZ_DIG_SIZE * 3) > DIG_MASK + #error cannot encode MP_SSIZE_MAX as mpz + #endif + #endif + #endif #endif }; // *FORMAT-ON* @@ -244,6 +244,7 @@ mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i case MP_BINARY_OP_INPLACE_FLOOR_DIVIDE: { if (mpz_is_zero(zrhs)) { zero_division_error: + // CIRCUITPY-CHANGE: remove redundant message mp_raise_ZeroDivisionError(); } mpz_t rem; @@ -364,6 +365,7 @@ mp_obj_t mp_obj_int_pow3(mp_obj_t base, mp_obj_t exponent, mp_obj_t modulus) { mpz_t *rhs = mp_mpz_for_int(exponent, &r_temp); mpz_t *mod = mp_mpz_for_int(modulus, &m_temp); + // CIRCUITPY-CHANGE: extra checking if (mpz_is_zero(mod)) { mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("pow() 3rd argument cannot be 0")); } diff --git a/py/objlist.c b/py/objlist.c index 07cfbd7a51d6..d2de15fb642f 100644 --- a/py/objlist.c +++ b/py/objlist.c @@ -93,11 +93,13 @@ mp_obj_t mp_obj_list_make_new(const mp_obj_type_t *type_in, size_t n_args, size_ } } +// CIRCUITPY-CHANGE STATIC mp_obj_list_t *native_list(mp_obj_t self_in) { return MP_OBJ_TO_PTR(mp_obj_cast_to_native_base(self_in, MP_OBJ_FROM_PTR(&mp_type_list))); } STATIC mp_obj_t list_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + // CIRCUITPY-CHANGE mp_obj_list_t *self = native_list(self_in); switch (op) { case MP_UNARY_OP_BOOL: @@ -116,6 +118,7 @@ STATIC mp_obj_t list_unary_op(mp_unary_op_t op, mp_obj_t self_in) { } STATIC mp_obj_t list_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) { + // CIRCUITPY-CHANGE mp_obj_list_t *o = native_list(lhs); switch (op) { case MP_BINARY_OP_ADD: { @@ -139,6 +142,7 @@ STATIC mp_obj_t list_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) { if (n < 0) { n = 0; } + // CIRCUITPY-CHANGE size_t new_len = mp_seq_multiply_len(o->len, n); mp_obj_list_t *s = list_new(new_len); mp_seq_multiply(o->items, sizeof(*o->items), o->len, n, s->items); @@ -167,6 +171,7 @@ STATIC mp_obj_t list_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) { } STATIC mp_obj_t list_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { + // CIRCUITPY-CHANGE mp_obj_list_t *self = native_list(self_in); if (value == MP_OBJ_NULL) { // delete @@ -186,6 +191,7 @@ STATIC mp_obj_t list_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { return mp_const_none; } #endif + // CIRCUITPY-CHANGE mp_obj_t args[2] = {MP_OBJ_FROM_PTR(self), index}; list_pop(2, args); return mp_const_none; @@ -246,6 +252,7 @@ STATIC mp_obj_t list_getiter(mp_obj_t o_in, mp_obj_iter_buf_t *iter_buf) { mp_obj_t mp_obj_list_append(mp_obj_t self_in, mp_obj_t arg) { mp_check_self(mp_obj_is_type(self_in, &mp_type_list)); + // CIRCUITPY-CHANGE mp_obj_list_t *self = native_list(self_in); if (self->len >= self->alloc) { self->items = m_renew(mp_obj_t, self->items, self->alloc, self->alloc * 2); @@ -259,6 +266,7 @@ mp_obj_t mp_obj_list_append(mp_obj_t self_in, mp_obj_t arg) { STATIC mp_obj_t list_extend(mp_obj_t self_in, mp_obj_t arg_in) { mp_check_self(mp_obj_is_type(self_in, &mp_type_list)); if (mp_obj_is_type(arg_in, &mp_type_list)) { + // CIRCUITPY-CHANGE mp_obj_list_t *self = native_list(self_in); mp_obj_list_t *arg = native_list(arg_in); @@ -277,6 +285,7 @@ STATIC mp_obj_t list_extend(mp_obj_t self_in, mp_obj_t arg_in) { return mp_const_none; // return None, as per CPython } +// CIRCUITPY-CHANGE: used elsewhere so not static inline mp_obj_t mp_obj_list_pop(mp_obj_list_t *self, size_t index) { if (self->len == 0) { mp_raise_IndexError_varg(MP_ERROR_TEXT("pop from empty %q"), MP_QSTR_list); @@ -348,6 +357,7 @@ mp_obj_t mp_obj_list_sort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ MP_ARRAY_SIZE(allowed_args), allowed_args, (mp_arg_val_t *)&args); mp_check_self(mp_obj_is_type(pos_args[0], &mp_type_list)); + // CIRCUITPY-CHANGE mp_obj_list_t *self = native_list(pos_args[0]); if (self->len > 1) { @@ -359,8 +369,10 @@ mp_obj_t mp_obj_list_sort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ return mp_const_none; } +// CIRCUITPY-CHANGE: used elsewhere so not static mp_obj_t mp_obj_list_clear(mp_obj_t self_in) { mp_check_self(mp_obj_is_type(self_in, &mp_type_list)); + // CIRCUITPY-CHANGE mp_obj_list_t *self = native_list(self_in); self->len = 0; self->items = m_renew(mp_obj_t, self->items, self->alloc, LIST_MIN_ALLOC); @@ -371,22 +383,26 @@ mp_obj_t mp_obj_list_clear(mp_obj_t self_in) { STATIC mp_obj_t list_copy(mp_obj_t self_in) { mp_check_self(mp_obj_is_type(self_in, &mp_type_list)); + // CIRCUITPY-CHANGE mp_obj_list_t *self = native_list(self_in); return mp_obj_new_list(self->len, self->items); } STATIC mp_obj_t list_count(mp_obj_t self_in, mp_obj_t value) { mp_check_self(mp_obj_is_type(self_in, &mp_type_list)); + // CIRCUITPY-CHANGE mp_obj_list_t *self = native_list(self_in); return mp_seq_count_obj(self->items, self->len, value); } STATIC mp_obj_t list_index(size_t n_args, const mp_obj_t *args) { mp_check_self(mp_obj_is_type(args[0], &mp_type_list)); + // CIRCUITPY-CHANGE mp_obj_list_t *self = native_list(args[0]); return mp_seq_index_obj(self->items, self->len, n_args, args); } +// CIRCUITPY-CHANGE: used elsewhere so not static inline void mp_obj_list_insert(mp_obj_list_t *self, size_t index, mp_obj_t obj) { mp_obj_list_append(MP_OBJ_FROM_PTR(self), mp_const_none); @@ -398,6 +414,7 @@ inline void mp_obj_list_insert(mp_obj_list_t *self, size_t index, mp_obj_t obj) STATIC mp_obj_t list_insert(mp_obj_t self_in, mp_obj_t idx, mp_obj_t obj) { mp_check_self(mp_obj_is_type(self_in, &mp_type_list)); + // CIRCUITPY-CHANGE mp_obj_list_t *self = native_list(self_in); // insert has its own strange index logic mp_int_t index = MP_OBJ_SMALL_INT_VALUE(idx); @@ -410,6 +427,7 @@ STATIC mp_obj_t list_insert(mp_obj_t self_in, mp_obj_t idx, mp_obj_t obj) { if ((size_t)index > self->len) { index = self->len; } + // CIRCUITPY-CHANGE mp_obj_list_insert(self, index, obj); return mp_const_none; } @@ -425,6 +443,7 @@ mp_obj_t mp_obj_list_remove(mp_obj_t self_in, mp_obj_t value) { STATIC mp_obj_t list_reverse(mp_obj_t self_in) { mp_check_self(mp_obj_is_type(self_in, &mp_type_list)); + // CIRCUITPY-CHANGE mp_obj_list_t *self = native_list(self_in); mp_int_t len = self->len; @@ -439,6 +458,7 @@ STATIC mp_obj_t list_reverse(mp_obj_t self_in) { STATIC MP_DEFINE_CONST_FUN_OBJ_2(list_append_obj, mp_obj_list_append); STATIC MP_DEFINE_CONST_FUN_OBJ_2(list_extend_obj, list_extend); +// CIRCUITPY-CHANGE: use renamed public function STATIC MP_DEFINE_CONST_FUN_OBJ_1(list_clear_obj, mp_obj_list_clear); STATIC MP_DEFINE_CONST_FUN_OBJ_1(list_copy_obj, list_copy); STATIC MP_DEFINE_CONST_FUN_OBJ_2(list_count_obj, list_count); @@ -504,6 +524,7 @@ mp_obj_t mp_obj_new_list(size_t n, mp_obj_t *items) { } void mp_obj_list_get(mp_obj_t self_in, size_t *len, mp_obj_t **items) { + // CIRCUITPY-CHANGE mp_obj_list_t *self = native_list(self_in); *len = self->len; *items = self->items; @@ -517,6 +538,7 @@ void mp_obj_list_set_len(mp_obj_t self_in, size_t len) { } void mp_obj_list_store(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { + // CIRCUITPY-CHANGE mp_obj_list_t *self = native_list(self_in); size_t i = mp_get_index(self->base.type, self->len, index, false); self->items[i] = value; diff --git a/py/objlist.h b/py/objlist.h index 2046de4b9ef8..3fd49baf29ff 100644 --- a/py/objlist.h +++ b/py/objlist.h @@ -37,7 +37,7 @@ typedef struct _mp_obj_list_t { void mp_obj_list_init(mp_obj_list_t *o, size_t n); mp_obj_t mp_obj_list_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args); -// CIRCUITPY-CHANGE +// CIRCUITPY-CHANGE: new public functions mp_obj_t mp_obj_list_pop(mp_obj_list_t *self, size_t index); void mp_obj_list_insert(mp_obj_list_t *self, size_t index, mp_obj_t obj); diff --git a/py/objmodule.c b/py/objmodule.c index 33dc02636580..d211ccc78d4f 100644 --- a/py/objmodule.c +++ b/py/objmodule.c @@ -34,6 +34,7 @@ #include "py/runtime.h" #include "py/builtin.h" +// CIRCUITPY-CHANGE #if CIRCUITPY_WARNINGS #include "shared-module/warnings/__init__.h" #endif @@ -66,6 +67,7 @@ STATIC void module_attr_try_delegation(mp_obj_t self_in, qstr attr, mp_obj_t *de STATIC void module_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { mp_obj_module_t *self = MP_OBJ_TO_PTR(self_in); if (dest[0] == MP_OBJ_NULL) { + // CIRCUITPY-CHANGE #if CIRCUITPY_8_9_WARNINGS && CIRCUITPY_DISPLAYIO && CIRCUITPY_WARNINGS if (self == &displayio_module) { #if CIRCUITPY_BUSDISPLAY @@ -203,6 +205,7 @@ STATIC const mp_module_delegation_entry_t mp_builtin_module_delegation_table[] = // Attempts to find (and initialise) a built-in, otherwise returns // MP_OBJ_NULL. mp_obj_t mp_module_get_builtin(qstr module_name, bool extensible) { + // CIRCUITPY-CHANGE #if CIRCUITPY_PARALLELDISPLAYBUS && CIRCUITPY_WARNINGS if (module_name == MP_QSTR_paralleldisplay) { warnings_warn(&mp_type_FutureWarning, MP_ERROR_TEXT("%q renamed %q"), MP_QSTR_paralleldisplay, MP_QSTR_paralleldisplaybus); diff --git a/py/objnamedtuple.c b/py/objnamedtuple.c index fec8028b1925..bcf7ac7c3c81 100644 --- a/py/objnamedtuple.c +++ b/py/objnamedtuple.c @@ -31,6 +31,7 @@ #include "py/runtime.h" #include "py/objstr.h" #include "py/objnamedtuple.h" +// CIRCUITPY-CHANGE #include "py/objtype.h" #if MICROPY_PY_COLLECTIONS @@ -68,6 +69,7 @@ STATIC mp_obj_t namedtuple_asdict(mp_obj_t self_in) { MP_DEFINE_CONST_FUN_OBJ_1(namedtuple_asdict_obj, namedtuple_asdict); #endif +// CIRCUITPY-CHANGE: make public void namedtuple_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { (void)kind; mp_obj_namedtuple_t *o = MP_OBJ_TO_PTR(o_in); @@ -76,6 +78,7 @@ void namedtuple_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t ki mp_obj_attrtuple_print_helper(print, fields, &o->tuple); } +// CIRCUITPY-CHANGE: make public void namedtuple_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { if (dest[0] == MP_OBJ_NULL) { // load attribute @@ -95,10 +98,12 @@ void namedtuple_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { } else { // delete/store attribute // provide more detailed error message than we'd get by just returning + // CIRCUITPY-CHANGE: use more specific mp_raise mp_raise_AttributeError(MP_ERROR_TEXT("can't set attribute")); } } +// CIRCUITPY-CHANGE: make public mp_obj_t namedtuple_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { const mp_obj_namedtuple_type_t *type = (const mp_obj_namedtuple_type_t *)type_in; size_t num_fields = type->n_fields; @@ -106,13 +111,15 @@ mp_obj_t namedtuple_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_arg_error_terse_mismatch(); #elif MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_NORMAL + // CIRCUITPY-CHANGE: use more specific mp_raise mp_raise_TypeError_varg( MP_ERROR_TEXT("function takes %d positional arguments but %d were given"), num_fields, n_args + n_kw); #else + // CIRCUITPY-CHANGE: use more specific mp_raise mp_raise_TypeError_varg( MP_ERROR_TEXT("%q() takes %d positional arguments but %d were given"), - type->base.name, num_fields, n_args + n_kw); + ((mp_obj_type_t *)&type->base)->name, num_fields, n_args + n_kw); #endif } @@ -151,7 +158,7 @@ mp_obj_t namedtuple_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t } mp_obj_namedtuple_type_t *mp_obj_new_namedtuple_base(size_t n_fields, mp_obj_t *fields) { - mp_obj_namedtuple_type_t *o = m_new_obj_var0(mp_obj_namedtuple_type_t, qstr, n_fields); + mp_obj_namedtuple_type_t *o = m_new_obj_var0(mp_obj_namedtuple_type_t, fields, qstr, n_fields); o->n_fields = n_fields; for (size_t i = 0; i < n_fields; i++) { o->fields[i] = mp_obj_str_get_qstr(fields[i]); diff --git a/py/objnamedtuple.h b/py/objnamedtuple.h index f0db2dafd44c..b16b54d37517 100644 --- a/py/objnamedtuple.h +++ b/py/objnamedtuple.h @@ -26,12 +26,14 @@ #ifndef MICROPY_INCLUDED_PY_OBJNAMEDTUPLE_H #define MICROPY_INCLUDED_PY_OBJNAMEDTUPLE_H +// CIRCUITPY-CHANGE #include #include "py/objtuple.h" #include "py/runtime.h" #include "py/objstr.h" +// CIRCUITPY-CHANGE #if MICROPY_PY_COLLECTIONS typedef struct _mp_obj_namedtuple_type_t { @@ -46,6 +48,7 @@ typedef struct _mp_obj_namedtuple_t { mp_obj_tuple_t tuple; } mp_obj_namedtuple_t; +// CIRCUITPY-CHANGE: make public void namedtuple_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind); size_t mp_obj_namedtuple_find_field(const mp_obj_namedtuple_type_t *type, qstr name); void namedtuple_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest); diff --git a/py/objproperty.c b/py/objproperty.c index ee8d589b5e50..824f82ea802e 100644 --- a/py/objproperty.c +++ b/py/objproperty.c @@ -27,6 +27,7 @@ #include #include +// CIRCUITPY-CHANGE #include "py/nlr.h" #include "py/objproperty.h" #include "py/runtime.h" @@ -98,6 +99,7 @@ MP_DEFINE_CONST_OBJ_TYPE( locals_dict, &property_locals_dict ); +// CIRCUITPY-CHANGE #if MICROPY_PY_OPTIMIZE_PROPERTY_FLASH_SIZE extern const mp_obj_property_t __property_getter_start, __property_getter_end, __property_getset_start, __property_getset_end; #endif diff --git a/py/objrange.c b/py/objrange.c index bfe95b1f72e6..c5a6d1ff6e82 100644 --- a/py/objrange.c +++ b/py/objrange.c @@ -104,6 +104,7 @@ STATIC mp_obj_t range_make_new(const mp_obj_type_t *type, size_t n_args, size_t if (n_args == 3) { o->step = mp_obj_get_int(args[2]); if (o->step == 0) { + // CIRCUITPY-CHANGE mp_raise_ValueError_varg(MP_ERROR_TEXT("%q step cannot be zero"), MP_QSTR_range); } } diff --git a/py/objset.c b/py/objset.c index 4965eca3b07e..1906163e2d17 100644 --- a/py/objset.c +++ b/py/objset.c @@ -361,6 +361,7 @@ STATIC mp_obj_t set_pop(mp_obj_t self_in) { mp_obj_set_t *self = MP_OBJ_TO_PTR(self_in); mp_obj_t obj = mp_set_remove_first(&self->set); if (obj == MP_OBJ_NULL) { + // CIRCUITPY-CHANGE mp_raise_msg_varg(&mp_type_KeyError, MP_ERROR_TEXT("pop from empty %q"), MP_QSTR_set); } return obj; @@ -446,6 +447,7 @@ STATIC mp_obj_t set_unary_op(mp_unary_op_t op, mp_obj_t self_in) { } MP_FALLTHROUGH #endif + // CIRCUITPY-CHANGE /* FALLTHROUGH */ default: return MP_OBJ_NULL; // op not supported diff --git a/py/objslice.c b/py/objslice.c index 19c69db34ad9..abc1e8754995 100644 --- a/py/objslice.c +++ b/py/objslice.c @@ -55,7 +55,7 @@ STATIC mp_obj_t slice_unary_op(mp_unary_op_t op, mp_obj_t o_in) { #if MICROPY_PY_BUILTINS_SLICE_INDICES STATIC mp_obj_t slice_indices(mp_obj_t self_in, mp_obj_t length_obj) { - mp_int_t length = mp_obj_int_get_checked(length_obj); + mp_int_t length = mp_obj_get_int(length_obj); mp_bound_slice_t bound_indices; mp_obj_slice_indices(self_in, length, &bound_indices); @@ -92,6 +92,7 @@ STATIC void slice_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { } #endif +// CIRCUITPY-CHANGE #if MICROPY_PY_BUILTINS_SLICE_ATTRS STATIC mp_obj_t slice_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { @@ -134,6 +135,7 @@ STATIC MP_DEFINE_CONST_DICT(slice_locals_dict, slice_locals_dict_table); #define SLICE_TYPE_ATTR_OR_LOCALS_DICT #endif +// CIRCUITPY-CHANGE #if MICROPY_PY_BUILTINS_SLICE_INDICES || MICROPY_PY_BUILTINS_SLICE_ATTRS #define SLICE_MAKE_NEW make_new, slice_make_new, #else @@ -170,6 +172,7 @@ void mp_obj_slice_indices(mp_obj_t self_in, mp_int_t length, mp_bound_slice_t *r } else { step = mp_obj_get_int(self->step); if (step == 0) { + // CIRCUITPY-CHANGE mp_raise_ValueError_varg(MP_ERROR_TEXT("%q step cannot be zero"), MP_QSTR_slice); } } diff --git a/py/objstr.c b/py/objstr.c index b624c3598737..5794765e7dac 100644 --- a/py/objstr.c +++ b/py/objstr.c @@ -1102,6 +1102,7 @@ STATIC vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *ar #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE terse_str_format_value_error(); #else + // CIRCUITPY-CHANGE mp_raise_ValueError_varg(MP_ERROR_TEXT("unmatched '%c' in format"), '{'); #endif } @@ -1128,6 +1129,7 @@ STATIC vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *ar } field_name = str_to_int(field_name, field_name_top, &index); if ((uint)index >= n_args - 1) { + // CIRCUITPY-CHANGE mp_raise_IndexError_varg(MP_ERROR_TEXT("%q index out of range"), MP_QSTR_tuple); } arg = args[index + 1]; @@ -1157,6 +1159,7 @@ STATIC vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *ar #endif } if ((uint)*arg_i >= n_args - 1) { + // CIRCUITPY-CHANGE: more specific mp_raise mp_raise_IndexError_varg(MP_ERROR_TEXT("%q index out of range"), MP_QSTR_tuple); } arg = args[(*arg_i) + 1]; @@ -1337,6 +1340,7 @@ STATIC vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *ar #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE terse_str_format_value_error(); #else + // CIRCUITPY-CHANGE: more specific mp_raise mp_raise_ValueError_varg( MP_ERROR_TEXT("unknown format code '%c' for object of type '%q'"), type, mp_obj_get_type_qstr(arg)); @@ -1409,6 +1413,7 @@ STATIC vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *ar #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE terse_str_format_value_error(); #else + // CIRCUITPY-CHANGE: more specific mp_raise mp_raise_ValueError_varg( MP_ERROR_TEXT("unknown format code '%c' for object of type '%q'"), type, mp_obj_get_type_qstr(arg)); @@ -1445,6 +1450,7 @@ STATIC vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *ar #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE terse_str_format_value_error(); #else + // CIRCUITPY-CHANGE: more specific mp_raise mp_raise_ValueError_varg( MP_ERROR_TEXT("unknown format code '%c' for object of type '%q'"), type, mp_obj_get_type_qstr(arg)); @@ -1497,6 +1503,7 @@ STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, size_t n_args, const mp_obj_ // Dictionary value lookup if (*str == '(') { if (dict == MP_OBJ_NULL) { + // CIRCUITPY-CHANGE: clearer message mp_raise_TypeError(MP_ERROR_TEXT("format requires a dict")); } arg_i = 1; // we used up the single dict argument @@ -1578,6 +1585,7 @@ STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, size_t n_args, const mp_obj_ if (arg == MP_OBJ_NULL) { if (arg_i >= n_args) { not_enough_args: + // CIRCUITPY-CHANGE: clearer message mp_raise_TypeError(MP_ERROR_TEXT("not enough arguments for format string")); } arg = args[arg_i++]; @@ -1588,6 +1596,7 @@ STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, size_t n_args, const mp_obj_ size_t slen; const char *s = mp_obj_str_get_data(arg, &slen); if (slen != 1) { + // CIRCUITPY-CHANGE: clearer message mp_raise_TypeError(MP_ERROR_TEXT("%%c requires int or char")); } mp_print_strn(&print, s, 1, flags, ' ', width); @@ -1595,6 +1604,7 @@ STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, size_t n_args, const mp_obj_ char ch = mp_obj_get_int(arg); mp_print_strn(&print, &ch, 1, flags, ' ', width); } else { + // CIRCUITPY-CHANGE: clearer message mp_raise_TypeError(MP_ERROR_TEXT("%%c requires int or char")); } break; @@ -1656,6 +1666,7 @@ STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, size_t n_args, const mp_obj_ #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE terse_str_format_value_error(); #else + // CIRCUITPY-CHANGE: more specific mp_raise mp_raise_ValueError_varg( MP_ERROR_TEXT("unsupported format character '%c' (0x%x) at index %d"), *str, *str, str - start_str); @@ -1666,6 +1677,7 @@ STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, size_t n_args, const mp_obj_ if (dict == MP_OBJ_NULL && arg_i != n_args) { // NOTE: if `dict` exists, then `n_args` is 1 and the dict is always consumed; either // positionally, or as a map of named args, even if none were actually referenced. + // CIRCUITPY-CHANGE: clearer message mp_raise_TypeError(MP_ERROR_TEXT("not all arguments converted during string formatting")); } @@ -2059,6 +2071,7 @@ mp_obj_t mp_obj_bytes_fromhex(mp_obj_t type_in, mp_obj_t data) { STATIC mp_obj_t bytes_hex_as_str(size_t n_args, const mp_obj_t *args) { return mp_obj_bytes_hex(n_args, args, &mp_type_str); } +// CIRCUITPY-CHANGE: make public MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_obj_bytes_hex_as_str_obj, 1, 2, bytes_hex_as_str); STATIC MP_DEFINE_CONST_FUN_OBJ_2(bytes_fromhex_obj, mp_obj_bytes_fromhex); @@ -2092,6 +2105,7 @@ STATIC const mp_rom_map_elem_t array_bytearray_str_bytes_locals_table[] = { { MP_ROM_QSTR(MP_QSTR_extend), MP_ROM_PTR(&mp_obj_array_extend_obj) }, #endif #if MICROPY_PY_BUILTINS_BYTES_HEX + // CIRCUITPY-CHANGE: different name { MP_ROM_QSTR(MP_QSTR_hex), MP_ROM_PTR(&mp_obj_bytes_hex_as_str_obj) }, { MP_ROM_QSTR(MP_QSTR_fromhex), MP_ROM_PTR(&bytes_fromhex_classmethod_obj) }, #endif @@ -2342,7 +2356,7 @@ mp_obj_t mp_obj_new_bytes(const byte *data, size_t len) { return mp_obj_new_str_copy(&mp_type_bytes, data, len); } -// CIRCUITPY-CHANGE +// CIRCUITPY-CHANGE: new function mp_obj_t mp_obj_new_bytes_of_zeros(size_t len) { vstr_t vstr; vstr_init_len(&vstr, len); @@ -2374,7 +2388,9 @@ STATIC NORETURN void bad_implicit_conversion(mp_obj_t self_in) { #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("can't convert to str implicitly")); #else + // CIRCUTTPY-CHANGE const qstr src_name = mp_obj_get_type_qstr(self_in); + // CIRCUTTPY-CHANGE: more specific mp_raise mp_raise_TypeError_varg(MP_ERROR_TEXT("can't convert '%q' object to %q implicitly"), src_name, src_name == MP_QSTR_str ? MP_QSTR_bytes : MP_QSTR_str); #endif diff --git a/py/objstringio.c b/py/objstringio.c index a5167768d78e..d191974a1fc0 100644 --- a/py/objstringio.c +++ b/py/objstringio.c @@ -45,6 +45,7 @@ STATIC void check_stringio_is_open(const mp_obj_stringio_t *o) { #define check_stringio_is_open(o) #endif +// CIRCUITPY-CHANGE: handling subclassing STATIC mp_obj_stringio_t *native_obj(mp_obj_t o_in) { mp_obj_stringio_t *native = mp_obj_cast_to_native_base(o_in, &mp_type_stringio); @@ -58,12 +59,14 @@ STATIC mp_obj_stringio_t *native_obj(mp_obj_t o_in) { STATIC void stringio_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { (void)kind; + // CIRCUITPY-CHANGE mp_obj_stringio_t *self = native_obj(self_in); mp_printf(print, self->base.type == &mp_type_stringio ? "" : "", self); } STATIC mp_uint_t stringio_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errcode) { (void)errcode; + // CIRCUITPY-CHANGE mp_obj_stringio_t *o = native_obj(o_in); check_stringio_is_open(o); if (o->vstr->len <= o->pos) { // read to EOF, or seeked to EOF or beyond @@ -88,6 +91,7 @@ STATIC void stringio_copy_on_write(mp_obj_stringio_t *o) { STATIC mp_uint_t stringio_write(mp_obj_t o_in, const void *buf, mp_uint_t size, int *errcode) { (void)errcode; + // CIRCUITPY-CHANGE mp_obj_stringio_t *o = native_obj(o_in); check_stringio_is_open(o); @@ -122,6 +126,7 @@ STATIC mp_uint_t stringio_write(mp_obj_t o_in, const void *buf, mp_uint_t size, STATIC mp_uint_t stringio_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) { (void)errcode; + // CIRCUITPY-CHANGE mp_obj_stringio_t *o = native_obj(o_in); switch (request) { case MP_STREAM_SEEK: { @@ -174,6 +179,7 @@ STATIC mp_uint_t stringio_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, #define STREAM_TO_CONTENT_TYPE(o) (((o)->base.type == &mp_type_stringio) ? &mp_type_str : &mp_type_bytes) STATIC mp_obj_t stringio_getvalue(mp_obj_t self_in) { + // CIRCUITPY-CHANGE mp_obj_stringio_t *self = native_obj(self_in); check_stringio_is_open(self); // TODO: Try to avoid copying string diff --git a/py/objstrunicode.c b/py/objstrunicode.c index 563bce9fd27f..b1c1185d2d6a 100644 --- a/py/objstrunicode.c +++ b/py/objstrunicode.c @@ -59,6 +59,7 @@ STATIC void uni_print_quoted(const mp_print_t *print, const byte *str_data, uint while (s < top) { unichar ch; ch = utf8_get_char(s); + // CIRCUITPY-CHANGE: print printable Unicode chars const byte *start = s; s = utf8_next_char(s); if (ch == quote_char) { @@ -76,8 +77,10 @@ STATIC void uni_print_quoted(const mp_print_t *print, const byte *str_data, uint // CIRCUITPY-CHANGE: print printable Unicode chars } else if (ch <= 0x1f || (0x7f <= ch && ch <= 0xa0) || ch == 0xad) { mp_printf(print, "\\x%02x", ch); - } else if ((0x2000 <= ch && ch <= 0x200f) || ch == 0x2028 || ch == 0x2029) { + } else if ((0x2000 <= ch && ch <= 0x200f) || ch == 0x2028 || ch == 0x2029 || ch == 0xffff) { mp_printf(print, "\\u%04x", ch); + } else if (ch == 0x1ffff) { + mp_printf(print, "\\U%08x", ch); } else { // Print the full character out. int width = s - start; diff --git a/py/objtuple.c b/py/objtuple.c index cf678f43e1a2..79703d68c83d 100644 --- a/py/objtuple.c +++ b/py/objtuple.c @@ -187,6 +187,7 @@ mp_obj_t mp_obj_tuple_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { if (value == MP_OBJ_SENTINEL) { // load mp_obj_tuple_t *self = MP_OBJ_TO_PTR(self_in); + // CIRCUITPY-CHANGE // when called with a native type (eg namedtuple) using mp_obj_tuple_subscr, get the native self if (MP_OBJ_TYPE_GET_SLOT_OR_NULL(self->base.type, subscr) != &mp_obj_tuple_subscr) { self = MP_OBJ_TO_PTR(mp_obj_cast_to_native_base(self_in, MP_OBJ_FROM_PTR(&mp_type_tuple))); @@ -271,7 +272,7 @@ void mp_obj_tuple_get(mp_obj_t self_in, size_t *len, mp_obj_t **items) { void mp_obj_tuple_del(mp_obj_t self_in) { assert(mp_obj_is_type(self_in, &mp_type_tuple)); mp_obj_tuple_t *self = MP_OBJ_TO_PTR(self_in); - m_del_var(mp_obj_tuple_t, mp_obj_t, self->len, self); + m_del_var(mp_obj_tuple_t, items, mp_obj_t, self->len, self); } /******************************************************************************/ diff --git a/py/objtuple.h b/py/objtuple.h index 825f5868c4fa..e2d010e6c5a3 100644 --- a/py/objtuple.h +++ b/py/objtuple.h @@ -40,6 +40,7 @@ typedef struct _mp_rom_obj_tuple_t { mp_rom_obj_t items[]; } mp_rom_obj_tuple_t; +// CIRCUITPY-CHANGE extern const mp_obj_type_t mp_type_tuple; void mp_obj_tuple_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind); diff --git a/py/objtype.c b/py/objtype.c index b9d83f9c85ce..504806d807d6 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -82,7 +82,7 @@ STATIC int instance_count_native_bases(const mp_obj_type_t *type, const mp_obj_t } } -// CIRCUITPY-CHANGE: differences +// CIRCUITPY-CHANGE: support superclass constructors that take kw args // This wrapper function is allows a subclass of a native type to call the // __init__() method (corresponding to type->make_new) of the native type. STATIC mp_obj_t native_base_init_wrapper(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -650,9 +650,11 @@ STATIC void mp_obj_instance_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *des // be called by the descriptor code down below. But that way // requires overhead for the nested mp_call's and overhead for // the code. + // CIRCUITPY-CHANGE: mp_obj_property_get arg size_t n_proxy; const mp_obj_t *proxy = mp_obj_property_get(member, &n_proxy); if (proxy[0] == mp_const_none) { + // CIRCUITPY-CHANGE: more specific mp_raise mp_raise_AttributeError(MP_ERROR_TEXT("unreadable attribute")); } else { dest[0] = mp_call_function_n_kw(proxy[0], 1, 0, &self_in); @@ -1025,8 +1027,10 @@ STATIC mp_obj_t type_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp if (!MP_OBJ_TYPE_HAS_SLOT(self, make_new)) { #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + // CIRCUITPY-CHANGE: error message change mp_raise_TypeError(MP_ERROR_TEXT("cannot create instance")); #else + // CIRCUITPY-CHANGE: error message change mp_raise_TypeError_varg(MP_ERROR_TEXT("cannot create '%q' instances"), self->name); #endif } @@ -1168,13 +1172,17 @@ mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict) // TODO: Verify with CPy, tested on function type if (!MP_OBJ_TYPE_HAS_SLOT(t, make_new)) { #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + // CIRCUITPY-CHANGE: error message change mp_raise_TypeError(MP_ERROR_TEXT("type is not an acceptable base type")); #else + // CIRCUITPY-CHANGE: error message change mp_raise_TypeError_varg( MP_ERROR_TEXT("type '%q' is not an acceptable base type"), t->name); #endif } #if ENABLE_SPECIAL_ACCESSORS + // CIRCUITPY-CHANGE: https://github.com/adafruit/circuitpython/pull/8493 + // Fix native property setting from subclass // Inherit the special accessors flag. base_flags |= t->flags & MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS; if (mp_obj_is_instance_type(t)) { @@ -1192,7 +1200,7 @@ mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict) // (currently 10, plus 1 for base, plus 1 for base-protocol). // Note: mp_obj_type_t is (2 + 3 + #slots) words, so going from 11 to 12 slots // moves from 4 to 5 gc blocks. - mp_obj_type_t *o = m_new_obj_var0(mp_obj_type_t, void *, 10 + (bases_len ? 1 : 0) + (base_protocol ? 1 : 0)); + mp_obj_type_t *o = m_new_obj_var0(mp_obj_type_t, slots, void *, 10 + (bases_len ? 1 : 0) + (base_protocol ? 1 : 0)); o->base.type = &mp_type_type; o->flags = base_flags; o->name = name; @@ -1288,6 +1296,7 @@ STATIC mp_obj_t super_make_new(const mp_obj_type_t *type_in, size_t n_args, size // 1 argument is not yet implemented mp_arg_check_num(n_args, n_kw, 2, 2, false); if (!mp_obj_is_type(args[0], &mp_type_type)) { + // CIRCUITPY-CHANGE: error message mp_raise_TypeError(MP_ERROR_TEXT("first argument to super() must be type")); } mp_obj_super_t *o = m_new_obj(mp_obj_super_t); diff --git a/py/parse.c b/py/parse.c index a430f8257893..fae55d0e42ac 100644 --- a/py/parse.c +++ b/py/parse.c @@ -653,6 +653,7 @@ STATIC const mp_rom_map_elem_t mp_constants_table[] = { STATIC MP_DEFINE_CONST_MAP(mp_constants_map, mp_constants_table); #endif +// CIRCUITPY-CHANGE: avoid compiler warning #if defined(MICROPY_COMP_CONST_FOLDING_COMPILER_WORKAROUND) && MICROPY_COMP_CONST_FOLDING_COMPILER_WORKAROUND // Some versions of the xtensa-esp32-elf-gcc compiler generate wrong code if this // function is static, so provide a hook for them to work around this problem. @@ -1009,7 +1010,7 @@ STATIC void push_result_rule(parser_t *parser, size_t src_line, uint8_t rule_id, #if MICROPY_COMP_CONST_TUPLE if (build_tuple(parser, src_line, rule_id, num_args)) { - // we built a tuple from this rule so return straightaway + // we built a tuple from this rule so return straight away return; } #endif diff --git a/py/persistentcode.c b/py/persistentcode.c index 3a9772cd1b47..9c78c93b23bb 100644 --- a/py/persistentcode.c +++ b/py/persistentcode.c @@ -40,6 +40,11 @@ #include "py/smallint.h" +// makeqstrdata.py has a fixed list of qstrs at the start that we can assume +// are available with know indices on all MicroPython implementations, and +// avoid needing to duplicate the string data in the .mpy file. This is the +// last one in that list (anything with a qstr less than or equal to this is +// assumed to be in the list). #define QSTR_LAST_STATIC MP_QSTR_zip #if MICROPY_DYNAMIC_COMPILER @@ -459,7 +464,7 @@ void mp_raw_code_load_mem(const byte *buf, size_t len, mp_compiled_module_t *con #if MICROPY_HAS_FILE_READER -void mp_raw_code_load_file(const char *filename, mp_compiled_module_t *context) { +void mp_raw_code_load_file(qstr filename, mp_compiled_module_t *context) { mp_reader_t reader; mp_reader_new_file(&reader, filename); mp_raw_code_load(&reader, context); @@ -598,6 +603,7 @@ STATIC void save_raw_code(mp_print_t *print, const mp_raw_code_t *rc) { void mp_raw_code_save(mp_compiled_module_t *cm, mp_print_t *print) { // header contains: + // CIRCUITPY-CHANGE // byte 'C' (CIRCUITPY) // byte version // byte native arch (and sub-version if native) @@ -646,12 +652,12 @@ STATIC void fd_print_strn(void *env, const char *str, size_t len) { (void)ret; } -void mp_raw_code_save_file(mp_compiled_module_t *cm, const char *filename) { +void mp_raw_code_save_file(mp_compiled_module_t *cm, qstr filename) { MP_THREAD_GIL_EXIT(); - int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644); + int fd = open(qstr_str(filename), O_WRONLY | O_CREAT | O_TRUNC, 0644); MP_THREAD_GIL_ENTER(); if (fd < 0) { - mp_raise_OSError_with_filename(errno, filename); + mp_raise_OSError_with_filename(errno, qstr_str(filename)); } mp_print_t fd_print = {(void *)(intptr_t)fd, fd_print_strn}; mp_raw_code_save(cm, &fd_print); diff --git a/py/persistentcode.h b/py/persistentcode.h index d363f544ad55..45a268d63fb9 100644 --- a/py/persistentcode.h +++ b/py/persistentcode.h @@ -35,7 +35,7 @@ // set) must also match MPY_SUB_VERSION. This allows 3 additional updates to // the native ABI per bytecode revision. #define MPY_VERSION 6 -#define MPY_SUB_VERSION 1 +#define MPY_SUB_VERSION 2 // Macros to encode/decode sub-version to/from the feature byte. This replaces // the bits previously used to encode the flags (map caching and unicode) @@ -113,10 +113,10 @@ enum { void mp_raw_code_load(mp_reader_t *reader, mp_compiled_module_t *ctx); void mp_raw_code_load_mem(const byte *buf, size_t len, mp_compiled_module_t *ctx); -void mp_raw_code_load_file(const char *filename, mp_compiled_module_t *ctx); +void mp_raw_code_load_file(qstr filename, mp_compiled_module_t *ctx); void mp_raw_code_save(mp_compiled_module_t *cm, mp_print_t *print); -void mp_raw_code_save_file(mp_compiled_module_t *cm, const char *filename); +void mp_raw_code_save_file(mp_compiled_module_t *cm, qstr filename); void mp_native_relocate(void *reloc, uint8_t *text, uintptr_t reloc_text); diff --git a/py/py.cmake b/py/py.cmake index 8904b6406ee7..1cbbe08f0136 100644 --- a/py/py.cmake +++ b/py/py.cmake @@ -96,6 +96,7 @@ set(MICROPY_SOURCE_PY ${MICROPY_PY_DIR}/objstr.c ${MICROPY_PY_DIR}/objstringio.c ${MICROPY_PY_DIR}/objstrunicode.c +# CIRCUITPY-CHANGE: add objtraceback.c ${MICROPY_PY_DIR}/objtraceback.c ${MICROPY_PY_DIR}/objtuple.c ${MICROPY_PY_DIR}/objtype.c diff --git a/py/py.mk b/py/py.mk index c710936f98b6..e45ed89b8c63 100644 --- a/py/py.mk +++ b/py/py.mk @@ -88,6 +88,7 @@ $(BUILD)/extmod/ulab/code/%.o: CFLAGS += -Os endif # CIRCUITPY_ULAB_OPTIMIZE_SIZE endif # CIRCUITPY_ULAB +# CIRCUITPY-CHANGE: additional files # py object files PY_CORE_O_BASENAME = $(addprefix py/,\ mpstate.o \ @@ -321,5 +322,6 @@ endif # http://www.emulators.com/docs/nx25_nostradamus.htm #-fno-crossjumping +# CIRCUITPY-CHANGE: include extmod.mk here instead of in port/*/Makefile # Include rules for extmod related code include $(TOP)/extmod/extmod.mk diff --git a/py/pystack.c b/py/pystack.c index 409ca11a7ffe..dcbef9c1d0c0 100644 --- a/py/pystack.c +++ b/py/pystack.c @@ -36,6 +36,7 @@ void mp_pystack_init(void *start, void *end) { MP_STATE_THREAD(pystack_cur) = start; } +// CIRCUITPY-CHANGE: PLACE_IN_ITCM void *PLACE_IN_ITCM(mp_pystack_alloc)(size_t n_bytes) { n_bytes = (n_bytes + (MICROPY_PYSTACK_ALIGN - 1)) & ~(MICROPY_PYSTACK_ALIGN - 1); #if MP_PYSTACK_DEBUG diff --git a/py/qstr.c b/py/qstr.c index c17a0016c64d..41a031d10143 100644 --- a/py/qstr.c +++ b/py/qstr.c @@ -33,11 +33,6 @@ #include "py/gc.h" #include "py/runtime.h" -// CIRCUITPY-CHANGE: changes for TRANSLATION - -// NOTE: we are using linear arrays to store and search for qstr's (unique strings, interned strings) -// ultimately we will replace this with a static hash table of some kind - #if MICROPY_DEBUG_VERBOSE // print debugging info #define DEBUG_printf DEBUG_printf #else // don't print debugging info @@ -76,40 +71,112 @@ size_t qstr_compute_hash(const byte *data, size_t len) { return hash; } +// The first pool is the static qstr table. The contents must remain stable as +// it is part of the .mpy ABI. See the top of py/persistentcode.c and +// static_qstr_list in makeqstrdata.py. This pool is unsorted (although in a +// future .mpy version we could re-order them and make it sorted). It also +// contains additional qstrs that must have IDs <256, see operator_qstr_list +// in makeqstrdata.py. +const qstr_hash_t mp_qstr_const_hashes_static[] = { + #ifndef NO_QSTR +#define QDEF0(id, hash, len, str) hash, +#define QDEF1(id, hash, len, str) +// CIRCUITPY-CHANGE: translations +#define TRANSLATION(id, length, compressed ...) + #include "genhdr/qstrdefs.generated.h" +#undef QDEF0 +#undef QDEF1 +// CIRCUITPY-CHANGE: translations +#undef TRANSLATION + #endif +}; + +const qstr_len_t mp_qstr_const_lengths_static[] = { + #ifndef NO_QSTR +#define QDEF0(id, hash, len, str) len, +#define QDEF1(id, hash, len, str) +// CIRCUITPY-CHANGE: translations +#define TRANSLATION(id, length, compressed ...) + #include "genhdr/qstrdefs.generated.h" +#undef QDEF0 +#undef QDEF1 +// CIRCUITPY-CHANGE: translations +#undef TRANSLATION + #endif +}; + +const qstr_pool_t mp_qstr_const_pool_static = { + NULL, // no previous pool + 0, // no previous pool + false, // is_sorted + MICROPY_ALLOC_QSTR_ENTRIES_INIT, + MP_QSTRnumber_of_static, // corresponds to number of strings in array just below + (qstr_hash_t *)mp_qstr_const_hashes_static, + (qstr_len_t *)mp_qstr_const_lengths_static, + { + #ifndef NO_QSTR +#define QDEF0(id, hash, len, str) str, +#define QDEF1(id, hash, len, str) +// CIRCUITPY-CHANGE: translations +#define TRANSLATION(id, length, compressed ...) + #include "genhdr/qstrdefs.generated.h" +#undef QDEF0 +#undef QDEF1 +// CIRCUITPY-CHANGE: translations +#undef TRANSLATION + #endif + }, +}; + +// The next pool is the remainder of the qstrs defined in the firmware. This +// is sorted. const qstr_hash_t mp_qstr_const_hashes[] = { #ifndef NO_QSTR -#define QDEF(id, hash, len, str) hash, +#define QDEF0(id, hash, len, str) +#define QDEF1(id, hash, len, str) hash, +// CIRCUITPY-CHANGE: translations #define TRANSLATION(id, length, compressed ...) #include "genhdr/qstrdefs.generated.h" +#undef QDEF0 +#undef QDEF1 +// CIRCUITPY-CHANGE: translations #undef TRANSLATION -#undef QDEF #endif }; const qstr_len_t mp_qstr_const_lengths[] = { #ifndef NO_QSTR -#define QDEF(id, hash, len, str) len, +#define QDEF0(id, hash, len, str) +#define QDEF1(id, hash, len, str) len, +// CIRCUITPY-CHANGE: translations #define TRANSLATION(id, length, compressed ...) #include "genhdr/qstrdefs.generated.h" +#undef QDEF0 +#undef QDEF1 +// CIRCUITPY-CHANGE: translations #undef TRANSLATION -#undef QDEF #endif }; const qstr_pool_t mp_qstr_const_pool = { - NULL, // no previous pool - 0, // no previous pool + &mp_qstr_const_pool_static, + MP_QSTRnumber_of_static, + true, // is_sorted MICROPY_ALLOC_QSTR_ENTRIES_INIT, - MP_QSTRnumber_of, // corresponds to number of strings in array just below + MP_QSTRnumber_of - MP_QSTRnumber_of_static, // corresponds to number of strings in array just below (qstr_hash_t *)mp_qstr_const_hashes, (qstr_len_t *)mp_qstr_const_lengths, { #ifndef NO_QSTR -#define QDEF(id, hash, len, str) str, +#define QDEF0(id, hash, len, str) +#define QDEF1(id, hash, len, str) str, +// CIRCUITPY-CHANGE: translations #define TRANSLATION(id, length, compressed ...) #include "genhdr/qstrdefs.generated.h" +#undef QDEF0 +#undef QDEF1 +// CIRCUITPY-CHANGE: translations #undef TRANSLATION -#undef QDEF #endif }, }; @@ -121,7 +188,7 @@ extern const qstr_pool_t MICROPY_QSTR_EXTRA_POOL; #define CONST_POOL mp_qstr_const_pool #endif -// CIRCUITPY-CHANGE +// CIRCUITPY-CHANGE: provide separate reset function void qstr_reset(void) { MP_STATE_VM(last_pool) = (qstr_pool_t *)&CONST_POOL; // we won't modify the const_pool since it has no allocated room left MP_STATE_VM(qstr_last_chunk) = NULL; @@ -135,7 +202,7 @@ void qstr_init(void) { #endif } -STATIC const qstr_pool_t *PLACE_IN_ITCM(find_qstr)(qstr *q) { +STATIC const qstr_pool_t *find_qstr(qstr *q) { // search pool for this qstr // total_prev_len==0 in the final pool, so the loop will always terminate const qstr_pool_t *pool = MP_STATE_VM(last_pool); @@ -193,12 +260,34 @@ STATIC qstr qstr_add(mp_uint_t hash, mp_uint_t len, const char *q_ptr) { } qstr qstr_find_strn(const char *str, size_t str_len) { + if (str_len == 0) { + // strncmp behaviour is undefined for str==NULL. + return MP_QSTR_; + } + // work out hash of str size_t str_hash = qstr_compute_hash((const byte *)str, str_len); // search pools for the data for (const qstr_pool_t *pool = MP_STATE_VM(last_pool); pool != NULL; pool = pool->prev) { - for (mp_uint_t at = 0, top = pool->len; at < top; at++) { + size_t low = 0; + size_t high = pool->len - 1; + + // binary search inside the pool + if (pool->is_sorted) { + while (high - low > 1) { + size_t mid = (low + high) / 2; + int cmp = strncmp(str, pool->qstrs[mid], str_len); + if (cmp <= 0) { + high = mid; + } else { + low = mid; + } + } + } + + // sequential search for the remaining strings + for (mp_uint_t at = low; at < high + 1; at++) { if (pool->hashes[at] == str_hash && pool->lengths[at] == str_len && memcmp(pool->qstrs[at], str, str_len) == 0) { return pool->total_prev_len + at; @@ -207,7 +296,7 @@ qstr qstr_find_strn(const char *str, size_t str_len) { } // not found; return null qstr - return 0; + return MP_QSTRnull; } qstr qstr_from_str(const char *str) { @@ -223,7 +312,7 @@ qstr qstr_from_strn(const char *str, size_t len) { // check that len is not too big if (len >= (1 << (8 * MICROPY_QSTR_BYTES_IN_LEN))) { QSTR_EXIT(); - mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Name too long")); + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("name too long")); } // compute number of bytes needed to intern this string @@ -370,7 +459,7 @@ STATIC const byte *find_uncompressed_string(uint8_t n) { // Given a compressed string in src, decompresses it into dst. // dst must be large enough (use MP_MAX_UNCOMPRESSED_TEXT_LEN+1). -void mp_decompress_rom_string(byte *dst, mp_rom_error_text_t src_chr) { +void mp_decompress_rom_string(byte *dst, const mp_rom_error_text_t src_chr) { // Skip past the 0xff marker. const byte *src = (byte *)src_chr + 1; // Need to add spaces around compressed words, except for the first (i.e. transition from 1<->2). diff --git a/py/qstr.h b/py/qstr.h index c031fb919dab..426726fe556b 100644 --- a/py/qstr.h +++ b/py/qstr.h @@ -38,11 +38,24 @@ // first entry in enum will be MP_QSTRnull=0, which indicates invalid/no qstr enum { #ifndef NO_QSTR -#define QDEF(id, hash, len, str) id, +#define QDEF0(id, hash, len, str) id, +#define QDEF1(id, hash, len, str) // CIRCUITPY-CHANGE #define TRANSLATION(english_id, number) #include "genhdr/qstrdefs.generated.h" -#undef QDEF +#undef QDEF0 +#undef QDEF1 + #endif + MP_QSTRnumber_of_static, + MP_QSTRstart_of_main = MP_QSTRnumber_of_static - 1, // unused but shifts the enum counter back one + + #ifndef NO_QSTR +#define QDEF0(id, hash, len, str) +#define QDEF1(id, hash, len, str) id, +#define TRANSLATION(english_id, number) + #include "genhdr/qstrdefs.generated.h" +#undef QDEF0 +#undef QDEF1 #undef TRANSLATION #endif MP_QSTRnumber_of, // no underscore so it can't clash with any of the above @@ -69,7 +82,8 @@ typedef uint16_t qstr_len_t; typedef struct _qstr_pool_t { const struct _qstr_pool_t *prev; - size_t total_prev_len; + size_t total_prev_len : (8 * sizeof(size_t) - 1); + size_t is_sorted : 1; size_t alloc; size_t len; qstr_hash_t *hashes; @@ -79,6 +93,7 @@ typedef struct _qstr_pool_t { #define QSTR_TOTAL() (MP_STATE_VM(last_pool)->total_prev_len + MP_STATE_VM(last_pool)->len) +// CIRCUITPY-CHANGE: reset() added void qstr_reset(void); void qstr_init(void); @@ -97,7 +112,7 @@ void qstr_pool_info(size_t *n_pool, size_t *n_qstr, size_t *n_str_data_bytes, si void qstr_dump_data(void); #if MICROPY_ROM_TEXT_COMPRESSION -void mp_decompress_rom_string(byte *dst, mp_rom_error_text_t src); +void mp_decompress_rom_string(byte *dst, const mp_rom_error_text_t src); #define MP_IS_COMPRESSED_ROM_STRING(s) (*(byte *)(s) == 0xff) #endif diff --git a/py/reader.c b/py/reader.c index 57b12b3b655c..d2af62cfb344 100644 --- a/py/reader.c +++ b/py/reader.c @@ -134,12 +134,12 @@ void mp_reader_new_file_from_fd(mp_reader_t *reader, int fd, bool close_fd) { #if !MICROPY_VFS_POSIX // If MICROPY_VFS_POSIX is defined then this function is provided by the VFS layer -void mp_reader_new_file(mp_reader_t *reader, const char *filename) { +void mp_reader_new_file(mp_reader_t *reader, qstr filename) { MP_THREAD_GIL_EXIT(); - int fd = open(filename, O_RDONLY, 0644); + int fd = open(qstr_str(filename), O_RDONLY, 0644); MP_THREAD_GIL_ENTER(); if (fd < 0) { - mp_raise_OSError_with_filename(errno, filename); + mp_raise_OSError_with_filename(errno, qstr_str(filename)); } mp_reader_new_file_from_fd(reader, fd, true); } diff --git a/py/reader.h b/py/reader.h index 8511c72ce503..5cb1e67966c6 100644 --- a/py/reader.h +++ b/py/reader.h @@ -40,7 +40,7 @@ typedef struct _mp_reader_t { } mp_reader_t; void mp_reader_new_mem(mp_reader_t *reader, const byte *buf, size_t len, size_t free_len); -void mp_reader_new_file(mp_reader_t *reader, const char *filename); +void mp_reader_new_file(mp_reader_t *reader, qstr filename); void mp_reader_new_file_from_fd(mp_reader_t *reader, int fd, bool close_fd); #endif // MICROPY_INCLUDED_PY_READER_H diff --git a/py/ringbuf.c b/py/ringbuf.c index 4f90c9e317a1..b81f671fdd9e 100644 --- a/py/ringbuf.c +++ b/py/ringbuf.c @@ -6,7 +6,8 @@ // // SPDX-License-Identifier: MIT -// CIRCUITPY-CHANGE: thoroughly reworked +// CIRCUITPY-CHANGE: API and implementation thoroughly reworked +// No attempt to have atomic operations. Add guards if atomicity required. #include "ringbuf.h" diff --git a/py/ringbuf.h b/py/ringbuf.h index cef6a1b1fcd3..52f50cd9c0bd 100644 --- a/py/ringbuf.h +++ b/py/ringbuf.h @@ -12,7 +12,7 @@ #include #include -// CIRCUITPY-CHANGE: thoroughly reworked +// CIRCUITPY-CHANGE: API and implementation thoroughly reworked typedef struct _ringbuf_t { uint8_t *buf; diff --git a/py/runtime.c b/py/runtime.c index 701af3f1a2e9..24caceaaac8b 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -33,6 +33,7 @@ #include "py/parsenum.h" #include "py/compile.h" +// CIRCUITPY-CHANGE: added #include "py/mperrno.h" #include "py/objstr.h" #include "py/objtuple.h" @@ -41,12 +42,13 @@ #include "py/objmodule.h" #include "py/objgenerator.h" #include "py/smallint.h" +#include "py/stream.h" #include "py/runtime.h" #include "py/builtin.h" #include "py/stackctrl.h" -#include "py/stream.h" #include "py/gc.h" +// CIRCUITPY-CHANGE #if CIRCUITPY_WARNINGS #include "shared-module/warnings/__init__.h" #endif @@ -245,6 +247,7 @@ mp_obj_t MICROPY_WRAP_MP_LOAD_GLOBAL(mp_load_global)(qstr qst) { #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_msg(&mp_type_NameError, MP_ERROR_TEXT("name not defined")); #else + // CIRCUITPY-CHANGE: slight message change mp_raise_msg_varg(&mp_type_NameError, MP_ERROR_TEXT("name '%q' is not defined"), qst); #endif } @@ -268,6 +271,7 @@ mp_obj_t __attribute__((noinline)) mp_load_build_class(void) { return MP_OBJ_FROM_PTR(&mp_builtin___build_class___obj); } +// CIRCUITPY-CHANGE: PLACE_IN_ITCM void PLACE_IN_ITCM(mp_store_name)(qstr qst, mp_obj_t obj) { DEBUG_OP_printf("store name %s <- %p\n", qstr_str(qst), obj); mp_obj_dict_store(MP_OBJ_FROM_PTR(mp_locals_get()), MP_OBJ_NEW_QSTR(qst), obj); @@ -279,6 +283,7 @@ void mp_delete_name(qstr qst) { mp_obj_dict_delete(MP_OBJ_FROM_PTR(mp_locals_get()), MP_OBJ_NEW_QSTR(qst)); } +// CIRCUITPY-CHANGE: PLACE_IN_ITCM void PLACE_IN_ITCM(mp_store_global)(qstr qst, mp_obj_t obj) { DEBUG_OP_printf("store global %s <- %p\n", qstr_str(qst), obj); mp_obj_dict_store(MP_OBJ_FROM_PTR(mp_globals_get()), MP_OBJ_NEW_QSTR(qst), obj); @@ -686,12 +691,14 @@ mp_obj_t MICROPY_WRAP_MP_BINARY_OP(mp_binary_op)(mp_binary_op_t op, mp_obj_t lhs #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("unsupported type for operator")); #else + // CIRCUITPY-CHANGE: use new raise function mp_raise_TypeError_varg( MP_ERROR_TEXT("unsupported types for %q: '%q', '%q'"), mp_binary_op_method_name[op], mp_obj_get_type_qstr(lhs), mp_obj_get_type_qstr(rhs)); #endif zero_division: + // CIRCUITPY-CHANGE: use new raise function mp_raise_ZeroDivisionError(); } @@ -728,6 +735,7 @@ mp_obj_t mp_call_function_n_kw(mp_obj_t fun_in, size_t n_args, size_t n_kw, cons #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("object not callable")); #else + // CIRCUITPY-CHANGE: use new raise function and different message mp_raise_TypeError_varg(MP_ERROR_TEXT("'%q' object is not callable"), mp_obj_get_type_qstr(fun_in)); #endif } @@ -744,6 +752,7 @@ mp_obj_t mp_call_method_n_kw(size_t n_args, size_t n_kw, const mp_obj_t *args) { #if !MICROPY_STACKLESS STATIC #endif +// CIRCUITPY-CHANGE: PLACE_IN_ITCM void PLACE_IN_ITCM(mp_call_prepare_args_n_kw_var)(bool have_self, size_t n_args_n_kw, const mp_obj_t *args, mp_call_args_t *out_args) { mp_obj_t fun = *args++; mp_obj_t self = MP_OBJ_NULL; @@ -989,6 +998,7 @@ void __attribute__((noinline, )) mp_unpack_sequence(mp_obj_t seq_in, size_t num, #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_ValueError(MP_ERROR_TEXT("wrong number of values to unpack")); #else + // CIRCUITPY-CHANGE: use new raise function mp_raise_ValueError_varg(MP_ERROR_TEXT("need more than %d values to unpack"), (int)seq_len); #endif @@ -996,6 +1006,7 @@ void __attribute__((noinline, )) mp_unpack_sequence(mp_obj_t seq_in, size_t num, #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_ValueError(MP_ERROR_TEXT("wrong number of values to unpack")); #else + // CIRCUITPY-CHANGE: use new raise function mp_raise_ValueError_varg(MP_ERROR_TEXT("too many values to unpack (expected %d)"), (int)num); #endif @@ -1059,6 +1070,7 @@ void __attribute__((noinline)) mp_unpack_ex(mp_obj_t seq_in, size_t num_in, mp_o #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_ValueError(MP_ERROR_TEXT("wrong number of values to unpack")); #else + // CIRCUITPY-CHANGE: use new raise function mp_raise_ValueError_varg(MP_ERROR_TEXT("need more than %d values to unpack"), (int)seq_len); #endif @@ -1095,6 +1107,7 @@ STATIC mp_obj_t checked_fun_call(mp_obj_t self_in, size_t n_args, size_t n_kw, c if (n_args > 0) { const mp_obj_type_t *arg0_type = mp_obj_get_type(args[0]); if (arg0_type != self->type) { + // CIRCUITPY-CHANGE: clearer error message mp_raise_TypeError_varg(MP_ERROR_TEXT("%q must be of type %q, not %q"), MP_QSTR_self, self->type->name, arg0_type->name); } } @@ -1264,6 +1277,7 @@ void mp_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest) { if (dest[0] == MP_OBJ_NULL) { // no attribute/method called attr #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + // CIRCUITPY-CHANGE: use new raise function mp_raise_AttributeError(MP_ERROR_TEXT("no such attribute")); #else // following CPython, we give a more detailed error message for type objects @@ -1340,6 +1354,7 @@ void mp_store_attr(mp_obj_t base, qstr attr, mp_obj_t value) { #endif } #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + // CIRCUITPY-CHANGE: use new raise function mp_raise_AttributeError(MP_ERROR_TEXT("no such attribute")); #else mp_raise_msg_varg(&mp_type_AttributeError, @@ -1394,6 +1409,7 @@ mp_obj_t mp_getiter(mp_obj_t o_in, mp_obj_iter_buf_t *iter_buf) { #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("object not iterable")); #else + // CIRCUITPY-CHANGE: raise function mp_raise_TypeError_varg( MP_ERROR_TEXT("'%q' object is not iterable"), mp_obj_get_type_qstr(o_in)); #endif @@ -1402,8 +1418,6 @@ mp_obj_t mp_getiter(mp_obj_t o_in, mp_obj_iter_buf_t *iter_buf) { STATIC mp_fun_1_t type_get_iternext(const mp_obj_type_t *type) { if ((type->flags & MP_TYPE_FLAG_ITER_IS_STREAM) == MP_TYPE_FLAG_ITER_IS_STREAM) { - // CIRCUITPY-CHANGE: unneeded declaration - // mp_obj_t mp_stream_unbuffered_iter(mp_obj_t self); return mp_stream_unbuffered_iter; } else if (type->flags & MP_TYPE_FLAG_ITER_IS_ITERNEXT) { return (mp_fun_1_t)MP_OBJ_TYPE_GET_SLOT(type, iter); @@ -1432,6 +1446,7 @@ mp_obj_t mp_iternext_allow_raise(mp_obj_t o_in) { #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("object not an iterator")); #else + // CIRCUITPY-CHANGE: raise function mp_raise_TypeError_varg(MP_ERROR_TEXT("'%q' object is not an iterator"), mp_obj_get_type_qstr(o_in)); #endif @@ -1469,6 +1484,7 @@ mp_obj_t mp_iternext(mp_obj_t o_in) { #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("object not an iterator")); #else + // CIRCUITPY-CHANGE: raise function mp_raise_TypeError_varg(MP_ERROR_TEXT("'%q' object is not an iterator"), mp_obj_get_type_qstr(o_in)); #endif @@ -1480,7 +1496,7 @@ mp_vm_return_kind_t mp_resume(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t th assert((send_value != MP_OBJ_NULL) ^ (throw_value != MP_OBJ_NULL)); const mp_obj_type_t *type = mp_obj_get_type(self_in); - // CIRCUITPY-CHANGE: distinguishes generators and coroutines. + // CIRCUITPY-CHANGE: CircuitPython distinguishes generators and coroutines. if (type == &mp_type_gen_instance #if MICROPY_PY_ASYNC_AWAIT || type == &mp_type_coro_instance @@ -1658,6 +1674,7 @@ mp_obj_t __attribute__((noinline, )) mp_import_from(mp_obj_t module, qstr name) void mp_import_all(mp_obj_t module) { DEBUG_printf("import all %p\n", module); + // CIRCUITPY-CHANGE: displayio name changes; remove in 10.0 #if CIRCUITPY_DISPLAYIO && CIRCUITPY_WARNINGS if (module == &displayio_module) { #if CIRCUITPY_BUSDISPLAY @@ -1730,7 +1747,7 @@ mp_obj_t mp_parse_compile_execute(mp_lexer_t *lex, mp_parse_input_kind_t parse_i #endif // MICROPY_ENABLE_COMPILER -// CIRCUITPY-CHANGE: MP_COLD are CIRCUITPY +// CIRCUITPY-CHANGE: MP_COLD NORETURN MP_COLD void m_malloc_fail(size_t num_bytes) { DEBUG_printf("memory allocation failed, allocating %u bytes\n", (uint)num_bytes); #if MICROPY_ENABLE_GC @@ -1744,24 +1761,29 @@ NORETURN MP_COLD void m_malloc_fail(size_t num_bytes) { #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_NONE +// CIRCUITPY-CHANGE: MP_COLD NORETURN MP_COLD void mp_raise_type(const mp_obj_type_t *exc_type) { nlr_raise(mp_obj_new_exception(exc_type)); } +// CIRCUITPY-CHANGE: MP_COLD NORETURN MP_COLD void mp_raise_ValueError_no_msg(void) { mp_raise_type(&mp_type_ValueError); } +// CIRCUITPY-CHANGE: MP_COLD NORETURN MP_COLD void mp_raise_TypeError_no_msg(void) { mp_raise_type(&mp_type_TypeError); } +// CIRCUITPY-CHANGE: MP_COLD NORETURN MP_COLD void mp_raise_NotImplementedError_no_msg(void) { mp_raise_type(&mp_type_NotImplementedError); } #else +// CIRCUITPY-CHANGE: MP_COLD NORETURN MP_COLD void mp_raise_msg(const mp_obj_type_t *exc_type, mp_rom_error_text_t msg) { if (msg == NULL) { nlr_raise(mp_obj_new_exception(exc_type)); @@ -1770,11 +1792,13 @@ NORETURN MP_COLD void mp_raise_msg(const mp_obj_type_t *exc_type, mp_rom_error_t } } +// CIRCUITPY-CHANGE: new function for use below. NORETURN MP_COLD void mp_raise_msg_vlist(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, va_list argptr) { mp_obj_t exception = mp_obj_new_exception_msg_vlist(exc_type, fmt, argptr); nlr_raise(exception); } +// CIRCUITPY-CHANGE: MP_COLD and use mp_raise_msg_vlist() NORETURN MP_COLD void mp_raise_msg_varg(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, ...) { va_list argptr; va_start(argptr, fmt); @@ -1782,6 +1806,10 @@ NORETURN MP_COLD void mp_raise_msg_varg(const mp_obj_type_t *exc_type, mp_rom_er va_end(argptr); } +NORETURN MP_COLD void mp_raise_ValueError(mp_rom_error_text_t msg) { + mp_raise_msg(&mp_type_ValueError, msg); +} + NORETURN MP_COLD void mp_raise_msg_str(const mp_obj_type_t *exc_type, const char *msg) { if (msg == NULL) { nlr_raise(mp_obj_new_exception(exc_type)); @@ -1790,14 +1818,17 @@ NORETURN MP_COLD void mp_raise_msg_str(const mp_obj_type_t *exc_type, const char } } +// CIRCUITPY-CHANGE: added NORETURN MP_COLD void mp_raise_AttributeError(mp_rom_error_text_t msg) { mp_raise_msg(&mp_type_AttributeError, msg); } +// CIRCUITPY-CHANGE: added NORETURN MP_COLD void mp_raise_RuntimeError(mp_rom_error_text_t msg) { mp_raise_msg(&mp_type_RuntimeError, msg); } +// CIRCUITPY-CHANGE: added NORETURN MP_COLD void mp_raise_RuntimeError_varg(mp_rom_error_text_t fmt, ...) { va_list argptr; va_start(argptr, fmt); @@ -1805,14 +1836,17 @@ NORETURN MP_COLD void mp_raise_RuntimeError_varg(mp_rom_error_text_t fmt, ...) { va_end(argptr); } +// CIRCUITPY-CHANGE: added NORETURN MP_COLD void mp_raise_ImportError(mp_rom_error_text_t msg) { mp_raise_msg(&mp_type_ImportError, msg); } +// CIRCUITPY-CHANGE: added NORETURN MP_COLD void mp_raise_IndexError(mp_rom_error_text_t msg) { mp_raise_msg(&mp_type_IndexError, msg); } +// CIRCUITPY-CHANGE: added NORETURN MP_COLD void mp_raise_IndexError_varg(mp_rom_error_text_t fmt, ...) { va_list argptr; va_start(argptr, fmt); @@ -1820,10 +1854,7 @@ NORETURN MP_COLD void mp_raise_IndexError_varg(mp_rom_error_text_t fmt, ...) { va_end(argptr); } -NORETURN MP_COLD void mp_raise_ValueError(mp_rom_error_text_t msg) { - mp_raise_msg(&mp_type_ValueError, msg); -} - +// CIRCUITPY-CHANGE: added NORETURN MP_COLD void mp_raise_ValueError_varg(mp_rom_error_text_t fmt, ...) { va_list argptr; va_start(argptr, fmt); @@ -1831,10 +1862,12 @@ NORETURN MP_COLD void mp_raise_ValueError_varg(mp_rom_error_text_t fmt, ...) { va_end(argptr); } +// CIRCUITPY-CHANGE: MP_COLD NORETURN MP_COLD void mp_raise_TypeError(mp_rom_error_text_t msg) { mp_raise_msg(&mp_type_TypeError, msg); } +// CIRCUITPY-CHANGE: added NORETURN MP_COLD void mp_raise_TypeError_varg(mp_rom_error_text_t fmt, ...) { va_list argptr; va_start(argptr, fmt); @@ -1842,10 +1875,12 @@ NORETURN MP_COLD void mp_raise_TypeError_varg(mp_rom_error_text_t fmt, ...) { va_end(argptr); } +// CIRCUITPY-CHANGE: added NORETURN MP_COLD void mp_raise_OSError_msg(mp_rom_error_text_t msg) { mp_raise_msg(&mp_type_OSError, msg); } +// CIRCUITPY-CHANGE: added NORETURN MP_COLD void mp_raise_OSError_errno_str(int errno_, mp_obj_t str) { mp_obj_t args[2] = { MP_OBJ_NEW_SMALL_INT(errno_), @@ -1854,6 +1889,7 @@ NORETURN MP_COLD void mp_raise_OSError_errno_str(int errno_, mp_obj_t str) { nlr_raise(mp_obj_new_exception_args(&mp_type_OSError, 2, args)); } +// CIRCUITPY-CHANGE: added NORETURN MP_COLD void mp_raise_OSError_msg_varg(mp_rom_error_text_t fmt, ...) { va_list argptr; va_start(argptr, fmt); @@ -1861,10 +1897,12 @@ NORETURN MP_COLD void mp_raise_OSError_msg_varg(mp_rom_error_text_t fmt, ...) { va_end(argptr); } +// CIRCUITPY-CHANGE: added NORETURN MP_COLD void mp_raise_ConnectionError(mp_rom_error_text_t msg) { mp_raise_msg(&mp_type_ConnectionError, msg); } +// CIRCUITPY-CHANGE: added NORETURN MP_COLD void mp_raise_BrokenPipeError(void) { mp_raise_type_arg(&mp_type_BrokenPipeError, MP_OBJ_NEW_SMALL_INT(MP_EPIPE)); } @@ -1873,6 +1911,7 @@ NORETURN MP_COLD void mp_raise_NotImplementedError(mp_rom_error_text_t msg) { mp_raise_msg(&mp_type_NotImplementedError, msg); } +// CIRCUITPY-CHANGE: added NORETURN MP_COLD void mp_raise_NotImplementedError_varg(mp_rom_error_text_t fmt, ...) { va_list argptr; va_start(argptr, fmt); @@ -1880,7 +1919,7 @@ NORETURN MP_COLD void mp_raise_NotImplementedError_varg(mp_rom_error_text_t fmt, va_end(argptr); } - +// CIRCUITPY-CHANGE: added NORETURN MP_COLD void mp_raise_OverflowError_varg(mp_rom_error_text_t fmt, ...) { va_list argptr; va_start(argptr, fmt); @@ -1888,11 +1927,12 @@ NORETURN MP_COLD void mp_raise_OverflowError_varg(mp_rom_error_text_t fmt, ...) va_end(argptr); } +// CIRCUITPY-CHANGE: MP_COLD NORETURN MP_COLD void mp_raise_type_arg(const mp_obj_type_t *exc_type, mp_obj_t arg) { nlr_raise(mp_obj_new_exception_arg1(exc_type, arg)); } -// Leave this as not COLD because it is used by iterators in normal execution. +// CIRCUITPY-CHANGE: MP_COLD NORETURN void mp_raise_StopIteration(mp_obj_t arg) { if (arg == MP_OBJ_NULL) { mp_raise_type(&mp_type_StopIteration); @@ -1911,10 +1951,12 @@ NORETURN void mp_raise_TypeError_int_conversion(mp_const_obj_t arg) { #endif } +// CIRCUITPY-CHANGE: MP_COLD NORETURN MP_COLD void mp_raise_OSError(int errno_) { mp_raise_type_arg(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(errno_)); } +// CIRCUITPY-CHANGE: MP_COLD NORETURN MP_COLD void mp_raise_OSError_with_filename(int errno_, const char *filename) { vstr_t vstr; vstr_init(&vstr, 32); @@ -1924,10 +1966,12 @@ NORETURN MP_COLD void mp_raise_OSError_with_filename(int errno_, const char *fil nlr_raise(mp_obj_exception_make_new(&mp_type_OSError, 2, 0, args)); } +// CIRCUITPY-CHANGE: added NORETURN MP_COLD void mp_raise_ZeroDivisionError(void) { mp_raise_msg(&mp_type_ZeroDivisionError, MP_ERROR_TEXT("division by zero")); } #if MICROPY_STACK_CHECK || MICROPY_ENABLE_PYSTACK +// CIRCUITPY-CHANGE: MP_COLD NORETURN MP_COLD void mp_raise_recursion_depth(void) { mp_raise_RuntimeError(MP_ERROR_TEXT("maximum recursion depth exceeded")); } diff --git a/py/runtime.h b/py/runtime.h index c197b3d2cdd3..379dd65c81e3 100644 --- a/py/runtime.h +++ b/py/runtime.h @@ -115,6 +115,23 @@ bool mp_sched_schedule(mp_obj_t function, mp_obj_t arg); bool mp_sched_schedule_node(mp_sched_node_t *node, mp_sched_callback_t callback); #endif +// Handles any pending MicroPython events without waiting for an interrupt or event. +void mp_event_handle_nowait(void); + +// Handles any pending MicroPython events and then suspends execution until the +// next interrupt or event. +// +// Note: on "tickless" ports this can suspend execution for a long time, +// don't call unless you know an interrupt is coming to continue execution. +// On "ticked" ports it may return early due to the tick interrupt. +void mp_event_wait_indefinite(void); + +// Handle any pending MicroPython events and then suspends execution until the +// next interrupt or event, or until timeout_ms milliseconds have elapsed. +// +// On "ticked" ports it may return early due to the tick interrupt. +void mp_event_wait_ms(mp_uint_t timeout_ms); + // extra printing method specifically for mp_obj_t's which are integral type int mp_print_mp_int(const mp_print_t *print, mp_obj_t x, int base, int base_char, int flags, char fill, int width, int prec); @@ -249,6 +266,7 @@ NORETURN void mp_raise_TypeError(mp_rom_error_text_t msg); NORETURN void mp_raise_NotImplementedError(mp_rom_error_text_t msg); #endif +// CIRCUITPY-CHANGE: new mp_raise routines NORETURN void mp_raise_type_arg(const mp_obj_type_t *exc_type, mp_obj_t arg); NORETURN void mp_raise_msg(const mp_obj_type_t *exc_type, mp_rom_error_text_t msg); NORETURN void mp_raise_msg_varg(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt diff --git a/py/runtime0.h b/py/runtime0.h index abb8fd03d7bb..87a7c6fd1579 100644 --- a/py/runtime0.h +++ b/py/runtime0.h @@ -25,6 +25,8 @@ */ #ifndef MICROPY_INCLUDED_PY_RUNTIME0_H #define MICROPY_INCLUDED_PY_RUNTIME0_H + +// CIRCUITPY-CHANGE #include "mpconfig.h" // The first four must fit in 8 bits, see emitbc.c @@ -35,7 +37,7 @@ #define MP_SCOPE_FLAG_VARKEYWORDS (0x02) #define MP_SCOPE_FLAG_VARARGS (0x04) #define MP_SCOPE_FLAG_DEFKWARGS (0x08) -// CIRCUITPY-CHANGE +// CIRCUITPY-CHANGE: FLAG_ASYNC #define MP_SCOPE_FLAG_ASYNC (0x10) #define MP_SCOPE_FLAG_REFGLOBALS (0x20) // used only if native emitter enabled #define MP_SCOPE_FLAG_HASCONSTS (0x40) // used only if native emitter enabled diff --git a/py/runtime_utils.c b/py/runtime_utils.c index fd3f07111384..98252c312204 100644 --- a/py/runtime_utils.c +++ b/py/runtime_utils.c @@ -25,9 +25,11 @@ * THE SOFTWARE. */ +// CIRCUITPY-CHANGE #include "py/mpconfig.h" #include "py/runtime.h" +// CIRCUITPY-CHANGE: no inline MP_NOINLINE mp_obj_t mp_call_function_1_protected(mp_obj_t fun, mp_obj_t arg) { nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { diff --git a/py/scheduler.c b/py/scheduler.c index d80dc9d9273e..cb5f1043cc4c 100644 --- a/py/scheduler.c +++ b/py/scheduler.c @@ -26,6 +26,7 @@ #include +#include "py/mphal.h" #include "py/runtime.h" // Schedules an exception on the main thread (for exceptions "thrown" by async @@ -46,7 +47,7 @@ void MICROPY_WRAP_MP_SCHED_EXCEPTION(mp_sched_exception)(mp_obj_t exc) { #if MICROPY_KBD_EXCEPTION // This function may be called asynchronously at any time so only do the bare minimum. void MICROPY_WRAP_MP_SCHED_KEYBOARD_INTERRUPT(mp_sched_keyboard_interrupt)(void) { - // CIRCUITPY-CHANGE + // CIRCUITPY-CHANGE: traceback differences MP_STATE_VM(mp_kbd_exception).traceback = (mp_obj_traceback_t *)&mp_const_empty_traceback_obj; mp_sched_exception(MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception))); } @@ -241,3 +242,47 @@ void mp_handle_pending(bool raise_exc) { } #endif } + +// Handles any pending MicroPython events without waiting for an interrupt or event. +void mp_event_handle_nowait(void) { + #if defined(MICROPY_EVENT_POLL_HOOK_FAST) && !MICROPY_PREVIEW_VERSION_2 + // For ports still using the old macros. + MICROPY_EVENT_POLL_HOOK_FAST + #else + // Process any port layer (non-blocking) events. + MICROPY_INTERNAL_EVENT_HOOK; + mp_handle_pending(true); + #endif +} + +// Handles any pending MicroPython events and then suspends execution until the +// next interrupt or event. +void mp_event_wait_indefinite(void) { + #if defined(MICROPY_EVENT_POLL_HOOK) && !MICROPY_PREVIEW_VERSION_2 + // For ports still using the old macros. + MICROPY_EVENT_POLL_HOOK + #else + mp_event_handle_nowait(); + + // CIRCUITPY-CHANGE: don't starve CircuitPython background tasks + RUN_BACKGROUND_TASKS; + + MICROPY_INTERNAL_WFE(-1); + #endif +} + +// Handle any pending MicroPython events and then suspends execution until the +// next interrupt or event, or until timeout_ms milliseconds have elapsed. +void mp_event_wait_ms(mp_uint_t timeout_ms) { + #if defined(MICROPY_EVENT_POLL_HOOK) && !MICROPY_PREVIEW_VERSION_2 + // For ports still using the old macros. + MICROPY_EVENT_POLL_HOOK + #else + mp_event_handle_nowait(); + + // CIRCUITPY-CHANGE: don't starve CircuitPython background tasks + RUN_BACKGROUND_TASKS; + + MICROPY_INTERNAL_WFE(timeout_ms); + #endif +} diff --git a/py/sequence.c b/py/sequence.c index 2d42e1c6c454..cc89d1b0b05a 100644 --- a/py/sequence.c +++ b/py/sequence.c @@ -33,6 +33,7 @@ #define SWAP(type, var1, var2) { type t = var2; var2 = var1; var1 = t; } +// CIRCUITPY-CHANGE: detect sequence overflow #if __GNUC__ < 5 // n.b. does not actually detect overflow! #define __builtin_mul_overflow(a, b, x) (*(x) = (a) * (b), false) diff --git a/py/stackctrl.c b/py/stackctrl.c index 14e60380dd00..f192fad0534d 100644 --- a/py/stackctrl.c +++ b/py/stackctrl.c @@ -45,6 +45,7 @@ void mp_stack_set_top(void *top) { MP_STATE_THREAD(stack_top) = top; } +// CIRCUITPY-CHANGE: PLACE_IN_ITCM mp_uint_t PLACE_IN_ITCM(mp_stack_usage)(void) { // Assumes descending stack // CIRCUITPY-CHANGE: Force routine to not be inlined. Better guarantee than MP_NOINLINE for -flto. @@ -59,6 +60,7 @@ void mp_stack_set_limit(mp_uint_t limit) { MP_STATE_THREAD(stack_limit) = limit; } +// CIRCUITPY-CHANGE: PLACE_IN_ITCM void PLACE_IN_ITCM(mp_stack_check)(void) { if (mp_stack_usage() >= MP_STATE_THREAD(stack_limit)) { mp_raise_recursion_depth(); diff --git a/py/stackctrl.h b/py/stackctrl.h index a3dfadb05a11..63667f8a5512 100644 --- a/py/stackctrl.h +++ b/py/stackctrl.h @@ -45,6 +45,7 @@ void mp_stack_check(void); #endif +// CIRCUITPY-CHANGE: provide max stack usage #if MICROPY_MAX_STACK_USAGE const char MP_MAX_STACK_USAGE_SENTINEL_BYTE; diff --git a/py/stream.c b/py/stream.c index eb50f5a9cca0..7f62b3be9d61 100644 --- a/py/stream.c +++ b/py/stream.c @@ -477,6 +477,7 @@ STATIC mp_obj_t stream_tell(mp_obj_t self) { } MP_DEFINE_CONST_FUN_OBJ_1(mp_stream_tell_obj, stream_tell); +// CIRCUITPY-CHANGE: make public mp_obj_t mp_stream_flush(mp_obj_t self) { const mp_stream_p_t *stream_p = mp_get_stream(self); int error; diff --git a/py/stream.h b/py/stream.h index 56e3726bea22..8ee4fe055b3f 100644 --- a/py/stream.h +++ b/py/stream.h @@ -45,6 +45,7 @@ #define MP_STREAM_GET_DATA_OPTS (8) // Get data/message options #define MP_STREAM_SET_DATA_OPTS (9) // Set data/message options #define MP_STREAM_GET_FILENO (10) // Get fileno of underlying file +#define MP_STREAM_GET_BUFFER_SIZE (11) // Get preferred buffer size for file // These poll ioctl values are compatible with Linux #define MP_STREAM_POLL_RD (0x0001) @@ -77,6 +78,7 @@ typedef struct _mp_stream_p_t { mp_uint_t (*write)(mp_obj_t obj, const void *buf, mp_uint_t size, int *errcode); mp_uint_t (*ioctl)(mp_obj_t obj, mp_uint_t request, uintptr_t arg, int *errcode); mp_uint_t is_text : 1; // default is bytes, set this for text stream + // CIRCUITPY-CHANGE: pyserial compatibility bool pyserial_readinto_compatibility : 1; // Disallow size parameter in readinto() bool pyserial_read_compatibility : 1; // Disallow omitting read(size) size parameter bool pyserial_dont_return_none_compatibility : 1; // Don't return None for read() or readinto() @@ -124,7 +126,7 @@ mp_uint_t mp_stream_rw(mp_obj_t stream, void *buf, mp_uint_t size, int *errcode, #define mp_stream_read_exactly(stream, buf, size, err) mp_stream_rw(stream, buf, size, err, MP_STREAM_RW_READ) void mp_stream_write_adaptor(void *self, const char *buf, size_t len); -// CIRCUITPY-CHANGE +// CIRCUITPY-CHANGE: make public mp_obj_t mp_stream_flush(mp_obj_t self); #if MICROPY_STREAMS_POSIX_API diff --git a/py/vm.c b/py/vm.c index 8ccf9631b4aa..d58efa08309a 100644 --- a/py/vm.c +++ b/py/vm.c @@ -235,6 +235,7 @@ mp_vm_return_kind_t MICROPY_WRAP_MP_EXECUTE_BYTECODE(mp_execute_bytecode)(mp_cod // about to be dispatched. #define MARK_EXC_IP_GLOBAL() { code_state->ip = ip; } #endif + #if MICROPY_OPT_COMPUTED_GOTO #include "py/vmentrytable.h" // CIRCUITPY-CHANGE @@ -1444,8 +1445,8 @@ unwind_jump:; // - constant GeneratorExit object, because it's const // - exceptions re-raised by END_FINALLY // - exceptions re-raised explicitly by "raise" + // CIRCUITPY-CHANGE: MICROPY_CONST_GENERATOREXIT_OBJ check; true just helps formatting. if ( true - // CIRCUITPY-CHANGE #if MICROPY_CONST_GENERATOREXIT_OBJ && nlr.ret_val != &mp_const_GeneratorExit_obj #endif @@ -1488,20 +1489,20 @@ unwind_jump:; // catch exception and pass to byte code code_state->ip = exc_sp->handler; mp_obj_t *sp = MP_TAGPTR_PTR(exc_sp->val_sp); - //CIRCUITPY + // CIRCUITPY-CHANGE #if MICROPY_CPYTHON_EXCEPTION_CHAIN mp_obj_t active_exception = get_active_exception(exc_sp, exc_stack); #endif // save this exception in the stack so it can be used in a reraise, if needed exc_sp->prev_exc = nlr.ret_val; - mp_obj_t obj = MP_OBJ_FROM_PTR(nlr.ret_val); + mp_obj_t ret_val_obj = MP_OBJ_FROM_PTR(nlr.ret_val); #if MICROPY_CPYTHON_EXCEPTION_CHAIN - if (active_exception != MP_OBJ_NULL && active_exception != obj) { - mp_store_attr(obj, MP_QSTR___context__, active_exception); + if (active_exception != MP_OBJ_NULL && active_exception != ret_val_obj) { + mp_store_attr(ret_val_obj, MP_QSTR___context__, active_exception); } #endif // push exception object so it can be handled by bytecode - PUSH(obj); + PUSH(MP_OBJ_FROM_PTR(ret_val_obj)); code_state->sp = sp; #if MICROPY_STACKLESS diff --git a/py/vmentrytable.h b/py/vmentrytable.h index 8b8781de93ca..b24f337f9a9c 100644 --- a/py/vmentrytable.h +++ b/py/vmentrytable.h @@ -26,6 +26,7 @@ // *FORMAT-OFF* +// CIRCUITPY-CHANGE: #ifdef instead of #if #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Winitializer-overrides" @@ -35,8 +36,10 @@ #pragma GCC diagnostic ignored "-Woverride-init" #endif // __GNUC__ >= 5 +// CIRCUITPY-CHANGE #include "supervisor/linker.h" +// CIRCUITPY-CHANGE #if MICROPY_OPT_COMPUTED_GOTO_SAVE_SPACE #define COMPUTE_ENTRY(x) ((char *)(x) - (char *) && entry_MP_BC_LOAD_CONST_FALSE) typedef int16_t entry_table_type; @@ -45,6 +48,7 @@ typedef int16_t entry_table_type; typedef void *entry_table_type; #endif +// CIRCUITPY-CHANGE: PLACE_IN_DTCM_DATA static entry_table_type const PLACE_IN_DTCM_DATA(entry_table[256]) = { [0 ... 255] = COMPUTE_ENTRY(&& entry_default), [MP_BC_LOAD_CONST_FALSE] = COMPUTE_ENTRY(&& entry_MP_BC_LOAD_CONST_FALSE), @@ -130,6 +134,7 @@ static entry_table_type const PLACE_IN_DTCM_DATA(entry_table[256]) = { [MP_BC_BINARY_OP_MULTI ... MP_BC_BINARY_OP_MULTI + MP_BC_BINARY_OP_MULTI_NUM - 1] = COMPUTE_ENTRY(&& entry_MP_BC_BINARY_OP_MULTI), }; +// CIRCUITPY-CHANGE: #ifdef instead of #if #ifdef __clang__ #pragma clang diagnostic pop #endif // __clang__ diff --git a/shared-bindings/__future__/__init__.h b/shared-bindings/__future__/__init__.h index 9289c128578e..436e8fd0a0f6 100644 --- a/shared-bindings/__future__/__init__.h +++ b/shared-bindings/__future__/__init__.h @@ -5,8 +5,3 @@ // SPDX-License-Identifier: MIT #pragma once - -#ifndef MICROPY_INCLUDED_SHARED_BINDINGS___FUTURE_____INIT___H -#define MICROPY_INCLUDED_SHARED_BINDINGS___FUTURE_____INIT___H - -#endif // MICROPY_INCLUDED_SHARED_BINDINGS___FUTURE_____INIT___H diff --git a/shared-bindings/_pixelmap/__init__.h b/shared-bindings/_pixelmap/__init__.h index a98da1ed66c8..f4647dba1e91 100644 --- a/shared-bindings/_pixelmap/__init__.h +++ b/shared-bindings/_pixelmap/__init__.h @@ -5,8 +5,3 @@ // SPDX-License-Identifier: MIT #pragma once - -#ifndef CP_SHARED_BINDINGS_PIXELBUF_INIT_H -#define CP_SHARED_BINDINGS_PIXELBUF_INIT_H - -#endif // CP_SHARED_BINDINGS_PIXELBUF_INIT_H diff --git a/shared-bindings/adafruit_bus_device/__init__.h b/shared-bindings/adafruit_bus_device/__init__.h index 8c0321cd1143..844fc5281fa0 100644 --- a/shared-bindings/adafruit_bus_device/__init__.h +++ b/shared-bindings/adafruit_bus_device/__init__.h @@ -5,5 +5,3 @@ // SPDX-License-Identifier: MIT #pragma once - -// Nothing now. diff --git a/shared-bindings/adafruit_pixelbuf/__init__.h b/shared-bindings/adafruit_pixelbuf/__init__.h index a98da1ed66c8..f4647dba1e91 100644 --- a/shared-bindings/adafruit_pixelbuf/__init__.h +++ b/shared-bindings/adafruit_pixelbuf/__init__.h @@ -5,8 +5,3 @@ // SPDX-License-Identifier: MIT #pragma once - -#ifndef CP_SHARED_BINDINGS_PIXELBUF_INIT_H -#define CP_SHARED_BINDINGS_PIXELBUF_INIT_H - -#endif // CP_SHARED_BINDINGS_PIXELBUF_INIT_H diff --git a/shared-bindings/analogbufio/__init__.h b/shared-bindings/analogbufio/__init__.h index 752f8d079571..34c639ae8c44 100644 --- a/shared-bindings/analogbufio/__init__.h +++ b/shared-bindings/analogbufio/__init__.h @@ -5,7 +5,3 @@ // SPDX-License-Identifier: MIT #pragma once - -#include "py/obj.h" - -// Nothing now. diff --git a/shared-bindings/analogio/__init__.h b/shared-bindings/analogio/__init__.h index 48f52ceb0f3d..370e233985f7 100644 --- a/shared-bindings/analogio/__init__.h +++ b/shared-bindings/analogio/__init__.h @@ -5,7 +5,3 @@ // SPDX-License-Identifier: MIT #pragma once - -#include "py/obj.h" - -// Nothing now. diff --git a/shared-bindings/audiobusio/__init__.h b/shared-bindings/audiobusio/__init__.h index 889bd8bcd3e3..2c669f638b6b 100644 --- a/shared-bindings/audiobusio/__init__.h +++ b/shared-bindings/audiobusio/__init__.h @@ -5,7 +5,3 @@ // SPDX-License-Identifier: MIT #pragma once - -#include "py/obj.h" - -// Nothing now. diff --git a/shared-bindings/audiocore/__init__.h b/shared-bindings/audiocore/__init__.h index 889bd8bcd3e3..2c669f638b6b 100644 --- a/shared-bindings/audiocore/__init__.h +++ b/shared-bindings/audiocore/__init__.h @@ -5,7 +5,3 @@ // SPDX-License-Identifier: MIT #pragma once - -#include "py/obj.h" - -// Nothing now. diff --git a/shared-bindings/audioio/__init__.h b/shared-bindings/audioio/__init__.h index 889bd8bcd3e3..2c669f638b6b 100644 --- a/shared-bindings/audioio/__init__.h +++ b/shared-bindings/audioio/__init__.h @@ -5,7 +5,3 @@ // SPDX-License-Identifier: MIT #pragma once - -#include "py/obj.h" - -// Nothing now. diff --git a/shared-bindings/audiomixer/__init__.h b/shared-bindings/audiomixer/__init__.h index 3da23ce63582..8af87c5d5cd8 100644 --- a/shared-bindings/audiomixer/__init__.h +++ b/shared-bindings/audiomixer/__init__.h @@ -5,7 +5,3 @@ // SPDX-License-Identifier: MIT #pragma once - -#include "py/obj.h" - -// Nothing now. diff --git a/shared-bindings/audiomp3/__init__.h b/shared-bindings/audiomp3/__init__.h index 41b0dc84106e..a97027634a9c 100644 --- a/shared-bindings/audiomp3/__init__.h +++ b/shared-bindings/audiomp3/__init__.h @@ -5,7 +5,3 @@ // SPDX-License-Identifier: MIT #pragma once - -#include "py/obj.h" - -// Nothing now. diff --git a/shared-bindings/audiopwmio/__init__.h b/shared-bindings/audiopwmio/__init__.h index 889bd8bcd3e3..2c669f638b6b 100644 --- a/shared-bindings/audiopwmio/__init__.h +++ b/shared-bindings/audiopwmio/__init__.h @@ -5,7 +5,3 @@ // SPDX-License-Identifier: MIT #pragma once - -#include "py/obj.h" - -// Nothing now. diff --git a/shared-bindings/busio/__init__.h b/shared-bindings/busio/__init__.h index 48f52ceb0f3d..370e233985f7 100644 --- a/shared-bindings/busio/__init__.h +++ b/shared-bindings/busio/__init__.h @@ -5,7 +5,3 @@ // SPDX-License-Identifier: MIT #pragma once - -#include "py/obj.h" - -// Nothing now. diff --git a/shared-bindings/countio/__init__.h b/shared-bindings/countio/__init__.h index 8918616129c7..04de72846ac8 100644 --- a/shared-bindings/countio/__init__.h +++ b/shared-bindings/countio/__init__.h @@ -5,7 +5,3 @@ // SPDX-License-Identifier: MIT #pragma once - -#include "py/obj.h" - -// Nothing now. diff --git a/shared-bindings/digitalio/__init__.h b/shared-bindings/digitalio/__init__.h index 48f52ceb0f3d..370e233985f7 100644 --- a/shared-bindings/digitalio/__init__.h +++ b/shared-bindings/digitalio/__init__.h @@ -5,7 +5,3 @@ // SPDX-License-Identifier: MIT #pragma once - -#include "py/obj.h" - -// Nothing now. diff --git a/shared-bindings/fontio/__init__.h b/shared-bindings/fontio/__init__.h index 76b9fa8e6a5d..70cc2d4786f3 100644 --- a/shared-bindings/fontio/__init__.h +++ b/shared-bindings/fontio/__init__.h @@ -5,9 +5,3 @@ // SPDX-License-Identifier: MIT #pragma once - -#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_FONTIO___INIT___H -#define MICROPY_INCLUDED_SHARED_BINDINGS_FONTIO___INIT___H - - -#endif // MICROPY_INCLUDED_SHARED_BINDINGS_FONTIO___INIT___H diff --git a/shared-bindings/frequencyio/__init__.h b/shared-bindings/frequencyio/__init__.h index 3da23ce63582..8af87c5d5cd8 100644 --- a/shared-bindings/frequencyio/__init__.h +++ b/shared-bindings/frequencyio/__init__.h @@ -5,7 +5,3 @@ // SPDX-License-Identifier: MIT #pragma once - -#include "py/obj.h" - -// Nothing now. diff --git a/shared-bindings/gnss/GNSS.h b/shared-bindings/gnss/GNSS.h index dca640b6907b..d6ff4297acee 100644 --- a/shared-bindings/gnss/GNSS.h +++ b/shared-bindings/gnss/GNSS.h @@ -2,8 +2,7 @@ // // SPDX-License-Identifier: MIT -#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_GNSS_GNSS_H -#define MICROPY_INCLUDED_SHARED_BINDINGS_GNSS_GNSS_H +#pragma once #include "common-hal/gnss/GNSS.h" #include "shared-bindings/gnss/SatelliteSystem.h" @@ -23,5 +22,3 @@ mp_float_t common_hal_gnss_get_longitude(gnss_obj_t *self); mp_float_t common_hal_gnss_get_altitude(gnss_obj_t *self); void common_hal_gnss_get_timestamp(gnss_obj_t *self, timeutils_struct_time_t *tm); gnss_positionfix_t common_hal_gnss_get_fix(gnss_obj_t *self); - -#endif // MICROPY_INCLUDED_SHARED_BINDINGS_GNSS_GNSS_H diff --git a/shared-bindings/gnss/PositionFix.h b/shared-bindings/gnss/PositionFix.h index 0fd595fc6c1a..35c79b3af72f 100644 --- a/shared-bindings/gnss/PositionFix.h +++ b/shared-bindings/gnss/PositionFix.h @@ -2,8 +2,7 @@ // // SPDX-License-Identifier: MIT -#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_GNSS_POSITIONFIX_H -#define MICROPY_INCLUDED_SHARED_BINDINGS_GNSS_POSITIONFIX_H +#pragma once #include "py/obj.h" @@ -24,5 +23,3 @@ typedef struct { extern const gnss_positionfix_obj_t gnss_positionfix_invalid_obj; extern const gnss_positionfix_obj_t gnss_positionfix_fix2d_obj; extern const gnss_positionfix_obj_t gnss_positionfix_fix3d_obj; - -#endif // MICROPY_INCLUDED_SHARED_BINDINGS_GNSS_POSITIONFIX_H diff --git a/shared-bindings/gnss/SatelliteSystem.h b/shared-bindings/gnss/SatelliteSystem.h index 02cd17db2fe9..94f8b80ec504 100644 --- a/shared-bindings/gnss/SatelliteSystem.h +++ b/shared-bindings/gnss/SatelliteSystem.h @@ -2,8 +2,7 @@ // // SPDX-License-Identifier: MIT -#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_GNSS_SATELLITESYSTEM_H -#define MICROPY_INCLUDED_SHARED_BINDINGS_GNSS_SATELLITESYSTEM_H +#pragma once #include "py/obj.h" @@ -29,5 +28,3 @@ extern const gnss_satellitesystem_obj_t gnss_satellitesystem_glonass_obj; extern const gnss_satellitesystem_obj_t gnss_satellitesystem_sbas_obj; extern const gnss_satellitesystem_obj_t gnss_satellitesystem_qzss_l1ca_obj; extern const gnss_satellitesystem_obj_t gnss_satellitesystem_qzss_l1s_obj; - -#endif // MICROPY_INCLUDED_SHARED_BINDINGS_GNSS_SATELLITESYSTEM_H diff --git a/shared-bindings/memorymap/__init__.h b/shared-bindings/memorymap/__init__.h index 5762fd34fcaf..a3eb3cbe1476 100644 --- a/shared-bindings/memorymap/__init__.h +++ b/shared-bindings/memorymap/__init__.h @@ -5,8 +5,3 @@ // SPDX-License-Identifier: MIT #pragma once - -#ifndef SHARED_BINDINGS_MEMORYMAP_H -#define SHARED_BINDINGS_MEMORYMAP_H - -#endif // SHARED_BINDINGS_MEMORYMAP_H diff --git a/shared-bindings/nvm/__init__.h b/shared-bindings/nvm/__init__.h index 96c6efa67463..2c669f638b6b 100644 --- a/shared-bindings/nvm/__init__.h +++ b/shared-bindings/nvm/__init__.h @@ -5,8 +5,3 @@ // SPDX-License-Identifier: MIT #pragma once - -#ifndef SHARED_BINDINGS_NVM_H -#define SHARED_BINDINGS_NVM_H - -#endif // SHARED_BINDINGS_NVM_H diff --git a/shared-bindings/onewireio/__init__.h b/shared-bindings/onewireio/__init__.h index 48f52ceb0f3d..370e233985f7 100644 --- a/shared-bindings/onewireio/__init__.h +++ b/shared-bindings/onewireio/__init__.h @@ -5,7 +5,3 @@ // SPDX-License-Identifier: MIT #pragma once - -#include "py/obj.h" - -// Nothing now. diff --git a/shared-bindings/ps2io/__init__.h b/shared-bindings/ps2io/__init__.h index 25aceee328c1..f8afe079871c 100644 --- a/shared-bindings/ps2io/__init__.h +++ b/shared-bindings/ps2io/__init__.h @@ -6,7 +6,3 @@ // SPDX-License-Identifier: MIT #pragma once - -#include "py/obj.h" - -// Nothing now. diff --git a/shared-bindings/pulseio/__init__.h b/shared-bindings/pulseio/__init__.h index 48f52ceb0f3d..370e233985f7 100644 --- a/shared-bindings/pulseio/__init__.h +++ b/shared-bindings/pulseio/__init__.h @@ -5,7 +5,3 @@ // SPDX-License-Identifier: MIT #pragma once - -#include "py/obj.h" - -// Nothing now. diff --git a/shared-bindings/pwmio/__init__.h b/shared-bindings/pwmio/__init__.h index 48f52ceb0f3d..370e233985f7 100644 --- a/shared-bindings/pwmio/__init__.h +++ b/shared-bindings/pwmio/__init__.h @@ -5,7 +5,3 @@ // SPDX-License-Identifier: MIT #pragma once - -#include "py/obj.h" - -// Nothing now. diff --git a/shared-bindings/rainbowio/__init__.c b/shared-bindings/rainbowio/__init__.c index ad32f091ad1e..a5a941e2a781 100644 --- a/shared-bindings/rainbowio/__init__.c +++ b/shared-bindings/rainbowio/__init__.c @@ -4,9 +4,11 @@ // // SPDX-License-Identifier: MIT -#include "shared-bindings/rainbowio/__init__.h" #include "py/mpconfig.h" #include "py/obj.h" + +#include "shared-bindings/rainbowio/__init__.h" + //| """`rainbowio` module. //| //| Provides the `colorwheel()` function.""" diff --git a/shared-bindings/rainbowio/__init__.h b/shared-bindings/rainbowio/__init__.h index d688c1330005..bb9fa52fed0e 100644 --- a/shared-bindings/rainbowio/__init__.h +++ b/shared-bindings/rainbowio/__init__.h @@ -6,6 +6,7 @@ #pragma once #include -#include "py/misc.h" + +#include "py/obj.h" int32_t colorwheel(mp_float_t pos); diff --git a/shared-bindings/rotaryio/__init__.h b/shared-bindings/rotaryio/__init__.h index f7ea9dc23521..2c51a0baf119 100644 --- a/shared-bindings/rotaryio/__init__.h +++ b/shared-bindings/rotaryio/__init__.h @@ -5,7 +5,3 @@ // SPDX-License-Identifier: MIT #pragma once - -#include "py/obj.h" - -// Nothing now. diff --git a/shared-bindings/terminalio/__init__.h b/shared-bindings/terminalio/__init__.h index e23230b3f7b8..db86dd27d910 100644 --- a/shared-bindings/terminalio/__init__.h +++ b/shared-bindings/terminalio/__init__.h @@ -5,5 +5,3 @@ // SPDX-License-Identifier: MIT #pragma once - -// Nothing now. diff --git a/shared-bindings/touchio/__init__.h b/shared-bindings/touchio/__init__.h index 48f52ceb0f3d..370e233985f7 100644 --- a/shared-bindings/touchio/__init__.h +++ b/shared-bindings/touchio/__init__.h @@ -5,7 +5,3 @@ // SPDX-License-Identifier: MIT #pragma once - -#include "py/obj.h" - -// Nothing now. diff --git a/shared-module/terminalio/__init__.h b/shared-module/terminalio/__init__.h index 452d4223e55c..70cc2d4786f3 100644 --- a/shared-module/terminalio/__init__.h +++ b/shared-module/terminalio/__init__.h @@ -5,8 +5,3 @@ // SPDX-License-Identifier: MIT #pragma once - -#ifndef SHARED_MODULE_TERMINALIO___INIT___H -#define SHARED_MODULE_TERMINALIO___INIT___H - -#endif /* SHARED_MODULE_TERMINALIO___INIT___H */ diff --git a/shared/libc/string0.c b/shared/libc/string0.c index b0a2620300bf..8d513e933c28 100644 --- a/shared/libc/string0.c +++ b/shared/libc/string0.c @@ -172,7 +172,7 @@ int strcmp(const char *s1, const char *s2) { } int strncmp(const char *s1, const char *s2, size_t n) { - while (*s1 && *s2 && n > 0) { + while (n > 0 && *s1 && *s2) { char c1 = *s1++; // XXX UTF8 get char, next char char c2 = *s2++; // XXX UTF8 get char, next char n--; diff --git a/shared/memzip/README.md b/shared/memzip/README.md index 9aa12a94c88f..8c7006c71b0a 100644 --- a/shared/memzip/README.md +++ b/shared/memzip/README.md @@ -18,9 +18,6 @@ OBJ += $(BUILD)/memzip-files.o MAKE_MEMZIP = ../shared/memzip/make-memzip.py -$(BUILD)/memzip-files.o: $(BUILD)/memzip-files.c - $(call compile_c) - $(BUILD)/memzip-files.c: $(shell find ${MEMZIP_DIR} -type f) @$(ECHO) "Creating $@" $(Q)$(PYTHON) $(MAKE_MEMZIP) --zip-file $(BUILD)/memzip-files.zip --c-file $@ $(MEMZIP_DIR) diff --git a/shared/memzip/lexermemzip.c b/shared/memzip/lexermemzip.c index c1f21c1caa45..aef64ffa0a71 100644 --- a/shared/memzip/lexermemzip.c +++ b/shared/memzip/lexermemzip.c @@ -5,14 +5,14 @@ #include "py/mperrno.h" #include "memzip.h" -mp_lexer_t *mp_lexer_new_from_file(const char *filename) +mp_lexer_t *mp_lexer_new_from_file(qstr filename) { void *data; size_t len; - if (memzip_locate(filename, &data, &len) != MZ_OK) { + if (memzip_locate(qstr_str(filename), &data, &len) != MZ_OK) { mp_raise_OSError(MP_ENOENT); } - return mp_lexer_new_from_str_len(qstr_from_str(filename), (const char *)data, (mp_uint_t)len, 0); + return mp_lexer_new_from_str_len(filename, (const char *)data, (mp_uint_t)len, 0); } diff --git a/shared/memzip/make-memzip.py b/shared/memzip/make-memzip.py index 9fc92f2c4d1f..92a5d6168bb1 100755 --- a/shared/memzip/make-memzip.py +++ b/shared/memzip/make-memzip.py @@ -15,64 +15,67 @@ import sys import types + def create_zip(zip_filename, zip_dir): abs_zip_filename = os.path.abspath(zip_filename) save_cwd = os.getcwd() os.chdir(zip_dir) if os.path.exists(abs_zip_filename): os.remove(abs_zip_filename) - subprocess.check_call(['zip', '-0', '-r', '-D', abs_zip_filename, '.']) + subprocess.check_call(["zip", "-0", "-r", "-D", abs_zip_filename, "."]) os.chdir(save_cwd) + def create_c_from_file(c_filename, zip_filename): - with open(zip_filename, 'rb') as zip_file: - with open(c_filename, 'wb') as c_file: - print('#include ', file=c_file) - print('', file=c_file) - print('const uint8_t memzip_data[] = {', file=c_file) + with open(zip_filename, "rb") as zip_file: + with open(c_filename, "wb") as c_file: + print("#include ", file=c_file) + print("", file=c_file) + print("const uint8_t memzip_data[] = {", file=c_file) while True: buf = zip_file.read(16) if not buf: break - print(' ', end='', file=c_file) + print(" ", end="", file=c_file) for byte in buf: if isinstance(byte, types.StringType): - print(' 0x{:02x},'.format(ord(byte)), end='', file=c_file) + print(" 0x{:02x},".format(ord(byte)), end="", file=c_file) else: - print(' 0x{:02x},'.format(byte), end='', file=c_file) - print('', file=c_file) - print('};', file=c_file) + print(" 0x{:02x},".format(byte), end="", file=c_file) + print("", file=c_file) + print("};", file=c_file) + def main(): parser = argparse.ArgumentParser( - prog='make-memzip.py', - usage='%(prog)s [options] [command]', - description='Generates a C source memzip file.' + prog="make-memzip.py", + usage="%(prog)s [options] [command]", + description="Generates a C source memzip file.", ) parser.add_argument( - '-z', '--zip-file', - dest='zip_filename', - help='Specifies the name of the created zip file.', - default='memzip_files.zip' + "-z", + "--zip-file", + dest="zip_filename", + help="Specifies the name of the created zip file.", + default="memzip_files.zip", ) parser.add_argument( - '-c', '--c-file', - dest='c_filename', - help='Specifies the name of the created C source file.', - default='memzip_files.c' - ) - parser.add_argument( - dest='source_dir', - default='memzip_files' + "-c", + "--c-file", + dest="c_filename", + help="Specifies the name of the created C source file.", + default="memzip_files.c", ) + parser.add_argument(dest="source_dir", default="memzip_files") args = parser.parse_args(sys.argv[1:]) - print('args.zip_filename =', args.zip_filename) - print('args.c_filename =', args.c_filename) - print('args.source_dir =', args.source_dir) + print("args.zip_filename =", args.zip_filename) + print("args.c_filename =", args.c_filename) + print("args.source_dir =", args.source_dir) create_zip(args.zip_filename, args.source_dir) create_c_from_file(args.c_filename, args.zip_filename) + if __name__ == "__main__": main() diff --git a/shared/netutils/dhcpserver.c b/shared/netutils/dhcpserver.c index 1609ea964689..41bc4ffdcb87 100644 --- a/shared/netutils/dhcpserver.c +++ b/shared/netutils/dhcpserver.c @@ -34,6 +34,7 @@ #include "py/mphal.h" #include "lwip/opt.h" +// CIRCUITPY-CHANGE: comment // Used in CIRCUITPY without MICROPY_PY_LWIP #if LWIP_UDP @@ -273,6 +274,7 @@ static void dhcp_server_process(void *arg, struct udp_pcb *upcb, struct pbuf *p, d->lease[yi].expiry = (mp_hal_ticks_ms() + DEFAULT_LEASE_TIME_S * 1000) >> 16; dhcp_msg.yiaddr[3] = DHCPS_BASE_IP + yi; opt_write_u8(&opt, DHCP_OPT_MSG_TYPE, DHCPACK); + // CIRCUITPY-CHANGE: use LWIP_DEBUGF instead of printf LWIP_DEBUGF(DHCP_DEBUG, ("DHCPS: client connected: MAC=%02x:%02x:%02x:%02x:%02x:%02x IP=%u.%u.%u.%u\n", dhcp_msg.chaddr[0], dhcp_msg.chaddr[1], dhcp_msg.chaddr[2], dhcp_msg.chaddr[3], dhcp_msg.chaddr[4], dhcp_msg.chaddr[5], dhcp_msg.yiaddr[0], dhcp_msg.yiaddr[1], dhcp_msg.yiaddr[2], dhcp_msg.yiaddr[3])); diff --git a/shared/netutils/netutils.h b/shared/netutils/netutils.h index 58113d17b9c3..f82960ba800b 100644 --- a/shared/netutils/netutils.h +++ b/shared/netutils/netutils.h @@ -35,8 +35,6 @@ #define NETUTILS_TRACE_PAYLOAD (0x0002) #define NETUTILS_TRACE_NEWLINE (0x0004) -#include "py/runtime.h" - typedef enum _netutils_endian_t { NETUTILS_LITTLE, NETUTILS_BIG, diff --git a/shared/readline/readline.c b/shared/readline/readline.c index 2f8180b226b8..4906f51af3c1 100644 --- a/shared/readline/readline.c +++ b/shared/readline/readline.c @@ -40,18 +40,19 @@ #define DEBUG_printf(...) (void)0 #endif -// CIRCUITPY-CHANGE: a number of changes - -#define READLINE_HIST_SIZE (MP_ARRAY_SIZE(MP_STATE_PORT(readline_hist))) - // flags for readline_t.auto_indent_state #define AUTO_INDENT_ENABLED (0x01) #define AUTO_INDENT_JUST_ADDED (0x02) enum { ESEQ_NONE, ESEQ_ESC, ESEQ_ESC_BRACKET, ESEQ_ESC_BRACKET_DIGIT, ESEQ_ESC_O }; +#ifdef _MSC_VER +// work around MSVC compiler bug: https://stackoverflow.com/q/62259834/1976323 +#pragma warning(disable : 4090) +#endif + void readline_init0(void) { - memset(MP_STATE_PORT(readline_hist), 0, READLINE_HIST_SIZE * sizeof(const char*)); + memset(MP_STATE_PORT(readline_hist), 0, MICROPY_READLINE_HISTORY_SIZE * sizeof(const char*)); } STATIC char *str_dup_maybe(const char *str) { @@ -64,6 +65,7 @@ STATIC char *str_dup_maybe(const char *str) { return s2; } +// CIRCUITPY-CHANGE STATIC size_t count_cont_bytes(char *start, char *end) { int count = 0; for (char *pos = start; pos < end; pos++) { @@ -109,6 +111,7 @@ typedef struct _readline_t { int escape_seq; int hist_cur; size_t cursor_pos; + // CIRCUITPY-CHANGE uint8_t utf8_cont_chars; char escape_seq_buf[1]; #if MICROPY_REPL_AUTO_INDENT @@ -149,6 +152,7 @@ STATIC size_t cursor_count_word(int forward) { #endif int readline_process_char(int c) { + // CIRCUITPY-CHANGE size_t last_line_len = utf8_charlen((byte *)rl.line->buf, rl.line->len); int cont_chars = 0; int redraw_step_back = 0; @@ -187,6 +191,7 @@ int readline_process_char(int c) { // set redraw parameters redraw_from_cursor = true; #endif + // CIRCUITPY-CHANGE: add ctrl-L } else if (c == CHAR_CTRL_L) { // CTRL-L is clear screen / redraw. This specific sequence is used // (instead of a slightly more minimal sequence) for compatibility @@ -205,6 +210,7 @@ int readline_process_char(int c) { goto up_arrow_key; } else if (c == CHAR_CTRL_U) { // CTRL-U is kill from beginning-of-line up to cursor + // CIRCUITPY-CHANGE cont_chars = count_cont_bytes(rl.line->buf+rl.orig_line_len, rl.line->buf+rl.cursor_pos); vstr_cut_out_bytes(rl.line, rl.orig_line_len, rl.cursor_pos - rl.orig_line_len); // set redraw parameters @@ -245,6 +251,7 @@ int readline_process_char(int c) { int nspace = 1; #endif + // CIRCUITPY-CHANGE // Check if we have moved into a UTF-8 continuation byte while (UTF8_IS_CONT(rl.line->buf[rl.cursor_pos-nspace])) { nspace++; @@ -305,6 +312,7 @@ int readline_process_char(int c) { redraw_step_forward = compl_len; } #endif + // CIRCUITPY-CHANGE: UTF8 handling } else if (32 <= c) { // printable character uint8_t lcp = rl.line->buf[rl.cursor_pos]; @@ -381,7 +389,8 @@ int readline_process_char(int c) { up_arrow_key: #endif // up arrow - if (rl.hist_cur + 1 < (int)READLINE_HIST_SIZE && MP_STATE_PORT(readline_hist)[rl.hist_cur + 1] != NULL) { + if (rl.hist_cur + 1 < MICROPY_READLINE_HISTORY_SIZE && MP_STATE_PORT(readline_hist)[rl.hist_cur + 1] != NULL) { + // CIRCUITPY-CHANGE // Check for continuation characters cont_chars = count_cont_bytes(rl.line->buf+rl.orig_line_len, rl.line->buf+rl.cursor_pos); // increase hist num @@ -400,6 +409,7 @@ int readline_process_char(int c) { #endif // down arrow if (rl.hist_cur >= 0) { + // CIRCUITPY-CHANGE // Check for continuation characters cont_chars = count_cont_bytes(rl.line->buf+rl.orig_line_len, rl.line->buf+rl.cursor_pos); // decrease hist num @@ -444,6 +454,7 @@ int readline_process_char(int c) { if (c == '~') { if (rl.escape_seq_buf[0] == '1' || rl.escape_seq_buf[0] == '7') { home_key: + // CIRCUITPY-CHANGE cont_chars = count_cont_bytes(rl.line->buf+rl.orig_line_len, rl.line->buf+rl.cursor_pos); redraw_step_back = rl.cursor_pos - rl.orig_line_len; } else if (rl.escape_seq_buf[0] == '4' || rl.escape_seq_buf[0] == '8') { @@ -455,6 +466,7 @@ int readline_process_char(int c) { delete_key: #endif if (rl.cursor_pos < rl.line->len) { + // CIRCUITPY-CHANGE size_t len = 1; while (UTF8_IS_CONT(rl.line->buf[rl.cursor_pos+len]) && rl.cursor_pos+len < rl.line->len) { @@ -508,19 +520,23 @@ int readline_process_char(int c) { // redraw command prompt, efficiently if (redraw_step_back > 0) { + // CIRCUITPY-CHANGE mp_hal_move_cursor_back(redraw_step_back-cont_chars); rl.cursor_pos -= redraw_step_back; } if (redraw_from_cursor) { + // CIRCUITPY-CHANGE if (utf8_charlen((byte *)rl.line->buf, rl.line->len) < last_line_len) { // erase old chars mp_hal_erase_line_from_cursor(last_line_len - rl.cursor_pos); } + // CIRCUITPY-CHANGE // Check for continuation characters cont_chars = count_cont_bytes(rl.line->buf+rl.cursor_pos+redraw_step_forward, rl.line->buf+rl.line->len); // draw new chars mp_hal_stdout_tx_strn(rl.line->buf + rl.cursor_pos, rl.line->len - rl.cursor_pos); // move cursor forward if needed (already moved forward by length of line, so move it back) + // CIRCUITPY-CHANGE mp_hal_move_cursor_back(rl.line->len - (rl.cursor_pos + redraw_step_forward) - cont_chars); rl.cursor_pos += redraw_step_forward; } else if (redraw_step_forward > 0) { diff --git a/shared/readline/readline.h b/shared/readline/readline.h index 0764daa62cc8..fcae9c6c8407 100644 --- a/shared/readline/readline.h +++ b/shared/readline/readline.h @@ -26,8 +26,7 @@ #ifndef MICROPY_INCLUDED_LIB_MP_READLINE_READLINE_H #define MICROPY_INCLUDED_LIB_MP_READLINE_READLINE_H -// CIRCUITPY-CHANGE: a number of changes - +// CIRCUITPY-CHANGE: for vstr_t #include "py/misc.h" #define CHAR_CTRL_A (1) @@ -37,6 +36,7 @@ #define CHAR_CTRL_E (5) #define CHAR_CTRL_F (6) #define CHAR_CTRL_K (11) +// CIRCUITPY-CHANGE: ctrl-L support #define CHAR_CTRL_L (12) #define CHAR_CTRL_N (14) #define CHAR_CTRL_P (16) diff --git a/shared/runtime/interrupt_char.c b/shared/runtime/interrupt_char.c index a70255721b1e..1dfccc4b5c8e 100644 --- a/shared/runtime/interrupt_char.c +++ b/shared/runtime/interrupt_char.c @@ -26,6 +26,7 @@ #include "py/obj.h" #include "py/mpstate.h" +// CIRCUITPY-CHANGE #include "shared/runtime/interrupt_char.h" #if MICROPY_KBD_EXCEPTION diff --git a/shared/runtime/interrupt_char.h b/shared/runtime/interrupt_char.h index 6d9fe63453e5..f15036545c17 100644 --- a/shared/runtime/interrupt_char.h +++ b/shared/runtime/interrupt_char.h @@ -26,11 +26,12 @@ #ifndef MICROPY_INCLUDED_LIB_UTILS_INTERRUPT_CHAR_H #define MICROPY_INCLUDED_LIB_UTILS_INTERRUPT_CHAR_H -// CIRCUITPY-CHANGE: various changes +// CIRCUITPY-CHANGE #include extern int mp_interrupt_char; void mp_hal_set_interrupt_char(int c); +// CIRCUITPY-CHANGE bool mp_hal_is_interrupted(void); #endif // MICROPY_INCLUDED_LIB_UTILS_INTERRUPT_CHAR_H diff --git a/shared/runtime/pyexec.c b/shared/runtime/pyexec.c index 8cda8dfd37a7..94c506502c30 100644 --- a/shared/runtime/pyexec.c +++ b/shared/runtime/pyexec.c @@ -29,6 +29,7 @@ #include #include +// CIRCUITPY-CHANGE: add #include "py/mphal.h" #include "py/compile.h" #include "py/runtime.h" @@ -36,6 +37,7 @@ #include "py/gc.h" #include "py/frozenmod.h" #include "py/mphal.h" +// CIRCUITPY-CHANGE: prevent undefined warning #if defined(MICROPY_HW_ENABLE_USB) && MICROPY_HW_ENABLE_USB #include "irq.h" #include "usb.h" @@ -44,8 +46,7 @@ #include "shared/runtime/pyexec.h" #include "genhdr/mpversion.h" -// CIRCUITPY-CHANGE: multiple changes for atexit(), interrupts - +// CIRCUITPY-CHANGE: atexit support #if CIRCUITPY_ATEXIT #include "shared-module/atexit/__init__.h" #endif @@ -64,13 +65,16 @@ STATIC bool repl_display_debugging_info = 0; #define EXEC_FLAG_SOURCE_IS_VSTR (1 << 4) #define EXEC_FLAG_SOURCE_IS_FILENAME (1 << 5) #define EXEC_FLAG_SOURCE_IS_READER (1 << 6) -#define EXEC_FLAG_SOURCE_IS_ATEXIT (1 << 7) +#define EXEC_FLAG_NO_INTERRUPT (1 << 7) +// CIRCUITPY-CHANGE: add atexit support +#define EXEC_FLAG_SOURCE_IS_ATEXIT (1 << 8) // parses, compiles and executes the code in the lexer // frees the lexer before returning // EXEC_FLAG_PRINT_EOF prints 2 EOF chars: 1 after normal output, 1 after exception output // EXEC_FLAG_ALLOW_DEBUGGING allows debugging info to be printed after executing the code // EXEC_FLAG_IS_REPL is used for REPL inputs (flag passed on to mp_compile) +// CIRCUITPY-CHANGE: add result support STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input_kind, mp_uint_t exec_flags, pyexec_result_t *result) { int ret = 0; #if MICROPY_REPL_INFO @@ -88,9 +92,11 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input nlr.ret_val = NULL; if (nlr_push(&nlr) == 0) { mp_obj_t module_fun; + // CIRCUITPY-CHANGE #if CIRCUITPY_ATEXIT if (!(exec_flags & EXEC_FLAG_SOURCE_IS_ATEXIT)) #endif + // CIRCUITPY-CHANGE: multiple code changes { #if MICROPY_MODULE_FROZEN_MPY if (exec_flags & EXEC_FLAG_SOURCE_IS_RAW_CODE) { @@ -111,7 +117,7 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input } else if (exec_flags & EXEC_FLAG_SOURCE_IS_READER) { lex = mp_lexer_new(MP_QSTR__lt_stdin_gt_, *(mp_reader_t *)source); } else if (exec_flags & EXEC_FLAG_SOURCE_IS_FILENAME) { - lex = mp_lexer_new_from_file(source); + lex = mp_lexer_new_from_file(qstr_from_str(source)); } else { lex = (mp_lexer_t *)source; } @@ -138,10 +144,13 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input } // execute code - mp_hal_set_interrupt_char(CHAR_CTRL_C); // allow ctrl-C to interrupt us + if (!(exec_flags & EXEC_FLAG_NO_INTERRUPT)) { + mp_hal_set_interrupt_char(CHAR_CTRL_C); + } #if MICROPY_REPL_INFO start = mp_hal_ticks_ms(); #endif + // CIRCUITPY-CHANGE #if CIRCUITPY_ATEXIT if (exec_flags & EXEC_FLAG_SOURCE_IS_ATEXIT) { atexit_callback_t *callback = (atexit_callback_t *)source; @@ -154,6 +163,7 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input mp_hal_set_interrupt_char(-1); // disable interrupt mp_handle_pending(true); // handle any pending exceptions (and any callbacks) nlr_pop(); + // CIRCUITPY-CHANGE ret = 0; if (exec_flags & EXEC_FLAG_PRINT_EOF) { mp_hal_stdout_tx_strn("\x04", 1); @@ -175,12 +185,14 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input // check for SystemExit + // CIRCUITPY-CHANGE // nlr.ret_val is an exception object. mp_obj_t exception_obj = (mp_obj_t)nlr.ret_val; if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(mp_obj_get_type(exception_obj)), MP_OBJ_FROM_PTR(&mp_type_SystemExit))) { // at the moment, the value of SystemExit is unused ret = pyexec_system_exit; + // CIRCUITPY-CHANGE #if CIRCUITPY_ALARM } else if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(mp_obj_get_type(exception_obj)), MP_OBJ_FROM_PTR(&mp_type_DeepSleepRequest))) { ret = PYEXEC_DEEP_SLEEP; @@ -221,20 +233,20 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input // display debugging info if wanted if ((exec_flags & EXEC_FLAG_ALLOW_DEBUGGING) && repl_display_debugging_info) { mp_uint_t ticks = mp_hal_ticks_ms() - start; // TODO implement a function that does this properly - printf("took " UINT_FMT " ms\n", ticks); + mp_printf(&mp_plat_print, "took " UINT_FMT " ms\n", ticks); // qstr info { size_t n_pool, n_qstr, n_str_data_bytes, n_total_bytes; qstr_pool_info(&n_pool, &n_qstr, &n_str_data_bytes, &n_total_bytes); - printf("qstr:\n n_pool=" UINT_FMT "\n n_qstr=" UINT_FMT "\n " - "n_str_data_bytes=" UINT_FMT "\n n_total_bytes=" UINT_FMT "\n", + mp_printf(&mp_plat_print, "qstr:\n n_pool=%u\n n_qstr=%u\n " + "n_str_data_bytes=%u\n n_total_bytes=%u\n", (unsigned)n_pool, (unsigned)n_qstr, (unsigned)n_str_data_bytes, (unsigned)n_total_bytes); } #if MICROPY_ENABLE_GC // run collection and print GC info gc_collect(); - gc_dump_info(); + gc_dump_info(&mp_plat_print); #endif } #endif @@ -279,6 +291,7 @@ STATIC mp_uint_t mp_reader_stdin_readbyte(void *data) { mp_hal_stdout_tx_strn("\x04", 1); // indicate end to host if (c == CHAR_CTRL_C) { #if MICROPY_KBD_EXCEPTION + // CIRCUITPY-CHANGE: traceback struct difference MP_STATE_VM(mp_kbd_exception).traceback->data = NULL; nlr_raise(MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception))); #else @@ -341,6 +354,7 @@ STATIC int do_reader_stdin(int c) { mp_reader_stdin_t reader_stdin; mp_reader_new_stdin(&reader, &reader_stdin, MICROPY_REPL_STDIN_BUFFER_MAX); int exec_flags = EXEC_FLAG_PRINT_EOF | EXEC_FLAG_SOURCE_IS_READER; + // CIRCUITPY-CHANGE: add last arg return parse_compile_execute(&reader, MP_PARSE_FILE_INPUT, exec_flags, NULL); } @@ -416,6 +430,7 @@ STATIC int pyexec_raw_repl_process_char(int c) { return PYEXEC_FORCED_EXIT; } + // CIRCUITPY-CHANGE: add last arg int ret = parse_compile_execute(MP_STATE_VM(repl_line), MP_PARSE_FILE_INPUT, EXEC_FLAG_PRINT_EOF | EXEC_FLAG_SOURCE_IS_VSTR, NULL); if (ret & PYEXEC_FORCED_EXIT) { return ret; @@ -468,6 +483,7 @@ STATIC int pyexec_friendly_repl_process_char(int c) { } else if (ret == CHAR_CTRL_B) { // reset friendly REPL mp_hal_stdout_tx_str("\r\n"); + // CIRCUITPY-CHANGE: print CircuitPython-style banner. mp_hal_stdout_tx_str(MICROPY_FULL_VERSION_INFO); mp_hal_stdout_tx_str("\r\n"); // mp_hal_stdout_tx_str("Type \"help()\" for more information.\r\n"); @@ -608,6 +624,7 @@ int pyexec_raw_repl(void) { return PYEXEC_FORCED_EXIT; } + // CIRCUITPY-CHANGE: add last arg, handle reload int ret = parse_compile_execute(&line, MP_PARSE_FILE_INPUT, EXEC_FLAG_PRINT_EOF | EXEC_FLAG_SOURCE_IS_VSTR, NULL); if (ret & (PYEXEC_FORCED_EXIT | PYEXEC_RELOAD)) { return ret; @@ -620,6 +637,7 @@ int pyexec_friendly_repl(void) { vstr_init(&line, 32); friendly_repl_reset: + // CIRCUITPY-CHANGE: CircuitPython-style banner. mp_hal_stdout_tx_str("\r\n"); mp_hal_stdout_tx_str(MICROPY_FULL_VERSION_INFO); mp_hal_stdout_tx_str("\r\n"); @@ -646,6 +664,7 @@ int pyexec_friendly_repl(void) { for (;;) { input_restart: + // CIRCUITPY-CHANGE: prevent undef warning #if defined(MICROPY_HW_ENABLE_USB) && MICROPY_HW_ENABLE_USB if (usb_vcp_is_enabled()) { // If the user gets to here and interrupts are disabled then @@ -667,11 +686,12 @@ int pyexec_friendly_repl(void) { vstr_reset(&line); + // CIRCUITPY-CHANGE: handling uncaught exceptions differently nlr_buf_t nlr; nlr.ret_val = NULL; int ret = 0; if (nlr_push(&nlr) == 0) { - ret = readline(&line, ">>> "); + ret = readline(&line, mp_repl_get_ps1()); } else { // Uncaught exception mp_handle_pending(false); // clear any pending exceptions (and run any callbacks) @@ -745,6 +765,7 @@ int pyexec_friendly_repl(void) { } } + // CIRCUITPY-CHANGE: add last arg ret = parse_compile_execute(&line, parse_input_kind, EXEC_FLAG_ALLOW_DEBUGGING | EXEC_FLAG_IS_REPL | EXEC_FLAG_SOURCE_IS_VSTR, NULL); if (ret & (PYEXEC_FORCED_EXIT | PYEXEC_RELOAD)) { return ret; @@ -755,46 +776,56 @@ int pyexec_friendly_repl(void) { #endif // MICROPY_REPL_EVENT_DRIVEN #endif // MICROPY_ENABLE_COMPILER +// CIRCUITPY-CHANGE: add result arg int pyexec_file(const char *filename, pyexec_result_t *result) { return parse_compile_execute(filename, MP_PARSE_FILE_INPUT, EXEC_FLAG_SOURCE_IS_FILENAME, result); } +// CIRCUITPY-CHANGE: add result arg int pyexec_file_if_exists(const char *filename, pyexec_result_t *result) { #if MICROPY_MODULE_FROZEN if (mp_find_frozen_module(filename, NULL, NULL) == MP_IMPORT_STAT_FILE) { - return pyexec_frozen_module(filename, result); + // CIRCUITPY-CHANGE: pass result arg + return pyexec_frozen_module(filename, true, result); } #endif if (mp_import_stat(filename) != MP_IMPORT_STAT_FILE) { return 1; // success (no file is the same as an empty file executing without fail) } + // CIRCUITPY-CHANGE: pass result arg return pyexec_file(filename, result); } #if MICROPY_MODULE_FROZEN -int pyexec_frozen_module(const char *name, pyexec_result_t *result) { +// CIRCUITPY-CHANGE: add result arg +int pyexec_frozen_module(const char *name, bool allow_keyboard_interrupt, pyexec_result_t *result) { void *frozen_data; int frozen_type; mp_find_frozen_module(name, &frozen_type, &frozen_data); + mp_uint_t exec_flags = allow_keyboard_interrupt ? 0 : EXEC_FLAG_NO_INTERRUPT; switch (frozen_type) { #if MICROPY_MODULE_FROZEN_STR case MP_FROZEN_STR: + // CIRCUITPY-CHANGE: pass result arg return parse_compile_execute(frozen_data, MP_PARSE_FILE_INPUT, 0, result); #endif #if MICROPY_MODULE_FROZEN_MPY case MP_FROZEN_MPY: - return parse_compile_execute(frozen_data, MP_PARSE_FILE_INPUT, EXEC_FLAG_SOURCE_IS_RAW_CODE, result); + // CIRCUITPY-CHANGE: pass result arg + return parse_compile_execute(frozen_data, MP_PARSE_FILE_INPUT, exec_flags | + EXEC_FLAG_SOURCE_IS_RAW_CODE, result); #endif default: - printf("could not find module '%s'\n", name); + mp_printf(MICROPY_ERROR_PRINTER, "could not find module '%s'\n", name); return false; } } #endif +// CIRCUITPY-CHANGE: atexit support #if CIRCUITPY_ATEXIT int pyexec_exit_handler(const void *source, pyexec_result_t *result) { return parse_compile_execute(source, MP_PARSE_FILE_INPUT, EXEC_FLAG_SOURCE_IS_ATEXIT, result); diff --git a/shared/runtime/pyexec.h b/shared/runtime/pyexec.h index b980ef66a569..762b926c9c38 100644 --- a/shared/runtime/pyexec.h +++ b/shared/runtime/pyexec.h @@ -28,13 +28,12 @@ #include "py/obj.h" -// CIRCUITPY-CHANGE: multiple changes - typedef enum { PYEXEC_MODE_FRIENDLY_REPL, PYEXEC_MODE_RAW_REPL, } pyexec_mode_kind_t; +// CIRCUITPY-CHANGE typedef struct { int return_code; mp_obj_t exception; @@ -52,23 +51,27 @@ extern pyexec_mode_kind_t pyexec_mode_kind; extern int pyexec_system_exit; #define PYEXEC_FORCED_EXIT (0x100) +// CIRCUITPY-CHANGE: additional flags #define PYEXEC_EXCEPTION (0x200) #define PYEXEC_DEEP_SLEEP (0x400) #define PYEXEC_RELOAD (0x800) int pyexec_raw_repl(void); int pyexec_friendly_repl(void); +// CIRCUITPY-CHANGE: result out argument int pyexec_file(const char *filename, pyexec_result_t *result); int pyexec_file_if_exists(const char *filename, pyexec_result_t *result); -int pyexec_frozen_module(const char *name, pyexec_result_t *result); +int pyexec_frozen_module(const char *name, bool allow_keyboard_interrupt, pyexec_result_t *result); void pyexec_event_repl_init(void); int pyexec_event_repl_process_char(int c); extern uint8_t pyexec_repl_active; +// CIRCUITPY-CHANGE: atexit support #if CIRCUITPY_ATEXIT int pyexec_exit_handler(const void *source, pyexec_result_t *result); #endif +// CIRCUITPY-CHANGE #if CIRCUITPY_WATCHDOG pyexec_result_t *pyexec_result(void); #endif diff --git a/shared/runtime/stdout_helpers.c b/shared/runtime/stdout_helpers.c index e32aa342e4bb..5d68747b6aa2 100644 --- a/shared/runtime/stdout_helpers.c +++ b/shared/runtime/stdout_helpers.c @@ -25,8 +25,8 @@ */ #include -#include -#include "py/mpconfig.h" +// CIRCUITPY-CHANGE +#include #include "py/mphal.h" /* @@ -36,8 +36,11 @@ */ // CIRCUITPY-CHANGE: changes +// Note https://github.com/adafruit/circuitpython/pull/8614 // Send "cooked" string of given length, where every occurrence of -// LF character is replaced with CR LF. +// LF character is replaced with CR LF ("\n" is converted to "\r\n"). +// This is an optimised version to reduce the number of calls made +// to mp_hal_stdout_tx_strn. void mp_hal_stdout_tx_strn_cooked(const char *str, size_t len) { bool last_cr = false; while (len > 0) { diff --git a/shared/runtime/sys_stdio_mphal.c b/shared/runtime/sys_stdio_mphal.c index 84ce5828ef04..b7a35a5cbaa0 100644 --- a/shared/runtime/sys_stdio_mphal.c +++ b/shared/runtime/sys_stdio_mphal.c @@ -142,8 +142,7 @@ STATIC mp_uint_t stdio_buffer_read(mp_obj_t self_in, void *buf, mp_uint_t size, } STATIC mp_uint_t stdio_buffer_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) { - mp_hal_stdout_tx_strn(buf, size); - return size; + return mp_hal_stdout_tx_strn(buf, size); } STATIC const mp_stream_p_t stdio_buffer_obj_stream_p = { diff --git a/shared/timeutils/timeutils.h b/shared/timeutils/timeutils.h index 13ec3a01f2b2..ebebfa898016 100644 --- a/shared/timeutils/timeutils.h +++ b/shared/timeutils/timeutils.h @@ -27,7 +27,8 @@ #ifndef MICROPY_INCLUDED_LIB_TIMEUTILS_TIMEUTILS_H #define MICROPY_INCLUDED_LIB_TIMEUTILS_TIMEUTILS_H -#include "mpconfigport.h" // CIRCUITPY-CHANGE for MICROPY_EPOCH_IS_1970 +// CIRCUITPY-CHANGE: MICROPY_EPOCH_IS_1970 +#include "mpconfigport.h" // The number of seconds between 1970/1/1 and 2000/1/1 is calculated using: // time.mktime((2000,1,1,0,0,0,0,0,0)) - time.mktime((1970,1,1,0,0,0,0,0,0)) diff --git a/supervisor/shared/micropython.c b/supervisor/shared/micropython.c index e006d4805941..d03ecef8a3dd 100644 --- a/supervisor/shared/micropython.c +++ b/supervisor/shared/micropython.c @@ -36,7 +36,7 @@ int mp_hal_stdin_rx_chr(void) { } } -void mp_hal_stdout_tx_strn(const char *str, size_t len) { +mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) { toggle_tx_led(); #ifdef CIRCUITPY_BOOT_OUTPUT_FILE @@ -58,7 +58,7 @@ void mp_hal_stdout_tx_strn(const char *str, size_t len) { } #endif - serial_write_substring(str, len); + return serial_write_substring(str, len); } uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { diff --git a/supervisor/shared/serial.c b/supervisor/shared/serial.c index 4e4dae3d855d..fce066f70de7 100644 --- a/supervisor/shared/serial.c +++ b/supervisor/shared/serial.c @@ -318,25 +318,31 @@ uint32_t serial_bytes_available(void) { return count; } -void serial_write_substring(const char *text, uint32_t length) { +uint32_t serial_write_substring(const char *text, uint32_t length) { if (length == 0) { - return; + return 0; } + // See https://github.com/micropython/micropython/pull/11850 for the motivation for returning + // the number of chars written. + + // Assume that unless otherwise reported, we sent all that we got. + uint32_t length_sent = length; + #if CIRCUITPY_TERMINALIO int errcode; if (!_serial_display_write_disabled) { - common_hal_terminalio_terminal_write(&supervisor_terminal, (const uint8_t *)text, length, &errcode); + length_sent = common_hal_terminalio_terminal_write(&supervisor_terminal, (const uint8_t *)text, length, &errcode); } #endif if (_serial_console_write_disabled) { - return; + return length_sent; } #if CIRCUITPY_USB_DEVICE && CIRCUITPY_USB_VENDOR if (tud_vendor_connected()) { - tud_vendor_write(text, length); + length_sent = tud_vendor_write(text, length); } #endif @@ -346,7 +352,7 @@ void serial_write_substring(const char *text, uint32_t length) { _first_write_done = true; } int uart_errcode; - common_hal_busio_uart_write(&console_uart, (const uint8_t *)text, length, &uart_errcode); + length_sent = common_hal_busio_uart_write(&console_uart, (const uint8_t *)text, length, &uart_errcode); #endif #if CIRCUITPY_SERIAL_BLE @@ -359,7 +365,7 @@ void serial_write_substring(const char *text, uint32_t length) { #if CIRCUITPY_USB_DEVICE && CIRCUITPY_USB_CDC if (!usb_cdc_console_enabled()) { - return; + return length; } #endif @@ -384,6 +390,8 @@ void serial_write_substring(const char *text, uint32_t length) { board_serial_write_substring(text, length); port_serial_write_substring(text, length); + + return length_sent; } void serial_write(const char *text) { diff --git a/supervisor/shared/serial.h b/supervisor/shared/serial.h index b2af28066c62..ea0f147cc49e 100644 --- a/supervisor/shared/serial.h +++ b/supervisor/shared/serial.h @@ -23,7 +23,7 @@ void serial_early_init(void); void serial_init(void); void serial_write(const char *text); // Only writes up to given length. Does not check for null termination at all. -void serial_write_substring(const char *text, uint32_t length); +uint32_t serial_write_substring(const char *text, uint32_t length); char serial_read(void); uint32_t serial_bytes_available(void); bool serial_connected(void); diff --git a/supervisor/shared/translate/translate_impl.h b/supervisor/shared/translate/translate_impl.h index 292d77440f7d..5139210db30b 100644 --- a/supervisor/shared/translate/translate_impl.h +++ b/supervisor/shared/translate/translate_impl.h @@ -13,11 +13,13 @@ #include "supervisor/shared/translate/compressed_string.h" #ifndef NO_QSTR -#define QDEF(id, hash, len, str) +#define QDEF0(id, hash, len, str) +#define QDEF1(id, hash, len, str) #define TRANSLATION(english_id, number) extern struct compressed_string translation##number; #include "genhdr/qstrdefs.generated.h" +#undef QDEF0 +#undef QDEF1 #undef TRANSLATION -#undef QDEF #endif #if CIRCUITPY_TRANSLATE_OBJECT == 0 @@ -32,11 +34,13 @@ __attribute__((always_inline)) // optimization. __attribute__((no_instrument_function)) mp_rom_error_text_t MP_COMPRESSED_ROM_TEXT(const char *original) { #ifndef NO_QSTR - #define QDEF(id, hash, len, str) + #define QDEF0(id, hash, len, str) + #define QDEF1(id, hash, len, str) #define TRANSLATION(english_id, number) if (strcmp(original, english_id) == 0) { return (mp_rom_error_text_t)&translation##number; } else #include "genhdr/qstrdefs.generated.h" #undef TRANSLATION -#undef QDEF +#undef QDEF0 +#undef QDEF1 #endif return NULL; } diff --git a/tests/README.md b/tests/README.md index c89930d0c088..9a93c69b29cc 100644 --- a/tests/README.md +++ b/tests/README.md @@ -174,3 +174,21 @@ internal_bench/bytebuf: 0.177s (+87.78%) internal_bench/bytebuf-3-bytarray_map.py 1 tests performed (3 individual testcases) ``` + +## Test key/certificates + +SSL/TLS tests in `multi_net` and `net_inet` use a +self-signed key/cert pair that is randomly generated and to be used for +testing/demonstration only. You should always generate your own key/cert. + +To generate a new self-signed key/cert pair with openssl do: +``` +$ openssl req -x509 -newkey rsa:4096 -keyout rsa_key.pem -out rsa_cert.pem -days 365 -nodes +``` +In this case CN is: micropython.local + +Convert them to DER format: +``` +$ openssl rsa -in rsa_key.pem -out rsa_key.der -outform DER +$ openssl x509 -in rsa_cert.pem -out rsa_cert.der -outform DER +``` diff --git a/tests/basics/array_intbig.py b/tests/basics/array_intbig.py index ef3b567935df..eb45dd694e12 100644 --- a/tests/basics/array_intbig.py +++ b/tests/basics/array_intbig.py @@ -1,4 +1,5 @@ # test array types QqLl that require big-ints +# CIRCUITPY-CHANGE: conditionalize import skip_if skip_if.no_bigint() diff --git a/tests/basics/array_mul.py b/tests/basics/array_mul.py index bb5f3aa6b146..8c0abd34d739 100644 --- a/tests/basics/array_mul.py +++ b/tests/basics/array_mul.py @@ -1,3 +1,4 @@ +# CIRCUITPY-CHANGE: new test try: import array except ImportError: diff --git a/tests/basics/async_await2.py b/tests/basics/async_await2.py index 102bf9f277e9..730a3c425f28 100644 --- a/tests/basics/async_await2.py +++ b/tests/basics/async_await2.py @@ -1,5 +1,6 @@ # test await expression +# CIRCUITPY-CHANGE # uPy allows normal generators to be awaitables. # CircuitPython does not. # In CircuitPython you need to have an __await__ method on an awaitable like in CPython; diff --git a/tests/basics/async_for2.py b/tests/basics/async_for2.py index d7b97cb3869b..bbdb02c49b20 100644 --- a/tests/basics/async_for2.py +++ b/tests/basics/async_for2.py @@ -1,5 +1,6 @@ # test waiting within "async for" __anext__ function +# CIRCUITPY-CHANGE # uPy allows normal generators to be awaitables. # CircuitPython does not. # In CircuitPython you need to have an __await__ method on an awaitable like in CPython; diff --git a/tests/basics/async_with2.py b/tests/basics/async_with2.py index 2e9e9e5a752d..5bac38236fe4 100644 --- a/tests/basics/async_with2.py +++ b/tests/basics/async_with2.py @@ -1,5 +1,6 @@ # test waiting within async with enter/exit functions +# CIRCUITPY-CHANGE # uPy allows normal generators to be awaitables. # CircuitPython does not. # In CircuitPython you need to have an __await__ method on an awaitable like in CPython; diff --git a/tests/basics/boundmeth1.py b/tests/basics/boundmeth1.py index f483ba406de7..fe1b454688d6 100644 --- a/tests/basics/boundmeth1.py +++ b/tests/basics/boundmeth1.py @@ -3,14 +3,18 @@ # uPy and CPython differ when printing a bound method, so just print the type print(type(repr([].append))) + class A: def f(self): return 0 + def g(self, a): return a + def h(self, a, b, c, d, e, f): return a + b + c + d + e + f + # bound method with no extra args m = A().f print(m()) @@ -27,4 +31,46 @@ def h(self, a, b, c, d, e, f): try: A().f.x = 1 except AttributeError: - print('AttributeError') + print("AttributeError") + +print("--------") + +# bound method comparison with same object +a = A() +m1 = a.f +m2 = a.f +print(m1 == a.f) # should result in True +print(m2 == a.f) # should result in True +print(m1 == m2) # should result in True +print(m1 != a.f) # should result in False + +# bound method comparison with different objects +a1 = A() +a2 = A() +m1 = a1.f +m2 = a2.f +print(m1 == a2.f) # should result in False +print(m2 == a1.f) # should result in False +print(m1 != a2.f) # should result in True + +# bound method comparison with non-bound-method objects +print(A().f == None) # should result in False +print(A().f != None) # should result in True +print(None == A().f) # should result in False +print(None != A().f) # should result in True + +print("--------") + +# bound method hashing +a = A() +m1 = a.f +m2 = a.f +print(hash(m1) == hash(a.f)) # should result in True +print(hash(m2) == hash(a.f)) # should result in True +print(hash(m1) == hash(m2)) # should result in True +print(hash(m1) != hash(a.g)) # should result in True + +# bound method hashing with different objects +a2 = A() +m2 = a2.f +print(hash(m1) == hash(a2.f)) # should result in False diff --git a/tests/basics/builtin_pow3.py b/tests/basics/builtin_pow3.py index b9e0b8e04602..4eeef85c276f 100644 --- a/tests/basics/builtin_pow3.py +++ b/tests/basics/builtin_pow3.py @@ -29,6 +29,7 @@ except TypeError: print("TypeError expected") +# CIRCUITPY-CHANGE try: print(pow(4, 5, 0)) except ValueError: diff --git a/tests/basics/builtin_print.py b/tests/basics/builtin_print.py index 8b53e32c95b1..39456476611f 100644 --- a/tests/basics/builtin_print.py +++ b/tests/basics/builtin_print.py @@ -1,5 +1,6 @@ # test builtin print function +# CIRCUITPY-CHANGE: break the test print("break the test") print() print(None) diff --git a/tests/basics/builtin_reversed.py b/tests/basics/builtin_reversed.py index 6f07beaadd60..2131cf94519a 100644 --- a/tests/basics/builtin_reversed.py +++ b/tests/basics/builtin_reversed.py @@ -1,3 +1,4 @@ +# CIRCUITPY-CHANGE import skip_if skip_if.no_reversed() diff --git a/tests/basics/builtin_slice.py b/tests/basics/builtin_slice.py index 68a6635b3534..3ff3c3dd04d4 100644 --- a/tests/basics/builtin_slice.py +++ b/tests/basics/builtin_slice.py @@ -10,6 +10,7 @@ def __getitem__(self, idx): # check type print(type(s) is slice) +# CIRCUITPY-CHANGE: more tests s = slice(10) print(s) diff --git a/tests/basics/bytearray_intbig.py b/tests/basics/bytearray_intbig.py index 77bcf2225441..321f31f18a24 100644 --- a/tests/basics/bytearray_intbig.py +++ b/tests/basics/bytearray_intbig.py @@ -1,3 +1,4 @@ +# CIRCUITPY-CHANGE # Skip if long ints are not supported. try: # We have to use variables because 1 << 40 causes an exception on parse and diff --git a/tests/basics/bytes.py b/tests/basics/bytes.py index 0b6b14fa55bb..ba14c26da8de 100644 --- a/tests/basics/bytes.py +++ b/tests/basics/bytes.py @@ -2,7 +2,6 @@ print(b'123') print(br'123') print(rb'123') -print(b'\u1234') # construction print(bytes()) diff --git a/tests/basics/bytes_escape_unicode.py b/tests/basics/bytes_escape_unicode.py new file mode 100644 index 000000000000..1e450696f4f4 --- /dev/null +++ b/tests/basics/bytes_escape_unicode.py @@ -0,0 +1,3 @@ +# Coverage test for unicode escape in a bytes literal. +# CPython issues a SyntaxWarning for this. +print(b"\u1234") diff --git a/tests/basics/bytes_escape_unicode.py.exp b/tests/basics/bytes_escape_unicode.py.exp new file mode 100644 index 000000000000..6affe51896eb --- /dev/null +++ b/tests/basics/bytes_escape_unicode.py.exp @@ -0,0 +1 @@ +b'\\u1234' diff --git a/tests/basics/class_reverse_op.py b/tests/basics/class_reverse_op.py index a00928818b57..c5f6f4fb7829 100644 --- a/tests/basics/class_reverse_op.py +++ b/tests/basics/class_reverse_op.py @@ -1,3 +1,4 @@ +# CIRCUITPY-CHANGE import skip_if skip_if.no_reverse_ops() diff --git a/tests/basics/class_store_class.py b/tests/basics/class_store_class.py index 504f460a7d2e..d0c3f22f6b28 100644 --- a/tests/basics/class_store_class.py +++ b/tests/basics/class_store_class.py @@ -8,6 +8,7 @@ print("SKIP") raise SystemExit +# CIRCUITPY-CHANGE import skip_if skip_if.no_cpython_compat() diff --git a/tests/basics/class_super.py b/tests/basics/class_super.py index 629b7ddb4860..ea9d5bfc5987 100644 --- a/tests/basics/class_super.py +++ b/tests/basics/class_super.py @@ -35,6 +35,7 @@ def foo(self): return super().foo().count(2) # calling a subsequent method print(B().foo()) +# CIRCUITPY-CHANGE: extra test try: super(1, 1).x except TypeError: diff --git a/tests/basics/deque1.py b/tests/basics/deque1.py index 9ca6d434a75f..2cb3c2f87134 100644 --- a/tests/basics/deque1.py +++ b/tests/basics/deque1.py @@ -64,7 +64,7 @@ except TypeError: print("TypeError") - +# CIRCUITPY-CHANGE # Same tests, but now with pop() and appendleft() d = deque((), 2) diff --git a/tests/basics/deque2.py b/tests/basics/deque2.py index 743c040789d6..4c3743cf6e8e 100644 --- a/tests/basics/deque2.py +++ b/tests/basics/deque2.py @@ -6,6 +6,7 @@ print("SKIP") raise SystemExit +# CIRCUITPY-CHANGE: more functionality # Initial sequence is supported d = deque([1, 2, 3], 10) diff --git a/tests/basics/dict_fromkeys2.py b/tests/basics/dict_fromkeys2.py index a5371bc53859..37d65b533fa3 100644 --- a/tests/basics/dict_fromkeys2.py +++ b/tests/basics/dict_fromkeys2.py @@ -1,3 +1,4 @@ +# CIRCUITPY-CHANGE import skip_if skip_if.no_reversed() diff --git a/tests/basics/exception_chain.py b/tests/basics/exception_chain.py index b84ff19dd69e..579756c85fa2 100644 --- a/tests/basics/exception_chain.py +++ b/tests/basics/exception_chain.py @@ -1,3 +1,5 @@ +# CIRCUITPY-CHANGE: exception chaining is supported + try: Exception().__cause__ except AttributeError: diff --git a/tests/basics/gen_yield_from_close.py b/tests/basics/gen_yield_from_close.py index 08e0d41bebf9..a493a4103533 100644 --- a/tests/basics/gen_yield_from_close.py +++ b/tests/basics/gen_yield_from_close.py @@ -33,6 +33,7 @@ def gen3(): yield 3 yield 4 +# CIRCUITPY-CHANGE: traceback def gen4(): yield -1 try: @@ -126,6 +127,7 @@ def gen9(): print(next(g)) g.close() +# CIRCUITPY-CHANGE # Test that, when chaining to a GeneratorExit exception generated internally, # no exception or crash occurs def gen10(): diff --git a/tests/basics/gen_yield_from_throw.py b/tests/basics/gen_yield_from_throw.py index 1f76e13f3c34..063bed25e200 100644 --- a/tests/basics/gen_yield_from_throw.py +++ b/tests/basics/gen_yield_from_throw.py @@ -17,24 +17,6 @@ def gen2(): except TypeError: print("got TypeError from downstream!") -# passing None as second argument to throw -g = gen2() -print(next(g)) -print(g.throw(ValueError, None)) -try: - print(next(g)) -except TypeError: - print("got TypeError from downstream!") - -# passing an exception instance as second argument to throw -g = gen2() -print(next(g)) -print(g.throw(ValueError, ValueError(123))) -try: - print(next(g)) -except TypeError: - print("got TypeError from downstream!") - # thrown value is caught and then generator returns normally def gen(): try: diff --git a/tests/basics/gen_yield_from_throw_multi_arg.py b/tests/basics/gen_yield_from_throw_multi_arg.py new file mode 100644 index 000000000000..86d580a6d533 --- /dev/null +++ b/tests/basics/gen_yield_from_throw_multi_arg.py @@ -0,0 +1,34 @@ +# Test generator .throw() with multiple arguments. +# Using multiple arguments is deprecated since CPython 3.12. + + +def gen(): + try: + yield 1 + except ValueError as e: + print("got ValueError from upstream!", repr(e.args)) + yield "str1" + raise TypeError + + +def gen2(): + print((yield from gen())) + + +# Passing None as second argument to throw. +g = gen2() +print(next(g)) +print(g.throw(ValueError, None)) +try: + print(next(g)) +except TypeError: + print("got TypeError from downstream!") + +# Passing an exception instance as second argument to throw. +g = gen2() +print(next(g)) +print(g.throw(ValueError, ValueError(123))) +try: + print(next(g)) +except TypeError: + print("got TypeError from downstream!") diff --git a/tests/basics/gen_yield_from_throw_multi_arg.py.exp b/tests/basics/gen_yield_from_throw_multi_arg.py.exp new file mode 100644 index 000000000000..218e332e5017 --- /dev/null +++ b/tests/basics/gen_yield_from_throw_multi_arg.py.exp @@ -0,0 +1,8 @@ +1 +got ValueError from upstream! () +str1 +got TypeError from downstream! +1 +got ValueError from upstream! (123,) +str1 +got TypeError from downstream! diff --git a/tests/basics/gen_yield_from_throw_repeat.py b/tests/basics/gen_yield_from_throw_repeat.py new file mode 100644 index 000000000000..96636a624432 --- /dev/null +++ b/tests/basics/gen_yield_from_throw_repeat.py @@ -0,0 +1,22 @@ +# Test throwing repeatedly into the same generator, where that generator +# is yielding from another generator. + + +def yielder(): + yield 4 + yield 5 + + +def gen(): + while True: + try: + print("gen received:", (yield from yielder())) + except ValueError as exc: + print(repr(exc)) + + +g = gen() +for i in range(2): + print("send, got:", g.send(None)) + print("throw, got:", g.throw(ValueError("a", i))) + print("throw, got:", g.throw(ValueError("b", i))) diff --git a/tests/basics/generator_throw.py b/tests/basics/generator_throw.py index 067ab2b8eb2a..536f50082364 100644 --- a/tests/basics/generator_throw.py +++ b/tests/basics/generator_throw.py @@ -41,13 +41,3 @@ def gen(): g = gen() print(next(g)) print(g.throw(GeneratorExit())) - -# thrown an instance with None as second arg -g = gen() -print(next(g)) -print(g.throw(GeneratorExit(), None)) - -# thrown a class and instance -g = gen() -print(next(g)) -print(g.throw(GeneratorExit, GeneratorExit(123))) diff --git a/tests/basics/generator_throw_multi_arg.py b/tests/basics/generator_throw_multi_arg.py new file mode 100644 index 000000000000..acdb6a06c96d --- /dev/null +++ b/tests/basics/generator_throw_multi_arg.py @@ -0,0 +1,21 @@ +# Test generator .throw() with multiple arguments. +# Using multiple arguments is deprecated since CPython 3.12. + +# Generator ignores a thrown GeneratorExit (this is allowed). +def gen(): + try: + yield 123 + except GeneratorExit as e: + print("GeneratorExit", repr(e.args)) + yield 456 + + +# Thrown an instance with None as second arg. +g = gen() +print(next(g)) +print(g.throw(GeneratorExit(), None)) + +# Thrown a class and instance. +g = gen() +print(next(g)) +print(g.throw(GeneratorExit, GeneratorExit(123))) diff --git a/tests/basics/generator_throw_multi_arg.py.exp b/tests/basics/generator_throw_multi_arg.py.exp new file mode 100644 index 000000000000..6e976eab1db2 --- /dev/null +++ b/tests/basics/generator_throw_multi_arg.py.exp @@ -0,0 +1,6 @@ +123 +GeneratorExit () +456 +123 +GeneratorExit (123,) +456 diff --git a/tests/basics/generator_throw_repeat.py b/tests/basics/generator_throw_repeat.py new file mode 100644 index 000000000000..6d6ef60a9b0d --- /dev/null +++ b/tests/basics/generator_throw_repeat.py @@ -0,0 +1,16 @@ +# Test throwing repeatedly into the same generator. + + +def gen(): + while True: + try: + print("gen received:", (yield "value")) + except ValueError as exc: + print(repr(exc)) + + +g = gen() +for i in range(2): + print("send, got:", g.send(None)) + print("throw, got:", g.throw(ValueError("a", i))) + print("throw, got:", g.throw(ValueError("b", i))) diff --git a/tests/basics/int_big_zeroone.py b/tests/basics/int_big_zeroone.py index e97369a2db67..24ff3809fe47 100644 --- a/tests/basics/int_big_zeroone.py +++ b/tests/basics/int_big_zeroone.py @@ -1,5 +1,6 @@ # test [0,1,-1] edge cases of bignum +# CIRCUITPY-CHANGE import skip_if skip_if.no_bigint() diff --git a/tests/basics/int_bytes.py b/tests/basics/int_bytes.py index d42afac1fd4e..3840aa8e4f00 100644 --- a/tests/basics/int_bytes.py +++ b/tests/basics/int_bytes.py @@ -1,3 +1,4 @@ +# CIRCUITPY-CHANGE: signed support print((10).to_bytes(1, "little")) print((-10).to_bytes(1, "little", signed=True)) # Test fitting in length that's not a power of two. @@ -25,6 +26,7 @@ except ValueError: print("ValueError") +# CIRCUITPY-CHANGE: more tests # too small buffer should raise an error try: (256).to_bytes(1, "little") diff --git a/tests/basics/int_bytes_intbig.py b/tests/basics/int_bytes_intbig.py index a9f069902462..e78f55de01fb 100644 --- a/tests/basics/int_bytes_intbig.py +++ b/tests/basics/int_bytes_intbig.py @@ -1,6 +1,8 @@ +# CIRCUITPY-CHANGE import skip_if skip_if.no_bigint() +# CIRCUITPY-CHANGE: signed support print((2**64).to_bytes(9, "little")) print((-2**64).to_bytes(9, "little", signed=True)) print((2**64).to_bytes(9, "big")) @@ -18,6 +20,7 @@ # check that extra zero bytes don't change the internal int value print(int.from_bytes(b + bytes(10), "little") == int.from_bytes(b, "little")) +# CIRCUITPY-CHANGE: more tests # too small buffer should raise an error try: (2**64).to_bytes(8, "little") diff --git a/tests/basics/ordereddict1.py b/tests/basics/ordereddict1.py index b70d7ff5d1ba..153e2a5625fe 100644 --- a/tests/basics/ordereddict1.py +++ b/tests/basics/ordereddict1.py @@ -42,6 +42,7 @@ except: print('empty') +# CIRCUITPY-CHANGE: more tests # fromkeys returns the correct type and order d = dict.fromkeys('abcdefghij') print(type(d) == dict) diff --git a/tests/basics/self_type_check.py b/tests/basics/self_type_check.py index 206678e25d1b..3bd5b3eed605 100644 --- a/tests/basics/self_type_check.py +++ b/tests/basics/self_type_check.py @@ -1,4 +1,5 @@ # make sure type of first arg (self) to a builtin method is checked +# CIRCUITPY-CHANGE import skip_if skip_if.board_in("gemma_m0", "trinket_m0") diff --git a/tests/basics/slice_indices.py b/tests/basics/slice_indices.py index b7f439ccca26..10dcafcfaaa0 100644 --- a/tests/basics/slice_indices.py +++ b/tests/basics/slice_indices.py @@ -25,3 +25,9 @@ def __getitem__(self, idx): print(A()[2:7:-2].indices(5)) print(A()[7:2:2].indices(5)) print(A()[7:2:-2].indices(5)) + +# CIRCUITPY-CHANGE +try: + print(A()[::].indices(None)) +except TypeError: + print("TypeError") diff --git a/tests/basics/slice_op.py.exp b/tests/basics/slice_op.py.exp new file mode 100644 index 000000000000..6002b71c56ea --- /dev/null +++ b/tests/basics/slice_op.py.exp @@ -0,0 +1 @@ +TypeError diff --git a/tests/basics/string1.py b/tests/basics/string1.py index b3abfb9c608b..ca8dfd5399a5 100644 --- a/tests/basics/string1.py +++ b/tests/basics/string1.py @@ -5,7 +5,6 @@ print(r'abc') print(u'abc') print(repr('\a\b\t\n\v\f\r')) -print('\z') # unrecognised escape char # construction print(str()) diff --git a/tests/basics/string_escape_invalid.py b/tests/basics/string_escape_invalid.py new file mode 100644 index 000000000000..d9fdd6e54658 --- /dev/null +++ b/tests/basics/string_escape_invalid.py @@ -0,0 +1,4 @@ +# Test invalid escape characters. +# CPython issues a SyntaxWarning for this. + +print("\z") diff --git a/tests/basics/string_escape_invalid.py.exp b/tests/basics/string_escape_invalid.py.exp new file mode 100644 index 000000000000..c642929c6e84 --- /dev/null +++ b/tests/basics/string_escape_invalid.py.exp @@ -0,0 +1 @@ +\z diff --git a/tests/basics/string_fstring.py b/tests/basics/string_fstring.py index 1a3960680e4f..42d093b37b50 100644 --- a/tests/basics/string_fstring.py +++ b/tests/basics/string_fstring.py @@ -29,21 +29,6 @@ def foo(a, b): # Nested '{' and '}' characters. print(f"a{ {0,1,2}}") -# PEP-0498 specifies that '\\' and '#' must be disallowed explicitly, whereas -# MicroPython relies on the syntax error as a result of the substitution. - -print(f"\\") -print(f'#') -try: - eval("f'{\}'") -except SyntaxError: - print('SyntaxError') -try: - eval("f'{#}'") -except SyntaxError: - print('SyntaxError') - - # PEP-0498 specifies that handling of double braces '{{' or '}}' should # behave like str.format. print(f'{{}}') diff --git a/tests/basics/string_fstring_invalid.py b/tests/basics/string_fstring_invalid.py new file mode 100644 index 000000000000..6fce323c5bce --- /dev/null +++ b/tests/basics/string_fstring_invalid.py @@ -0,0 +1,13 @@ +# PEP-0498 specifies that '\\' and '#' must be disallowed explicitly, whereas +# MicroPython relies on the syntax error as a result of the substitution. + +print(f"\\") +print(f"#") +try: + eval("f'{\}'") +except SyntaxError: + print("SyntaxError") +try: + eval("f'{#}'") +except SyntaxError: + print("SyntaxError") diff --git a/tests/basics/string_fstring_invalid.py.exp b/tests/basics/string_fstring_invalid.py.exp new file mode 100644 index 000000000000..bcb7f259e556 --- /dev/null +++ b/tests/basics/string_fstring_invalid.py.exp @@ -0,0 +1,4 @@ +\ +# +SyntaxError +SyntaxError diff --git a/tests/basics/struct1.py b/tests/basics/struct1.py index 39d1bef05cff..d989f1855389 100644 --- a/tests/basics/struct1.py +++ b/tests/basics/struct1.py @@ -45,6 +45,7 @@ # network byte order print(struct.pack('!i', 123)) +# CIRCUITPY-CHANGE: more tests # too short / too long arguments buf = bytearray(b'>>>123<<<') try: diff --git a/tests/basics/sys1.py b/tests/basics/sys1.py index c007b5ebdf71..6266440e353c 100644 --- a/tests/basics/sys1.py +++ b/tests/basics/sys1.py @@ -24,3 +24,18 @@ else: # Effectively skip subtests print(int) + +try: + print(sys.intern('micropython') == 'micropython') + has_intern = True +except AttributeError: + has_intern = False + print(True) + +if has_intern: + try: + print(sys.intern(0)) + except TypeError: + print(True) +else: + print(True) diff --git a/tests/basics/try_finally_return.py b/tests/basics/try_finally_return.py index 31a507e8d075..21e01ea21bf2 100644 --- a/tests/basics/try_finally_return.py +++ b/tests/basics/try_finally_return.py @@ -70,3 +70,13 @@ def f(): finally: print('finally 1') print(f()) + +# the finally block uses a lot of Python stack and then a local is accessed +# (tests that the use of the stack doesn't clobber the local) +def f(x): + try: + return x + finally: + print(2, 3, 4, 5, 6) + print(x) +print(f(1)) diff --git a/tests/cmdline/cmd_parsetree.py.exp b/tests/cmdline/cmd_parsetree.py.exp index 3049267c0b95..6ec553b8a9ae 100644 --- a/tests/cmdline/cmd_parsetree.py.exp +++ b/tests/cmdline/cmd_parsetree.py.exp @@ -1,5 +1,5 @@ ---------------- -[ 4] \(rule\|file_input_2\)(1) (n=10) +[ 1] file_input_2(1) (n=10) tok(6) [ 4] \(rule\|for_stmt\)(22) (n=4) id(i) diff --git a/tests/extmod/asyncio_as_uasyncio.py b/tests/extmod/asyncio_as_uasyncio.py index 612292299c17..b021980590cd 100644 --- a/tests/extmod/asyncio_as_uasyncio.py +++ b/tests/extmod/asyncio_as_uasyncio.py @@ -1,12 +1,33 @@ try: import uasyncio - import asyncio except ImportError: print("SKIP") raise SystemExit -x = set(dir(uasyncio)) -y = set(dir(asyncio)) - set(["event", "lock", "stream", "funcs"]) -print(x - y) -print(y - x) +# Sample of public symbols we expect to see from `asyncio`. Verify they're all +# available on `uasyncio`. +expected = [ + "CancelledError", + "create_task", + "current_task", + "Event", + "gather", + "get_event_loop", + "Lock", + "Loop", + "open_connection", + "run", + "run_until_complete", + "sleep", + "sleep_ms", + "start_server", + "StreamReader", + "StreamWriter", + "Task", + "ThreadSafeFlag", + "wait_for", +] + +for e in expected: + getattr(uasyncio, e) diff --git a/tests/extmod/asyncio_as_uasyncio.py.exp b/tests/extmod/asyncio_as_uasyncio.py.exp index 9405b8010912..e69de29bb2d1 100644 --- a/tests/extmod/asyncio_as_uasyncio.py.exp +++ b/tests/extmod/asyncio_as_uasyncio.py.exp @@ -1,2 +0,0 @@ -set() -set() diff --git a/tests/extmod/asyncio_await_return.py b/tests/extmod/asyncio_await_return.py index 8042b5488de1..be6b3f4f11b0 100644 --- a/tests/extmod/asyncio_await_return.py +++ b/tests/extmod/asyncio_await_return.py @@ -11,6 +11,7 @@ async def foo(): return 42 +# CIRCUITPY-CHANGE: await try: foo().__await__ except AttributeError: diff --git a/tests/extmod/asyncio_gather_finished_early.py b/tests/extmod/asyncio_gather_finished_early.py new file mode 100644 index 000000000000..030e79e3571b --- /dev/null +++ b/tests/extmod/asyncio_gather_finished_early.py @@ -0,0 +1,65 @@ +# Test asyncio.gather() when a task is already finished before the gather starts. + +try: + import asyncio +except ImportError: + print("SKIP") + raise SystemExit + + +# CPython and MicroPython differ in when they signal (and print) that a task raised an +# uncaught exception. So define an empty custom_handler() to suppress this output. +def custom_handler(loop, context): + pass + + +async def task_that_finishes_early(id, event, fail): + print("task_that_finishes_early", id) + event.set() + if fail: + raise ValueError("intentional exception", id) + + +async def task_that_runs(): + for i in range(5): + print("task_that_runs", i) + await asyncio.sleep(0) + + +async def main(start_task_that_runs, task_fail, return_exceptions): + print("== start", start_task_that_runs, task_fail, return_exceptions) + + # Set exception handler to suppress exception output. + loop = asyncio.get_event_loop() + loop.set_exception_handler(custom_handler) + + # Create tasks. + event_a = asyncio.Event() + event_b = asyncio.Event() + tasks = [] + if start_task_that_runs: + tasks.append(asyncio.create_task(task_that_runs())) + tasks.append(asyncio.create_task(task_that_finishes_early("a", event_a, task_fail))) + tasks.append(asyncio.create_task(task_that_finishes_early("b", event_b, task_fail))) + + # Make sure task_that_finishes_early() are both done, before calling gather(). + await event_a.wait() + await event_b.wait() + + # Gather the tasks. + try: + result = "complete", await asyncio.gather(*tasks, return_exceptions=return_exceptions) + except Exception as er: + result = "exception", er, start_task_that_runs and tasks[0].done() + + # Wait for the final task to finish (if it was started). + if start_task_that_runs: + await tasks[0] + + # Print results. + print(result) + + +# Run the test in the 8 different combinations of its arguments. +for i in range(8): + asyncio.run(main(bool(i & 4), bool(i & 2), bool(i & 1))) diff --git a/tests/extmod/asyncio_get_event_loop.py b/tests/extmod/asyncio_get_event_loop.py index b2d50e3cba7a..6ecbb13b57a6 100644 --- a/tests/extmod/asyncio_get_event_loop.py +++ b/tests/extmod/asyncio_get_event_loop.py @@ -6,6 +6,11 @@ print("SKIP") raise SystemExit +# CPython 3.12 deprecated calling get_event_loop() when there is no current event +# loop, so to make this test run on CPython requires setting the event loop. +if hasattr(asyncio, "set_event_loop"): + asyncio.set_event_loop(asyncio.new_event_loop()) + async def main(): print("start") diff --git a/tests/extmod/binascii_a2b_base64.py b/tests/extmod/binascii_a2b_base64.py index f29330b6fccc..29d6233d2d1d 100644 --- a/tests/extmod/binascii_a2b_base64.py +++ b/tests/extmod/binascii_a2b_base64.py @@ -25,6 +25,7 @@ print(binascii.a2b_base64(b"Zm9v===")) print(binascii.a2b_base64(b"Zm9v===YmFy")) +# CIRCUITPY-CHANGE # Unicode strings can be decoded print(binascii.a2b_base64("Zm9v===YmFy")) diff --git a/tests/extmod/binascii_b2a_base64.py b/tests/extmod/binascii_b2a_base64.py index a6072d50ec66..d5e82628832e 100644 --- a/tests/extmod/binascii_b2a_base64.py +++ b/tests/extmod/binascii_b2a_base64.py @@ -17,6 +17,7 @@ print(binascii.b2a_base64(b"\x7f\x80\xff")) print(binascii.b2a_base64(b"1234ABCDabcd")) print(binascii.b2a_base64(b"\x00\x00>")) # convert into '+' +# CIRCUITPY-CHANGE try: print(binascii.b2a_base64("")) except TypeError: diff --git a/tests/extmod/binascii_crc32.py b/tests/extmod/binascii_crc32.py index b2f73d947528..532d26d4061f 100644 --- a/tests/extmod/binascii_crc32.py +++ b/tests/extmod/binascii_crc32.py @@ -19,6 +19,7 @@ print(hex(binascii.crc32(b"\x00" * 16, binascii.crc32(b"\x00" * 16)))) print(hex(binascii.crc32(b"\xff" * 16, binascii.crc32(b"\xff" * 16)))) print(hex(binascii.crc32(bytes(range(16, 32)), binascii.crc32(bytes(range(16)))))) +# CIRCUITPY-CHANGE try: binascii.crc32("") except TypeError: diff --git a/tests/extmod/binascii_unhexlify.py b/tests/extmod/binascii_unhexlify.py index 866fca7345dd..731b6a2bd4d1 100644 --- a/tests/extmod/binascii_unhexlify.py +++ b/tests/extmod/binascii_unhexlify.py @@ -12,6 +12,7 @@ ): print(binascii.unhexlify(x)) +# CIRCUITPY-CHANGE # Unicode strings can be decoded print(binascii.unhexlify("313233344142434461626364")) diff --git a/tests/extmod/deflate_compress.py b/tests/extmod/deflate_compress.py index 612af663e4fd..981a986cbf53 100644 --- a/tests/extmod/deflate_compress.py +++ b/tests/extmod/deflate_compress.py @@ -146,3 +146,9 @@ def check_header(n, a, b): next_len = len(result) print(next_len < prev_len and decompress(result, deflate.RAW, wbits) == buf) prev_len = next_len + +# Verify that compression is optimal: in the bytes below, the final "123" should be +# compressed by referencing the "123" just before it, and not the one all the way back +# at the start of the bytes. +compressed = compress(b"1234567890abcdefghijklmnopqrstuvwxyz123123", deflate.RAW) +print(len(compressed), compressed) diff --git a/tests/extmod/deflate_compress.py.exp b/tests/extmod/deflate_compress.py.exp index 5da70f491bde..7b00fa3cb29d 100644 --- a/tests/extmod/deflate_compress.py.exp +++ b/tests/extmod/deflate_compress.py.exp @@ -39,3 +39,4 @@ True True True True +41 b'3426153\xb7\xb04HLJNIMK\xcf\xc8\xcc\xca\xce\xc9\xcd\xcb/(,*.)-+\xaf\xa8\xac\x02\xaa\x01"\x00' diff --git a/tests/extmod/framebuf_ellipse.py b/tests/extmod/framebuf_ellipse.py deleted file mode 100644 index a4c784aff876..000000000000 --- a/tests/extmod/framebuf_ellipse.py +++ /dev/null @@ -1,65 +0,0 @@ -try: - import framebuf -except ImportError: - print("SKIP") - raise SystemExit - - -def printbuf(): - print("--8<--") - for y in range(h): - for x in range(w): - print("%02x" % buf[(x + y * w)], end="") - print() - print("-->8--") - - -w = 30 -h = 30 -buf = bytearray(w * h) -fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.GS8) - -# Outline -fbuf.fill(0) -fbuf.ellipse(15, 15, 12, 6, 0xFF, False) -printbuf() - -# Fill -fbuf.fill(0) -fbuf.ellipse(15, 15, 6, 12, 0xAA, True) -printbuf() - -# Outline and fill some different quadrant combos. -for m in (0, 0b0001, 0b0010, 0b0100, 0b1000, 0b1010): - fbuf.fill(0) - fbuf.ellipse(15, 15, 6, 12, 0xAA, False, m) - printbuf() - fbuf.fill(0) - fbuf.ellipse(15, 15, 6, 12, 0xAA, True, m) - printbuf() - -# Draw ellipses that will go out of bounds at each of the edges. -for x, y in ( - ( - 4, - 4, - ), - ( - 26, - 4, - ), - ( - 26, - 26, - ), - ( - 4, - 26, - ), -): - fbuf.fill(0) - fbuf.ellipse(x, y, 6, 12, 0xAA, False) - printbuf() - fbuf.fill(0) - fbuf.ellipse(x, y, 6, 12, 0xAA, True) - printbuf() diff --git a/tests/extmod/framebuf_ellipse.py.exp b/tests/extmod/framebuf_ellipse.py.exp deleted file mode 100644 index ae6ad1ee7e48..000000000000 --- a/tests/extmod/framebuf_ellipse.py.exp +++ /dev/null @@ -1,704 +0,0 @@ ---8<-- -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000ffffffffffffffffff00000000000000000000 -0000000000000000ffffff000000000000000000ffffff00000000000000 -000000000000ffff000000000000000000000000000000ffff0000000000 -0000000000ff00000000000000000000000000000000000000ff00000000 -00000000ff000000000000000000000000000000000000000000ff000000 -000000ff0000000000000000000000000000000000000000000000ff0000 -000000ff0000000000000000000000000000000000000000000000ff0000 -000000ff0000000000000000000000000000000000000000000000ff0000 -00000000ff000000000000000000000000000000000000000000ff000000 -0000000000ff00000000000000000000000000000000000000ff00000000 -000000000000ffff000000000000000000000000000000ffff0000000000 -0000000000000000ffffff000000000000000000ffffff00000000000000 -0000000000000000000000ffffffffffffffffff00000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 --->8-- ---8<-- -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000aaaaaa00000000000000000000000000 -00000000000000000000000000aaaaaaaaaa000000000000000000000000 -000000000000000000000000aaaaaaaaaaaaaa0000000000000000000000 -0000000000000000000000aaaaaaaaaaaaaaaaaa00000000000000000000 -0000000000000000000000aaaaaaaaaaaaaaaaaa00000000000000000000 -00000000000000000000aaaaaaaaaaaaaaaaaaaaaa000000000000000000 -00000000000000000000aaaaaaaaaaaaaaaaaaaaaa000000000000000000 -00000000000000000000aaaaaaaaaaaaaaaaaaaaaa000000000000000000 -000000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaa0000000000000000 -000000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaa0000000000000000 -000000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaa0000000000000000 -000000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaa0000000000000000 -000000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaa0000000000000000 -000000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaa0000000000000000 -000000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaa0000000000000000 -000000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaa0000000000000000 -000000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaa0000000000000000 -00000000000000000000aaaaaaaaaaaaaaaaaaaaaa000000000000000000 -00000000000000000000aaaaaaaaaaaaaaaaaaaaaa000000000000000000 -00000000000000000000aaaaaaaaaaaaaaaaaaaaaa000000000000000000 -0000000000000000000000aaaaaaaaaaaaaaaaaa00000000000000000000 -0000000000000000000000aaaaaaaaaaaaaaaaaa00000000000000000000 -000000000000000000000000aaaaaaaaaaaaaa0000000000000000000000 -00000000000000000000000000aaaaaaaaaa000000000000000000000000 -0000000000000000000000000000aaaaaa00000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 --->8-- ---8<-- -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 --->8-- ---8<-- -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 --->8-- ---8<-- -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000aaaa00000000000000000000000000 -0000000000000000000000000000000000aa000000000000000000000000 -000000000000000000000000000000000000aa0000000000000000000000 -00000000000000000000000000000000000000aa00000000000000000000 -00000000000000000000000000000000000000aa00000000000000000000 -0000000000000000000000000000000000000000aa000000000000000000 -0000000000000000000000000000000000000000aa000000000000000000 -0000000000000000000000000000000000000000aa000000000000000000 -000000000000000000000000000000000000000000aa0000000000000000 -000000000000000000000000000000000000000000aa0000000000000000 -000000000000000000000000000000000000000000aa0000000000000000 -000000000000000000000000000000000000000000aa0000000000000000 -000000000000000000000000000000000000000000aa0000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 --->8-- ---8<-- -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000aaaa00000000000000000000000000 -000000000000000000000000000000aaaaaa000000000000000000000000 -000000000000000000000000000000aaaaaaaa0000000000000000000000 -000000000000000000000000000000aaaaaaaaaa00000000000000000000 -000000000000000000000000000000aaaaaaaaaa00000000000000000000 -000000000000000000000000000000aaaaaaaaaaaa000000000000000000 -000000000000000000000000000000aaaaaaaaaaaa000000000000000000 -000000000000000000000000000000aaaaaaaaaaaa000000000000000000 -000000000000000000000000000000aaaaaaaaaaaaaa0000000000000000 -000000000000000000000000000000aaaaaaaaaaaaaa0000000000000000 -000000000000000000000000000000aaaaaaaaaaaaaa0000000000000000 -000000000000000000000000000000aaaaaaaaaaaaaa0000000000000000 -000000000000000000000000000000aaaaaaaaaaaaaa0000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 --->8-- ---8<-- -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000aaaa0000000000000000000000000000 -00000000000000000000000000aa00000000000000000000000000000000 -000000000000000000000000aa0000000000000000000000000000000000 -0000000000000000000000aa000000000000000000000000000000000000 -0000000000000000000000aa000000000000000000000000000000000000 -00000000000000000000aa00000000000000000000000000000000000000 -00000000000000000000aa00000000000000000000000000000000000000 -00000000000000000000aa00000000000000000000000000000000000000 -000000000000000000aa0000000000000000000000000000000000000000 -000000000000000000aa0000000000000000000000000000000000000000 -000000000000000000aa0000000000000000000000000000000000000000 -000000000000000000aa0000000000000000000000000000000000000000 -000000000000000000aa0000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 --->8-- ---8<-- -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000aaaa0000000000000000000000000000 -00000000000000000000000000aaaaaa0000000000000000000000000000 -000000000000000000000000aaaaaaaa0000000000000000000000000000 -0000000000000000000000aaaaaaaaaa0000000000000000000000000000 -0000000000000000000000aaaaaaaaaa0000000000000000000000000000 -00000000000000000000aaaaaaaaaaaa0000000000000000000000000000 -00000000000000000000aaaaaaaaaaaa0000000000000000000000000000 -00000000000000000000aaaaaaaaaaaa0000000000000000000000000000 -000000000000000000aaaaaaaaaaaaaa0000000000000000000000000000 -000000000000000000aaaaaaaaaaaaaa0000000000000000000000000000 -000000000000000000aaaaaaaaaaaaaa0000000000000000000000000000 -000000000000000000aaaaaaaaaaaaaa0000000000000000000000000000 -000000000000000000aaaaaaaaaaaaaa0000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 --->8-- ---8<-- -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000aa0000000000000000000000000000000000000000 -000000000000000000aa0000000000000000000000000000000000000000 -000000000000000000aa0000000000000000000000000000000000000000 -000000000000000000aa0000000000000000000000000000000000000000 -000000000000000000aa0000000000000000000000000000000000000000 -00000000000000000000aa00000000000000000000000000000000000000 -00000000000000000000aa00000000000000000000000000000000000000 -00000000000000000000aa00000000000000000000000000000000000000 -0000000000000000000000aa000000000000000000000000000000000000 -0000000000000000000000aa000000000000000000000000000000000000 -000000000000000000000000aa0000000000000000000000000000000000 -00000000000000000000000000aa00000000000000000000000000000000 -0000000000000000000000000000aaaa0000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 --->8-- ---8<-- -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000aaaaaaaaaaaaaa0000000000000000000000000000 -000000000000000000aaaaaaaaaaaaaa0000000000000000000000000000 -000000000000000000aaaaaaaaaaaaaa0000000000000000000000000000 -000000000000000000aaaaaaaaaaaaaa0000000000000000000000000000 -000000000000000000aaaaaaaaaaaaaa0000000000000000000000000000 -00000000000000000000aaaaaaaaaaaa0000000000000000000000000000 -00000000000000000000aaaaaaaaaaaa0000000000000000000000000000 -00000000000000000000aaaaaaaaaaaa0000000000000000000000000000 -0000000000000000000000aaaaaaaaaa0000000000000000000000000000 -0000000000000000000000aaaaaaaaaa0000000000000000000000000000 -000000000000000000000000aaaaaaaa0000000000000000000000000000 -00000000000000000000000000aaaaaa0000000000000000000000000000 -0000000000000000000000000000aaaa0000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 --->8-- ---8<-- -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000aa0000000000000000 -000000000000000000000000000000000000000000aa0000000000000000 -000000000000000000000000000000000000000000aa0000000000000000 -000000000000000000000000000000000000000000aa0000000000000000 -000000000000000000000000000000000000000000aa0000000000000000 -0000000000000000000000000000000000000000aa000000000000000000 -0000000000000000000000000000000000000000aa000000000000000000 -0000000000000000000000000000000000000000aa000000000000000000 -00000000000000000000000000000000000000aa00000000000000000000 -00000000000000000000000000000000000000aa00000000000000000000 -000000000000000000000000000000000000aa0000000000000000000000 -0000000000000000000000000000000000aa000000000000000000000000 -000000000000000000000000000000aaaa00000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 --->8-- ---8<-- -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000aaaaaaaaaaaaaa0000000000000000 -000000000000000000000000000000aaaaaaaaaaaaaa0000000000000000 -000000000000000000000000000000aaaaaaaaaaaaaa0000000000000000 -000000000000000000000000000000aaaaaaaaaaaaaa0000000000000000 -000000000000000000000000000000aaaaaaaaaaaaaa0000000000000000 -000000000000000000000000000000aaaaaaaaaaaa000000000000000000 -000000000000000000000000000000aaaaaaaaaaaa000000000000000000 -000000000000000000000000000000aaaaaaaaaaaa000000000000000000 -000000000000000000000000000000aaaaaaaaaa00000000000000000000 -000000000000000000000000000000aaaaaaaaaa00000000000000000000 -000000000000000000000000000000aaaaaaaa0000000000000000000000 -000000000000000000000000000000aaaaaa000000000000000000000000 -000000000000000000000000000000aaaa00000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 --->8-- ---8<-- -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000aaaa0000000000000000000000000000 -00000000000000000000000000aa00000000000000000000000000000000 -000000000000000000000000aa0000000000000000000000000000000000 -0000000000000000000000aa000000000000000000000000000000000000 -0000000000000000000000aa000000000000000000000000000000000000 -00000000000000000000aa00000000000000000000000000000000000000 -00000000000000000000aa00000000000000000000000000000000000000 -00000000000000000000aa00000000000000000000000000000000000000 -000000000000000000aa0000000000000000000000000000000000000000 -000000000000000000aa0000000000000000000000000000000000000000 -000000000000000000aa0000000000000000000000000000000000000000 -000000000000000000aa0000000000000000000000000000000000000000 -000000000000000000aa0000000000000000000000aa0000000000000000 -000000000000000000000000000000000000000000aa0000000000000000 -000000000000000000000000000000000000000000aa0000000000000000 -000000000000000000000000000000000000000000aa0000000000000000 -000000000000000000000000000000000000000000aa0000000000000000 -0000000000000000000000000000000000000000aa000000000000000000 -0000000000000000000000000000000000000000aa000000000000000000 -0000000000000000000000000000000000000000aa000000000000000000 -00000000000000000000000000000000000000aa00000000000000000000 -00000000000000000000000000000000000000aa00000000000000000000 -000000000000000000000000000000000000aa0000000000000000000000 -0000000000000000000000000000000000aa000000000000000000000000 -000000000000000000000000000000aaaa00000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 --->8-- ---8<-- -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000aaaa0000000000000000000000000000 -00000000000000000000000000aaaaaa0000000000000000000000000000 -000000000000000000000000aaaaaaaa0000000000000000000000000000 -0000000000000000000000aaaaaaaaaa0000000000000000000000000000 -0000000000000000000000aaaaaaaaaa0000000000000000000000000000 -00000000000000000000aaaaaaaaaaaa0000000000000000000000000000 -00000000000000000000aaaaaaaaaaaa0000000000000000000000000000 -00000000000000000000aaaaaaaaaaaa0000000000000000000000000000 -000000000000000000aaaaaaaaaaaaaa0000000000000000000000000000 -000000000000000000aaaaaaaaaaaaaa0000000000000000000000000000 -000000000000000000aaaaaaaaaaaaaa0000000000000000000000000000 -000000000000000000aaaaaaaaaaaaaa0000000000000000000000000000 -000000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaa0000000000000000 -000000000000000000000000000000aaaaaaaaaaaaaa0000000000000000 -000000000000000000000000000000aaaaaaaaaaaaaa0000000000000000 -000000000000000000000000000000aaaaaaaaaaaaaa0000000000000000 -000000000000000000000000000000aaaaaaaaaaaaaa0000000000000000 -000000000000000000000000000000aaaaaaaaaaaa000000000000000000 -000000000000000000000000000000aaaaaaaaaaaa000000000000000000 -000000000000000000000000000000aaaaaaaaaaaa000000000000000000 -000000000000000000000000000000aaaaaaaaaa00000000000000000000 -000000000000000000000000000000aaaaaaaaaa00000000000000000000 -000000000000000000000000000000aaaaaaaa0000000000000000000000 -000000000000000000000000000000aaaaaa000000000000000000000000 -000000000000000000000000000000aaaa00000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 --->8-- ---8<-- -00000000000000000000aa00000000000000000000000000000000000000 -00000000000000000000aa00000000000000000000000000000000000000 -00000000000000000000aa00000000000000000000000000000000000000 -00000000000000000000aa00000000000000000000000000000000000000 -00000000000000000000aa00000000000000000000000000000000000000 -00000000000000000000aa00000000000000000000000000000000000000 -00000000000000000000aa00000000000000000000000000000000000000 -00000000000000000000aa00000000000000000000000000000000000000 -00000000000000000000aa00000000000000000000000000000000000000 -000000000000000000aa0000000000000000000000000000000000000000 -000000000000000000aa0000000000000000000000000000000000000000 -000000000000000000aa0000000000000000000000000000000000000000 -aa00000000000000aa000000000000000000000000000000000000000000 -aa00000000000000aa000000000000000000000000000000000000000000 -00aa0000000000aa00000000000000000000000000000000000000000000 -0000aa000000aa0000000000000000000000000000000000000000000000 -000000aaaaaa000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 --->8-- ---8<-- -aaaaaaaaaaaaaaaaaaaaaa00000000000000000000000000000000000000 -aaaaaaaaaaaaaaaaaaaaaa00000000000000000000000000000000000000 -aaaaaaaaaaaaaaaaaaaaaa00000000000000000000000000000000000000 -aaaaaaaaaaaaaaaaaaaaaa00000000000000000000000000000000000000 -aaaaaaaaaaaaaaaaaaaaaa00000000000000000000000000000000000000 -aaaaaaaaaaaaaaaaaaaaaa00000000000000000000000000000000000000 -aaaaaaaaaaaaaaaaaaaaaa00000000000000000000000000000000000000 -aaaaaaaaaaaaaaaaaaaaaa00000000000000000000000000000000000000 -aaaaaaaaaaaaaaaaaaaaaa00000000000000000000000000000000000000 -aaaaaaaaaaaaaaaaaaaa0000000000000000000000000000000000000000 -aaaaaaaaaaaaaaaaaaaa0000000000000000000000000000000000000000 -aaaaaaaaaaaaaaaaaaaa0000000000000000000000000000000000000000 -aaaaaaaaaaaaaaaaaa000000000000000000000000000000000000000000 -aaaaaaaaaaaaaaaaaa000000000000000000000000000000000000000000 -00aaaaaaaaaaaaaa00000000000000000000000000000000000000000000 -0000aaaaaaaaaa0000000000000000000000000000000000000000000000 -000000aaaaaa000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 --->8-- ---8<-- -0000000000000000000000000000000000000000aa000000000000000000 -0000000000000000000000000000000000000000aa000000000000000000 -0000000000000000000000000000000000000000aa000000000000000000 -0000000000000000000000000000000000000000aa000000000000000000 -0000000000000000000000000000000000000000aa000000000000000000 -0000000000000000000000000000000000000000aa000000000000000000 -0000000000000000000000000000000000000000aa000000000000000000 -0000000000000000000000000000000000000000aa000000000000000000 -0000000000000000000000000000000000000000aa000000000000000000 -000000000000000000000000000000000000000000aa0000000000000000 -000000000000000000000000000000000000000000aa0000000000000000 -000000000000000000000000000000000000000000aa0000000000000000 -00000000000000000000000000000000000000000000aa00000000000000 -00000000000000000000000000000000000000000000aa00000000000000 -0000000000000000000000000000000000000000000000aa0000000000aa -000000000000000000000000000000000000000000000000aa000000aa00 -00000000000000000000000000000000000000000000000000aaaaaa0000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 --->8-- ---8<-- -0000000000000000000000000000000000000000aaaaaaaaaaaaaaaaaaaa -0000000000000000000000000000000000000000aaaaaaaaaaaaaaaaaaaa -0000000000000000000000000000000000000000aaaaaaaaaaaaaaaaaaaa -0000000000000000000000000000000000000000aaaaaaaaaaaaaaaaaaaa -0000000000000000000000000000000000000000aaaaaaaaaaaaaaaaaaaa -0000000000000000000000000000000000000000aaaaaaaaaaaaaaaaaaaa -0000000000000000000000000000000000000000aaaaaaaaaaaaaaaaaaaa -0000000000000000000000000000000000000000aaaaaaaaaaaaaaaaaaaa -0000000000000000000000000000000000000000aaaaaaaaaaaaaaaaaaaa -000000000000000000000000000000000000000000aaaaaaaaaaaaaaaaaa -000000000000000000000000000000000000000000aaaaaaaaaaaaaaaaaa -000000000000000000000000000000000000000000aaaaaaaaaaaaaaaaaa -00000000000000000000000000000000000000000000aaaaaaaaaaaaaaaa -00000000000000000000000000000000000000000000aaaaaaaaaaaaaaaa -0000000000000000000000000000000000000000000000aaaaaaaaaaaaaa -000000000000000000000000000000000000000000000000aaaaaaaaaa00 -00000000000000000000000000000000000000000000000000aaaaaa0000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 --->8-- ---8<-- -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000aaaaaa0000 -000000000000000000000000000000000000000000000000aa000000aa00 -0000000000000000000000000000000000000000000000aa0000000000aa -00000000000000000000000000000000000000000000aa00000000000000 -00000000000000000000000000000000000000000000aa00000000000000 -000000000000000000000000000000000000000000aa0000000000000000 -000000000000000000000000000000000000000000aa0000000000000000 -000000000000000000000000000000000000000000aa0000000000000000 -0000000000000000000000000000000000000000aa000000000000000000 -0000000000000000000000000000000000000000aa000000000000000000 -0000000000000000000000000000000000000000aa000000000000000000 -0000000000000000000000000000000000000000aa000000000000000000 -0000000000000000000000000000000000000000aa000000000000000000 -0000000000000000000000000000000000000000aa000000000000000000 -0000000000000000000000000000000000000000aa000000000000000000 -0000000000000000000000000000000000000000aa000000000000000000 --->8-- ---8<-- -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000aaaaaa0000 -000000000000000000000000000000000000000000000000aaaaaaaaaa00 -0000000000000000000000000000000000000000000000aaaaaaaaaaaaaa -00000000000000000000000000000000000000000000aaaaaaaaaaaaaaaa -00000000000000000000000000000000000000000000aaaaaaaaaaaaaaaa -000000000000000000000000000000000000000000aaaaaaaaaaaaaaaaaa -000000000000000000000000000000000000000000aaaaaaaaaaaaaaaaaa -000000000000000000000000000000000000000000aaaaaaaaaaaaaaaaaa -0000000000000000000000000000000000000000aaaaaaaaaaaaaaaaaaaa -0000000000000000000000000000000000000000aaaaaaaaaaaaaaaaaaaa -0000000000000000000000000000000000000000aaaaaaaaaaaaaaaaaaaa -0000000000000000000000000000000000000000aaaaaaaaaaaaaaaaaaaa -0000000000000000000000000000000000000000aaaaaaaaaaaaaaaaaaaa -0000000000000000000000000000000000000000aaaaaaaaaaaaaaaaaaaa -0000000000000000000000000000000000000000aaaaaaaaaaaaaaaaaaaa -0000000000000000000000000000000000000000aaaaaaaaaaaaaaaaaaaa --->8-- ---8<-- -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000aaaaaa000000000000000000000000000000000000000000000000 -0000aa000000aa0000000000000000000000000000000000000000000000 -00aa0000000000aa00000000000000000000000000000000000000000000 -aa00000000000000aa000000000000000000000000000000000000000000 -aa00000000000000aa000000000000000000000000000000000000000000 -000000000000000000aa0000000000000000000000000000000000000000 -000000000000000000aa0000000000000000000000000000000000000000 -000000000000000000aa0000000000000000000000000000000000000000 -00000000000000000000aa00000000000000000000000000000000000000 -00000000000000000000aa00000000000000000000000000000000000000 -00000000000000000000aa00000000000000000000000000000000000000 -00000000000000000000aa00000000000000000000000000000000000000 -00000000000000000000aa00000000000000000000000000000000000000 -00000000000000000000aa00000000000000000000000000000000000000 -00000000000000000000aa00000000000000000000000000000000000000 -00000000000000000000aa00000000000000000000000000000000000000 --->8-- ---8<-- -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000 -000000aaaaaa000000000000000000000000000000000000000000000000 -0000aaaaaaaaaa0000000000000000000000000000000000000000000000 -00aaaaaaaaaaaaaa00000000000000000000000000000000000000000000 -aaaaaaaaaaaaaaaaaa000000000000000000000000000000000000000000 -aaaaaaaaaaaaaaaaaa000000000000000000000000000000000000000000 -aaaaaaaaaaaaaaaaaaaa0000000000000000000000000000000000000000 -aaaaaaaaaaaaaaaaaaaa0000000000000000000000000000000000000000 -aaaaaaaaaaaaaaaaaaaa0000000000000000000000000000000000000000 -aaaaaaaaaaaaaaaaaaaaaa00000000000000000000000000000000000000 -aaaaaaaaaaaaaaaaaaaaaa00000000000000000000000000000000000000 -aaaaaaaaaaaaaaaaaaaaaa00000000000000000000000000000000000000 -aaaaaaaaaaaaaaaaaaaaaa00000000000000000000000000000000000000 -aaaaaaaaaaaaaaaaaaaaaa00000000000000000000000000000000000000 -aaaaaaaaaaaaaaaaaaaaaa00000000000000000000000000000000000000 -aaaaaaaaaaaaaaaaaaaaaa00000000000000000000000000000000000000 -aaaaaaaaaaaaaaaaaaaaaa00000000000000000000000000000000000000 --->8-- diff --git a/tests/extmod/framebuf_polygon.py b/tests/extmod/framebuf_polygon.py deleted file mode 100644 index 03130b3bf0fa..000000000000 --- a/tests/extmod/framebuf_polygon.py +++ /dev/null @@ -1,222 +0,0 @@ -import sys - -try: - import framebuf - from array import array -except ImportError: - print("SKIP") - raise SystemExit - - -# TODO: poly needs functions that aren't in dynruntime.h yet. -if not hasattr(framebuf.FrameBuffer, "poly"): - print("SKIP") - raise SystemExit - - -def print_buffer(buffer, width, height): - for row in range(height): - for col in range(width): - val = buffer[(row * width) + col] - sys.stdout.write(" {:02x}".format(val) if val else " ··") - sys.stdout.write("\n") - - -buf = bytearray(70 * 70) - -w = 30 -h = 25 -fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.GS8) -col = 0xFF -col_fill = 0x99 - -# This describes a arbitrary polygon (this happens to be a concave polygon in -# the shape of an upper-case letter 'M'). -poly = array( - "h", - ( - 0, - 20, - 3, - 20, - 3, - 10, - 6, - 17, - 9, - 10, - 9, - 20, - 12, - 20, - 12, - 3, - 9, - 3, - 6, - 10, - 3, - 3, - 0, - 3, - ), -) -# This describes the same polygon, but the points are in reverse order -# (it shouldn't matter if the polygon has clockwise or anti-clockwise -# winding). Also defined as a bytes instead of array. -poly_reversed = bytes( - ( - 0, - 3, - 3, - 3, - 6, - 10, - 9, - 3, - 12, - 3, - 12, - 20, - 9, - 20, - 9, - 10, - 6, - 17, - 3, - 10, - 3, - 20, - 0, - 20, - ) -) - -# Draw the line polygon (at the origin) and the reversed-order polygon (offset). -fbuf.fill(0) -fbuf.poly(0, 0, poly, col) -fbuf.poly(15, -2, poly_reversed, col) -print_buffer(buf, w, h) -print() - -# Same but filled. -fbuf.fill(0) -fbuf.poly(0, 0, poly, col_fill, True) -fbuf.poly(15, -2, poly_reversed, col_fill, True) -print_buffer(buf, w, h) -print() - -# Draw the fill then the outline to ensure that no fill goes outside the outline. -fbuf.fill(0) -fbuf.poly(0, 0, poly, col_fill, True) -fbuf.poly(0, 0, poly, col) -fbuf.poly(15, -2, poly, col_fill, True) -fbuf.poly(15, -2, poly, col) -print_buffer(buf, w, h) -print() - -# Draw the outline then the fill to ensure the fill completely covers the outline. -fbuf.fill(0) -fbuf.poly(0, 0, poly, col) -fbuf.poly(0, 0, poly, col_fill, True) -fbuf.poly(15, -2, poly, col) -fbuf.poly(15, -2, poly, col_fill, True) -print_buffer(buf, w, h) -print() - -# Draw polygons that will go out of bounds at each of the edges. -for x, y in ( - ( - -8, - -8, - ), - ( - 24, - -6, - ), - ( - 20, - 12, - ), - ( - -2, - 10, - ), -): - fbuf.fill(0) - fbuf.poly(x, y, poly, col) - print_buffer(buf, w, h) - print() - fbuf.fill(0) - fbuf.poly(x, y, poly_reversed, col, True) - print_buffer(buf, w, h) - print() - -# Edge cases: These two lists describe self-intersecting polygons -poly_hourglass = array("h", (0, 0, 9, 0, 0, 19, 9, 19)) -poly_star = array("h", (7, 0, 3, 18, 14, 5, 0, 5, 11, 18)) - -# As before, fill then outline. -fbuf.fill(0) -fbuf.poly(0, 2, poly_hourglass, col_fill, True) -fbuf.poly(0, 2, poly_hourglass, col) -fbuf.poly(12, 2, poly_star, col_fill, True) -fbuf.poly(12, 2, poly_star, col) -print_buffer(buf, w, h) -print() - -# Outline then fill. -fbuf.fill(0) -fbuf.poly(0, 2, poly_hourglass, col) -fbuf.poly(0, 2, poly_hourglass, col_fill, True) -fbuf.poly(12, 2, poly_star, col) -fbuf.poly(12, 2, poly_star, col_fill, True) -print_buffer(buf, w, h) -print() - -# Edge cases: These are "degenerate" polygons. -poly_empty = array("h") # Will draw nothing at all. -poly_one = array("h", (20, 20)) # Will draw a single point. -poly_two = array("h", (10, 10, 5, 5)) # Will draw a single line. -poly_wrong_length = array("h", (2, 2, 4)) # Will round down to one point. - -fbuf.fill(0) -fbuf.poly(0, 0, poly_empty, col) -fbuf.poly(0, 0, poly_one, col) -fbuf.poly(0, 0, poly_two, col) -fbuf.poly(0, 0, poly_wrong_length, col) -print_buffer(buf, w, h) -print() - -# A shape with a horizontal overhang. -poly_overhang = array("h", (0, 0, 0, 5, 5, 5, 5, 10, 10, 10, 10, 0)) - -fbuf.fill(0) -fbuf.poly(0, 0, poly_overhang, col) -fbuf.poly(0, 0, poly_overhang, col_fill, True) -print_buffer(buf, w, h) -print() - -fbuf.fill(0) -fbuf.poly(0, 0, poly_overhang, col_fill, True) -fbuf.poly(0, 0, poly_overhang, col) -print_buffer(buf, w, h) -print() - -# Triangles -w = 70 -h = 70 -fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.GS8) -t1 = array("h", [40, 0, 20, 68, 62, 40]) -t2 = array("h", [40, 0, 0, 16, 20, 68]) - -fbuf.fill(0) -fbuf.poly(0, 0, t1, 0xFF, False) -fbuf.poly(0, 0, t2, 0xFF, False) -print_buffer(buf, w, h) - -fbuf.fill(0) -fbuf.poly(0, 0, t1, 0xFF, True) -fbuf.poly(0, 0, t2, 0xFF, True) -print_buffer(buf, w, h) diff --git a/tests/extmod/framebuf_polygon.py.exp b/tests/extmod/framebuf_polygon.py.exp deleted file mode 100644 index 9b4801c788a5..000000000000 --- a/tests/extmod/framebuf_polygon.py.exp +++ /dev/null @@ -1,582 +0,0 @@ - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ·· ·· ·· ff ff ff ff ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· - ff ff ff ff ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ff ·· ·· ·· ff ·· ·· ·· ff ·· ·· ·· ff ·· ·· - ff ·· ·· ff ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ·· ff ·· ·· ·· ff ·· ·· ·· ff ·· ·· - ff ·· ·· ·· ff ·· ·· ·· ff ·· ·· ·· ff ·· ·· ff ·· ·· ·· ·· ff ·· ff ·· ·· ·· ·· ff ·· ·· - ff ·· ·· ·· ff ·· ·· ·· ff ·· ·· ·· ff ·· ·· ff ·· ·· ·· ·· ff ·· ff ·· ·· ·· ·· ff ·· ·· - ff ·· ·· ·· ·· ff ·· ff ·· ·· ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ff ·· ·· - ff ·· ·· ·· ·· ff ·· ff ·· ·· ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· - ff ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· - ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ff ·· ·· ·· ff ff ·· ·· ff ·· ·· - ff ·· ·· ff ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ff ·· ·· ·· ff ff ·· ·· ff ·· ·· - ff ·· ·· ff ff ·· ·· ·· ff ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ff ·· ff ·· ff ·· ·· ff ·· ·· - ff ·· ·· ff ff ·· ·· ·· ff ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ff ·· ff ·· ff ·· ·· ff ·· ·· - ff ·· ·· ff ·· ff ·· ff ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· - ff ·· ·· ff ·· ff ·· ff ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· - ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· - ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· - ff ·· ·· ff ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· ff ff ff ff ·· ·· ·· ·· ·· ff ff ff ff ·· ·· - ff ·· ·· ff ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff ff ff ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 99 99 99 99 ·· ·· ·· ·· ·· 99 99 99 99 ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 99 99 99 99 ·· ·· ·· ·· ·· 99 99 99 99 ·· ·· - 99 99 99 99 ·· ·· ·· ·· ·· 99 99 99 99 ·· ·· 99 99 99 99 99 ·· ·· ·· 99 99 99 99 99 ·· ·· - 99 99 99 99 ·· ·· ·· ·· ·· 99 99 99 99 ·· ·· 99 99 99 99 99 ·· ·· ·· 99 99 99 99 99 ·· ·· - 99 99 99 99 99 ·· ·· ·· 99 99 99 99 99 ·· ·· 99 99 99 99 99 99 ·· 99 99 99 99 99 99 ·· ·· - 99 99 99 99 99 ·· ·· ·· 99 99 99 99 99 ·· ·· 99 99 99 99 99 99 ·· 99 99 99 99 99 99 ·· ·· - 99 99 99 99 99 99 ·· 99 99 99 99 99 99 ·· ·· 99 99 99 99 99 99 99 99 99 99 99 99 99 ·· ·· - 99 99 99 99 99 99 ·· 99 99 99 99 99 99 ·· ·· 99 99 99 99 99 99 99 99 99 99 99 99 99 ·· ·· - 99 99 99 99 99 99 99 99 99 99 99 99 99 ·· ·· 99 99 99 99 99 99 99 99 99 99 99 99 99 ·· ·· - 99 99 99 99 99 99 99 99 99 99 99 99 99 ·· ·· 99 99 99 99 99 99 99 99 99 99 99 99 99 ·· ·· - 99 99 99 99 99 99 99 99 99 99 99 99 99 ·· ·· 99 99 99 99 99 99 99 99 99 99 99 99 99 ·· ·· - 99 99 99 99 99 99 99 99 99 99 99 99 99 ·· ·· 99 99 99 99 ·· 99 99 99 ·· 99 99 99 99 ·· ·· - 99 99 99 99 99 99 99 99 99 99 99 99 99 ·· ·· 99 99 99 99 ·· 99 99 99 ·· 99 99 99 99 ·· ·· - 99 99 99 99 ·· 99 99 99 ·· 99 99 99 99 ·· ·· 99 99 99 99 ·· ·· 99 ·· ·· 99 99 99 99 ·· ·· - 99 99 99 99 ·· 99 99 99 ·· 99 99 99 99 ·· ·· 99 99 99 99 ·· ·· 99 ·· ·· 99 99 99 99 ·· ·· - 99 99 99 99 ·· ·· 99 ·· ·· 99 99 99 99 ·· ·· 99 99 99 99 ·· ·· ·· ·· ·· 99 99 99 99 ·· ·· - 99 99 99 99 ·· ·· 99 ·· ·· 99 99 99 99 ·· ·· 99 99 99 99 ·· ·· ·· ·· ·· 99 99 99 99 ·· ·· - 99 99 99 99 ·· ·· ·· ·· ·· 99 99 99 99 ·· ·· 99 99 99 99 ·· ·· ·· ·· ·· 99 99 99 99 ·· ·· - 99 99 99 99 ·· ·· ·· ·· ·· 99 99 99 99 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - 99 99 99 99 ·· ·· ·· ·· ·· 99 99 99 99 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ·· ·· ·· ff ff ff ff ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff 99 99 ff ·· ·· ·· ·· ·· ff 99 99 ff ·· ·· - ff ff ff ff ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ff 99 99 99 ff ·· ·· ·· ff 99 99 99 ff ·· ·· - ff 99 99 ff ·· ·· ·· ·· ·· ff 99 99 ff ·· ·· ff 99 99 99 ff ·· ·· ·· ff 99 99 99 ff ·· ·· - ff 99 99 99 ff ·· ·· ·· ff 99 99 99 ff ·· ·· ff 99 99 99 99 ff ·· ff 99 99 99 99 ff ·· ·· - ff 99 99 99 ff ·· ·· ·· ff 99 99 99 ff ·· ·· ff 99 99 99 99 ff ·· ff 99 99 99 99 ff ·· ·· - ff 99 99 99 99 ff ·· ff 99 99 99 99 ff ·· ·· ff 99 99 99 99 99 ff 99 99 99 99 99 ff ·· ·· - ff 99 99 99 99 ff ·· ff 99 99 99 99 ff ·· ·· ff 99 99 ff 99 99 ff 99 99 ff 99 99 ff ·· ·· - ff 99 99 99 99 99 ff 99 99 99 99 99 ff ·· ·· ff 99 99 ff 99 99 99 99 99 ff 99 99 ff ·· ·· - ff 99 99 ff 99 99 ff 99 99 ff 99 99 ff ·· ·· ff 99 99 ff ff 99 99 99 ff ff 99 99 ff ·· ·· - ff 99 99 ff 99 99 99 99 99 ff 99 99 ff ·· ·· ff 99 99 ff ff 99 99 99 ff ff 99 99 ff ·· ·· - ff 99 99 ff ff 99 99 99 ff ff 99 99 ff ·· ·· ff 99 99 ff ·· ff 99 ff ·· ff 99 99 ff ·· ·· - ff 99 99 ff ff 99 99 99 ff ff 99 99 ff ·· ·· ff 99 99 ff ·· ff 99 ff ·· ff 99 99 ff ·· ·· - ff 99 99 ff ·· ff 99 ff ·· ff 99 99 ff ·· ·· ff 99 99 ff ·· ·· ff ·· ·· ff 99 99 ff ·· ·· - ff 99 99 ff ·· ff 99 ff ·· ff 99 99 ff ·· ·· ff 99 99 ff ·· ·· ff ·· ·· ff 99 99 ff ·· ·· - ff 99 99 ff ·· ·· ff ·· ·· ff 99 99 ff ·· ·· ff 99 99 ff ·· ·· ·· ·· ·· ff 99 99 ff ·· ·· - ff 99 99 ff ·· ·· ff ·· ·· ff 99 99 ff ·· ·· ff 99 99 ff ·· ·· ·· ·· ·· ff 99 99 ff ·· ·· - ff 99 99 ff ·· ·· ·· ·· ·· ff 99 99 ff ·· ·· ff ff ff ff ·· ·· ·· ·· ·· ff ff ff ff ·· ·· - ff 99 99 ff ·· ·· ·· ·· ·· ff 99 99 ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff ff ff ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 99 99 99 99 ·· ·· ·· ·· ·· 99 99 99 99 ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 99 99 99 99 ·· ·· ·· ·· ·· 99 99 99 99 ·· ·· - 99 99 99 99 ·· ·· ·· ·· ·· 99 99 99 99 ·· ·· 99 99 99 99 99 ·· ·· ·· 99 99 99 99 99 ·· ·· - 99 99 99 99 ·· ·· ·· ·· ·· 99 99 99 99 ·· ·· 99 99 99 99 99 ·· ·· ·· 99 99 99 99 99 ·· ·· - 99 99 99 99 99 ·· ·· ·· 99 99 99 99 99 ·· ·· 99 99 99 99 99 99 ·· 99 99 99 99 99 99 ·· ·· - 99 99 99 99 99 ·· ·· ·· 99 99 99 99 99 ·· ·· 99 99 99 99 99 99 ·· 99 99 99 99 99 99 ·· ·· - 99 99 99 99 99 99 ·· 99 99 99 99 99 99 ·· ·· 99 99 99 99 99 99 99 99 99 99 99 99 99 ·· ·· - 99 99 99 99 99 99 ·· 99 99 99 99 99 99 ·· ·· 99 99 99 99 99 99 99 99 99 99 99 99 99 ·· ·· - 99 99 99 99 99 99 99 99 99 99 99 99 99 ·· ·· 99 99 99 99 99 99 99 99 99 99 99 99 99 ·· ·· - 99 99 99 99 99 99 99 99 99 99 99 99 99 ·· ·· 99 99 99 99 99 99 99 99 99 99 99 99 99 ·· ·· - 99 99 99 99 99 99 99 99 99 99 99 99 99 ·· ·· 99 99 99 99 99 99 99 99 99 99 99 99 99 ·· ·· - 99 99 99 99 99 99 99 99 99 99 99 99 99 ·· ·· 99 99 99 99 ·· 99 99 99 ·· 99 99 99 99 ·· ·· - 99 99 99 99 99 99 99 99 99 99 99 99 99 ·· ·· 99 99 99 99 ·· 99 99 99 ·· 99 99 99 99 ·· ·· - 99 99 99 99 ·· 99 99 99 ·· 99 99 99 99 ·· ·· 99 99 99 99 ·· ·· 99 ·· ·· 99 99 99 99 ·· ·· - 99 99 99 99 ·· 99 99 99 ·· 99 99 99 99 ·· ·· 99 99 99 99 ·· ·· 99 ·· ·· 99 99 99 99 ·· ·· - 99 99 99 99 ·· ·· 99 ·· ·· 99 99 99 99 ·· ·· 99 99 99 99 ·· ·· ·· ·· ·· 99 99 99 99 ·· ·· - 99 99 99 99 ·· ·· 99 ·· ·· 99 99 99 99 ·· ·· 99 99 99 99 ·· ·· ·· ·· ·· 99 99 99 99 ·· ·· - 99 99 99 99 ·· ·· ·· ·· ·· 99 99 99 99 ·· ·· 99 99 99 99 ·· ·· ·· ·· ·· 99 99 99 99 ·· ·· - 99 99 99 99 ·· ·· ·· ·· ·· 99 99 99 99 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - 99 99 99 99 ·· ·· ·· ·· ·· 99 99 99 99 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - - ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - - ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ff ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ff ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ff ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ·· ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ·· ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ·· ·· ·· ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ff ·· ·· ·· ff ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ff ·· ·· ·· ff ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ff ·· ff ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ff ·· ff ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ff ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ff ·· ·· ·· ff ff - - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ·· ·· ·· ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ·· ·· ·· ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ·· ·· ·· ff ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ·· ·· ·· ff ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ·· ff ff ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ·· ff ff ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff - - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ff ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ff ·· ·· ·· ff ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ff ·· ·· ·· ff ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ff ·· ff ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ff ·· ff ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ff ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ff ff ·· ·· ·· ff ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ff ff ·· ·· ·· ff ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ff ·· ff ·· ff ·· ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff ff ·· ·· ·· ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff ff ·· ·· ·· ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff ff ff ·· ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff ff ff ·· ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff ·· ff ff ff ·· ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff 99 99 99 99 99 99 99 99 ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ff 99 99 99 99 99 99 ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ff 99 99 99 99 99 99 ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ff 99 ff ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ff 99 99 99 99 ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff 99 ff ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ff 99 99 99 99 ff ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· - ·· ·· ·· ff 99 99 ff ·· ·· ·· ·· ·· ·· ff 99 99 99 99 ff ·· ff 99 99 99 99 ff ·· ·· ·· ·· - ·· ·· ·· ff 99 99 ff ·· ·· ·· ·· ·· ·· ·· ff 99 99 ff ·· ·· ·· ff 99 99 ff ·· ·· ·· ·· ·· - ·· ·· ·· ·· ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ff 99 ff ·· ·· ·· ff 99 ff ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ff 99 ff ·· ·· ·· ff 99 ff ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ·· ·· ·· ff ff ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ff 99 99 ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ff 99 ff ·· ff 99 ff ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ff 99 99 ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ff 99 99 ff 99 99 ff ·· ·· ·· ·· ·· ·· ·· - ·· ·· ff 99 99 99 99 ff ·· ·· ·· ·· ·· ·· ·· ·· ff 99 ff ·· ff 99 ff ·· ·· ·· ·· ·· ·· ·· - ·· ·· ff 99 99 99 99 ff ·· ·· ·· ·· ·· ·· ·· ·· ff 99 ff ·· ff 99 ff ·· ·· ·· ·· ·· ·· ·· - ·· ff 99 99 99 99 99 99 ff ·· ·· ·· ·· ·· ·· ff 99 ff ·· ·· ·· ff 99 ff ·· ·· ·· ·· ·· ·· - ·· ff 99 99 99 99 99 99 ff ·· ·· ·· ·· ·· ·· ff ff ·· ·· ·· ·· ·· ff ff ·· ·· ·· ·· ·· ·· - ff 99 99 99 99 99 99 99 99 ff ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· - ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - 99 99 99 99 99 99 99 99 99 99 ·· ·· ·· ·· ·· ·· ·· ·· ·· 99 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - 99 99 99 99 99 99 99 99 99 99 ·· ·· ·· ·· ·· ·· ·· ·· ·· 99 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· 99 99 99 99 99 99 99 99 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 99 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· 99 99 99 99 99 99 99 99 ·· ·· ·· ·· ·· ·· ·· ·· ·· 99 99 99 ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· 99 99 99 99 99 99 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 99 99 99 ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· 99 99 99 99 99 99 ·· ·· ·· ·· 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 ·· ·· ·· - ·· ·· ·· 99 99 99 99 ·· ·· ·· ·· ·· ·· 99 99 99 99 99 99 ·· 99 99 99 99 99 99 ·· ·· ·· ·· - ·· ·· ·· 99 99 99 99 ·· ·· ·· ·· ·· ·· ·· 99 99 99 99 ·· ·· ·· 99 99 99 99 ·· ·· ·· ·· ·· - ·· ·· ·· ·· 99 99 ·· ·· ·· ·· ·· ·· ·· ·· ·· 99 99 99 ·· ·· ·· 99 99 99 ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· 99 99 ·· ·· ·· ·· ·· ·· ·· ·· ·· 99 99 99 ·· ·· ·· 99 99 99 ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· 99 99 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 99 99 ·· ·· ·· 99 99 ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· 99 99 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· 99 ·· ·· ·· 99 ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· 99 99 99 99 ·· ·· ·· ·· ·· ·· ·· ·· ·· 99 99 99 ·· 99 99 99 ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· 99 99 99 99 ·· ·· ·· ·· ·· ·· ·· ·· ·· 99 99 99 99 99 99 99 ·· ·· ·· ·· ·· ·· ·· - ·· ·· 99 99 99 99 99 99 ·· ·· ·· ·· ·· ·· ·· ·· 99 99 99 ·· 99 99 99 ·· ·· ·· ·· ·· ·· ·· - ·· ·· 99 99 99 99 99 99 ·· ·· ·· ·· ·· ·· ·· ·· 99 99 99 ·· 99 99 99 ·· ·· ·· ·· ·· ·· ·· - ·· 99 99 99 99 99 99 99 99 ·· ·· ·· ·· ·· ·· 99 99 99 ·· ·· ·· 99 99 99 ·· ·· ·· ·· ·· ·· - ·· 99 99 99 99 99 99 99 99 ·· ·· ·· ·· ·· ·· 99 99 ·· ·· ·· ·· ·· 99 99 ·· ·· ·· ·· ·· ·· - 99 99 99 99 99 99 99 99 99 99 ·· ·· ·· ·· ·· 99 ·· ·· ·· ·· ·· ·· ·· 99 ·· ·· ·· ·· ·· ·· - 99 99 99 99 99 99 99 99 99 99 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - - 99 99 99 99 99 99 99 99 99 99 99 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - 99 99 99 99 99 99 99 99 99 99 99 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - 99 99 99 99 99 99 99 99 99 99 99 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - 99 99 99 99 99 99 99 99 99 99 99 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - 99 99 99 99 99 99 99 99 99 99 99 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - 99 99 99 99 99 99 99 99 99 99 99 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· 99 99 99 99 99 99 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· 99 99 99 99 99 99 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· 99 99 99 99 99 99 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· 99 99 99 99 99 99 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· 99 99 99 99 99 99 ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - - ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff 99 99 99 99 99 99 99 99 99 ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff 99 99 99 99 99 99 99 99 99 ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff 99 99 99 99 99 99 99 99 99 ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff 99 99 99 99 99 99 99 99 99 ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff ff ff ff ff 99 99 99 99 ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ff 99 99 99 99 ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ff 99 99 99 99 ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ff 99 99 99 99 ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ff 99 99 99 99 ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ·· ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ·· ·· ff ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ff ·· ·· ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ff ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· - ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· diff --git a/tests/extmod/framebuf_scroll.py b/tests/extmod/framebuf_scroll.py deleted file mode 100644 index db9b6ea1e964..000000000000 --- a/tests/extmod/framebuf_scroll.py +++ /dev/null @@ -1,45 +0,0 @@ -try: - import framebuf -except ImportError: - print("SKIP") - raise SystemExit - - -def printbuf(): - print("--8<--") - bytes_per_row = w // 2 - for y in range(h): - for x in range(bytes_per_row): - print("%02x" % buf[(x + y * bytes_per_row)], end="") - print() - print("-->8--") - - -w = 10 -h = 10 -buf = bytearray(w * h // 2) -fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.GS4_HMSB) - - -def prepare_buffer(): - fbuf.fill(0) - fbuf.rect(2, 0, 6, 10, 0x07, True) - fbuf.rect(0, 2, 10, 6, 0x01, True) - - -prepare_buffer() -printbuf() - -fbuf.scroll(5, -1) -printbuf() - -prepare_buffer() -fbuf.scroll(-5, 5) -printbuf() - -prepare_buffer() -# Scrolling by at least the size of buffer, no change to buffer. -fbuf.scroll(15, 7) -fbuf.scroll(10, -1) -fbuf.scroll(1, -10) -printbuf() diff --git a/tests/extmod/framebuf_scroll.py.exp b/tests/extmod/framebuf_scroll.py.exp deleted file mode 100644 index 7e99b275da7c..000000000000 --- a/tests/extmod/framebuf_scroll.py.exp +++ /dev/null @@ -1,48 +0,0 @@ ---8<-- -0077777700 -0077777700 -1111111111 -1111111111 -1111111111 -1111111111 -1111111111 -1111111111 -0077777700 -0077777700 --->8-- ---8<-- -0077700777 -0077711111 -1111111111 -1111111111 -1111111111 -1111111111 -1111111111 -1111100777 -0077700777 -0077777700 --->8-- ---8<-- -0077777700 -0077777700 -1111111111 -1111111111 -1111111111 -7770011111 -7770011111 -1111111111 -1111177700 -1111177700 --->8-- ---8<-- -0077777700 -0077777700 -1111111111 -1111111111 -1111111111 -1111111111 -1111111111 -1111111111 -0077777700 -0077777700 --->8-- diff --git a/tests/extmod/hashlib_sha256.py b/tests/extmod/hashlib_sha256.py index a0629dbc47b3..95cd301d160d 100644 --- a/tests/extmod/hashlib_sha256.py +++ b/tests/extmod/hashlib_sha256.py @@ -23,6 +23,7 @@ # 56 bytes is a boundary case in the algorithm print(hashlib.sha256(b"\xff" * 56).digest()) +# CIRCUITPY-CHANGE sha256 = hashlib.sha256(b"hello") try: sha256.update("world") diff --git a/tests/extmod/re1.py b/tests/extmod/re1.py index 32a71acc2130..0680a65889d3 100644 --- a/tests/extmod/re1.py +++ b/tests/extmod/re1.py @@ -25,6 +25,7 @@ except IndexError: print("IndexError") +# CIRCUITPY-CHANGE r = re.compile(r"\n") m = r.match("\n") print(m.group(0)) diff --git a/tests/extmod/re_namedclass.py b/tests/extmod/re_namedclass.py index 442172f4ab0c..e71256671923 100644 --- a/tests/extmod/re_namedclass.py +++ b/tests/extmod/re_namedclass.py @@ -31,6 +31,6 @@ def print_groups(match): print_groups(m) # named class within a class set -print_groups(re.match("([^\s]+)\s*([^\s]+)", "1 23")) -print_groups(re.match("([\s\d]+)([\W]+)", "1 2-+=")) -print_groups(re.match("([\W]+)([^\W]+)([^\S]+)([^\D]+)", " a_1 23")) +print_groups(re.match(r"([^\s]+)\s*([^\s]+)", "1 23")) +print_groups(re.match(r"([\s\d]+)([\W]+)", "1 2-+=")) +print_groups(re.match(r"([\W]+)([^\W]+)([^\S]+)([^\D]+)", " a_1 23")) diff --git a/tests/extmod/re_split_empty.py b/tests/extmod/re_split_empty.py index d386672308a1..bc17aa8d7836 100644 --- a/tests/extmod/re_split_empty.py +++ b/tests/extmod/re_split_empty.py @@ -1,7 +1,7 @@ # test splitting with pattern matches that can be empty # -# CPython 3.5 issues a FutreWarning for these tests because their -# behaviour will change in a futre version. MicroPython just stops +# CPython 3.5 issues a FutureWarning for these tests because their +# behaviour will change in a future version. MicroPython just stops # splitting as soon as an empty match is found. try: diff --git a/tests/extmod/re_sub.py b/tests/extmod/re_sub.py index 253b2dc48281..2c7c6c10f1a4 100644 --- a/tests/extmod/re_sub.py +++ b/tests/extmod/re_sub.py @@ -15,9 +15,9 @@ def multiply(m): return str(int(m.group(0)) * 2) -print(re.sub("\d+", multiply, "10 20 30 40 50")) +print(re.sub(r"\d+", multiply, "10 20 30 40 50")) -print(re.sub("\d+", lambda m: str(int(m.group(0)) // 2), "10 20 30 40 50")) +print(re.sub(r"\d+", lambda m: str(int(m.group(0)) // 2), "10 20 30 40 50")) def A(): @@ -73,6 +73,6 @@ def A(): # Include \ in the sub replacement print(re.sub("b", "\\\\b", "abc")) -# Using ^, make sre it doesn't repeatedly match +# Using ^, make sure it doesn't repeatedly match print(re.sub("^ab", "*", "abababcabab")) print(re.sub("^ab|cab", "*", "abababcabab")) diff --git a/tests/extmod/select_poll_fd.py b/tests/extmod/select_poll_fd.py index fab9c0e0e945..5f9dcc286a0c 100644 --- a/tests/extmod/select_poll_fd.py +++ b/tests/extmod/select_poll_fd.py @@ -34,11 +34,30 @@ # Poll for input, should return an empty list. print(poller.poll(0)) -# Test registering a very large number of file descriptors. +# Test registering a very large number of file descriptors (will trigger +# EINVAL due to more than OPEN_MAX fds). Typically it's 1024 (and on GitHub CI +# we force this via `ulimit -n 1024`). +# CIRCUITPY-CHANGE: Skip this test. poller.poll() does not have a limit and will `assert False` +# The ulimit change in the micropython tests may not be working properly. +# on GitHub CI, the limit is far larger than 6000. It is 1024 on desktop Ubuntu, but +# higher on the runners. I don't think this test is testing what it means to test. +# poller = select.poll() +# fd_last = 0 +# for fd in range(6000): +# fd_last = fd +# poller.register(fd) +# try: +# poller.poll() +# assert False +# except OSError as er: +# print("fd_last", fd_last) +# print(er.errno == errno.EINVAL) + +# Register stdout/stderr, plus many extra ones to trigger the fd vector +# resizing. Then unregister the excess ones and verify poll still works. poller = select.poll() -for fd in range(6000): +for fd in range(1, 1000): poller.register(fd) -try: - poller.poll() -except OSError as er: - print(er.errno == errno.EINVAL) +for i in range(3, 1000): + poller.unregister(i) +print(sorted(poller.poll())) diff --git a/tests/extmod/ssl_sslcontext_ciphers.py b/tests/extmod/ssl_sslcontext_ciphers.py new file mode 100644 index 000000000000..20c00b917623 --- /dev/null +++ b/tests/extmod/ssl_sslcontext_ciphers.py @@ -0,0 +1,31 @@ +# Basic test of ssl.SSLContext get_ciphers() and set_ciphers() methods. + +try: + import ssl +except ImportError: + print("SKIP") + raise SystemExit + + +ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + +ciphers = ctx.get_ciphers() + +for ci in ciphers: + # Only print those ciphers know to exist on all ports. + if ("TLS-ECDHE-ECDSA-WITH-AES" in ci or "TLS-RSA-WITH-AES" in ci) and "CBC" in ci: + print(ci) + +ctx.set_ciphers(ciphers[:1]) + +# Test error cases. + +try: + ctx.set_ciphers(ciphers[0]) +except TypeError as e: + print(e) + +try: + ctx.set_ciphers(["BAR"]) +except OSError as e: + print(e) diff --git a/tests/extmod/ssl_sslcontext_ciphers.py.exp b/tests/extmod/ssl_sslcontext_ciphers.py.exp new file mode 100644 index 000000000000..0d21a3bd253e --- /dev/null +++ b/tests/extmod/ssl_sslcontext_ciphers.py.exp @@ -0,0 +1,10 @@ +TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384 +TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA +TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA256 +TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA +TLS-RSA-WITH-AES-256-CBC-SHA256 +TLS-RSA-WITH-AES-256-CBC-SHA +TLS-RSA-WITH-AES-128-CBC-SHA256 +TLS-RSA-WITH-AES-128-CBC-SHA +object 'str' isn't a tuple or list +(-24192, 'MBEDTLS_ERR_SSL_BAD_CONFIG') diff --git a/tests/extmod/uctypes_error.py b/tests/extmod/uctypes_error.py index d42956261589..f8aea94a7f1a 100644 --- a/tests/extmod/uctypes_error.py +++ b/tests/extmod/uctypes_error.py @@ -8,6 +8,12 @@ data = bytearray(b"01234567") +# first argument isn't an integer +try: + uctypes.struct(data, {}) +except TypeError: + print("TypeError") + # del subscr not supported S = uctypes.struct(uctypes.addressof(data), {}) try: diff --git a/tests/extmod/uctypes_error.py.exp b/tests/extmod/uctypes_error.py.exp index f2e9c12f7f94..9910ced6c6ae 100644 --- a/tests/extmod/uctypes_error.py.exp +++ b/tests/extmod/uctypes_error.py.exp @@ -3,3 +3,4 @@ TypeError TypeError TypeError TypeError +TypeError diff --git a/tests/extmod/uctypes_le.py b/tests/extmod/uctypes_le.py index 466191c27a6f..f69da30b61ea 100644 --- a/tests/extmod/uctypes_le.py +++ b/tests/extmod/uctypes_le.py @@ -6,13 +6,7 @@ desc = { "s0": uctypes.UINT16 | 0, - "sub": ( - 0, - { - "b0": uctypes.UINT8 | 0, - "b1": uctypes.UINT8 | 1, - }, - ), + "sub": (0, {"b0": uctypes.UINT8 | 0, "b1": uctypes.UINT8 | 1}), "arr": (uctypes.ARRAY | 0, uctypes.UINT8 | 2), "arr2": (uctypes.ARRAY | 0, 2, {"b": uctypes.UINT8 | 0}), "bitf0": uctypes.BFUINT16 | 0 | 0 << uctypes.BF_POS | 8 << uctypes.BF_LEN, diff --git a/tests/extmod/uctypes_native_float.py b/tests/extmod/uctypes_native_float.py index 1fdc65849967..e7d3ddabd93b 100644 --- a/tests/extmod/uctypes_native_float.py +++ b/tests/extmod/uctypes_native_float.py @@ -4,7 +4,10 @@ print("SKIP") raise SystemExit -desc = {"f32": uctypes.FLOAT32 | 0, "f64": uctypes.FLOAT64 | 0} +desc = { + "f32": uctypes.FLOAT32 | 0, + "f64": uctypes.FLOAT64 | 0, +} data = bytearray(8) diff --git a/tests/extmod/uctypes_native_le.py b/tests/extmod/uctypes_native_le.py index 10477e669431..7958e5c22ada 100644 --- a/tests/extmod/uctypes_native_le.py +++ b/tests/extmod/uctypes_native_le.py @@ -16,13 +16,7 @@ desc = { "s0": uctypes.UINT16 | 0, - "sub": ( - 0, - { - "b0": uctypes.UINT8 | 0, - "b1": uctypes.UINT8 | 1, - }, - ), + "sub": (0, {"b0": uctypes.UINT8 | 0, "b1": uctypes.UINT8 | 1}), "arr": (uctypes.ARRAY | 0, uctypes.UINT8 | 2), "arr2": (uctypes.ARRAY | 0, 2, {"b": uctypes.UINT8 | 0}), "bitf0": uctypes.BFUINT16 | 0 | 0 << uctypes.BF_POS | 8 << uctypes.BF_LEN, diff --git a/tests/extmod/uctypes_sizeof_native.py b/tests/extmod/uctypes_sizeof_native.py index 991cd25f5299..157e554b09d4 100644 --- a/tests/extmod/uctypes_sizeof_native.py +++ b/tests/extmod/uctypes_sizeof_native.py @@ -28,13 +28,7 @@ "b": uctypes.UINT32 | 4, "c": uctypes.UINT8 | 8, "d": uctypes.UINT32 | 0, - "sub": ( - 4, - { - "b0": uctypes.UINT8 | 0, - "b1": uctypes.UINT8 | 1, - }, - ), + "sub": (4, {"b0": uctypes.UINT8 | 0, "b1": uctypes.UINT8 | 1}), } assert uctypes.sizeof(S5) == 12 diff --git a/tests/extmod/vfs_fat_fileio1.py b/tests/extmod/vfs_fat_fileio1.py index e10c44bb9a49..9f8bb9162db8 100644 --- a/tests/extmod/vfs_fat_fileio1.py +++ b/tests/extmod/vfs_fat_fileio1.py @@ -18,6 +18,7 @@ class RAMFS: def __init__(self, blocks): self.data = bytearray(blocks * self.SEC_SIZE) + # CIRCUITPY-CHANGES: made during v1.12 merge # Don't do any allocations in the below functions because they may be called # during a gc_sweep from a finalizer. def readblocks(self, n, buf): diff --git a/tests/extmod/vfs_fat_oldproto.py b/tests/extmod/vfs_fat_oldproto.py index df8a13c96e89..72b414cf01b1 100644 --- a/tests/extmod/vfs_fat_oldproto.py +++ b/tests/extmod/vfs_fat_oldproto.py @@ -22,12 +22,14 @@ def readblocks(self, n, buf): # print("readblocks(%s, %x(%d))" % (n, id(buf), len(buf))) for i in range(len(buf)): buf[i] = self.data[n * self.SEC_SIZE + i] + # CIRCUITPY-CHANGE return 0 def writeblocks(self, n, buf): # print("writeblocks(%s, %x)" % (n, id(buf))) for i in range(len(buf)): self.data[n * self.SEC_SIZE + i] = buf[i] + # CIRCUITPY-CHANGE return 0 def sync(self): diff --git a/tests/extmod/vfs_fat_ramdisk.py b/tests/extmod/vfs_fat_ramdisk.py index 3d66d7576643..6f822892d408 100644 --- a/tests/extmod/vfs_fat_ramdisk.py +++ b/tests/extmod/vfs_fat_ramdisk.py @@ -22,12 +22,14 @@ def readblocks(self, n, buf): # print("readblocks(%s, %x(%d))" % (n, id(buf), len(buf))) for i in range(len(buf)): buf[i] = self.data[n * self.SEC_SIZE + i] + # CIRCUITPY-CHANGE return 0 def writeblocks(self, n, buf): # print("writeblocks(%s, %x)" % (n, id(buf))) for i in range(len(buf)): self.data[n * self.SEC_SIZE + i] = buf[i] + # CIRCUITPY-CHANGE return 0 def ioctl(self, op, arg): @@ -51,6 +53,7 @@ def ioctl(self, op, arg): vfs = os.VfsFat(bdev) os.mount(vfs, "/ramdisk") +# CIRCUITPY-CHANGE vfs.label = "label test" print("label:", vfs.label) print("statvfs:", vfs.statvfs("/ramdisk")) diff --git a/tests/extmod/vfs_lfs.py b/tests/extmod/vfs_lfs.py index f555e00b8c61..83377653b2cf 100644 --- a/tests/extmod/vfs_lfs.py +++ b/tests/extmod/vfs_lfs.py @@ -115,7 +115,7 @@ def test(bdev, vfs_class): vfs.chdir("/testdir") print(vfs.getcwd()) - # create file in directory to make sre paths are relative + # create file in directory to make sure paths are relative vfs.open("test2", "w").close() print_stat(vfs.stat("test2")) print_stat(vfs.stat("/testdir/test2")) diff --git a/tests/extmod/vfs_lfs_file.py b/tests/extmod/vfs_lfs_file.py index 574ce1eecb1c..b745bb8112de 100644 --- a/tests/extmod/vfs_lfs_file.py +++ b/tests/extmod/vfs_lfs_file.py @@ -56,12 +56,14 @@ def test(bdev, vfs_class): # create binary, print, write, flush, close f = vfs.open("test.bin", "wb") print(f) + # CIRCUITPY-CHANGE f.write(b"littlefs") f.flush() f.close() # create for append f = vfs.open("test.bin", "ab") + # CIRCUITPY-CHANGE f.write(b"more") f.close() @@ -90,6 +92,7 @@ def test(bdev, vfs_class): # create read and write with vfs.open("test.bin", "r+b") as f: print(f.read(8)) + # CIRCUITPY-CHANGE f.write(b"MORE") with vfs.open("test.bin", "rb") as f: print(f.read()) diff --git a/tests/extmod/vfs_lfs_mtime.py b/tests/extmod/vfs_lfs_mtime.py index 2f8bfae55522..ade02fa14498 100644 --- a/tests/extmod/vfs_lfs_mtime.py +++ b/tests/extmod/vfs_lfs_mtime.py @@ -70,11 +70,11 @@ def test(bdev, vfs_class): # Wait 1 second so mtime will increase by at least 1. time.sleep(1) - # Open test1 for reading and ensre mtime did not change. + # Open test1 for reading and ensure mtime did not change. vfs.open("test1", "rt").close() print(vfs.stat("test1") == stat1) - # Open test1 for writing and ensre mtime increased from the previous value. + # Open test1 for writing and ensure mtime increased from the previous value. vfs.open("test1", "wt").close() stat1_old = stat1 stat1 = vfs.stat("test1") diff --git a/tests/extmod/vfs_posix.py b/tests/extmod/vfs_posix.py index 05dc0f537e3c..bc4c7c2014b6 100644 --- a/tests/extmod/vfs_posix.py +++ b/tests/extmod/vfs_posix.py @@ -88,6 +88,9 @@ def write_files_without_closing(): # construct new VfsPosix with path argument vfs = os.VfsPosix(temp_dir) +# when VfsPosix is used the intended way via os.mount(), it can only be called +# with relative paths when the CWD is inside or at its root, so simulate that +os.chdir(temp_dir) print(list(i[0] for i in vfs.ilistdir("."))) # stat, statvfs (statvfs may not exist) @@ -99,6 +102,22 @@ def write_files_without_closing(): print(type(list(vfs.ilistdir("."))[0][0])) print(type(list(vfs.ilistdir(b"."))[0][0])) +# chdir should not affect absolute paths (regression test) +vfs.mkdir("/subdir") +vfs.mkdir("/subdir/micropy_test_dir") +with vfs.open("/subdir/micropy_test_dir/test2", "w") as f: + f.write("wrong") +vfs.chdir("/subdir") +with vfs.open("/test2", "r") as f: + print(f.read()) +os.chdir(curdir) +vfs.remove("/subdir/micropy_test_dir/test2") +vfs.rmdir("/subdir/micropy_test_dir") +vfs.rmdir("/subdir") + +# done with vfs, restore CWD +os.chdir(curdir) + # remove os.remove(temp_dir + "/test2") print(os.listdir(temp_dir)) diff --git a/tests/extmod/vfs_posix.py.exp b/tests/extmod/vfs_posix.py.exp index 99922e621d4c..bd1ec7bad67b 100644 --- a/tests/extmod/vfs_posix.py.exp +++ b/tests/extmod/vfs_posix.py.exp @@ -10,6 +10,7 @@ next_file_no <= base_file_no True +hello [] remove OSError False diff --git a/tests/extmod/vfs_posix_enoent.py b/tests/extmod/vfs_posix_enoent.py new file mode 100644 index 000000000000..e6010d79d58f --- /dev/null +++ b/tests/extmod/vfs_posix_enoent.py @@ -0,0 +1,42 @@ +# Test for VfsPosix error conditions + +try: + import os + import sys + + os.VfsPosix +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit + +if sys.platform == "win32": + # Windows doesn't let you delete the current directory, so this cannot be + # tested. + print("SKIP") + raise SystemExit + +# We need an empty directory for testing. +# Skip the test if it already exists. +temp_dir = "vfs_posix_enoent_test_dir" +try: + os.stat(temp_dir) + print("SKIP") + raise SystemExit +except OSError: + pass + +curdir = os.getcwd() +os.mkdir(temp_dir) +os.chdir(temp_dir) +os.rmdir(curdir + "/" + temp_dir) +try: + print("getcwd():", os.getcwd()) +except OSError as e: + # expecting ENOENT = 2 + print("getcwd():", repr(e)) + +try: + print("VfsPosix():", os.VfsPosix("something")) +except OSError as e: + # expecting ENOENT = 2 + print("VfsPosix():", repr(e)) diff --git a/tests/extmod/vfs_posix_enoent.py.exp b/tests/extmod/vfs_posix_enoent.py.exp new file mode 100644 index 000000000000..f2d9a0d55106 --- /dev/null +++ b/tests/extmod/vfs_posix_enoent.py.exp @@ -0,0 +1,2 @@ +getcwd(): OSError(2,) +VfsPosix(): OSError(2,) diff --git a/tests/extmod/vfs_posix_ilistdir_del.py b/tests/extmod/vfs_posix_ilistdir_del.py index edb50dfd6229..8c8e6978b654 100644 --- a/tests/extmod/vfs_posix_ilistdir_del.py +++ b/tests/extmod/vfs_posix_ilistdir_del.py @@ -11,7 +11,13 @@ def test(testdir): + curdir = os.getcwd() vfs = os.VfsPosix(testdir) + # When VfsPosix is used the intended way via os.mount(), it can only be called + # with relative paths when the CWD is inside or at its root, so simulate that. + # (Although perhaps calling with a relative path was an oversight in this case + # and the respective line below was meant to read `vfs.rmdir("/" + dname)`.) + os.chdir(testdir) vfs.mkdir("/test_d1") vfs.mkdir("/test_d2") vfs.mkdir("/test_d3") @@ -48,6 +54,9 @@ def test(testdir): vfs.open("/test", "w").close() vfs.remove("/test") + # Done with vfs, restore CWD. + os.chdir(curdir) + # We need an empty directory for testing. # Skip the test if it already exists. diff --git a/tests/extmod/vfs_posix_ilistdir_filter.py b/tests/extmod/vfs_posix_ilistdir_filter.py index c32d1249710d..54012746057a 100644 --- a/tests/extmod/vfs_posix_ilistdir_filter.py +++ b/tests/extmod/vfs_posix_ilistdir_filter.py @@ -10,7 +10,11 @@ def test(testdir): + curdir = os.getcwd() vfs = os.VfsPosix(testdir) + # When VfsPosix is used the intended way via os.mount(), it can only be called + # with relative paths when the CWD is inside or at its root, so simulate that. + os.chdir(testdir) dirs = [".a", "..a", "...a", "a.b", "a..b"] @@ -24,6 +28,9 @@ def test(testdir): print(dirs) + # Done with vfs, restore CWD. + os.chdir(curdir) + # We need an empty directory for testing. # Skip the test if it already exists. diff --git a/tests/extmod/vfs_posix_paths.py b/tests/extmod/vfs_posix_paths.py new file mode 100644 index 000000000000..5806a34521a2 --- /dev/null +++ b/tests/extmod/vfs_posix_paths.py @@ -0,0 +1,92 @@ +# Test for VfsPosix with relative paths + +try: + import os + + os.VfsPosix +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit + +# We need a directory for testing that doesn't already exist. +# Skip the test if it does exist. +temp_dir = "vfs_posix_paths_test_dir" +try: + import os + + os.stat(temp_dir) + print("SKIP") + raise SystemExit +except OSError: + pass + +curdir = os.getcwd() +os.mkdir(temp_dir) + +# construct new VfsPosix with absolute path argument +temp_dir_abs = os.getcwd() + os.sep + temp_dir +vfs = os.VfsPosix(temp_dir_abs) +# when VfsPosix is used the intended way via os.mount(), it can only be called +# with relative paths when the CWD is inside or at its root, so simulate that +os.chdir(temp_dir_abs) +vfs.mkdir("subdir") +vfs.mkdir("subdir/one") +print('listdir("/"):', sorted(i[0] for i in vfs.ilistdir("/"))) +print('listdir("."):', sorted(i[0] for i in vfs.ilistdir("."))) +print('getcwd() in {"", "/"}:', vfs.getcwd() in {"", "/"}) +print('chdir("subdir"):', vfs.chdir("subdir")) +print("getcwd():", vfs.getcwd()) +print('mkdir("two"):', vfs.mkdir("two")) +f = vfs.open("file.py", "w") +f.write("print('hello')") +f.close() +print('listdir("/"):', sorted(i[0] for i in vfs.ilistdir("/"))) +print('listdir("/subdir"):', sorted(i[0] for i in vfs.ilistdir("/subdir"))) +print('listdir("."):', sorted(i[0] for i in vfs.ilistdir("."))) +try: + f = vfs.open("/subdir/file.py", "r") + print(f.read()) + f.close() +except Exception as e: + print(e) +import sys + +sys.path.insert(0, "") +try: + import file + + print(file) +except Exception as e: + print(e) +del sys.path[0] +vfs.remove("file.py") +vfs.rmdir("two") +vfs.rmdir("/subdir/one") +vfs.chdir("/") +vfs.rmdir("/subdir") + +# done with vfs, restore CWD +os.chdir(curdir) + +# some integration tests with a mounted VFS +os.mount(os.VfsPosix(temp_dir_abs), "/mnt") +os.mkdir("/mnt/dir") +print('chdir("/mnt/dir"):', os.chdir("/mnt/dir")) +print("getcwd():", os.getcwd()) +print('chdir("/mnt"):', os.chdir("/mnt")) +print("getcwd():", os.getcwd()) +print('chdir("/"):', os.chdir("/")) +print("getcwd():", os.getcwd()) +print('chdir("/mnt/dir"):', os.chdir("/mnt/dir")) +print("getcwd():", os.getcwd()) +print('chdir(".."):', os.chdir("..")) +print("getcwd():", os.getcwd()) +os.rmdir("/mnt/dir") +os.umount("/mnt") + +# restore CWD +os.chdir(curdir) + +# rmdir +os.rmdir(temp_dir) +print(temp_dir in os.listdir()) diff --git a/tests/extmod/vfs_posix_paths.py.exp b/tests/extmod/vfs_posix_paths.py.exp new file mode 100644 index 000000000000..ecc13222aaae --- /dev/null +++ b/tests/extmod/vfs_posix_paths.py.exp @@ -0,0 +1,23 @@ +listdir("/"): ['subdir'] +listdir("."): ['subdir'] +getcwd() in {"", "/"}: True +chdir("subdir"): None +getcwd(): /subdir +mkdir("two"): None +listdir("/"): ['subdir'] +listdir("/subdir"): ['file.py', 'one', 'two'] +listdir("."): ['file.py', 'one', 'two'] +print('hello') +hello + +chdir("/mnt/dir"): None +getcwd(): /mnt/dir +chdir("/mnt"): None +getcwd(): /mnt +chdir("/"): None +getcwd(): / +chdir("/mnt/dir"): None +getcwd(): /mnt/dir +chdir(".."): None +getcwd(): /mnt +False diff --git a/tests/extmod/vfs_userfs.py b/tests/extmod/vfs_userfs.py index 36f1088870d8..5c487315eb5e 100644 --- a/tests/extmod/vfs_userfs.py +++ b/tests/extmod/vfs_userfs.py @@ -16,6 +16,8 @@ class UserFile(io.IOBase): + buffer_size = 16 + def __init__(self, mode, data): assert isinstance(data, bytes) self.is_text = mode.find("b") == -1 @@ -39,7 +41,11 @@ def readinto(self, buf): def ioctl(self, req, arg): print("ioctl", req, arg) - return 0 + if req == 4: # MP_STREAM_CLOSE + return 0 + if req == 11: # MP_STREAM_GET_BUFFER_SIZE + return UserFile.buffer_size + return -1 class UserFS: @@ -70,6 +76,8 @@ def open(self, path, mode): "/usermod2.py": b"print('in usermod2')", "/usermod3.py": b"syntax error", "/usermod4.mpy": b"syntax error", + "/usermod5.py": b"print('in usermod5')", + "/usermod6.py": b"print('in usermod6')", } os.mount(UserFS(user_files), "/userfs") @@ -93,6 +101,14 @@ def open(self, path, mode): except ValueError: print("ValueError in usermod4") +# Test an import with largest buffer size +UserFile.buffer_size = 255 +import usermod5 + +# Test an import with over-size buffer size (should be safely limited internally) +UserFile.buffer_size = 1024 +import usermod6 + # unmount and undo path addition os.umount("/userfs") sys.path.pop() diff --git a/tests/extmod/vfs_userfs.py.exp b/tests/extmod/vfs_userfs.py.exp index 05cff0452c68..be8011d567eb 100644 --- a/tests/extmod/vfs_userfs.py.exp +++ b/tests/extmod/vfs_userfs.py.exp @@ -3,21 +3,37 @@ some data in a text file stat /usermod1 stat /usermod1.py open /usermod1.py rb +ioctl 11 0 ioctl 4 0 in usermod1 stat /usermod2 stat /usermod2.py open /usermod2.py rb +ioctl 11 0 ioctl 4 0 in usermod2 stat /usermod3 stat /usermod3.py open /usermod3.py rb +ioctl 11 0 ioctl 4 0 SyntaxError in usermod3 stat /usermod4 stat /usermod4.py stat /usermod4.mpy open /usermod4.mpy rb +ioctl 11 0 ioctl 4 0 ValueError in usermod4 +stat /usermod5 +stat /usermod5.py +open /usermod5.py rb +ioctl 11 0 +ioctl 4 0 +in usermod5 +stat /usermod6 +stat /usermod6.py +open /usermod6.py rb +ioctl 11 0 +ioctl 4 0 +in usermod6 diff --git a/tests/feature_check/inlineasm_thumb2.py b/tests/feature_check/inlineasm_thumb2.py new file mode 100644 index 000000000000..bc4c128baf5d --- /dev/null +++ b/tests/feature_check/inlineasm_thumb2.py @@ -0,0 +1,10 @@ +# check if Thumb2/ARMV7M instructions are supported + + +@micropython.asm_thumb +def f(): + it(eq) + nop() + + +print("thumb2") diff --git a/tests/feature_check/inlineasm_thumb2.py.exp b/tests/feature_check/inlineasm_thumb2.py.exp new file mode 100644 index 000000000000..05d125af9f65 --- /dev/null +++ b/tests/feature_check/inlineasm_thumb2.py.exp @@ -0,0 +1 @@ +thumb2 diff --git a/tests/float/float2int_doubleprec_intbig.py b/tests/float/float2int_doubleprec_intbig.py index 402966cace27..565698d8770b 100644 --- a/tests/float/float2int_doubleprec_intbig.py +++ b/tests/float/float2int_doubleprec_intbig.py @@ -59,7 +59,7 @@ def fp2int_test(num, name, should_fail): try: x = int(num) - passed = ~should_fail + passed = not should_fail except: passed = should_fail print("%s: %s" % (name, passed and "passed" or "failed")) diff --git a/tests/float/float2int_fp30_intbig.py b/tests/float/float2int_fp30_intbig.py index 1b22fe9646f5..eb65f8950251 100644 --- a/tests/float/float2int_fp30_intbig.py +++ b/tests/float/float2int_fp30_intbig.py @@ -56,7 +56,7 @@ def fp2int_test(num, name, should_fail): try: x = int(num) - passed = ~should_fail + passed = not should_fail except: passed = should_fail print("%s: %s" % (name, passed and "passed" or "failed")) diff --git a/tests/float/float2int_intbig.py b/tests/float/float2int_intbig.py index d047f247f277..4167a2721ca1 100644 --- a/tests/float/float2int_intbig.py +++ b/tests/float/float2int_intbig.py @@ -47,7 +47,7 @@ # TODO why does 10**12 fail this test for single precision float? testpass = True -p10_rng = 9 if (ll_type == 0 and ~is_64bit) else 11 +p10_rng = 9 if (ll_type == 0 and not is_64bit) else 11 for i in range(0, p10_rng): digcnt = len(str(int(10.0**i))) - 1 if i != digcnt: @@ -59,7 +59,7 @@ def fp2int_test(num, name, should_fail): try: x = int(num) - passed = ~should_fail + passed = not should_fail except: passed = should_fail print("%s: %s" % (name, passed and "passed" or "failed")) diff --git a/tests/float/inf_nan_arith.py b/tests/float/inf_nan_arith.py index d1a6b1887202..33cadbf3993b 100644 --- a/tests/float/inf_nan_arith.py +++ b/tests/float/inf_nan_arith.py @@ -3,7 +3,7 @@ inf = float("inf") nan = float("nan") -values = (-2, -1, 0, 1, 2, inf, nan) +values = (-2, -1, 0, 1, 2, inf, -inf, nan) for x in values: for y in values: diff --git a/tests/float/math_fun.py b/tests/float/math_fun.py index 9920caf32baa..789158c0e2b6 100644 --- a/tests/float/math_fun.py +++ b/tests/float/math_fun.py @@ -61,50 +61,10 @@ copysign, [(23.0, 42.0), (-23.0, 42.0), (23.0, -42.0), (-23.0, -42.0), (1.0, 0.0), (1.0, -0.0)], ), - ( - "pow", - pow, - ( - (1.0, 0.0), - (0.0, 1.0), - (2.0, 0.5), - (-3.0, 5.0), - (-3.0, -4.0), - ), - ), - ( - "atan2", - atan2, - ( - (1.0, 0.0), - (0.0, 1.0), - (2.0, 0.5), - (-3.0, 5.0), - (-3.0, -4.0), - ), - ), - ( - "fmod", - fmod, - ( - (1.0, 1.0), - (0.0, 1.0), - (2.0, 0.5), - (-3.0, 5.0), - (-3.0, -4.0), - ), - ), - ( - "ldexp", - ldexp, - ( - (1.0, 0), - (0.0, 1), - (2.0, 2), - (3.0, -2), - (-3.0, -4), - ), - ), + ("pow", pow, ((1.0, 0.0), (0.0, 1.0), (2.0, 0.5), (-3.0, 5.0), (-3.0, -4.0))), + ("atan2", atan2, ((1.0, 0.0), (0.0, 1.0), (2.0, 0.5), (-3.0, 5.0), (-3.0, -4.0))), + ("fmod", fmod, ((1.0, 1.0), (0.0, 1.0), (2.0, 0.5), (-3.0, 5.0), (-3.0, -4.0))), + ("ldexp", ldexp, ((1.0, 0), (0.0, 1), (2.0, 2), (3.0, -2), (-3.0, -4))), ( "log", log, diff --git a/tests/float/math_fun_special.py b/tests/float/math_fun_special.py index 9c0fe3555f2e..e674ec8dfd0d 100644 --- a/tests/float/math_fun_special.py +++ b/tests/float/math_fun_special.py @@ -40,15 +40,7 @@ ("erf", erf, test_values), ("erfc", erfc, test_values), ("gamma", gamma, pos_test_values), - ( - "lgamma", - lgamma, - pos_test_values - + [ - 50.0, - 100.0, - ], - ), + ("lgamma", lgamma, pos_test_values + [50.0, 100.0]), ] for function_name, function, test_vals in functions: diff --git a/tests/micropython/builtin_execfile.py b/tests/micropython/builtin_execfile.py index 5a26ccf0ad35..9b6d6a0aa00b 100644 --- a/tests/micropython/builtin_execfile.py +++ b/tests/micropython/builtin_execfile.py @@ -70,5 +70,11 @@ def open(self, file, mode): # Test execfile with a file that does exist. execfile("/test_mnt/test.py") +# Test that it only works with string arguments. +try: + execfile(b"aaa") +except TypeError: + print("TypeError") + # Unmount the VFS object. os.umount(fs) diff --git a/tests/micropython/builtin_execfile.py.exp b/tests/micropython/builtin_execfile.py.exp index 1d5d8ee571e7..49703d570763 100644 --- a/tests/micropython/builtin_execfile.py.exp +++ b/tests/micropython/builtin_execfile.py.exp @@ -3,4 +3,5 @@ open /noexist.py rb OSError open /test.py rb 123 +TypeError umount diff --git a/tests/micropython/heapalloc_fail_set.py b/tests/micropython/heapalloc_fail_set.py index 8eb887f711ad..3c347660ad76 100644 --- a/tests/micropython/heapalloc_fail_set.py +++ b/tests/micropython/heapalloc_fail_set.py @@ -6,9 +6,7 @@ x = 1 micropython.heap_lock() try: - { - x, - } + {x} except MemoryError: print("MemoryError: set create") micropython.heap_unlock() diff --git a/tests/micropython/import_mpy_invalid.py b/tests/micropython/import_mpy_invalid.py index d2f0c9db2804..d4077a31f33e 100644 --- a/tests/micropython/import_mpy_invalid.py +++ b/tests/micropython/import_mpy_invalid.py @@ -15,6 +15,7 @@ def __init__(self, data): self.data = memoryview(data) self.pos = 0 + # CIRCUITPY-CHANGE def read(self): return self.data @@ -48,6 +49,7 @@ def open(self, path, mode): # these are the test .mpy files +# CIRCUITPY-CHANGE: C instead of M user_files = { "/mod0.mpy": b"", # empty file "/mod1.mpy": b"C", # too short header @@ -63,6 +65,7 @@ def open(self, path, mode): mod = "mod%u" % i try: __import__(mod) + # CIRCUITPY-CHANGE except Exception as e: print(mod, type(e).__name__, e) diff --git a/tests/micropython/import_mpy_native.py b/tests/micropython/import_mpy_native.py index 4945a981662f..4efb1e18a32c 100644 --- a/tests/micropython/import_mpy_native.py +++ b/tests/micropython/import_mpy_native.py @@ -52,10 +52,12 @@ def open(self, path, mode): # these are the test .mpy files +# CIRCUITPY-CHANGE valid_header = bytes([ord("C"), 6, mpy_arch, 31]) # fmt: off user_files = { # bad architecture (mpy_arch needed for sub-version) + # CIRCUITPY-CHANGE '/mod0.mpy': bytes([ord('C'), 6, 0xfc | mpy_arch, 31]), # test loading of viper and asm diff --git a/tests/micropython/import_mpy_native_gc.py b/tests/micropython/import_mpy_native_gc.py index a945910f5c51..35759ce95ba7 100644 --- a/tests/micropython/import_mpy_native_gc.py +++ b/tests/micropython/import_mpy_native_gc.py @@ -51,11 +51,13 @@ def open(self, path, mode): # make clean # make ARCH=x64 # or ARCH=armv6m # cat features0.mpy | python -c 'import sys; print(sys.stdin.buffer.read())' +# CIRCUITPY-CHANGE: 'C' instead of 'M' mpy marker. features0_file_contents = { # -march=x64 - 0x806: b'C\x06\t\x1f\x02\x004build/features0.native.mpy\x00\x12factorial\x00\x8a\x02\xe9/\x00\x00\x00SH\x8b\x1d\x83\x00\x00\x00\xbe\x02\x00\x00\x00\xffS\x18\xbf\x01\x00\x00\x00H\x85\xc0u\x0cH\x8bC \xbe\x02\x00\x00\x00[\xff\xe0H\x0f\xaf\xf8H\xff\xc8\xeb\xe6ATUSH\x8b\x1dQ\x00\x00\x00H\x8bG\x08L\x8bc(H\x8bx\x08A\xff\xd4H\x8d5+\x00\x00\x00H\x89\xc5H\x8b\x059\x00\x00\x00\x0f\xb7x\x02\xffShH\x89\xefA\xff\xd4H\x8b\x03[]A\\\xc3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x11$\r&\xa5 \x01"\xff', + 0x806: b'C\x06\n\x1f\x02\x004build/features0.native.mpy\x00\x12factorial\x00\x8a\x02\xe93\x00\x00\x00\xf3\x0f\x1e\xfaSH\x8b\x1d\x7f\x00\x00\x00\xbe\x02\x00\x00\x00\xffS\x18\xbf\x01\x00\x00\x00H\x85\xc0u\x0cH\x8bC \xbe\x02\x00\x00\x00[\xff\xe0H\x0f\xaf\xf8H\xff\xc8\xeb\xe6\xf3\x0f\x1e\xfaATUSH\x8b\x1dI\x00\x00\x00H\x8bG\x08L\x8bc(H\x8bx\x08A\xff\xd4H\x8d5#\x00\x00\x00H\x89\xc5H\x8b\x051\x00\x00\x00\x0f\xb7x\x02\xffShH\x89\xefA\xff\xd4H\x8b\x03[]A\\\xc3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x11$\r&\xa5 \x01"\xff', # -march=armv6m - 0x1006: b'C\x06\x11\x1f\x02\x004build/features0.native.mpy\x00\x12factorial\x00\x88"\x1a\xe0\x00\x00\x13\xb5\nK\nJ{D\x9cX\x02!\xe3h\x01\x93\x98G\x03\x00\x01 \x00+\x02\xd0XC\x01;\xfa\xe7#i\x02!\x01\x93\x98G\x16\xbd\xc0Fn\x00\x00\x00\x00\x00\x00\x00\xf7\xb5\nN\nK~D\xf4XChgiXh\xb8G\x05\x00\x07K\x08I\xf3XyDX\x88\x01\x93ck\x98G(\x00\xb8G h\xfe\xbd:\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x1e\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x11>\r@\xa5:\x01<\xff', + 0x1006: b'C\x06\x12\x1f\x02\x004build/features0.native.mpy\x00\x12factorial\x00\x88\x02\x18\xe0\x00\x00\x10\xb5\tK\tJ{D\x9cX\x02!\xe3h\x98G\x03\x00\x01 \x00+\x02\xd0XC\x01;\xfa\xe7\x02!#i\x98G\x10\xbd\xc0Fj\x00\x00\x00\x00\x00\x00\x00\xf8\xb5\nN\nK~D\xf4XChgiXh\xb8G\x05\x00\x07K\x08I\xf3XyDX\x88ck\x98G(\x00\xb8G h\xf8\xbd\xc0F:\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x1e\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x11<\r>\xa58\x01:\xff' +, } # Populate armv7m-derived archs based on armv6m. diff --git a/tests/micropython/opt_level.py b/tests/micropython/opt_level.py index 8ba7b3a22855..dd5493a7a3cb 100644 --- a/tests/micropython/opt_level.py +++ b/tests/micropython/opt_level.py @@ -1,4 +1,4 @@ -import micropython +import micropython as micropython # check we can get and set the level micropython.opt_level(0) diff --git a/tests/misc/rge_sm.py b/tests/misc/rge_sm.py index 00b0a7a02167..5e071687c495 100644 --- a/tests/misc/rge_sm.py +++ b/tests/misc/rge_sm.py @@ -136,6 +136,4 @@ def singleTraj(system, trajStart, h=0.02, tend=1.0): # phaseDiagram(sysSM, (lambda i, j: [0.354, 0.654, 1.278, 0.8 + 0.2 * i, 0.1 + 0.1 * j]), (lambda a: (a[4], a[5])), h=0.1, tend=math.log(10**17)) # initial conditions at M_Z -singleTraj( - sysSM, [0.354, 0.654, 1.278, 0.983, 0.131], h=0.5, tend=math.log(10**17) -) # true values +singleTraj(sysSM, [0.354, 0.654, 1.278, 0.983, 0.131], h=0.5, tend=math.log(10**17)) # true values diff --git a/tests/perf_bench/benchrun.py b/tests/perf_bench/benchrun.py index 9c55f338cc4e..0e206d35a151 100644 --- a/tests/perf_bench/benchrun.py +++ b/tests/perf_bench/benchrun.py @@ -2,6 +2,7 @@ def bm_run(N, M): try: from time import ticks_us, ticks_diff except ImportError: + # CIRCUITPY-CHANGE import time ticks_us = lambda: int(time.monotonic_ns() // 1000) diff --git a/tests/perf_bench/bm_wordcount.py b/tests/perf_bench/bm_wordcount.py new file mode 100644 index 000000000000..ef55046956a0 --- /dev/null +++ b/tests/perf_bench/bm_wordcount.py @@ -0,0 +1,45 @@ +# This tests using string as dictionary keys when they are not qstrs + +ZEN = "the zen of python beautiful is better than ugly explicit is better than implicit simple is better than complex complex is better than complicated flat is better than nested sparse is better than dense readability counts special cases arent special enough to break the rules although practicality beats purity errors should never pass silently unless explicitly silenced in the face of ambiguity refuse the temptation to guess there should be one and preferably only one obvious way to do it although that way may not be obvious at first unless youre dutch now is better than never although never is often better than right now if the implementation is hard to explain its a bad idea if the implementation is easy to explain it may be a good idea namespaces are one honking great idea lets do more of those" + + +def test(niter): + words = ZEN.split(" ") + for _ in range(niter): + counts = {} + for _ in range(niter): + for word in words: + counts[word] = counts.get(word, 0) + 1 + + return ( + counts["python"], + counts["is"], + counts["than"], + ) + + +########################################################################### +# Benchmark interface + +bm_params = { + (32, 10): (2,), + (50, 10): (4,), + (100, 10): (8,), + (500, 10): (40,), + (1000, 10): (80,), + (5000, 10): (400,), +} + + +def bm_setup(params): + (niter,) = params + state = None + + def run(): + nonlocal state + state = test(niter) + + def result(): + return niter, state + + return run, result diff --git a/tests/perf_bench/core_locals.py b/tests/perf_bench/core_locals.py new file mode 100644 index 000000000000..d28e078b26e1 --- /dev/null +++ b/tests/perf_bench/core_locals.py @@ -0,0 +1,191 @@ +# This tests the performance of an instance class locals dict (importantly, that has all keys as qstrs) + +# These are all shorter than 10 characters, so will be interned by the parser. +ZEN = [ + "the", + "zen", + "of", + "python", + "beautiful", + "is", + "better", + "than", + "ugly", + "explicit", + "is", + "better", + "than", + "implicit", + "simple", + "is", + "better", + "than", + "complex", + "complex", + "is", + "better", + "than", + "complicate", + "flat", + "is", + "better", + "than", + "nested", + "sparse", + "is", + "better", + "than", + "dense", + "readabilit", + "counts", + "special", + "cases", + "arent", + "special", + "enough", + "to", + "break", + "the", + "rules", + "although", + "practicali", + "beats", + "purity", + "errors", + "should", + "never", + "pass", + "silently", + "unless", + "explicitly", + "silenced", + "in", + "the", + "face", + "of", + "ambiguity", + "refuse", + "the", + "temptation", + "to", + "guess", + "there", + "should", + "be", + "one", + "and", + "preferably", + "only", + "one", + "obvious", + "way", + "to", + "do", + "it", + "although", + "that", + "way", + "may", + "not", + "be", + "obvious", + "at", + "first", + "unless", + "youre", + "dutch", + "now", + "is", + "better", + "than", + "never", + "although", + "never", + "is", + "often", + "better", + "than", + "right", + "now", + "if", + "the", + "implementa", + "is", + "hard", + "to", + "explain", + "its", + "a", + "bad", + "idea", + "if", + "the", + "implementa", + "is", + "easy", + "to", + "explain", + "it", + "may", + "be", + "a", + "good", + "idea", + "namespaces", + "are", + "one", + "honking", + "great", + "idea", + "", + "lets", + "do", + "more", + "of", + "those", +] + + +class A: + pass + + +def test(niter): + for _ in range(niter): + a = A() + for _ in range(niter): + for word in ZEN: + setattr(a, word, getattr(a, word, 0) + 1) + + return ( + getattr(a, "python"), + getattr(a, "is"), + getattr(a, "than"), + ) + + +########################################################################### +# Benchmark interface + +bm_params = { + (32, 10): (2,), + (50, 10): (4,), + (100, 10): (8,), + (500, 10): (40,), + (1000, 10): (80,), + (5000, 10): (400,), +} + + +def bm_setup(params): + (niter,) = params + state = None + + def run(): + nonlocal state + state = test(niter) + + def result(): + return niter, state + + return run, result diff --git a/tests/perf_bench/core_str.py b/tests/perf_bench/core_str.py new file mode 100644 index 000000000000..ca827194d6fd --- /dev/null +++ b/tests/perf_bench/core_str.py @@ -0,0 +1,65 @@ +# This tests string handling operations + +ZEN = """ +The Zen of Python +Beautiful is better than ugly. +Explicit is better than implicit. +Simple is better than complex. +Complex is better than complicated. +Flat is better than nested. +Sparse is better than dense. +Readability counts. +Special cases aren't special enough to break the rules. +Although practicality beats purity. +Errors should never pass silently. +Unless explicitly silenced. +In the face of ambiguity, refuse the temptation to guess. +There should be one-- and preferably only one --obvious way to do it. +Although that way may not be obvious at first unless you're Dutch. +Now is better than never. +Although never is often better than *right* now. +If the implementation is hard to explain, it's a bad idea. +If the implementation is easy to explain, it may be a good idea. +Namespaces are one honking great idea -- let's do more of those! +""" + + +def test(niter): + counts = {} + for _ in range(niter): + x = ZEN.replace("\n", " ").split(" ") + y = " ".join(x) + for i in range(50): + a = ZEN[i : i * 2] + b = a + "hello world" + for c in ZEN: + i = ord(c) + c = chr(i) + return (x[0], a) + + +########################################################################### +# Benchmark interface + +bm_params = { + (32, 10): (2,), + (50, 10): (3,), + (100, 10): (6,), + (500, 10): (30,), + (1000, 10): (60,), + (5000, 10): (300,), +} + + +def bm_setup(params): + (niter,) = params + state = None + + def run(): + nonlocal state + state = test(niter) + + def result(): + return niter, state + + return run, result diff --git a/tests/run-natmodtests.py b/tests/run-natmodtests.py index f5e86bffbc42..85ac92672a12 100755 --- a/tests/run-natmodtests.py +++ b/tests/run-natmodtests.py @@ -9,12 +9,15 @@ import sys import argparse +#CIRCUITPY-CHANGE: no pyboard + # Paths for host executables CPYTHON3 = os.getenv("MICROPY_CPYTHON3", "python3") MICROPYTHON = os.getenv("MICROPY_MICROPYTHON", "../ports/unix/build-coverage/micropython") NATMOD_EXAMPLE_DIR = "../examples/natmod/" +#CIRCUITPY-CHANGE: different TEST_MAPPINGS # Supported tests and their corresponding mpy module TEST_MAPPINGS = { "heapq": "heapq/heapq_$(ARCH).mpy", @@ -174,10 +177,6 @@ def main(): target_truth = TargetSubprocess([CPYTHON3]) if args.pyboard: - global pyboard - sys.path.append("../tools") - import pyboard - target = TargetPyboard(pyboard.Pyboard(args.device)) else: target = TargetSubprocess([MICROPYTHON]) diff --git a/tests/run-tests.py b/tests/run-tests.py index fbf67cd95e10..52204cc9fe2e 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -467,6 +467,21 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): if output != b"a=1\n": skip_fstring = True + # Check if @micropython.asm_thumb supports Thumb2 instructions, and skip such tests if it doesn't + output = run_feature_check(pyb, args, base_path, "inlineasm_thumb2.py") + if output != b"thumb2\n": + skip_tests.add("inlineasm/asmbcc.py") + skip_tests.add("inlineasm/asmbitops.py") + skip_tests.add("inlineasm/asmconst.py") + skip_tests.add("inlineasm/asmdiv.py") + skip_tests.add("inlineasm/asmfpaddsub.py") + skip_tests.add("inlineasm/asmfpcmp.py") + skip_tests.add("inlineasm/asmfpldrstr.py") + skip_tests.add("inlineasm/asmfpmuldiv.py") + skip_tests.add("inlineasm/asmfpsqrt.py") + skip_tests.add("inlineasm/asmit.py") + skip_tests.add("inlineasm/asmspecialregs.py") + # Check if emacs repl is supported, and skip such tests if it's not t = run_feature_check(pyb, args, base_path, "repl_emacs_check.py") if "True" not in str(t, "ascii"): diff --git a/tests/thread/mutate_bytearray.py b/tests/thread/mutate_bytearray.py index 7bacabec3afb..b4664781a152 100644 --- a/tests/thread/mutate_bytearray.py +++ b/tests/thread/mutate_bytearray.py @@ -1,8 +1,6 @@ # test concurrent mutating access to a shared bytearray object # -# SPDX-FileCopyrightText: Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd -# -# SPDX-License-Identifier: MIT +# MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd import _thread diff --git a/tests/thread/mutate_dict.py b/tests/thread/mutate_dict.py index fb87190e7706..3777af66248c 100644 --- a/tests/thread/mutate_dict.py +++ b/tests/thread/mutate_dict.py @@ -1,8 +1,6 @@ # test concurrent mutating access to a shared dict object # -# SPDX-FileCopyrightText: Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd -# -# SPDX-License-Identifier: MIT +# MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd import _thread diff --git a/tests/thread/mutate_instance.py b/tests/thread/mutate_instance.py index cc0ad9885dd9..306ad91c95cf 100644 --- a/tests/thread/mutate_instance.py +++ b/tests/thread/mutate_instance.py @@ -1,8 +1,6 @@ # test concurrent mutating access to a shared user instance # -# SPDX-FileCopyrightText: Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd -# -# SPDX-License-Identifier: MIT +# MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd import _thread diff --git a/tests/thread/mutate_list.py b/tests/thread/mutate_list.py index 4df86b3008cd..6f1e8812541a 100644 --- a/tests/thread/mutate_list.py +++ b/tests/thread/mutate_list.py @@ -1,8 +1,6 @@ # test concurrent mutating access to a shared list object # -# SPDX-FileCopyrightText: Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd -# -# SPDX-License-Identifier: MIT +# MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd import _thread diff --git a/tests/thread/mutate_set.py b/tests/thread/mutate_set.py index 587a3a259ad6..2d9a3e0ce9ef 100644 --- a/tests/thread/mutate_set.py +++ b/tests/thread/mutate_set.py @@ -1,8 +1,6 @@ # test concurrent mutating access to a shared set object # -# SPDX-FileCopyrightText: Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd -# -# SPDX-License-Identifier: MIT +# MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd import _thread diff --git a/tests/thread/stress_aes.py b/tests/thread/stress_aes.py index 319087c487e1..b25da855aeff 100644 --- a/tests/thread/stress_aes.py +++ b/tests/thread/stress_aes.py @@ -11,9 +11,7 @@ # aggressive by changing the amount of data to encrypt, the number of loops and # the number of threads. # -# SPDX-FileCopyrightText: Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd -# -# SPDX-License-Identifier: MIT +# MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd ################################################################## # discrete arithmetic routines, mostly from a precomputed table diff --git a/tests/thread/stress_heap.py b/tests/thread/stress_heap.py index 9487b310ac86..dec65c7ce053 100644 --- a/tests/thread/stress_heap.py +++ b/tests/thread/stress_heap.py @@ -1,9 +1,7 @@ # stress test for the heap by allocating lots of objects within threads # allocates about 5mb on the heap # -# SPDX-FileCopyrightText: Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd -# -# SPDX-License-Identifier: MIT +# MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd import time import _thread diff --git a/tests/thread/stress_recurse.py b/tests/thread/stress_recurse.py index b7a1288211dc..73b3a40f33da 100644 --- a/tests/thread/stress_recurse.py +++ b/tests/thread/stress_recurse.py @@ -1,8 +1,6 @@ # test hitting the function recursion limit within a thread # -# SPDX-FileCopyrightText: Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd -# -# SPDX-License-Identifier: MIT +# MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd import _thread diff --git a/tests/thread/thread_exc1.py b/tests/thread/thread_exc1.py index 6dcfda121e0b..cd8774092910 100644 --- a/tests/thread/thread_exc1.py +++ b/tests/thread/thread_exc1.py @@ -1,8 +1,6 @@ # test raising and catching an exception within a thread # -# SPDX-FileCopyrightText: Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd -# -# SPDX-License-Identifier: MIT +# MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd import _thread diff --git a/tests/thread/thread_exit1.py b/tests/thread/thread_exit1.py index 8e405c0d0f4a..e34ca827ca38 100644 --- a/tests/thread/thread_exit1.py +++ b/tests/thread/thread_exit1.py @@ -1,8 +1,6 @@ # test _thread.exit() function # -# SPDX-FileCopyrightText: Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd -# -# SPDX-License-Identifier: MIT +# MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd import time import _thread diff --git a/tests/thread/thread_exit2.py b/tests/thread/thread_exit2.py index 842eabf89578..630b66475826 100644 --- a/tests/thread/thread_exit2.py +++ b/tests/thread/thread_exit2.py @@ -1,8 +1,6 @@ # test raising SystemExit to finish a thread # -# SPDX-FileCopyrightText: Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd -# -# SPDX-License-Identifier: MIT +# MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd import time import _thread diff --git a/tests/thread/thread_gc1.py b/tests/thread/thread_gc1.py index 4e4faeaf89f2..dd1e64d89444 100644 --- a/tests/thread/thread_gc1.py +++ b/tests/thread/thread_gc1.py @@ -1,8 +1,6 @@ # test that we can run the garbage collector within threads # -# SPDX-FileCopyrightText: Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd -# -# SPDX-License-Identifier: MIT +# MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd import gc import _thread diff --git a/tests/thread/thread_ident1.py b/tests/thread/thread_ident1.py index 5ecf3774f10c..8e106cd317f4 100644 --- a/tests/thread/thread_ident1.py +++ b/tests/thread/thread_ident1.py @@ -1,8 +1,6 @@ # test _thread.get_ident() function # -# SPDX-FileCopyrightText: Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd -# -# SPDX-License-Identifier: MIT +# MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd import _thread diff --git a/tests/thread/thread_lock1.py b/tests/thread/thread_lock1.py index f49f57ce8375..1d0701da01fd 100644 --- a/tests/thread/thread_lock1.py +++ b/tests/thread/thread_lock1.py @@ -1,8 +1,6 @@ # test _thread lock object using a single thread # -# SPDX-FileCopyrightText: Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd -# -# SPDX-License-Identifier: MIT +# MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd import _thread diff --git a/tests/thread/thread_lock2.py b/tests/thread/thread_lock2.py index b476de2f7c72..96b3a6af809c 100644 --- a/tests/thread/thread_lock2.py +++ b/tests/thread/thread_lock2.py @@ -1,8 +1,6 @@ # test _thread lock objects with multiple threads # -# SPDX-FileCopyrightText: Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd -# -# SPDX-License-Identifier: MIT +# MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd import time import _thread diff --git a/tests/thread/thread_lock3.py b/tests/thread/thread_lock3.py index 32d0c54b59e4..a927dc6829e1 100644 --- a/tests/thread/thread_lock3.py +++ b/tests/thread/thread_lock3.py @@ -1,8 +1,6 @@ # test thread coordination using a lock object # -# SPDX-FileCopyrightText: Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd -# -# SPDX-License-Identifier: MIT +# MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd import _thread diff --git a/tests/thread/thread_lock4.py b/tests/thread/thread_lock4.py index faa1fd724a18..97c3dc538007 100644 --- a/tests/thread/thread_lock4.py +++ b/tests/thread/thread_lock4.py @@ -1,8 +1,6 @@ # test using lock to coordinate access to global mutable objects # -# SPDX-FileCopyrightText: Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd -# -# SPDX-License-Identifier: MIT +# MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd import time import _thread diff --git a/tests/thread/thread_qstr1.py b/tests/thread/thread_qstr1.py index 97783bd0909e..f184d2a58e1d 100644 --- a/tests/thread/thread_qstr1.py +++ b/tests/thread/thread_qstr1.py @@ -1,8 +1,6 @@ # test concurrent interning of strings # -# SPDX-FileCopyrightText: Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd -# -# SPDX-License-Identifier: MIT +# MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd import time import _thread diff --git a/tests/thread/thread_shared1.py b/tests/thread/thread_shared1.py index 87f8b51e2690..582b01fc3435 100644 --- a/tests/thread/thread_shared1.py +++ b/tests/thread/thread_shared1.py @@ -1,8 +1,6 @@ # test capability for threads to access a shared immutable data structure # -# SPDX-FileCopyrightText: Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd -# -# SPDX-License-Identifier: MIT +# MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd import _thread diff --git a/tests/thread/thread_shared2.py b/tests/thread/thread_shared2.py index dbe18f9875b7..a1223c2b94f4 100644 --- a/tests/thread/thread_shared2.py +++ b/tests/thread/thread_shared2.py @@ -1,9 +1,7 @@ # test capability for threads to access a shared mutable data structure # (without contention because they access different parts of the structure) # -# SPDX-FileCopyrightText: Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd -# -# SPDX-License-Identifier: MIT +# MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd import _thread diff --git a/tests/thread/thread_sleep1.py b/tests/thread/thread_sleep1.py index 711e3562205d..add9b02f1573 100644 --- a/tests/thread/thread_sleep1.py +++ b/tests/thread/thread_sleep1.py @@ -1,8 +1,6 @@ # test threads sleeping # -# SPDX-FileCopyrightText: Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd -# -# SPDX-License-Identifier: MIT +# MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd import time diff --git a/tests/thread/thread_start1.py b/tests/thread/thread_start1.py index 673e46261b52..ea8c4f000250 100644 --- a/tests/thread/thread_start1.py +++ b/tests/thread/thread_start1.py @@ -1,8 +1,6 @@ # test basic capability to start a new thread # -# SPDX-FileCopyrightText: Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd -# -# SPDX-License-Identifier: MIT +# MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd import time import _thread diff --git a/tests/thread/thread_start2.py b/tests/thread/thread_start2.py index 114a1025e21f..f8239a779a67 100644 --- a/tests/thread/thread_start2.py +++ b/tests/thread/thread_start2.py @@ -1,8 +1,6 @@ # test capability to start a thread with keyword args # -# SPDX-FileCopyrightText: Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd -# -# SPDX-License-Identifier: MIT +# MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd import time import _thread diff --git a/tests/unicode/unicode.py b/tests/unicode/unicode.py index 8ac34ca28231..072e049fde41 100644 --- a/tests/unicode/unicode.py +++ b/tests/unicode/unicode.py @@ -19,7 +19,8 @@ # printing of unicode chars using repr # NOTE: for some characters (eg \u10ff) we differ to CPython -print(repr("a\u2000")) +print(repr("a\uffff")) +print(repr("a\U0001ffff")) # test invalid escape code try: diff --git a/tests/unix/extra_coverage.py.exp b/tests/unix/extra_coverage.py.exp index 4aa9c351b4ba..c75676fad9af 100644 --- a/tests/unix/extra_coverage.py.exp +++ b/tests/unix/extra_coverage.py.exp @@ -68,10 +68,10 @@ rainbowio random argv atexit byteorder exc_info executable exit getsizeof implementation -maxsize modules path platform -print_exception ps1 ps2 -stderr stdin stdout tracebacklimit -version version_info +intern maxsize modules path +platform print_exception ps1 +ps2 stderr stdin stdout +tracebacklimit version version_info ementation # attrtuple (start=1, stop=2, step=3) diff --git a/tools/autobuild/build-boards.sh b/tools/autobuild/build-boards.sh deleted file mode 100755 index 4b8cf315a2f9..000000000000 --- a/tools/autobuild/build-boards.sh +++ /dev/null @@ -1,125 +0,0 @@ -#!/bin/bash -# -# The functions in this file can be run independently to build boards. -# For example: -# -# $ source tools/autobuild/build-boards.sh -# $ cd ports/rp2 -# $ MICROPY_AUTOBUILD_MAKE="make -j8" build_rp2_boards -latest /tmp -# -# Or to build a single board: -# -# $ source tools/autobuild/build-boards.sh -# $ cd ports/rp2 -# $ MICROPY_AUTOBUILD_MAKE="make -j8" build_board boards/PICO/board.json -latest /tmp uf2 - -function copy_artefacts { - local dest_dir=$1 - local descr=$2 - local fw_tag=$3 - local build_dir=$4 - shift 4 - - for ext in $@; do - dest=$dest_dir/$descr$fw_tag.$ext - if [ -r $build_dir/firmware.$ext ]; then - mv $build_dir/firmware.$ext $dest - elif [ -r $build_dir/micropython.$ext ]; then - # esp32 has micropython.elf, etc - mv $build_dir/micropython.$ext $dest - elif [ $ext = app-bin -a -r $build_dir/micropython.bin ]; then - # esp32 has micropython.bin which is just the application - mv $build_dir/micropython.bin $dest - fi - done -} - -function build_board { - # check/get parameters - if [ $# -lt 4 ]; then - echo "usage: $0 " - return 1 - fi - - local board_json=$1 - local fw_tag=$2 - local dest_dir=$3 - shift 3 - - local board=$(echo $board_json | awk -F '/' '{ print $2 }') - local descr=$(cat $board_json | python3 -c "import json,sys; print(json.load(sys.stdin).get('id', '$board'))") - - # Build the "default" variant. For most boards this is the only thing we build. - echo "building $descr $board" - local build_dir=/tmp/micropython-build-$board - $MICROPY_AUTOBUILD_MAKE BOARD=$board BUILD=$build_dir && copy_artefacts $dest_dir $descr $fw_tag $build_dir $@ - rm -rf $build_dir - - # Query variants from board.json and build them. Ignore the special "IDF3" - # variant for ESP32 boards (this allows the downloads page to still have - # the idf3 files for older releases that used to be explicitly built). - for variant in `cat $board_json | python3 -c "import json,sys; print(' '.join(v for v in json.load(sys.stdin).get('variants', {}).keys() if v != 'IDF3'))"`; do - local variant_build_dir=$build_dir-$variant - echo "building variant $descr $board $variant" - $MICROPY_AUTOBUILD_MAKE BOARD=$board BOARD_VARIANT=$variant BUILD=$variant_build_dir && copy_artefacts $dest_dir $descr-$variant $fw_tag $variant_build_dir $@ - rm -rf $variant_build_dir - done -} - -function build_boards { - # check/get parameters - if [ $# -lt 4 ]; then - echo "usage: $0 " - return 1 - fi - - local check_file=$1 - shift - - # check we are in the correct directory - if [ ! -r $check_file ]; then - echo "must be in directory containing $check_file" - return 1 - fi - - # build all boards that have a board.json file - for board_json in $(find boards/ -name board.json | sort); do - build_board $board_json $@ - done -} - -function build_cc3200_boards { - build_boards hal/cc3200_hal.c $1 $2 zip -} - -function build_esp32_boards { - build_boards modesp32.c $1 $2 bin elf map uf2 app-bin -} - -function build_esp8266_boards { - build_boards modesp.c $1 $2 bin elf map -} - -function build_mimxrt_boards { - build_boards modmimxrt.c $1 $2 bin hex -} - -function build_nrf_boards { - build_boards nrfx_glue.h $1 $2 bin hex uf2 -} - -function build_renesas_ra_boards { - build_boards ra_it.c $1 $2 bin hex -} - -function build_rp2_boards { - build_boards modrp2.c $1 $2 uf2 -} - -function build_samd_boards { - build_boards samd_soc.c $1 $2 uf2 -} - -function build_stm32_boards { - build_boards modpyb.c $1 $2 dfu hex -} diff --git a/tools/autobuild/build-downloads.py b/tools/autobuild/build-downloads.py deleted file mode 100755 index c03d98aa5dea..000000000000 --- a/tools/autobuild/build-downloads.py +++ /dev/null @@ -1,104 +0,0 @@ -#!/usr/bin/env python3 - -import glob -import json -import os -import sys - -VALID_FEATURES = { - # Connectivity - "BLE", - "CAN", - "Ethernet", - "LoRa", - "USB", - "USB-C", - "WiFi", - # MCU features - "Dual-core", - "External Flash", - "External RAM", - # Form factor - "Feather", - # Connectors / sockets - "JST-PH", - "JST-SH", - "mikroBUS", - "microSD", - "SDCard", - # Sensors - "Environment Sensor", - "IMU", - # Other - "Audio Codec", - "Battery Charging", - "Camera", - "DAC", - "Display", - "Microphone", - "PoE", - "RGB LED", - "Secure Element", -} - - -def main(repo_path, output_path): - boards_index = [] - board_ids = set() - - for board_json in glob.glob(os.path.join(repo_path, "ports/*/boards/*/board.json")): - # Relative path to the board directory (e.g. "ports/stm32/boards/PYBV11"). - board_dir = os.path.dirname(board_json) - # Relative path to the port (e.g. "ports/stm32") - port_dir = os.path.dirname(os.path.dirname(board_dir)) - - with open(board_json, "r") as f: - blob = json.load(f) - - features = set(blob.get("features", [])) - if not features.issubset(VALID_FEATURES): - print( - board_json, - "unknown features:", - features.difference(VALID_FEATURES), - file=sys.stderr, - ) - sys.exit(1) - - # Use "id" if specified, otherwise default to board dir (e.g. "PYBV11"). - # We allow boards to override ID for the historical build names. - blob["id"] = blob.get("id", os.path.basename(board_dir)) - - # Check for duplicate board IDs. - if blob["id"] in board_ids: - print("Duplicate board ID: '{}'".format(blob["id"]), file=sys.stderr) - board_ids.add(blob["id"]) - - # Add in default fields. - blob["port"] = os.path.basename(port_dir) - blob["build"] = os.path.basename(board_dir) - boards_index.append(blob) - - # Create the board markdown, which is the concatenation of the - # default "board.md" file (if exists), as well as any flashing - # instructions. - board_markdown = os.path.join(board_dir, "board.md") - with open(os.path.join(output_path, blob["id"] + ".md"), "w") as f: - if os.path.exists(board_markdown): - with open(board_markdown, "r") as fin: - f.write(fin.read()) - - if blob["deploy"]: - f.write("\n\n## Installation instructions\n") - for deploy in blob["deploy"]: - with open(os.path.join(board_dir, deploy), "r") as fin: - f.write(fin.read()) - - # Write the full index for the website to load. - with open(os.path.join(output_path, "index.json"), "w") as f: - json.dump(boards_index, f, indent=4, sort_keys=True) - f.write("\n") - - -if __name__ == "__main__": - main(sys.argv[1], sys.argv[2]) diff --git a/tools/boardgen.py b/tools/boardgen.py new file mode 100644 index 000000000000..caa9fe851c76 --- /dev/null +++ b/tools/boardgen.py @@ -0,0 +1,556 @@ +# This file is part of the MicroPython project, http://micropython.org/ +# +# The MIT License (MIT) +# +# Copyright (c) 2023 Jim Mussared +# +# 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. + +# This script contains common functionality to assist a port in implementing +# make-pins.py, which is used to emit compile-time definitions of pin, AF, and +# ADC objects based on inputs from the vendor HAL/SDK and the board +# definition's pins.csv. + +# The pins.csv file can contain empty lines, comments (a line beginning with "#") +# or pin definition lines. Pin definition lines must be of the form: +# +# board,cpu +# +# Where "board" is the user-facing name of the pin as specified by the particular +# board layout and markings, and "cpu" is the corresponding name of the CPU/MCU +# pin. +# +# The "board" entry may be absent if the CPU pin has no additional name, and both +# entries may start with "-" to hide them from the corresponding Python dict of +# pins, and hence hide them from the user (but they are still accessible in C). +# +# For example, take the following pins.csv file: +# +# X1,PA0 +# -X2,PA1 +# X3,-PA2 +# -X4,-PA3 +# ,PA4 +# ,-PA5 +# +# The first row here configures: +# - The CPU pin PA0 is labelled X1. +# - The Python user can access both by the names Pin("X1") and Pin("A0"). +# - The Python user can access both by the members Pin.board.X1 and Pin.cpu.A0. +# - In C code they are available as pyb_pin_X1 and pin_A0. +# +# Prefixing the names with "-" hides them from the user. The following table +# summarises the various possibilities: +# +# pins.csv entry | board name | cpu name | C board name | C cpu name +# ---------------+------------+----------+--------------+----------- +# X1,PA0 "X1" "A0" pyb_pin_X1 pin_A0 +# -X2,PA1 - "A1" pyb_pin_X2 pin_A1 +# X3,-PA2 "X3" - pyb_pin_X3 pin_A2 +# -X4,-PA3 - - pyb_pin_X4 pin_A3 +# ,PA4 - "A4" - pin_A4 +# ,-PA5 - - - pin_A5 + +import argparse +import csv +import os +import sys + + +class PinGeneratorError(Exception): + pass + + +# A port should define a subclass of Pin that knows how to validate cpu/board +# names and emits the required structures. +class Pin: + def __init__(self, cpu_pin_name): + self._cpu_pin_name = cpu_pin_name + # Optional aliases for the board from pins.csv. Each entry is a tuple + # of (name, hidden). Hidden board pins are in pins.csv with with a "-" + # prefix and will available to C but not Python. + self._board_pin_names = set() + # An unavailable pin is one that is not explicitly mentioned at all in + # pins.csv (or added explicitly with PinGenerator.add_cpu_pin). + self._available = False + # A hidden pin is one that is in pins.csv with a "-" prefix and will + # be still available to C but not Python. + self._hidden = False + # Reference to the PinGenerator instance. + self._generator = None + + # The name of the pin to use in MP_QSTR_{} or pin_{}. Defaults to the cpu name. + def name(self): + return self._cpu_pin_name + + # Add a board alias (e.g. from pins.csv). + def add_board_pin_name(self, board_pin_name, hidden=False): + self._board_pin_names.add( + ( + board_pin_name, + hidden, + ) + ) + + # Override this to handle an af specified in af.csv. + def add_af(self, af_idx, af_name, af): + raise NotImplementedError + + # Override this to verify that naming matches the MCU standard (e.g. "GPIOn" or "PXn"). + @staticmethod + def validate_cpu_pin_name(cpu_pin_name): + if not cpu_pin_name.strip(): + raise PinGeneratorError("Missing cpu pin name") + + # Override this to provide additional validation of board names. + @staticmethod + def validate_board_pin_name(board_pin_name): + # TODO: ensure this is a valid Python/C identifier and can be used as MP_QSTR_foo. + pass + + # Must be implemented when using NumericPinGenerator. + # Returns the integer index, or None to exclude this pin from the table. + def index(self): + raise NotImplementedError + + # Can be overridden when using NumericPinGenerator. + # Returns a string which is a C expression that evaluates to the index + # e.g. `GPIO_NUM_7`. + # This is used whenever the index is emitted in source code and defaults + # to just returning the pin index as a literal. + # Return None to exclude this pin from the table. + def index_name(self): + i = self.index() + return str(i) if i is not None else None + + # Returns an expression that defines the pin. e.g. `{ .base { ... }, .x }`. + # This is used as the RHS of the `const machine_pin_obj_t + # pin_EXT_GPIO0_obj =` statements for named pins, and elements of + # `machine_pin_obj_table` for numeric pins. + # This will typically might be implemented as an invocation of a macro + # defined in the port-specific prefix. + def definition(self): + raise NotImplementedError + + # Whether the pin object should be declared as "const". This should be True + # for most pins, but some special cases (e.g. external pins on rp2) might + # need mutable pin objects (e.g. to track current pin state). + def is_const(self): + return True + + # Optionally return a preprocessor expression that can be used to suppress + # this pin (e.g. `MICROPY_HW_ENABLE_GPIOn`). + def enable_macro(self): + return None + + # Override this to output any additional per-pin definitions or other + # content that should appear at the start of the source output. + # This could be used to define additional objects such as IRQs or AFs. + def print_source(self, out_source): + pass + + +# A port should define a subclass of PinGenerator (or NumericPinGenerator). +class PinGenerator: + def __init__(self, pin_type, enable_af=False): + self._pins = [] + self._pin_type = pin_type + self._enable_af = enable_af + + # Allows a port to define a known cpu pin (without relying on it being in the + # csv file). + def add_cpu_pin(self, cpu_pin_name, available=True): + pin = self._pin_type(cpu_pin_name) + pin._available = available + self._pins.append(pin) + pin._generator = self + return pin + + # Iterate just the available pins (i.e. ones in pins.csv). + def available_pins(self, exclude_hidden=False): + for pin in self._pins: + if not pin._available: + continue + if exclude_hidden and pin._hidden: + continue + yield pin + + # Allows a port to add additional command-line arguments to be handled. + def extra_args(self, parser): + pass + + # Load board->cpu mapping from csv. + def parse_board_csv(self, filename): + with open(filename, "r") as csvfile: + rows = csv.reader(csvfile) + for linenum, row in enumerate(rows): + try: + # Skip empty lines, and lines starting with "#". + if len(row) == 0 or row[0].startswith("#"): + continue + + # Lines must be pairs of names. + if len(row) != 2: + raise PinGeneratorError("Expecting two entries in each row") + board_pin_name, cpu_pin_name = (x.strip() for x in row) + + # All rows must include a cpu name. + cpu_hidden = False + if cpu_pin_name.startswith("-"): + cpu_hidden = True + cpu_pin_name = cpu_pin_name[1:] + self._pin_type.validate_cpu_pin_name(cpu_pin_name) + pin = self.find_pin_by_cpu_pin_name(cpu_pin_name, create=True) + pin._available = True # It's in pins.csv so must be available. + pin._hidden = cpu_hidden # Optionally don't make available to Python. + + # Rows can optionally alias to a board name. + if board_pin_name: + board_hidden = False + if board_pin_name.startswith("-"): + board_hidden = True + board_pin_name = board_pin_name[1:] + self._pin_type.validate_board_pin_name(board_pin_name) + pin.add_board_pin_name(board_pin_name, board_hidden) + + # Inject "file:line: " into the exception. + except PinGeneratorError as er: + raise PinGeneratorError("{}:{}: {}".format(filename, linenum, er)) + + def parse_af_csv(self, filename, header_rows=1, pin_col=0, af_col=1): + headings = {} + with open(filename, "r") as csvfile: + rows = csv.reader(csvfile) + for linenum, row in enumerate(rows): + try: + # Skip empty lines, and lines starting with "#". + if len(row) == 0 or row[0].startswith("#"): + continue + + # Consume `header_rows` non-blank/comment rows at the start. + if header_rows: + if not headings: + # If this is the first header row then initialise + # the headings dict. + for af_idx, header in enumerate(row[af_col:]): + headings[af_idx] = header.strip() + header_rows -= 1 + continue + + # Lines must be pairs of names. + if len(row) <= max(pin_col, af_col): + raise PinGeneratorError( + "Expecting {} entries in each row".format(max(pin_col, af_col)) + ) + + cpu_pin_name = row[pin_col].strip() + if cpu_pin_name == "-": + continue + self._pin_type.validate_cpu_pin_name(cpu_pin_name) + pin = self.find_pin_by_cpu_pin_name(cpu_pin_name, create=True) + + for af_idx, af in enumerate(row[af_col:]): + af = af.strip() + if not af: + continue + pin.add_af(af_idx, headings.get(af_idx, ""), af) + + # Inject "file:line: " into the exception. + except PinGeneratorError as er: + raise PinGeneratorError("{}:{}: {}".format(filename, linenum, er)) + + # Find an existing pin. + def find_pin_by_cpu_pin_name(self, cpu_pin_name, create=True): + for pin in self._pins: + if pin._cpu_pin_name == cpu_pin_name: + return pin + if create: + return self.add_cpu_pin(cpu_pin_name, available=False) + else: + raise PinGeneratorError("Unknown cpu pin {}".format(cpu_pin_name)) + + # Print the locals dict for Pin.board. + def print_board_locals_dict(self, out_source): + print(file=out_source) + print( + "STATIC const mp_rom_map_elem_t machine_pin_board_pins_locals_dict_table[] = {", + file=out_source, + ) + for pin in self.available_pins(): + for board_pin_name, board_hidden in pin._board_pin_names: + if board_hidden: + # Don't include hidden pins in Pins.board. + continue + + # We don't use the enable macro for board pins, because they + # shouldn't be referenced in pins.csv unless they're + # available. + print( + " {{ MP_ROM_QSTR(MP_QSTR_{:s}), MP_ROM_PTR(pin_{:s}) }},".format( + board_pin_name, + pin.name(), + ), + file=out_source, + ) + print("};", file=out_source) + print( + "MP_DEFINE_CONST_DICT(machine_pin_board_pins_locals_dict, machine_pin_board_pins_locals_dict_table);", + file=out_source, + ) + + # Print the locals dict for Pin.cpu. + def print_cpu_locals_dict(self, out_source): + print(file=out_source) + print( + "STATIC const mp_rom_map_elem_t machine_pin_cpu_pins_locals_dict_table[] = {", + file=out_source, + ) + for pin in self.available_pins(exclude_hidden=True): + m = pin.enable_macro() + if m: + print(" #if {}".format(m), file=out_source) + print( + " {{ MP_ROM_QSTR(MP_QSTR_{:s}), MP_ROM_PTR(pin_{:s}) }},".format( + pin.name(), + pin.name(), + ), + file=out_source, + ) + if m: + print(" #endif", file=out_source) + print("};", file=out_source) + print( + "MP_DEFINE_CONST_DICT(machine_pin_cpu_pins_locals_dict, machine_pin_cpu_pins_locals_dict_table);", + file=out_source, + ) + + # NumericPinGenerator can override this to use an entry in machine_pin_obj_table. + def _cpu_pin_pointer(self, pin): + return "&pin_{:s}_obj".format(pin.name()) + + # Allow a port to prefix the board pin macro names with something. + # e.g. STM32 does pyb_pin_NAME whereas other ports using pin_NAME. + def board_name_define_prefix(self): + return "" + + # Print the pin_CPUNAME and pin_BOARDNAME macros. + def print_defines(self, out_header, cpu=True, board=True): + # Provide #defines for each cpu pin. + for pin in self.available_pins(): + print(file=out_header) + m = pin.enable_macro() + if m: + print("#if {}".format(m), file=out_header) + + # #define pin_CPUNAME (...) + if cpu: + print( + "#define pin_{:s} ({:s})".format(pin.name(), self._cpu_pin_pointer(pin)), + file=out_header, + ) + + # #define pin_BOARDNAME (pin_CPUNAME) + if board: + for board_pin_name, _board_hidden in pin._board_pin_names: + # Note: Hidden board pins are still available to C via the macro. + # Note: The RHS isn't wrapped in (), which is necessary to make the + # STATIC_AF_ macro work on STM32. + print( + "#define {:s}pin_{:s} pin_{:s}".format( + self.board_name_define_prefix(), + board_pin_name, + pin.name(), + ), + file=out_header, + ) + + if m: + print("#endif", file=out_header) + + def print_pin_objects(self, out_source): + print(file=out_source) + for pin in self.available_pins(): + m = pin.enable_macro() + if m: + print("#if {}".format(m), file=out_source) + print( + "{:s}machine_pin_obj_t pin_{:s}_obj = {:s};".format( + "const " if pin.is_const() else "", + pin.name(), + pin.definition(), + ), + file=out_source, + ) + if m: + print("#endif", file=out_source) + + def print_pin_object_externs(self, out_header): + print(file=out_header) + for pin in self.available_pins(): + m = pin.enable_macro() + if m: + print("#if {}".format(m), file=out_header) + print( + "extern {:s}machine_pin_obj_t pin_{:s}_obj;".format( + "const " if pin.is_const() else "", + pin.name(), + ), + file=out_header, + ) + if m: + print("#endif", file=out_header) + + def print_source(self, out_source): + self.print_pin_objects(out_source) + self.print_cpu_locals_dict(out_source) + self.print_board_locals_dict(out_source) + + def print_header(self, out_header): + self.print_pin_object_externs(out_header) + self.print_defines(out_header) + + # A port can override this if it has extra input files (e.g. af.csv) to load. + def load_inputs(self, out_source): + # Optionally load pins.csv to get cpu->board name mappings. + if self._enable_af and self.args.af_csv: + print("// --af-csv {:s}".format(self.args.af_csv), file=out_source) + self.parse_af_csv(self.args.af_csv) + + # Optionally load pins.csv to get cpu->board name mappings. + if self.args.board_csv: + print("// --board-csv {:s}".format(self.args.board_csv), file=out_source) + self.parse_board_csv(self.args.board_csv) + + # Prepend the prefix file to the start of the output. + if self.args.prefix: + print("// --prefix {:s}".format(self.args.prefix), file=out_source) + print(file=out_source) + with open(self.args.prefix, "r") as prefix_file: + print(prefix_file.read(), end="", file=out_source) + + # A port can override this to do extra work after the main source+header + # have been written, such as generating additional header files. + def generate_extra_files(self): + pass + + def main(self): + parser = argparse.ArgumentParser(description="Generate board specific pin file") + parser.add_argument("--board-csv") + if self._enable_af: + parser.add_argument("--af-csv") + parser.add_argument("--prefix") + parser.add_argument("--output-source") + parser.add_argument("--output-header") + self.extra_args(parser) + self.args = parser.parse_args() + + try: + with open(self.args.output_source, "w") as out_source: + print("// This file was automatically generated by make-pins.py", file=out_source) + print("//", file=out_source) + + # Load additional files (including port-specific ones). + self.load_inputs(out_source) + + # Allow a port to print arbitrary per-pin content. + for pin in self.available_pins(): + pin.print_source(out_source) + + # Print the tables and dictionaries. + self.print_source(out_source) + + with open(self.args.output_header, "w") as out_header: + self.print_header(out_header) + + self.generate_extra_files() + except PinGeneratorError as er: + print(er) + sys.exit(1) + + +# For ports that use numeric pin identifiers (e.g. ESP32, rp2). +# This emits the machine_pin_obj_t instances as an array (machine_pin_obj_table). +class NumericPinGenerator(PinGenerator): + # This should return a const expression that is the number of GPIO pins + # for this board. + def cpu_table_size(self): + raise NotImplementedError + + def print_cpu_table(self, out_source): + # Print machine_pin_obj_table, where each element is `[n] = {obj}`. + print(file=out_source) + print( + "const machine_pin_obj_t machine_pin_obj_table[{}] = {{".format(self.cpu_table_size()), + file=out_source, + ) + for pin in self.available_pins(): + n = pin.index_name() + if n is None: + continue + + m = pin.enable_macro() + if m: + print(" #if {}".format(m), file=out_source) + print( + " [{:s}] = {:s},".format( + pin.index_name(), + pin.definition(), + ), + file=out_source, + ) + if m: + print(" #endif", file=out_source) + print("};", file=out_source) + + # For pins that do not have an index, print them out in the same style as PinGenerator. + print(file=out_source) + for pin in self.available_pins(): + n = pin.index_name() + if n is not None: + continue + + m = pin.enable_macro() + if m: + print("#if {}".format(m), file=out_source) + print( + "{:s}machine_pin_obj_t pin_{:s}_obj = {:s};".format( + "const " if pin.is_const() else "", + pin.name(), + pin.definition(), + ), + file=out_source, + ) + if m: + print("#endif", file=out_source) + + # Replace PinGenerator's implementation to print the numeric table. + def print_source(self, out_source): + self.print_cpu_table(out_source) + self.print_board_locals_dict(out_source) + + # Replace PinGenerator's implementation to only print the defines. + def print_header(self, out_header): + self.print_defines(out_header) + + def _cpu_pin_pointer(self, pin): + n = pin.index_name() + if n is not None: + return "&machine_pin_obj_table[{:s}]".format(pin.index_name()) + else: + return super()._cpu_pin_pointer(pin) diff --git a/tools/ci.sh b/tools/ci.sh index efa492bd19ed..95b1f19ffc4a 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -6,6 +6,9 @@ else MAKEOPTS="-j$(sysctl -n hw.ncpu)" fi +# Ensure known OPEN_MAX (NO_FILES) limit. +ulimit -n 1024 + ######################################################################################## # general helper functions @@ -15,17 +18,16 @@ function ci_gcc_arm_setup { } ######################################################################################## -# code formatting +# c code formatting -function ci_code_formatting_setup { +function ci_c_code_formatting_setup { sudo apt-get install uncrustify - pip3 install black uncrustify --version - black --version } -function ci_code_formatting_run { - tools/codeformat.py -v +function ci_c_code_formatting_run { + # Only run on C files. The ruff rule runs separately on Python. + tools/codeformat.py -v -c } ######################################################################################## @@ -44,7 +46,9 @@ function ci_code_spell_run { function ci_commit_formatting_run { git remote add upstream https://github.com/micropython/micropython.git - git fetch --depth=100 upstream master + git fetch --depth=100 upstream master + # If the common ancestor commit hasn't been found, fetch more. + git merge-base upstream/master HEAD || git fetch --unshallow upstream master # For a PR, upstream/master..HEAD ends with a merge commit into master, exclude that one. tools/verifygitlog.py -v upstream/master..HEAD --no-merges } @@ -68,6 +72,8 @@ function ci_code_size_build { git checkout -b pull_request # save the current location git remote add upstream https://github.com/micropython/micropython.git git fetch --depth=100 upstream master + # If the common ancestor commit hasn't been found, fetch more. + git merge-base upstream/master HEAD || git fetch --unshallow upstream master # build reference, save to size0 # ignore any errors with this build, in case master is failing git checkout `git merge-base --fork-point upstream/master pull_request` @@ -115,26 +121,45 @@ function ci_cc3200_build { ######################################################################################## # ports/esp32 -function ci_esp32_idf50_setup { +# GitHub tag of ESP-IDF to use for CI (note: must be a tag or a branch) +IDF_VER=v5.0.4 + +export IDF_CCACHE_ENABLE=1 + +function ci_esp32_idf_setup { pip3 install pyelftools - git clone https://github.com/espressif/esp-idf.git - git -C esp-idf checkout v5.0.2 + git clone --depth 1 --branch $IDF_VER https://github.com/espressif/esp-idf.git + # doing a treeless clone isn't quite as good as --shallow-submodules, but it + # is smaller than full clones and works when the submodule commit isn't a head. + git -C esp-idf submodule update --init --recursive --filter=tree:0 ./esp-idf/install.sh } -function ci_esp32_build { +function ci_esp32_build_common { source esp-idf/export.sh make ${MAKEOPTS} -C mpy-cross make ${MAKEOPTS} -C ports/esp32 submodules +} + +function ci_esp32_build_cmod_spiram_s2 { + ci_esp32_build_common + make ${MAKEOPTS} -C ports/esp32 \ USER_C_MODULES=../../../examples/usercmodule/micropython.cmake \ FROZEN_MANIFEST=$(pwd)/ports/esp32/boards/manifest_test.py - make ${MAKEOPTS} -C ports/esp32 BOARD=ESP32_GENERIC_C3 - make ${MAKEOPTS} -C ports/esp32 BOARD=ESP32_GENERIC_S2 - make ${MAKEOPTS} -C ports/esp32 BOARD=ESP32_GENERIC_S3 # Test building native .mpy with xtensawin architecture. ci_native_mpy_modules_build xtensawin + + make ${MAKEOPTS} -C ports/esp32 BOARD=ESP32_GENERIC BOARD_VARIANT=SPIRAM + make ${MAKEOPTS} -C ports/esp32 BOARD=ESP32_GENERIC_S2 +} + +function ci_esp32_build_s3_c3 { + ci_esp32_build_common + + make ${MAKEOPTS} -C ports/esp32 BOARD=ESP32_GENERIC_S3 + make ${MAKEOPTS} -C ports/esp32 BOARD=ESP32_GENERIC_C3 } ######################################################################################## @@ -352,19 +377,6 @@ function ci_stm32_nucleo_build { diff $BUILD_WB55/firmware.unpack.dfu $BUILD_WB55/firmware.unpack_no_sk.dfu } -######################################################################################## -# ports/teensy - -function ci_teensy_setup { - ci_gcc_arm_setup -} - -function ci_teensy_build { - make ${MAKEOPTS} -C mpy-cross - make ${MAKEOPTS} -C ports/teensy submodules - make ${MAKEOPTS} -C ports/teensy -} - ######################################################################################## # ports/unix @@ -458,6 +470,15 @@ function ci_unix_standard_run_tests { ci_unix_run_tests_full_helper standard } +function ci_unix_standard_v2_build { + ci_unix_build_helper VARIANT=standard MICROPY_PREVIEW_VERSION_2=1 + ci_unix_build_ffi_lib_helper gcc +} + +function ci_unix_standard_v2_run_tests { + ci_unix_run_tests_full_helper standard +} + function ci_unix_coverage_setup { sudo pip3 install setuptools sudo pip3 install pyelftools diff --git a/tools/codeformat.py b/tools/codeformat.py index fa831448d5da..8a95e7894f2c 100644 --- a/tools/codeformat.py +++ b/tools/codeformat.py @@ -25,6 +25,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. +# CIRCUITPY-CHANGE: more imports import argparse import glob import fnmatch @@ -36,6 +37,7 @@ import subprocess # Relative to top-level repo dir. +# CIRCUITPY-CHANGE: different directory trees PATHS = [ # C "main.c", @@ -58,6 +60,7 @@ "tests/circuitpython-*/**/*.py", ] +# CIRCUITPY-CHANGE: different exclusions EXCLUSIONS = [ # STM32 build includes generated Python code. "ports/*/build*", @@ -67,9 +70,12 @@ "tests/**/repl_*.py", # don't reindent this third-party code we vendored in "ports/raspberrypi/lwip_src", + # line breaks + "tools/mpy-tool.py", ] +# CIRCUITPY-CHANGE # None of the standard Python path matching routines implement the matching # we want, which is most like git's "pathspec" version of globs. # In particular, we want "**/" to match all directories. @@ -113,7 +119,7 @@ def transform(m): TOP = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) UNCRUSTIFY_CFG = os.path.join(TOP, "tools/uncrustify.cfg") - +# CIRCUITPY-CHANGE C_EXTS = ( ".c", ".h", @@ -140,6 +146,7 @@ def list_files(args): return sorted(arg for arg in args if path_rx.match(relative_filename(arg))) +# CIRCUITPY-CHANGE: handle inline documentation def fixup_c(filename): # Read file. with open(filename) as f: @@ -188,6 +195,7 @@ def fixup_c(filename): assert not dedent_stack, filename +# CIRCUITPY-CHANGE: different options and logic. def main(): cmd_parser = argparse.ArgumentParser( description="Auto-format C and Python files -- to be used via pre-commit only." diff --git a/tools/manifestfile.py b/tools/manifestfile.py index 295170c34cab..aa85c4650f2a 100644 --- a/tools/manifestfile.py +++ b/tools/manifestfile.py @@ -194,6 +194,8 @@ def __init__(self, mode, path_vars=None): self._visited = set() # Stack of metadata for each level. self._metadata = [ManifestPackageMetadata()] + # Registered external libraries. + self._libraries = {} def _resolve_path(self, path): # Convert path to an absolute path, applying variable substitutions. @@ -208,6 +210,7 @@ def _manifest_globals(self, kwargs): "metadata": self.metadata, "include": self.include, "require": self.require, + "add_library": self.add_library, "package": self.package, "module": self.module, "options": IncludeOptions(**kwargs), @@ -388,14 +391,25 @@ def include(self, manifest_path, is_require=False, **kwargs): if is_require: self._metadata.pop() - def require(self, name, version=None, unix_ffi=False, pypi=None, **kwargs): + def _require_from_path(self, library_path, name, version, extra_kwargs): + for root, dirnames, filenames in os.walk(library_path): + if os.path.basename(root) == name and "manifest.py" in filenames: + self.include(root, is_require=True, **extra_kwargs) + return True + return False + + def require(self, name, version=None, unix_ffi=False, pypi=None, library=None, **kwargs): """ - Require a module by name from micropython-lib. + Require a package by name from micropython-lib. Optionally specify unix_ffi=True to use a module from the unix-ffi directory. Optionally specify pipy="package-name" to indicate that this should use the named package from PyPI when building for CPython. + + Optionally specify library="name" to reference a package from a + library that has been previously registered with add_library(). Otherwise + micropython-lib will be used. """ self._metadata[-1].check_initialised(self._mode) @@ -406,27 +420,46 @@ def require(self, name, version=None, unix_ffi=False, pypi=None, **kwargs): self._pypi_dependencies.append(pypi) return - if self._path_vars["MPY_LIB_DIR"]: + if library is not None: + # Find package in external library. + if library not in self._libraries: + raise ValueError("Unknown library '{}' for require('{}').".format(library, name)) + library_path = self._libraries[library] + # Search for {library_path}/**/{name}/manifest.py. + if not self._require_from_path(library_path, name, version, kwargs): + raise ValueError( + "Package '{}' not found in external library '{}' ({}).".format( + name, library, library_path + ) + ) + elif self._path_vars["MPY_LIB_DIR"]: + # Find package in micropython-lib, in one of the three top-level directories. lib_dirs = ["micropython", "python-stdlib", "python-ecosys"] if unix_ffi: - # Search unix-ffi only if unix_ffi=True, and make unix-ffi modules + # Additionally search unix-ffi only if unix_ffi=True, and make unix-ffi modules # take precedence. lib_dirs = ["unix-ffi"] + lib_dirs for lib_dir in lib_dirs: # Search for {lib_dir}/**/{name}/manifest.py. - for root, dirnames, filenames in os.walk( - os.path.join(self._path_vars["MPY_LIB_DIR"], lib_dir) + if self._require_from_path( + os.path.join(self._path_vars["MPY_LIB_DIR"], lib_dir), name, version, kwargs ): - if os.path.basename(root) == name and "manifest.py" in filenames: - self.include(root, is_require=True, **kwargs) - return + return - raise ValueError("Library not found in local micropython-lib: {}".format(name)) + raise ValueError("Package '{}' not found in local micropython-lib.".format(name)) else: # TODO: HTTP request to obtain URLs from manifest.json. raise ValueError("micropython-lib not available for require('{}').", name) + def add_library(self, library, library_path): + """ + Register the path to an external named library. + + This allows require("name", library="library") to find packages in that library. + """ + self._libraries[library] = self._resolve_path(library_path) + def package(self, package_path, files=None, base_path=".", opt=None): """ Define a package, optionally restricting to a set of files. diff --git a/tools/merge_micropython.py b/tools/merge_micropython.py index f90316bed388..37d44c3b7e3a 100644 --- a/tools/merge_micropython.py +++ b/tools/merge_micropython.py @@ -8,7 +8,7 @@ I add a sys.exit(0) after a section, and once a section runs, I delete it temporarily and move on to the next section. -- dhalbert -Updated for v1.21.0 merge - dhalbert +Updated for v1.22.2 merge - dhalbert """ @@ -16,6 +16,7 @@ import sh from sh import git +import sys out_buf = StringIO() @@ -27,16 +28,14 @@ "esp8266", "mimxrt", "minimal", - "nordic", + "nrf", "pic16bit", "powerpc", "qemu-arm", - "raspberrypi", "renesas-ra", "rp2", "samd", "stm32", - "teensy", "webassembly", "windows", "zephyr", diff --git a/tools/mpremote/mpremote/transport.py b/tools/mpremote/mpremote/transport.py deleted file mode 100644 index 6e9a77b2bb6c..000000000000 --- a/tools/mpremote/mpremote/transport.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python3 -# -# This file is part of the MicroPython project, http://micropython.org/ -# -# The MIT License (MIT) -# -# Copyright (c) 2023 Jim Mussared -# -# 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. - - -class TransportError(Exception): - pass - - -class Transport: - pass diff --git a/tools/mpremote/mpremote/transport_serial.py b/tools/mpremote/mpremote/transport_serial.py deleted file mode 100644 index e04f5b4ac9ab..000000000000 --- a/tools/mpremote/mpremote/transport_serial.py +++ /dev/null @@ -1,1211 +0,0 @@ -#!/usr/bin/env python3 -# -# This file is part of the MicroPython project, http://micropython.org/ -# -# The MIT License (MIT) -# -# Copyright (c) 2014-2021 Damien P. George -# Copyright (c) 2017 Paul Sokolovsky -# Copyright (c) 2023 Jim Mussared -# -# 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. - -# This is based on the serial-only parts of tools/pyboard.py, with Python 2 -# support removed, and is currently in the process of being refactored to -# support multiple transports (webrepl, socket, BLE, etc). At the moment, -# SerialTransport is just the old Pyboard+PyboardExtended class without any -# of this refactoring. The API is going to change significantly. - -# Once the API is stabilised, the idea is that mpremote can be used both -# as a command line tool and a library for interacting with devices. - -import ast, io, errno, os, re, struct, sys, time -from collections import namedtuple -from errno import EPERM -from .console import VT_ENABLED -from .transport import TransportError, Transport - - -def stdout_write_bytes(b): - b = b.replace(b"\x04", b"") - sys.stdout.buffer.write(b) - sys.stdout.buffer.flush() - - -listdir_result = namedtuple("dir_result", ["name", "st_mode", "st_ino", "st_size"]) - - -def reraise_filesystem_error(e, info): - if len(e.args) >= 3: - if b"OSError" in e.args[2] and b"ENOENT" in e.args[2]: - raise FileNotFoundError(info) - raise - - -class SerialTransport(Transport): - def __init__(self, device, baudrate=115200, wait=0, exclusive=True): - self.in_raw_repl = False - self.use_raw_paste = True - self.device_name = device - self.mounted = False - - import serial - import serial.tools.list_ports - - # Set options, and exclusive if pyserial supports it - serial_kwargs = {"baudrate": baudrate, "interCharTimeout": 1} - if serial.__version__ >= "3.3": - serial_kwargs["exclusive"] = exclusive - - delayed = False - for attempt in range(wait + 1): - try: - if device.startswith("rfc2217://"): - self.serial = serial.serial_for_url(device, **serial_kwargs) - elif os.name == "nt": - self.serial = serial.Serial(**serial_kwargs) - self.serial.port = device - portinfo = list(serial.tools.list_ports.grep(device)) # type: ignore - if portinfo and portinfo[0].manufacturer != "Microsoft": - # ESP8266/ESP32 boards use RTS/CTS for flashing and boot mode selection. - # DTR False: to avoid using the reset button will hang the MCU in bootloader mode - # RTS False: to prevent pulses on rts on serial.close() that would POWERON_RESET an ESPxx - self.serial.dtr = False # DTR False = gpio0 High = Normal boot - self.serial.rts = False # RTS False = EN High = MCU enabled - self.serial.open() - else: - self.serial = serial.Serial(device, **serial_kwargs) - break - except OSError: - if wait == 0: - continue - if attempt == 0: - sys.stdout.write("Waiting {} seconds for pyboard ".format(wait)) - delayed = True - time.sleep(1) - sys.stdout.write(".") - sys.stdout.flush() - else: - if delayed: - print("") - raise TransportError("failed to access " + device) - if delayed: - print("") - - def close(self): - self.serial.close() - - def read_until(self, min_num_bytes, ending, timeout=10, data_consumer=None): - # if data_consumer is used then data is not accumulated and the ending must be 1 byte long - assert data_consumer is None or len(ending) == 1 - - data = self.serial.read(min_num_bytes) - if data_consumer: - data_consumer(data) - timeout_count = 0 - while True: - if data.endswith(ending): - break - elif self.serial.inWaiting() > 0: - new_data = self.serial.read(1) - if data_consumer: - data_consumer(new_data) - data = new_data - else: - data = data + new_data - timeout_count = 0 - else: - timeout_count += 1 - if timeout is not None and timeout_count >= 100 * timeout: - break - time.sleep(0.01) - return data - - def enter_raw_repl(self, soft_reset=True): - self.serial.write(b"\r\x03\x03") # ctrl-C twice: interrupt any running program - - # flush input (without relying on serial.flushInput()) - n = self.serial.inWaiting() - while n > 0: - self.serial.read(n) - n = self.serial.inWaiting() - - self.serial.write(b"\r\x01") # ctrl-A: enter raw REPL - - if soft_reset: - data = self.read_until(1, b"raw REPL; CTRL-B to exit\r\n>") - if not data.endswith(b"raw REPL; CTRL-B to exit\r\n>"): - print(data) - raise TransportError("could not enter raw repl") - - self.serial.write(b"\x04") # ctrl-D: soft reset - - # Waiting for "soft reboot" independently to "raw REPL" (done below) - # allows boot.py to print, which will show up after "soft reboot" - # and before "raw REPL". - data = self.read_until(1, b"soft reboot\r\n") - if not data.endswith(b"soft reboot\r\n"): - print(data) - raise TransportError("could not enter raw repl") - - data = self.read_until(1, b"raw REPL; CTRL-B to exit\r\n") - if not data.endswith(b"raw REPL; CTRL-B to exit\r\n"): - print(data) - raise TransportError("could not enter raw repl") - - self.in_raw_repl = True - - def exit_raw_repl(self): - self.serial.write(b"\r\x02") # ctrl-B: enter friendly REPL - self.in_raw_repl = False - - def follow(self, timeout, data_consumer=None): - # wait for normal output - data = self.read_until(1, b"\x04", timeout=timeout, data_consumer=data_consumer) - if not data.endswith(b"\x04"): - raise TransportError("timeout waiting for first EOF reception") - data = data[:-1] - - # wait for error output - data_err = self.read_until(1, b"\x04", timeout=timeout) - if not data_err.endswith(b"\x04"): - raise TransportError("timeout waiting for second EOF reception") - data_err = data_err[:-1] - - # return normal and error output - return data, data_err - - def raw_paste_write(self, command_bytes): - # Read initial header, with window size. - data = self.serial.read(2) - window_size = struct.unpack("") - if not data.endswith(b">"): - raise TransportError("could not enter raw repl") - - if self.use_raw_paste: - # Try to enter raw-paste mode. - self.serial.write(b"\x05A\x01") - data = self.serial.read(2) - if data == b"R\x00": - # Device understood raw-paste command but doesn't support it. - pass - elif data == b"R\x01": - # Device supports raw-paste mode, write out the command using this mode. - return self.raw_paste_write(command_bytes) - else: - # Device doesn't support raw-paste, fall back to normal raw REPL. - data = self.read_until(1, b"w REPL; CTRL-B to exit\r\n>") - if not data.endswith(b"w REPL; CTRL-B to exit\r\n>"): - print(data) - raise TransportError("could not enter raw repl") - # Don't try to use raw-paste mode again for this connection. - self.use_raw_paste = False - - # Write command using standard raw REPL, 256 bytes every 10ms. - for i in range(0, len(command_bytes), 256): - self.serial.write(command_bytes[i : min(i + 256, len(command_bytes))]) - time.sleep(0.01) - self.serial.write(b"\x04") - - # check if we could exec command - data = self.serial.read(2) - if data != b"OK": - raise TransportError("could not exec command (response: %r)" % data) - - def exec_raw(self, command, timeout=10, data_consumer=None): - self.exec_raw_no_follow(command) - return self.follow(timeout, data_consumer) - - def eval(self, expression, parse=False): - if parse: - ret = self.exec("print(repr({}))".format(expression)) - ret = ret.strip() - return ast.literal_eval(ret.decode()) - else: - ret = self.exec("print({})".format(expression)) - ret = ret.strip() - return ret - - def exec(self, command, data_consumer=None): - ret, ret_err = self.exec_raw(command, data_consumer=data_consumer) - if ret_err: - raise TransportError("exception", ret, ret_err) - return ret - - def execfile(self, filename): - with open(filename, "rb") as f: - pyfile = f.read() - return self.exec(pyfile) - - def fs_exists(self, src): - try: - self.exec("import os\nos.stat(%s)" % (("'%s'" % src) if src else "")) - return True - except TransportError: - return False - - def fs_ls(self, src): - cmd = ( - "import os\nfor f in os.ilistdir(%s):\n" - " print('{:12} {}{}'.format(f[3]if len(f)>3 else 0,f[0],'/'if f[1]&0x4000 else ''))" - % (("'%s'" % src) if src else "") - ) - self.exec(cmd, data_consumer=stdout_write_bytes) - - def fs_listdir(self, src=""): - buf = bytearray() - - def repr_consumer(b): - buf.extend(b.replace(b"\x04", b"")) - - cmd = "import os\nfor f in os.ilistdir(%s):\n" " print(repr(f), end=',')" % ( - ("'%s'" % src) if src else "" - ) - try: - buf.extend(b"[") - self.exec(cmd, data_consumer=repr_consumer) - buf.extend(b"]") - except TransportError as e: - reraise_filesystem_error(e, src) - - return [ - listdir_result(*f) if len(f) == 4 else listdir_result(*(f + (0,))) - for f in ast.literal_eval(buf.decode()) - ] - - def fs_stat(self, src): - try: - self.exec("import os") - return os.stat_result(self.eval("os.stat(%s)" % (("'%s'" % src)), parse=True)) - except TransportError as e: - reraise_filesystem_error(e, src) - - def fs_cat(self, src, chunk_size=256): - cmd = ( - "with open('%s') as f:\n while 1:\n" - " b=f.read(%u)\n if not b:break\n print(b,end='')" % (src, chunk_size) - ) - self.exec(cmd, data_consumer=stdout_write_bytes) - - def fs_readfile(self, src, chunk_size=256): - buf = bytearray() - - def repr_consumer(b): - buf.extend(b.replace(b"\x04", b"")) - - cmd = ( - "with open('%s', 'rb') as f:\n while 1:\n" - " b=f.read(%u)\n if not b:break\n print(b,end='')" % (src, chunk_size) - ) - try: - self.exec(cmd, data_consumer=repr_consumer) - except TransportError as e: - reraise_filesystem_error(e, src) - return ast.literal_eval(buf.decode()) - - def fs_writefile(self, dest, data, chunk_size=256): - self.exec("f=open('%s','wb')\nw=f.write" % dest) - while data: - chunk = data[:chunk_size] - self.exec("w(" + repr(chunk) + ")") - data = data[len(chunk) :] - self.exec("f.close()") - - def fs_cp(self, src, dest, chunk_size=256, progress_callback=None): - if progress_callback: - src_size = self.fs_stat(src).st_size - written = 0 - self.exec("fr=open('%s','rb')\nr=fr.read\nfw=open('%s','wb')\nw=fw.write" % (src, dest)) - while True: - data_len = int(self.exec("d=r(%u)\nw(d)\nprint(len(d))" % chunk_size)) - if not data_len: - break - if progress_callback: - written += data_len - progress_callback(written, src_size) - self.exec("fr.close()\nfw.close()") - - def fs_get(self, src, dest, chunk_size=256, progress_callback=None): - if progress_callback: - src_size = self.fs_stat(src).st_size - written = 0 - self.exec("f=open('%s','rb')\nr=f.read" % src) - with open(dest, "wb") as f: - while True: - data = bytearray() - self.exec("print(r(%u))" % chunk_size, data_consumer=lambda d: data.extend(d)) - assert data.endswith(b"\r\n\x04") - try: - data = ast.literal_eval(str(data[:-3], "ascii")) - if not isinstance(data, bytes): - raise ValueError("Not bytes") - except (UnicodeError, ValueError) as e: - raise TransportError("fs_get: Could not interpret received data: %s" % str(e)) - if not data: - break - f.write(data) - if progress_callback: - written += len(data) - progress_callback(written, src_size) - self.exec("f.close()") - - def fs_put(self, src, dest, chunk_size=256, progress_callback=None): - if progress_callback: - src_size = os.path.getsize(src) - written = 0 - self.exec("f=open('%s','wb')\nw=f.write" % dest) - with open(src, "rb") as f: - while True: - data = f.read(chunk_size) - if not data: - break - if sys.version_info < (3,): - self.exec("w(b" + repr(data) + ")") - else: - self.exec("w(" + repr(data) + ")") - if progress_callback: - written += len(data) - progress_callback(written, src_size) - self.exec("f.close()") - - def fs_mkdir(self, dir): - self.exec("import os\nos.mkdir('%s')" % dir) - - def fs_rmdir(self, dir): - self.exec("import os\nos.rmdir('%s')" % dir) - - def fs_rm(self, src): - self.exec("import os\nos.remove('%s')" % src) - - def fs_touch(self, src): - self.exec("f=open('%s','a')\nf.close()" % src) - - def filesystem_command(self, args, progress_callback=None, verbose=False): - def fname_remote(src): - if src.startswith(":"): - src = src[1:] - # Convert all path separators to "/", because that's what a remote device uses. - return src.replace(os.path.sep, "/") - - def fname_cp_dest(src, dest): - _, src = os.path.split(src) - if dest is None or dest == "": - dest = src - elif dest == ".": - dest = "./" + src - elif dest.endswith("/"): - dest += src - return dest - - cmd = args[0] - args = args[1:] - try: - if cmd == "cp": - srcs = args[:-1] - dest = args[-1] - if dest.startswith(":"): - op_remote_src = self.fs_cp - op_local_src = self.fs_put - else: - op_remote_src = self.fs_get - op_local_src = lambda src, dest, **_: __import__("shutil").copy(src, dest) - for src in srcs: - if verbose: - print("cp %s %s" % (src, dest)) - if src.startswith(":"): - op = op_remote_src - else: - op = op_local_src - src2 = fname_remote(src) - dest2 = fname_cp_dest(src2, fname_remote(dest)) - op(src2, dest2, progress_callback=progress_callback) - else: - ops = { - "cat": self.fs_cat, - "ls": self.fs_ls, - "mkdir": self.fs_mkdir, - "rm": self.fs_rm, - "rmdir": self.fs_rmdir, - "touch": self.fs_touch, - } - if cmd not in ops: - raise TransportError("'{}' is not a filesystem command".format(cmd)) - if cmd == "ls" and not args: - args = [""] - for src in args: - src = fname_remote(src) - if verbose: - print("%s :%s" % (cmd, src)) - ops[cmd](src) - except TransportError as er: - if len(er.args) > 1: - print(str(er.args[2], "ascii")) - else: - print(er) - self.exit_raw_repl() - self.close() - sys.exit(1) - - def mount_local(self, path, unsafe_links=False): - fout = self.serial - if self.eval('"RemoteFS" in globals()') == b"False": - self.exec(fs_hook_code) - self.exec("__mount()") - self.mounted = True - self.cmd = PyboardCommand(self.serial, fout, path, unsafe_links=unsafe_links) - self.serial = SerialIntercept(self.serial, self.cmd) - - def write_ctrl_d(self, out_callback): - self.serial.write(b"\x04") - if not self.mounted: - return - - # Read response from the device until it is quiet (with a timeout). - INITIAL_TIMEOUT = 0.5 - BANNER_TIMEOUT = 2 - QUIET_TIMEOUT = 0.1 - FULL_TIMEOUT = 5 - t_start = t_last_activity = time.monotonic() - data_all = b"" - soft_reboot_started = False - soft_reboot_banner = False - while True: - t = time.monotonic() - n = self.serial.inWaiting() - if n > 0: - data = self.serial.read(n) - out_callback(data) - data_all += data - t_last_activity = t - else: - if len(data_all) == 0: - if t - t_start > INITIAL_TIMEOUT: - return - else: - if t - t_start > FULL_TIMEOUT: - if soft_reboot_started: - break - return - - next_data_timeout = QUIET_TIMEOUT - - if not soft_reboot_started and data_all.find(b"MPY: soft reboot") != -1: - soft_reboot_started = True - - if soft_reboot_started and not soft_reboot_banner: - # Once soft reboot has been initiated, give some more time for the startup - # banner to be shown - if data_all.find(b"\nMicroPython ") != -1: - soft_reboot_banner = True - elif data_all.find(b"\nraw REPL; CTRL-B to exit\r\n") != -1: - soft_reboot_banner = True - else: - next_data_timeout = BANNER_TIMEOUT - - if t - t_last_activity > next_data_timeout: - break - - if not soft_reboot_started: - return - - if not soft_reboot_banner: - out_callback(b"Warning: Could not remount local filesystem\r\n") - return - - # Determine type of prompt - if data_all.endswith(b">"): - in_friendly_repl = False - prompt = b">" - else: - in_friendly_repl = True - prompt = data_all.rsplit(b"\r\n", 1)[-1] - - # Clear state while board remounts, it will be re-set once mounted. - self.mounted = False - self.serial = self.serial.orig_serial - - # Provide a message about the remount. - out_callback(bytes(f"\r\nRemount local directory {self.cmd.root} at /remote\r\n", "utf8")) - - # Enter raw REPL and re-mount the remote filesystem. - self.serial.write(b"\x01") - self.exec(fs_hook_code) - self.exec("__mount()") - self.mounted = True - - # Exit raw REPL if needed, and wait for the friendly REPL prompt. - if in_friendly_repl: - self.exit_raw_repl() - self.read_until(len(prompt), prompt) - out_callback(prompt) - self.serial = SerialIntercept(self.serial, self.cmd) - - def umount_local(self): - if self.mounted: - self.exec('os.umount("/remote")') - self.mounted = False - self.serial = self.serial.orig_serial - - -fs_hook_cmds = { - "CMD_STAT": 1, - "CMD_ILISTDIR_START": 2, - "CMD_ILISTDIR_NEXT": 3, - "CMD_OPEN": 4, - "CMD_CLOSE": 5, - "CMD_READ": 6, - "CMD_WRITE": 7, - "CMD_SEEK": 8, - "CMD_REMOVE": 9, - "CMD_RENAME": 10, - "CMD_MKDIR": 11, - "CMD_RMDIR": 12, -} - -fs_hook_code = """\ -import os, io, struct, micropython - -SEEK_SET = 0 - -class RemoteCommand: - def __init__(self): - import select, sys - self.buf4 = bytearray(4) - self.fout = sys.stdout.buffer - self.fin = sys.stdin.buffer - self.poller = select.poll() - self.poller.register(self.fin, select.POLLIN) - - def poll_in(self): - for _ in self.poller.ipoll(1000): - return - self.end() - raise Exception('timeout waiting for remote') - - def rd(self, n): - buf = bytearray(n) - self.rd_into(buf, n) - return buf - - def rd_into(self, buf, n): - # implement reading with a timeout in case other side disappears - if n == 0: - return - self.poll_in() - r = self.fin.readinto(buf, n) - if r < n: - mv = memoryview(buf) - while r < n: - self.poll_in() - r += self.fin.readinto(mv[r:], n - r) - - def begin(self, type): - micropython.kbd_intr(-1) - buf4 = self.buf4 - buf4[0] = 0x18 - buf4[1] = type - self.fout.write(buf4, 2) - # Wait for sync byte 0x18, but don't get stuck forever - for i in range(30): - self.poller.poll(1000) - self.fin.readinto(buf4, 1) - if buf4[0] == 0x18: - break - - def end(self): - micropython.kbd_intr(3) - - def rd_s8(self): - self.rd_into(self.buf4, 1) - n = self.buf4[0] - if n & 0x80: - n -= 0x100 - return n - - def rd_s32(self): - buf4 = self.buf4 - self.rd_into(buf4, 4) - n = buf4[0] | buf4[1] << 8 | buf4[2] << 16 | buf4[3] << 24 - if buf4[3] & 0x80: - n -= 0x100000000 - return n - - def rd_u32(self): - buf4 = self.buf4 - self.rd_into(buf4, 4) - return buf4[0] | buf4[1] << 8 | buf4[2] << 16 | buf4[3] << 24 - - def rd_bytes(self, buf): - # TODO if n is large (eg >256) then we may miss bytes on stdin - n = self.rd_s32() - if buf is None: - ret = buf = bytearray(n) - else: - ret = n - self.rd_into(buf, n) - return ret - - def rd_str(self): - n = self.rd_s32() - if n == 0: - return '' - else: - return str(self.rd(n), 'utf8') - - def wr_s8(self, i): - self.buf4[0] = i - self.fout.write(self.buf4, 1) - - def wr_s32(self, i): - struct.pack_into(' {len(buf)}") - - def do_seek(self): - fd = self.rd_s8() - n = self.rd_s32() - whence = self.rd_s8() - # self.log_cmd(f"seek {fd} {n}") - try: - n = self.data_files[fd][0].seek(n, whence) - except io.UnsupportedOperation: - n = -1 - self.wr_s32(n) - - def do_write(self): - fd = self.rd_s8() - buf = self.rd_bytes() - if self.data_files[fd][1]: - buf = str(buf, "utf8") - n = self.data_files[fd][0].write(buf) - self.wr_s32(n) - # self.log_cmd(f"write {fd} {len(buf)} -> {n}") - - def do_remove(self): - path = self.root + self.rd_str() - # self.log_cmd(f"remove {path}") - try: - self.path_check(path) - os.remove(path) - ret = 0 - except OSError as er: - ret = -abs(er.errno) - self.wr_s32(ret) - - def do_rename(self): - old = self.root + self.rd_str() - new = self.root + self.rd_str() - # self.log_cmd(f"rename {old} {new}") - try: - self.path_check(old) - self.path_check(new) - os.rename(old, new) - ret = 0 - except OSError as er: - ret = -abs(er.errno) - self.wr_s32(ret) - - def do_mkdir(self): - path = self.root + self.rd_str() - # self.log_cmd(f"mkdir {path}") - try: - self.path_check(path) - os.mkdir(path) - ret = 0 - except OSError as er: - ret = -abs(er.errno) - self.wr_s32(ret) - - def do_rmdir(self): - path = self.root + self.rd_str() - # self.log_cmd(f"rmdir {path}") - try: - self.path_check(path) - os.rmdir(path) - ret = 0 - except OSError as er: - ret = -abs(er.errno) - self.wr_s32(ret) - - cmd_table = { - fs_hook_cmds["CMD_STAT"]: do_stat, - fs_hook_cmds["CMD_ILISTDIR_START"]: do_ilistdir_start, - fs_hook_cmds["CMD_ILISTDIR_NEXT"]: do_ilistdir_next, - fs_hook_cmds["CMD_OPEN"]: do_open, - fs_hook_cmds["CMD_CLOSE"]: do_close, - fs_hook_cmds["CMD_READ"]: do_read, - fs_hook_cmds["CMD_WRITE"]: do_write, - fs_hook_cmds["CMD_SEEK"]: do_seek, - fs_hook_cmds["CMD_REMOVE"]: do_remove, - fs_hook_cmds["CMD_RENAME"]: do_rename, - fs_hook_cmds["CMD_MKDIR"]: do_mkdir, - fs_hook_cmds["CMD_RMDIR"]: do_rmdir, - } - - -class SerialIntercept: - def __init__(self, serial, cmd): - self.orig_serial = serial - self.cmd = cmd - self.buf = b"" - self.orig_serial.timeout = 5.0 - - def _check_input(self, blocking): - if blocking or self.orig_serial.inWaiting() > 0: - c = self.orig_serial.read(1) - if c == b"\x18": - # a special command - c = self.orig_serial.read(1)[0] - self.orig_serial.write(b"\x18") # Acknowledge command - PyboardCommand.cmd_table[c](self.cmd) - elif not VT_ENABLED and c == b"\x1b": - # ESC code, ignore these on windows - esctype = self.orig_serial.read(1) - if esctype == b"[": # CSI - while not (0x40 < self.orig_serial.read(1)[0] < 0x7E): - # Looking for "final byte" of escape sequence - pass - else: - self.buf += c - - @property - def fd(self): - return self.orig_serial.fd - - def close(self): - self.orig_serial.close() - - def inWaiting(self): - self._check_input(False) - return len(self.buf) - - def read(self, n): - while len(self.buf) < n: - self._check_input(True) - out = self.buf[:n] - self.buf = self.buf[n:] - return out - - def write(self, buf): - self.orig_serial.write(buf) diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index e3901473c746..8bb1c6020d09 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -88,7 +88,7 @@ def __str__(self): class Config: MPY_VERSION = 6 - MPY_SUB_VERSION = 1 + MPY_SUB_VERSION = 2 MICROPY_LONGINT_IMPL_NONE = 0 MICROPY_LONGINT_IMPL_LONGLONG = 1 MICROPY_LONGINT_IMPL_MPZ = 2 @@ -1395,15 +1395,16 @@ def disassemble_mpy(compiled_modules): cm.disassemble() -def freeze_mpy(base_qstrs, compiled_modules): +def freeze_mpy(firmware_qstr_idents, compiled_modules): # add to qstrs new = {} for q in global_qstrs.qstrs: - # don't add duplicates - if q is None or q.qstr_esc in base_qstrs or q.qstr_esc in new: + # don't add duplicates that are already in the firmware + if q is None or q.qstr_esc in firmware_qstr_idents or q.qstr_esc in new: continue new[q.qstr_esc] = (len(new), q.qstr_esc, q.str, bytes_cons(q.str, "utf8")) - new = sorted(new.values(), key=lambda x: x[0]) + # Sort by string value (because this is a sorted pool). + new = sorted(new.values(), key=lambda x: x[2]) print('#include "py/mpconfig.h"') print('#include "py/objint.h"') @@ -1452,7 +1453,15 @@ def freeze_mpy(base_qstrs, compiled_modules): # As in qstr.c, set so that the first dynamically allocated pool is twice this size; must be <= the len qstr_pool_alloc = min(len(new), 10) - global bc_content, const_str_content, const_int_content, const_obj_content, const_table_qstr_content, const_table_ptr_content, raw_code_count, raw_code_content + global \ + bc_content, \ + const_str_content, \ + const_int_content, \ + const_obj_content, \ + const_table_qstr_content, \ + const_table_ptr_content, \ + raw_code_count, \ + raw_code_content qstr_content = 0 bc_content = 0 const_str_content = 0 @@ -1484,6 +1493,7 @@ def freeze_mpy(base_qstrs, compiled_modules): print("const qstr_pool_t mp_qstr_frozen_const_pool = {") print(" &mp_qstr_const_pool, // previous pool") print(" MP_QSTRnumber_of, // previous pool size") + print(" true, // is_sorted") print(" %u, // allocated entries" % qstr_pool_alloc) print(" %u, // used entries" % len(new)) print(" (qstr_hash_t *)mp_qstr_frozen_const_hashes,") @@ -1778,14 +1788,16 @@ def main(): config.native_arch = MP_NATIVE_ARCH_NONE # set config values for qstrs, and get the existing base set of qstrs + # already in the firmware if args.qstr_header: - qcfgs, base_qstrs = qstrutil.parse_input_headers([args.qstr_header]) + qcfgs, extra_qstrs = qstrutil.parse_input_headers([args.qstr_header]) + firmware_qstr_idents = set(qstrutil.static_qstr_list_ident) | set(extra_qstrs.keys()) config.MICROPY_QSTR_BYTES_IN_LEN = int(qcfgs["BYTES_IN_LEN"]) config.MICROPY_QSTR_BYTES_IN_HASH = int(qcfgs["BYTES_IN_HASH"]) else: config.MICROPY_QSTR_BYTES_IN_LEN = 1 config.MICROPY_QSTR_BYTES_IN_HASH = 1 - base_qstrs = list(qstrutil.static_qstr_list) + firmware_qstr_idents = set(qstrutil.static_qstr_list) # Create initial list of global qstrs. global_qstrs = GlobalQStrList() @@ -1807,7 +1819,7 @@ def main(): if args.freeze: try: - freeze_mpy(base_qstrs, compiled_modules) + freeze_mpy(firmware_qstr_idents, compiled_modules) except FreezeError as er: print(er, file=sys.stderr) sys.exit(1) diff --git a/tools/mpy_ld.py b/tools/mpy_ld.py index 0fd9e038473d..cecb65d0efec 100755 --- a/tools/mpy_ld.py +++ b/tools/mpy_ld.py @@ -36,7 +36,7 @@ # MicroPython constants MPY_VERSION = 6 -MPY_SUB_VERSION = 1 +MPY_SUB_VERSION = 2 MP_CODE_BYTECODE = 2 MP_CODE_NATIVE_VIPER = 4 MP_NATIVE_ARCH_X86 = 1 diff --git a/tools/pyboard.py b/tools/pyboard.py index a9977cec5814..87b3851ead1a 100755 --- a/tools/pyboard.py +++ b/tools/pyboard.py @@ -527,7 +527,7 @@ def repr_consumer(b): def fs_stat(self, src): try: self.exec_("import os") - return os.stat_result(self.eval("os.stat(%s)" % (("'%s'" % src)), parse=True)) + return os.stat_result(self.eval("os.stat(%s)" % ("'%s'" % src), parse=True)) except PyboardError as e: raise e.convert(src) diff --git a/tools/tinytest-codegen.py b/tools/tinytest-codegen.py index 3d2f4d1f7300..0f0428a8f105 100755 --- a/tools/tinytest-codegen.py +++ b/tools/tinytest-codegen.py @@ -53,6 +53,12 @@ def script_to_map(test_file): return r +def load_profile(profile_file, test_dirs, exclude_tests): + profile_globals = {"test_dirs": test_dirs, "exclude_tests": exclude_tests} + exec(profile_file.read(), profile_globals) + return profile_globals["test_dirs"], profile_globals["exclude_tests"] + + test_function = ( "void {name}(void* data) {{\n" " static const char pystr[] = {script};\n" @@ -71,58 +77,55 @@ def script_to_map(test_file): testgroup_member = ' {{ "{name}", {name}_tests }},' ## XXX: may be we could have `--without ` argument... -# currently these tests are selected because they pass on qemu-arm -test_dirs = ( - "basics", - "micropython", - "misc", - "extmod", - "float", - "inlineasm", - "qemu-arm", -) # 'import', 'io',) -exclude_tests = ( - # pattern matching in .exp - "basics/bytes_compare3.py", - "extmod/ticks_diff.py", - "extmod/time_ms_us.py", - # unicode char issue - "extmod/json_loads.py", - # doesn't output to python stdout - "extmod/re_debug.py", - "extmod/vfs_basic.py", - "extmod/vfs_fat_ramdisk.py", - "extmod/vfs_fat_fileio.py", - "extmod/vfs_fat_fsusermount.py", - "extmod/vfs_fat_oldproto.py", - # rounding issues - "float/float_divmod.py", - # requires double precision floating point to work - "float/float2int_doubleprec_intbig.py", - "float/float_format_ints_doubleprec.py", - "float/float_parse_doubleprec.py", - # inline asm FP tests (require Cortex-M4) - "inlineasm/asmfpaddsub.py", - "inlineasm/asmfpcmp.py", - "inlineasm/asmfpldrstr.py", - "inlineasm/asmfpmuldiv.py", - "inlineasm/asmfpsqrt.py", - # different filename in output - "micropython/emg_exc.py", - "micropython/heapalloc_traceback.py", - # don't have emergency exception buffer - "micropython/heapalloc_exc_compressed_emg_exc.py", - # pattern matching in .exp - "micropython/meminfo.py", - # needs sys stdfiles - "misc/print_exception.py", - # settrace .exp files are too large - "misc/sys_settrace_loop.py", - "misc/sys_settrace_generator.py", - "misc/sys_settrace_features.py", - # don't have f-string - "basics/string_fstring.py", - "basics/string_fstring_debug.py", + +test_dirs = set( + ( + "basics", + "extmod", + "float", + "micropython", + "misc", + ) +) + +exclude_tests = set( + ( + # pattern matching in .exp + "basics/bytes_compare3.py", + "extmod/ticks_diff.py", + "extmod/time_ms_us.py", + # unicode char issue + "extmod/json_loads.py", + # doesn't output to python stdout + "extmod/re_debug.py", + "extmod/vfs_basic.py", + "extmod/vfs_fat_ramdisk.py", + "extmod/vfs_fat_fileio.py", + "extmod/vfs_fat_fsusermount.py", + "extmod/vfs_fat_oldproto.py", + # rounding issues + "float/float_divmod.py", + # requires double precision floating point to work + "float/float2int_doubleprec_intbig.py", + "float/float_format_ints_doubleprec.py", + "float/float_parse_doubleprec.py", + # different filename in output + "micropython/emg_exc.py", + "micropython/heapalloc_traceback.py", + # don't have emergency exception buffer + "micropython/heapalloc_exc_compressed_emg_exc.py", + # pattern matching in .exp + "micropython/meminfo.py", + # needs sys stdfiles + "misc/print_exception.py", + # settrace .exp files are too large + "misc/sys_settrace_loop.py", + "misc/sys_settrace_generator.py", + "misc/sys_settrace_features.py", + # don't have f-string + "basics/string_fstring.py", + "basics/string_fstring_debug.py", + ) ) output = [] @@ -133,11 +136,18 @@ def script_to_map(test_file): ) argparser.add_argument("--stdin", action="store_true", help="read list of tests from stdin") argparser.add_argument("--exclude", action="append", help="exclude test by name") +argparser.add_argument( + "--profile", + type=argparse.FileType("rt", encoding="utf-8"), + help="optional profile file providing test directories and exclusion list", +) args = argparser.parse_args() if not args.stdin: + if args.profile: + test_dirs, exclude_tests = load_profile(args.profile, test_dirs, exclude_tests) if args.exclude: - exclude_tests += tuple(args.exclude) + exclude_tests = exclude_tests.union(args.exclude) for group in test_dirs: tests += [test for test in glob("{}/*.py".format(group)) if test not in exclude_tests] else: