diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 0ffa4fcfd9..7b63f7549d 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -1,4 +1,7 @@ -#all: Reformat remaining C code that doesn't have a space after a comma. +# top: Update Python formatting to black "2023 stable style". +8b2748269244304854b3462cb8902952b4dcb892 + +# all: Reformat remaining C code that doesn't have a space after a comma. 5b700b0af90591d6b1a2c087bb8de6b7f1bfdd2d # ports: Reformat more C and Python source code. diff --git a/LICENSE b/LICENSE index 5b8797814f..aec1da6017 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ -MIT License +The MIT License (MIT) -Copyright (c) 2013-2022 Damien P. George and others +Copyright (c) 2013-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 @@ -9,8 +9,8 @@ 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 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, diff --git a/devices/ble_hci/common-hal/_bleio/att.c b/devices/ble_hci/common-hal/_bleio/att.c index 9dfe9eb26c..08d37c0feb 100644 --- a/devices/ble_hci/common-hal/_bleio/att.c +++ b/devices/ble_hci/common-hal/_bleio/att.c @@ -44,7 +44,6 @@ #include "shared-bindings/_bleio/Service.h" #include "shared-bindings/_bleio/UUID.h" #include "supervisor/shared/tick.h" -#include "supervisor/shared/translate/translate.h" STATIC uint16_t max_mtu = BT_ATT_DEFAULT_LE_MTU; // 23 STATIC unsigned long timeout = 5000; diff --git a/docs/library/array.rst b/docs/library/array.rst index a78ba82404..62146e3f4c 100644 --- a/docs/library/array.rst +++ b/docs/library/array.rst @@ -27,3 +27,55 @@ Classes Append new elements as contained in `iterable` to the end of array, growing it. + + .. method:: __getitem__(index) + + Indexed read of the array, called as ``a[index]`` (where ``a`` is an ``array``). + Returns a value if *index* is an ``int`` and an ``array`` if *index* is a slice. + Negative indices count from the end and ``IndexError`` is thrown if the index is + out of range. + + **Note:** ``__getitem__`` cannot be called directly (``a.__getitem__(index)`` fails) and + is not present in ``__dict__``, however ``a[index]`` does work. + + .. method:: __setitem__(index, value) + + Indexed write into the array, called as ``a[index] = value`` (where ``a`` is an ``array``). + ``value`` is a single value if *index* is an ``int`` and an ``array`` if *index* is a slice. + Negative indices count from the end and ``IndexError`` is thrown if the index is out of range. + + **Note:** ``__setitem__`` cannot be called directly (``a.__setitem__(index, value)`` fails) and + is not present in ``__dict__``, however ``a[index] = value`` does work. + + .. method:: __len__() + + Returns the number of items in the array, called as ``len(a)`` (where ``a`` is an ``array``). + + **Note:** ``__len__`` cannot be called directly (``a.__len__()`` fails) and the + method is not present in ``__dict__``, however ``len(a)`` does work. + + .. method:: __add__(other) + + Return a new ``array`` that is the concatenation of the array with *other*, called as + ``a + other`` (where ``a`` and *other* are both ``arrays``). + + **Note:** ``__add__`` cannot be called directly (``a.__add__(other)`` fails) and + is not present in ``__dict__``, however ``a + other`` does work. + + .. method:: __iadd__(other) + + Concatenates the array with *other* in-place, called as ``a += other`` (where ``a`` and *other* + are both ``arrays``). Equivalent to ``extend(other)``. + + **Note:** ``__iadd__`` cannot be called directly (``a.__iadd__(other)`` fails) and + is not present in ``__dict__``, however ``a += other`` does work. + + .. method:: __repr__() + + Returns the string representation of the array, called as ``str(a)`` or ``repr(a)``` + (where ``a`` is an ``array``). Returns the string ``"array(, [])"``, + where ```` is the type code letter for the array and ```` is a comma + seperated list of the elements of the array. + + **Note:** ``__repr__`` cannot be called directly (``a.__repr__()`` fails) and + is not present in ``__dict__``, however ``str(a)`` and ``repr(a)`` both work. diff --git a/docs/reference/glossary.rst b/docs/reference/glossary.rst deleted file mode 100644 index fe65ddaa14..0000000000 --- a/docs/reference/glossary.rst +++ /dev/null @@ -1,175 +0,0 @@ -Glossary -======== - -.. glossary:: - - baremetal - A system without a (full-fledged) operating system, for example an - :term:`MCU`-based system. When running on a baremetal system, - MicroPython effectively functions like a small operating system, - running user programs and providing a command interpreter - (:term:`REPL`). - - buffer protocol - Any Python object that can be automatically converted into bytes, such - as ``bytes``, ``bytearray``, ``memoryview`` and ``str`` objects, which - all implement the "buffer protocol". - - board - Typically this refers to a printed circuit board (PCB) containing a - :term:`microcontroller ` and supporting components. - MicroPython firmware is typically provided per-board, as the firmware - contains both MCU-specific functionality but also board-level - functionality such as drivers or pin names. - - bytecode - A compact representation of a Python program that generated by - compiling the Python source code. This is what the VM actually - executes. Bytecode is typically generated automatically at runtime and - is invisible to the user. Note that while :term:`CPython` and - MicroPython both use bytecode, the format is different. You can also - pre-compile source code offline using the :term:`cross-compiler`. - - callee-owned tuple - This is a MicroPython-specific construct where, for efficiency - reasons, some built-in functions or methods may re-use the same - underlying tuple object to return data. This avoids having to allocate - a new tuple for every call, and reduces heap fragmentation. Programs - should not hold references to callee-owned tuples and instead only - extract data from them (or make a copy). - - CircuitPython - A variant of MicroPython developed by `Adafruit Industries - `_. - - CPython - CPython is the reference implementation of the Python programming - language, and the most well-known one. It is, however, one of many - implementations (including Jython, IronPython, PyPy, and MicroPython). - While MicroPython's implementation differs substantially from CPython, - it aims to maintain as much compatibility as possible. - - cross-compiler - Also known as ``mpy-cross``. This tool runs on your PC and converts a - :term:`.py file` containing MicroPython code into a :term:`.mpy file` - containing MicroPython bytecode. This means it loads faster (the board - doesn't have to compile the code), and uses less space on flash (the - bytecode is more space efficient). - - driver - A MicroPython library that implements support for a particular - component, such as a sensor or display. - - FFI - Acronym for Foreign Function Interface. A mechanism used by the - :term:`MicroPython Unix port` to access operating system functionality. - This is not available on :term:`baremetal` ports. - - filesystem - Most MicroPython ports and boards provide a filesystem stored in flash - that is available to user code via the standard Python file APIs such - as ``open()``. Some boards also make this internal filesystem - accessible to the host via USB mass-storage. - - frozen module - A Python module that has been cross compiled and bundled into the - firmware image. This reduces RAM requirements as the code is executed - directly from flash. - - Garbage Collector - A background process that runs in Python (and MicroPython) to reclaim - unused memory in the :term:`heap`. - - GPIO - General-purpose input/output. The simplest means to control electrical - signals (commonly referred to as "pins") on a microcontroller. GPIO - typically allows pins to be either input or output, and to set or get - their digital value (logical "0" or "1"). MicroPython abstracts GPIO - access using the :class:`machine.Pin` and :class:`machine.Signal` - classes. - - GPIO port - A group of :term:`GPIO` pins, usually based on hardware properties of - these pins (e.g. controllable by the same register). - - heap - A region of RAM where MicroPython stores dynamic data. It is managed - automatically by the :term:`Garbage Collector`. Different MCUs and - boards have vastly different amounts of RAM available for the heap, so - this will affect how complex your program can be. - - interned string - An optimisation used by MicroPython to improve the efficiency of - working with strings. An interned string is referenced by its (unique) - identity rather than its address and can therefore be quickly compared - just by its identifier. It also means that identical strings can be - de-duplicated in memory. String interning is almost always invisible to - the user. - - MCU - Microcontroller. Microcontrollers usually have much less resources - than a desktop, laptop, or phone, but are smaller, cheaper and - require much less power. MicroPython is designed to be small and - optimized enough to run on an average modern microcontroller. - - MicroPython port - MicroPython supports different :term:`boards `, RTOSes, and - OSes, and can be relatively easily adapted to new systems. MicroPython - with support for a particular system is called a "port" to that - system. Different ports may have widely different functionality. This - documentation is intended to be a reference of the generic APIs - available across different ports ("MicroPython core"). Note that some - ports may still omit some APIs described here (e.g. due to resource - constraints). Any such differences, and port-specific extensions - beyond the MicroPython core functionality, would be described in the - separate port-specific documentation. - - MicroPython Unix port - The unix port is one of the major :term:`MicroPython ports - `. It is intended to run on POSIX-compatible - operating systems, like Linux, MacOS, FreeBSD, Solaris, etc. It also - serves as the basis of Windows port. The Unix port is very useful for - quick development and testing of the MicroPython language and - machine-independent features. It can also function in a similar way to - :term:`CPython`'s ``python`` executable. - - .mpy file - The output of the :term:`cross-compiler`. A compiled form of a - :term:`.py file` that contains MicroPython bytecode instead of Python - source code. - - native - Usually refers to "native code", i.e. machine code for the target - microcontroller (such as ARM Thumb, Xtensa, x86/x64). The ``@native`` - decorator can be applied to a MicroPython function to generate native - code instead of bytecode for that function, which will likely be - faster but use more RAM. - - port - Usually short for :term:`MicroPython port`, but could also refer to - :term:`GPIO port`. - - .py file - A file containing Python source code. - - REPL - An acronym for "Read, Eval, Print, Loop". This is the interactive - Python prompt, useful for debugging or testing short snippets of code. - Most MicroPython boards make a REPL available over a UART, and this is - typically accessible on a host PC via USB. - - stream - Also known as a "file-like object". A Python object which provides - sequential read-write access to the underlying data. A stream object - implements a corresponding interface, which consists of methods like - ``read()``, ``write()``, ``readinto()``, ``seek()``, ``flush()``, - ``close()``, etc. A stream is an important concept in MicroPython; - many I/O objects implement the stream interface, and thus can be used - consistently and interchangeably in different contexts. For more - information on streams in MicroPython, see the `io` module. - - UART - Acronym for "Universal Asynchronous Receiver/Transmitter". This is a - peripheral that sends data over a pair of pins (TX & RX). Many boards - include a way to make at least one of the UARTs available to a host PC - as a serial port over USB. diff --git a/docs/reference/mpyfiles.rst b/docs/reference/mpyfiles.rst deleted file mode 100644 index b65f62f9ca..0000000000 --- a/docs/reference/mpyfiles.rst +++ /dev/null @@ -1,199 +0,0 @@ -.. _mpy_files: - -MicroPython .mpy files -====================== - -MicroPython defines the concept of an .mpy file which is a binary container -file format that holds precompiled code, and which can be imported like a -normal .py module. The file ``foo.mpy`` can be imported via ``import foo``, -as long as ``foo.mpy`` can be found in the usual way by the import machinery. -Usually, each directory listed in ``sys.path`` is searched in order. When -searching a particular directory ``foo.py`` is looked for first and if that -is not found then ``foo.mpy`` is looked for, then the search continues in the -next directory if neither is found. As such, ``foo.py`` will take precedence -over ``foo.mpy``. - -These .mpy files can contain bytecode which is usually generated from Python -source files (.py files) via the ``mpy-cross`` program. For some architectures -an .mpy file can also contain native machine code, which can be generated in -a variety of ways, most notably from C source code. - -Versioning and compatibility of .mpy files ------------------------------------------- - -A given .mpy file may or may not be compatible with a given MicroPython system. -Compatibility is based on the following: - -* Version of the .mpy file: the version of the file must match the version - supported by the system loading it. - -* Sub-version of the .mpy file: if the .mpy file contains native machine code - then the sub-version of the file must match the version support by the - system loading it. Otherwise, if there is no native machine code in the .mpy - file, then the sub-version is ignored when loading. - -* Small integer bits: the .mpy file will require a minimum number of bits in - a small integer and the system loading it must support at least this many - bits. - -* Native architecture: if the .mpy file contains native machine code then - it will specify the architecture of that machine code and the system - loading it must support execution of that architecture's code. - -If a MicroPython system supports importing .mpy files then the -``sys.implementation._mpy`` field will exist and return an integer which -encodes the version (lower 8 bits), features and native architecture. - -Trying to import an .mpy file that fails one of the first four tests will -raise ``ValueError('incompatible .mpy file')``. Trying to import an .mpy -file that fails the native architecture test (if it contains native machine -code) will raise ``ValueError('incompatible .mpy arch')``. - -If importing an .mpy file fails then try the following: - -* Determine the .mpy version and flags supported by your MicroPython system - by executing:: - - import sys - sys_mpy = sys.implementation._mpy - arch = [None, 'x86', 'x64', - 'armv6', 'armv6m', 'armv7m', 'armv7em', 'armv7emsp', 'armv7emdp', - 'xtensa', 'xtensawin'][sys_mpy >> 10] - print('mpy version:', sys_mpy & 0xff) - print('mpy flags:', end='') - if arch: - print(' -march=' + arch, end='') - print() - -* Check the validity of the .mpy file by inspecting the first two bytes of - the file. The first byte should be an uppercase 'C' and the second byte - will be the version number, which should match the system version from above. - If it doesn't match then rebuild the .mpy file. - -* Check if the system .mpy version matches the version emitted by ``mpy-cross`` - that was used to build the .mpy file, found by ``mpy-cross --version``. - If it doesn't match then recompile ``mpy-cross`` from the Git repository - checked out at the tag (or hash) reported by ``mpy-cross --version``. - -* Make sure you are using the correct ``mpy-cross`` flags, found by the code - above, or by inspecting the ``MPY_CROSS_FLAGS`` Makefile variable for the - port that you are using. - -The following table shows the correspondence between MicroPython release -and .mpy version. - -=================== ============ -MicroPython release .mpy version -=================== ============ -v1.19 and up 6 -v1.12 - v1.18 5 -v1.11 4 -v1.9.3 - v1.10 3 -v1.9 - v1.9.2 2 -v1.5.1 - v1.8.7 0 -=================== ============ - -For completeness, the next table shows the Git commit of the main -MicroPython repository at which the .mpy version was changed. - -=================== ======================================== -.mpy version change Git commit -=================== ======================================== -5 to 6 f2040bfc7ee033e48acef9f289790f3b4e6b74e5 -4 to 5 5716c5cf65e9b2cb46c2906f40302401bdd27517 -3 to 4 9a5f92ea72754c01cc03e5efcdfe94021120531e -2 to 3 ff93fd4f50321c6190e1659b19e64fef3045a484 -1 to 2 dd11af209d226b7d18d5148b239662e30ed60bad -0 to 1 6a11048af1d01c78bdacddadd1b72dc7ba7c6478 -initial version 0 d8c834c95d506db979ec871417de90b7951edc30 -=================== ======================================== - -Binary encoding of .mpy files ------------------------------ - -MicroPython .mpy files are a binary container format with code objects (bytecode -and native machine code) stored internally in a nested hierarchy. The code for -the outer module is stored first, and then its children follow. Each child may -have further children, for example in the case of a class having methods, or a -function defining a lambda or comprehension. To keep files small while still -providing a large range of possible values it uses the concept of a -variably-encoded-unsigned-integer (vuint) in many places. Similar to utf-8 -encoding, this encoding stores 7 bits per byte with the 8th bit (MSB) set -if one or more bytes follow. The bits of the unsigned integer are stored -in the vuint in LSB form. - -The top-level of an .mpy file consists of three parts: - -* The header. - -* The global qstr and constant tables. - -* The raw-code for the outer scope of the module. - This outer scope is executed when the .mpy file is imported. - -You can inspect the contents of a .mpy file by using ``mpy-tool.py``, for -example (run from the root of the main MicroPython repository):: - - $ ./tools/mpy-tool.py -xd myfile.mpy - -The header -~~~~~~~~~~ - -The .mpy header is: - -====== ================================ -size field -====== ================================ -byte value 0x43 (ASCII 'C') -byte .mpy version number -byte feature flags -byte number of bits in a small int -====== ================================ - -The global qstr and constant tables -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -An .mpy file contains a single qstr table, and a single constant object table. -These are global to the .mpy file, they are referenced by all nested raw-code -objects. The qstr table maps internal qstr number (internal to the .mpy file) -to the resolved qstr number of the runtime that the .mpy file is imported into. -This links the .mpy file with the rest of the system that it executes within. -The constant object table is populated with references to all constant objects -that the .mpy file needs. - -====== ================================ -size field -====== ================================ -vuint number of qstrs -vuint number of constant objects -... qstr data -... encoded constant objects -====== ================================ - -Raw code elements -~~~~~~~~~~~~~~~~~ - -A raw-code element contains code, either bytecode or native machine code. Its -contents are: - -====== ================================ -size field -====== ================================ -vuint type, size and whether there are sub-raw-code elements -... code (bytecode or machine code) -vuint number of sub-raw-code elements (only if non-zero) -... sub-raw-code elements -====== ================================ - -The first vuint in a raw-code element encodes the type of code stored in this -element (the two least-significant bits), whether this raw-code has any -children (the third least-significant bit), and the length of the code that -follows (the amount of RAM to allocate for it). - -Following the vuint comes the code itself. Unless the code type is viper code -with relocations, this code is constant data and does not need to be modified. - -If this raw-code has any children (as indicated by a bit in the first vuint), -following the code comes a vuint counting the number of sub-raw-code elements. - -Finally any sub-raw-code elements are stored, recursively. diff --git a/docs/static/custom.css b/docs/static/custom.css new file mode 100644 index 0000000000..b0a7f746fc --- /dev/null +++ b/docs/static/custom.css @@ -0,0 +1,5 @@ +/* Workaround to force Sphinx to render tables to 100% and wordwrap */ +/* See https://stackoverflow.com/questions/69359978/grid-table-does-not-word-wrap for more details */ +.wy-table-responsive table td, .wy-table-responsive table th { + white-space: inherit; +} diff --git a/examples/natmod/ure/ure.c b/examples/natmod/ure/ure.c index 175b93e395..d4bd680abd 100644 --- a/examples/natmod/ure/ure.c +++ b/examples/natmod/ure/ure.c @@ -32,8 +32,8 @@ void *memmove(void *dest, const void *src, size_t n) { return mp_fun_table.memmove_(dest, src, n); } -mp_obj_type_t match_type; -mp_obj_type_t re_type; +mp_obj_full_type_t match_type; +mp_obj_full_type_t re_type; #include "extmod/modure.c" @@ -54,21 +54,21 @@ mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *a match_type.base.type = (void*)&mp_fun_table.type_type; match_type.name = MP_QSTR_match; - match_type.print = match_print; + MP_OBJ_TYPE_SET_SLOT(&match_type, print, match_print, 0); match_locals_dict_table[0] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_group), MP_OBJ_FROM_PTR(&match_group_obj) }; match_locals_dict_table[1] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_groups), MP_OBJ_FROM_PTR(&match_groups_obj) }; match_locals_dict_table[2] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_span), MP_OBJ_FROM_PTR(&match_span_obj) }; match_locals_dict_table[3] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_start), MP_OBJ_FROM_PTR(&match_start_obj) }; match_locals_dict_table[4] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_end), MP_OBJ_FROM_PTR(&match_end_obj) }; - match_type.locals_dict = (void*)&match_locals_dict; + MP_OBJ_TYPE_SET_SLOT(&match_type, locals_dict, (void*)&match_locals_dict, 1); re_type.base.type = (void*)&mp_fun_table.type_type; re_type.name = MP_QSTR_ure; - re_type.print = re_print; + MP_OBJ_TYPE_SET_SLOT(&re_type, print, re_print, 0); re_locals_dict_table[0] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_match), MP_OBJ_FROM_PTR(&re_match_obj) }; re_locals_dict_table[1] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_search), MP_OBJ_FROM_PTR(&re_search_obj) }; re_locals_dict_table[2] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_split), MP_OBJ_FROM_PTR(&re_split_obj) }; - re_type.locals_dict = (void*)&re_locals_dict; + MP_OBJ_TYPE_SET_SLOT(&re_type, locals_dict, (void*)&re_locals_dict, 1); mp_store_global(MP_QSTR_compile, MP_OBJ_FROM_PTR(&mod_re_compile_obj)); mp_store_global(MP_QSTR_match, MP_OBJ_FROM_PTR(&re_match_obj)); diff --git a/examples/natmod/uzlib/uzlib.c b/examples/natmod/uzlib/uzlib.c index 6df9944d6a..9cf58b10e7 100644 --- a/examples/natmod/uzlib/uzlib.c +++ b/examples/natmod/uzlib/uzlib.c @@ -9,7 +9,6 @@ void *memset(void *s, int c, size_t n) { #endif mp_obj_full_type_t decompio_type; -mp_stream_p_t decompio_stream_p; #include "extmod/moduzlib.c" @@ -19,18 +18,14 @@ STATIC MP_DEFINE_CONST_DICT(decompio_locals_dict, decompio_locals_dict_table); mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *args) { MP_DYNRUNTIME_INIT_ENTRY - decompio_stream_p.name = MP_QSTR_protocol_stream; - decompio_stream_p.read = decompio_read; - decompio_type.base.type = mp_fun_table.type_type; - decompio_type.flags = MP_TYPE_FLAG_EXTENDED; decompio_type.name = MP_QSTR_DecompIO; - decompio_type.make_new = decompio_make_new; - decompio_type.ext[0].protocol = &decompio_stream_p; + MP_OBJ_TYPE_SET_SLOT(&decompio_type, make_new, &decompio_make_new, 0); + MP_OBJ_TYPE_SET_SLOT(&decompio_type, protocol, &decompio_stream_p, 1); decompio_locals_dict_table[0] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_read), MP_OBJ_FROM_PTR(&mp_stream_read_obj) }; decompio_locals_dict_table[1] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_readinto), MP_OBJ_FROM_PTR(&mp_stream_readinto_obj) }; decompio_locals_dict_table[2] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_readline), MP_OBJ_FROM_PTR(&mp_stream_unbuffered_readline_obj) }; - decompio_type.locals_dict = (void*)&decompio_locals_dict; + MP_OBJ_TYPE_SET_SLOT(&decompio_type, locals_dict, (void*)&decompio_locals_dict, 2); mp_store_global(MP_QSTR___name__, MP_OBJ_NEW_QSTR(MP_QSTR_uzlib)); mp_store_global(MP_QSTR_decompress, MP_OBJ_FROM_PTR(&mod_uzlib_decompress_obj)); diff --git a/examples/usercmodule/cexample/examplemodule.c b/examples/usercmodule/cexample/examplemodule.c index 93a58be2ed..ccce03bcbd 100644 --- a/examples/usercmodule/cexample/examplemodule.c +++ b/examples/usercmodule/cexample/examplemodule.c @@ -1,6 +1,9 @@ // Include MicroPython API. #include "py/runtime.h" +// Used to get the time in the Timer class example. +#include "py/mphal.h" + // This is the function which will be called from Python as cexample.add_ints(a, b). STATIC mp_obj_t example_add_ints(mp_obj_t a_obj, mp_obj_t b_obj) { // Extract the ints from the micropython input objects. @@ -13,6 +16,58 @@ STATIC mp_obj_t example_add_ints(mp_obj_t a_obj, mp_obj_t b_obj) { // Define a Python reference to the function above. STATIC MP_DEFINE_CONST_FUN_OBJ_2(example_add_ints_obj, example_add_ints); +// This structure represents Timer instance objects. +typedef struct _example_Timer_obj_t { + // All objects start with the base. + mp_obj_base_t base; + // Everything below can be thought of as instance attributes, but they + // cannot be accessed by MicroPython code directly. In this example we + // store the time at which the object was created. + mp_uint_t start_time; +} example_Timer_obj_t; + +// This is the Timer.time() method. After creating a Timer object, this +// can be called to get the time elapsed since creating the Timer. +STATIC mp_obj_t example_Timer_time(mp_obj_t self_in) { + // The first argument is self. It is cast to the *example_Timer_obj_t + // type so we can read its attributes. + example_Timer_obj_t *self = MP_OBJ_TO_PTR(self_in); + + // Get the elapsed time and return it as a MicroPython integer. + mp_uint_t elapsed = mp_hal_ticks_ms() - self->start_time; + return mp_obj_new_int_from_uint(elapsed); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(example_Timer_time_obj, example_Timer_time); + +// This represents Timer.__new__ and Timer.__init__, which is called when +// the user instantiates a Timer object. +STATIC mp_obj_t example_Timer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + // Allocates the new object and sets the type. + example_Timer_obj_t *self = mp_obj_malloc(example_Timer_obj_t, type); + + // Initializes the time for this Timer instance. + self->start_time = mp_hal_ticks_ms(); + + // The make_new function always returns self. + return MP_OBJ_FROM_PTR(self); +} + +// This collects all methods and other static class attributes of the Timer. +// The table structure is similar to the module table, as detailed below. +STATIC const mp_rom_map_elem_t example_Timer_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_time), MP_ROM_PTR(&example_Timer_time_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(example_Timer_locals_dict, example_Timer_locals_dict_table); + +// This defines the type(Timer) object. +MP_DEFINE_CONST_OBJ_TYPE( + example_type_Timer, + MP_QSTR_Timer, + MP_TYPE_FLAG_NONE, + make_new, example_Timer_make_new, + locals_dict, &example_Timer_locals_dict + ); + // Define all properties of the module. // Table entries are key/value pairs of the attribute name (a string) // and the MicroPython object reference. @@ -21,6 +76,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(example_add_ints_obj, example_add_ints); STATIC const mp_rom_map_elem_t example_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_cexample) }, { MP_ROM_QSTR(MP_QSTR_add_ints), MP_ROM_PTR(&example_add_ints_obj) }, + { MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&example_type_Timer) }, }; STATIC MP_DEFINE_CONST_DICT(example_module_globals, example_module_globals_table); diff --git a/extmod/extmod.mk b/extmod/extmod.mk index 83b1854c1e..98bea29db7 100644 --- a/extmod/extmod.mk +++ b/extmod/extmod.mk @@ -1,4 +1,68 @@ -# This makefile fragment provides rules to build 3rd-party components for extmod modules +# This makefile fragment adds the source code files for the core extmod modules +# and provides rules to build 3rd-party components for extmod modules. + +SRC_EXTMOD_C += \ + extmod/machine_bitstream.c \ + extmod/machine_i2c.c \ + extmod/machine_mem.c \ + extmod/machine_pinbase.c \ + extmod/machine_pulse.c \ + extmod/machine_pwm.c \ + extmod/machine_signal.c \ + extmod/machine_spi.c \ + extmod/machine_timer.c \ + extmod/modbluetooth.c \ + extmod/modbtree.c \ + extmod/modframebuf.c \ + extmod/modlwip.c \ + extmod/modnetwork.c \ + extmod/modonewire.c \ + extmod/moduasyncio.c \ + extmod/modubinascii.c \ + extmod/moducryptolib.c \ + extmod/moductypes.c \ + extmod/moduhashlib.c \ + extmod/moduheapq.c \ + extmod/modujson.c \ + extmod/moduos.c \ + extmod/moduplatform.c\ + extmod/modurandom.c \ + extmod/modure.c \ + extmod/moduselect.c \ + extmod/modusocket.c \ + extmod/modussl_axtls.c \ + extmod/modussl_mbedtls.c \ + extmod/modutimeq.c \ + extmod/moduwebsocket.c \ + extmod/moduzlib.c \ + extmod/modwebrepl.c \ + extmod/network_cyw43.c \ + extmod/network_lwip.c \ + extmod/network_ninaw10.c \ + extmod/network_wiznet5k.c \ + extmod/uos_dupterm.c \ + extmod/utime_mphal.c \ + extmod/vfs.c \ + extmod/vfs_blockdev.c \ + extmod/vfs_fat.c \ + extmod/vfs_fat_diskio.c \ + extmod/vfs_fat_file.c \ + extmod/vfs_lfs.c \ + extmod/vfs_posix.c \ + extmod/vfs_posix_file.c \ + extmod/vfs_reader.c \ + extmod/virtpin.c \ + shared/libc/abort_.c \ + shared/libc/printf.c \ + +SRC_THIRDPARTY_C += \ + +PY_O += $(addprefix $(BUILD)/, $(SRC_EXTMOD_C:.c=.o)) +PY_O += $(addprefix $(BUILD)/, $(SRC_THIRDPARTY_C:.c=.o)) +SRC_QSTR += $(SRC_EXTMOD_C) + +CFLAGS += $(CFLAGS_EXTMOD) $(CFLAGS_THIRDPARTY) +LDFLAGS += $(LDFLAGS_EXTMOD) $(LDFLAGS_THIRDPARTY) ################################################################################ # VFS FAT FS @@ -6,11 +70,11 @@ OOFATFS_DIR = lib/oofatfs # this sets the config file for FatFs -CFLAGS_MOD += -DFFCONF_H=\"$(OOFATFS_DIR)/ffconf.h\" +CFLAGS_THIRDPARTY += -DFFCONF_H=\"$(OOFATFS_DIR)/ffconf.h\" ifeq ($(MICROPY_VFS_FAT),1) -CFLAGS_MOD += -DMICROPY_VFS_FAT=1 -SRC_MOD += $(addprefix $(OOFATFS_DIR)/,\ +CFLAGS_EXTMOD += -DMICROPY_VFS_FAT=1 +SRC_THIRDPARTY_C += $(addprefix $(OOFATFS_DIR)/,\ ff.c \ ffunicode.c \ ) @@ -22,18 +86,18 @@ endif LITTLEFS_DIR = lib/littlefs ifeq ($(MICROPY_VFS_LFS1),1) -CFLAGS_MOD += -DMICROPY_VFS_LFS1=1 -CFLAGS_MOD += -DLFS1_NO_MALLOC -DLFS1_NO_DEBUG -DLFS1_NO_WARN -DLFS1_NO_ERROR -DLFS1_NO_ASSERT -SRC_MOD += $(addprefix $(LITTLEFS_DIR)/,\ +CFLAGS_EXTMOD += -DMICROPY_VFS_LFS1=1 +CFLAGS_THIRDPARTY += -DLFS1_NO_MALLOC -DLFS1_NO_DEBUG -DLFS1_NO_WARN -DLFS1_NO_ERROR -DLFS1_NO_ASSERT +SRC_THIRDPARTY_C += $(addprefix $(LITTLEFS_DIR)/,\ lfs1.c \ lfs1_util.c \ ) endif ifeq ($(MICROPY_VFS_LFS2),1) -CFLAGS_MOD += -DMICROPY_VFS_LFS2=1 -CFLAGS_MOD += -DLFS2_NO_MALLOC -DLFS2_NO_DEBUG -DLFS2_NO_WARN -DLFS2_NO_ERROR -DLFS2_NO_ASSERT -SRC_MOD += $(addprefix $(LITTLEFS_DIR)/,\ +CFLAGS_EXTMOD += -DMICROPY_VFS_LFS2=1 +CFLAGS_THIRDPARTY += -DLFS2_NO_MALLOC -DLFS2_NO_DEBUG -DLFS2_NO_WARN -DLFS2_NO_ERROR -DLFS2_NO_ASSERT +SRC_THIRDPARTY_C += $(addprefix $(LITTLEFS_DIR)/,\ lfs2.c \ lfs2_util.c \ ) @@ -45,12 +109,14 @@ endif # ussl ifeq ($(MICROPY_PY_USSL),1) -CFLAGS_MOD += -DMICROPY_PY_USSL=1 +CFLAGS_EXTMOD += -DMICROPY_PY_USSL=1 ifeq ($(MICROPY_SSL_AXTLS),1) CFLAGS_MOD += -DMICROPY_SSL_AXTLS=1 -I$(TOP)/lib/axtls/ssl -I$(TOP)/lib/axtls/crypto -I$(TOP)/extmod/axtls-include AXTLS_DIR = lib/axtls +GIT_SUBMODULES += $(AXTLS_DIR) +CFLAGS_EXTMOD += -DMICROPY_SSL_AXTLS=1 -I$(TOP)/lib/axtls/ssl -I$(TOP)/lib/axtls/crypto -I$(TOP)/extmod/axtls-include $(BUILD)/$(AXTLS_DIR)/%.o: CFLAGS += -Wno-all -Wno-unused-parameter -Wno-uninitialized -Wno-sign-compare -Wno-old-style-definition -Dmp_stream_errno=errno $(AXTLS_DEFS_EXTRA) -SRC_MOD += $(addprefix $(AXTLS_DIR)/,\ +SRC_THIRDPARTY_C += $(addprefix $(AXTLS_DIR)/,\ ssl/asn1.c \ ssl/loader.c \ ssl/tls1.c \ @@ -67,8 +133,12 @@ SRC_MOD += $(addprefix $(AXTLS_DIR)/,\ ) else ifeq ($(MICROPY_SSL_MBEDTLS),1) MBEDTLS_DIR = lib/mbedtls -CFLAGS_MOD += -DMICROPY_SSL_MBEDTLS=1 -I$(TOP)/$(MBEDTLS_DIR)/include -SRC_MOD += $(addprefix $(MBEDTLS_DIR)/library/,\ +MBEDTLS_CONFIG_FILE ?= \"mbedtls/mbedtls_config.h\" +GIT_SUBMODULES += $(MBEDTLS_DIR) +CFLAGS_EXTMOD += -DMBEDTLS_CONFIG_FILE=$(MBEDTLS_CONFIG_FILE) +CFLAGS_EXTMOD += -DMICROPY_SSL_MBEDTLS=1 -I$(TOP)/$(MBEDTLS_DIR)/include +SRC_THIRDPARTY_C += lib/mbedtls_errors/mp_mbedtls_errors.c +SRC_THIRDPARTY_C += $(addprefix $(MBEDTLS_DIR)/library/,\ aes.c \ aesni.c \ arc4.c \ @@ -96,7 +166,6 @@ SRC_MOD += $(addprefix $(MBEDTLS_DIR)/library/,\ ecp_curves.c \ entropy.c \ entropy_poll.c \ - error.c \ gcm.c \ havege.c \ hmac_drbg.c \ @@ -148,13 +217,14 @@ endif # lwip ifeq ($(MICROPY_PY_LWIP),1) +GIT_SUBMODULES += lib/lwip # A port should add an include path where lwipopts.h can be found (eg extmod/lwip-include) LWIP_DIR = lib/lwip/src INC += -I$(TOP)/$(LWIP_DIR)/include -CFLAGS_MOD += -DMICROPY_PY_LWIP=1 -$(BUILD)/$(LWIP_DIR)/core/ipv4/dhcp.o: CFLAGS_MOD += -Wno-address -SRC_MOD += extmod/modlwip.c shared/netutils/netutils.c -SRC_MOD += $(addprefix $(LWIP_DIR)/,\ +CFLAGS_EXTMOD += -DMICROPY_PY_LWIP=1 +$(BUILD)/$(LWIP_DIR)/core/ipv4/dhcp.o: CFLAGS += -Wno-address +SRC_THIRDPARTY_C += shared/netutils/netutils.c +SRC_THIRDPARTY_C += $(addprefix $(LWIP_DIR)/,\ apps/mdns/mdns.c \ core/def.c \ core/dns.c \ @@ -193,8 +263,8 @@ SRC_MOD += $(addprefix $(LWIP_DIR)/,\ netif/ethernet.c \ ) ifeq ($(MICROPY_PY_LWIP_SLIP),1) -CFLAGS_MOD += -DMICROPY_PY_LWIP_SLIP=1 -SRC_MOD += $(LWIP_DIR)/netif/slipif.c +CFLAGS_EXTMOD += -DMICROPY_PY_LWIP_SLIP=1 +SRC_THIRDPARTY_C += $(LWIP_DIR)/netif/slipif.c endif endif @@ -205,8 +275,7 @@ ifeq ($(MICROPY_PY_BTREE),1) BTREE_DIR = lib/berkeley-db-1.xx BTREE_DEFS = -D__DBINTERFACE_PRIVATE=1 -Dmpool_error=printf -Dabort=abort_ "-Dvirt_fd_t=void*" $(BTREE_DEFS_EXTRA) INC += -I$(TOP)/$(BTREE_DIR)/PORT/include -SRC_MOD += extmod/modbtree.c -SRC_MOD += $(addprefix $(BTREE_DIR)/,\ +SRC_THIRDPARTY_C += $(addprefix $(BTREE_DIR)/,\ btree/bt_close.c \ btree/bt_conv.c \ btree/bt_debug.c \ @@ -222,9 +291,79 @@ SRC_MOD += $(addprefix $(BTREE_DIR)/,\ btree/bt_utils.c \ mpool/mpool.c \ ) -CFLAGS_MOD += -DMICROPY_PY_BTREE=1 +CFLAGS_EXTMOD += -DMICROPY_PY_BTREE=1 # we need to suppress certain warnings to get berkeley-db to compile cleanly # and we have separate BTREE_DEFS so the definitions don't interfere with other source code $(BUILD)/$(BTREE_DIR)/%.o: CFLAGS += -Wno-old-style-definition -Wno-sign-compare -Wno-unused-parameter $(BTREE_DEFS) $(BUILD)/extmod/modbtree.o: CFLAGS += $(BTREE_DEFS) endif + +################################################################################ +# networking + +ifeq ($(MICROPY_PY_NETWORK_CYW43),1) +CYW43_DIR = lib/cyw43-driver +GIT_SUBMODULES += $(CYW43_DIR) +CFLAGS_EXTMOD += -DMICROPY_PY_NETWORK_CYW43=1 +SRC_THIRDPARTY_C += $(addprefix $(CYW43_DIR)/src/,\ + cyw43_ctrl.c \ + cyw43_lwip.c \ + cyw43_ll.c \ + cyw43_sdio.c \ + cyw43_stats.c \ + ) +ifeq ($(MICROPY_PY_BLUETOOTH),1) +DRIVERS_SRC_C += drivers/cyw43/cywbt.c +endif + +$(BUILD)/$(CYW43_DIR)/src/cyw43_%.o: CFLAGS += -std=c11 +endif # MICROPY_PY_NETWORK_CYW43 + +ifneq ($(MICROPY_PY_NETWORK_WIZNET5K),) +ifneq ($(MICROPY_PY_NETWORK_WIZNET5K),0) +WIZNET5K_DIR=lib/wiznet5k +GIT_SUBMODULES += lib/wiznet5k +INC += -I$(TOP)/$(WIZNET5K_DIR) -I$(TOP)/$(WIZNET5K_DIR)/Ethernet +CFLAGS += -DMICROPY_PY_NETWORK_WIZNET5K=$(MICROPY_PY_NETWORK_WIZNET5K) -D_WIZCHIP_=$(MICROPY_PY_NETWORK_WIZNET5K) +CFLAGS_THIRDPARTY += -DWIZCHIP_PREFIXED_EXPORTS=1 +ifeq ($(MICROPY_PY_LWIP),1) +# When using MACRAW mode (with lwIP), maximum buffer space must be used for the raw socket +CFLAGS_THIRDPARTY += -DWIZCHIP_USE_MAX_BUFFER +endif +SRC_THIRDPARTY_C += $(addprefix $(WIZNET5K_DIR)/,\ + Ethernet/W$(MICROPY_PY_NETWORK_WIZNET5K)/w$(MICROPY_PY_NETWORK_WIZNET5K).c \ + Ethernet/wizchip_conf.c \ + Ethernet/socket.c \ + Internet/DNS/dns.c \ + Internet/DHCP/dhcp.c \ + ) +endif +endif + +################################################################################ +# bluetooth + +ifeq ($(MICROPY_PY_BLUETOOTH),1) +CFLAGS_EXTMOD += -DMICROPY_PY_BLUETOOTH=1 + +ifeq ($(MICROPY_BLUETOOTH_NIMBLE),1) +ifeq ($(MICROPY_BLUETOOTH_BTSTACK),1) +$(error Cannot enable both NimBLE and BTstack at the same time) +endif +endif + +ifneq ($(MICROPY_BLUETOOTH_NIMBLE),1) +ifneq ($(MICROPY_BLUETOOTH_BTSTACK),1) +$(error Must enable one of MICROPY_BLUETOOTH_NIMBLE or MICROPY_BLUETOOTH_BTSTACK) +endif +endif + +ifeq ($(MICROPY_BLUETOOTH_NIMBLE),1) +include $(TOP)/extmod/nimble/nimble.mk +endif + +ifeq ($(MICROPY_BLUETOOTH_BTSTACK),1) +include $(TOP)/extmod/btstack/btstack.mk +endif + +endif diff --git a/extmod/moduasyncio.c b/extmod/moduasyncio.c index 2ba8efb1fe..93a36ebe47 100644 --- a/extmod/moduasyncio.c +++ b/extmod/moduasyncio.c @@ -35,8 +35,6 @@ #include "shared-bindings/supervisor/__init__.h" #endif -#include "supervisor/shared/translate/translate.h" - // Used when task cannot be guaranteed to be non-NULL. #define TASK_PAIRHEAP(task) ((task) ? &(task)->pairheap : NULL) @@ -65,32 +63,19 @@ STATIC const mp_obj_type_t task_queue_type; STATIC const mp_obj_type_t task_type; STATIC mp_obj_t task_queue_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args); -STATIC mp_obj_t task_getiter(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf); /******************************************************************************/ // Ticks for task ordering in pairing heap -// CIRCUITPY-style ticks -#define _TICKS_PERIOD (1lu << 29) -#define _TICKS_MAX (_TICKS_PERIOD - 1) -#define _TICKS_HALFPERIOD (_TICKS_PERIOD >> 1) -#if !CIRCUITPY || (defined(__unix__) || defined(__APPLE__)) STATIC mp_obj_t ticks(void) { - return MP_OBJ_NEW_SMALL_INT(mp_hal_ticks_ms() & _TICKS_MAX); + return MP_OBJ_NEW_SMALL_INT(mp_hal_ticks_ms() & (MICROPY_PY_UTIME_TICKS_PERIOD - 1)); } -#else -// We don't share the implementation above because our supervisor_ticks_ms -// starts the epoch about 65 seconds before the first overflow (see -// shared-bindings/supervisor/__init__.c). We assume/require that -// supervisor.ticks_ms is picked as the ticks implementation under -// CircuitPython for the Python-coded bits of asyncio. -#define ticks() supervisor_ticks_ms() -#endif STATIC mp_int_t ticks_diff(mp_obj_t t1_in, mp_obj_t t0_in) { mp_uint_t t0 = MP_OBJ_SMALL_INT_VALUE(t0_in); mp_uint_t t1 = MP_OBJ_SMALL_INT_VALUE(t1_in); - mp_int_t diff = ((t1 - t0 + _TICKS_HALFPERIOD) & _TICKS_MAX) - _TICKS_HALFPERIOD; + mp_int_t diff = ((t1 - t0 + MICROPY_PY_UTIME_TICKS_PERIOD / 2) & (MICROPY_PY_UTIME_TICKS_PERIOD - 1)) + - MICROPY_PY_UTIME_TICKS_PERIOD / 2; return diff; } @@ -160,20 +145,16 @@ STATIC const mp_rom_map_elem_t task_queue_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_push), MP_ROM_PTR(&task_queue_push_obj) }, { MP_ROM_QSTR(MP_QSTR_pop), MP_ROM_PTR(&task_queue_pop_obj) }, { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&task_queue_remove_obj) }, - - // CIRCUITPYTHON: remove these after the bundle need not support 8.x - { MP_ROM_QSTR(MP_QSTR_push_head), MP_ROM_PTR(&task_queue_push_obj) }, - { MP_ROM_QSTR(MP_QSTR_push_sorted), MP_ROM_PTR(&task_queue_push_obj) }, - { MP_ROM_QSTR(MP_QSTR_pop_head), MP_ROM_PTR(&task_queue_pop_obj) }, }; STATIC MP_DEFINE_CONST_DICT(task_queue_locals_dict, task_queue_locals_dict_table); -STATIC const mp_obj_type_t task_queue_type = { - { &mp_type_type }, - .name = MP_QSTR_TaskQueue, - .make_new = task_queue_make_new, - .locals_dict = (mp_obj_dict_t *)&task_queue_locals_dict, -}; +STATIC MP_DEFINE_CONST_OBJ_TYPE( + task_queue_type, + MP_QSTR_TaskQueue, + MP_TYPE_FLAG_NONE, + make_new, task_queue_make_new, + locals_dict, &task_queue_locals_dict + ); /******************************************************************************/ // Task class @@ -247,12 +228,6 @@ STATIC mp_obj_t task_cancel(mp_obj_t self_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(task_cancel_obj, task_cancel); -// CIRCUITPY provides __await__(). -STATIC mp_obj_t task_await(mp_obj_t self_in) { - return task_getiter(self_in, NULL); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(task_await_obj, task_await); - STATIC void task_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { mp_obj_task_t *self = MP_OBJ_TO_PTR(self_in); if (dest[0] == MP_OBJ_NULL) { @@ -271,10 +246,6 @@ STATIC void task_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { dest[1] = self_in; } else if (attr == MP_QSTR_ph_key) { dest[0] = self->ph_key; - // CIRCUITPY provides __await__(). - } else if (attr == MP_QSTR___await__) { - dest[0] = MP_OBJ_FROM_PTR(&task_await_obj); - dest[1] = self_in; } } else if (dest[1] != MP_OBJ_NULL) { // Store @@ -307,14 +278,8 @@ 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 - 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); - } else { - // Task finished, raise return value to caller so it can continue. - nlr_raise(self->data); - } + // Task finished, raise return value to caller so it can continue. + nlr_raise(self->data); } else { // Put calling task on waiting queue. mp_obj_t cur_task = mp_obj_dict_get(uasyncio_context, MP_OBJ_NEW_QSTR(MP_QSTR_cur_task)); @@ -326,27 +291,25 @@ STATIC mp_obj_t task_iternext(mp_obj_t self_in) { return mp_const_none; } -STATIC const mp_obj_type_t task_type = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_Task, - .make_new = task_make_new, - .attr = task_attr, - MP_TYPE_EXTENDED_FIELDS( - .getiter = task_getiter, - .iternext = task_iternext, - ), +STATIC const mp_getiter_iternext_custom_t task_getiter_iternext = { + .getiter = task_getiter, + .iternext = task_iternext, }; +STATIC MP_DEFINE_CONST_OBJ_TYPE( + task_type, + MP_QSTR_Task, + MP_TYPE_FLAG_ITER_IS_CUSTOM, + make_new, task_make_new, + attr, task_attr, + iter, &task_getiter_iternext + ); + /******************************************************************************/ // C-level uasyncio module STATIC const mp_rom_map_elem_t mp_module_uasyncio_globals_table[] = { - #if CIRCUITPY - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR__asyncio) }, - #else { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR__uasyncio) }, - #endif { MP_ROM_QSTR(MP_QSTR_TaskQueue), MP_ROM_PTR(&task_queue_type) }, { MP_ROM_QSTR(MP_QSTR_Task), MP_ROM_PTR(&task_type) }, }; diff --git a/extmod/modubinascii.c b/extmod/modubinascii.c index d95f6f5554..e3b846e120 100644 --- a/extmod/modubinascii.c +++ b/extmod/modubinascii.c @@ -30,11 +30,14 @@ #include "py/runtime.h" #include "py/binary.h" - -#include "supervisor/shared/translate/translate.h" +#include "py/objstr.h" #if MICROPY_PY_UBINASCII +#if MICROPY_PY_BUILTINS_BYTES_HEX +STATIC mp_obj_t bytes_hex_as_bytes(size_t n_args, const mp_obj_t *args) { + return mp_obj_bytes_hex(n_args, args, &mp_type_bytes); + static void check_not_unicode(const mp_obj_t arg) { #if MICROPY_CPYTHON_COMPAT if (mp_obj_is_str(arg)) { @@ -42,76 +45,13 @@ static void check_not_unicode(const mp_obj_t arg) { } #endif } -STATIC mp_obj_t mod_binascii_hexlify(size_t n_args, const mp_obj_t *args) { - // First argument is the data to convert. - // Second argument is an optional separator to be used between values. - const char *sep = NULL; - mp_buffer_info_t bufinfo; - check_not_unicode(args[0]); - mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bytes_hex_as_bytes_obj, 1, 2, bytes_hex_as_bytes); - // Code below assumes non-zero buffer length when computing size with - // separator, so handle the zero-length case here. - if (bufinfo.len == 0) { - return mp_const_empty_bytes; - } - - vstr_t vstr; - size_t out_len = bufinfo.len * 2; - if (n_args > 1) { - // 1-char separator between hex numbers - out_len += bufinfo.len - 1; - sep = mp_obj_str_get_str(args[1]); - } - vstr_init_len(&vstr, out_len); - byte *in = bufinfo.buf, *out = (byte *)vstr.buf; - for (mp_uint_t i = bufinfo.len; i--;) { - byte d = (*in >> 4); - if (d > 9) { - d += 'a' - '9' - 1; - } - *out++ = d + '0'; - d = (*in++ & 0xf); - if (d > 9) { - d += 'a' - '9' - 1; - } - *out++ = d + '0'; - if (sep != NULL && i != 0) { - *out++ = *sep; - } - } - return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); +STATIC mp_obj_t bytes_fromhex_bytes(mp_obj_t data) { + return mp_obj_bytes_fromhex(MP_OBJ_FROM_PTR(&mp_type_bytes), data); } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_binascii_hexlify_obj, 1, 2, mod_binascii_hexlify); - -STATIC mp_obj_t mod_binascii_unhexlify(mp_obj_t data) { - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_READ); - - if ((bufinfo.len & 1) != 0) { - mp_raise_ValueError(MP_ERROR_TEXT("odd-length string")); - } - vstr_t vstr; - vstr_init_len(&vstr, bufinfo.len / 2); - byte *in = bufinfo.buf, *out = (byte *)vstr.buf; - byte hex_byte = 0; - for (mp_uint_t i = bufinfo.len; i--;) { - byte hex_ch = *in++; - if (unichar_isxdigit(hex_ch)) { - hex_byte += unichar_xdigit_value(hex_ch); - } else { - mp_raise_ValueError(MP_ERROR_TEXT("non-hex digit found")); - } - if (i & 1) { - hex_byte <<= 4; - } else { - *out++ = hex_byte; - hex_byte = 0; - } - } - return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_binascii_unhexlify_obj, mod_binascii_unhexlify); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(bytes_fromhex_obj, bytes_fromhex_bytes); +#endif // If ch is a character in the base64 alphabet, and is not a pad character, then // the corresponding integer between 0 and 63, inclusively, is returned. @@ -171,7 +111,7 @@ STATIC mp_obj_t mod_binascii_a2b_base64(mp_obj_t data) { mp_raise_ValueError(MP_ERROR_TEXT("incorrect padding")); } - return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); + return mp_obj_new_bytes_from_vstr(&vstr); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_binascii_a2b_base64_obj, mod_binascii_a2b_base64); @@ -184,6 +124,7 @@ STATIC mp_obj_t mod_binascii_b2a_base64(size_t n_args, const mp_obj_t *pos_args, mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); uint8_t newline = args[ARG_newline].u_bool; + // CIRCUITPY check_not_unicode(pos_args[0]); mp_buffer_info_t bufinfo; mp_get_buffer_raise(pos_args[0], &bufinfo, MP_BUFFER_READ); @@ -234,7 +175,7 @@ STATIC mp_obj_t mod_binascii_b2a_base64(size_t n_args, const mp_obj_t *pos_args, if (newline) { *out = '\n'; } - return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); + return mp_obj_new_bytes_from_vstr(&vstr); } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_binascii_b2a_base64_obj, 1, mod_binascii_b2a_base64); @@ -305,22 +246,25 @@ static uint32_t from_uzlib_crc32(const void *data, unsigned int length, uint32_t STATIC mp_obj_t mod_binascii_crc32(size_t n_args, const mp_obj_t *args) { mp_buffer_info_t bufinfo; - check_not_unicode(args[0]); mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ); uint32_t crc = (n_args > 1) ? mp_obj_get_int_truncated(args[1]) : 0; - crc = from_uzlib_crc32(bufinfo.buf, bufinfo.len, crc ^ 0xffffffff); + crc = uzlib_crc32(bufinfo.buf, bufinfo.len, crc ^ 0xffffffff); return mp_obj_new_int_from_uint(crc ^ 0xffffffff); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_binascii_crc32_obj, 1, 2, mod_binascii_crc32); - +#endif STATIC const mp_rom_map_elem_t mp_module_binascii_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_binascii) }, - { MP_ROM_QSTR(MP_QSTR_hexlify), MP_ROM_PTR(&mod_binascii_hexlify_obj) }, - { MP_ROM_QSTR(MP_QSTR_unhexlify), MP_ROM_PTR(&mod_binascii_unhexlify_obj) }, + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ubinascii) }, + #if MICROPY_PY_BUILTINS_BYTES_HEX + { MP_ROM_QSTR(MP_QSTR_hexlify), MP_ROM_PTR(&bytes_hex_as_bytes_obj) }, + { MP_ROM_QSTR(MP_QSTR_unhexlify), MP_ROM_PTR(&bytes_fromhex_obj) }, + #endif { MP_ROM_QSTR(MP_QSTR_a2b_base64), MP_ROM_PTR(&mod_binascii_a2b_base64_obj) }, { MP_ROM_QSTR(MP_QSTR_b2a_base64), MP_ROM_PTR(&mod_binascii_b2a_base64_obj) }, + #if MICROPY_PY_UBINASCII_CRC32 { MP_ROM_QSTR(MP_QSTR_crc32), MP_ROM_PTR(&mod_binascii_crc32_obj) }, + #endif }; STATIC MP_DEFINE_CONST_DICT(mp_module_binascii_globals, mp_module_binascii_globals_table); diff --git a/extmod/moductypes.c b/extmod/moductypes.c index 78cf44a17a..15c36290a9 100644 --- a/extmod/moductypes.c +++ b/extmod/moductypes.c @@ -32,8 +32,6 @@ #include "py/objtuple.h" #include "py/binary.h" -#include "supervisor/shared/translate/translate.h" - #if MICROPY_PY_UCTYPES // The uctypes module allows defining the layout of a raw data structure (using @@ -192,7 +190,7 @@ STATIC mp_uint_t uctypes_struct_size(mp_obj_t desc_in, int layout_type, mp_uint_ // but scalar structure field is lowered into native Python int, so all // type info is lost. So, we cannot say if it's scalar type description, // or such lowered scalar. - mp_raise_TypeError(MP_ERROR_TEXT("cannot unambiguously get sizeof scalar")); + mp_raise_TypeError(MP_ERROR_TEXT("can't unambiguously get sizeof scalar")); } syntax_error(); } @@ -504,8 +502,8 @@ STATIC void uctypes_struct_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { } } -STATIC mp_obj_t uctypes_struct_subscr(mp_obj_t base_in, mp_obj_t index_in, mp_obj_t value) { - mp_obj_uctypes_struct_t *self = mp_obj_cast_to_native_base(base_in, &uctypes_struct_type); +STATIC mp_obj_t uctypes_struct_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value) { + mp_obj_uctypes_struct_t *self = MP_OBJ_TO_PTR(self_in); if (value == MP_OBJ_NULL) { // delete @@ -561,7 +559,7 @@ STATIC mp_obj_t uctypes_struct_subscr(mp_obj_t base_in, mp_obj_t index_in, mp_ob } } else if (agg_type == PTR) { - byte *p = *(void **)(void *)self->addr; + byte *p = *(void **)self->addr; if (mp_obj_is_small_int(t->items[1])) { uint val_type = GET_TYPE(MP_OBJ_SMALL_INT_VALUE(t->items[1]), VAL_TYPE_BITS); return get_aligned(val_type, p, index); @@ -590,7 +588,7 @@ STATIC mp_obj_t uctypes_struct_unary_op(mp_unary_op_t op, mp_obj_t self_in) { mp_int_t offset = MP_OBJ_SMALL_INT_VALUE(t->items[0]); uint agg_type = GET_TYPE(offset, AGG_TYPE_BITS); if (agg_type == PTR) { - byte *p = *(void **)(void *)self->addr; + byte *p = *(void **)self->addr; return mp_obj_new_int((mp_int_t)(uintptr_t)p); } } @@ -636,19 +634,17 @@ STATIC mp_obj_t uctypes_struct_bytes_at(mp_obj_t ptr, mp_obj_t size) { } MP_DEFINE_CONST_FUN_OBJ_2(uctypes_struct_bytes_at_obj, uctypes_struct_bytes_at); -STATIC const mp_obj_type_t uctypes_struct_type = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_struct, - .print = uctypes_struct_print, - .make_new = uctypes_struct_make_new, - .attr = uctypes_struct_attr, - MP_TYPE_EXTENDED_FIELDS( - .subscr = uctypes_struct_subscr, - .unary_op = uctypes_struct_unary_op, - .buffer_p = { .get_buffer = uctypes_get_buffer }, - ), -}; +STATIC MP_DEFINE_CONST_OBJ_TYPE( + uctypes_struct_type, + MP_QSTR_struct, + MP_TYPE_FLAG_NONE, + make_new, uctypes_struct_make_new, + print, uctypes_struct_print, + attr, uctypes_struct_attr, + subscr, uctypes_struct_subscr, + unary_op, uctypes_struct_unary_op, + buffer, uctypes_get_buffer + ); STATIC const mp_rom_map_elem_t mp_module_uctypes_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uctypes) }, diff --git a/extmod/moduhashlib.c b/extmod/moduhashlib.c index 11f8d1e03d..bed7c704e1 100644 --- a/extmod/moduhashlib.c +++ b/extmod/moduhashlib.c @@ -29,8 +29,6 @@ #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" - #if MICROPY_PY_UHASHLIB #if MICROPY_SSL_MBEDTLS @@ -111,7 +109,7 @@ STATIC mp_obj_t uhashlib_sha256_digest(mp_obj_t self_in) { vstr_t vstr; vstr_init_len(&vstr, 32); mbedtls_sha256_finish_ret((mbedtls_sha256_context *)&self->state, (unsigned char *)vstr.buf); - return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); + return mp_obj_new_bytes_from_vstr(&vstr); } #else @@ -154,7 +152,7 @@ STATIC mp_obj_t uhashlib_sha256_digest(mp_obj_t self_in) { vstr_t vstr; vstr_init_len(&vstr, SHA256_BLOCK_SIZE); sha256_final((CRYAL_SHA256_CTX *)self->state, (byte *)vstr.buf); - return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); + return mp_obj_new_bytes_from_vstr(&vstr); } #endif @@ -168,12 +166,13 @@ STATIC const mp_rom_map_elem_t uhashlib_sha256_locals_dict_table[] = { STATIC MP_DEFINE_CONST_DICT(uhashlib_sha256_locals_dict, uhashlib_sha256_locals_dict_table); -STATIC const mp_obj_type_t uhashlib_sha256_type = { - { &mp_type_type }, - .name = MP_QSTR_sha256, - .make_new = uhashlib_sha256_make_new, - .locals_dict = (void *)&uhashlib_sha256_locals_dict, -}; +STATIC MP_DEFINE_CONST_OBJ_TYPE( + uhashlib_sha256_type, + MP_QSTR_sha256, + MP_TYPE_FLAG_NONE, + make_new, uhashlib_sha256_make_new, + locals_dict, &uhashlib_sha256_locals_dict + ); #endif #if MICROPY_PY_UHASHLIB_SHA1 @@ -207,7 +206,7 @@ STATIC mp_obj_t uhashlib_sha1_digest(mp_obj_t self_in) { vstr_t vstr; vstr_init_len(&vstr, SHA1_SIZE); SHA1_Final((byte *)vstr.buf, (SHA1_CTX *)self->state); - return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); + return mp_obj_new_bytes_from_vstr(&vstr); } #endif @@ -248,7 +247,7 @@ STATIC mp_obj_t uhashlib_sha1_digest(mp_obj_t self_in) { vstr_init_len(&vstr, 20); mbedtls_sha1_finish_ret((mbedtls_sha1_context *)self->state, (byte *)vstr.buf); mbedtls_sha1_free((mbedtls_sha1_context *)self->state); - return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); + return mp_obj_new_bytes_from_vstr(&vstr); } #endif @@ -261,12 +260,13 @@ STATIC const mp_rom_map_elem_t uhashlib_sha1_locals_dict_table[] = { }; STATIC MP_DEFINE_CONST_DICT(uhashlib_sha1_locals_dict, uhashlib_sha1_locals_dict_table); -STATIC const mp_obj_type_t uhashlib_sha1_type = { - { &mp_type_type }, - .name = MP_QSTR_sha1, - .make_new = uhashlib_sha1_make_new, - .locals_dict = (void *)&uhashlib_sha1_locals_dict, -}; +STATIC MP_DEFINE_CONST_OBJ_TYPE( + uhashlib_sha1_type, + MP_QSTR_sha1, + MP_TYPE_FLAG_NONE, + make_new, uhashlib_sha1_make_new, + locals_dict, &uhashlib_sha1_locals_dict + ); #endif #if MICROPY_PY_UHASHLIB_MD5 @@ -300,7 +300,7 @@ STATIC mp_obj_t uhashlib_md5_digest(mp_obj_t self_in) { vstr_t vstr; vstr_init_len(&vstr, MD5_SIZE); MD5_Final((byte *)vstr.buf, (MD5_CTX *)self->state); - return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); + return mp_obj_new_bytes_from_vstr(&vstr); } #endif // MICROPY_SSL_AXTLS @@ -341,7 +341,7 @@ STATIC mp_obj_t uhashlib_md5_digest(mp_obj_t self_in) { vstr_init_len(&vstr, 16); mbedtls_md5_finish_ret((mbedtls_md5_context *)self->state, (byte *)vstr.buf); mbedtls_md5_free((mbedtls_md5_context *)self->state); - return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); + return mp_obj_new_bytes_from_vstr(&vstr); } #endif // MICROPY_SSL_MBEDTLS @@ -354,12 +354,13 @@ STATIC const mp_rom_map_elem_t uhashlib_md5_locals_dict_table[] = { }; STATIC MP_DEFINE_CONST_DICT(uhashlib_md5_locals_dict, uhashlib_md5_locals_dict_table); -STATIC const mp_obj_type_t uhashlib_md5_type = { - { &mp_type_type }, - .name = MP_QSTR_md5, - .make_new = uhashlib_md5_make_new, - .locals_dict = (void *)&uhashlib_md5_locals_dict, -}; +STATIC MP_DEFINE_CONST_OBJ_TYPE( + uhashlib_md5_type, + MP_QSTR_md5, + MP_TYPE_FLAG_NONE, + make_new, uhashlib_md5_make_new, + locals_dict, &uhashlib_md5_locals_dict + ); #endif // MICROPY_PY_UHASHLIB_MD5 STATIC const mp_rom_map_elem_t mp_module_uhashlib_globals_table[] = { diff --git a/extmod/moduheapq.c b/extmod/moduheapq.c index f48d9bceb1..bfa6ba608d 100644 --- a/extmod/moduheapq.c +++ b/extmod/moduheapq.c @@ -27,8 +27,6 @@ #include "py/objlist.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" - #if MICROPY_PY_UHEAPQ // the algorithm here is modelled on CPython's heapq.py diff --git a/extmod/modujson.c b/extmod/modujson.c index 14e45e2cc1..9f454f1fc3 100644 --- a/extmod/modujson.c +++ b/extmod/modujson.c @@ -34,8 +34,6 @@ #include "py/runtime.h" #include "py/stream.h" -#include "supervisor/shared/translate/translate.h" - #if MICROPY_PY_UJSON #if MICROPY_PY_UJSON_SEPARATORS @@ -71,7 +69,7 @@ STATIC mp_obj_t mod_ujson_dump_helper(size_t n_args, const mp_obj_t *pos_args, m vstr_t vstr; vstr_init_print(&vstr, 8, &print_ext.base); mp_obj_print_helper(&print_ext.base, pos_args[0], PRINT_JSON); - return mp_obj_new_str_from_vstr(&mp_type_str, &vstr); + return mp_obj_new_str_from_utf8_vstr(&vstr); } else { // dump(obj, stream) print_ext.base.data = MP_OBJ_TO_PTR(pos_args[1]); @@ -107,7 +105,7 @@ STATIC mp_obj_t mod_ujson_dumps(mp_obj_t obj) { mp_print_t print; vstr_init_print(&vstr, 8, &print); mp_obj_print_helper(&print, obj, PRINT_JSON); - return mp_obj_new_str_from_vstr(&mp_type_str, &vstr); + return mp_obj_new_str_from_utf8_vstr(&vstr); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_ujson_dumps_obj, mod_ujson_dumps); #endif @@ -332,7 +330,7 @@ STATIC mp_obj_t _mod_ujson_load(mp_obj_t stream_obj, bool return_first_json) { S_NEXT(s); } if (flt) { - next = mp_parse_num_decimal(vstr.buf, vstr.len, false, false, NULL); + next = mp_parse_num_float(vstr.buf, vstr.len, false, NULL); } else { next = mp_parse_num_integer(vstr.buf, vstr.len, 10, NULL); } diff --git a/extmod/moduplatform.h b/extmod/moduplatform.h index d52ad7da7d..3597f7559f 100644 --- a/extmod/moduplatform.h +++ b/extmod/moduplatform.h @@ -37,11 +37,11 @@ #if defined(__ARM_ARCH) #define MICROPY_PLATFORM_ARCH "arm" -#elif defined(__x86_64__) || defined(_WIN64) +#elif defined(__x86_64__) || defined(_M_X64) #define MICROPY_PLATFORM_ARCH "x86_64" #elif defined(__i386__) || defined(_M_IX86) #define MICROPY_PLATFORM_ARCH "x86" -#elif defined(__xtensa__) || defined(_M_IX86) +#elif defined(__xtensa__) #define MICROPY_PLATFORM_ARCH "xtensa" #else #define MICROPY_PLATFORM_ARCH "" diff --git a/extmod/modure.c b/extmod/modure.c index 805c23d4ad..62aeed9b28 100644 --- a/extmod/modure.c +++ b/extmod/modure.c @@ -33,16 +33,17 @@ #include "py/objstr.h" #include "py/stackctrl.h" +#if MICROPY_PY_BUILTINS_STR_UNICODE +#include "py/unicode.h" +#endif + #if MICROPY_PY_URE #define re1_5_stack_chk() MP_STACK_CHECK() #include "lib/re1.5/re1.5.h" -// CIRCUITPY -#if MICROPY_PY_URE_DEBUG #define FLAG_DEBUG 0x1000 -#endif typedef struct _mp_obj_re_t { mp_obj_base_t base; @@ -124,6 +125,18 @@ STATIC void match_span_helper(size_t n_args, const mp_obj_t *args, mp_obj_t span e = self->caps[no * 2 + 1] - begin; } + #if MICROPY_PY_BUILTINS_STR_UNICODE + if (mp_obj_get_type(self->str) == &mp_type_str) { + const byte *begin = (const byte *)mp_obj_str_get_str(self->str); + if (s != -1) { + s = utf8_ptr_to_index(begin, begin + s); + } + if (e != -1) { + e = utf8_ptr_to_index(begin, begin + e); + } + } + #endif + span[0] = mp_obj_new_int(s); span[1] = mp_obj_new_int(e); } @@ -166,12 +179,13 @@ STATIC const mp_rom_map_elem_t match_locals_dict_table[] = { STATIC MP_DEFINE_CONST_DICT(match_locals_dict, match_locals_dict_table); -STATIC const mp_obj_type_t match_type = { - { &mp_type_type }, - .name = MP_QSTR_match, - .print = match_print, - .locals_dict = (void *)&match_locals_dict, -}; +STATIC MP_DEFINE_CONST_OBJ_TYPE( + match_type, + MP_QSTR_match, + MP_TYPE_FLAG_NONE, + print, match_print, + locals_dict, &match_locals_dict + ); #endif STATIC void re_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { @@ -183,7 +197,7 @@ STATIC void re_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t STATIC mp_obj_t ure_exec(bool is_anchored, uint n_args, const mp_obj_t *args) { (void)n_args; mp_obj_re_t *self; - if (mp_obj_is_type(args[0], &re_type)) { + if (mp_obj_is_type(args[0], (mp_obj_type_t *)&re_type)) { self = MP_OBJ_TO_PTR(args[0]); } else { self = MP_OBJ_TO_PTR(mod_re_compile(1, args)); @@ -232,7 +246,7 @@ STATIC mp_obj_t ure_exec(bool is_anchored, uint n_args, const mp_obj_t *args) { return mp_const_none; } - match->base.type = &match_type; + match->base.type = (mp_obj_type_t *)&match_type; match->num_matches = caps_num / 2; // caps_num counts start and end pointers match->str = args[1]; return MP_OBJ_FROM_PTR(match); @@ -277,7 +291,7 @@ STATIC mp_obj_t re_split(size_t n_args, const mp_obj_t *args) { mp_obj_t s = mp_obj_new_str_of_type(str_type, (const byte *)subj.begin, caps[0] - subj.begin); mp_obj_list_append(retval, s); if (self->re.sub > 0) { - mp_raise_NotImplementedError(MP_ERROR_TEXT("Splitting with sub-captures")); + mp_raise_NotImplementedError(MP_ERROR_TEXT("splitting with sub-captures")); } subj.begin = caps[1]; if (maxsplit > 0 && --maxsplit == 0) { @@ -297,7 +311,7 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(re_split_obj, 2, 3, re_split); STATIC mp_obj_t re_sub_helper(size_t n_args, const mp_obj_t *args) { mp_obj_re_t *self; - if (mp_obj_is_type(args[0], &re_type)) { + if (mp_obj_is_type(args[0], (mp_obj_type_t *)&re_type)) { self = MP_OBJ_TO_PTR(args[0]); } else { self = MP_OBJ_TO_PTR(mod_re_compile(1, args)); @@ -320,7 +334,7 @@ STATIC mp_obj_t re_sub_helper(size_t n_args, const mp_obj_t *args) { vstr_t vstr_return; vstr_return.buf = NULL; // We'll init the vstr after the first match mp_obj_match_t *match = mp_local_alloc(sizeof(mp_obj_match_t) + caps_num * sizeof(char *)); - match->base.type = &match_type; + match->base.type = (mp_obj_type_t *)&match_type; match->num_matches = caps_num / 2; // caps_num counts start and end pointers match->str = where; @@ -405,7 +419,11 @@ STATIC mp_obj_t re_sub_helper(size_t n_args, const mp_obj_t *args) { // Add post-match string vstr_add_strn(&vstr_return, subj.begin, subj.end - subj.begin); - return mp_obj_new_str_from_vstr(mp_obj_get_type(where), &vstr_return); + if (mp_obj_get_type(where) == &mp_type_str) { + return mp_obj_new_str_from_utf8_vstr(&vstr_return); + } else { + return mp_obj_new_bytes_from_vstr(&vstr_return); + } } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(re_sub_obj, 3, 5, re_sub_helper); @@ -424,16 +442,13 @@ STATIC const mp_rom_map_elem_t re_locals_dict_table[] = { STATIC MP_DEFINE_CONST_DICT(re_locals_dict, re_locals_dict_table); -STATIC const mp_obj_type_t re_type = { - { &mp_type_type }, - #if CIRCUITPY - .name = MP_QSTR_re, - #else - .name = MP_QSTR_ure, - #endif - .print = re_print, - .locals_dict = (void *)&re_locals_dict, -}; +STATIC MP_DEFINE_CONST_OBJ_TYPE( + re_type, + MP_QSTR_ure, + MP_TYPE_FLAG_NONE, + print, re_print, + locals_dict, &re_locals_dict + ); #endif STATIC mp_obj_t mod_re_compile(size_t n_args, const mp_obj_t *args) { @@ -443,14 +458,12 @@ STATIC mp_obj_t mod_re_compile(size_t n_args, const mp_obj_t *args) { if (size == -1) { goto error; } - mp_obj_re_t *o = mp_obj_malloc_var(mp_obj_re_t, char, size, &re_type); + mp_obj_re_t *o = mp_obj_malloc_var(mp_obj_re_t, char, size, (mp_obj_type_t *)&re_type); #if MICROPY_PY_URE_DEBUG int flags = 0; if (n_args > 1) { flags = mp_obj_get_int(args[1]); } - #else - (void)n_args; #endif int error = re1_5_compilecode(&o->re, re_str); if (error != 0) { diff --git a/extmod/moduselect.c b/extmod/moduselect.c index e41adbc791..14f9ffb661 100644 --- a/extmod/moduselect.c +++ b/extmod/moduselect.c @@ -30,7 +30,6 @@ #include -#include "py/stream.h" #include "py/runtime.h" #include "py/obj.h" #include "py/objlist.h" @@ -172,8 +171,7 @@ STATIC mp_obj_t select_select(size_t n_args, const mp_obj_t *args) { mp_map_deinit(&poll_map); return mp_obj_new_tuple(3, list_array); } - // CIRCUITPY - RUN_BACKGROUND_TASKS; + MICROPY_EVENT_POLL_HOOK } } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_select_select_obj, 3, 4, select_select); @@ -252,11 +250,7 @@ STATIC mp_uint_t poll_poll_internal(uint n_args, const mp_obj_t *args) { if (n_ready > 0 || (timeout != (mp_uint_t)-1 && mp_hal_ticks_ms() - start_tick >= timeout)) { break; } - // CIRCUITPY - RUN_BACKGROUND_TASKS; - if (mp_hal_is_interrupted()) { - return 0; - } + MICROPY_EVENT_POLL_HOOK } return n_ready; @@ -343,16 +337,13 @@ STATIC const mp_rom_map_elem_t poll_locals_dict_table[] = { }; STATIC MP_DEFINE_CONST_DICT(poll_locals_dict, poll_locals_dict_table); -STATIC const mp_obj_type_t mp_type_poll = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_poll, - .locals_dict = (void *)&poll_locals_dict, - MP_TYPE_EXTENDED_FIELDS( - .getiter = mp_identity_getiter, - .iternext = poll_iternext, - ), -}; +STATIC MP_DEFINE_CONST_OBJ_TYPE( + mp_type_poll, + MP_QSTR_poll, + MP_TYPE_FLAG_ITER_IS_ITERNEXT, + iter, poll_iternext, + locals_dict, &poll_locals_dict + ); // poll() STATIC mp_obj_t select_poll(void) { diff --git a/extmod/modutimeq.c b/extmod/modutimeq.c deleted file mode 100644 index 7cabfee4be..0000000000 --- a/extmod/modutimeq.c +++ /dev/null @@ -1,239 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2014 Damien P. George - * Copyright (c) 2016-2017 Paul Sokolovsky - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include - -#include "py/objlist.h" -#include "py/runtime.h" -#include "py/smallint.h" - -#include "supervisor/shared/translate/translate.h" - -#if MICROPY_PY_UTIMEQ - -#define MODULO MICROPY_PY_UTIME_TICKS_PERIOD - -#define DEBUG 0 - -// the algorithm here is modelled on CPython's heapq.py - -struct qentry { - mp_uint_t time; - mp_uint_t id; - mp_obj_t callback; - mp_obj_t args; -}; - -typedef struct _mp_obj_utimeq_t { - mp_obj_base_t base; - mp_uint_t alloc; - mp_uint_t len; - struct qentry items[]; -} mp_obj_utimeq_t; - -STATIC mp_uint_t utimeq_id; - -STATIC mp_obj_utimeq_t *utimeq_get_heap(mp_obj_t heap_in) { - return MP_OBJ_TO_PTR(heap_in); -} - -STATIC bool time_less_than(struct qentry *item, struct qentry *parent) { - mp_uint_t item_tm = item->time; - mp_uint_t parent_tm = parent->time; - mp_uint_t res = parent_tm - item_tm; - if (res == 0) { - // TODO: This actually should use the same "ring" logic - // as for time, to avoid artifacts when id's overflow. - return item->id < parent->id; - } - if ((mp_int_t)res < 0) { - res += MODULO; - } - return res && res < (MODULO / 2); -} - -STATIC mp_obj_t utimeq_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { - mp_arg_check_num(n_args, n_kw, 1, 1, false); - mp_uint_t alloc = mp_obj_get_int(args[0]); - mp_obj_utimeq_t *o = mp_obj_malloc_var(mp_obj_utimeq_t, struct qentry, alloc, type); - memset(o->items, 0, sizeof(*o->items) * alloc); - o->alloc = alloc; - o->len = 0; - return MP_OBJ_FROM_PTR(o); -} - -STATIC void utimeq_heap_siftdown(mp_obj_utimeq_t *heap, mp_uint_t start_pos, mp_uint_t pos) { - struct qentry item = heap->items[pos]; - while (pos > start_pos) { - mp_uint_t parent_pos = (pos - 1) >> 1; - struct qentry *parent = &heap->items[parent_pos]; - bool lessthan = time_less_than(&item, parent); - if (lessthan) { - heap->items[pos] = *parent; - pos = parent_pos; - } else { - break; - } - } - heap->items[pos] = item; -} - -STATIC void utimeq_heap_siftup(mp_obj_utimeq_t *heap, mp_uint_t pos) { - mp_uint_t start_pos = pos; - mp_uint_t end_pos = heap->len; - struct qentry item = heap->items[pos]; - for (mp_uint_t child_pos = 2 * pos + 1; child_pos < end_pos; child_pos = 2 * pos + 1) { - // choose right child if it's <= left child - if (child_pos + 1 < end_pos) { - bool lessthan = time_less_than(&heap->items[child_pos], &heap->items[child_pos + 1]); - if (!lessthan) { - child_pos += 1; - } - } - // bubble up the smaller child - heap->items[pos] = heap->items[child_pos]; - pos = child_pos; - } - heap->items[pos] = item; - utimeq_heap_siftdown(heap, start_pos, pos); -} - -STATIC mp_obj_t mod_utimeq_heappush(size_t n_args, const mp_obj_t *args) { - (void)n_args; - mp_obj_t heap_in = args[0]; - mp_obj_utimeq_t *heap = utimeq_get_heap(heap_in); - if (heap->len == heap->alloc) { - mp_raise_IndexError(MP_ERROR_TEXT("queue overflow")); - } - mp_uint_t l = heap->len; - heap->items[l].time = MP_OBJ_SMALL_INT_VALUE(args[1]); - heap->items[l].id = utimeq_id++; - heap->items[l].callback = args[2]; - heap->items[l].args = args[3]; - utimeq_heap_siftdown(heap, 0, heap->len); - heap->len++; - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_utimeq_heappush_obj, 4, 4, mod_utimeq_heappush); - -STATIC mp_obj_t mod_utimeq_heappop(mp_obj_t heap_in, mp_obj_t list_ref) { - mp_obj_utimeq_t *heap = utimeq_get_heap(heap_in); - if (heap->len == 0) { - mp_raise_IndexError(MP_ERROR_TEXT("empty heap")); - } - mp_obj_list_t *ret = MP_OBJ_TO_PTR(list_ref); - if (!mp_obj_is_type(list_ref, &mp_type_list) || ret->len < 3) { - mp_raise_TypeError(NULL); - } - - struct qentry *item = &heap->items[0]; - ret->items[0] = MP_OBJ_NEW_SMALL_INT(item->time); - ret->items[1] = item->callback; - ret->items[2] = item->args; - heap->len -= 1; - heap->items[0] = heap->items[heap->len]; - heap->items[heap->len].callback = MP_OBJ_NULL; // so we don't retain a pointer - heap->items[heap->len].args = MP_OBJ_NULL; - if (heap->len) { - utimeq_heap_siftup(heap, 0); - } - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_utimeq_heappop_obj, mod_utimeq_heappop); - -STATIC mp_obj_t mod_utimeq_peektime(mp_obj_t heap_in) { - mp_obj_utimeq_t *heap = utimeq_get_heap(heap_in); - if (heap->len == 0) { - mp_raise_IndexError(MP_ERROR_TEXT("empty heap")); - } - - struct qentry *item = &heap->items[0]; - return MP_OBJ_NEW_SMALL_INT(item->time); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_utimeq_peektime_obj, mod_utimeq_peektime); - -#if DEBUG -STATIC mp_obj_t mod_utimeq_dump(mp_obj_t heap_in) { - mp_obj_utimeq_t *heap = utimeq_get_heap(heap_in); - for (int i = 0; i < heap->len; i++) { - printf(UINT_FMT "\t%p\t%p\n", heap->items[i].time, - MP_OBJ_TO_PTR(heap->items[i].callback), MP_OBJ_TO_PTR(heap->items[i].args)); - } - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_utimeq_dump_obj, mod_utimeq_dump); -#endif - -STATIC mp_obj_t utimeq_unary_op(mp_unary_op_t op, mp_obj_t self_in) { - mp_obj_utimeq_t *self = MP_OBJ_TO_PTR(self_in); - switch (op) { - case MP_UNARY_OP_BOOL: - return mp_obj_new_bool(self->len != 0); - case MP_UNARY_OP_LEN: - return MP_OBJ_NEW_SMALL_INT(self->len); - default: - return MP_OBJ_NULL; // op not supported - } -} - -STATIC const mp_rom_map_elem_t utimeq_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_push), MP_ROM_PTR(&mod_utimeq_heappush_obj) }, - { MP_ROM_QSTR(MP_QSTR_pop), MP_ROM_PTR(&mod_utimeq_heappop_obj) }, - { MP_ROM_QSTR(MP_QSTR_peektime), MP_ROM_PTR(&mod_utimeq_peektime_obj) }, - #if DEBUG - { MP_ROM_QSTR(MP_QSTR_dump), MP_ROM_PTR(&mod_utimeq_dump_obj) }, - #endif -}; - -STATIC MP_DEFINE_CONST_DICT(utimeq_locals_dict, utimeq_locals_dict_table); - -STATIC const mp_obj_type_t utimeq_type = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_utimeq, - .make_new = utimeq_make_new, - .locals_dict = (void *)&utimeq_locals_dict, - MP_TYPE_EXTENDED_FIELDS( - .unary_op = utimeq_unary_op, - ), -}; - -STATIC const mp_rom_map_elem_t mp_module_utimeq_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_utimeq) }, - { MP_ROM_QSTR(MP_QSTR_utimeq), MP_ROM_PTR(&utimeq_type) }, -}; - -STATIC MP_DEFINE_CONST_DICT(mp_module_utimeq_globals, mp_module_utimeq_globals_table); - -const mp_obj_module_t mp_module_utimeq = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t *)&mp_module_utimeq_globals, -}; - -MP_REGISTER_MODULE(MP_QSTR_utimeq, mp_module_utimeq); - -#endif // MICROPY_PY_UTIMEQ diff --git a/extmod/moduzlib.c b/extmod/moduzlib.c index 5c16ea732c..7adf280d50 100644 --- a/extmod/moduzlib.c +++ b/extmod/moduzlib.c @@ -31,8 +31,6 @@ #include "py/stream.h" #include "py/mperrno.h" -#include "supervisor/shared/translate/translate.h" - #if MICROPY_PY_UZLIB #define UZLIB_CONF_PARANOID_CHECKS (1) @@ -138,22 +136,19 @@ STATIC const mp_rom_map_elem_t decompio_locals_dict_table[] = { STATIC MP_DEFINE_CONST_DICT(decompio_locals_dict, decompio_locals_dict_table); #endif -#if !MICROPY_ENABLE_DYNRUNTIME STATIC const mp_stream_p_t decompio_stream_p = { - MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream) .read = decompio_read, }; -STATIC const mp_obj_type_t decompio_type = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_DecompIO, - .make_new = decompio_make_new, - .locals_dict = (void *)&decompio_locals_dict, - MP_TYPE_EXTENDED_FIELDS( - .protocol = &decompio_stream_p, - ), -}; +#if !MICROPY_ENABLE_DYNRUNTIME +STATIC MP_DEFINE_CONST_OBJ_TYPE( + decompio_type, + MP_QSTR_DecompIO, + MP_TYPE_FLAG_NONE, + make_new, decompio_make_new, + protocol, &decompio_stream_p, + locals_dict, &decompio_locals_dict + ); #endif STATIC mp_obj_t mod_uzlib_decompress(size_t n_args, const mp_obj_t *args) { diff --git a/extmod/utime_mphal.c b/extmod/utime_mphal.c index 3d1cdfd820..9cd5d938d0 100644 --- a/extmod/utime_mphal.c +++ b/extmod/utime_mphal.c @@ -95,6 +95,19 @@ STATIC mp_obj_t time_ticks_add(mp_obj_t ticks_in, mp_obj_t delta_in) { // we assume that first argument come from ticks_xx so is small int mp_uint_t ticks = MP_OBJ_SMALL_INT_VALUE(ticks_in); mp_uint_t delta = mp_obj_get_int(delta_in); + + // Check that delta does not overflow the range that ticks_diff can handle. + // This ensures the following: + // - ticks_diff(ticks_add(T, delta), T) == delta + // - ticks_diff(T, ticks_add(T, delta)) == -delta + // The latter requires excluding delta=-TICKS_PERIOD/2. + // + // This unsigned comparison is equivalent to a signed comparison of: + // delta <= -TICKS_PERIOD/2 || delta >= TICKS_PERIOD/2 + if (delta + MICROPY_PY_UTIME_TICKS_PERIOD / 2 - 1 >= MICROPY_PY_UTIME_TICKS_PERIOD - 1) { + mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("ticks interval overflow")); + } + return MP_OBJ_NEW_SMALL_INT((ticks + delta) & (MICROPY_PY_UTIME_TICKS_PERIOD - 1)); } MP_DEFINE_CONST_FUN_OBJ_2(mp_utime_ticks_add_obj, time_ticks_add); diff --git a/extmod/vfs.c b/extmod/vfs.c index 61d85b293b..f35b4cabe6 100644 --- a/extmod/vfs.c +++ b/extmod/vfs.c @@ -96,11 +96,12 @@ mp_vfs_mount_t *mp_vfs_lookup_path(const char *path, const char **path_out) { STATIC mp_vfs_mount_t *lookup_path(mp_obj_t path_in, mp_obj_t *path_out) { const char *path = mp_obj_str_get_str(path_in); const char *p_out; - *path_out = mp_const_none; mp_vfs_mount_t *vfs = mp_vfs_lookup_path(path, &p_out); if (vfs != MP_VFS_NONE && vfs != MP_VFS_ROOT) { *path_out = mp_obj_new_str_of_type(mp_obj_get_type(path_in), (const byte *)p_out, strlen(p_out)); + } else { + *path_out = MP_OBJ_NULL; } return vfs; } @@ -131,8 +132,9 @@ mp_import_stat_t mp_vfs_import_stat(const char *path) { } // If the mounted object has the VFS protocol, call its import_stat helper - const mp_vfs_proto_t *proto = (mp_vfs_proto_t *)mp_proto_get(MP_QSTR_protocol_vfs, vfs->obj); - if (proto != NULL) { + const mp_obj_type_t *type = mp_obj_get_type(vfs->obj); + if (MP_OBJ_TYPE_HAS_SLOT(type, protocol)) { + const mp_vfs_proto_t *proto = MP_OBJ_TYPE_GET_SLOT(type, protocol); return proto->import_stat(MP_OBJ_TO_PTR(vfs->obj), path_out); } @@ -171,7 +173,7 @@ STATIC mp_obj_t mp_vfs_autodetect(mp_obj_t bdev_obj) { #if MICROPY_VFS_LFS1 if (memcmp(&buf[32], "littlefs", 8) == 0) { // LFS1 - mp_obj_t vfs = mp_type_vfs_lfs1.make_new(&mp_type_vfs_lfs1, 1, 0, &bdev_obj); + mp_obj_t vfs = MP_OBJ_TYPE_GET_SLOT(&mp_type_vfs_lfs1, make_new)(&mp_type_vfs_lfs1, 1, 0, &bdev_obj); nlr_pop(); return vfs; } @@ -179,7 +181,7 @@ STATIC mp_obj_t mp_vfs_autodetect(mp_obj_t bdev_obj) { #if MICROPY_VFS_LFS2 if (memcmp(&buf[0], "littlefs", 8) == 0) { // LFS2 - mp_obj_t vfs = mp_type_vfs_lfs2.make_new(&mp_type_vfs_lfs2, 1, 0, &bdev_obj); + mp_obj_t vfs = MP_OBJ_TYPE_GET_SLOT(&mp_type_vfs_lfs2, make_new)(&mp_type_vfs_lfs2, 1, 0, &bdev_obj); nlr_pop(); return vfs; } @@ -192,7 +194,7 @@ STATIC mp_obj_t mp_vfs_autodetect(mp_obj_t bdev_obj) { #endif #if MICROPY_VFS_FAT - return mp_fat_vfs_type.make_new(&mp_fat_vfs_type, 1, 0, &bdev_obj); + return MP_OBJ_TYPE_GET_SLOT(&mp_fat_vfs_type, make_new)(&mp_fat_vfs_type, 1, 0, &bdev_obj); #endif // no filesystem found @@ -311,7 +313,7 @@ mp_obj_t mp_vfs_open(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) #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)) { - return mp_vfs_posix_file_open(&mp_type_textio, args[ARG_file].u_obj, args[ARG_mode].u_obj); + return mp_vfs_posix_file_open(&mp_type_vfs_posix_textio, args[ARG_file].u_obj, args[ARG_mode].u_obj); } #endif @@ -363,6 +365,7 @@ mp_obj_t mp_vfs_getcwd(void) { } MP_DEFINE_CONST_FUN_OBJ_0(mp_vfs_getcwd_obj, mp_vfs_getcwd); +// CIRCUITPY: accessible from shared-module/os/__init__.c mp_obj_t mp_vfs_ilistdir_it_iternext(mp_obj_t self_in) { mp_vfs_ilistdir_it_t *self = MP_OBJ_TO_PTR(self_in); if (self->is_iter) { @@ -402,7 +405,7 @@ mp_obj_t mp_vfs_ilistdir(size_t n_args, const mp_obj_t *args) { path_in = MP_OBJ_NEW_QSTR(MP_QSTR_); } - mp_obj_t path_out = mp_const_none; + mp_obj_t path_out; mp_vfs_mount_t *vfs = lookup_path(path_in, &path_out); if (vfs == MP_VFS_ROOT) { @@ -535,4 +538,7 @@ int mp_vfs_mount_and_chdir_protected(mp_obj_t bdev, mp_obj_t mount_point) { return ret; } +MP_REGISTER_ROOT_POINTER(struct _mp_vfs_mount_t *vfs_cur); +MP_REGISTER_ROOT_POINTER(struct _mp_vfs_mount_t *vfs_mount_table); + #endif // MICROPY_VFS diff --git a/extmod/vfs_fat.c b/extmod/vfs_fat.c index 4d4e7e881a..f9f21f6e35 100644 --- a/extmod/vfs_fat.c +++ b/extmod/vfs_fat.c @@ -28,6 +28,10 @@ #include "py/mpconfig.h" #if MICROPY_VFS_FAT +#if !MICROPY_ENABLE_FINALISER +#error "MICROPY_VFS_FAT requires MICROPY_ENABLE_FINALISER" +#endif + #if !MICROPY_VFS #error "with MICROPY_VFS_FAT enabled, must also enable MICROPY_VFS" #endif @@ -41,7 +45,6 @@ #include "extmod/vfs_fat.h" #include "shared/timeutils/timeutils.h" #include "supervisor/filesystem.h" -#include "supervisor/shared/translate/translate.h" #if FF_MAX_SS == FF_MIN_SS #define SECSIZE(fs) (FF_MIN_SS) @@ -101,7 +104,7 @@ STATIC void verify_fs_writable(fs_user_mount_t *vfs) { } } -#if FF_FS_REENTRANT +#if _FS_REENTRANT STATIC mp_obj_t fat_vfs_del(mp_obj_t self_in) { mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(self_in); // f_umount only needs to be called to release the sync object @@ -177,6 +180,13 @@ STATIC mp_obj_t mp_vfs_fat_ilistdir_it_iternext(mp_obj_t self_in) { return MP_OBJ_STOP_ITERATION; } +STATIC mp_obj_t mp_vfs_fat_ilistdir_it_del(mp_obj_t self_in) { + mp_vfs_fat_ilistdir_it_t *self = MP_OBJ_TO_PTR(self_in); + // ignore result / error because we may be closing a second time. + f_closedir(&self->dir); + return mp_const_none; +} + STATIC mp_obj_t fat_vfs_ilistdir_func(size_t n_args, const mp_obj_t *args) { mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(args[0]); bool is_str_type = true; @@ -191,8 +201,10 @@ STATIC mp_obj_t fat_vfs_ilistdir_func(size_t n_args, const mp_obj_t *args) { } // Create a new iterator object to list the dir - mp_vfs_fat_ilistdir_it_t *iter = mp_obj_malloc(mp_vfs_fat_ilistdir_it_t, &mp_type_polymorph_iter); + mp_vfs_fat_ilistdir_it_t *iter = m_new_obj_with_finaliser(mp_vfs_fat_ilistdir_it_t); + iter->base.type = &mp_type_polymorph_iter_with_finaliser; iter->iternext = mp_vfs_fat_ilistdir_it_iternext; + iter->finaliser = mp_vfs_fat_ilistdir_it_del; iter->is_str = is_str_type; FRESULT res = f_opendir(&self->fatfs, &iter->dir, path); if (res != FR_OK) { @@ -495,7 +507,7 @@ STATIC const mp_obj_property_t fat_vfs_label_obj = { #endif STATIC const mp_rom_map_elem_t fat_vfs_locals_dict_table[] = { - #if FF_FS_REENTRANT + #if _FS_REENTRANT { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&fat_vfs_del_obj) }, #endif { MP_ROM_QSTR(MP_QSTR_mkfs), MP_ROM_PTR(&fat_vfs_mkfs_obj) }, @@ -524,16 +536,13 @@ STATIC const mp_vfs_proto_t fat_vfs_proto = { .import_stat = fat_vfs_import_stat, }; -const mp_obj_type_t mp_fat_vfs_type = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_VfsFat, - .make_new = fat_vfs_make_new, - .locals_dict = (mp_obj_dict_t *)&fat_vfs_locals_dict, - MP_TYPE_EXTENDED_FIELDS( - .protocol = &fat_vfs_proto, - ), - -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_fat_vfs_type, + MP_QSTR_VfsFat, + MP_TYPE_FLAG_NONE, + make_new, fat_vfs_make_new, + protocol, &fat_vfs_proto, + locals_dict, &fat_vfs_locals_dict + ); #endif // MICROPY_VFS_FAT diff --git a/extmod/vfs_fat_file.c b/extmod/vfs_fat_file.c index 36070f1011..81f07e1803 100644 --- a/extmod/vfs_fat_file.c +++ b/extmod/vfs_fat_file.c @@ -148,17 +148,62 @@ STATIC mp_uint_t file_obj_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, } } -// Note: encoding is ignored for now; it's also not a valid kwarg for CPython's FileIO, -// but by adding it here we can use one single mp_arg_t array for open() and FileIO's constructor -STATIC const mp_arg_t file_open_args[] = { - { MP_QSTR_file, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_NONE} }, - { MP_QSTR_mode, MP_ARG_OBJ, {.u_obj = MP_OBJ_NEW_QSTR(MP_QSTR_r)} }, - { MP_QSTR_encoding, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = MP_ROM_NONE} }, +// TODO gc hook to close the file if not already closed + +STATIC const mp_rom_map_elem_t vfs_fat_rawfile_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, + { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, + { MP_ROM_QSTR(MP_QSTR_readlines), MP_ROM_PTR(&mp_stream_unbuffered_readlines_obj) }, + { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, + { MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&mp_stream_flush_obj) }, + { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) }, + { MP_ROM_QSTR(MP_QSTR_seek), MP_ROM_PTR(&mp_stream_seek_obj) }, + { MP_ROM_QSTR(MP_QSTR_tell), MP_ROM_PTR(&mp_stream_tell_obj) }, + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mp_stream_close_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&mp_identity_obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&file_obj___exit___obj) }, }; #define FILE_OPEN_NUM_ARGS MP_ARRAY_SIZE(file_open_args) -// CIRCUITPY is more careful about validating the open mode. -STATIC mp_obj_t file_open(fs_user_mount_t *vfs, const mp_obj_type_t *type, mp_arg_val_t *args) { +STATIC MP_DEFINE_CONST_DICT(vfs_fat_rawfile_locals_dict, vfs_fat_rawfile_locals_dict_table); + +STATIC const mp_stream_p_t vfs_fat_fileio_stream_p = { + .read = file_obj_read, + .write = file_obj_write, + .ioctl = file_obj_ioctl, +}; + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_vfs_fat_fileio, + MP_QSTR_FileIO, + MP_TYPE_FLAG_ITER_IS_STREAM, + print, file_obj_print, + protocol, &vfs_fat_fileio_stream_p, + locals_dict, &vfs_fat_rawfile_locals_dict + ); + +STATIC const mp_stream_p_t vfs_fat_textio_stream_p = { + .read = file_obj_read, + .write = file_obj_write, + .ioctl = file_obj_ioctl, + .is_text = true, +}; + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_vfs_fat_textio, + MP_QSTR_TextIOWrapper, + MP_TYPE_FLAG_ITER_IS_STREAM, + print, file_obj_print, + protocol, &vfs_fat_textio_stream_p, + locals_dict, &vfs_fat_rawfile_locals_dict + ); + +// Factory function for I/O stream classes +STATIC mp_obj_t fat_vfs_open(mp_obj_t self_in, mp_obj_t path_in, mp_obj_t mode_in) { + fs_user_mount_t *self = MP_OBJ_TO_PTR(self_in); + + const mp_obj_type_t *type = &mp_type_vfs_fat_textio; int mode = 0; const char *mode_s = mp_obj_str_get_str(args[1].u_obj); uint32_t rwxa_count = 0; @@ -222,6 +267,7 @@ STATIC mp_obj_t file_open(fs_user_mount_t *vfs, const mp_obj_type_t *type, mp_ar m_del_obj(pyb_file_obj_t, o); mp_raise_OSError_errno_str(fresult_to_errno_table[res], args[0].u_obj); } + // CIRCUITPY does fast seek. // If we're reading, turn on fast seek. if (mode == FA_READ) { // One call to determine how much space we need. @@ -249,87 +295,6 @@ STATIC mp_obj_t file_open(fs_user_mount_t *vfs, const mp_obj_type_t *type, mp_ar return MP_OBJ_FROM_PTR(o); } - -STATIC mp_obj_t file_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { - mp_arg_val_t arg_vals[FILE_OPEN_NUM_ARGS]; - mp_arg_parse_all_kw_array(n_args, n_kw, args, FILE_OPEN_NUM_ARGS, file_open_args, arg_vals); - return file_open(NULL, type, arg_vals); -} - -// TODO gc hook to close the file if not already closed - -STATIC const mp_rom_map_elem_t vfs_fat_rawfile_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, - { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, - { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, - { MP_ROM_QSTR(MP_QSTR_readlines), MP_ROM_PTR(&mp_stream_unbuffered_readlines_obj) }, - { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, - { MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&mp_stream_flush_obj) }, - { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) }, - { MP_ROM_QSTR(MP_QSTR_seek), MP_ROM_PTR(&mp_stream_seek_obj) }, - { MP_ROM_QSTR(MP_QSTR_tell), MP_ROM_PTR(&mp_stream_tell_obj) }, - { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mp_stream_close_obj) }, - { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&mp_identity_obj) }, - { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&file_obj___exit___obj) }, -}; - -STATIC MP_DEFINE_CONST_DICT(vfs_fat_rawfile_locals_dict, vfs_fat_rawfile_locals_dict_table); - -#if MICROPY_PY_IO_FILEIO -STATIC const mp_stream_p_t vfs_fat_fileio_stream_p = { - MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream) - .read = file_obj_read, - .write = file_obj_write, - .ioctl = file_obj_ioctl, -}; - -const mp_obj_type_t mp_type_vfs_fat_fileio = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_FileIO, - .print = file_obj_print, - .make_new = file_obj_make_new, - .locals_dict = (mp_obj_dict_t *)&vfs_fat_rawfile_locals_dict, - MP_TYPE_EXTENDED_FIELDS( - .getiter = mp_identity_getiter, - .iternext = mp_stream_unbuffered_iter, - .protocol = &vfs_fat_fileio_stream_p, - ), -}; -#endif - -STATIC const mp_stream_p_t vfs_fat_textio_stream_p = { - MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream) - .read = file_obj_read, - .write = file_obj_write, - .ioctl = file_obj_ioctl, - .is_text = true, -}; - -const mp_obj_type_t mp_type_vfs_fat_textio = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_TextIOWrapper, - .print = file_obj_print, - .make_new = file_obj_make_new, - .locals_dict = (mp_obj_dict_t *)&vfs_fat_rawfile_locals_dict, - MP_TYPE_EXTENDED_FIELDS( - .getiter = mp_identity_getiter, - .iternext = mp_stream_unbuffered_iter, - .protocol = &vfs_fat_textio_stream_p, - ), -}; - -// Factory function for I/O stream classes -STATIC mp_obj_t fatfs_builtin_open_self(mp_obj_t self_in, mp_obj_t path, mp_obj_t mode) { - // TODO: analyze buffering args and instantiate appropriate type - fs_user_mount_t *self = MP_OBJ_TO_PTR(self_in); - mp_arg_val_t arg_vals[FILE_OPEN_NUM_ARGS]; - arg_vals[0].u_obj = path; - arg_vals[1].u_obj = mode; - arg_vals[2].u_obj = mp_const_none; - return file_open(self, &mp_type_vfs_fat_textio, arg_vals); -} -MP_DEFINE_CONST_FUN_OBJ_3(fat_vfs_open_obj, fatfs_builtin_open_self); +MP_DEFINE_CONST_FUN_OBJ_3(fat_vfs_open_obj, fat_vfs_open); #endif // MICROPY_VFS && MICROPY_VFS_FAT diff --git a/extmod/vfs_lfs.c b/extmod/vfs_lfs.c index f6a9a24623..4fb86b89b7 100644 --- a/extmod/vfs_lfs.c +++ b/extmod/vfs_lfs.c @@ -26,12 +26,13 @@ #include "py/runtime.h" #include "py/mphal.h" + +#if MICROPY_VFS && (MICROPY_VFS_LFS1 || MICROPY_VFS_LFS2) + #include "shared/timeutils/timeutils.h" #include "extmod/vfs.h" #include "extmod/vfs_lfs.h" -#if MICROPY_VFS && (MICROPY_VFS_LFS1 || MICROPY_VFS_LFS2) - enum { LFS_MAKE_ARG_bdev, LFS_MAKE_ARG_readsize, LFS_MAKE_ARG_progsize, LFS_MAKE_ARG_lookahead, LFS_MAKE_ARG_mtime }; static const mp_arg_t lfs_make_allowed_args[] = { diff --git a/extmod/vfs_lfsx.c b/extmod/vfs_lfsx.c index 0cd3295faf..de1f421977 100644 --- a/extmod/vfs_lfsx.c +++ b/extmod/vfs_lfsx.c @@ -24,6 +24,9 @@ * THE SOFTWARE. */ +// This file should be compiled when included from vfs_lfs.c. +#if defined(LFS_BUILD_VERSION) + #include #include @@ -36,6 +39,10 @@ #include "extmod/vfs.h" #include "shared/timeutils/timeutils.h" +#if !MICROPY_ENABLE_FINALISER +#error "MICROPY_VFS_LFS requires MICROPY_ENABLE_FINALISER" +#endif + STATIC int MP_VFS_LFSx(dev_ioctl)(const struct LFSx_API (config) * c, int cmd, int arg, bool must_return_int) { mp_obj_t ret = mp_vfs_blockdev_ioctl(c->context, cmd, arg); int ret_i = 0; @@ -155,6 +162,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_3(MP_VFS_LFSx(open_obj), MP_VFS_LFSx(file_open)); typedef struct MP_VFS_LFSx (_ilistdir_it_t) { mp_obj_base_t base; mp_fun_1_t iternext; + mp_fun_1_t finaliser; bool is_str; MP_OBJ_VFS_LFSx *vfs; LFSx_API(dir_t) dir; @@ -163,11 +171,16 @@ typedef struct MP_VFS_LFSx (_ilistdir_it_t) { STATIC mp_obj_t MP_VFS_LFSx(ilistdir_it_iternext)(mp_obj_t self_in) { MP_VFS_LFSx(ilistdir_it_t) * self = MP_OBJ_TO_PTR(self_in); + if (self->vfs == NULL) { + return MP_OBJ_STOP_ITERATION; + } + struct LFSx_API (info) info; for (;;) { int ret = LFSx_API(dir_read)(&self->vfs->lfs, &self->dir, &info); if (ret == 0) { LFSx_API(dir_close)(&self->vfs->lfs, &self->dir); + self->vfs = NULL; return MP_OBJ_STOP_ITERATION; } if (!(info.name[0] == '.' && (info.name[1] == '\0' @@ -190,6 +203,14 @@ STATIC mp_obj_t MP_VFS_LFSx(ilistdir_it_iternext)(mp_obj_t self_in) { return MP_OBJ_FROM_PTR(t); } +STATIC mp_obj_t MP_VFS_LFSx(ilistdir_it_del)(mp_obj_t self_in) { + MP_VFS_LFSx(ilistdir_it_t) * self = MP_OBJ_TO_PTR(self_in); + if (self->vfs != NULL) { + LFSx_API(dir_close)(&self->vfs->lfs, &self->dir); + } + return mp_const_none; +} + STATIC mp_obj_t MP_VFS_LFSx(ilistdir_func)(size_t n_args, const mp_obj_t *args) { MP_OBJ_VFS_LFSx *self = MP_OBJ_TO_PTR(args[0]); bool is_str_type = true; @@ -203,14 +224,17 @@ STATIC mp_obj_t MP_VFS_LFSx(ilistdir_func)(size_t n_args, const mp_obj_t *args) path = vstr_null_terminated_str(&self->cur_dir); } - MP_VFS_LFSx(ilistdir_it_t) * iter = mp_obj_malloc(MP_VFS_LFSx(ilistdir_it_t), &mp_type_polymorph_iter); + MP_VFS_LFSx(ilistdir_it_t) * iter = m_new_obj_with_finaliser(MP_VFS_LFSx(ilistdir_it_t)); + iter->base.type = &mp_type_polymorph_iter_with_finaliser; + iter->iternext = MP_VFS_LFSx(ilistdir_it_iternext); + iter->finaliser = MP_VFS_LFSx(ilistdir_it_del); iter->is_str = is_str_type; - iter->vfs = self; int ret = LFSx_API(dir_open)(&self->lfs, &iter->dir, path); if (ret < 0) { mp_raise_OSError(-ret); } + iter->vfs = self; return MP_OBJ_FROM_PTR(iter); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(MP_VFS_LFSx(ilistdir_obj), 1, 2, MP_VFS_LFSx(ilistdir_func)); @@ -481,17 +505,21 @@ STATIC const mp_vfs_proto_t MP_VFS_LFSx(proto) = { .import_stat = MP_VFS_LFSx(import_stat), }; -const mp_obj_type_t MP_TYPE_VFS_LFSx = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - #if LFS_BUILD_VERSION == 1 - .name = MP_QSTR_VfsLfs1, - #else - .name = MP_QSTR_VfsLfs2, - #endif - .make_new = MP_VFS_LFSx(make_new), - .locals_dict = (mp_obj_dict_t *)&MP_VFS_LFSx(locals_dict), - MP_TYPE_EXTENDED_FIELDS( - .protocol = &MP_VFS_LFSx(proto), - ), -}; +#if LFS_BUILD_VERSION == 1 +#define VFS_LFSx_QSTR MP_QSTR_VfsLfs1 +#else +#define VFS_LFSx_QSTR MP_QSTR_VfsLfs2 +#endif + +MP_DEFINE_CONST_OBJ_TYPE( + MP_TYPE_VFS_LFSx, + VFS_LFSx_QSTR, + MP_TYPE_FLAG_NONE, + make_new, MP_VFS_LFSx(make_new), + protocol, &MP_VFS_LFSx(proto), + locals_dict, &MP_VFS_LFSx(locals_dict) + ); + +#undef VFS_LFSx_QSTR + +#endif // defined(LFS_BUILD_VERSION) diff --git a/extmod/vfs_lfsx_file.c b/extmod/vfs_lfsx_file.c index da0c1405f1..5f0155d3c5 100644 --- a/extmod/vfs_lfsx_file.c +++ b/extmod/vfs_lfsx_file.c @@ -24,6 +24,9 @@ * THE SOFTWARE. */ +// This file should be compiled when included from vfs_lfs.c. +#if defined(LFS_BUILD_VERSION) + #include #include @@ -68,11 +71,9 @@ mp_obj_t MP_VFS_LFSx(file_open)(mp_obj_t self_in, mp_obj_t path_in, mp_obj_t mod case '+': flags |= LFSx_MACRO(_O_RDWR); break; - #if MICROPY_PY_IO_FILEIO case 'b': type = &MP_TYPE_VFS_LFSx_(_fileio); break; - #endif case 't': type = &MP_TYPE_VFS_LFSx_(_textio); break; @@ -216,45 +217,35 @@ STATIC const mp_rom_map_elem_t MP_VFS_LFSx(file_locals_dict_table)[] = { }; STATIC MP_DEFINE_CONST_DICT(MP_VFS_LFSx(file_locals_dict), MP_VFS_LFSx(file_locals_dict_table)); -#if MICROPY_PY_IO_FILEIO STATIC const mp_stream_p_t MP_VFS_LFSx(fileio_stream_p) = { - MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream) .read = MP_VFS_LFSx(file_read), .write = MP_VFS_LFSx(file_write), .ioctl = MP_VFS_LFSx(file_ioctl), }; -const mp_obj_type_t MP_TYPE_VFS_LFSx_(_fileio) = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_FileIO, - .print = MP_VFS_LFSx(file_print), - .locals_dict = (mp_obj_dict_t *)&MP_VFS_LFSx(file_locals_dict), - MP_TYPE_EXTENDED_FIELDS( - .getiter = mp_identity_getiter, - .iternext = mp_stream_unbuffered_iter, - .protocol = &MP_VFS_LFSx(fileio_stream_p), - ), -}; -#endif +MP_DEFINE_CONST_OBJ_TYPE( + MP_TYPE_VFS_LFSx_(_fileio), + MP_QSTR_FileIO, + MP_TYPE_FLAG_ITER_IS_STREAM, + print, MP_VFS_LFSx(file_print), + protocol, &MP_VFS_LFSx(fileio_stream_p), + locals_dict, &MP_VFS_LFSx(file_locals_dict) + ); STATIC const mp_stream_p_t MP_VFS_LFSx(textio_stream_p) = { - MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream) .read = MP_VFS_LFSx(file_read), .write = MP_VFS_LFSx(file_write), .ioctl = MP_VFS_LFSx(file_ioctl), .is_text = true, }; -const mp_obj_type_t MP_TYPE_VFS_LFSx_(_textio) = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_TextIOWrapper, - .print = MP_VFS_LFSx(file_print), - .locals_dict = (mp_obj_dict_t *)&MP_VFS_LFSx(file_locals_dict), - MP_TYPE_EXTENDED_FIELDS( - .getiter = mp_identity_getiter, - .iternext = mp_stream_unbuffered_iter, - .protocol = &MP_VFS_LFSx(textio_stream_p), - ), -}; +MP_DEFINE_CONST_OBJ_TYPE( + MP_TYPE_VFS_LFSx_(_textio), + MP_QSTR_TextIOWrapper, + MP_TYPE_FLAG_ITER_IS_STREAM, + print, MP_VFS_LFSx(file_print), + protocol, &MP_VFS_LFSx(textio_stream_p), + locals_dict, &MP_VFS_LFSx(file_locals_dict) + ); + +#endif // defined(LFS_BUILD_VERSION) diff --git a/extmod/vfs_posix.c b/extmod/vfs_posix.c index 4e73acea6f..9b856e1f01 100644 --- a/extmod/vfs_posix.c +++ b/extmod/vfs_posix.c @@ -31,11 +31,17 @@ #include "extmod/vfs.h" #include "extmod/vfs_posix.h" -#if defined(MICROPY_VFS_POSIX) && MICROPY_VFS_POSIX +#if MICROPY_VFS_POSIX +#if !MICROPY_ENABLE_FINALISER +#error "MICROPY_VFS_POSIX requires MICROPY_ENABLE_FINALISER" +#endif + +#include #include #include #include +#include #include #ifdef _MSC_VER #include // For mkdir etc. @@ -138,7 +144,7 @@ STATIC mp_obj_t vfs_posix_open(mp_obj_t self_in, mp_obj_t path_in, mp_obj_t mode if (!mp_obj_is_small_int(path_in)) { path_in = vfs_posix_get_path_obj(self, path_in); } - return mp_vfs_posix_file_open(&mp_type_textio, path_in, mode_in); + return mp_vfs_posix_file_open(&mp_type_vfs_posix_textio, path_in, mode_in); } STATIC MP_DEFINE_CONST_FUN_OBJ_3(vfs_posix_open_obj, vfs_posix_open); @@ -162,6 +168,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(vfs_posix_getcwd_obj, vfs_posix_getcwd); typedef struct _vfs_posix_ilistdir_it_t { mp_obj_base_t base; mp_fun_1_t iternext; + mp_fun_1_t finaliser; bool is_str; DIR *dir; } vfs_posix_ilistdir_it_t; @@ -185,7 +192,7 @@ STATIC mp_obj_t vfs_posix_ilistdir_it_iternext(mp_obj_t self_in) { MP_THREAD_GIL_ENTER(); const char *fn = dirent->d_name; - if (fn[0] == '.' && (fn[1] == 0 || fn[1] == '.')) { + if (fn[0] == '.' && (fn[1] == 0 || (fn[1] == '.' && fn[2] == 0))) { // skip . and .. continue; } @@ -226,10 +233,22 @@ STATIC mp_obj_t vfs_posix_ilistdir_it_iternext(mp_obj_t self_in) { } } +STATIC mp_obj_t vfs_posix_ilistdir_it_del(mp_obj_t self_in) { + vfs_posix_ilistdir_it_t *self = MP_OBJ_TO_PTR(self_in); + if (self->dir != NULL) { + MP_THREAD_GIL_EXIT(); + closedir(self->dir); + MP_THREAD_GIL_ENTER(); + } + return mp_const_none; +} + STATIC mp_obj_t vfs_posix_ilistdir(mp_obj_t self_in, mp_obj_t path_in) { mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in); - vfs_posix_ilistdir_it_t *iter = mp_obj_malloc(vfs_posix_ilistdir_it_t, &mp_type_polymorph_iter); + vfs_posix_ilistdir_it_t *iter = m_new_obj_with_finaliser(vfs_posix_ilistdir_it_t); + iter->base.type = &mp_type_polymorph_iter_with_finaliser; iter->iternext = vfs_posix_ilistdir_it_iternext; + iter->finaliser = vfs_posix_ilistdir_it_del; iter->is_str = mp_obj_get_type(path_in) == &mp_type_str; const char *path = vfs_posix_get_path_str(self, path_in); if (path[0] == '\0') { @@ -378,19 +397,16 @@ STATIC const mp_rom_map_elem_t vfs_posix_locals_dict_table[] = { STATIC MP_DEFINE_CONST_DICT(vfs_posix_locals_dict, vfs_posix_locals_dict_table); STATIC const mp_vfs_proto_t vfs_posix_proto = { - MP_PROTO_IMPLEMENT(MP_QSTR_protocol_vfs) .import_stat = mp_vfs_posix_import_stat, }; -const mp_obj_type_t mp_type_vfs_posix = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_VfsPosix, - .locals_dict = (mp_obj_dict_t *)&vfs_posix_locals_dict, - .make_new = vfs_posix_make_new, - MP_TYPE_EXTENDED_FIELDS( - .protocol = &vfs_posix_proto, - ), -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_vfs_posix, + MP_QSTR_VfsPosix, + MP_TYPE_FLAG_NONE, + make_new, vfs_posix_make_new, + protocol, &vfs_posix_proto, + locals_dict, &vfs_posix_locals_dict + ); #endif // MICROPY_VFS_POSIX diff --git a/extmod/vfs_posix_file.c b/extmod/vfs_posix_file.c index 534b11c08c..ea19de7fd0 100644 --- a/extmod/vfs_posix_file.c +++ b/extmod/vfs_posix_file.c @@ -29,9 +29,8 @@ #include "py/runtime.h" #include "py/stream.h" #include "extmod/vfs_posix.h" -#include "supervisor/shared/translate/translate.h" -#if (defined(MICROPY_VFS_POSIX) && MICROPY_VFS_POSIX) || (defined(MICROPY_VFS_POSIX_FILE) && MICROPY_VFS_POSIX_FILE) +#if MICROPY_VFS_POSIX #include #include @@ -47,7 +46,7 @@ typedef struct _mp_obj_vfs_posix_file_t { int fd; } mp_obj_vfs_posix_file_t; -#ifdef MICROPY_CPYTHON_COMPAT +#if MICROPY_CPYTHON_COMPAT STATIC void check_fd_is_open(const mp_obj_vfs_posix_file_t *o) { if (o->fd < 0) { mp_raise_ValueError(MP_ERROR_TEXT("I/O operation on closed file")); @@ -60,11 +59,11 @@ STATIC void check_fd_is_open(const mp_obj_vfs_posix_file_t *o) { STATIC void vfs_posix_file_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { (void)kind; mp_obj_vfs_posix_file_t *self = MP_OBJ_TO_PTR(self_in); - mp_printf(print, "", mp_obj_get_type_qstr(self_in), self->fd); + mp_printf(print, "", mp_obj_get_type_str(self_in), self->fd); } mp_obj_t mp_vfs_posix_file_open(const mp_obj_type_t *type, mp_obj_t file_in, mp_obj_t mode_in) { - mp_obj_vfs_posix_file_t *o = m_new_obj(mp_obj_vfs_posix_file_t); + mp_obj_vfs_posix_file_t *o = m_new_obj_with_finaliser(mp_obj_vfs_posix_file_t); const char *mode_s = mp_obj_str_get_str(mode_in); int mode_rw = 0, mode_x = 0; @@ -84,15 +83,12 @@ mp_obj_t mp_vfs_posix_file_open(const mp_obj_type_t *type, mp_obj_t file_in, mp_ case '+': mode_rw = O_RDWR; break; - #if MICROPY_PY_IO_FILEIO - // If we don't have io.FileIO, then files are in text mode implicitly case 'b': type = &mp_type_vfs_posix_fileio; break; case 't': type = &mp_type_vfs_posix_textio; break; - #endif } } @@ -112,17 +108,6 @@ mp_obj_t mp_vfs_posix_file_open(const mp_obj_type_t *type, mp_obj_t file_in, mp_ return MP_OBJ_FROM_PTR(o); } -STATIC mp_obj_t vfs_posix_file_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { - static const mp_arg_t allowed_args[] = { - { MP_QSTR_file, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_NONE} }, - { MP_QSTR_mode, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_QSTR(MP_QSTR_r)} }, - }; - - mp_arg_val_t arg_vals[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all_kw_array(n_args, n_kw, args, MP_ARRAY_SIZE(allowed_args), allowed_args, arg_vals); - return mp_vfs_posix_file_open(type, arg_vals[0].u_obj, arg_vals[1].u_obj); -} - STATIC mp_obj_t vfs_posix_file_fileno(mp_obj_t self_in) { mp_obj_vfs_posix_file_t *self = MP_OBJ_TO_PTR(self_in); check_fd_is_open(self); @@ -150,6 +135,12 @@ STATIC mp_uint_t vfs_posix_file_read(mp_obj_t o_in, void *buf, mp_uint_t size, i STATIC mp_uint_t vfs_posix_file_write(mp_obj_t o_in, const void *buf, mp_uint_t size, int *errcode) { mp_obj_vfs_posix_file_t *o = MP_OBJ_TO_PTR(o_in); check_fd_is_open(o); + #if MICROPY_PY_OS_DUPTERM + if (o->fd <= STDERR_FILENO) { + mp_hal_stdout_tx_strn(buf, size); + return size; + } + #endif ssize_t r; MP_HAL_RETRY_SYSCALL(r, write(o->fd, buf, size), { *errcode = err; @@ -194,12 +185,12 @@ STATIC mp_uint_t vfs_posix_file_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_ return 0; } case MP_STREAM_CLOSE: - MP_THREAD_GIL_EXIT(); - close(o->fd); - MP_THREAD_GIL_ENTER(); - #ifdef MICROPY_CPYTHON_COMPAT + if (o->fd >= 0) { + MP_THREAD_GIL_EXIT(); + close(o->fd); + MP_THREAD_GIL_ENTER(); + } o->fd = -1; - #endif return 0; case MP_STREAM_GET_FILENO: return o->fd; @@ -246,59 +237,46 @@ STATIC const mp_rom_map_elem_t vfs_posix_rawfile_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_tell), MP_ROM_PTR(&mp_stream_tell_obj) }, { MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&mp_stream_flush_obj) }, { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) }, + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mp_stream_close_obj) }, { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&mp_identity_obj) }, { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&vfs_posix_file___exit___obj) }, }; STATIC MP_DEFINE_CONST_DICT(vfs_posix_rawfile_locals_dict, vfs_posix_rawfile_locals_dict_table); -#if MICROPY_PY_IO_FILEIO STATIC const mp_stream_p_t vfs_posix_fileio_stream_p = { - MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream) .read = vfs_posix_file_read, .write = vfs_posix_file_write, .ioctl = vfs_posix_file_ioctl, }; -const mp_obj_type_t mp_type_vfs_posix_fileio = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_FileIO, - .print = vfs_posix_file_print, - .make_new = vfs_posix_file_make_new, - .locals_dict = (mp_obj_dict_t *)&vfs_posix_rawfile_locals_dict, - MP_TYPE_EXTENDED_FIELDS( - .getiter = mp_identity_getiter, - .iternext = mp_stream_unbuffered_iter, - .protocol = &vfs_posix_fileio_stream_p, - ), -}; -#endif +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_vfs_posix_fileio, + MP_QSTR_FileIO, + MP_TYPE_FLAG_ITER_IS_STREAM, + print, vfs_posix_file_print, + protocol, &vfs_posix_fileio_stream_p, + locals_dict, &vfs_posix_rawfile_locals_dict + ); STATIC const mp_stream_p_t vfs_posix_textio_stream_p = { - MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream) .read = vfs_posix_file_read, .write = vfs_posix_file_write, .ioctl = vfs_posix_file_ioctl, .is_text = true, }; -const mp_obj_type_t mp_type_vfs_posix_textio = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_TextIOWrapper, - .print = vfs_posix_file_print, - .make_new = vfs_posix_file_make_new, - .locals_dict = (mp_obj_dict_t *)&vfs_posix_rawfile_locals_dict, - MP_TYPE_EXTENDED_FIELDS( - .getiter = mp_identity_getiter, - .iternext = mp_stream_unbuffered_iter, - .protocol = &vfs_posix_textio_stream_p, - ), -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_vfs_posix_textio, + MP_QSTR_TextIOWrapper, + MP_TYPE_FLAG_ITER_IS_STREAM, + print, vfs_posix_file_print, + protocol, &vfs_posix_textio_stream_p, + locals_dict, &vfs_posix_rawfile_locals_dict + ); -const mp_obj_vfs_posix_file_t mp_sys_stdin_obj = {{&mp_type_textio}, STDIN_FILENO}; -const mp_obj_vfs_posix_file_t mp_sys_stdout_obj = {{&mp_type_textio}, STDOUT_FILENO}; -const mp_obj_vfs_posix_file_t mp_sys_stderr_obj = {{&mp_type_textio}, STDERR_FILENO}; +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}; -#endif // MICROPY_VFS_POSIX || MICROPY_VFS_POSIX_FILE +#endif // MICROPY_VFS_POSIX diff --git a/extmod/virtpin.c b/extmod/virtpin.c index 510ba8dbaf..cd0b9f92f8 100644 --- a/extmod/virtpin.c +++ b/extmod/virtpin.c @@ -25,16 +25,15 @@ */ #include "extmod/virtpin.h" -#include "py/proto.h" int mp_virtual_pin_read(mp_obj_t pin) { mp_obj_base_t *s = (mp_obj_base_t *)MP_OBJ_TO_PTR(pin); - const mp_pin_p_t *pin_p = mp_proto_get(MP_QSTR_protocol_pin, s); + mp_pin_p_t *pin_p = (mp_pin_p_t *)MP_OBJ_TYPE_GET_SLOT(s->type, protocol); return pin_p->ioctl(pin, MP_PIN_READ, 0, NULL); } void mp_virtual_pin_write(mp_obj_t pin, int value) { mp_obj_base_t *s = (mp_obj_base_t *)MP_OBJ_TO_PTR(pin); - const mp_pin_p_t *pin_p = mp_proto_get(MP_QSTR_protocol_pin, s); + mp_pin_p_t *pin_p = (mp_pin_p_t *)MP_OBJ_TYPE_GET_SLOT(s->type, protocol); pin_p->ioctl(pin, MP_PIN_WRITE, value, NULL); } diff --git a/lib/cyw43-driver b/lib/cyw43-driver new file mode 160000 index 0000000000..36144f94be --- /dev/null +++ b/lib/cyw43-driver @@ -0,0 +1 @@ +Subproject commit 36144f94bee69ad5e7b46fef06ce0c2405bafee5 diff --git a/lib/libm/thumb_vfp_sqrtf.c b/lib/libm/thumb_vfp_sqrtf.c index 12ffebf827..25b8823163 100644 --- a/lib/libm/thumb_vfp_sqrtf.c +++ b/lib/libm/thumb_vfp_sqrtf.c @@ -3,7 +3,7 @@ #include float sqrtf(float x) { - asm volatile ( + __asm__ volatile ( "vsqrt.f32 %[r], %[x]\n" : [r] "=t" (x) : [x] "t" (x)); diff --git a/lib/libm_dbl/thumb_vfp_sqrt.c b/lib/libm_dbl/thumb_vfp_sqrt.c index dd37a07b05..ccd33e9796 100644 --- a/lib/libm_dbl/thumb_vfp_sqrt.c +++ b/lib/libm_dbl/thumb_vfp_sqrt.c @@ -2,7 +2,7 @@ double sqrt(double x) { double ret; - asm volatile ( + __asm__ volatile ( "vsqrt.f64 %P0, %P1\n" : "=w" (ret) : "w" (x)); diff --git a/lib/mbedtls b/lib/mbedtls deleted file mode 160000 index 1bc2c9cb8b..0000000000 --- a/lib/mbedtls +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1bc2c9cb8b8fe4659bd94b8ebba5a4c02029b7fa diff --git a/lib/mbedtls_errors/README.md b/lib/mbedtls_errors/README.md deleted file mode 100644 index 0e13021eb1..0000000000 --- a/lib/mbedtls_errors/README.md +++ /dev/null @@ -1,42 +0,0 @@ -MBEDTLS Error Strings for MicroPython -===================================== - -This directory contains source code and tools to rework the Mbedtls error strings for -micropython to use less space. In short, instead of storing and printing something like -"SSL - Our own certificate(s) is/are too large to send in an SSL message" it prints -the name of the error #define, which would be "MBEDTLS_ERR_SSL_CERTIFICATE_TOO_LARGE" in -this case, and only stores `SSL_CERTIFICATE_TOO_LARGE` in flash. The exact Mbedtls error -defines are used because they're easy to search for to find more detailed information. - -Mbedtls defines a specific format for error value #defines and -includes a Perl script to gather all `MBEDTLS_ERR` defines from includes files together with -english error text. From that the Perl script generates `mbedtls_strerror()`. The files in this -directory modify this process to produce a more space efficient error lookup table with -shorter error strings. - -The files are as follows: -- `generate_errors.diff` - diff for original mbedtls perl script -- `error.fmt` - modified code template for MicroPython -- `mp_mbedtls_errors.c` - source file with `mbedtls_strerror` this is built using the include - files in `../mbedtls` -- `do-mp.sh` - shell script to produce `mp_mbedtls_errors.c` -- `tester.c` - simple C main to test `mp_mbedtls_errors.c` locally on a dev box -- `do-test.sh` - shell script to produce `mp_mbedtls_errors.c` and compile the `tester` app -- `do-esp32.sh` - shell script to produce `esp32_mbedtls_errors.c` -- see below - -In order not to store multiple copies of `mbedtls_errors.c` -([https://github.com/micropython/micropython/pull/5819#discussion_r445528006](see)) -it is assumed that all ports use the same version of mbedtls with the same error #defines. -This is true as of MP v1.13, and ESP-IDF versions 3.3.2 and 4.0.1. If anything changes in the -future the `do-esp32.sh` script can be used to generate an esp32-specific version. - -### How-to - -- To build MicroPython all that is needed is to include the `mp_mbedtls_errors.c` into the build - (the Makefiles do this automatically). Note that Perl is not needed for routine MicroPython - builds. -- When a new version of Mbedtls is pulled-in the `do-mp.sh` script should be run to - re-generate `mp_mbedtls_errors.c`. -- The `tester` app should be run if changes to the string handling in `error.fmt` are made: - it tests that there is not an off-by-one error in the string copying/appending, etc. -- To include `mbedtls_strerror` error strings define `MBEDTLS_ERROR_C` in the build. diff --git a/lib/mbedtls_errors/do-esp32.sh b/lib/mbedtls_errors/do-esp32.sh deleted file mode 100755 index 6fd4682415..0000000000 --- a/lib/mbedtls_errors/do-esp32.sh +++ /dev/null @@ -1,7 +0,0 @@ -#! /bin/bash -e -# Generate esp32_mbedtls_errors.c for use in the Esp32 port, with the ESP-IDF version of mbedtls -# The IDF_PATH env var must be set to the top-level dir of ESPIDF -echo "IDF_PATH=$IDF_PATH" -MBEDTLS=$IDF_PATH/components/mbedtls/mbedtls -patch -o esp32_generate_errors.pl $MBEDTLS/scripts/generate_errors.pl -#endif - -#if defined(MBEDTLS_PLATFORM_C) -#include "mbedtls/platform.h" -#else -#define mbedtls_snprintf snprintf -#define mbedtls_time_t time_t -#endif - -#if defined(MBEDTLS_ERROR_C) - -#include - -HEADER_INCLUDED - -// Error code table type -struct ssl_errs { - int16_t errnum; - const char *errstr; -}; - -// Table of high level error codes -static const struct ssl_errs mbedtls_high_level_error_tab[] = { -// BEGIN generated code -HIGH_LEVEL_CODE_CHECKS -// END generated code -}; - -static const struct ssl_errs mbedtls_low_level_error_tab[] = { -// Low level error codes -// -// BEGIN generated code -LOW_LEVEL_CODE_CHECKS -// END generated code -}; - -static const char *mbedtls_err_prefix = "MBEDTLS_ERR_"; -#define MBEDTLS_ERR_PREFIX_LEN ( sizeof("MBEDTLS_ERR_")-1 ) - -// copy error text into buffer, ensure null termination, return strlen of result -static size_t mbedtls_err_to_str(int err, const struct ssl_errs tab[], int tab_len, char *buf, size_t buflen) { - if (buflen == 0) return 0; - - // prefix for all error names - strncpy(buf, mbedtls_err_prefix, buflen); - if (buflen <= MBEDTLS_ERR_PREFIX_LEN+1) { - buf[buflen-1] = 0; - return buflen-1; - } - - // append error name from table - for (int i = 0; i < tab_len; i++) { - if (tab[i].errnum == err) { - strncpy(buf+MBEDTLS_ERR_PREFIX_LEN, tab[i].errstr, buflen-MBEDTLS_ERR_PREFIX_LEN); - buf[buflen-1] = 0; - return strlen(buf); - } - } - - mbedtls_snprintf(buf+MBEDTLS_ERR_PREFIX_LEN, buflen-MBEDTLS_ERR_PREFIX_LEN, "UNKNOWN (0x%04X)", - err); - return strlen(buf); -} - -#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) - -void mbedtls_strerror(int ret, char *buf, size_t buflen) { - int use_ret; - - if (buflen == 0) return; - - buf[buflen-1] = 0; - - if (ret < 0) ret = -ret; - - // - // High-level error codes - // - uint8_t got_hl = (ret & 0xFF80) != 0; - if (got_hl) { - use_ret = ret & 0xFF80; - - // special case -#if defined(MBEDTLS_SSL_TLS_C) - if (use_ret == -(MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE)) { - strncpy(buf, "MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE", buflen); - buf[buflen-1] = 0; - return; - } -#endif - - size_t len = mbedtls_err_to_str(use_ret, mbedtls_high_level_error_tab, - ARRAY_SIZE(mbedtls_high_level_error_tab), buf, buflen); - - buf += len; - buflen -= len; - if (buflen == 0) return; - } - - // - // Low-level error codes - // - use_ret = ret & ~0xFF80; - - if (use_ret == 0) return; - - // If high level code is present, make a concatenation between both error strings. - if (got_hl) { - if (buflen < 2) return; - *buf++ = '+'; - buflen--; - } - - mbedtls_err_to_str(use_ret, mbedtls_low_level_error_tab, - ARRAY_SIZE(mbedtls_low_level_error_tab), buf, buflen); -} - -#else /* MBEDTLS_ERROR_C */ - -#if defined(MBEDTLS_ERROR_STRERROR_DUMMY) - -/* - * Provide an non-function in case MBEDTLS_ERROR_C is not defined - */ -void mbedtls_strerror( int ret, char *buf, size_t buflen ) -{ - ((void) ret); - - if( buflen > 0 ) - buf[0] = '\0'; -} - -#endif /* MBEDTLS_ERROR_STRERROR_DUMMY */ - -#endif /* MBEDTLS_ERROR_C */ diff --git a/lib/mbedtls_errors/generate_errors.diff b/lib/mbedtls_errors/generate_errors.diff deleted file mode 100644 index ad24c372fa..0000000000 --- a/lib/mbedtls_errors/generate_errors.diff +++ /dev/null @@ -1,22 +0,0 @@ ---- generate_errors_orig.pl 2020-06-20 08:40:38.819060379 -0700 -+++ generate_errors.pl 2020-06-20 08:47:26.511163591 -0700 -@@ -162,16 +162,12 @@ - - if ($error_name eq "MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE") - { -- ${$code_check} .= "${white_space}if( use_ret == -($error_name) )\n". -- "${white_space}\{\n". -- "${white_space} mbedtls_snprintf( buf, buflen, \"$module_name - $description\" );\n". -- "${white_space} return;\n". -- "${white_space}}\n" -+ # no-op, this case is hard-coded in error.fmt - } - else - { -- ${$code_check} .= "${white_space}if( use_ret == -($error_name) )\n". -- "${white_space} mbedtls_snprintf( buf, buflen, \"$module_name - $description\" );\n" -+ my $error_text = $error_name =~ s/^MBEDTLS_ERR_//r; -+ ${$code_check} .= "${white_space}{ -($error_name), \"$error_text\" },\n" - } - }; - diff --git a/lib/mbedtls_errors/mp_mbedtls_errors.c b/lib/mbedtls_errors/mp_mbedtls_errors.c deleted file mode 100644 index 03a91f0dc9..0000000000 --- a/lib/mbedtls_errors/mp_mbedtls_errors.c +++ /dev/null @@ -1,705 +0,0 @@ -/* - * Error message information - * - * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This file is part of mbed TLS (https://tls.mbed.org) - */ - -#if !defined(MBEDTLS_CONFIG_FILE) -#include "mbedtls/config.h" -#else -#include MBEDTLS_CONFIG_FILE -#endif - -#if defined(MBEDTLS_ERROR_C) || defined(MBEDTLS_ERROR_STRERROR_DUMMY) -#include "mbedtls/error.h" -#include -#endif - -#if defined(MBEDTLS_PLATFORM_C) -#include "mbedtls/platform.h" -#else -#define mbedtls_snprintf snprintf -#define mbedtls_time_t time_t -#endif - -#if defined(MBEDTLS_ERROR_C) - -#include - -#if defined(MBEDTLS_AES_C) -#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 - -#if defined(MBEDTLS_BASE64_C) -#include "mbedtls/base64.h" -#endif - -#if defined(MBEDTLS_BIGNUM_C) -#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 - -#if defined(MBEDTLS_CCM_C) -#include "mbedtls/ccm.h" -#endif - -#if defined(MBEDTLS_CHACHA20_C) -#include "mbedtls/chacha20.h" -#endif - -#if defined(MBEDTLS_CHACHAPOLY_C) -#include "mbedtls/chachapoly.h" -#endif - -#if defined(MBEDTLS_CIPHER_C) -#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 - -#if defined(MBEDTLS_DES_C) -#include "mbedtls/des.h" -#endif - -#if defined(MBEDTLS_DHM_C) -#include "mbedtls/dhm.h" -#endif - -#if defined(MBEDTLS_ECP_C) -#include "mbedtls/ecp.h" -#endif - -#if defined(MBEDTLS_ENTROPY_C) -#include "mbedtls/entropy.h" -#endif - -#if defined(MBEDTLS_GCM_C) -#include "mbedtls/gcm.h" -#endif - -#if defined(MBEDTLS_HKDF_C) -#include "mbedtls/hkdf.h" -#endif - -#if defined(MBEDTLS_HMAC_DRBG_C) -#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" -#endif - -#if defined(MBEDTLS_MD4_C) -#include "mbedtls/md4.h" -#endif - -#if defined(MBEDTLS_MD5_C) -#include "mbedtls/md5.h" -#endif - -#if defined(MBEDTLS_NET_C) -#include "mbedtls/net_sockets.h" -#endif - -#if defined(MBEDTLS_OID_C) -#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 - -#if defined(MBEDTLS_PK_C) -#include "mbedtls/pk.h" -#endif - -#if defined(MBEDTLS_PKCS12_C) -#include "mbedtls/pkcs12.h" -#endif - -#if defined(MBEDTLS_PKCS5_C) -#include "mbedtls/pkcs5.h" -#endif - -#if defined(MBEDTLS_PLATFORM_C) -#include "mbedtls/platform.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 - -#if defined(MBEDTLS_SHA1_C) -#include "mbedtls/sha1.h" -#endif - -#if defined(MBEDTLS_SHA256_C) -#include "mbedtls/sha256.h" -#endif - -#if defined(MBEDTLS_SHA512_C) -#include "mbedtls/sha512.h" -#endif - -#if defined(MBEDTLS_SSL_TLS_C) -#include "mbedtls/ssl.h" -#endif - -#if defined(MBEDTLS_THREADING_C) -#include "mbedtls/threading.h" -#endif - -#if defined(MBEDTLS_X509_USE_C) || defined(MBEDTLS_X509_CREATE_C) -#include "mbedtls/x509.h" -#endif - -#if defined(MBEDTLS_XTEA_C) -#include "mbedtls/xtea.h" -#endif - - -// Error code table type -struct ssl_errs { - int16_t errnum; - const char *errstr; -}; - -// Table of high level error codes -static const struct ssl_errs mbedtls_high_level_error_tab[] = { -// BEGIN generated code -#if defined(MBEDTLS_CIPHER_C) - { -(MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE), "CIPHER_FEATURE_UNAVAILABLE" }, - { -(MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA), "CIPHER_BAD_INPUT_DATA" }, - { -(MBEDTLS_ERR_CIPHER_ALLOC_FAILED), "CIPHER_ALLOC_FAILED" }, - { -(MBEDTLS_ERR_CIPHER_INVALID_PADDING), "CIPHER_INVALID_PADDING" }, - { -(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) - { -(MBEDTLS_ERR_DHM_BAD_INPUT_DATA), "DHM_BAD_INPUT_DATA" }, - { -(MBEDTLS_ERR_DHM_READ_PARAMS_FAILED), "DHM_READ_PARAMS_FAILED" }, - { -(MBEDTLS_ERR_DHM_MAKE_PARAMS_FAILED), "DHM_MAKE_PARAMS_FAILED" }, - { -(MBEDTLS_ERR_DHM_READ_PUBLIC_FAILED), "DHM_READ_PUBLIC_FAILED" }, - { -(MBEDTLS_ERR_DHM_MAKE_PUBLIC_FAILED), "DHM_MAKE_PUBLIC_FAILED" }, - { -(MBEDTLS_ERR_DHM_CALC_SECRET_FAILED), "DHM_CALC_SECRET_FAILED" }, - { -(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 */ - -#if defined(MBEDTLS_ECP_C) - { -(MBEDTLS_ERR_ECP_BAD_INPUT_DATA), "ECP_BAD_INPUT_DATA" }, - { -(MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL), "ECP_BUFFER_TOO_SMALL" }, - { -(MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE), "ECP_FEATURE_UNAVAILABLE" }, - { -(MBEDTLS_ERR_ECP_VERIFY_FAILED), "ECP_VERIFY_FAILED" }, - { -(MBEDTLS_ERR_ECP_ALLOC_FAILED), "ECP_ALLOC_FAILED" }, - { -(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 */ - -#if defined(MBEDTLS_MD_C) - { -(MBEDTLS_ERR_MD_FEATURE_UNAVAILABLE), "MD_FEATURE_UNAVAILABLE" }, - { -(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) - { -(MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT), "PEM_NO_HEADER_FOOTER_PRESENT" }, - { -(MBEDTLS_ERR_PEM_INVALID_DATA), "PEM_INVALID_DATA" }, - { -(MBEDTLS_ERR_PEM_ALLOC_FAILED), "PEM_ALLOC_FAILED" }, - { -(MBEDTLS_ERR_PEM_INVALID_ENC_IV), "PEM_INVALID_ENC_IV" }, - { -(MBEDTLS_ERR_PEM_UNKNOWN_ENC_ALG), "PEM_UNKNOWN_ENC_ALG" }, - { -(MBEDTLS_ERR_PEM_PASSWORD_REQUIRED), "PEM_PASSWORD_REQUIRED" }, - { -(MBEDTLS_ERR_PEM_PASSWORD_MISMATCH), "PEM_PASSWORD_MISMATCH" }, - { -(MBEDTLS_ERR_PEM_FEATURE_UNAVAILABLE), "PEM_FEATURE_UNAVAILABLE" }, - { -(MBEDTLS_ERR_PEM_BAD_INPUT_DATA), "PEM_BAD_INPUT_DATA" }, -#endif /* MBEDTLS_PEM_PARSE_C || MBEDTLS_PEM_WRITE_C */ - -#if defined(MBEDTLS_PK_C) - { -(MBEDTLS_ERR_PK_ALLOC_FAILED), "PK_ALLOC_FAILED" }, - { -(MBEDTLS_ERR_PK_TYPE_MISMATCH), "PK_TYPE_MISMATCH" }, - { -(MBEDTLS_ERR_PK_BAD_INPUT_DATA), "PK_BAD_INPUT_DATA" }, - { -(MBEDTLS_ERR_PK_FILE_IO_ERROR), "PK_FILE_IO_ERROR" }, - { -(MBEDTLS_ERR_PK_KEY_INVALID_VERSION), "PK_KEY_INVALID_VERSION" }, - { -(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT), "PK_KEY_INVALID_FORMAT" }, - { -(MBEDTLS_ERR_PK_UNKNOWN_PK_ALG), "PK_UNKNOWN_PK_ALG" }, - { -(MBEDTLS_ERR_PK_PASSWORD_REQUIRED), "PK_PASSWORD_REQUIRED" }, - { -(MBEDTLS_ERR_PK_PASSWORD_MISMATCH), "PK_PASSWORD_MISMATCH" }, - { -(MBEDTLS_ERR_PK_INVALID_PUBKEY), "PK_INVALID_PUBKEY" }, - { -(MBEDTLS_ERR_PK_INVALID_ALG), "PK_INVALID_ALG" }, - { -(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" }, -#endif /* MBEDTLS_PK_C */ - -#if defined(MBEDTLS_PKCS12_C) - { -(MBEDTLS_ERR_PKCS12_BAD_INPUT_DATA), "PKCS12_BAD_INPUT_DATA" }, - { -(MBEDTLS_ERR_PKCS12_FEATURE_UNAVAILABLE), "PKCS12_FEATURE_UNAVAILABLE" }, - { -(MBEDTLS_ERR_PKCS12_PBE_INVALID_FORMAT), "PKCS12_PBE_INVALID_FORMAT" }, - { -(MBEDTLS_ERR_PKCS12_PASSWORD_MISMATCH), "PKCS12_PASSWORD_MISMATCH" }, -#endif /* MBEDTLS_PKCS12_C */ - -#if defined(MBEDTLS_PKCS5_C) - { -(MBEDTLS_ERR_PKCS5_BAD_INPUT_DATA), "PKCS5_BAD_INPUT_DATA" }, - { -(MBEDTLS_ERR_PKCS5_INVALID_FORMAT), "PKCS5_INVALID_FORMAT" }, - { -(MBEDTLS_ERR_PKCS5_FEATURE_UNAVAILABLE), "PKCS5_FEATURE_UNAVAILABLE" }, - { -(MBEDTLS_ERR_PKCS5_PASSWORD_MISMATCH), "PKCS5_PASSWORD_MISMATCH" }, -#endif /* MBEDTLS_PKCS5_C */ - -#if defined(MBEDTLS_RSA_C) - { -(MBEDTLS_ERR_RSA_BAD_INPUT_DATA), "RSA_BAD_INPUT_DATA" }, - { -(MBEDTLS_ERR_RSA_INVALID_PADDING), "RSA_INVALID_PADDING" }, - { -(MBEDTLS_ERR_RSA_KEY_GEN_FAILED), "RSA_KEY_GEN_FAILED" }, - { -(MBEDTLS_ERR_RSA_KEY_CHECK_FAILED), "RSA_KEY_CHECK_FAILED" }, - { -(MBEDTLS_ERR_RSA_PUBLIC_FAILED), "RSA_PUBLIC_FAILED" }, - { -(MBEDTLS_ERR_RSA_PRIVATE_FAILED), "RSA_PRIVATE_FAILED" }, - { -(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_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_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_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_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_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_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" }, - { -(MBEDTLS_ERR_SSL_INTERNAL_ERROR), "SSL_INTERNAL_ERROR" }, - { -(MBEDTLS_ERR_SSL_COUNTER_WRAPPING), "SSL_COUNTER_WRAPPING" }, - { -(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_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" }, -#endif /* MBEDTLS_SSL_TLS_C */ - -#if defined(MBEDTLS_X509_USE_C) || defined(MBEDTLS_X509_CREATE_C) - { -(MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE), "X509_FEATURE_UNAVAILABLE" }, - { -(MBEDTLS_ERR_X509_UNKNOWN_OID), "X509_UNKNOWN_OID" }, - { -(MBEDTLS_ERR_X509_INVALID_FORMAT), "X509_INVALID_FORMAT" }, - { -(MBEDTLS_ERR_X509_INVALID_VERSION), "X509_INVALID_VERSION" }, - { -(MBEDTLS_ERR_X509_INVALID_SERIAL), "X509_INVALID_SERIAL" }, - { -(MBEDTLS_ERR_X509_INVALID_ALG), "X509_INVALID_ALG" }, - { -(MBEDTLS_ERR_X509_INVALID_NAME), "X509_INVALID_NAME" }, - { -(MBEDTLS_ERR_X509_INVALID_DATE), "X509_INVALID_DATE" }, - { -(MBEDTLS_ERR_X509_INVALID_SIGNATURE), "X509_INVALID_SIGNATURE" }, - { -(MBEDTLS_ERR_X509_INVALID_EXTENSIONS), "X509_INVALID_EXTENSIONS" }, - { -(MBEDTLS_ERR_X509_UNKNOWN_VERSION), "X509_UNKNOWN_VERSION" }, - { -(MBEDTLS_ERR_X509_UNKNOWN_SIG_ALG), "X509_UNKNOWN_SIG_ALG" }, - { -(MBEDTLS_ERR_X509_SIG_MISMATCH), "X509_SIG_MISMATCH" }, - { -(MBEDTLS_ERR_X509_CERT_VERIFY_FAILED), "X509_CERT_VERIFY_FAILED" }, - { -(MBEDTLS_ERR_X509_CERT_UNKNOWN_FORMAT), "X509_CERT_UNKNOWN_FORMAT" }, - { -(MBEDTLS_ERR_X509_BAD_INPUT_DATA), "X509_BAD_INPUT_DATA" }, - { -(MBEDTLS_ERR_X509_ALLOC_FAILED), "X509_ALLOC_FAILED" }, - { -(MBEDTLS_ERR_X509_FILE_IO_ERROR), "X509_FILE_IO_ERROR" }, - { -(MBEDTLS_ERR_X509_BUFFER_TOO_SMALL), "X509_BUFFER_TOO_SMALL" }, - { -(MBEDTLS_ERR_X509_FATAL_ERROR), "X509_FATAL_ERROR" }, -#endif /* MBEDTLS_X509_USE_C || MBEDTLS_X509_CREATE_C */ -// END generated code -}; - -static const struct ssl_errs mbedtls_low_level_error_tab[] = { -// Low level error codes -// -// BEGIN generated code -#if defined(MBEDTLS_AES_C) - { -(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) - { -(MBEDTLS_ERR_ASN1_OUT_OF_DATA), "ASN1_OUT_OF_DATA" }, - { -(MBEDTLS_ERR_ASN1_UNEXPECTED_TAG), "ASN1_UNEXPECTED_TAG" }, - { -(MBEDTLS_ERR_ASN1_INVALID_LENGTH), "ASN1_INVALID_LENGTH" }, - { -(MBEDTLS_ERR_ASN1_LENGTH_MISMATCH), "ASN1_LENGTH_MISMATCH" }, - { -(MBEDTLS_ERR_ASN1_INVALID_DATA), "ASN1_INVALID_DATA" }, - { -(MBEDTLS_ERR_ASN1_ALLOC_FAILED), "ASN1_ALLOC_FAILED" }, - { -(MBEDTLS_ERR_ASN1_BUF_TOO_SMALL), "ASN1_BUF_TOO_SMALL" }, -#endif /* MBEDTLS_ASN1_PARSE_C */ - -#if defined(MBEDTLS_BASE64_C) - { -(MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL), "BASE64_BUFFER_TOO_SMALL" }, - { -(MBEDTLS_ERR_BASE64_INVALID_CHARACTER), "BASE64_INVALID_CHARACTER" }, -#endif /* MBEDTLS_BASE64_C */ - -#if defined(MBEDTLS_BIGNUM_C) - { -(MBEDTLS_ERR_MPI_FILE_IO_ERROR), "MPI_FILE_IO_ERROR" }, - { -(MBEDTLS_ERR_MPI_BAD_INPUT_DATA), "MPI_BAD_INPUT_DATA" }, - { -(MBEDTLS_ERR_MPI_INVALID_CHARACTER), "MPI_INVALID_CHARACTER" }, - { -(MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL), "MPI_BUFFER_TOO_SMALL" }, - { -(MBEDTLS_ERR_MPI_NEGATIVE_VALUE), "MPI_NEGATIVE_VALUE" }, - { -(MBEDTLS_ERR_MPI_DIVISION_BY_ZERO), "MPI_DIVISION_BY_ZERO" }, - { -(MBEDTLS_ERR_MPI_NOT_ACCEPTABLE), "MPI_NOT_ACCEPTABLE" }, - { -(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) - { -(MBEDTLS_ERR_CHACHAPOLY_BAD_STATE), "CHACHAPOLY_BAD_STATE" }, - { -(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" }, - { -(MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG), "CTR_DRBG_INPUT_TOO_BIG" }, - { -(MBEDTLS_ERR_CTR_DRBG_FILE_IO_ERROR), "CTR_DRBG_FILE_IO_ERROR" }, -#endif /* MBEDTLS_CTR_DRBG_C */ - -#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) - { -(MBEDTLS_ERR_ENTROPY_SOURCE_FAILED), "ENTROPY_SOURCE_FAILED" }, - { -(MBEDTLS_ERR_ENTROPY_MAX_SOURCES), "ENTROPY_MAX_SOURCES" }, - { -(MBEDTLS_ERR_ENTROPY_NO_SOURCES_DEFINED), "ENTROPY_NO_SOURCES_DEFINED" }, - { -(MBEDTLS_ERR_ENTROPY_NO_STRONG_SOURCE), "ENTROPY_NO_STRONG_SOURCE" }, - { -(MBEDTLS_ERR_ENTROPY_FILE_IO_ERROR), "ENTROPY_FILE_IO_ERROR" }, -#endif /* MBEDTLS_ENTROPY_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" }, -#endif /* MBEDTLS_GCM_C */ - -#if defined(MBEDTLS_HKDF_C) - { -(MBEDTLS_ERR_HKDF_BAD_INPUT_DATA), "HKDF_BAD_INPUT_DATA" }, -#endif /* MBEDTLS_HKDF_C */ - -#if defined(MBEDTLS_HMAC_DRBG_C) - { -(MBEDTLS_ERR_HMAC_DRBG_REQUEST_TOO_BIG), "HMAC_DRBG_REQUEST_TOO_BIG" }, - { -(MBEDTLS_ERR_HMAC_DRBG_INPUT_TOO_BIG), "HMAC_DRBG_INPUT_TOO_BIG" }, - { -(MBEDTLS_ERR_HMAC_DRBG_FILE_IO_ERROR), "HMAC_DRBG_FILE_IO_ERROR" }, - { -(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_NET_C) - { -(MBEDTLS_ERR_NET_SOCKET_FAILED), "NET_SOCKET_FAILED" }, - { -(MBEDTLS_ERR_NET_CONNECT_FAILED), "NET_CONNECT_FAILED" }, - { -(MBEDTLS_ERR_NET_BIND_FAILED), "NET_BIND_FAILED" }, - { -(MBEDTLS_ERR_NET_LISTEN_FAILED), "NET_LISTEN_FAILED" }, - { -(MBEDTLS_ERR_NET_ACCEPT_FAILED), "NET_ACCEPT_FAILED" }, - { -(MBEDTLS_ERR_NET_RECV_FAILED), "NET_RECV_FAILED" }, - { -(MBEDTLS_ERR_NET_SEND_FAILED), "NET_SEND_FAILED" }, - { -(MBEDTLS_ERR_NET_CONN_RESET), "NET_CONN_RESET" }, - { -(MBEDTLS_ERR_NET_UNKNOWN_HOST), "NET_UNKNOWN_HOST" }, - { -(MBEDTLS_ERR_NET_BUFFER_TOO_SMALL), "NET_BUFFER_TOO_SMALL" }, - { -(MBEDTLS_ERR_NET_INVALID_CONTEXT), "NET_INVALID_CONTEXT" }, - { -(MBEDTLS_ERR_NET_POLL_FAILED), "NET_POLL_FAILED" }, - { -(MBEDTLS_ERR_NET_BAD_INPUT_DATA), "NET_BAD_INPUT_DATA" }, -#endif /* MBEDTLS_NET_C */ - -#if defined(MBEDTLS_OID_C) - { -(MBEDTLS_ERR_OID_NOT_FOUND), "OID_NOT_FOUND" }, - { -(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 -}; - -static const char *mbedtls_err_prefix = "MBEDTLS_ERR_"; -#define MBEDTLS_ERR_PREFIX_LEN ( sizeof("MBEDTLS_ERR_")-1 ) - -// copy error text into buffer, ensure null termination, return strlen of result -static size_t mbedtls_err_to_str(int err, const struct ssl_errs tab[], int tab_len, char *buf, size_t buflen) { - if (buflen == 0) return 0; - - // prefix for all error names - strncpy(buf, mbedtls_err_prefix, buflen); - if (buflen <= MBEDTLS_ERR_PREFIX_LEN+1) { - buf[buflen-1] = 0; - return buflen-1; - } - - // append error name from table - for (int i = 0; i < tab_len; i++) { - if (tab[i].errnum == err) { - strncpy(buf+MBEDTLS_ERR_PREFIX_LEN, tab[i].errstr, buflen-MBEDTLS_ERR_PREFIX_LEN); - buf[buflen-1] = 0; - return strlen(buf); - } - } - - mbedtls_snprintf(buf+MBEDTLS_ERR_PREFIX_LEN, buflen-MBEDTLS_ERR_PREFIX_LEN, "UNKNOWN (0x%04X)", - err); - return strlen(buf); -} - -#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) - -void mbedtls_strerror(int ret, char *buf, size_t buflen) { - int use_ret; - - if (buflen == 0) return; - - buf[buflen-1] = 0; - - if (ret < 0) ret = -ret; - - // - // High-level error codes - // - uint8_t got_hl = (ret & 0xFF80) != 0; - if (got_hl) { - use_ret = ret & 0xFF80; - - // special case -#if defined(MBEDTLS_SSL_TLS_C) - if (use_ret == -(MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE)) { - strncpy(buf, "MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE", buflen); - buf[buflen-1] = 0; - return; - } -#endif - - size_t len = mbedtls_err_to_str(use_ret, mbedtls_high_level_error_tab, - ARRAY_SIZE(mbedtls_high_level_error_tab), buf, buflen); - - buf += len; - buflen -= len; - if (buflen == 0) return; - } - - // - // Low-level error codes - // - use_ret = ret & ~0xFF80; - - if (use_ret == 0) return; - - // If high level code is present, make a concatenation between both error strings. - if (got_hl) { - if (buflen < 2) return; - *buf++ = '+'; - buflen--; - } - - mbedtls_err_to_str(use_ret, mbedtls_low_level_error_tab, - ARRAY_SIZE(mbedtls_low_level_error_tab), buf, buflen); -} - -#else /* MBEDTLS_ERROR_C */ - -#if defined(MBEDTLS_ERROR_STRERROR_DUMMY) - -/* - * Provide an non-function in case MBEDTLS_ERROR_C is not defined - */ -void mbedtls_strerror( int ret, char *buf, size_t buflen ) -{ - ((void) ret); - - if( buflen > 0 ) - buf[0] = '\0'; -} - -#endif /* MBEDTLS_ERROR_STRERROR_DUMMY */ - -#endif /* MBEDTLS_ERROR_C */ diff --git a/lib/mbedtls_errors/tester.c b/lib/mbedtls_errors/tester.c deleted file mode 100644 index 6f1c788f50..0000000000 --- a/lib/mbedtls_errors/tester.c +++ /dev/null @@ -1,58 +0,0 @@ -#include "mbedtls/error.h" -#include -#include - -// test_code checks that the provided code results in the provided error string for any size -// buffer. It calls mbedtls_strerror() to fill a buffer that is from 1 to 100 bytes in length -// and then checks that the buffer contents is OK and that a few guard bytes before and after -// the buffer were not overwritten. -int test_code(int code, char *str) { - char buf[100]; - int ok = 1; - int res; - - // test zero-length buffer - memset(buf, -3, 100); - mbedtls_strerror(code, buf + 4, 0); - for (int i = 0; i < 10; i++) { - if (buf[i] != -3) { - printf("Error: guard overwritten buflen=0 i=%d buf[i]=%d\n", i, buf[i]); - ok = 0; - } - } - - // test - for (size_t buflen = 1; buflen < 90; buflen++) { - memset(buf, -3, 100); - mbedtls_strerror(code, buf + 4, buflen); - for (int i = 0; i < 4; i++) { - if (buf[i] != -3) { - printf("Error: pre-guard overwritten buflen=%d i=%d buf[i]=%d\n", buflen, i, buf[i]); - ok = 0; - } - } - for (int i = 4 + buflen; i < 100; i++) { - if (buf[i] != -3) { - printf("Error: post-guard overwritten buflen=%d i=%d buf[i]=%d\n", buflen, i, buf[i]); - ok = 0; - } - } - char exp[100]; - strncpy(exp, str, buflen); - exp[buflen - 1] = 0; - if (strcmp(buf + 4, exp) != 0) { - printf("Error: expected %s, got %s\n", exp, buf); - ok = 0; - } - } - - printf("Test %x -> %s is %s\n", code, str, ok?"OK":"*** BAD ***"); -} - -int main() { - test_code(0x7200, "MBEDTLS_ERR_SSL_INVALID_RECORD"); - test_code(0x7780, "MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE"); - test_code(0x0074, "MBEDTLS_ERR_SHA256_BAD_INPUT_DATA"); - test_code(0x6600 | 0x0074, "MBEDTLS_ERR_SSL_INVALID_VERIFY_HASH+MBEDTLS_ERR_SHA256_BAD_INPUT_DATA"); - test_code(103, "MBEDTLS_ERR_UNKNOWN (0x0067)"); -} diff --git a/lib/re1.5/charclass.c b/lib/re1.5/charclass.c index 7f6388c93d..2553b40530 100644 --- a/lib/re1.5/charclass.c +++ b/lib/re1.5/charclass.c @@ -6,7 +6,15 @@ int _re1_5_classmatch(const char *pc, const char *sp) int is_positive = (pc[-1] == Class); int cnt = *pc++; while (cnt--) { - if (*sp >= *pc && *sp <= pc[1]) return is_positive; + if (*pc == RE15_CLASS_NAMED_CLASS_INDICATOR) { + if (_re1_5_namedclassmatch(pc + 1, sp)) { + return is_positive; + } + } else { + if (*sp >= *pc && *sp <= pc[1]) { + return is_positive; + } + } pc += 2; } return !is_positive; diff --git a/lib/re1.5/compilecode.c b/lib/re1.5/compilecode.c index 936f6ed28a..7eda55e201 100644 --- a/lib/re1.5/compilecode.c +++ b/lib/re1.5/compilecode.c @@ -4,6 +4,9 @@ #include "re1.5.h" +// Matches: DSWdsw +#define MATCH_NAMED_CLASS_CHAR(c) (((c) | 0x20) == 'd' || ((c) | 0x24) == 'w') + #define INSERT_CODE(at, num, pc) \ ((code ? memmove(code + at + num, code + at, pc - at) : 0), pc += num) #define REL(at, to) (to - at - 2) @@ -11,28 +14,6 @@ #define EMIT_CHECKED(at, byte) (_emit_checked(at, code, byte, &err)) #define PC (prog->bytelen) -static char unescape(char c) { - switch (c) { - case 'a': - return '\a'; - case 'b': - return '\b'; - case 'f': - return '\f'; - case 'n': - return '\n'; - case 'r': - return '\r'; - case 't': - return '\t'; - case 'v': - return '\v'; - default: - return c; - } -} - - static void _emit_checked(int at, char *code, int val, bool *err) { *err |= val != (int8_t)val; if (code) { @@ -53,16 +34,14 @@ static const char *_compilecode(const char *re, ByteProg *prog, int sizecode) case '\\': re++; if (!*re) return NULL; // Trailing backslash - term = PC; - if ((*re | 0x20) == 'd' || (*re | 0x20) == 's' || (*re | 0x20) == 'w') { + if (MATCH_NAMED_CLASS_CHAR(*re)) { + term = PC; EMIT(PC++, NamedClass); EMIT(PC++, *re); - } else { - EMIT(PC++, Char); - EMIT(PC++, unescape(*re)); + prog->len++; + break; } - prog->len++; - break; + MP_FALLTHROUGH default: term = PC; EMIT(PC++, Char); @@ -87,27 +66,25 @@ static const char *_compilecode(const char *re, ByteProg *prog, int sizecode) PC++; // Skip # of pair byte prog->len++; for (cnt = 0; *re != ']'; re++, cnt++) { - if (!*re) return NULL; - const char *b = re; - if (*re == '\\') { - re += 1; - if (!*re) return NULL; // Trailing backslash - EMIT(PC++, unescape(*re)); - } else { - EMIT(PC++, *re); + char c = *re; + if (c == '\\') { + ++re; + c = *re; + if (MATCH_NAMED_CLASS_CHAR(c)) { + c = RE15_CLASS_NAMED_CLASS_INDICATOR; + goto emit_char_pair; + else { + // CIRCUITPY TODO: handle unescape here again PR #1544 + } } + if (!c) return NULL; if (re[1] == '-' && re[2] != ']') { re += 2; - } else { - re = b; - } - if (*re == '\\') { - re += 1; - if (!*re) return NULL; // Trailing backslash - EMIT(PC++, unescape(*re)); - } else { - EMIT(PC++, *re); } + emit_char_pair: + EMIT(PC++, c); + EMIT(PC++, *re); + // CIRCUITPY TODO handle unescape here again PR #1544 } EMIT_CHECKED(term + 1, cnt); break; @@ -255,6 +232,7 @@ int re1_5_compilecode(ByteProg *prog, const char *re) return 0; } +// CIRCUITPY debug as main program #if defined(DEBUG_COMPILECODE) #include void re1_5_fatal(char *x) { diff --git a/lib/re1.5/re1.5.h b/lib/re1.5/re1.5.h index 43a0416160..68ec27e389 100644 --- a/lib/re1.5/re1.5.h +++ b/lib/re1.5/re1.5.h @@ -140,6 +140,7 @@ struct Subject { #define NON_ANCHORED_PREFIX 5 #define HANDLE_ANCHORED(bytecode, is_anchored) ((is_anchored) ? (bytecode) + NON_ANCHORED_PREFIX : (bytecode)) +#define RE15_CLASS_NAMED_CLASS_INDICATOR 0 int re1_5_backtrack(ByteProg*, Subject*, const char**, int, int); int re1_5_pikevm(ByteProg*, Subject*, const char**, int, int); diff --git a/main.c b/main.c index 94a3feac6b..eb2adef6de 100644 --- a/main.c +++ b/main.c @@ -58,7 +58,6 @@ #include "supervisor/shared/status_leds.h" #include "supervisor/shared/tick.h" #include "supervisor/shared/traceback.h" -#include "supervisor/shared/translate/translate.h" #include "supervisor/shared/workflow.h" #include "supervisor/usb.h" #include "supervisor/workflow.h" diff --git a/mpy-cross/.gitignore b/mpy-cross/.gitignore deleted file mode 100644 index 6daeea5040..0000000000 --- a/mpy-cross/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -# SPDX-FileCopyrightText: 2014 MicroPython & CircuitPython contributors (https://github.com/adafruit/circuitpython/graphs/contributors) -# -# SPDX-License-Identifier: MIT - -/build-* -/mpy-cross -/mpy-cross.static -/mpy-cross.static.exe -/mpy-cross.static-raspbian -/mpy-cross.fuzz -/pitools diff --git a/mpy-cross/Makefile b/mpy-cross/Makefile index 4858cb25c7..7a71577e2b 100644 --- a/mpy-cross/Makefile +++ b/mpy-cross/Makefile @@ -1,24 +1,62 @@ -# SPDX-FileCopyrightText: 2014 MicroPython & CircuitPython contributors (https://github.com/adafruit/circuitpython/graphs/contributors) -# -# SPDX-License-Identifier: MIT +include ../py/mkenv.mk -# The following is a temporary hack to forefully undefine vars that might have -# be defined by a calling Makefile (from recursive make). -# TODO: Find a better way to be able to call this Makefile recursively. -ifneq ($(findstring undefine,$(.FEATURES)),) -override undefine COPT -override undefine CFLAGS_EXTRA -override undefine LDFLAGS_EXTRA -override undefine MICROPY_FORCE_32BIT -override undefine CROSS_COMPILE -override undefine FROZEN_DIR -override undefine FROZEN_MPY_DIR -override undefine USER_C_MODULES -override undefine SRC_MOD -override undefine BUILD -override undefine PROG +# define main target +PROG = mpy-cross + +# qstr definitions (must come before including py.mk) +QSTR_DEFS = qstrdefsport.h + +# OS name, for simple autoconfig +UNAME_S := $(shell uname -s) + +# include py core make definitions +include $(TOP)/py/py.mk + +INC += -I. +INC += -I$(BUILD) +INC += -I$(TOP) + +# compiler settings +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 + +# Debugging/Optimization +ifdef DEBUG +CFLAGS += -g +COPT = -O0 +else +COPT = -Os #-DNDEBUG endif -include mpy-cross.mk -CFLAGS += -g -STRIP = : +# On OSX, 'gcc' is a symlink to clang unless a real gcc is installed. +# The unix port of MicroPython on OSX must be compiled with clang, +# while cross-compile ports require gcc, so we test here for OSX and +# if necessary override the value of 'CC' set in py/mkenv.mk +ifeq ($(UNAME_S),Darwin) +CC = clang +# Use clang syntax for map file +LDFLAGS_ARCH = -Wl,-map,$@.map -Wl,-dead_strip +else +# Use gcc syntax for map file +LDFLAGS_ARCH = -Wl,-Map=$@.map,--cref -Wl,--gc-sections +endif +LDFLAGS += $(LDFLAGS_MOD) $(LDFLAGS_ARCH) -lm $(LDFLAGS_EXTRA) + +# source files +SRC_C = \ + main.c \ + gccollect.c \ + shared/runtime/gchelper_generic.c \ + +# Add fmode when compiling with mingw gcc +COMPILER_TARGET := $(shell $(CC) -dumpmachine) +ifneq (,$(findstring mingw,$(COMPILER_TARGET))) + SRC_C += ports/windows/fmode.c +endif + +OBJ = $(PY_CORE_O) +OBJ += $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) + +include $(TOP)/py/mkrules.mk diff --git a/mpy-cross/main.c b/mpy-cross/main.c index 88d84915f8..67e73e0eba 100644 --- a/mpy-cross/main.c +++ b/mpy-cross/main.c @@ -73,8 +73,9 @@ STATIC int compile_and_save(const char *file, const char *output_file, const cha #endif mp_parse_tree_t parse_tree = mp_parse(lex, MP_PARSE_FILE_INPUT); - mp_module_context_t *ctx = m_new_obj(mp_module_context_t); - mp_compiled_module_t cm = mp_compile_to_raw_code(&parse_tree, source_name, false, ctx); + mp_compiled_module_t cm; + cm.context = m_new_obj(mp_module_context_t); + mp_compile_to_raw_code(&parse_tree, source_name, false, &cm); vstr_t vstr; vstr_init(&vstr, 16); @@ -181,6 +182,15 @@ STATIC void pre_process_options(int argc, char **argv) { } } +STATIC char *backslash_to_forwardslash(char *path) { + for (char *p = path; p != NULL && *p != '\0'; ++p) { + if (*p == '\\') { + *p = '/'; + } + } + return path; +} + MP_NOINLINE int main_(int argc, char **argv) { mp_stack_set_limit(40000 * (sizeof(void *) / 4)); @@ -203,19 +213,9 @@ MP_NOINLINE int main_(int argc, char **argv) { // set default compiler configuration mp_dynamic_compiler.small_int_bits = 31; - #if defined(__i386__) - mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_X86; - mp_dynamic_compiler.nlr_buf_num_regs = MICROPY_NLR_NUM_REGS_X86; - #elif defined(__x86_64__) - mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_X64; - mp_dynamic_compiler.nlr_buf_num_regs = MAX(MICROPY_NLR_NUM_REGS_X64, MICROPY_NLR_NUM_REGS_X64_WIN); - #elif defined(__arm__) && !defined(__thumb2__) - mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_ARMV6; - mp_dynamic_compiler.nlr_buf_num_regs = MICROPY_NLR_NUM_REGS_ARM_THUMB_FP; - #else + // don't support native emitter unless -march is specified mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_NONE; mp_dynamic_compiler.nlr_buf_num_regs = 0; - #endif const char *input_file = NULL; const char *output_file = NULL; @@ -228,7 +228,7 @@ MP_NOINLINE int main_(int argc, char **argv) { a += 1; } else if (strcmp(argv[a], "--version") == 0) { printf("CircuitPython " MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE - "; mpy-cross emitting mpy v" MP_STRINGIFY(MPY_VERSION) "-CircuitPython\n"); + "; mpy-cross emitting mpy v" MP_STRINGIFY(MPY_VERSION) "." MP_STRINGIFY(MPY_SUB_VERSION) "\n"); return 0; } else if (strcmp(argv[a], "-v") == 0) { mp_verbose_flag++; @@ -251,7 +251,7 @@ MP_NOINLINE int main_(int argc, char **argv) { exit(usage(argv)); } a += 1; - source_file = argv[a]; + source_file = backslash_to_forwardslash(argv[a]); } else if (strncmp(argv[a], "-msmall-int-bits=", sizeof("-msmall-int-bits=") - 1) == 0) { char *end; mp_dynamic_compiler.small_int_bits = @@ -292,6 +292,20 @@ MP_NOINLINE int main_(int argc, char **argv) { } else if (strcmp(arch, "xtensawin") == 0) { mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_XTENSAWIN; mp_dynamic_compiler.nlr_buf_num_regs = MICROPY_NLR_NUM_REGS_XTENSAWIN; + } else if (strcmp(arch, "host") == 0) { + #if defined(__i386__) || defined(_M_IX86) + mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_X86; + mp_dynamic_compiler.nlr_buf_num_regs = MICROPY_NLR_NUM_REGS_X86; + #elif defined(__x86_64__) || defined(_M_X64) + mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_X64; + mp_dynamic_compiler.nlr_buf_num_regs = MAX(MICROPY_NLR_NUM_REGS_X64, MICROPY_NLR_NUM_REGS_X64_WIN); + #elif defined(__arm__) && !defined(__thumb2__) + mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_ARMV6; + mp_dynamic_compiler.nlr_buf_num_regs = MICROPY_NLR_NUM_REGS_ARM_THUMB_FP; + #else + mp_printf(&mp_stderr_print, "unable to determine host architecture for -march=host\n"); + exit(1); + #endif } else { return usage(argv); } @@ -303,7 +317,7 @@ MP_NOINLINE int main_(int argc, char **argv) { mp_printf(&mp_stderr_print, "multiple input files\n"); exit(1); } - input_file = argv[a]; + input_file = backslash_to_forwardslash(argv[a]); } } @@ -339,7 +353,3 @@ void nlr_jump_fail(void *val) { fprintf(stderr, "FATAL: uncaught NLR %p\n", val); exit(1); } - -void serial_write(const char *text) { - printf("%s", text); -} diff --git a/mpy-cross/mpconfigport.h b/mpy-cross/mpconfigport.h index 099c372fdd..d835e81b41 100644 --- a/mpy-cross/mpconfigport.h +++ b/mpy-cross/mpconfigport.h @@ -166,7 +166,7 @@ typedef long mp_off_t; #define MP_PLAT_PRINT_STRN(str, len) (void)0 // We need to provide a declaration/definition of alloca() -#ifdef __FreeBSD__ +#if defined(__FreeBSD__) || defined(__NetBSD__) #include #elif defined(_WIN32) #include @@ -182,6 +182,7 @@ typedef long mp_off_t; #define MP_ENDIANNESS_LITTLE (1) #define NORETURN __declspec(noreturn) #define MP_NOINLINE __declspec(noinline) +#define MP_ALWAYSINLINE __forceinline #define MP_LIKELY(x) (x) #define MP_UNLIKELY(x) (x) #define MICROPY_PORT_CONSTANTS { MP_ROM_QSTR(MP_QSTR_dummy), MP_ROM_PTR(NULL) } diff --git a/mpy-cross/mpy-cross.vcxproj b/mpy-cross/mpy-cross.vcxproj index 53cb0fa1fe..322d8c25d1 100644 --- a/mpy-cross/mpy-cross.vcxproj +++ b/mpy-cross/mpy-cross.vcxproj @@ -24,7 +24,6 @@ True $(MSBuildThisFileDirectory)build\ $(MSBuildThisFileDirectory) - $(MSBuildThisFileDirectory) $(MSBuildThisFileDirectory)..\ports\windows\msvc\ diff --git a/mpy-cross/mpy_cross/__init__.py b/mpy-cross/mpy_cross/__init__.py new file mode 100644 index 0000000000..8eadbc8352 --- /dev/null +++ b/mpy-cross/mpy_cross/__init__.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python3 +# +# This file is part of the MicroPython project, http://micropython.org/ +# +# The MIT License (MIT) +# +# Copyright (c) 2022 Andrew Leech +# Copyright (c) 2022 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. + +from __future__ import print_function +import os +import re +import stat +import subprocess + +NATIVE_ARCHS = { + "NATIVE_ARCH_NONE": "", + "NATIVE_ARCH_X86": "x86", + "NATIVE_ARCH_X64": "x64", + "NATIVE_ARCH_ARMV6": "armv6", + "NATIVE_ARCH_ARMV6M": "armv6m", + "NATIVE_ARCH_ARMV7M": "armv7m", + "NATIVE_ARCH_ARMV7EM": "armv7em", + "NATIVE_ARCH_ARMV7EMSP": "armv7emsp", + "NATIVE_ARCH_ARMV7EMDP": "armv7emdp", + "NATIVE_ARCH_XTENSA": "xtensa", + "NATIVE_ARCH_XTENSAWIN": "xtensawin", +} + +globals().update(NATIVE_ARCHS) + +__all__ = ["version", "compile", "run", "CrossCompileError"] + list(NATIVE_ARCHS.keys()) + + +class CrossCompileError(Exception): + pass + + +_VERSION_RE = re.compile("mpy-cross emitting mpy v([0-9]+)(?:.([0-9]+))?") + + +def _find_mpy_cross_binary(mpy_cross): + if mpy_cross: + return mpy_cross + return os.path.abspath(os.path.join(os.path.dirname(__file__), "../build/mpy-cross")) + + +def mpy_version(mpy_cross=None): + """ + Get the version and sub-version of the .mpy file format generated by this version of mpy-cross. + + Returns: A tuple of `(mpy_version, mpy_sub_version)` + Optional keyword arguments: + - mpy_cross: Specific mpy-cross binary to use + """ + version_info = run(["--version"], mpy_cross=mpy_cross) + match = re.search(_VERSION_RE, version_info) + mpy_version, mpy_sub_version = int(match.group(1)), int(match.group(2) or "0") + return ( + mpy_version, + mpy_sub_version, + ) + + +def compile(src, dest=None, src_path=None, opt=None, march=None, mpy_cross=None, extra_args=None): + """ + Compile the specified .py file with mpy-cross. + + Returns: Standard output from mpy-cross as a string. + + Required arguments: + - src: The path to the .py file + + Optional keyword arguments: + - dest: The output .mpy file. Defaults to `src` (with .mpy extension) + - src_path: The path to embed in the .mpy file (defaults to `src`) + - opt: Optimisation level (0-3, default 0) + - march: One of the `NATIVE_ARCH_*` constants (defaults to NATIVE_ARCH_NONE) + - mpy_cross: Specific mpy-cross binary to use + - extra_args: Additional arguments to pass to mpy-cross (e.g. `["-X", "emit=native"]`) + """ + if not src: + raise ValueError("src is required") + if not os.path.exists(src): + raise CrossCompileError("Input .py file not found: {}.".format(src_py)) + + args = [] + + if src_path: + args += ["-s", src_path] + + if dest: + args += ["-o", dest] + + if march: + args += ["-march=" + march] + + if opt is not None: + args += ["-O{}".format(opt)] + + if extra_args: + args += extra_args + + args += [src] + + run(args, mpy_cross) + + +def run(args, mpy_cross=None): + """ + Run mpy-cross with the specified command line arguments. + Prefer to use `compile()` instead. + + Returns: Standard output from mpy-cross as a string. + + Optional keyword arguments: + - mpy_cross: Specific mpy-cross binary to use + """ + mpy_cross = _find_mpy_cross_binary(mpy_cross) + + if not os.path.exists(mpy_cross): + raise CrossCompileError("mpy-cross binary not found at {}.".format(mpy_cross)) + + try: + st = os.stat(mpy_cross) + os.chmod(mpy_cross, st.st_mode | stat.S_IEXEC) + except OSError: + pass + + try: + return subprocess.check_output([mpy_cross] + args, stderr=subprocess.STDOUT).decode() + except subprocess.CalledProcessError as er: + raise CrossCompileError(er.output.decode()) diff --git a/mpy-cross/mpy_cross/__main__.py b/mpy-cross/mpy_cross/__main__.py new file mode 100644 index 0000000000..2b6b81c333 --- /dev/null +++ b/mpy-cross/mpy_cross/__main__.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# +# This file is part of the MicroPython project, http://micropython.org/ +# +# The MIT License (MIT) +# +# Copyright (c) 2022 Andrew Leech +# Copyright (c) 2022 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. + +from __future__ import print_function +import argparse +import sys + +from . import run, CrossCompileError + +try: + print(run(sys.argv[1:])) +except CrossCompileError as er: + print(er.args[0], file=sys.stderr) + raise SystemExit(1) diff --git a/ports/atmel-samd/common-hal/_pew/PewPew.c b/ports/atmel-samd/common-hal/_pew/PewPew.c index c4d94e3dbd..4d8d7ccb83 100644 --- a/ports/atmel-samd/common-hal/_pew/PewPew.c +++ b/ports/atmel-samd/common-hal/_pew/PewPew.c @@ -35,7 +35,6 @@ #include "shared-bindings/digitalio/DigitalInOut.h" #include "shared-bindings/util.h" #include "samd/timers.h" -#include "supervisor/shared/translate/translate.h" #include "timer_handler.h" diff --git a/ports/atmel-samd/common-hal/analogio/AnalogIn.c b/ports/atmel-samd/common-hal/analogio/AnalogIn.c index d15be8308a..f51f299bd3 100644 --- a/ports/atmel-samd/common-hal/analogio/AnalogIn.c +++ b/ports/atmel-samd/common-hal/analogio/AnalogIn.c @@ -37,7 +37,6 @@ #include "samd/adc.h" #include "shared-bindings/analogio/AnalogIn.h" #include "shared-bindings/microcontroller/Pin.h" -#include "supervisor/shared/translate/translate.h" #include "atmel_start_pins.h" #include "hal/include/hal_adc_sync.h" diff --git a/ports/atmel-samd/common-hal/analogio/AnalogOut.c b/ports/atmel-samd/common-hal/analogio/AnalogOut.c index ec690e8ddb..dbf9d5e8dd 100644 --- a/ports/atmel-samd/common-hal/analogio/AnalogOut.c +++ b/ports/atmel-samd/common-hal/analogio/AnalogOut.c @@ -33,7 +33,6 @@ #include "shared-bindings/analogio/AnalogOut.h" #include "shared-bindings/audioio/AudioOut.h" #include "shared-bindings/microcontroller/Pin.h" -#include "supervisor/shared/translate/translate.h" #include "atmel_start_pins.h" #include "hal/include/hal_dac_sync.h" diff --git a/ports/atmel-samd/common-hal/audiobusio/I2SOut.c b/ports/atmel-samd/common-hal/audiobusio/I2SOut.c index 447252aace..946c4062b6 100644 --- a/ports/atmel-samd/common-hal/audiobusio/I2SOut.c +++ b/ports/atmel-samd/common-hal/audiobusio/I2SOut.c @@ -40,7 +40,6 @@ #include "shared-bindings/audiobusio/I2SOut.h" #include "shared-bindings/audiocore/RawSample.h" #include "shared-bindings/microcontroller/Pin.h" -#include "supervisor/shared/translate/translate.h" #include "atmel_start_pins.h" #include "hal/include/hal_gpio.h" diff --git a/ports/atmel-samd/common-hal/audiobusio/PDMIn.c b/ports/atmel-samd/common-hal/audiobusio/PDMIn.c index 6796fe03f8..b3c6f9df5e 100644 --- a/ports/atmel-samd/common-hal/audiobusio/PDMIn.c +++ b/ports/atmel-samd/common-hal/audiobusio/PDMIn.c @@ -37,7 +37,6 @@ #include "shared-bindings/audiobusio/PDMIn.h" #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/microcontroller/Pin.h" -#include "supervisor/shared/translate/translate.h" #include "atmel_start_pins.h" #include "hal/include/hal_gpio.h" diff --git a/ports/atmel-samd/common-hal/audioio/AudioOut.c b/ports/atmel-samd/common-hal/audioio/AudioOut.c index 7007672b3f..52cf8d422b 100644 --- a/ports/atmel-samd/common-hal/audioio/AudioOut.c +++ b/ports/atmel-samd/common-hal/audioio/AudioOut.c @@ -35,7 +35,6 @@ #include "shared-bindings/audioio/AudioOut.h" #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/microcontroller/Pin.h" -#include "supervisor/shared/translate/translate.h" #include "atmel_start_pins.h" #include "hal/include/hal_gpio.h" diff --git a/ports/atmel-samd/common-hal/busio/I2C.c b/ports/atmel-samd/common-hal/busio/I2C.c index 902e696408..b1e981fb31 100644 --- a/ports/atmel-samd/common-hal/busio/I2C.c +++ b/ports/atmel-samd/common-hal/busio/I2C.c @@ -35,7 +35,6 @@ #include "samd/sercom.h" #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/microcontroller/Pin.h" -#include "supervisor/shared/translate/translate.h" #include "common-hal/busio/__init__.h" diff --git a/ports/atmel-samd/common-hal/busio/SPI.c b/ports/atmel-samd/common-hal/busio/SPI.c index 02776928ca..1bc5152b52 100644 --- a/ports/atmel-samd/common-hal/busio/SPI.c +++ b/ports/atmel-samd/common-hal/busio/SPI.c @@ -33,7 +33,6 @@ #include "peripheral_clk_config.h" #include "supervisor/board.h" -#include "supervisor/shared/translate/translate.h" #include "common-hal/busio/__init__.h" #include "hal/include/hal_gpio.h" diff --git a/ports/atmel-samd/common-hal/busio/SPI.h b/ports/atmel-samd/common-hal/busio/SPI.h index 2fced6d642..45a81e9651 100644 --- a/ports/atmel-samd/common-hal/busio/SPI.h +++ b/ports/atmel-samd/common-hal/busio/SPI.h @@ -3,7 +3,11 @@ * * The MIT License (MIT) * +<<<<<<<< HEAD:ports/atmel-samd/common-hal/busio/SPI.h * Copyright (c) 2016 Scott Shawcroft +======== + * Copyright (c) 2022 Blake W. Felt & Angus Gratton +>>>>>>>> v1.20.0:shared/tinyusb/mp_usbd.c * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,6 +28,7 @@ * THE SOFTWARE. */ +<<<<<<<< HEAD:ports/atmel-samd/common-hal/busio/SPI.h #ifndef MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_BUSIO_SPI_H #define MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_BUSIO_SPI_H @@ -43,3 +48,22 @@ typedef struct { } busio_spi_obj_t; #endif // MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_BUSIO_SPI_H +======== +#include + +#include "py/mpconfig.h" + +#if MICROPY_HW_ENABLE_USBDEV + +#ifndef NO_QSTR +#include "tusb.h" // TinyUSB is not avaiable when running the string preprocessor +#include "device/usbd.h" +#include "device/usbd_pvt.h" +#endif + +void usbd_task(void) { + tud_task_ext(0, false); +} + +#endif +>>>>>>>> v1.20.0:shared/tinyusb/mp_usbd.c diff --git a/ports/atmel-samd/common-hal/busio/UART.c b/ports/atmel-samd/common-hal/busio/UART.c index 36c7f020bc..424b16b75c 100644 --- a/ports/atmel-samd/common-hal/busio/UART.c +++ b/ports/atmel-samd/common-hal/busio/UART.c @@ -35,7 +35,6 @@ #include "py/mperrno.h" #include "py/runtime.h" #include "py/stream.h" -#include "supervisor/shared/translate/translate.h" #include "supervisor/shared/tick.h" #include "hpl_sercom_config.h" diff --git a/ports/atmel-samd/common-hal/countio/Counter.c b/ports/atmel-samd/common-hal/countio/Counter.c index e3694615ab..6b280c1a60 100644 --- a/ports/atmel-samd/common-hal/countio/Counter.c +++ b/ports/atmel-samd/common-hal/countio/Counter.c @@ -7,7 +7,6 @@ #include "eic_handler.h" #include "samd/external_interrupts.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" void common_hal_countio_counter_construct(countio_counter_obj_t *self, const mcu_pin_obj_t *pin, countio_edge_t edge, digitalio_pull_t pull) { diff --git a/ports/atmel-samd/common-hal/digitalio/DigitalInOut.c b/ports/atmel-samd/common-hal/digitalio/DigitalInOut.c index 5e8381e337..3b68748e47 100644 --- a/ports/atmel-samd/common-hal/digitalio/DigitalInOut.c +++ b/ports/atmel-samd/common-hal/digitalio/DigitalInOut.c @@ -34,7 +34,6 @@ #include "common-hal/microcontroller/Pin.h" #include "shared-bindings/digitalio/DigitalInOut.h" -#include "supervisor/shared/translate/translate.h" digitalinout_result_t common_hal_digitalio_digitalinout_construct( digitalio_digitalinout_obj_t *self, const mcu_pin_obj_t *pin) { diff --git a/ports/atmel-samd/common-hal/frequencyio/FrequencyIn.c b/ports/atmel-samd/common-hal/frequencyio/FrequencyIn.c index df26379170..da0230f60b 100644 --- a/ports/atmel-samd/common-hal/frequencyio/FrequencyIn.c +++ b/ports/atmel-samd/common-hal/frequencyio/FrequencyIn.c @@ -48,7 +48,6 @@ #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/time/__init__.h" #include "supervisor/shared/tick.h" -#include "supervisor/shared/translate/translate.h" #ifdef SAMD21 #include "hpl/gclk/hpl_gclk_base.h" diff --git a/ports/atmel-samd/common-hal/i2ctarget/I2CTarget.h b/ports/atmel-samd/common-hal/i2ctarget/I2CTarget.h index 894961ab7b..20426e61cc 100644 --- a/ports/atmel-samd/common-hal/i2ctarget/I2CTarget.h +++ b/ports/atmel-samd/common-hal/i2ctarget/I2CTarget.h @@ -3,7 +3,11 @@ * * The MIT License (MIT) * +<<<<<<<< HEAD:ports/atmel-samd/common-hal/i2ctarget/I2CTarget.h * Copyright (c) 2018 Noralf Trønnes +======== + * Copyright (c) 2013, 2014 Damien P. George +>>>>>>>> v1.20.0:ports/samd/pendsv.h * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,7 +27,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#ifndef MICROPY_INCLUDED_SAMD_PENDSV_H +#define MICROPY_INCLUDED_SAMD_PENDSV_H +<<<<<<<< HEAD:ports/atmel-samd/common-hal/i2ctarget/I2CTarget.h #ifndef MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_BUSIO_I2C_TARGET_H #define MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_BUSIO_I2C_TARGET_H @@ -43,3 +50,19 @@ typedef struct { } i2ctarget_i2c_target_obj_t; #endif // MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_BUSIO_I2C_TARGET_H +======== +enum { + PENDSV_DISPATCH_SOFT_TIMER, // For later & for having at least one entry + MICROPY_BOARD_PENDSV_ENTRIES + PENDSV_DISPATCH_MAX +}; + +#define PENDSV_DISPATCH_NUM_SLOTS PENDSV_DISPATCH_MAX + +typedef void (*pendsv_dispatch_t)(void); + +void pendsv_init(void); +void pendsv_schedule_dispatch(size_t slot, pendsv_dispatch_t f); + +#endif // MICROPY_INCLUDED_SAMD_PENDSV_H +>>>>>>>> v1.20.0:ports/samd/pendsv.h diff --git a/ports/atmel-samd/common-hal/microcontroller/Pin.c b/ports/atmel-samd/common-hal/microcontroller/Pin.c index d70de33618..3db4ec4898 100644 --- a/ports/atmel-samd/common-hal/microcontroller/Pin.c +++ b/ports/atmel-samd/common-hal/microcontroller/Pin.c @@ -27,7 +27,6 @@ #include "py/runtime.h" #include "shared-bindings/microcontroller/Pin.h" -#include "supervisor/shared/translate/translate.h" #include "atmel_start_pins.h" #include "hal/include/hal_gpio.h" diff --git a/ports/atmel-samd/common-hal/microcontroller/__init__.c b/ports/atmel-samd/common-hal/microcontroller/__init__.c index 054d7ee7f8..c4b7c787b4 100644 --- a/ports/atmel-samd/common-hal/microcontroller/__init__.c +++ b/ports/atmel-samd/common-hal/microcontroller/__init__.c @@ -36,7 +36,6 @@ #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/microcontroller/Processor.h" #include "supervisor/shared/safe_mode.h" -#include "supervisor/shared/translate/translate.h" void common_hal_mcu_delay_us(uint32_t delay) { mp_hal_delay_us(delay); diff --git a/ports/atmel-samd/common-hal/ps2io/Ps2.c b/ports/atmel-samd/common-hal/ps2io/Ps2.c index 43f40aa48e..bde2ecfac6 100644 --- a/ports/atmel-samd/common-hal/ps2io/Ps2.c +++ b/ports/atmel-samd/common-hal/ps2io/Ps2.c @@ -43,7 +43,6 @@ #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/ps2io/Ps2.h" #include "supervisor/port.h" -#include "supervisor/shared/translate/translate.h" #define STATE_IDLE 0 #define STATE_RECV 1 diff --git a/ports/atmel-samd/common-hal/pulseio/PulseIn.c b/ports/atmel-samd/common-hal/pulseio/PulseIn.c index cc2c1f4fe0..69c6753c65 100644 --- a/ports/atmel-samd/common-hal/pulseio/PulseIn.c +++ b/ports/atmel-samd/common-hal/pulseio/PulseIn.c @@ -44,7 +44,6 @@ #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/pulseio/PulseIn.h" #include "supervisor/shared/tick.h" -#include "supervisor/shared/translate/translate.h" #include "supervisor/port.h" // This timer is shared amongst all PulseIn objects as a higher resolution clock. diff --git a/ports/atmel-samd/common-hal/pulseio/PulseOut.c b/ports/atmel-samd/common-hal/pulseio/PulseOut.c index dc98e4bd18..282c6035cd 100644 --- a/ports/atmel-samd/common-hal/pulseio/PulseOut.c +++ b/ports/atmel-samd/common-hal/pulseio/PulseOut.c @@ -36,7 +36,6 @@ #include "py/gc.h" #include "py/runtime.h" #include "shared-bindings/pulseio/PulseOut.h" -#include "supervisor/shared/translate/translate.h" #include "timer_handler.h" // This timer is shared amongst all PulseOut objects under the assumption that diff --git a/ports/atmel-samd/common-hal/pwmio/PWMOut.c b/ports/atmel-samd/common-hal/pwmio/PWMOut.c index 1bb955fce8..e18420c457 100644 --- a/ports/atmel-samd/common-hal/pwmio/PWMOut.c +++ b/ports/atmel-samd/common-hal/pwmio/PWMOut.c @@ -38,7 +38,6 @@ #include "hal/utils/include/utils_repeat_macro.h" #include "samd/pins.h" #include "samd/timers.h" -#include "supervisor/shared/translate/translate.h" #undef ENABLE diff --git a/ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.c b/ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.c index d253e0967e..d8d56e3172 100644 --- a/ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.c +++ b/ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.c @@ -33,7 +33,6 @@ #include "eic_handler.h" #include "samd/external_interrupts.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencoder_obj_t *self, const mcu_pin_obj_t *pin_a, const mcu_pin_obj_t *pin_b) { diff --git a/ports/atmel-samd/common-hal/rtc/RTC.c b/ports/atmel-samd/common-hal/rtc/RTC.c index 0ebb61e11b..2da624494b 100644 --- a/ports/atmel-samd/common-hal/rtc/RTC.c +++ b/ports/atmel-samd/common-hal/rtc/RTC.c @@ -37,7 +37,6 @@ #include "shared-bindings/rtc/__init__.h" #include "shared-bindings/rtc/RTC.h" #include "supervisor/port.h" -#include "supervisor/shared/translate/translate.h" // This is the time in seconds since 2000 that the RTC was started. // TODO: Change the offset to ticks so that it can be a subsecond adjustment. diff --git a/ports/atmel-samd/common-hal/sdioio/SDCard.c b/ports/atmel-samd/common-hal/sdioio/SDCard.c index e641db75ac..3490015cbe 100644 --- a/ports/atmel-samd/common-hal/sdioio/SDCard.c +++ b/ports/atmel-samd/common-hal/sdioio/SDCard.c @@ -32,7 +32,6 @@ #include "shared-bindings/sdioio/SDCard.h" #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/microcontroller/__init__.h" -#include "supervisor/shared/translate/translate.h" #include "genhdr/sdiodata.h" diff --git a/ports/atmel-samd/common-hal/touchio/TouchIn.c b/ports/atmel-samd/common-hal/touchio/TouchIn.c index c3fae041fb..8922dc50fb 100644 --- a/ports/atmel-samd/common-hal/touchio/TouchIn.c +++ b/ports/atmel-samd/common-hal/touchio/TouchIn.c @@ -33,7 +33,6 @@ #include "py/mphal.h" #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/touchio/TouchIn.h" -#include "supervisor/shared/translate/translate.h" // Native touchio only exists for SAMD21 #ifdef SAMD21 diff --git a/ports/broadcom/common-hal/digitalio/DigitalInOut.c b/ports/broadcom/common-hal/digitalio/DigitalInOut.c index 0dd74a8412..2173ac5684 100644 --- a/ports/broadcom/common-hal/digitalio/DigitalInOut.c +++ b/ports/broadcom/common-hal/digitalio/DigitalInOut.c @@ -32,7 +32,6 @@ #include "common-hal/microcontroller/Pin.h" #include "shared-bindings/digitalio/DigitalInOut.h" -#include "supervisor/shared/translate/translate.h" #include "peripherals/broadcom/gpio.h" diff --git a/ports/broadcom/common-hal/sdioio/SDCard.c b/ports/broadcom/common-hal/sdioio/SDCard.c index cf8bfb1faa..041a06c036 100644 --- a/ports/broadcom/common-hal/sdioio/SDCard.c +++ b/ports/broadcom/common-hal/sdioio/SDCard.c @@ -35,7 +35,6 @@ #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/microcontroller/__init__.h" #include "supervisor/port.h" -#include "supervisor/shared/translate/translate.h" #include "peripherals/broadcom/cpu.h" #include "peripherals/broadcom/defines.h" diff --git a/ports/espressif/common-hal/analogio/AnalogIn.c b/ports/espressif/common-hal/analogio/AnalogIn.c index fb2e098cb4..3dc771ea46 100644 --- a/ports/espressif/common-hal/analogio/AnalogIn.c +++ b/ports/espressif/common-hal/analogio/AnalogIn.c @@ -28,7 +28,6 @@ #include "shared-bindings/analogio/AnalogIn.h" #include "py/mperrno.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" #include "components/driver/include/driver/adc_common.h" #include "components/esp_adc_cal/include/esp_adc_cal.h" diff --git a/ports/espressif/common-hal/analogio/AnalogOut.c b/ports/espressif/common-hal/analogio/AnalogOut.c index 1a1e74391b..4f7bb40dfd 100644 --- a/ports/espressif/common-hal/analogio/AnalogOut.c +++ b/ports/espressif/common-hal/analogio/AnalogOut.c @@ -33,7 +33,6 @@ #include "shared-bindings/analogio/AnalogOut.h" #include "shared-bindings/microcontroller/Pin.h" -#include "supervisor/shared/translate/translate.h" #if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) #include "components/driver/include/driver/dac_common.h" diff --git a/ports/espressif/common-hal/audiobusio/I2SOut.c b/ports/espressif/common-hal/audiobusio/I2SOut.c index 440151e881..b527b1a93d 100644 --- a/ports/espressif/common-hal/audiobusio/I2SOut.c +++ b/ports/espressif/common-hal/audiobusio/I2SOut.c @@ -42,7 +42,6 @@ #include "shared-bindings/audiobusio/I2SOut.h" #include "shared-bindings/audiocore/RawSample.h" #include "shared-bindings/microcontroller/Pin.h" -#include "supervisor/shared/translate/translate.h" #include "driver/i2s.h" diff --git a/ports/espressif/common-hal/busio/I2C.c b/ports/espressif/common-hal/busio/I2C.c index 92bd4fb157..5b58240118 100644 --- a/ports/espressif/common-hal/busio/I2C.c +++ b/ports/espressif/common-hal/busio/I2C.c @@ -32,7 +32,6 @@ #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/microcontroller/Pin.h" -#include "supervisor/shared/translate/translate.h" void common_hal_busio_i2c_construct(busio_i2c_obj_t *self, const mcu_pin_obj_t *scl, const mcu_pin_obj_t *sda, uint32_t frequency, uint32_t timeout) { diff --git a/ports/espressif/common-hal/busio/UART.c b/ports/espressif/common-hal/busio/UART.c index 37e0092726..e31dfe541c 100644 --- a/ports/espressif/common-hal/busio/UART.c +++ b/ports/espressif/common-hal/busio/UART.c @@ -38,7 +38,6 @@ #include "py/runtime.h" #include "py/stream.h" #include "supervisor/port.h" -#include "supervisor/shared/translate/translate.h" #include "supervisor/shared/tick.h" static uint8_t never_reset_uart_mask = 0; diff --git a/ports/espressif/common-hal/countio/Counter.c b/ports/espressif/common-hal/countio/Counter.c index cc4c067266..fddfd3b0b7 100644 --- a/ports/espressif/common-hal/countio/Counter.c +++ b/ports/espressif/common-hal/countio/Counter.c @@ -29,7 +29,6 @@ #include "common-hal/microcontroller/Pin.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" void common_hal_countio_counter_construct(countio_counter_obj_t *self, const mcu_pin_obj_t *pin, countio_edge_t edge, digitalio_pull_t pull) { diff --git a/ports/espressif/common-hal/digitalio/DigitalInOut.c b/ports/espressif/common-hal/digitalio/DigitalInOut.c index 61671dadb9..cdbdbc04ef 100644 --- a/ports/espressif/common-hal/digitalio/DigitalInOut.c +++ b/ports/espressif/common-hal/digitalio/DigitalInOut.c @@ -26,7 +26,6 @@ #include "shared-bindings/digitalio/DigitalInOut.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" #include "components/driver/include/driver/gpio.h" diff --git a/ports/espressif/common-hal/espidf/__init__.c b/ports/espressif/common-hal/espidf/__init__.c index 379d7c5ab4..a049b35a72 100644 --- a/ports/espressif/common-hal/espidf/__init__.c +++ b/ports/espressif/common-hal/espidf/__init__.c @@ -25,7 +25,6 @@ */ #include "bindings/espidf/__init__.h" -#include "supervisor/shared/translate/translate.h" #include "supervisor/memory.h" #include "py/runtime.h" diff --git a/ports/espressif/common-hal/microcontroller/Processor.c b/ports/espressif/common-hal/microcontroller/Processor.c index 5b7463c9ff..c7f730ce95 100644 --- a/ports/espressif/common-hal/microcontroller/Processor.c +++ b/ports/espressif/common-hal/microcontroller/Processor.c @@ -33,7 +33,6 @@ #include "common-hal/microcontroller/Processor.h" #include "shared-bindings/microcontroller/Processor.h" #include "shared-bindings/microcontroller/ResetReason.h" -#include "supervisor/shared/translate/translate.h" #include "esp_sleep.h" #include "esp_system.h" diff --git a/ports/espressif/mphalport.h b/ports/espressif/mphalport.h index 7ff8129903..96b8268dc3 100644 --- a/ports/espressif/mphalport.h +++ b/ports/espressif/mphalport.h @@ -38,4 +38,31 @@ bool mp_hal_stdin_any(void); +<<<<<<<< HEAD:ports/espressif/mphalport.h #endif // MICROPY_INCLUDED_ESPRESSIF_MPHAL_H +======== +int mp_hal_get_interrupt_char(void); + +#if MICROPY_VFS_POSIX + +#include + +// This macro is used to implement PEP 475 to retry specified syscalls on EINTR +#define MP_HAL_RETRY_SYSCALL(ret, syscall, raise) \ + { \ + for (;;) { \ + ret = syscall; \ + if (ret == -1) { \ + int err = errno; \ + if (err == EINTR) { \ + mp_handle_pending(true); \ + continue; \ + } \ + raise; \ + } \ + break; \ + } \ + } + +#endif +>>>>>>>> v1.20.0:ports/webassembly/mphalport.h diff --git a/ports/litex/boards/fomu/board.c b/ports/litex/boards/fomu/board.c index 43fc74fd42..409ca61bd6 100644 --- a/ports/litex/boards/fomu/board.c +++ b/ports/litex/boards/fomu/board.c @@ -66,3 +66,4 @@ void board_init(void) { ledda_write(3, LEDDPWRG); // Green ledda_write(98, LEDDPWRB); // Blue } +#endif // MICROPY_INCLUDED_MIMXRT_HAL_FLEXSPI_NOR_FLASH_H diff --git a/ports/litex/common-hal/digitalio/DigitalInOut.c b/ports/litex/common-hal/digitalio/DigitalInOut.c index 7545722967..3a14d8e625 100644 --- a/ports/litex/common-hal/digitalio/DigitalInOut.c +++ b/ports/litex/common-hal/digitalio/DigitalInOut.c @@ -27,7 +27,6 @@ #include "shared-bindings/digitalio/DigitalInOut.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" #include "csr.h" diff --git a/ports/litex/common-hal/microcontroller/Processor.c b/ports/litex/common-hal/microcontroller/Processor.c index 12c3787c79..5e4a57dfeb 100644 --- a/ports/litex/common-hal/microcontroller/Processor.c +++ b/ports/litex/common-hal/microcontroller/Processor.c @@ -31,7 +31,6 @@ #include "common-hal/microcontroller/Processor.h" #include "shared-bindings/microcontroller/Processor.h" #include "shared-bindings/microcontroller/ResetReason.h" -#include "supervisor/shared/translate/translate.h" #include "csr.h" #include "generated/soc.h" diff --git a/ports/mimxrt10xx/boards/imxrt1010_evk/board.c b/ports/mimxrt10xx/boards/imxrt1010_evk/board.c index 9c7f4a256f..83162d40e5 100644 --- a/ports/mimxrt10xx/boards/imxrt1010_evk/board.c +++ b/ports/mimxrt10xx/boards/imxrt1010_evk/board.c @@ -3,8 +3,12 @@ * * The MIT License (MIT) * +<<<<<<<< HEAD:ports/mimxrt10xx/boards/imxrt1010_evk/board.c * Copyright (c) 2019 Scott Shawcroft for Adafruit Industries * Copyright (c) 2019 Artur Pacholec +======== + * Copyright (c) 2022 Damien P. George +>>>>>>>> v1.20.0:ports/raspberrypi/pendsv.h * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,7 +28,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#ifndef MICROPY_INCLUDED_RP2_PENDSV_H +#define MICROPY_INCLUDED_RP2_PENDSV_H +<<<<<<<< HEAD:ports/mimxrt10xx/boards/imxrt1010_evk/board.c #include "supervisor/board.h" #include "shared-bindings/microcontroller/Pin.h" @@ -45,3 +52,30 @@ const mcu_pin_obj_t *mimxrt10xx_reset_forbidden_pins[] = { }; // Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. +======== +#include + +enum { + #if MICROPY_PY_LWIP + PENDSV_DISPATCH_LWIP, + #endif + #if MICROPY_PY_NETWORK_CYW43 + PENDSV_DISPATCH_CYW43, + #endif + #if MICROPY_PY_NETWORK_WIZNET5K + PENDSV_DISPATCH_WIZNET, + #endif + MICROPY_BOARD_PENDSV_ENTRIES + PENDSV_DISPATCH_MAX +}; + +#define PENDSV_DISPATCH_NUM_SLOTS PENDSV_DISPATCH_MAX + +typedef void (*pendsv_dispatch_t)(void); + +void pendsv_suspend(void); +void pendsv_resume(void); +void pendsv_schedule_dispatch(size_t slot, pendsv_dispatch_t f); + +#endif // MICROPY_INCLUDED_RP2_PENDSV_H +>>>>>>>> v1.20.0:ports/raspberrypi/pendsv.h diff --git a/ports/mimxrt10xx/common-hal/analogio/AnalogOut.c b/ports/mimxrt10xx/common-hal/analogio/AnalogOut.c index 14e1faab09..28c134f2b2 100644 --- a/ports/mimxrt10xx/common-hal/analogio/AnalogOut.c +++ b/ports/mimxrt10xx/common-hal/analogio/AnalogOut.c @@ -29,7 +29,6 @@ #include "shared-bindings/analogio/AnalogOut.h" #include "shared-bindings/microcontroller/Pin.h" -#include "supervisor/shared/translate/translate.h" void common_hal_analogio_analogout_construct(analogio_analogout_obj_t *self, const mcu_pin_obj_t *pin) { mp_raise_NotImplementedError_varg(translate("%q"), MP_QSTR_AnalogOut); diff --git a/ports/mimxrt10xx/common-hal/audiobusio/I2SOut.c b/ports/mimxrt10xx/common-hal/audiobusio/I2SOut.c index 9ba2762f1c..71640e7d28 100644 --- a/ports/mimxrt10xx/common-hal/audiobusio/I2SOut.c +++ b/ports/mimxrt10xx/common-hal/audiobusio/I2SOut.c @@ -39,7 +39,6 @@ #include "shared-bindings/audiobusio/I2SOut.h" #include "shared-bindings/audiocore/RawSample.h" #include "shared-bindings/microcontroller/Pin.h" -#include "supervisor/shared/translate/translate.h" #include "supervisor/shared/tick.h" // Where required we use identifier names that are required by NXP's diff --git a/ports/mimxrt10xx/common-hal/audiobusio/I2SOut.h b/ports/mimxrt10xx/common-hal/audiobusio/I2SOut.h index 319e52c159..0c2568e481 100644 --- a/ports/mimxrt10xx/common-hal/audiobusio/I2SOut.h +++ b/ports/mimxrt10xx/common-hal/audiobusio/I2SOut.h @@ -1,9 +1,15 @@ /* * This file is part of the MicroPython project, http://micropython.org/ * + * This file provides functions for configuring the clocks. + * * The MIT License (MIT) * +<<<<<<<< HEAD:ports/mimxrt10xx/common-hal/audiobusio/I2SOut.h * Copyright (c) 2020 Jeff Epler for Adafruit Industries +======== + * Copyright (c) 2022 Robert Hammelrath +>>>>>>>> v1.20.0:ports/samd/clock_config.h * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,6 +30,7 @@ * THE SOFTWARE. */ +<<<<<<<< HEAD:ports/mimxrt10xx/common-hal/audiobusio/I2SOut.h #pragma once // Some boards don't implement I2SOut, so suppress any routines from here. @@ -42,3 +49,11 @@ typedef struct { } audiobusio_i2sout_obj_t; #endif +======== +void init_clocks(uint32_t cpu_freq); +void set_cpu_freq(uint32_t cpu_freq); +uint32_t get_cpu_freq(void); +uint32_t get_peripheral_freq(void); +void check_usb_recovery_mode(void); +void enable_sercom_clock(int id); +>>>>>>>> v1.20.0:ports/samd/clock_config.h diff --git a/ports/mimxrt10xx/common-hal/audiopwmio/PWMAudioOut.c b/ports/mimxrt10xx/common-hal/audiopwmio/PWMAudioOut.c index 8650522b41..11c2b8e87e 100644 --- a/ports/mimxrt10xx/common-hal/audiopwmio/PWMAudioOut.c +++ b/ports/mimxrt10xx/common-hal/audiopwmio/PWMAudioOut.c @@ -36,7 +36,6 @@ #include "common-hal/audiopwmio/PWMAudioOut.h" #include "shared-bindings/audiopwmio/PWMAudioOut.h" #include "shared-bindings/microcontroller/Pin.h" -#include "supervisor/shared/translate/translate.h" #include "supervisor/shared/tick.h" // Where required we use identifier names that are required by NXP's diff --git a/ports/mimxrt10xx/common-hal/digitalio/DigitalInOut.c b/ports/mimxrt10xx/common-hal/digitalio/DigitalInOut.c index 31dac38bdc..c37646b098 100644 --- a/ports/mimxrt10xx/common-hal/digitalio/DigitalInOut.c +++ b/ports/mimxrt10xx/common-hal/digitalio/DigitalInOut.c @@ -36,7 +36,6 @@ #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/digitalio/DigitalInOut.h" -#include "supervisor/shared/translate/translate.h" #define IOMUXC_SW_MUX_CTL_PAD_MUX_MODE_ALT5 5U diff --git a/ports/mimxrt10xx/common-hal/microcontroller/__init__.c b/ports/mimxrt10xx/common-hal/microcontroller/__init__.c index de970fa26a..ef7e481330 100644 --- a/ports/mimxrt10xx/common-hal/microcontroller/__init__.c +++ b/ports/mimxrt10xx/common-hal/microcontroller/__init__.c @@ -36,9 +36,7 @@ #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/microcontroller/Processor.h" -#include "supervisor/linker.h" #include "supervisor/shared/safe_mode.h" -#include "supervisor/shared/translate/translate.h" #include "pins.h" diff --git a/ports/mimxrt10xx/common-hal/pwmio/PWMOut.c b/ports/mimxrt10xx/common-hal/pwmio/PWMOut.c index 0bf11403d2..cb1e7b0529 100644 --- a/ports/mimxrt10xx/common-hal/pwmio/PWMOut.c +++ b/ports/mimxrt10xx/common-hal/pwmio/PWMOut.c @@ -35,7 +35,6 @@ #include "sdk/drivers/pwm/fsl_pwm.h" -#include "supervisor/shared/translate/translate.h" #include "periph.h" static PWM_Type *const _flexpwms[] = PWM_BASE_PTRS; diff --git a/ports/mimxrt10xx/common-hal/rtc/RTC.c b/ports/mimxrt10xx/common-hal/rtc/RTC.c index 386b6a3ac5..3cbaac9ccf 100644 --- a/ports/mimxrt10xx/common-hal/rtc/RTC.c +++ b/ports/mimxrt10xx/common-hal/rtc/RTC.c @@ -33,7 +33,6 @@ #include "shared-bindings/rtc/__init__.h" #include "shared-bindings/rtc/RTC.h" #include "common-hal/rtc/RTC.h" -#include "supervisor/shared/translate/translate.h" #include "sdk/drivers/snvs_hp/fsl_snvs_hp.h" #include "sdk/drivers/snvs_lp/fsl_snvs_lp.h" diff --git a/ports/mimxrt10xx/peripherals/mimxrt10xx/MIMXRT1042/clocks.c b/ports/mimxrt10xx/peripherals/mimxrt10xx/MIMXRT1042/clocks.c index 95db72df45..97d3d04f8f 100644 --- a/ports/mimxrt10xx/peripherals/mimxrt10xx/MIMXRT1042/clocks.c +++ b/ports/mimxrt10xx/peripherals/mimxrt10xx/MIMXRT1042/clocks.c @@ -379,4 +379,5 @@ void clocks_init(void) { IOMUXC_GPR->GPR5 &= ~IOMUXC_GPR_GPR5_VREF_1M_CLK_GPT2_MASK; /* Set SystemCoreClock variable. */ SystemCoreClock = BOARD_BOOTCLOCKRUN_CORE_CLOCK; + CLOCK_SetMode(kCLOCK_ModeRun); } diff --git a/ports/mimxrt10xx/peripherals/mimxrt10xx/MIMXRT1052/clocks.c b/ports/mimxrt10xx/peripherals/mimxrt10xx/MIMXRT1052/clocks.c index de12bea6a0..aafd0fde33 100644 --- a/ports/mimxrt10xx/peripherals/mimxrt10xx/MIMXRT1052/clocks.c +++ b/ports/mimxrt10xx/peripherals/mimxrt10xx/MIMXRT1052/clocks.c @@ -399,4 +399,5 @@ void clocks_init(void) { IOMUXC_GPR->GPR5 &= ~IOMUXC_GPR_GPR5_VREF_1M_CLK_GPT2_MASK; /* Set SystemCoreClock variable. */ SystemCoreClock = BOARD_BOOTCLOCKRUN_CORE_CLOCK; + CLOCK_SetMode(kCLOCK_ModeRun); } diff --git a/ports/nrf/common-hal/analogio/AnalogIn.c b/ports/nrf/common-hal/analogio/AnalogIn.c index b9d4b6a199..69b9eb984c 100644 --- a/ports/nrf/common-hal/analogio/AnalogIn.c +++ b/ports/nrf/common-hal/analogio/AnalogIn.c @@ -29,7 +29,6 @@ #include "shared-bindings/analogio/AnalogIn.h" #include "shared-bindings/microcontroller/Pin.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" #include "nrf_saadc.h" #include "nrf_gpio.h" diff --git a/ports/nrf/common-hal/analogio/AnalogOut.c b/ports/nrf/common-hal/analogio/AnalogOut.c index 38ff935674..03e0515f49 100644 --- a/ports/nrf/common-hal/analogio/AnalogOut.c +++ b/ports/nrf/common-hal/analogio/AnalogOut.c @@ -31,7 +31,6 @@ #include "py/mperrno.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" void common_hal_analogio_analogout_construct(analogio_analogout_obj_t *self, const mcu_pin_obj_t *pin) { mp_raise_NotImplementedError_varg(translate("%q"), MP_QSTR_AnalogOut); diff --git a/ports/nrf/common-hal/audiopwmio/PWMAudioOut.c b/ports/nrf/common-hal/audiopwmio/PWMAudioOut.c index 2334db851a..88843e81b1 100644 --- a/ports/nrf/common-hal/audiopwmio/PWMAudioOut.c +++ b/ports/nrf/common-hal/audiopwmio/PWMAudioOut.c @@ -37,7 +37,6 @@ #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/microcontroller/Pin.h" #include "supervisor/shared/tick.h" -#include "supervisor/shared/translate/translate.h" // TODO: This should be the same size as PWMOut.c:pwms[], but there's no trivial way to accomplish that STATIC audiopwmio_pwmaudioout_obj_t *active_audio[4]; diff --git a/ports/nrf/common-hal/busio/I2C.c b/ports/nrf/common-hal/busio/I2C.c index 2bd1416f5a..03c8931e7e 100644 --- a/ports/nrf/common-hal/busio/I2C.c +++ b/ports/nrf/common-hal/busio/I2C.c @@ -32,7 +32,6 @@ #include "shared-bindings/microcontroller/Pin.h" #include "py/mperrno.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" #include "nrfx_twim.h" #include "nrfx_spim.h" diff --git a/ports/nrf/common-hal/busio/UART.c b/ports/nrf/common-hal/busio/UART.c index 5c44ab16d5..574da3cd52 100644 --- a/ports/nrf/common-hal/busio/UART.c +++ b/ports/nrf/common-hal/busio/UART.c @@ -33,7 +33,6 @@ #include "py/mperrno.h" #include "py/runtime.h" #include "py/stream.h" -#include "supervisor/shared/translate/translate.h" #include "nrfx_uarte.h" #include "nrf_gpio.h" diff --git a/ports/nrf/common-hal/digitalio/DigitalInOut.c b/ports/nrf/common-hal/digitalio/DigitalInOut.c index a6440228f1..e1e9e24e34 100644 --- a/ports/nrf/common-hal/digitalio/DigitalInOut.c +++ b/ports/nrf/common-hal/digitalio/DigitalInOut.c @@ -26,7 +26,6 @@ #include "shared-bindings/digitalio/DigitalInOut.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" #include "nrf_gpio.h" diff --git a/ports/nrf/common-hal/pulseio/PulseOut.c b/ports/nrf/common-hal/pulseio/PulseOut.c index c2d59ffdba..e3ee9dbeb9 100644 --- a/ports/nrf/common-hal/pulseio/PulseOut.c +++ b/ports/nrf/common-hal/pulseio/PulseOut.c @@ -35,7 +35,6 @@ #include "py/runtime.h" #include "shared-bindings/pulseio/PulseOut.h" #include "shared-bindings/pwmio/PWMOut.h" -#include "supervisor/shared/translate/translate.h" // A single timer is shared amongst all PulseOut objects under the assumption that // the code is single threaded. diff --git a/ports/nrf/common-hal/pwmio/PWMOut.c b/ports/nrf/common-hal/pwmio/PWMOut.c index 6171f5eb76..8edaa77f66 100644 --- a/ports/nrf/common-hal/pwmio/PWMOut.c +++ b/ports/nrf/common-hal/pwmio/PWMOut.c @@ -30,7 +30,6 @@ #include "py/runtime.h" #include "common-hal/pwmio/PWMOut.h" #include "shared-bindings/pwmio/PWMOut.h" -#include "supervisor/shared/translate/translate.h" #include "nrf_gpio.h" diff --git a/ports/nrf/common-hal/rtc/RTC.c b/ports/nrf/common-hal/rtc/RTC.c index 29d308eccc..a7bc89afb0 100644 --- a/ports/nrf/common-hal/rtc/RTC.c +++ b/ports/nrf/common-hal/rtc/RTC.c @@ -33,7 +33,6 @@ #include "common-hal/rtc/RTC.h" #include "shared-bindings/rtc/RTC.h" #include "supervisor/port.h" -#include "supervisor/shared/translate/translate.h" // This is the time in seconds since 2000 that the RTC was started. __attribute__((section(".uninitialized"))) static uint32_t rtc_offset[3]; diff --git a/ports/raspberrypi/bindings/rp2pio/StateMachine.c b/ports/raspberrypi/bindings/rp2pio/StateMachine.c index d5924277c8..8e2df24c6c 100644 --- a/ports/raspberrypi/bindings/rp2pio/StateMachine.c +++ b/ports/raspberrypi/bindings/rp2pio/StateMachine.c @@ -40,7 +40,6 @@ #include "py/mperrno.h" #include "py/objproperty.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" //| class StateMachine: diff --git a/ports/raspberrypi/common-hal/analogbufio/BufferedIn.c b/ports/raspberrypi/common-hal/analogbufio/BufferedIn.c index bc5f9028b7..7dfc0fa11a 100644 --- a/ports/raspberrypi/common-hal/analogbufio/BufferedIn.c +++ b/ports/raspberrypi/common-hal/analogbufio/BufferedIn.c @@ -35,7 +35,6 @@ #include "shared-bindings/microcontroller/Pin.h" #include "shared/runtime/interrupt_char.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" #include "src/rp2_common/hardware_adc/include/hardware/adc.h" #include "src/rp2_common/hardware_dma/include/hardware/dma.h" #include "src/common/pico_stdlib/include/pico/stdlib.h" diff --git a/ports/raspberrypi/common-hal/analogio/AnalogIn.c b/ports/raspberrypi/common-hal/analogio/AnalogIn.c index afa31e3c83..37f1000d0e 100644 --- a/ports/raspberrypi/common-hal/analogio/AnalogIn.c +++ b/ports/raspberrypi/common-hal/analogio/AnalogIn.c @@ -29,7 +29,6 @@ #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/microcontroller/Pin.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" #include "src/rp2_common/hardware_adc/include/hardware/adc.h" diff --git a/ports/raspberrypi/common-hal/analogio/AnalogOut.c b/ports/raspberrypi/common-hal/analogio/AnalogOut.c index 38ff935674..03e0515f49 100644 --- a/ports/raspberrypi/common-hal/analogio/AnalogOut.c +++ b/ports/raspberrypi/common-hal/analogio/AnalogOut.c @@ -31,7 +31,6 @@ #include "py/mperrno.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" void common_hal_analogio_analogout_construct(analogio_analogout_obj_t *self, const mcu_pin_obj_t *pin) { mp_raise_NotImplementedError_varg(translate("%q"), MP_QSTR_AnalogOut); diff --git a/ports/raspberrypi/common-hal/audiobusio/I2SOut.c b/ports/raspberrypi/common-hal/audiobusio/I2SOut.c index 62d4058d1d..47617fedfd 100644 --- a/ports/raspberrypi/common-hal/audiobusio/I2SOut.c +++ b/ports/raspberrypi/common-hal/audiobusio/I2SOut.c @@ -37,7 +37,6 @@ #include "shared-bindings/microcontroller/Pin.h" #include "shared-module/audiocore/__init__.h" #include "bindings/rp2pio/StateMachine.h" -#include "supervisor/shared/translate/translate.h" const uint16_t i2s_program[] = { // ; Load the next set of samples diff --git a/ports/raspberrypi/common-hal/audiobusio/PDMIn.c b/ports/raspberrypi/common-hal/audiobusio/PDMIn.c index 5d94afcecc..1e7e882277 100644 --- a/ports/raspberrypi/common-hal/audiobusio/PDMIn.c +++ b/ports/raspberrypi/common-hal/audiobusio/PDMIn.c @@ -32,7 +32,6 @@ #include "py/runtime.h" #include "shared-bindings/audiobusio/PDMIn.h" #include "shared-bindings/microcontroller/Pin.h" -#include "supervisor/shared/translate/translate.h" #include "audio_dma.h" diff --git a/ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c b/ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c index 8b5c3accec..67141c2a18 100644 --- a/ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +++ b/ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c @@ -39,7 +39,6 @@ #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/microcontroller/Processor.h" -#include "supervisor/shared/translate/translate.h" #include "src/rp2040/hardware_structs/include/hardware/structs/dma.h" #include "src/rp2_common/hardware_pwm/include/hardware/pwm.h" diff --git a/ports/raspberrypi/common-hal/countio/Counter.c b/ports/raspberrypi/common-hal/countio/Counter.c index b51d182c3a..e360b6f06b 100644 --- a/ports/raspberrypi/common-hal/countio/Counter.c +++ b/ports/raspberrypi/common-hal/countio/Counter.c @@ -2,7 +2,6 @@ #include "py/runtime.h" #include "py/mpstate.h" -#include "supervisor/shared/translate/translate.h" #include "shared-bindings/countio/Edge.h" #include "shared-bindings/digitalio/Pull.h" diff --git a/ports/raspberrypi/common-hal/digitalio/DigitalInOut.c b/ports/raspberrypi/common-hal/digitalio/DigitalInOut.c index d90c0db81b..4eb2a6de9c 100644 --- a/ports/raspberrypi/common-hal/digitalio/DigitalInOut.c +++ b/ports/raspberrypi/common-hal/digitalio/DigitalInOut.c @@ -33,7 +33,6 @@ #include "common-hal/microcontroller/Pin.h" #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/digitalio/DigitalInOut.h" -#include "supervisor/shared/translate/translate.h" #include "src/rp2_common/hardware_gpio/include/hardware/gpio.h" diff --git a/ports/raspberrypi/common-hal/microcontroller/__init__.c b/ports/raspberrypi/common-hal/microcontroller/__init__.c index 6d9b3d7fa0..45d46f4519 100644 --- a/ports/raspberrypi/common-hal/microcontroller/__init__.c +++ b/ports/raspberrypi/common-hal/microcontroller/__init__.c @@ -38,7 +38,6 @@ #include "supervisor/filesystem.h" #include "supervisor/port.h" #include "supervisor/shared/safe_mode.h" -#include "supervisor/shared/translate/translate.h" #include "src/rp2040/hardware_structs/include/hardware/structs/sio.h" #include "src/rp2_common/hardware_sync/include/hardware/sync.h" diff --git a/ports/raspberrypi/common-hal/pulseio/PulseIn.c b/ports/raspberrypi/common-hal/pulseio/PulseIn.c index a074ee3604..cd81e61e92 100644 --- a/ports/raspberrypi/common-hal/pulseio/PulseIn.c +++ b/ports/raspberrypi/common-hal/pulseio/PulseIn.c @@ -32,7 +32,6 @@ #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/pulseio/PulseIn.h" #include "shared-bindings/microcontroller/Pin.h" -#include "supervisor/shared/translate/translate.h" #include "bindings/rp2pio/StateMachine.h" #include "common-hal/pulseio/PulseIn.h" diff --git a/ports/raspberrypi/common-hal/pulseio/PulseOut.c b/ports/raspberrypi/common-hal/pulseio/PulseOut.c index 881e003579..f0f3d36886 100644 --- a/ports/raspberrypi/common-hal/pulseio/PulseOut.c +++ b/ports/raspberrypi/common-hal/pulseio/PulseOut.c @@ -33,7 +33,6 @@ #include "shared-bindings/pwmio/PWMOut.h" #include "shared-bindings/microcontroller/__init__.h" #include "common-hal/pwmio/PWMOut.h" -#include "supervisor/shared/translate/translate.h" #include "src/rp2040/hardware_structs/include/hardware/structs/pwm.h" #include "src/rp2_common/hardware_gpio/include/hardware/gpio.h" #include "src/rp2_common/hardware_pwm/include/hardware/pwm.h" diff --git a/ports/raspberrypi/common-hal/pwmio/PWMOut.c b/ports/raspberrypi/common-hal/pwmio/PWMOut.c index 7925a734fb..512b88df1c 100644 --- a/ports/raspberrypi/common-hal/pwmio/PWMOut.c +++ b/ports/raspberrypi/common-hal/pwmio/PWMOut.c @@ -32,8 +32,6 @@ #include "shared-bindings/pwmio/PWMOut.h" #include "shared-bindings/microcontroller/Processor.h" -#include "supervisor/shared/translate/translate.h" - #include "src/rp2040/hardware_regs/include/hardware/platform_defs.h" #include "src/rp2_common/hardware_clocks/include/hardware/clocks.h" #include "src/rp2_common/hardware_gpio/include/hardware/gpio.h" diff --git a/ports/raspberrypi/common-hal/wifi/Monitor.c b/ports/raspberrypi/common-hal/wifi/Monitor.c index f288cd1ea1..a69dc34f02 100644 --- a/ports/raspberrypi/common-hal/wifi/Monitor.c +++ b/ports/raspberrypi/common-hal/wifi/Monitor.c @@ -3,7 +3,11 @@ * * The MIT License (MIT) * +<<<<<<<< HEAD:ports/raspberrypi/common-hal/wifi/Monitor.c * Copyright (c) 2021 microDev +======== + * Copyright (c) 2013, 2014 Damien P. George +>>>>>>>> v1.20.0:ports/samd/pendsv.c * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,6 +28,7 @@ * THE SOFTWARE. */ +<<<<<<<< HEAD:ports/raspberrypi/common-hal/wifi/Monitor.c #include #include "py/mpstate.h" @@ -72,3 +77,50 @@ mp_obj_t common_hal_wifi_monitor_get_queued(wifi_monitor_obj_t *self) { mp_obj_t common_hal_wifi_monitor_get_packet(wifi_monitor_obj_t *self) { return mp_const_none; } +======== +#include + +#include "py/runtime.h" +#include "shared/runtime/interrupt_char.h" +#include "sam.h" +#include "pendsv.h" + + +#if defined(PENDSV_DISPATCH_NUM_SLOTS) +uint32_t pendsv_dispatch_active; +pendsv_dispatch_t pendsv_dispatch_table[PENDSV_DISPATCH_NUM_SLOTS]; +#endif + +void pendsv_init(void) { + #if defined(PENDSV_DISPATCH_NUM_SLOTS) + pendsv_dispatch_active = false; + #endif + + // set PendSV interrupt at lowest priority + NVIC_SetPriority(PendSV_IRQn, IRQ_PRI_PENDSV); +} + +#if defined(PENDSV_DISPATCH_NUM_SLOTS) +void pendsv_schedule_dispatch(size_t slot, pendsv_dispatch_t f) { + pendsv_dispatch_table[slot] = f; + pendsv_dispatch_active = true; + SCB->ICSR = SCB_ICSR_PENDSVSET_Msk; +} + +void pendsv_dispatch_handler(void) { + for (size_t i = 0; i < PENDSV_DISPATCH_NUM_SLOTS; ++i) { + if (pendsv_dispatch_table[i] != NULL) { + pendsv_dispatch_t f = pendsv_dispatch_table[i]; + pendsv_dispatch_table[i] = NULL; + f(); + } + } +} + +void PendSV_Handler(void) { + if (pendsv_dispatch_active) { + pendsv_dispatch_handler(); + } +} +#endif +>>>>>>>> v1.20.0:ports/samd/pendsv.c diff --git a/ports/raspberrypi/common-hal/wifi/main.c b/ports/raspberrypi/common-hal/wifi/main.c new file mode 100644 index 0000000000..ee673fc93a --- /dev/null +++ b/ports/raspberrypi/common-hal/wifi/main.c @@ -0,0 +1,44 @@ +/* This file is part of the MicroPython project, http://micropython.org/ + * The MIT License (MIT) + * Copyright (c) 2022-2023 Damien P. George + */ + +#include "port/micropython_embed.h" + +// This is example 1 script, which will be compiled and executed. +static const char *example_1 = + "print('hello world!', list(x + 1 for x in range(10)), end='eol\\n')"; + +// This is example 2 script, which will be compiled and executed. +static const char *example_2 = + "for i in range(10):\n" + " print('iter {:08}'.format(i))\n" + "\n" + "try:\n" + " 1//0\n" + "except Exception as er:\n" + " print('caught exception', repr(er))\n" + "\n" + "import gc\n" + "print('run GC collect')\n" + "gc.collect()\n" + "\n" + "print('finish')\n" + ; + +// This array is the MicroPython GC heap. +static char heap[8 * 1024]; + +int main() { + // Initialise MicroPython. + mp_embed_init(&heap[0], sizeof(heap)); + + // Run the example scripts (they will be compiled first). + mp_embed_exec_str(example_1); + mp_embed_exec_str(example_2); + + // Deinitialise MicroPython. + mp_embed_deinit(); + + return 0; +} diff --git a/ports/raspberrypi/common-hal/wifi/micropython_embed.mk b/ports/raspberrypi/common-hal/wifi/micropython_embed.mk new file mode 100644 index 0000000000..5db5415927 --- /dev/null +++ b/ports/raspberrypi/common-hal/wifi/micropython_embed.mk @@ -0,0 +1,9 @@ +# This file is part of the MicroPython project, http://micropython.org/ +# The MIT License (MIT) +# Copyright (c) 2022-2023 Damien P. George + +# Set the location of the top of the MicroPython repository. +MICROPYTHON_TOP = ../.. + +# Include the main makefile fragment to build the MicroPython component. +include $(MICROPYTHON_TOP)/ports/embed/embed.mk diff --git a/ports/raspberrypi/cyw43_configport.h b/ports/raspberrypi/cyw43_configport.h new file mode 100644 index 0000000000..08cded6f1f --- /dev/null +++ b/ports/raspberrypi/cyw43_configport.h @@ -0,0 +1,122 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 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_RP2_CYW43_CONFIGPORT_H +#define MICROPY_INCLUDED_RP2_CYW43_CONFIGPORT_H + +// The board-level config will be included here, so it can set some CYW43 values. +#include "py/mpconfig.h" +#include "py/mperrno.h" +#include "py/mphal.h" +#include "extmod/modnetwork.h" +#include "pendsv.h" + +#define CYW43_CHIPSET_FIRMWARE_INCLUDE_FILE "w43439A0_7_95_49_00_combined.h" +#define CYW43_WIFI_NVRAM_INCLUDE_FILE "wifi_nvram_43439.h" +#define CYW43_IOCTL_TIMEOUT_US (1000000) +#define CYW43_SLEEP_MAX (10) +#define CYW43_NETUTILS (1) +#define CYW43_USE_OTP_MAC (1) + +#define CYW43_EPERM MP_EPERM // Operation not permitted +#define CYW43_EIO MP_EIO // I/O error +#define CYW43_EINVAL MP_EINVAL // Invalid argument +#define CYW43_ETIMEDOUT MP_ETIMEDOUT // Connection timed out + +#define CYW43_THREAD_ENTER MICROPY_PY_LWIP_ENTER +#define CYW43_THREAD_EXIT MICROPY_PY_LWIP_EXIT +#define CYW43_THREAD_LOCK_CHECK + +#define CYW43_HOST_NAME mod_network_hostname + +#define CYW43_SDPCM_SEND_COMMON_WAIT \ + if (get_core_num() == 0) { \ + cyw43_yield(); \ + } \ + +#define CYW43_DO_IOCTL_WAIT \ + if (get_core_num() == 0) { \ + cyw43_yield(); \ + } \ + +#define CYW43_ARRAY_SIZE(a) MP_ARRAY_SIZE(a) + +#define CYW43_HAL_PIN_MODE_INPUT MP_HAL_PIN_MODE_INPUT +#define CYW43_HAL_PIN_MODE_OUTPUT MP_HAL_PIN_MODE_OUTPUT +#define CYW43_HAL_PIN_PULL_NONE MP_HAL_PIN_PULL_NONE +#define CYW43_HAL_PIN_PULL_UP MP_HAL_PIN_PULL_UP +#define CYW43_HAL_PIN_PULL_DOWN MP_HAL_PIN_PULL_DOWN + +#define CYW43_HAL_MAC_WLAN0 MP_HAL_MAC_WLAN0 + +// set in SDK board header +#define CYW43_NUM_GPIOS CYW43_WL_GPIO_COUNT + +#define CYW43_POST_POLL_HOOK cyw43_post_poll_hook(); + +#define cyw43_hal_ticks_us mp_hal_ticks_us +#define cyw43_hal_ticks_ms mp_hal_ticks_ms + +#define cyw43_hal_pin_obj_t mp_hal_pin_obj_t +#define cyw43_hal_pin_config mp_hal_pin_config +#define cyw43_hal_pin_read mp_hal_pin_read +#define cyw43_hal_pin_low mp_hal_pin_low +#define cyw43_hal_pin_high mp_hal_pin_high + +#define cyw43_hal_get_mac mp_hal_get_mac +#define cyw43_hal_get_mac_ascii mp_hal_get_mac_ascii +#define cyw43_hal_generate_laa_mac mp_hal_generate_laa_mac + +#define cyw43_schedule_internal_poll_dispatch(func) pendsv_schedule_dispatch(PENDSV_DISPATCH_CYW43, func) + +void cyw43_post_poll_hook(void); +extern volatile int cyw43_has_pending; + +static inline void cyw43_yield(void) { + uint32_t my_interrupts = save_and_disable_interrupts(); + if (!cyw43_has_pending) { + __WFI(); + } + restore_interrupts(my_interrupts); +} + +static inline void cyw43_delay_us(uint32_t us) { + uint32_t start = mp_hal_ticks_us(); + while (mp_hal_ticks_us() - start < us) { + } +} + +static inline void cyw43_delay_ms(uint32_t ms) { + uint32_t us = ms * 1000; + int32_t start = mp_hal_ticks_us(); + while (mp_hal_ticks_us() - start < us) { + cyw43_yield(); + MICROPY_EVENT_POLL_HOOK_FAST; + } +} + +#define CYW43_EVENT_POLL_HOOK MICROPY_EVENT_POLL_HOOK_FAST + +#endif // MICROPY_INCLUDED_RP2_CYW43_CONFIGPORT_H diff --git a/ports/raspberrypi/mbedtls/mbedtls_port.c b/ports/raspberrypi/mbedtls/mbedtls_port.c index 596d08ccd9..73929c85e8 100644 --- a/ports/raspberrypi/mbedtls/mbedtls_port.c +++ b/ports/raspberrypi/mbedtls/mbedtls_port.c @@ -34,6 +34,9 @@ #include "shared/timeutils/timeutils.h" #include "shared-bindings/os/__init__.h" +#include "hardware/rtc.h" +#include "shared/timeutils/timeutils.h" + extern uint8_t rosc_random_u8(size_t cycles); int mbedtls_hardware_poll(void *data, unsigned char *output, size_t len, size_t *olen) { diff --git a/ports/silabs/boards/devkit_xg24_brd2601b/sensor.c b/ports/silabs/boards/devkit_xg24_brd2601b/sensor.c index d981f416ab..a3b0b68993 100644 --- a/ports/silabs/boards/devkit_xg24_brd2601b/sensor.c +++ b/ports/silabs/boards/devkit_xg24_brd2601b/sensor.c @@ -27,7 +27,6 @@ #include "py/runtime.h" #include "common-hal/busio/I2C.h" #include "shared-bindings/microcontroller/Pin.h" -#include "supervisor/shared/translate/translate.h" #include "em_i2c.h" #include "sl_i2cspm.h" #include "sl_i2cspm_sensor_config.h" diff --git a/ports/silabs/common-hal/analogio/AnalogIn.c b/ports/silabs/common-hal/analogio/AnalogIn.c index e22c5b3493..bfdce02415 100644 --- a/ports/silabs/common-hal/analogio/AnalogIn.c +++ b/ports/silabs/common-hal/analogio/AnalogIn.c @@ -28,7 +28,6 @@ #include "common-hal/analogio/AnalogIn.h" #include "shared-bindings/microcontroller/Pin.h" #include "shared/runtime/interrupt_char.h" -#include "supervisor/shared/translate/translate.h" #include "em_cmu.h" #include "em_iadc.h" diff --git a/ports/silabs/common-hal/analogio/AnalogOut.c b/ports/silabs/common-hal/analogio/AnalogOut.c index 88b2f53f9e..ae5a8a6903 100644 --- a/ports/silabs/common-hal/analogio/AnalogOut.c +++ b/ports/silabs/common-hal/analogio/AnalogOut.c @@ -28,7 +28,6 @@ #include "py/runtime.h" #include "shared-bindings/analogio/AnalogOut.h" #include "shared-bindings/microcontroller/Pin.h" -#include "supervisor/shared/translate/translate.h" #include "common-hal/microcontroller/Pin.h" #include "em_vdac.h" diff --git a/ports/silabs/common-hal/busio/I2C.c b/ports/silabs/common-hal/busio/I2C.c index 62ac14f4e9..cedd7371c1 100644 --- a/ports/silabs/common-hal/busio/I2C.c +++ b/ports/silabs/common-hal/busio/I2C.c @@ -28,7 +28,6 @@ #include "py/mperrno.h" #include "py/runtime.h" #include "shared-bindings/microcontroller/__init__.h" -#include "supervisor/shared/translate/translate.h" #include "shared-bindings/microcontroller/Pin.h" STATIC I2CSPM_Init_TypeDef i2cspm_init; diff --git a/ports/silabs/common-hal/busio/SPI.c b/ports/silabs/common-hal/busio/SPI.c index 89c8012477..e3b743309e 100644 --- a/ports/silabs/common-hal/busio/SPI.c +++ b/ports/silabs/common-hal/busio/SPI.c @@ -29,7 +29,6 @@ #include "py/runtime.h" #include "shared-bindings/microcontroller/__init__.h" #include "supervisor/board.h" -#include "supervisor/shared/translate/translate.h" #include "shared-bindings/microcontroller/Pin.h" // Note that any bugs introduced in this file can cause crashes diff --git a/ports/silabs/common-hal/busio/UART.c b/ports/silabs/common-hal/busio/UART.c index 813432e9aa..37273769ba 100644 --- a/ports/silabs/common-hal/busio/UART.c +++ b/ports/silabs/common-hal/busio/UART.c @@ -35,7 +35,6 @@ #include "py/mperrno.h" #include "py/runtime.h" #include "py/stream.h" -#include "supervisor/shared/translate/translate.h" #define UARTDRV_USART_BUFFER_SIZE 6 diff --git a/ports/silabs/common-hal/digitalio/DigitalInOut.c b/ports/silabs/common-hal/digitalio/DigitalInOut.c index b673d392f2..e9f2767308 100644 --- a/ports/silabs/common-hal/digitalio/DigitalInOut.c +++ b/ports/silabs/common-hal/digitalio/DigitalInOut.c @@ -27,7 +27,6 @@ #include "shared-bindings/digitalio/DigitalInOut.h" #include "shared-bindings/microcontroller/Pin.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" // Never reset pin when reload void common_hal_digitalio_digitalinout_never_reset( diff --git a/ports/silabs/common-hal/microcontroller/Processor.c b/ports/silabs/common-hal/microcontroller/Processor.c index 841f3ba35f..474cc30b00 100644 --- a/ports/silabs/common-hal/microcontroller/Processor.c +++ b/ports/silabs/common-hal/microcontroller/Processor.c @@ -33,7 +33,6 @@ #endif #include "common-hal/microcontroller/Processor.h" #include "shared-bindings/microcontroller/ResetReason.h" -#include "supervisor/shared/translate/translate.h" #include "tempdrv.h" #include "em_system.h" #include "em_cmu.h" diff --git a/ports/silabs/common-hal/nvm/ByteArray.c b/ports/silabs/common-hal/nvm/ByteArray.c index e94ed9198d..acdc7f131b 100644 --- a/ports/silabs/common-hal/nvm/ByteArray.c +++ b/ports/silabs/common-hal/nvm/ByteArray.c @@ -28,7 +28,6 @@ #include "supervisor/shared/stack.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" #include "nvm3_default.h" #include "nvm3_default_config.h" diff --git a/ports/silabs/common-hal/pwmio/PWMOut.c b/ports/silabs/common-hal/pwmio/PWMOut.c index b55c821462..7382fade97 100644 --- a/ports/silabs/common-hal/pwmio/PWMOut.c +++ b/ports/silabs/common-hal/pwmio/PWMOut.c @@ -26,7 +26,6 @@ #include "py/runtime.h" #include "common-hal/pwmio/PWMOut.h" #include "shared-bindings/pwmio/PWMOut.h" -#include "supervisor/shared/translate/translate.h" #include "shared-bindings/microcontroller/Pin.h" STATIC sl_pwm_instance_t pwm_handle[TIM_BANK_ARRAY_LEN]; diff --git a/ports/silabs/common-hal/rtc/RTC.c b/ports/silabs/common-hal/rtc/RTC.c index f3de48894c..956412c7b6 100644 --- a/ports/silabs/common-hal/rtc/RTC.c +++ b/ports/silabs/common-hal/rtc/RTC.c @@ -33,7 +33,6 @@ #include "common-hal/rtc/RTC.h" #include "shared-bindings/rtc/RTC.h" #include "supervisor/port.h" -#include "supervisor/shared/translate/translate.h" #include "peripherals/rtc.h" #include "sl_sleeptimer.h" diff --git a/ports/stm/common-hal/analogio/AnalogIn.c b/ports/stm/common-hal/analogio/AnalogIn.c index 6a67720970..ccf11f96bf 100644 --- a/ports/stm/common-hal/analogio/AnalogIn.c +++ b/ports/stm/common-hal/analogio/AnalogIn.c @@ -27,7 +27,6 @@ #include "common-hal/analogio/AnalogIn.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" #include "shared-bindings/microcontroller/Pin.h" diff --git a/ports/stm/common-hal/analogio/AnalogOut.c b/ports/stm/common-hal/analogio/AnalogOut.c index 129bb37266..23b8631c94 100644 --- a/ports/stm/common-hal/analogio/AnalogOut.c +++ b/ports/stm/common-hal/analogio/AnalogOut.c @@ -36,7 +36,6 @@ #include "shared-bindings/analogio/AnalogOut.h" #include "shared-bindings/microcontroller/Pin.h" -#include "supervisor/shared/translate/translate.h" #include "common-hal/microcontroller/Pin.h" diff --git a/ports/stm/common-hal/busio/I2C.c b/ports/stm/common-hal/busio/I2C.c index d565bd1633..b78aff8e4f 100644 --- a/ports/stm/common-hal/busio/I2C.c +++ b/ports/stm/common-hal/busio/I2C.c @@ -30,7 +30,6 @@ #include "py/mperrno.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" #include "shared-bindings/microcontroller/Pin.h" // I2C timing specs for the H7 and F7 diff --git a/ports/stm/common-hal/busio/SPI.c b/ports/stm/common-hal/busio/SPI.c index 54905d9d1e..d4975e9fc5 100644 --- a/ports/stm/common-hal/busio/SPI.c +++ b/ports/stm/common-hal/busio/SPI.c @@ -30,7 +30,6 @@ #include "shared-bindings/busio/SPI.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" #include "shared-bindings/microcontroller/Pin.h" // Note that any bugs introduced in this file can cause crashes at startup diff --git a/ports/stm/common-hal/busio/UART.c b/ports/stm/common-hal/busio/UART.c index 5c9e05250a..59e98409c7 100644 --- a/ports/stm/common-hal/busio/UART.c +++ b/ports/stm/common-hal/busio/UART.c @@ -35,7 +35,6 @@ #include "py/mperrno.h" #include "py/runtime.h" #include "py/stream.h" -#include "supervisor/shared/translate/translate.h" #define ALL_UARTS 0xFFFF diff --git a/ports/stm/common-hal/microcontroller/Processor.c b/ports/stm/common-hal/microcontroller/Processor.c index 7b3e6a7232..e4952d8fbd 100644 --- a/ports/stm/common-hal/microcontroller/Processor.c +++ b/ports/stm/common-hal/microcontroller/Processor.c @@ -32,7 +32,6 @@ #endif #include "common-hal/microcontroller/Processor.h" #include "shared-bindings/microcontroller/ResetReason.h" -#include "supervisor/shared/translate/translate.h" #include STM32_HAL_H diff --git a/ports/stm/common-hal/pwmio/PWMOut.c b/ports/stm/common-hal/pwmio/PWMOut.c index a983718ff2..2bb3f1f7ab 100644 --- a/ports/stm/common-hal/pwmio/PWMOut.c +++ b/ports/stm/common-hal/pwmio/PWMOut.c @@ -29,7 +29,6 @@ #include "py/runtime.h" #include "common-hal/pwmio/PWMOut.h" #include "shared-bindings/pwmio/PWMOut.h" -#include "supervisor/shared/translate/translate.h" #include STM32_HAL_H #include "shared-bindings/microcontroller/Pin.h" diff --git a/ports/stm/common-hal/rtc/RTC.c b/ports/stm/common-hal/rtc/RTC.c index c9dc50d769..2c34ab5ad6 100644 --- a/ports/stm/common-hal/rtc/RTC.c +++ b/ports/stm/common-hal/rtc/RTC.c @@ -29,7 +29,6 @@ #include "py/runtime.h" #include "shared/timeutils/timeutils.h" #include "shared-bindings/rtc/RTC.h" -#include "supervisor/shared/translate/translate.h" #include "peripherals/rtc.h" diff --git a/ports/stm/common-hal/sdioio/SDCard.c b/ports/stm/common-hal/sdioio/SDCard.c index 5297c47748..a1b22a2047 100644 --- a/ports/stm/common-hal/sdioio/SDCard.c +++ b/ports/stm/common-hal/sdioio/SDCard.c @@ -32,7 +32,6 @@ #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/util.h" #include "supervisor/board.h" -#include "supervisor/shared/translate/translate.h" #include "common-hal/microcontroller/Pin.h" #include "shared-bindings/microcontroller/Pin.h" diff --git a/ports/stm/peripherals/timers.c b/ports/stm/peripherals/timers.c index 20b6bcc759..47fb33c59e 100644 --- a/ports/stm/peripherals/timers.c +++ b/ports/stm/peripherals/timers.c @@ -28,7 +28,6 @@ #include "py/mpconfig.h" #include "py/obj.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/microcontroller/Pin.h" diff --git a/ports/unix/.gitignore b/ports/unix/.gitignore deleted file mode 100644 index 3ca8f6cb27..0000000000 --- a/ports/unix/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -micropython -micropython-* -*.gcov diff --git a/ports/unix/Makefile b/ports/unix/Makefile index a17e46b25e..51022ece72 100644 --- a/ports/unix/Makefile +++ b/ports/unix/Makefile @@ -1,14 +1,21 @@ -# Select the variant to build for. +# Select the variant to build for: +ifdef VARIANT_DIR +# Custom variant path - remove trailing slash and get the final component of +# the path as the variant name. +VARIANT ?= $(notdir $(VARIANT_DIR:/=)) +else +# If not given on the command line, then default to standard. VARIANT ?= standard - -# If the build directory is not given, make it reflect the variant name. -BUILD ?= build-$(VARIANT) - VARIANT_DIR ?= variants/$(VARIANT) +endif + ifeq ($(wildcard $(VARIANT_DIR)/.),) $(error Invalid VARIANT specified: $(VARIANT_DIR)) endif +# If the build directory is not given, make it reflect the variant name. +BUILD ?= build-$(VARIANT) + include ../../py/mkenv.mk -include mpconfigport.mk include $(VARIANT_DIR)/mpconfigvariant.mk @@ -30,7 +37,7 @@ UNAME_S := $(shell uname -s) include $(TOP)/py/py.mk include $(TOP)/extmod/extmod.mk -GIT_SUBMODULES += lib/axtls lib/berkeley-db-1.xx lib/libffi +GIT_SUBMODULES += lib/berkeley-db-1.xx INC += -I. INC += -I$(TOP) @@ -38,8 +45,8 @@ INC += -I$(BUILD) # compiler settings CWARN = -Wall -Werror -CWARN += -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -Wpointer-arith -Wdouble-promotion -Wfloat-conversion -CFLAGS += $(INC) $(CWARN) -std=gnu11 -DUNIX $(CFLAGS_MOD) $(COPT) -I$(VARIANT_DIR) $(CFLAGS_EXTRA) +CWARN += -Wextra -Wno-unused-parameter -Wpointer-arith -Wdouble-promotion -Wfloat-conversion +CFLAGS += $(INC) $(CWARN) -std=gnu99 -DUNIX $(COPT) -I$(VARIANT_DIR) $(CFLAGS_EXTRA) # Debugging/Optimization ifdef DEBUG @@ -110,60 +117,39 @@ ifeq ($(MICROPY_FORCE_32BIT),1) # starting with linux-libc-dev:i386 ifeq ($(MICROPY_PY_FFI),1) ifeq ($(UNAME_S),Linux) -CFLAGS_MOD += -I/usr/include/i686-linux-gnu +CFLAGS += -I/usr/include/i686-linux-gnu endif endif endif ifeq ($(MICROPY_USE_READLINE),1) INC += -I$(TOP)/shared/readline -CFLAGS_MOD += -DMICROPY_USE_READLINE=1 +CFLAGS += -DMICROPY_USE_READLINE=1 SHARED_SRC_C_EXTRA += readline/readline.c endif ifeq ($(MICROPY_PY_TERMIOS),1) -CFLAGS_MOD += -DMICROPY_PY_TERMIOS=1 -SRC_MOD += modtermios.c +CFLAGS += -DMICROPY_PY_TERMIOS=1 endif ifeq ($(MICROPY_PY_SOCKET),1) -CFLAGS_MOD += -DMICROPY_PY_SOCKET=1 -SRC_MOD += modusocket.c +CFLAGS += -DMICROPY_PY_SOCKET=1 endif ifeq ($(MICROPY_PY_THREAD),1) -CFLAGS_MOD += -DMICROPY_PY_THREAD=1 -DMICROPY_PY_THREAD_GIL=0 -LDFLAGS_MOD += $(LIBPTHREAD) +CFLAGS += -DMICROPY_PY_THREAD=1 -DMICROPY_PY_THREAD_GIL=0 +LDFLAGS += $(LIBPTHREAD) +endif + +ifeq ($(MICROPY_PY_USSL),1) +ifeq ($(MICROPY_SSL_AXTLS),1) + +endif endif # If the variant enables it, enable modbluetooth. ifeq ($(MICROPY_PY_BLUETOOTH),1) - +ifeq ($(MICROPY_BLUETOOTH_BTSTACK),1) HAVE_LIBUSB := $(shell (which pkg-config > /dev/null && pkg-config --exists libusb-1.0) 2>/dev/null && echo '1') -# Only one stack can be enabled. -ifeq ($(MICROPY_BLUETOOTH_NIMBLE),1) -ifeq ($(MICROPY_BLUETOOTH_BTSTACK),1) -$(error Cannot enable both NimBLE and BTstack at the same time) -endif -endif - -# Default to btstack, but a variant (or make command line) can set NimBLE -# explicitly (which is always via H4 UART). -ifneq ($(MICROPY_BLUETOOTH_NIMBLE),1) -ifneq ($(MICROPY_BLUETOOTH_BTSTACK),1) -MICROPY_BLUETOOTH_BTSTACK ?= 1 -endif -endif - -CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH=1 -CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE=1 - -ifeq ($(MICROPY_BLUETOOTH_BTSTACK),1) - # Figure out which BTstack transport to use. -ifeq ($(MICROPY_BLUETOOTH_BTSTACK_H4),1) -ifeq ($(MICROPY_BLUETOOTH_BTSTACK_USB),1) -$(error Cannot enable BTstack support for USB and H4 UART at the same time) -endif -else ifeq ($(HAVE_LIBUSB),1) # Default to btstack-over-usb. MICROPY_BLUETOOTH_BTSTACK_USB ?= 1 @@ -171,51 +157,40 @@ else # Fallback to HCI controller via a H4 UART (e.g. Zephyr on nRF) over a /dev/tty serial port. MICROPY_BLUETOOTH_BTSTACK_H4 ?= 1 endif + +SRC_BTSTACK_C += lib/btstack/platform/embedded/btstack_run_loop_embedded.c endif - -# BTstack is enabled. -GIT_SUBMODULES += lib/btstack -include $(TOP)/extmod/btstack/btstack.mk -SRC_BTSTACK += lib/btstack/platform/embedded/btstack_run_loop_embedded.c - -else - -# NimBLE is enabled. -GIT_SUBMODULES += lib/mynewt-nimble -CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS=1 -include $(TOP)/extmod/nimble/nimble.mk - -endif - endif ifeq ($(MICROPY_PY_FFI),1) ifeq ($(MICROPY_STANDALONE),1) -LIBFFI_CFLAGS_MOD := -I$(shell ls -1d $(BUILD)/lib/libffi/out/lib/libffi-*/include) +# Build libffi from source. +GIT_SUBMODULES += lib/libffi +DEPLIBS += libffi +LIBFFI_CFLAGS := -I$(shell ls -1d $(BUILD)/lib/libffi/out/lib/libffi-*/include) ifeq ($(MICROPY_FORCE_32BIT),1) - LIBFFI_LDFLAGS_MOD = $(BUILD)/lib/libffi/out/lib32/libffi.a + LIBFFI_LDFLAGS = $(BUILD)/lib/libffi/out/lib32/libffi.a else - LIBFFI_LDFLAGS_MOD = $(BUILD)/lib/libffi/out/lib/libffi.a + LIBFFI_LDFLAGS = $(BUILD)/lib/libffi/out/lib/libffi.a endif else -LIBFFI_CFLAGS_MOD := $(shell pkg-config --cflags libffi) -LIBFFI_LDFLAGS_MOD := $(shell pkg-config --libs libffi) +# Use system version of libffi. +LIBFFI_CFLAGS := $(shell pkg-config --cflags libffi) +LIBFFI_LDFLAGS := $(shell pkg-config --libs libffi) endif ifeq ($(UNAME_S),Linux) -LIBFFI_LDFLAGS_MOD += -ldl +LIBFFI_LDFLAGS += -ldl endif -CFLAGS_MOD += $(LIBFFI_CFLAGS_MOD) -DMICROPY_PY_FFI=1 -LDFLAGS_MOD += $(LIBFFI_LDFLAGS_MOD) -SRC_MOD += modffi.c +CFLAGS += $(LIBFFI_CFLAGS) -DMICROPY_PY_FFI=1 +LDFLAGS += $(LIBFFI_LDFLAGS) endif ifeq ($(MICROPY_PY_JNI),1) # Path for 64-bit OpenJDK, should be adjusted for other JDKs -CFLAGS_MOD += -I/usr/lib/jvm/java-7-openjdk-amd64/include -DMICROPY_PY_JNI=1 -SRC_MOD += modjni.c +CFLAGS += -I/usr/lib/jvm/java-7-openjdk-amd64/include -DMICROPY_PY_JNI=1 endif # source files @@ -243,17 +218,14 @@ SHARED_SRC_C += $(addprefix shared/,\ ) SRC_CXX += \ - $(SRC_MOD_CXX) OBJ = $(PY_O) OBJ += $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_CXX:.cpp=.o)) OBJ += $(addprefix $(BUILD)/, $(SHARED_SRC_C:.c=.o)) -OBJ += $(addprefix $(BUILD)/, $(EXTMOD_SRC_C:.c=.o)) -OBJ += $(addprefix $(BUILD)/, $(LIB_SRC_C:.c=.o)) # List of sources for qstr extraction -SRC_QSTR += $(SRC_C) $(SRC_CXX) $(SHARED_SRC_C) $(EXTMOD_SRC_C) +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 += @@ -267,13 +239,7 @@ CFLAGS += -DMPZ_DIG_SIZE=16 # force 16 bits to work on both 32 and 64 bit archs CFLAGS += -DMICROPY_MODULE_FROZEN_STR endif -HASCPP17 = $(shell expr `$(CC) -dumpversion | cut -f1 -d.` \>= 7) -ifeq ($(HASCPP17), 1) - CXXFLAGS += -std=c++17 -else - CXXFLAGS += -std=c++11 -endif -CXXFLAGS += $(filter-out -Wmissing-prototypes -Wold-style-definition -std=gnu99 -std=gnu11,$(CFLAGS) $(CXXFLAGS_MOD)) +CXXFLAGS += $(filter-out -Wmissing-prototypes -Wold-style-definition -std=gnu99,$(CFLAGS) $(CXXFLAGS_MOD)) ifeq ($(MICROPY_FORCE_32BIT),1) RUN_TESTS_MPY_CROSS_FLAGS = --mpy-cross-flags='-march=x86' @@ -294,40 +260,23 @@ include $(TOP)/py/mkrules.mk .PHONY: test test_full -TEST_EXTRA ?= -test: $(PROG) $(TOP)/tests/run-tests.py +test: $(BUILD)/$(PROG) $(TOP)/tests/run-tests.py $(eval DIRNAME=ports/$(notdir $(CURDIR))) - cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(PROG) ./run-tests.py $(TEST_EXTRA) + cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py -test_full: $(PROG) $(TOP)/tests/run-tests.py +test_full: $(BUILD)/$(PROG) $(TOP)/tests/run-tests.py $(eval DIRNAME=ports/$(notdir $(CURDIR))) - cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(PROG) ./run-tests.py - cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(PROG) ./run-tests.py -d thread - cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(PROG) ./run-tests.py --emit native - cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(PROG) ./run-tests.py --via-mpy $(RUN_TESTS_MPY_CROSS_FLAGS) -d basics float micropython - cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(PROG) ./run-tests.py --via-mpy $(RUN_TESTS_MPY_CROSS_FLAGS) --emit native -d basics float micropython - cat $(TOP)/tests/basics/0prelim.py | ./$(PROG) | grep -q 'abc' - -.PHONY: print-failures clean-failures -print-failures clean-failures: - ../../tests/run-tests.py --$@ + cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py + cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py -d thread + cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py --emit native + cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py --via-mpy $(RUN_TESTS_MPY_CROSS_FLAGS) -d basics float micropython + cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py --via-mpy $(RUN_TESTS_MPY_CROSS_FLAGS) --emit native -d basics float micropython + cat $(TOP)/tests/basics/0prelim.py | ./$(BUILD)/$(PROG) | grep -q 'abc' test_gcov: test_full gcov -o $(BUILD)/py $(TOP)/py/*.c gcov -o $(BUILD)/extmod $(TOP)/extmod/*.c -# build an interpreter for fuzzing -fuzz: - $(MAKE) \ - CC=afl-clang-fast DEBUG=1 \ - CFLAGS_EXTRA='$(CFLAGS_EXTRA) -ffunction-sections' \ - LDFLAGS_EXTRA='$(LDFLAGS_EXTRA)' \ - BUILD=build-fuzz PROG=micropython_fuzz - -fuzz_clean: - $(MAKE) V=2 BUILD=build-fuzz PROG=micropython_fuzz clean - - # Value of configure's --host= option (required for cross-compilation). # Deduce it from CROSS_COMPILE by default, but can be overridden. ifneq ($(CROSS_COMPILE),) @@ -336,7 +285,7 @@ else CROSS_COMPILE_HOST = endif -deplibs: libffi axtls +deplibs: $(DEPLIBS) libffi: $(BUILD)/lib/libffi/include/ffi.h @@ -347,23 +296,15 @@ $(TOP)/lib/libffi/configure: $(TOP)/lib/libffi/autogen.sh # docs and depending on makeinfo $(BUILD)/lib/libffi/include/ffi.h: $(TOP)/lib/libffi/configure mkdir -p $(BUILD)/lib/libffi; cd $(BUILD)/lib/libffi; \ - $(abspath $(TOP))/lib/libffi/configure $(CROSS_COMPILE_HOST) --prefix=$$PWD/out --disable-structs CC="$(CC)" CXX="$(CXX)" LD="$(LD)" CFLAGS="-Os -fomit-frame-pointer -fstrict-aliasing -ffast-math -fno-exceptions"; \ + $(abspath $(TOP))/lib/libffi/configure $(CROSS_COMPILE_HOST) --prefix=$$PWD/out --disable-shared --disable-structs CC="$(CC)" CXX="$(CXX)" LD="$(LD)" CFLAGS="-Os -fomit-frame-pointer -fstrict-aliasing -ffast-math -fno-exceptions"; \ $(MAKE) install-exec-recursive; $(MAKE) -C include install-data-am -axtls: $(TOP)/lib/axtls/README - -$(TOP)/lib/axtls/README: - @echo "You cloned without --recursive, fetching submodules for you." - (cd $(TOP); git submodule update --init --recursive) - PREFIX = /usr/local BINDIR = $(DESTDIR)$(PREFIX)/bin -install: $(PROG) +install: $(BUILD)/$(PROG) install -d $(BINDIR) - install $(PROG) $(BINDIR)/$(PROG) + install $(BUILD)/$(PROG) $(BINDIR)/$(PROG) uninstall: -rm $(BINDIR)/$(PROG) - -$(BUILD)/supervisor/shared/translate/translate.o: $(HEADER_BUILD)/qstrdefs.generated.h $(HEADER_BUILD)/compressed_translations.generated.h diff --git a/ports/unix/README.md b/ports/unix/README.md new file mode 100644 index 0000000000..a3a0dba75a --- /dev/null +++ b/ports/unix/README.md @@ -0,0 +1,74 @@ +The Unix version +---------------- + +The "unix" port requires a standard Unix-like environment with gcc and GNU +make. This includes Linux, BSD, macOS, and Windows Subsystem for Linux. The +x86 and x64 architectures are supported (i.e. x86 32- and 64-bit), as well as +ARM and MIPS. Making a full-featured port to another architecture requires +writing some assembly code for the exception handling and garbage collection. +Alternatively, a fallback implementation based on setjmp/longjmp can be used. + +To build (see section below for required dependencies): + + $ cd ports/unix + $ make submodules + $ make + +Then to give it a try: + + $ ./build-standard/micropython + >>> list(5 * x + y for x in range(10) for y in [4, 2, 1]) + +Use `CTRL-D` (i.e. EOF) to exit the shell. + +Learn about command-line options (in particular, how to increase heap size +which may be needed for larger applications): + + $ ./build-standard/micropython -h + +To run the complete testsuite, use: + + $ make test + +The Unix port comes with a built-in package manager called `mip`, e.g.: + + $ ./build-standard/micropython -m mip install hmac + +or + + $ ./build-standard/micropython + >>> import mip + >>> mip.install("hmac") + +Browse available modules at [micropython-lib] +(https://github.com/micropython/micropython-lib). See +[Package management](https://docs.micropython.org/en/latest/reference/packages.html) +for more information about `mip`. + +External dependencies +--------------------- + +The `libffi` library and `pkg-config` tool are required. On Debian/Ubuntu/Mint +derivative Linux distros, install `build-essential`(includes toolchain and +make), `libffi-dev`, and `pkg-config` packages. + +Other dependencies can be built together with MicroPython. This may +be required to enable extra features or capabilities, and in recent +versions of MicroPython, these may be enabled by default. To build +these additional dependencies, in the unix port directory first execute: + + $ make submodules + +This will fetch all the relevant git submodules (sub repositories) that +the port needs. Use the same command to get the latest versions of +submodules as they are updated from time to time. After that execute: + + $ make deplibs + +This will build all available dependencies (regardless whether they are used +or not). If you intend to build MicroPython with additional options +(like cross-compiling), the same set of options should be passed to `make +deplibs`. To actually enable/disable use of dependencies, edit the +`ports/unix/mpconfigport.mk` file, which has inline descriptions of the +options. For example, to build the SSL module, `MICROPY_PY_USSL` should be +set to 1. diff --git a/ports/unix/alloc.c b/ports/unix/alloc.c index 7fe7b4dba4..e9cf521583 100644 --- a/ports/unix/alloc.c +++ b/ports/unix/alloc.c @@ -104,4 +104,6 @@ void ffi_closure_free(void *ptr) { } #endif +MP_REGISTER_ROOT_POINTER(void *mmap_region_head); + #endif // MICROPY_EMIT_NATIVE || (MICROPY_PY_FFI && MICROPY_FORCE_PLAT_ALLOC_EXEC) diff --git a/ports/unix/coverage.c b/ports/unix/coverage.c index d2f730a602..f545674cee 100644 --- a/ports/unix/coverage.c +++ b/ports/unix/coverage.c @@ -1,4 +1,5 @@ #include +#include #include #include "py/obj.h" @@ -101,20 +102,18 @@ STATIC const mp_rom_map_elem_t rawfile_locals_dict_table[] = { STATIC MP_DEFINE_CONST_DICT(rawfile_locals_dict, rawfile_locals_dict_table); STATIC const mp_stream_p_t fileio_stream_p = { - MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream) .read = stest_read, .write = stest_write, .ioctl = stest_ioctl, }; -STATIC const mp_obj_type_t mp_type_stest_fileio = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .locals_dict = (mp_obj_dict_t *)&rawfile_locals_dict, - MP_TYPE_EXTENDED_FIELDS( - .protocol = &fileio_stream_p, - ), -}; +STATIC MP_DEFINE_CONST_OBJ_TYPE( + mp_type_stest_fileio, + MP_QSTR_stest_fileio, + MP_TYPE_FLAG_NONE, + protocol, &fileio_stream_p, + locals_dict, &rawfile_locals_dict + ); // stream read returns non-blocking error STATIC mp_uint_t stest_read2(mp_obj_t o_in, void *buf, mp_uint_t size, int *errcode) { @@ -132,20 +131,18 @@ STATIC const mp_rom_map_elem_t rawfile_locals_dict_table2[] = { STATIC MP_DEFINE_CONST_DICT(rawfile_locals_dict2, rawfile_locals_dict_table2); STATIC const mp_stream_p_t textio_stream_p2 = { - MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream) .read = stest_read2, .write = NULL, .is_text = true, }; -STATIC const mp_obj_type_t mp_type_stest_textio2 = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .locals_dict = (mp_obj_dict_t *)&rawfile_locals_dict2, - MP_TYPE_EXTENDED_FIELDS( - .protocol = &textio_stream_p2, - ), -}; +STATIC MP_DEFINE_CONST_OBJ_TYPE( + mp_type_stest_textio2, + MP_QSTR_stest_textio2, + MP_TYPE_FLAG_NONE, + protocol, &textio_stream_p2, + locals_dict, &rawfile_locals_dict2 + ); // str/bytes objects without a valid hash STATIC const mp_obj_str_t str_no_hash_obj = {{&mp_type_str}, 0, 10, (const byte *)"0123456789"}; @@ -228,6 +225,42 @@ STATIC mp_obj_t extra_coverage(void) { mp_printf(&mp_plat_print, "%p\n", gc_nbytes(NULL)); } + // GC initialisation and allocation stress test, to check the logic behind ALLOC_TABLE_GAP_BYTE + // (the following test should fail when ALLOC_TABLE_GAP_BYTE=0) + { + mp_printf(&mp_plat_print, "# GC part 2\n"); + + // check the GC is unlocked and save its state + assert(MP_STATE_THREAD(gc_lock_depth) == 0); + mp_state_mem_t mp_state_mem_orig = mp_state_ctx.mem; + + // perform the test + unsigned heap_size = 64 * MICROPY_BYTES_PER_GC_BLOCK; + for (unsigned j = 0; j < 256 * MP_BYTES_PER_OBJ_WORD; ++j) { + char *heap = calloc(heap_size, 1); + gc_init(heap, heap + heap_size); + + m_malloc(MICROPY_BYTES_PER_GC_BLOCK); + void *o = gc_alloc(MICROPY_BYTES_PER_GC_BLOCK, GC_ALLOC_FLAG_HAS_FINALISER); + ((mp_obj_base_t *)o)->type = NULL; // ensure type is cleared so GC doesn't look for finaliser + for (unsigned i = 0; i < heap_size / MICROPY_BYTES_PER_GC_BLOCK; ++i) { + void *p = m_malloc_maybe(MICROPY_BYTES_PER_GC_BLOCK); + if (!p) { + break; + } + *(void **)p = o; + o = p; + } + gc_collect(); + free(heap); + heap_size += MICROPY_BYTES_PER_GC_BLOCK / 16; + } + mp_printf(&mp_plat_print, "pass\n"); + + // restore the GC state (the original heap) + mp_state_ctx.mem = mp_state_mem_orig; + } + // tracked allocation { #define NUM_PTRS (8) @@ -506,7 +539,7 @@ STATIC mp_obj_t extra_coverage(void) { { mp_printf(&mp_plat_print, "# VM\n"); - // call mp_execute_bytecode with invalid bytecode (should raise NotImplementedError) + // call mp_execute_bytecode with invalide bytecode (should raise NotImplementedError) mp_module_context_t context; mp_obj_fun_bc_t fun_bc; fun_bc.context = &context; @@ -579,49 +612,47 @@ STATIC mp_obj_t extra_coverage(void) { // ringbuf { - #define RINGBUF_SIZE 99 - - byte buf[RINGBUF_SIZE]; - ringbuf_t ringbuf; - ringbuf_init(&ringbuf, &buf[0], sizeof(buf)); + byte buf[100]; + ringbuf_t ringbuf = {buf, sizeof(buf), 0, 0}; mp_printf(&mp_plat_print, "# ringbuf\n"); // Single-byte put/get with empty ringbuf. - mp_printf(&mp_plat_print, "%d %d\n", ringbuf_num_empty(&ringbuf), ringbuf_num_filled(&ringbuf)); + mp_printf(&mp_plat_print, "%d %d\n", ringbuf_free(&ringbuf), ringbuf_avail(&ringbuf)); ringbuf_put(&ringbuf, 22); - mp_printf(&mp_plat_print, "%d %d\n", ringbuf_num_empty(&ringbuf), ringbuf_num_filled(&ringbuf)); + mp_printf(&mp_plat_print, "%d %d\n", ringbuf_free(&ringbuf), ringbuf_avail(&ringbuf)); mp_printf(&mp_plat_print, "%d\n", ringbuf_get(&ringbuf)); - mp_printf(&mp_plat_print, "%d %d\n", ringbuf_num_empty(&ringbuf), ringbuf_num_filled(&ringbuf)); + mp_printf(&mp_plat_print, "%d %d\n", ringbuf_free(&ringbuf), ringbuf_avail(&ringbuf)); // Two-byte put/get with empty ringbuf. ringbuf_put16(&ringbuf, 0xaa55); - mp_printf(&mp_plat_print, "%d %d\n", ringbuf_num_empty(&ringbuf), ringbuf_num_filled(&ringbuf)); + mp_printf(&mp_plat_print, "%d %d\n", ringbuf_free(&ringbuf), ringbuf_avail(&ringbuf)); mp_printf(&mp_plat_print, "%04x\n", ringbuf_get16(&ringbuf)); - mp_printf(&mp_plat_print, "%d %d\n", ringbuf_num_empty(&ringbuf), ringbuf_num_filled(&ringbuf)); + mp_printf(&mp_plat_print, "%d %d\n", ringbuf_free(&ringbuf), ringbuf_avail(&ringbuf)); // Two-byte put with full ringbuf. - for (int i = 0; i < RINGBUF_SIZE; ++i) { + for (int i = 0; i < 99; ++i) { ringbuf_put(&ringbuf, i); } - mp_printf(&mp_plat_print, "%d %d\n", ringbuf_num_empty(&ringbuf), ringbuf_num_filled(&ringbuf)); + mp_printf(&mp_plat_print, "%d %d\n", ringbuf_free(&ringbuf), ringbuf_avail(&ringbuf)); mp_printf(&mp_plat_print, "%d\n", ringbuf_put16(&ringbuf, 0x11bb)); // Two-byte put with one byte free. ringbuf_get(&ringbuf); - mp_printf(&mp_plat_print, "%d %d\n", ringbuf_num_empty(&ringbuf), ringbuf_num_filled(&ringbuf)); + mp_printf(&mp_plat_print, "%d %d\n", ringbuf_free(&ringbuf), ringbuf_avail(&ringbuf)); mp_printf(&mp_plat_print, "%d\n", ringbuf_put16(&ringbuf, 0x3377)); ringbuf_get(&ringbuf); - mp_printf(&mp_plat_print, "%d %d\n", ringbuf_num_empty(&ringbuf), ringbuf_num_filled(&ringbuf)); + mp_printf(&mp_plat_print, "%d %d\n", ringbuf_free(&ringbuf), ringbuf_avail(&ringbuf)); mp_printf(&mp_plat_print, "%d\n", ringbuf_put16(&ringbuf, 0xcc99)); - for (int i = 0; i < RINGBUF_SIZE - 2; ++i) { + for (int i = 0; i < 97; ++i) { ringbuf_get(&ringbuf); } mp_printf(&mp_plat_print, "%04x\n", ringbuf_get16(&ringbuf)); - mp_printf(&mp_plat_print, "%d %d\n", ringbuf_num_empty(&ringbuf), ringbuf_num_filled(&ringbuf)); + mp_printf(&mp_plat_print, "%d %d\n", ringbuf_free(&ringbuf), ringbuf_avail(&ringbuf)); // Two-byte put with wrap around on first byte: - ringbuf_clear(&ringbuf); - for (int i = 0; i < RINGBUF_SIZE; ++i) { + ringbuf.iput = 0; + ringbuf.iget = 0; + for (int i = 0; i < 99; ++i) { ringbuf_put(&ringbuf, i); ringbuf_get(&ringbuf); } @@ -629,8 +660,9 @@ STATIC mp_obj_t extra_coverage(void) { mp_printf(&mp_plat_print, "%04x\n", ringbuf_get16(&ringbuf)); // Two-byte put with wrap around on second byte: - ringbuf_clear(&ringbuf); - for (int i = 0; i < RINGBUF_SIZE - 1; ++i) { + ringbuf.iput = 0; + ringbuf.iget = 0; + for (int i = 0; i < 98; ++i) { ringbuf_put(&ringbuf, i); ringbuf_get(&ringbuf); } @@ -638,11 +670,13 @@ STATIC mp_obj_t extra_coverage(void) { mp_printf(&mp_plat_print, "%04x\n", ringbuf_get16(&ringbuf)); // Two-byte get from empty ringbuf. - ringbuf_clear(&ringbuf); + ringbuf.iput = 0; + ringbuf.iget = 0; mp_printf(&mp_plat_print, "%d\n", ringbuf_get16(&ringbuf)); // Two-byte get from ringbuf with one byte available. - ringbuf_clear(&ringbuf); + ringbuf.iput = 0; + ringbuf.iget = 0; ringbuf_put(&ringbuf, 0xaa); mp_printf(&mp_plat_print, "%d\n", ringbuf_get16(&ringbuf)); } diff --git a/ports/unix/gccollect.c b/ports/unix/gccollect.c index 79b17663c3..94c9c61ea4 100644 --- a/ports/unix/gccollect.c +++ b/ports/unix/gccollect.c @@ -34,8 +34,6 @@ #if MICROPY_ENABLE_GC void gc_collect(void) { - // gc_dump_info(); - gc_collect_start(); gc_helper_collect_regs_and_stack(); #if MICROPY_PY_THREAD @@ -45,9 +43,6 @@ void gc_collect(void) { mp_unix_mark_exec(); #endif gc_collect_end(); - - // printf("-----\n"); - // gc_dump_info(); } #endif // MICROPY_ENABLE_GC diff --git a/ports/unix/main.c b/ports/unix/main.c index e2ea429439..87f58014ec 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -43,6 +43,7 @@ #include "py/builtin.h" #include "py/repl.h" #include "py/gc.h" +#include "py/objstr.h" #include "py/stackctrl.h" #include "py/mphal.h" #include "py/mpthread.h" @@ -63,6 +64,11 @@ STATIC uint emit_opt = MP_EMIT_OPT_NONE; long heap_size = 1024 * 1024 * (sizeof(mp_uint_t) / 4); #endif +// Number of heaps to assign by default if MICROPY_GC_SPLIT_HEAP=1 +#ifndef MICROPY_GC_SPLIT_HEAP_N_HEAPS +#define MICROPY_GC_SPLIT_HEAP_N_HEAPS (1) +#endif + STATIC void stderr_print_strn(void *env, const char *str, size_t len) { (void)env; ssize_t ret; @@ -432,6 +438,17 @@ STATIC void set_sys_argv(char *argv[], int argc, int start_arg) { } } +#if MICROPY_PY_SYS_EXECUTABLE +extern mp_obj_str_t mp_sys_executable_obj; +STATIC char executable_path[MICROPY_ALLOC_PATH_MAX]; + +STATIC void sys_set_excecutable(char *argv0) { + if (realpath(argv0, executable_path)) { + mp_obj_str_set_data(&mp_sys_executable_obj, (byte *)executable_path, strlen(executable_path)); + } +} +#endif + #ifdef _WIN32 #define PATHLIST_SEP_CHAR ';' #else @@ -479,8 +496,22 @@ MP_NOINLINE int main_(int argc, char **argv) { pre_process_options(argc, argv); #if MICROPY_ENABLE_GC + #if !MICROPY_GC_SPLIT_HEAP char *heap = malloc(heap_size); gc_init(heap, heap + heap_size); + #else + assert(MICROPY_GC_SPLIT_HEAP_N_HEAPS > 0); + char *heaps[MICROPY_GC_SPLIT_HEAP_N_HEAPS]; + long multi_heap_size = heap_size / MICROPY_GC_SPLIT_HEAP_N_HEAPS; + for (size_t i = 0; i < MICROPY_GC_SPLIT_HEAP_N_HEAPS; i++) { + heaps[i] = malloc(multi_heap_size); + if (i == 0) { + gc_init(heaps[i], heaps[i] + multi_heap_size); + } else { + gc_add(heaps[i], heaps[i] + multi_heap_size); + } + } + #endif #endif #if MICROPY_ENABLE_PYSTACK @@ -501,7 +532,7 @@ MP_NOINLINE int main_(int argc, char **argv) { { // Mount the host FS at the root of our internal VFS mp_obj_t args[2] = { - mp_type_vfs_posix.make_new(&mp_type_vfs_posix, 0, 0, NULL), + MP_OBJ_TYPE_GET_SLOT(&mp_type_vfs_posix, make_new)(&mp_type_vfs_posix, 0, 0, NULL), MP_OBJ_NEW_QSTR(MP_QSTR__slash_), }; mp_vfs_mount(2, args, (mp_map_t *)&mp_const_empty_map); @@ -542,7 +573,7 @@ MP_NOINLINE int main_(int argc, char **argv) { vstr_init(&vstr, home_l + (p1 - p - 1) + 1); vstr_add_strn(&vstr, home, home_l); vstr_add_strn(&vstr, p + 1, p1 - p - 1); - path_items[i] = mp_obj_new_str_from_vstr(&mp_type_str, &vstr); + path_items[i] = mp_obj_new_str_from_vstr(&vstr); } else { path_items[i] = mp_obj_new_str_via_qstr(p, p1 - p); } @@ -581,6 +612,10 @@ MP_NOINLINE int main_(int argc, char **argv) { printf(" peak %d\n", m_get_peak_bytes_allocated()); */ + #if MICROPY_PY_SYS_EXECUTABLE + sys_set_excecutable(argv[0]); + #endif + const int NOTHING_EXECUTED = -2; int ret = NOTHING_EXECUTED; bool inspect = false; @@ -638,7 +673,7 @@ MP_NOINLINE int main_(int argc, char **argv) { vstr_init(&vstr, len + sizeof(".__main__")); vstr_add_strn(&vstr, argv[a + 1], len); vstr_add_strn(&vstr, ".__main__", sizeof(".__main__") - 1); - import_args[0] = mp_obj_new_str_from_vstr(&mp_type_str, &vstr); + import_args[0] = mp_obj_new_str_from_vstr(&vstr); goto reimport; } @@ -666,6 +701,7 @@ MP_NOINLINE int main_(int argc, char **argv) { char *basedir = realpath(argv[a], pathbuf); if (basedir == NULL) { mp_printf(&mp_stderr_print, "%s: can't open file '%s': [Errno %d] %s\n", argv[0], argv[a], errno, strerror(errno)); + free(pathbuf); // CPython exits with 2 in such case ret = 2; break; @@ -731,7 +767,13 @@ MP_NOINLINE int main_(int argc, char **argv) { #if MICROPY_ENABLE_GC && !defined(NDEBUG) // We don't really need to free memory since we are about to exit the // process, but doing so helps to find memory leaks. + #if !MICROPY_GC_SPLIT_HEAP free(heap); + #else + for (size_t i = 0; i < MICROPY_GC_SPLIT_HEAP_N_HEAPS; i++) { + free(heaps[i]); + } + #endif #endif // printf("total bytes = %d\n", m_get_total_bytes_allocated()); @@ -739,6 +781,9 @@ MP_NOINLINE int main_(int argc, char **argv) { } void nlr_jump_fail(void *val) { + #if MICROPY_USE_READLINE == 1 + mp_hal_stdio_mode_orig(); + #endif fprintf(stderr, "FATAL: uncaught NLR %p\n", val); exit(1); } diff --git a/ports/unix/variants/fast/mpconfigvariant.h b/ports/unix/mbedtls/mbedtls_config.h similarity index 72% rename from ports/unix/variants/fast/mpconfigvariant.h rename to ports/unix/mbedtls/mbedtls_config.h index 8a531b056a..c8ffab0832 100644 --- a/ports/unix/variants/fast/mpconfigvariant.h +++ b/ports/unix/mbedtls/mbedtls_config.h @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2018-2019 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 @@ -23,12 +23,17 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#ifndef MICROPY_INCLUDED_MBEDTLS_CONFIG_H +#define MICROPY_INCLUDED_MBEDTLS_CONFIG_H -// This config file is intended to configure artificially fast uPy build for -// synthetic benchmarking, at the expense of features supported and memory -// usage. This config is not intended to be used in production. +// Set mbedtls configuration +#define MBEDTLS_CIPHER_MODE_CTR // needed for MICROPY_PY_UCRYPTOLIB_CTR -#define MICROPY_PY___FILE__ (0) -// 91 is a magic number proposed by @dpgeorge, which make pystone run ~ at tie -// with CPython 3.4. -#define MICROPY_MODULE_DICT_SIZE (91) +// Enable mbedtls modules +#define MBEDTLS_HAVEGE_C +#define MBEDTLS_TIMING_C + +// Include common mbedtls configuration. +#include "extmod/mbedtls/mbedtls_config_common.h" + +#endif /* MICROPY_INCLUDED_MBEDTLS_CONFIG_H */ diff --git a/ports/unix/modffi.c b/ports/unix/modffi.c index 468006ba5d..bc585f8647 100644 --- a/ports/unix/modffi.c +++ b/ports/unix/modffi.c @@ -25,20 +25,19 @@ * THE SOFTWARE. */ -#include -#include -#include -#include -#include -#include - #include "py/runtime.h" #include "py/binary.h" #include "py/mperrno.h" #include "py/objint.h" #include "py/gc.h" -#include "supervisor/shared/translate/translate.h" +#if MICROPY_PY_FFI + +#include +#include +#include +#include +#include /* * modffi uses character codes to encode a value type, based on "struct" @@ -352,7 +351,7 @@ STATIC mp_obj_t mod_ffi_callback(size_t n_args, const mp_obj_t *pos_args, mp_map int res = ffi_prep_cif(&o->cif, FFI_DEFAULT_ABI, nparams, char2ffi_type(*rettype), o->params); if (res != FFI_OK) { - mp_raise_ValueError(MP_ERROR_TEXT("Error in ffi_prep_cif")); + mp_raise_ValueError(MP_ERROR_TEXT("error in ffi_prep_cif")); } res = ffi_prep_closure_loc(o->clo, &o->cif, @@ -421,13 +420,14 @@ STATIC const mp_rom_map_elem_t ffimod_locals_dict_table[] = { STATIC MP_DEFINE_CONST_DICT(ffimod_locals_dict, ffimod_locals_dict_table); -STATIC const mp_obj_type_t ffimod_type = { - { &mp_type_type }, - .name = MP_QSTR_ffimod, - .print = ffimod_print, - .make_new = ffimod_make_new, - .locals_dict = (mp_obj_dict_t *)&ffimod_locals_dict, -}; +STATIC MP_DEFINE_CONST_OBJ_TYPE( + ffimod_type, + MP_QSTR_ffimod, + MP_TYPE_FLAG_NONE, + make_new, ffimod_make_new, + print, ffimod_print, + locals_dict, &ffimod_locals_dict + ); // FFI function @@ -505,22 +505,19 @@ STATIC mp_obj_t ffifunc_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const } else if (mp_obj_is_str(a)) { const char *s = mp_obj_str_get_str(a); values[i].ffi = (ffi_arg)(intptr_t)s; - } else { - mp_getbuffer_fun_t get_buffer = mp_type_get_getbuffer_slot(((mp_obj_base_t *)MP_OBJ_TO_PTR(a))->type); - if (get_buffer != NULL) { - mp_obj_base_t *o = (mp_obj_base_t *)MP_OBJ_TO_PTR(a); - mp_buffer_info_t bufinfo; - int ret = get_buffer(MP_OBJ_FROM_PTR(o), &bufinfo, MP_BUFFER_READ); // TODO: MP_BUFFER_READ? - if (ret != 0) { - goto error; - } - values[i].ffi = (ffi_arg)(intptr_t)bufinfo.buf; - } else if (mp_obj_is_type(a, &fficallback_type)) { - mp_obj_fficallback_t *p = MP_OBJ_TO_PTR(a); - values[i].ffi = (ffi_arg)(intptr_t)p->func; - } else { + } else if (MP_OBJ_TYPE_HAS_SLOT(((mp_obj_base_t *)MP_OBJ_TO_PTR(a))->type, buffer)) { + mp_obj_base_t *o = (mp_obj_base_t *)MP_OBJ_TO_PTR(a); + mp_buffer_info_t bufinfo; + int ret = MP_OBJ_TYPE_GET_SLOT(o->type, buffer)(MP_OBJ_FROM_PTR(o), &bufinfo, MP_BUFFER_READ); // TODO: MP_BUFFER_READ? + if (ret != 0) { goto error; } + values[i].ffi = (ffi_arg)(intptr_t)bufinfo.buf; + } else if (mp_obj_is_type(a, &fficallback_type)) { + mp_obj_fficallback_t *p = MP_OBJ_TO_PTR(a); + values[i].ffi = (ffi_arg)(intptr_t)p->func; + } else { + goto error; } valueptrs[i] = &values[i]; } @@ -530,18 +527,16 @@ STATIC mp_obj_t ffifunc_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const return return_ffi_value(&retval, self->rettype); error: - mp_raise_TypeError(MP_ERROR_TEXT("Don't know how to pass object to native function")); + mp_raise_TypeError(MP_ERROR_TEXT("don't know how to pass object to native function")); } -STATIC const mp_obj_type_t ffifunc_type = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_ffifunc, - .print = ffifunc_print, - MP_TYPE_EXTENDED_FIELDS( - .call = ffifunc_call, - ), -}; +STATIC MP_DEFINE_CONST_OBJ_TYPE( + ffifunc_type, + MP_QSTR_ffifunc, + MP_TYPE_FLAG_NONE, + print, ffifunc_print, + call, ffifunc_call + ); // FFI callback for Python function @@ -562,12 +557,13 @@ STATIC const mp_rom_map_elem_t fficallback_locals_dict_table[] = { }; STATIC MP_DEFINE_CONST_DICT(fficallback_locals_dict, fficallback_locals_dict_table); -STATIC const mp_obj_type_t fficallback_type = { - { &mp_type_type }, - .name = MP_QSTR_fficallback, - .print = fficallback_print, - .locals_dict = (mp_obj_dict_t *)&fficallback_locals_dict -}; +STATIC MP_DEFINE_CONST_OBJ_TYPE( + fficallback_type, + MP_QSTR_fficallback, + MP_TYPE_FLAG_NONE, + print, fficallback_print, + locals_dict, &fficallback_locals_dict + ); // FFI variable @@ -598,21 +594,23 @@ STATIC const mp_rom_map_elem_t ffivar_locals_dict_table[] = { STATIC MP_DEFINE_CONST_DICT(ffivar_locals_dict, ffivar_locals_dict_table); -STATIC const mp_obj_type_t ffivar_type = { - { &mp_type_type }, - .name = MP_QSTR_ffivar, - .print = ffivar_print, - .locals_dict = (mp_obj_dict_t *)&ffivar_locals_dict, -}; +STATIC MP_DEFINE_CONST_OBJ_TYPE( + ffivar_type, + MP_QSTR_ffivar, + MP_TYPE_FLAG_NONE, + print, ffivar_print, + locals_dict, &ffivar_locals_dict + ); // Generic opaque storage object (unused) /* -STATIC const mp_obj_type_t opaque_type = { - { &mp_type_type }, - .name = MP_QSTR_opaqueval, -// .print = opaque_print, -}; +STATIC MP_DEFINE_CONST_OBJ_TYPE( + opaque_type, + MP_QSTR_opaqueval, + MP_TYPE_FLAG_NONE, + make_new, // .print = opaque_print, + ); */ STATIC mp_obj_t mod_ffi_open(size_t n_args, const mp_obj_t *args) { @@ -639,3 +637,7 @@ const mp_obj_module_t mp_module_ffi = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t *)&mp_module_ffi_globals, }; + +MP_REGISTER_MODULE(MP_QSTR_ffi, mp_module_ffi); + +#endif // MICROPY_PY_FFI diff --git a/ports/unix/modjni.c b/ports/unix/modjni.c index c86f30653c..10622f588f 100644 --- a/ports/unix/modjni.c +++ b/ports/unix/modjni.c @@ -24,19 +24,19 @@ * THE SOFTWARE. */ +#include "py/runtime.h" +#include "py/binary.h" + +#if MICROPY_PY_JNI + #include #include #include #include #include -#include "py/runtime.h" -#include "py/binary.h" - #include -#if MICROPY_PY_JNI - #define JJ(call, ...) (*env)->call(env, __VA_ARGS__) #define JJ1(call) (*env)->call(env) #define MATCH(s, static) (!strncmp(s, static, sizeof(static) - 1)) @@ -174,14 +174,15 @@ STATIC const mp_rom_map_elem_t jclass_locals_dict_table[] = { STATIC MP_DEFINE_CONST_DICT(jclass_locals_dict, jclass_locals_dict_table); -STATIC const mp_obj_type_t jclass_type = { - { &mp_type_type }, - .name = MP_QSTR_jclass, - .print = jclass_print, - .attr = jclass_attr, - .call = jclass_call, - .locals_dict = (mp_obj_dict_t *)&jclass_locals_dict, -}; +STATIC MP_DEFINE_CONST_OBJ_TYPE( + jclass_type, + MP_QSTR_jclass, + MP_TYPE_FLAG_NONE, + print, jclass_print, + attr, jclass_attr, + call, jclass_call, + locals_dict, &jclass_locals_dict + ); STATIC mp_obj_t new_jclass(jclass jc) { mp_obj_jclass_t *o = mp_obj_malloc(mp_obj_jclass_t, &jclass_type); @@ -320,16 +321,17 @@ STATIC mp_obj_t subscr_getiter(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) { return mp_obj_new_getitem_iter(dest, iter_buf); } -STATIC const mp_obj_type_t jobject_type = { - { &mp_type_type }, - .name = MP_QSTR_jobject, - .print = jobject_print, - .unary_op = jobject_unary_op, - .attr = jobject_attr, - .subscr = jobject_subscr, - .getiter = subscr_getiter, -// .locals_dict = (mp_obj_dict_t*)&jobject_locals_dict, -}; +STATIC MP_DEFINE_CONST_OBJ_TYPE( + jobject_type, + MP_QSTR_jobject, + MP_TYPE_FLAG_ITER_IS_GETITER, + print, jobject_print, + unary_op, jobject_unary_op, + attr, jobject_attr, + subscr, jobject_subscr, + iter, subscr_getiter, + // .locals_dict = &jobject_locals_dict, + ); STATIC mp_obj_t new_jobject(jobject jo) { if (jo == NULL) { @@ -376,7 +378,7 @@ STATIC const char *strprev(const char *s, char c) { STATIC bool py2jvalue(const char **jtypesig, mp_obj_t arg, jvalue *out) { const char *arg_type = *jtypesig; - mp_obj_type_t *type = mp_obj_get_type(arg); + const mp_obj_type_t *type = mp_obj_get_type(arg); if (type == &mp_type_str) { if (IMATCH(arg_type, "java.lang.String") || IMATCH(arg_type, "java.lang.Object")) { @@ -567,14 +569,15 @@ STATIC mp_obj_t jmethod_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const return call_method(self->obj, name, methods, false, n_args, args); } -STATIC const mp_obj_type_t jmethod_type = { - { &mp_type_type }, - .name = MP_QSTR_jmethod, - .print = jmethod_print, - .call = jmethod_call, -// .attr = jobject_attr, -// .locals_dict = (mp_obj_dict_t*)&jobject_locals_dict, -}; +STATIC MP_DEFINE_CONST_OBJ_TYPE( + jmethod_type, + MP_QSTR_jmethod, + MP_TYPE_FLAG_NONE, + print, jmethod_print, + call, jmethod_call, + // .attr = jobject_attr, + // .locals_dict = &jobject_locals_dict, + ); #ifdef __ANDROID__ #define LIBJVM_SO "libdvm.so" diff --git a/ports/unix/modmachine.c b/ports/unix/modmachine.c index bcbf39406f..542ff50025 100644 --- a/ports/unix/modmachine.c +++ b/ports/unix/modmachine.c @@ -36,8 +36,6 @@ #include "extmod/machine_signal.h" #include "extmod/machine_pulse.h" -#include "supervisor/shared/translate/translate.h" - #if MICROPY_PLAT_DEV_MEM #include #include diff --git a/ports/unix/moduos.c b/ports/unix/moduos.c index e352b9f27b..f3e1635462 100644 --- a/ports/unix/moduos.c +++ b/ports/unix/moduos.c @@ -25,6 +25,7 @@ * THE SOFTWARE. */ +#include #include #include @@ -129,7 +130,7 @@ STATIC mp_obj_t mp_uos_urandom(mp_obj_t num) { vstr_t vstr; vstr_init_len(&vstr, n); mp_hal_get_random(n, vstr.buf); - return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); + return mp_obj_new_bytes_from_vstr(&vstr); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_uos_urandom_obj, mp_uos_urandom); diff --git a/ports/unix/moduselect.c b/ports/unix/moduselect.c index 7bfcf8fc56..8f71813e70 100644 --- a/ports/unix/moduselect.c +++ b/ports/unix/moduselect.c @@ -311,16 +311,13 @@ STATIC const mp_rom_map_elem_t poll_locals_dict_table[] = { }; STATIC MP_DEFINE_CONST_DICT(poll_locals_dict, poll_locals_dict_table); -STATIC const mp_obj_type_t mp_type_poll = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_poll, - .locals_dict = (void *)&poll_locals_dict, - MP_TYPE_EXTENDED_FIELDS( - .getiter = mp_identity_getiter, - .iternext = poll_iternext, - ), -}; +STATIC MP_DEFINE_CONST_OBJ_TYPE( + mp_type_poll, + MP_QSTR_poll, + MP_TYPE_FLAG_ITER_IS_ITERNEXT, + iter, poll_iternext, + locals_dict, &poll_locals_dict + ); STATIC mp_obj_t select_poll(size_t n_args, const mp_obj_t *args) { int alloc = 4; diff --git a/ports/unix/mpbthciport.c b/ports/unix/mpbthciport.c index 14afbebcd7..8813ce147c 100644 --- a/ports/unix/mpbthciport.c +++ b/ports/unix/mpbthciport.c @@ -46,7 +46,11 @@ #include #define DEBUG_printf(...) // printf(__VA_ARGS__) -#define DEBUG_HCI_DUMP (0) + +#define HCI_TRACE (0) +#define COL_OFF "\033[0m" +#define COL_GREEN "\033[0;32m" +#define COL_BLUE "\033[0;34m" uint8_t mp_bluetooth_hci_cmd_buf[4 + 256]; @@ -234,8 +238,8 @@ int mp_bluetooth_hci_uart_readchar(void) { ssize_t bytes_read = read(uart_fd, &c, 1); if (bytes_read == 1) { - #if DEBUG_HCI_DUMP - printf("[% 8ld] RX: %02x\n", mp_hal_ticks_ms(), c); + #if HCI_TRACE + printf(COL_BLUE "> [% 8ld] RX: %02x" COL_OFF "\n", mp_hal_ticks_ms(), c); #endif return c; } else { @@ -250,12 +254,12 @@ int mp_bluetooth_hci_uart_write(const uint8_t *buf, size_t len) { return 0; } - #if DEBUG_HCI_DUMP - printf("[% 8ld] TX: %02x", mp_hal_ticks_ms(), buf[0]); + #if HCI_TRACE + printf(COL_GREEN "< [% 8ld] TX: %02x", mp_hal_ticks_ms(), buf[0]); for (size_t i = 1; i < len; ++i) { printf(":%02x", buf[i]); } - printf("\n"); + printf(COL_OFF "\n"); #endif return write(uart_fd, buf, len); diff --git a/ports/unix/mpbtstackport_common.c b/ports/unix/mpbtstackport_common.c index ec40db65bc..36412e96f4 100644 --- a/ports/unix/mpbtstackport_common.c +++ b/ports/unix/mpbtstackport_common.c @@ -35,6 +35,7 @@ #include "lib/btstack/platform/embedded/btstack_run_loop_embedded.h" #include "lib/btstack/platform/embedded/hal_cpu.h" #include "lib/btstack/platform/embedded/hal_time_ms.h" +#include "lib/btstack/platform/embedded/hci_dump_embedded_stdout.h" #include "extmod/btstack/modbluetooth_btstack.h" @@ -79,15 +80,9 @@ uint32_t hal_time_ms(void) { } void mp_bluetooth_btstack_port_init(void) { - static bool run_loop_init = false; - if (!run_loop_init) { - run_loop_init = true; - btstack_run_loop_init(btstack_run_loop_embedded_get_instance()); - } else { - btstack_run_loop_embedded_get_instance()->init(); - } + btstack_run_loop_init(btstack_run_loop_embedded_get_instance()); - // hci_dump_open(NULL, HCI_DUMP_STDOUT); + // hci_dump_init(hci_dump_embedded_stdout_get_instance()); #if MICROPY_BLUETOOTH_BTSTACK_H4 mp_bluetooth_btstack_port_init_h4(); diff --git a/ports/unix/mpbtstackport_h4.c b/ports/unix/mpbtstackport_h4.c index 4fdc20c22b..dacfff9a49 100644 --- a/ports/unix/mpbtstackport_h4.c +++ b/ports/unix/mpbtstackport_h4.c @@ -32,6 +32,7 @@ #if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK && MICROPY_BLUETOOTH_BTSTACK_H4 +#include "lib/btstack/src/hci_transport_h4.h" #include "lib/btstack/chipset/zephyr/btstack_chipset_zephyr.h" #include "extmod/btstack/btstack_hci_uart.h" @@ -42,11 +43,12 @@ #define DEBUG_printf(...) // printf(__VA_ARGS__) STATIC hci_transport_config_uart_t hci_transport_config_uart = { - HCI_TRANSPORT_CONFIG_UART, - 1000000, // initial baudrate - 0, // main baudrate - 1, // flow control - NULL, // device name + .type = HCI_TRANSPORT_CONFIG_UART, + .baudrate_init = 1000000, + .baudrate_main = 0, + .flowcontrol = 1, + .device_name = NULL, + .parity = BTSTACK_UART_PARITY_OFF, }; void mp_bluetooth_hci_poll_h4(void) { @@ -58,7 +60,7 @@ void mp_bluetooth_hci_poll_h4(void) { void mp_bluetooth_btstack_port_init_h4(void) { DEBUG_printf("mp_bluetooth_btstack_port_init_h4\n"); - const hci_transport_t *transport = hci_transport_h4_instance(&mp_bluetooth_btstack_hci_uart_block); + const hci_transport_t *transport = hci_transport_h4_instance_for_uart(&mp_bluetooth_btstack_hci_uart_block); hci_init(transport, &hci_transport_config_uart); hci_set_chipset(btstack_chipset_zephyr_instance()); diff --git a/ports/unix/mpbtstackport_usb.c b/ports/unix/mpbtstackport_usb.c index 28d2c8c543..b8c7b758d9 100644 --- a/ports/unix/mpbtstackport_usb.c +++ b/ports/unix/mpbtstackport_usb.c @@ -34,6 +34,7 @@ #if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK && MICROPY_BLUETOOTH_BTSTACK_USB #include "lib/btstack/src/btstack.h" +#include "lib/btstack/src/hci_transport_usb.h" #include "lib/btstack/platform/embedded/btstack_run_loop_embedded.h" #include "lib/btstack/platform/embedded/hal_cpu.h" #include "lib/btstack/platform/embedded/hal_time_ms.h" diff --git a/ports/unix/mpconfigport.h b/ports/unix/mpconfigport.h index a9e6cf9885..040cb60402 100644 --- a/ports/unix/mpconfigport.h +++ b/ports/unix/mpconfigport.h @@ -24,91 +24,40 @@ * THE SOFTWARE. */ -// Options to control how MicroPython is built for this port, -// overriding defaults in py/mpconfig.h. +// Options to control how MicroPython is built for this port, overriding +// defaults in py/mpconfig.h. This file is mostly about configuring the +// features to work on Unix-like systems, see mpconfigvariant.h (and +// mpconfigvariant_common.h) for feature enabling. + +// For size_t and ssize_t +#include // Variant-specific definitions. #include "mpconfigvariant.h" -// The minimal variant's config covers everything. -// If we're building the minimal variant, ignore the rest of this file. -#ifndef MICROPY_UNIX_MINIMAL - // CIRCUITPY #define CIRCUITPY_MICROPYTHON_ADVANCED (1) #define MICROPY_PY_ASYNC_AWAIT (1) -// If the variant did not set a feature level then configure a set of features. #ifndef MICROPY_CONFIG_ROM_LEVEL -#define MICROPY_COMP_MODULE_CONST (1) -#define MICROPY_COMP_TRIPLE_TUPLE_ASSIGN (1) -#define MICROPY_COMP_RETURN_IF_EXPR (1) -#ifndef MICROPY_OPT_LOAD_ATTR_FAST_PATH -#define MICROPY_OPT_LOAD_ATTR_FAST_PATH (1) -#endif -#ifndef MICROPY_OPT_MAP_LOOKUP_CACHE -#define MICROPY_OPT_MAP_LOOKUP_CACHE (1) -#endif -#define MICROPY_ENABLE_FINALISER (1) -#define MICROPY_STACK_CHECK (1) -#define MICROPY_KBD_EXCEPTION (1) -#define MICROPY_HELPER_REPL (1) -#define MICROPY_REPL_EMACS_KEYS (1) -#define MICROPY_REPL_AUTO_INDENT (1) -#define MICROPY_ENABLE_SOURCE_LINE (1) -#ifndef MICROPY_STREAMS_NON_BLOCK -#define MICROPY_STREAMS_NON_BLOCK (1) -#endif -#define MICROPY_MODULE_WEAK_LINKS (1) -#define MICROPY_CAN_OVERRIDE_BUILTINS (1) -#define MICROPY_PY_FUNCTION_ATTRS (1) -#define MICROPY_PY_DESCRIPTORS (1) -#define MICROPY_PY_DELATTR_SETATTR (1) -#define MICROPY_PY_FSTRINGS (1) -#define MICROPY_PY_BUILTINS_STR_UNICODE (1) -#define MICROPY_PY_BUILTINS_STR_CENTER (1) -#define MICROPY_PY_BUILTINS_STR_PARTITION (1) -#define MICROPY_PY_BUILTINS_STR_SPLITLINES (1) -#define MICROPY_PY_BUILTINS_MEMORYVIEW (1) -#define MICROPY_PY_BUILTINS_SLICE_ATTRS (1) -#define MICROPY_PY_BUILTINS_SLICE_INDICES (1) -#define MICROPY_PY_BUILTINS_FROZENSET (1) -#define MICROPY_PY_BUILTINS_ROUND_INT (1) -#define MICROPY_PY_ALL_SPECIAL_METHODS (1) -#define MICROPY_PY_REVERSE_SPECIAL_METHODS (1) -#define MICROPY_PY_BUILTINS_COMPILE (1) -#define MICROPY_PY_BUILTINS_NOTIMPLEMENTED (1) -#define MICROPY_PY_BUILTINS_INPUT (1) -#define MICROPY_PY_BUILTINS_POW3 (1) -#define MICROPY_PY_MICROPYTHON_MEM_INFO (1) -#define MICROPY_PY_ARRAY_SLICE_ASSIGN (1) -#define MICROPY_PY_COLLECTIONS_DEQUE (1) -#define MICROPY_PY_COLLECTIONS_ORDEREDDICT (1) -#ifndef MICROPY_PY_MATH_SPECIAL_FUNCTIONS -#define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (1) -#endif -#define MICROPY_PY_MATH_ISCLOSE (MICROPY_PY_MATH_SPECIAL_FUNCTIONS) -#define MICROPY_PY_CMATH (1) -#define MICROPY_PY_IO_IOBASE (1) -#define MICROPY_PY_IO_FILEIO (1) -#define MICROPY_PY_SYS_MAXSIZE (1) -#define MICROPY_PY_SYS_STDFILES (1) -#define MICROPY_PY_UERRNO (1) -#define MICROPY_PY_UCTYPES (1) -#define MICROPY_PY_UZLIB (1) -#define MICROPY_PY_UJSON (1) -#define MICROPY_PY_UOS (1) -#define MICROPY_PY_URE (1) -#define MICROPY_PY_UHEAPQ (1) -#define MICROPY_PY_UHASHLIB (1) -#define MICROPY_PY_UBINASCII (1) -#define MICROPY_PY_UBINASCII_CRC32 (1) -#define MICROPY_PY_URANDOM (1) +#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_CORE_FEATURES) #endif -#define MICROPY_ALLOC_PATH_MAX (PATH_MAX) -#define MICROPY_PERSISTENT_CODE_LOAD (1) +#ifndef MICROPY_PY_SYS_PLATFORM +#if defined(__APPLE__) && defined(__MACH__) + #define MICROPY_PY_SYS_PLATFORM "darwin" +#else + #define MICROPY_PY_SYS_PLATFORM "linux" +#endif +#endif +#ifndef MICROPY_PY_SYS_PATH_DEFAULT +#define MICROPY_PY_SYS_PATH_DEFAULT ".frozen:~/.micropython/lib:/usr/lib/micropython" +#endif + +#define MP_STATE_PORT MP_STATE_VM + +// Configure which emitter to use for this target. #if !defined(MICROPY_EMIT_X64) && defined(__x86_64__) #define MICROPY_EMIT_X64 (1) #endif @@ -124,115 +73,8 @@ #if !defined(MICROPY_EMIT_ARM) && defined(__arm__) && !defined(__thumb2__) #define MICROPY_EMIT_ARM (1) #endif -#define MICROPY_ENABLE_GC (1) -#define MICROPY_MALLOC_USES_ALLOCATED_SIZE (1) -#define MICROPY_MEM_STATS (1) -#define MICROPY_DEBUG_PRINTERS (1) -// Printing debug to stderr may give tests which -// check stdout a chance to pass, etc. -#define MICROPY_DEBUG_PRINTER (&mp_stderr_print) -#define MICROPY_READER_POSIX (1) -#define MICROPY_READER_VFS (1) -#define MICROPY_USE_READLINE_HISTORY (1) -#define MICROPY_HELPER_LEXER_UNIX (1) -#ifndef MICROPY_FLOAT_IMPL -#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_DOUBLE) -#endif -#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) -#define MICROPY_STREAMS_POSIX_API (1) -#define MICROPY_OPT_COMPUTED_GOTO (1) -#define MICROPY_MODULE_OVERRIDE_MAIN_IMPORT (1) -#define MICROPY_VFS (1) -#define MICROPY_VFS_POSIX (1) -#define MICROPY_PY_SYS_PATH_ARGV_DEFAULTS (0) -#define MICROPY_PY_SYS_EXIT (1) -#define MICROPY_PY_SYS_ATEXIT (1) -#if MICROPY_PY_SYS_SETTRACE -#define MICROPY_PERSISTENT_CODE_SAVE (1) -#define MICROPY_COMP_CONST (0) -#endif -#ifndef MICROPY_PY_SYS_PLATFORM -#if defined(__APPLE__) && defined(__MACH__) - #define MICROPY_PY_SYS_PLATFORM "darwin" -#else - #define MICROPY_PY_SYS_PLATFORM "linux" -#endif -#endif -#ifndef MICROPY_PY_SYS_PATH_DEFAULT -#define MICROPY_PY_SYS_PATH_DEFAULT ".frozen:~/.micropython/lib:/usr/lib/micropython" -#endif -#define MICROPY_PY_SYS_EXC_INFO (1) -#define MICROPY_PY_GC_COLLECT_RETVAL (1) -#ifndef MICROPY_STACKLESS -#define MICROPY_STACKLESS (0) -#define MICROPY_STACKLESS_STRICT (0) -#endif - -#define MICROPY_PY_UOS_INCLUDEFILE "ports/unix/moduos.c" -#define MICROPY_PY_UOS_ERRNO (1) -#define MICROPY_PY_UOS_GETENV_PUTENV_UNSETENV (1) -#define MICROPY_PY_UOS_SEP (1) -#define MICROPY_PY_UOS_SYSTEM (1) -#define MICROPY_PY_UOS_URANDOM (1) -#define MICROPY_PY_UTIME (1) -#define MICROPY_PY_UTIME_MP_HAL (1) -#define MICROPY_PY_UTIMEQ (1) -#define MICROPY_PY_USOCKET_LISTEN_BACKLOG_DEFAULT (SOMAXCONN < 128 ? SOMAXCONN : 128) -#if MICROPY_PY_USSL -#define MICROPY_PY_UHASHLIB_MD5 (1) -#define MICROPY_PY_UHASHLIB_SHA1 (1) -#define MICROPY_PY_UCRYPTOLIB (1) -#endif -#ifndef MICROPY_PY_USELECT_POSIX -#define MICROPY_PY_USELECT_POSIX (1) -#endif -#define MICROPY_PY_UWEBSOCKET (0) -#define MICROPY_PY_MACHINE (0) -#define MICROPY_PY_MACHINE_PULSE (0) -#define MICROPY_MACHINE_MEM_GET_READ_ADDR mod_machine_mem_get_addr -#define MICROPY_MACHINE_MEM_GET_WRITE_ADDR mod_machine_mem_get_addr - -#define MICROPY_FATFS_ENABLE_LFN (1) -#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) */ -#define MICROPY_FATFS_LFN_UNICODE (2) - -#define FF_FS_CASE_INSENSITIVE_COMPARISON_ASCII_ONLY (1) - -// Define to MICROPY_ERROR_REPORTING_DETAILED to get function, etc. -// names in exception messages (may require more RAM). -#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_DETAILED) -#define MICROPY_WARNINGS (1) -#define MICROPY_ERROR_PRINTER (&mp_stderr_print) -#define MICROPY_PY_STR_BYTES_CMP_WARN (1) - -// VFS stat functions should return time values relative to 1970/1/1 -#define MICROPY_EPOCH_IS_1970 (1) - -extern const struct _mp_print_t mp_stderr_print; - -#define RUN_BACKGROUND_TASKS - -#if !(defined(MICROPY_GCREGS_SETJMP) || defined(__x86_64__) || defined(__i386__) || defined(__thumb2__) || defined(__thumb__) || defined(__arm__)) -// Fall back to setjmp() implementation for discovery of GC pointers in registers. -#define MICROPY_GCREGS_SETJMP (1) -#endif - -#define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) -#define MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE (256) -#define MICROPY_ASYNC_KBD_INTR (1) - -#define mp_type_fileio mp_type_vfs_posix_fileio -#define mp_type_textio mp_type_vfs_posix_textio - -// type definitions for the specific machine - -// For size_t and ssize_t -#include - -// assume that if we already defined the obj repr then we also defined types +// Type definitions for the specific machine based on the word size. #ifndef MICROPY_OBJ_REPR #ifdef __LP64__ typedef long mp_int_t; // must be pointer size @@ -243,6 +85,8 @@ typedef unsigned long mp_uint_t; // must be pointer size typedef int mp_int_t; // must be pointer size typedef unsigned int mp_uint_t; // must be pointer size #endif +#else +// Assume that if we already defined the obj repr then we also defined types. #endif // Cannot include , as it may lead to symbol name clashes @@ -252,6 +96,83 @@ typedef long long mp_off_t; typedef long mp_off_t; #endif +// We need to provide a declaration/definition of alloca() +// unless support for it is disabled. +#if !defined(MICROPY_NO_ALLOCA) || MICROPY_NO_ALLOCA == 0 +#if defined(__FreeBSD__) || defined(__NetBSD__) +#include +#else +#include +#endif +#endif + +// Always enable GC. +#define MICROPY_ENABLE_GC (1) + +#if !(defined(MICROPY_GCREGS_SETJMP) || defined(__x86_64__) || defined(__i386__) || defined(__thumb2__) || defined(__thumb__) || defined(__arm__)) +// Fall back to setjmp() implementation for discovery of GC pointers in registers. +#define MICROPY_GCREGS_SETJMP (1) +#endif + +// Enable the VFS, and enable the posix "filesystem". +#define MICROPY_ENABLE_FINALISER (1) +#define MICROPY_VFS (1) +#define MICROPY_READER_VFS (1) +#define MICROPY_HELPER_LEXER_UNIX (1) +#define MICROPY_VFS_POSIX (1) +#define MICROPY_READER_POSIX (1) +#ifndef MICROPY_TRACKED_ALLOC +#define MICROPY_TRACKED_ALLOC (MICROPY_BLUETOOTH_BTSTACK) +#endif + +// VFS stat functions should return time values relative to 1970/1/1 +#define MICROPY_EPOCH_IS_1970 (1) + +// Assume that select() call, interrupted with a signal, and erroring +// with EINTR, updates remaining timeout value. +#define MICROPY_SELECT_REMAINING_TIME (1) + +// Disable stackless by default. +#ifndef MICROPY_STACKLESS +#define MICROPY_STACKLESS (0) +#define MICROPY_STACKLESS_STRICT (0) +#endif + +// If settrace is enabled then we need code saving. +#if MICROPY_PY_SYS_SETTRACE +#define MICROPY_PERSISTENT_CODE_SAVE (1) +#define MICROPY_COMP_CONST (0) +#endif + +// 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 + +#define MICROPY_FATFS_ENABLE_LFN (1) +#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) */ + +#define MICROPY_ALLOC_PATH_MAX (PATH_MAX) + +// Ensure builtinimport.c works with -m. +#define MICROPY_MODULE_OVERRIDE_MAIN_IMPORT (1) + +// Don't default sys.argv because we do that in main. +#define MICROPY_PY_SYS_PATH_ARGV_DEFAULTS (0) + +// Enable sys.executable. +#define MICROPY_PY_SYS_EXECUTABLE (1) + +#define MICROPY_PY_USOCKET_LISTEN_BACKLOG_DEFAULT (SOMAXCONN < 128 ? SOMAXCONN : 128) + +// Bare-metal ports don't have stderr. Printing debug to stderr may give tests +// which check stdout a chance to pass, etc. +extern const struct _mp_print_t mp_stderr_print; +#define MICROPY_DEBUG_PRINTER (&mp_stderr_print) +#define MICROPY_ERROR_PRINTER (&mp_stderr_print) + +// For the native emitter configure how to mark a region as executable. void mp_unix_alloc_exec(size_t min_size, void **ptr, size_t *size); void mp_unix_free_exec(void *ptr, size_t size); void mp_unix_mark_exec(void); @@ -263,8 +184,8 @@ void mp_unix_mark_exec(void); #define MICROPY_FORCE_PLAT_ALLOC_EXEC (1) #endif +// If enabled, configure how to seed random on init. #ifdef MICROPY_PY_URANDOM_SEED_INIT_FUNC -// Support for seeding the random module on import. #include void mp_hal_get_random(size_t n, void *buf); static inline unsigned long mp_urandom_seed_init(void) { @@ -279,10 +200,6 @@ static inline unsigned long mp_urandom_seed_init(void) { #define MICROPY_PLAT_DEV_MEM (1) #endif -// Assume that select() call, interrupted with a signal, and erroring -// with EINTR, updates remaining timeout value. -#define MICROPY_SELECT_REMAINING_TIME (1) - #ifdef __ANDROID__ #include #if __ANDROID_API__ < 4 @@ -292,37 +209,6 @@ static inline unsigned long mp_urandom_seed_init(void) { #endif #endif -#define MP_STATE_PORT MP_STATE_VM - -#if MICROPY_PY_BLUETOOTH -#if MICROPY_BLUETOOTH_BTSTACK -struct _mp_bluetooth_btstack_root_pointers_t; -#define MICROPY_BLUETOOTH_ROOT_POINTERS struct _mp_bluetooth_btstack_root_pointers_t *bluetooth_btstack_root_pointers; -#endif -#if MICROPY_BLUETOOTH_NIMBLE -struct _mp_bluetooth_nimble_root_pointers_t; -struct _mp_bluetooth_nimble_malloc_t; -#define MICROPY_BLUETOOTH_ROOT_POINTERS struct _mp_bluetooth_nimble_malloc_t *bluetooth_nimble_memory; struct _mp_bluetooth_nimble_root_pointers_t *bluetooth_nimble_root_pointers; -#endif -#else -#define MICROPY_BLUETOOTH_ROOT_POINTERS -#endif - -#define MICROPY_PORT_ROOT_POINTERS \ - const char *readline_hist[50]; \ - void *mmap_region_head; \ - MICROPY_BLUETOOTH_ROOT_POINTERS \ - -// We need to provide a declaration/definition of alloca() -// unless support for it is disabled. -#if !defined(MICROPY_NO_ALLOCA) || MICROPY_NO_ALLOCA == 0 -#ifdef __FreeBSD__ -#include -#else -#include -#endif -#endif - // From "man readdir": "Under glibc, programs can check for the availability // of the fields [in struct dirent] not defined in POSIX.1 by testing whether // the macros [...], _DIRENT_HAVE_D_TYPE are defined." @@ -342,19 +228,30 @@ struct _mp_bluetooth_nimble_malloc_t; #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); \ - mp_hal_delay_us(500); \ + usleep(500); /* equivalent to mp_hal_delay_us(500) */ \ } while (0); +#endif +// Configure the implementation of machine.idle(). #include #define MICROPY_UNIX_MACHINE_IDLE sched_yield(); -#endif // MICROPY_UNIX_MINIMAL +#ifndef MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE +#define MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE (1) +#endif + +#ifndef MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS +#define MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS (MICROPY_BLUETOOTH_NIMBLE) +#endif diff --git a/ports/unix/mpconfigport.mk b/ports/unix/mpconfigport.mk index a0f654597b..2bdd87534e 100644 --- a/ports/unix/mpconfigport.mk +++ b/ports/unix/mpconfigport.mk @@ -40,6 +40,14 @@ MICROPY_PY_JNI = 0 # as submodules (currently affects only libffi). MICROPY_STANDALONE = 0 +# CIRCUITPY: not used +MICROPY_ROM_TEXT_COMPRESSION = 0 + +MICROPY_VFS_FAT = 1 +# CIRCUITPY: not used +MICROPY_VFS_LFS1 = 0 +MICROPY_VFS_LFS2 = 0 + # CIRCUITPY CIRCUITPY_ULAB = 1 MICROPY_EMIT_NATIVE = 0 diff --git a/ports/unix/unix_mphal.c b/ports/unix/unix_mphal.c index 665d038c0d..a385121775 100644 --- a/ports/unix/unix_mphal.c +++ b/ports/unix/unix_mphal.c @@ -206,6 +206,7 @@ void mp_hal_stdout_tx_str(const char *str) { mp_hal_stdout_tx_strn(str, strlen(str)); } +#ifndef mp_hal_ticks_ms mp_uint_t mp_hal_ticks_ms(void) { #if (defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0) && defined(_POSIX_MONOTONIC_CLOCK) struct timespec tv; @@ -217,7 +218,9 @@ mp_uint_t mp_hal_ticks_ms(void) { return tv.tv_sec * 1000 + tv.tv_usec / 1000; #endif } +#endif +#ifndef mp_hal_ticks_us mp_uint_t mp_hal_ticks_us(void) { #if (defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0) && defined(_POSIX_MONOTONIC_CLOCK) struct timespec tv; @@ -229,18 +232,22 @@ mp_uint_t mp_hal_ticks_us(void) { return tv.tv_sec * 1000000 + tv.tv_usec; #endif } +#endif +#ifndef mp_hal_time_ns uint64_t mp_hal_time_ns(void) { struct timeval tv; gettimeofday(&tv, NULL); return (uint64_t)tv.tv_sec * 1000000000ULL + (uint64_t)tv.tv_usec * 1000ULL; } +#endif +#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 mp_hal_delay_us(500) (i.e. usleep(500)). + // MICROPY_EVENT_POLL_HOOK does usleep(500). MICROPY_EVENT_POLL_HOOK } #else @@ -249,6 +256,7 @@ void mp_hal_delay_ms(mp_uint_t ms) { usleep(ms * 1000); #endif } +#endif void mp_hal_get_random(size_t n, void *buf) { #ifdef _HAVE_GETRANDOM diff --git a/ports/unix/variants/coverage/manifest.py b/ports/unix/variants/coverage/manifest.py index 6111050884..7c3d9a6b64 100644 --- a/ports/unix/variants/coverage/manifest.py +++ b/ports/unix/variants/coverage/manifest.py @@ -1,2 +1,3 @@ freeze_as_str("frzstr") freeze_as_mpy("frzmpy") +freeze_mpy("$(MPY_DIR)/tests/frozen") diff --git a/ports/unix/variants/coverage/mpconfigvariant.h b/ports/unix/variants/coverage/mpconfigvariant.h index 5828cb5616..2a48e57e7f 100644 --- a/ports/unix/variants/coverage/mpconfigvariant.h +++ b/ports/unix/variants/coverage/mpconfigvariant.h @@ -28,52 +28,17 @@ // for coverage testing. // Set base feature level. -#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES) +#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EVERYTHING) -#define MICROPY_VFS (1) -#define MICROPY_PY_UOS_VFS (1) +// Enable extra Unix features. +#include "../mpconfigvariant_common.h" -// Disable some features that come enabled by default with the feature level. -#define MICROPY_PY_BUILTINS_EXECFILE (0) -#define MICROPY_PY_SYS_STDIO_BUFFER (0) -#define MICROPY_PY_USELECT (0) +// Enable testing of split heap. +#define MICROPY_GC_SPLIT_HEAP (1) +#define MICROPY_GC_SPLIT_HEAP_N_HEAPS (4) // Enable additional features. #define MICROPY_DEBUG_PARSE_RULE_NAME (1) -#define MICROPY_TRACKED_ALLOC (1) -#define MICROPY_FLOAT_HIGH_QUALITY_HASH (1) -#define MICROPY_ENABLE_SCHEDULER (1) -#define MICROPY_READER_VFS (1) -#define MICROPY_REPL_EMACS_WORDS_MOVE (1) -#define MICROPY_REPL_EMACS_EXTRA_WORDS_MOVE (1) +#define MICROPY_TRACKED_ALLOC (1) #define MICROPY_WARNINGS_CATEGORY (1) -#define MICROPY_MODULE_GETATTR (1) -#define MICROPY_PY_DELATTR_SETATTR (1) -#define MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS (1) -#define MICROPY_PY_REVERSE_SPECIAL_METHODS (1) -#define MICROPY_PY_BUILTINS_MEMORYVIEW_ITEMSIZE (1) -#define MICROPY_PY_BUILTINS_NEXT2 (1) -#define MICROPY_PY_BUILTINS_RANGE_BINOP (1) -#define MICROPY_PY_BUILTINS_HELP (1) -#define MICROPY_PY_BUILTINS_HELP_MODULES (1) -#define MICROPY_PY_SYS_GETSIZEOF (1) -#define MICROPY_PY_SYS_TRACEBACKLIMIT (1) -#define MICROPY_PY_MATH_FACTORIAL (1) -#define MICROPY_PY_URANDOM_EXTRA_FUNCS (1) -#define MICROPY_PY_IO_BUFFEREDWRITER (1) -#define MICROPY_PY_UASYNCIO (1) -#define MICROPY_PY_URANDOM_SEED_INIT_FUNC (mp_urandom_seed_init()) -#define MICROPY_PY_URE_DEBUG (1) -#define MICROPY_PY_URE_MATCH_GROUPS (1) -#define MICROPY_PY_URE_MATCH_SPAN_START_END (1) -#define MICROPY_PY_URE_SUB (1) -#define MICROPY_VFS_POSIX (1) -#define MICROPY_FATFS_USE_LABEL (1) -#define MICROPY_FF_MKFS_FAT32 (1) -#define MICROPY_PY_FRAMEBUF (1) -#define MICROPY_PY_COLLECTIONS_NAMEDTUPLE__ASDICT (1) -#define MICROPY_PY_COLLECTIONS_ORDEREDDICT (1) -#define MICROPY_PY_STRUCT (0) // uses shared-bindings struct -#define MICROPY_PY_UCRYPTOLIB (1) #define MICROPY_PY_UCRYPTOLIB_CTR (1) -#define MICROPY_PY_MICROPYTHON_HEAP_LOCKED (1) diff --git a/ports/unix/variants/coverage/mpconfigvariant.mk b/ports/unix/variants/coverage/mpconfigvariant.mk index 656fd77289..37b7cb46b6 100644 --- a/ports/unix/variants/coverage/mpconfigvariant.mk +++ b/ports/unix/variants/coverage/mpconfigvariant.mk @@ -1,5 +1,3 @@ -PROG ?= micropython-coverage - # Disable optimisations and enable assert() on coverage builds. DEBUG ?= 1 @@ -8,18 +6,13 @@ CFLAGS += \ -Wformat -Wmissing-declarations -Wmissing-prototypes \ -Wold-style-definition -Wpointer-arith -Wshadow -Wuninitialized -Wunused-parameter \ -DMICROPY_UNIX_COVERAGE \ - -DMICROPY_CPYTHON_EXCEPTION_CHAIN=1 \ - -DMODULE_CEXAMPLE_ENABLED=1 -DMODULE_CPPEXAMPLE_ENABLED=1 + -DMICROPY_CPYTHON_EXCEPTION_CHAIN=1 LDFLAGS += -fprofile-arcs -ftest-coverage FROZEN_MANIFEST ?= $(VARIANT_DIR)/manifest.py USER_C_MODULES = $(TOP)/examples/usercmodule -MICROPY_VFS_FAT = 1 -MICROPY_VFS_LFS1 = 1 -MICROPY_VFS_LFS2 = 1 - 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/dev/manifest.py b/ports/unix/variants/dev/manifest.py deleted file mode 100644 index 92a681116a..0000000000 --- a/ports/unix/variants/dev/manifest.py +++ /dev/null @@ -1,3 +0,0 @@ -include("$(PORT_DIR)/variants/manifest.py") - -include("$(MPY_DIR)/extmod/uasyncio/manifest.py") diff --git a/ports/unix/variants/dev/mpconfigvariant.mk b/ports/unix/variants/dev/mpconfigvariant.mk deleted file mode 100644 index 91bd28da9b..0000000000 --- a/ports/unix/variants/dev/mpconfigvariant.mk +++ /dev/null @@ -1,10 +0,0 @@ -PROG ?= micropython-dev - -FROZEN_MANIFEST ?= $(VARIANT_DIR)/manifest.py - -MICROPY_ROM_TEXT_COMPRESSION = 1 -MICROPY_VFS_FAT = 1 -MICROPY_VFS_LFS1 = 1 -MICROPY_VFS_LFS2 = 1 - -MICROPY_PY_BLUETOOTH ?= 1 diff --git a/ports/unix/variants/fast/mpconfigvariant.mk b/ports/unix/variants/fast/mpconfigvariant.mk deleted file mode 100644 index 595e575645..0000000000 --- a/ports/unix/variants/fast/mpconfigvariant.mk +++ /dev/null @@ -1,7 +0,0 @@ -# build synthetically fast interpreter for benchmarking - -COPT += -fno-crossjumping -O2 - -PROG = micropython-fast - -FROZEN_MANIFEST = diff --git a/ports/unix/variants/freedos/mpconfigvariant.mk b/ports/unix/variants/freedos/mpconfigvariant.mk deleted file mode 100644 index a30db3e0c1..0000000000 --- a/ports/unix/variants/freedos/mpconfigvariant.mk +++ /dev/null @@ -1,20 +0,0 @@ -CC = i586-pc-msdosdjgpp-gcc - -STRIP = i586-pc-msdosdjgpp-strip - -SIZE = i586-pc-msdosdjgpp-size - -CFLAGS += \ - -DMICROPY_NLR_SETJMP \ - -Dtgamma=gamma \ - -DMICROPY_EMIT_X86=0 \ - -DMICROPY_NO_ALLOCA=1 \ - -PROG = micropython-freedos - -MICROPY_PY_SOCKET = 0 -MICROPY_PY_FFI = 0 -MICROPY_PY_JNI = 0 -MICROPY_PY_BTREE = 0 -MICROPY_PY_THREAD = 0 -MICROPY_PY_USSL = 0 diff --git a/ports/unix/variants/manifest.py b/ports/unix/variants/manifest.py index 7708e598db..f8e8f250ac 100644 --- a/ports/unix/variants/manifest.py +++ b/ports/unix/variants/manifest.py @@ -1,2 +1 @@ -freeze_as_mpy("$(MPY_DIR)/tools", "upip.py") -freeze_as_mpy("$(MPY_DIR)/tools", "upip_utarfile.py", opt=3) +require("mip-cmdline") diff --git a/ports/unix/variants/minimal/mpconfigvariant.h b/ports/unix/variants/minimal/mpconfigvariant.h index 60d194b6a1..6b107e7790 100644 --- a/ports/unix/variants/minimal/mpconfigvariant.h +++ b/ports/unix/variants/minimal/mpconfigvariant.h @@ -26,123 +26,44 @@ // options to control how MicroPython is built -// Prevent the rest of the default mpconfigport.h being used. -#define MICROPY_UNIX_MINIMAL (1) +#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_MINIMUM) +// Disable native emitters. +#define MICROPY_EMIT_X86 (0) +#define MICROPY_EMIT_X64 (0) +#define MICROPY_EMIT_THUMB (0) +#define MICROPY_EMIT_ARM (0) + +// Tune the parser to use less RAM by default. #define MICROPY_ALLOC_QSTR_CHUNK_INIT (64) #define MICROPY_ALLOC_PARSE_RULE_INIT (8) #define MICROPY_ALLOC_PARSE_RULE_INC (8) #define MICROPY_ALLOC_PARSE_RESULT_INIT (8) #define MICROPY_ALLOC_PARSE_RESULT_INC (8) #define MICROPY_ALLOC_PARSE_CHUNK_INIT (64) -#define MICROPY_ALLOC_PATH_MAX (PATH_MAX) -#define MICROPY_ENABLE_GC (1) -#define MICROPY_GC_ALLOC_THRESHOLD (0) -#define MICROPY_ENABLE_FINALISER (0) -#define MICROPY_STACK_CHECK (0) -#define MICROPY_COMP_CONST (0) -#define MICROPY_MEM_STATS (0) -#define MICROPY_DEBUG_PRINTERS (0) -#define MICROPY_READER_POSIX (1) -#define MICROPY_READER_VFS (1) -#define MICROPY_HELPER_REPL (1) -#define MICROPY_HELPER_LEXER_UNIX (1) -#define MICROPY_ENABLE_SOURCE_LINE (0) -#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_TERSE) -#define MICROPY_WARNINGS (0) -#define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (0) -#define MICROPY_KBD_EXCEPTION (1) -#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_NONE) -#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_NONE) -#define MICROPY_STREAMS_NON_BLOCK (0) -#define MICROPY_OPT_COMPUTED_GOTO (0) -#define MICROPY_OPT_LOAD_ATTR_FAST_PATH (0) -#define MICROPY_OPT_MAP_LOOKUP_CACHE (0) -#define MICROPY_CAN_OVERRIDE_BUILTINS (0) -#define MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG (0) -#define MICROPY_VFS (1) -#define MICROPY_VFS_POSIX (1) -#define MICROPY_CPYTHON_COMPAT (0) -#define MICROPY_PY_BUILTINS_BYTEARRAY (0) -#define MICROPY_PY_BUILTINS_MEMORYVIEW (0) -#define MICROPY_PY_BUILTINS_COMPILE (0) -#define MICROPY_PY_BUILTINS_ENUMERATE (0) -#define MICROPY_PY_BUILTINS_FILTER (0) -#define MICROPY_PY_BUILTINS_FROZENSET (0) -#define MICROPY_PY_BUILTINS_REVERSED (0) -#define MICROPY_PY_BUILTINS_SET (0) -#define MICROPY_PY_BUILTINS_SLICE (0) -#define MICROPY_PY_BUILTINS_STR_COUNT (0) -#define MICROPY_PY_BUILTINS_STR_OP_MODULO (0) -#define MICROPY_PY_BUILTINS_STR_UNICODE (0) -#define MICROPY_PY_BUILTINS_PROPERTY (0) -#define MICROPY_PY_BUILTINS_MIN_MAX (0) -#define MICROPY_PY___FILE__ (0) -#define MICROPY_PY_MICROPYTHON_MEM_INFO (0) -#define MICROPY_PY_GC (0) -#define MICROPY_PY_GC_COLLECT_RETVAL (0) -#define MICROPY_PY_ARRAY (0) -#define MICROPY_PY_COLLECTIONS (0) -#define MICROPY_PY_MATH (0) -#define MICROPY_PY_CMATH (0) -#define MICROPY_PY_IO (0) -#define MICROPY_PY_IO_FILEIO (0) -#define MICROPY_PY_STRUCT (0) -#define MICROPY_PY_SYS (1) -#define MICROPY_PY_SYS_EXIT (0) -#define MICROPY_PY_SYS_PLATFORM "linux" -#ifndef MICROPY_PY_SYS_PATH_DEFAULT -#define MICROPY_PY_SYS_PATH_DEFAULT ".frozen:~/.micropython/lib:/usr/lib/micropython" -#endif -#define MICROPY_PY_SYS_MAXSIZE (0) -#define MICROPY_PY_SYS_STDFILES (0) -#define MICROPY_PY_CMATH (0) -#define MICROPY_PY_UCTYPES (0) -#define MICROPY_PY_UTIME (0) -#define MICROPY_PY_UZLIB (0) -#define MICROPY_PY_UJSON (0) -#define MICROPY_PY_UOS (1) -#define MICROPY_PY_URE (0) -#define MICROPY_PY_UHEAPQ (0) -#define MICROPY_PY_UHASHLIB (0) -#define MICROPY_PY_UBINASCII (0) -#define MICROPY_PORT_ROOT_POINTERS \ +// Enable features that are not enabled by default with the minimum config. +#define MICROPY_COMP_CONST_FOLDING (1) +#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) +#define MICROPY_KBD_EXCEPTION (1) +#define MICROPY_MODULE_GETATTR (1) +#define MICROPY_MULTIPLE_INHERITANCE (1) +#define MICROPY_PY_ASSIGN_EXPR (1) +#define MICROPY_PY_ASYNC_AWAIT (1) +#define MICROPY_PY_ATTRTUPLE (1) +#define MICROPY_PY_BUILTINS_DICT_FROMKEYS (1) +#define MICROPY_PY_BUILTINS_RANGE_ATTRS (1) +#define MICROPY_PY_GENERATOR_PEND_THROW (1) -#define mp_type_fileio mp_type_vfs_posix_fileio -#define mp_type_textio mp_type_vfs_posix_textio +// Enable just the sys and os built-in modules. +#define MICROPY_PY_SYS (1) +#define MICROPY_PY_UOS (1) -////////////////////////////////////////// -// Do not change anything beyond this line -////////////////////////////////////////// - -#if !(defined(MICROPY_GCREGS_SETJMP) || defined(__x86_64__) || defined(__i386__) || defined(__thumb2__) || defined(__thumb__) || defined(__arm__)) -// Fall back to setjmp() implementation for discovery of GC pointers in registers. -#define MICROPY_GCREGS_SETJMP (1) -#endif - -// type definitions for the specific machine - -#ifdef __LP64__ -typedef long mp_int_t; // must be pointer size -typedef unsigned long mp_uint_t; // must be pointer size -#else -// These are definitions for machines where sizeof(int) == sizeof(void*), -// regardless for actual size. -typedef int mp_int_t; // must be pointer size -typedef unsigned int mp_uint_t; // must be pointer size -#endif - -// Cannot include , as it may lead to symbol name clashes -#if _FILE_OFFSET_BITS == 64 && !defined(__LP64__) -typedef long long mp_off_t; -#else -typedef long mp_off_t; -#endif - -// We need to provide a declaration/definition of alloca() -#ifdef __FreeBSD__ -#include -#else -#include -#endif +// The minimum sets this to 1 to save flash. +#define MICROPY_QSTR_BYTES_IN_HASH (2) diff --git a/ports/unix/variants/minimal/mpconfigvariant.mk b/ports/unix/variants/minimal/mpconfigvariant.mk index ec3b21c0b9..d5c2a52e9a 100644 --- a/ports/unix/variants/minimal/mpconfigvariant.mk +++ b/ports/unix/variants/minimal/mpconfigvariant.mk @@ -1,9 +1,7 @@ # build a minimal interpreter -PROG = micropython-minimal FROZEN_MANIFEST = -MICROPY_ROM_TEXT_COMPRESSION = 1 MICROPY_PY_BTREE = 0 MICROPY_PY_FFI = 0 MICROPY_PY_SOCKET = 0 @@ -11,3 +9,7 @@ MICROPY_PY_THREAD = 0 MICROPY_PY_TERMIOS = 0 MICROPY_PY_USSL = 0 MICROPY_USE_READLINE = 0 + +MICROPY_VFS_FAT = 0 +MICROPY_VFS_LFS1 = 0 +MICROPY_VFS_LFS2 = 0 diff --git a/ports/unix/variants/mpconfigvariant_common.h b/ports/unix/variants/mpconfigvariant_common.h new file mode 100644 index 0000000000..1128f21d22 --- /dev/null +++ b/ports/unix/variants/mpconfigvariant_common.h @@ -0,0 +1,124 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 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 file enables and configures features common to all variants +// other than "minimal". + +// Send raise KeyboardInterrupt directly from the signal handler rather than +// scheduling it into the VM. +#define MICROPY_ASYNC_KBD_INTR (1) + +// Enable helpers for printing debugging information. +#ifndef MICROPY_DEBUG_PRINTERS +#define MICROPY_DEBUG_PRINTERS (1) +#endif + +// Enable floating point by default. +#ifndef MICROPY_FLOAT_IMPL +#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_DOUBLE) +#endif + +// Enable arbritrary precision long-int by default. +#ifndef MICROPY_LONGINT_IMPL +#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) +#endif + +// Enable use of C libraries that need read/write/lseek/fsync, e.g. axtls. +#define MICROPY_STREAMS_POSIX_API (1) + +// REPL conveniences. +#define MICROPY_REPL_EMACS_WORDS_MOVE (1) +#define MICROPY_REPL_EMACS_EXTRA_WORDS_MOVE (1) +#define MICROPY_USE_READLINE_HISTORY (1) +#ifndef MICROPY_READLINE_HISTORY_SIZE +#define MICROPY_READLINE_HISTORY_SIZE (50) +#endif + +// Seed random on import. +#define MICROPY_PY_URANDOM_SEED_INIT_FUNC (mp_urandom_seed_init()) + +// Allow exception details in low-memory conditions. +#define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) +#define MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE (256) + +// Allow loading of .mpy files. +#define MICROPY_PERSISTENT_CODE_LOAD (1) + +// Extra memory debugging. +#define MICROPY_MALLOC_USES_ALLOCATED_SIZE (1) +#define MICROPY_MEM_STATS (1) + +// Enable a small performance boost for the VM. +#define MICROPY_OPT_COMPUTED_GOTO (1) + +// Return number of collected objects from gc.collect(). +#define MICROPY_PY_GC_COLLECT_RETVAL (1) + +// Enable detailed error messages and warnings. +#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_DETAILED) +#define MICROPY_WARNINGS (1) +#define MICROPY_PY_STR_BYTES_CMP_WARN (1) + +// Configure the "sys" module with features not usually enabled on bare-metal. +#define MICROPY_PY_SYS_ATEXIT (1) +#define MICROPY_PY_SYS_EXC_INFO (1) + +// Configure the "os" module with extra unix features. +#define MICROPY_PY_UOS_INCLUDEFILE "ports/unix/moduos.c" +#define MICROPY_PY_UOS_ERRNO (1) +#define MICROPY_PY_UOS_GETENV_PUTENV_UNSETENV (1) +#define MICROPY_PY_UOS_SEP (1) +#define MICROPY_PY_UOS_SYSTEM (1) +#define MICROPY_PY_UOS_URANDOM (1) + +// Enable the unix-specific "time" module. +#define MICROPY_PY_UTIME (1) +#define MICROPY_PY_UTIME_MP_HAL (1) + +// Enable the utimeq module used by the previous (v2) version of uasyncio. +#define MICROPY_PY_UTIMEQ (1) + +#if MICROPY_PY_USSL +#define MICROPY_PY_UHASHLIB_MD5 (1) +#define MICROPY_PY_UHASHLIB_SHA1 (1) +#define MICROPY_PY_UCRYPTOLIB (1) +#endif + +// Use the posix implementation of the "select" module (unless the variant +// specifically asks for the MicroPython version). +#ifndef MICROPY_PY_USELECT +#define MICROPY_PY_USELECT (0) +#endif +#ifndef MICROPY_PY_USELECT_POSIX +#define MICROPY_PY_USELECT_POSIX (!MICROPY_PY_USELECT) +#endif + +// Enable the "websocket" module. +#define MICROPY_PY_UWEBSOCKET (1) + +// Enable the "machine" module, mostly for machine.mem*. +#define MICROPY_PY_MACHINE (1) +#define MICROPY_PY_MACHINE_PULSE (1) diff --git a/ports/unix/variants/nanbox/mpconfigvariant.h b/ports/unix/variants/nanbox/mpconfigvariant.h index f827158fb7..7b13b7dc6c 100644 --- a/ports/unix/variants/nanbox/mpconfigvariant.h +++ b/ports/unix/variants/nanbox/mpconfigvariant.h @@ -28,6 +28,11 @@ // continues to build (i.e. catches usage of mp_obj_t that don't work with // this representation). +#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES) + +// Enable extra Unix features. +#include "../mpconfigvariant_common.h" + // select nan-boxing object model #define MICROPY_OBJ_REPR (MICROPY_OBJ_REPR_D) diff --git a/ports/unix/variants/nanbox/mpconfigvariant.mk b/ports/unix/variants/nanbox/mpconfigvariant.mk index 9752b922c1..e588e657ef 100644 --- a/ports/unix/variants/nanbox/mpconfigvariant.mk +++ b/ports/unix/variants/nanbox/mpconfigvariant.mk @@ -1,4 +1,3 @@ # build interpreter with nan-boxing as object model (object repr D) -PROG = micropython-nanbox MICROPY_FORCE_32BIT = 1 diff --git a/ports/unix/variants/standard/manifest.py b/ports/unix/variants/standard/manifest.py new file mode 100644 index 0000000000..dd521258e1 --- /dev/null +++ b/ports/unix/variants/standard/manifest.py @@ -0,0 +1,3 @@ +include("$(PORT_DIR)/variants/manifest.py") + +include("$(MPY_DIR)/extmod/uasyncio") diff --git a/ports/unix/variants/standard/mpconfigvariant.h b/ports/unix/variants/standard/mpconfigvariant.h index 1ec46ef92d..75201e9abc 100644 --- a/ports/unix/variants/standard/mpconfigvariant.h +++ b/ports/unix/variants/standard/mpconfigvariant.h @@ -27,19 +27,5 @@ // Set base feature level. #define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES) -// Disable some features that come enabled by default with the feature level. -#define MICROPY_OPT_MPZ_BITWISE (0) -#define MICROPY_OPT_MATH_FACTORIAL (0) -#define MICROPY_MODULE_ATTR_DELEGATION (0) -#define MICROPY_MODULE_BUILTIN_INIT (0) -#define MICROPY_ENABLE_SCHEDULER (0) -#define MICROPY_PY_BUILTINS_EXECFILE (0) -#define MICROPY_PY_MATH_CONSTANTS (0) -#define MICROPY_PY_MATH_FACTORIAL (0) -#define MICROPY_PY_SYS_PS1_PS2 (0) -#define MICROPY_PY_SYS_STDIO_BUFFER (0) -#define MICROPY_PY_USELECT (0) -#define MICROPY_PY_UASYNCIO (0) -#define MICROPY_PY_URE_SUB (0) -#define MICROPY_PY_URANDOM_EXTRA_FUNCS (0) -#define MICROPY_PY_FRAMEBUF (0) +// Enable extra Unix features. +#include "../mpconfigvariant_common.h" diff --git a/ports/unix/variants/standard/mpconfigvariant.mk b/ports/unix/variants/standard/mpconfigvariant.mk index cf3efab8ae..c91db1aa10 100644 --- a/ports/unix/variants/standard/mpconfigvariant.mk +++ b/ports/unix/variants/standard/mpconfigvariant.mk @@ -1,3 +1,3 @@ # This is the default variant when you `make` the Unix port. -PROG ?= micropython +FROZEN_MANIFEST ?= $(VARIANT_DIR)/manifest.py diff --git a/py/argcheck.c b/py/argcheck.c index c3bde804d4..db22545f64 100644 --- a/py/argcheck.c +++ b/py/argcheck.c @@ -29,8 +29,6 @@ #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" - 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 @@ -153,7 +151,7 @@ NORETURN void mp_arg_error_terse_mismatch(void) { #if MICROPY_CPYTHON_COMPAT NORETURN void mp_arg_error_unimpl_kw(void) { - mp_raise_NotImplementedError(MP_ERROR_TEXT("keyword argument(s) not yet implemented - use normal args instead")); + mp_raise_NotImplementedError(MP_ERROR_TEXT("keyword argument(s) not implemented - use normal args instead")); } #endif diff --git a/py/asmbase.c b/py/asmbase.c index 4a3fd089cb..da4273506a 100644 --- a/py/asmbase.c +++ b/py/asmbase.c @@ -55,15 +55,20 @@ void mp_asm_base_start_pass(mp_asm_base_t *as, int pass) { assert(as->code_base != NULL); } as->pass = pass; + as->suppress = false; as->code_offset = 0; } // all functions must go through this one to emit bytes // if as->pass < MP_ASM_PASS_EMIT, then this function just counts the number // of bytes needed and returns NULL, and callers should not store any data +// It also returns NULL if generated code should be suppressed at this point. uint8_t *mp_asm_base_get_cur_to_write_bytes(void *as_in, size_t num_bytes_to_write) { mp_asm_base_t *as = as_in; uint8_t *c = NULL; + if (as->suppress) { + return c; + } if (as->pass == MP_ASM_PASS_EMIT) { assert(as->code_offset + num_bytes_to_write <= as->code_size); c = as->code_base + as->code_offset; @@ -74,6 +79,11 @@ uint8_t *mp_asm_base_get_cur_to_write_bytes(void *as_in, size_t num_bytes_to_wri void mp_asm_base_label_assign(mp_asm_base_t *as, size_t label) { assert(label < as->max_num_labels); + + // Assiging a label ends any dead-code region, and all following machine + // code should be emitted (until another mp_asm_base_suppress_code() call). + as->suppress = false; + if (as->pass < MP_ASM_PASS_EMIT) { // assign label offset assert(as->label_offsets[label] == (size_t)-1); diff --git a/py/asmbase.h b/py/asmbase.h index 960be7685f..352d2f54cc 100644 --- a/py/asmbase.h +++ b/py/asmbase.h @@ -33,7 +33,12 @@ #define MP_ASM_PASS_EMIT (2) typedef struct _mp_asm_base_t { - int pass; + uint8_t pass; + + // Set to true using mp_asm_base_suppress_code() if the code generator + // should suppress emitted code due to it being dead code. + bool suppress; + size_t code_offset; size_t code_size; uint8_t *code_base; @@ -50,6 +55,10 @@ void mp_asm_base_label_assign(mp_asm_base_t *as, size_t label); void mp_asm_base_align(mp_asm_base_t *as, unsigned int align); void mp_asm_base_data(mp_asm_base_t *as, unsigned int bytesize, uintptr_t val); +static inline void mp_asm_base_suppress_code(mp_asm_base_t *as) { + as->suppress = true; +} + static inline size_t mp_asm_base_get_code_pos(mp_asm_base_t *as) { return as->code_offset; } diff --git a/py/bc.c b/py/bc.c index ecd2f60879..0dc7229a2c 100644 --- a/py/bc.c +++ b/py/bc.c @@ -213,6 +213,7 @@ STATIC void mp_setup_code_state_helper(mp_code_state_t *code_state, size_t n_arg #endif if (wanted_arg_name == MP_OBJ_NEW_QSTR(arg_qstr)) { if (code_state_state[n_state - 1 - j] != MP_OBJ_NULL) { + error_multiple: mp_raise_msg_varg(&mp_type_TypeError, MP_ERROR_TEXT("function got multiple values for argument '%q'"), MP_OBJ_QSTR_VALUE(wanted_arg_name)); } @@ -229,7 +230,12 @@ STATIC void mp_setup_code_state_helper(mp_code_state_t *code_state, size_t n_arg MP_ERROR_TEXT("unexpected keyword argument '%q'"), MP_OBJ_QSTR_VALUE(wanted_arg_name)); #endif } - mp_obj_dict_store(dict, kwargs[2 * i], kwargs[2 * i + 1]); + mp_map_elem_t *elem = mp_map_lookup(mp_obj_dict_get_map(dict), wanted_arg_name, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); + if (elem->value == MP_OBJ_NULL) { + elem->value = kwargs[2 * i + 1]; + } else { + goto error_multiple; + } continue2:; } diff --git a/py/bc.h b/py/bc.h index d620be8e82..b732028dbf 100644 --- a/py/bc.h +++ b/py/bc.h @@ -219,7 +219,7 @@ typedef struct _mp_module_context_t { // Outer level struct defining a compiled module. typedef struct _mp_compiled_module_t { - const mp_module_context_t *context; + mp_module_context_t *context; const struct _mp_raw_code_t *rc; #if MICROPY_PERSISTENT_CODE_SAVE bool has_native; diff --git a/py/binary.c b/py/binary.c index 60a74d6162..6d2075f3b4 100644 --- a/py/binary.c +++ b/py/binary.c @@ -36,8 +36,6 @@ #include "py/objint.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" - // Helpers to work with binary-encoded data #ifndef alignof @@ -217,7 +215,7 @@ long long mp_binary_get_int(size_t size, bool is_signed, bool big_endian, const val = -1; } for (uint i = 0; i < size; i++) { - // CIRCUITPY fix + // CIRCUITPY fix for undefined behavior on left shift val *= 256; val |= *src; src += delta; @@ -254,13 +252,15 @@ mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte *p_base, byte * #endif #if MICROPY_PY_BUILTINS_FLOAT } else if (val_type == 'f') { - union { uint32_t i; - float f; + union { + uint32_t i; + float f; } fpu = {val}; return mp_obj_new_float_from_f(fpu.f); } else if (val_type == 'd') { - union { uint64_t i; - double f; + union { + uint64_t i; + double f; } fpu = {val}; return mp_obj_new_float_from_d(fpu.f); #endif @@ -323,17 +323,19 @@ void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte *p #endif #if MICROPY_PY_BUILTINS_FLOAT case 'f': { - union { uint32_t i; - float f; + union { + uint32_t i; + float f; } fp_sp; fp_sp.f = mp_obj_get_float_to_f(val_in); val = fp_sp.i; break; } case 'd': { - union { uint64_t i64; - uint32_t i32[2]; - double f; + union { + uint64_t i64; + uint32_t i32[2]; + double f; } fp_dp; fp_dp.f = mp_obj_get_float_to_d(val_in); if (MP_BYTES_PER_OBJ_WORD == 8) { @@ -350,7 +352,7 @@ void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte *p default: { bool signed_type = is_signed(val_type); #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE - if (mp_obj_is_type(val_in, &mp_type_int)) { + if (mp_obj_is_exact_type(val_in, &mp_type_int)) { // It's a longint. mp_obj_int_buffer_overflow_check(val_in, size, signed_type); mp_obj_int_to_bytes_impl(val_in, struct_type == '>', size, p); @@ -398,7 +400,7 @@ void mp_binary_set_val_array(char typecode, void *p, size_t index, mp_obj_t val_ bool signed_type = is_signed(typecode); #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE - if (mp_obj_is_type(val_in, &mp_type_int)) { + if (mp_obj_is_exact_type(val_in, &mp_type_int)) { // It's a long int. mp_obj_int_buffer_overflow_check(val_in, size, signed_type); mp_obj_int_to_bytes_impl(val_in, MP_ENDIANNESS_BIG, diff --git a/py/builtin.h b/py/builtin.h index ab228278a7..7232142b77 100644 --- a/py/builtin.h +++ b/py/builtin.h @@ -64,7 +64,13 @@ MP_DECLARE_CONST_FUN_OBJ_KW(mp_builtin_open_obj); #endif +// A port can provide its own import handler by defining mp_builtin___import__. +#ifndef mp_builtin___import__ +#define mp_builtin___import__ mp_builtin___import___default +#endif mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args); +mp_obj_t mp_builtin___import___default(size_t n_args, const mp_obj_t *args); + mp_obj_t mp_micropython_mem_info(size_t n_args, const mp_obj_t *args); MP_DECLARE_CONST_FUN_OBJ_VAR(mp_builtin___build_class___obj); @@ -120,40 +126,16 @@ MP_DECLARE_CONST_FUN_OBJ_2(mp_op_getitem_obj); MP_DECLARE_CONST_FUN_OBJ_3(mp_op_setitem_obj); MP_DECLARE_CONST_FUN_OBJ_2(mp_op_delitem_obj); +// Modules needed by the runtime. +extern const mp_obj_dict_t mp_module_builtins_globals; extern const mp_obj_module_t mp_module___main__; extern const mp_obj_module_t mp_module_builtins; -extern const mp_obj_module_t mp_module_array; -extern const mp_obj_module_t mp_module_collections; -extern const mp_obj_module_t mp_module_io; -extern const mp_obj_module_t mp_module_math; -extern const mp_obj_module_t mp_module_cmath; -extern const mp_obj_module_t mp_module_micropython; -extern const mp_obj_module_t mp_module_ustruct; extern const mp_obj_module_t mp_module_sys; -extern const mp_obj_module_t mp_module_gc; -extern const mp_obj_module_t mp_module_thread; -extern const mp_obj_dict_t mp_module_builtins_globals; - -// extmod modules -extern const mp_obj_module_t mp_module_uasyncio; +// Modules needed by the parser when MICROPY_COMP_MODULE_CONST is enabled. extern const mp_obj_module_t mp_module_uerrno; extern const mp_obj_module_t mp_module_uctypes; -extern const mp_obj_module_t mp_module_uzlib; -extern const mp_obj_module_t mp_module_ujson; -extern const mp_obj_module_t mp_module_ure; -extern const mp_obj_module_t mp_module_uheapq; -extern const mp_obj_module_t mp_module_uhashlib; -extern const mp_obj_module_t mp_module_ucryptolib; -extern const mp_obj_module_t mp_module_ubinascii; -extern const mp_obj_module_t mp_module_urandom; -extern const mp_obj_module_t mp_module_uselect; -extern const mp_obj_module_t mp_module_utimeq; extern const mp_obj_module_t mp_module_machine; -extern const mp_obj_module_t mp_module_framebuf; -extern const mp_obj_module_t mp_module_btree; -extern const mp_obj_module_t mp_module_ubluetooth; -extern const mp_obj_module_t mp_module_uplatform; extern const char MICROPY_PY_BUILTINS_HELP_TEXT[]; diff --git a/py/builtinevex.c b/py/builtinevex.c index b6b27c2a67..173978ef52 100644 --- a/py/builtinevex.c +++ b/py/builtinevex.c @@ -31,8 +31,6 @@ #include "py/runtime.h" #include "py/builtin.h" -#include "supervisor/shared/translate/translate.h" - #if MICROPY_PY_BUILTINS_COMPILE typedef struct _mp_obj_code_t { @@ -40,10 +38,11 @@ typedef struct _mp_obj_code_t { mp_obj_t module_fun; } mp_obj_code_t; -STATIC const mp_obj_type_t mp_type_code = { - { &mp_type_type }, - .name = MP_QSTR_code, -}; +STATIC MP_DEFINE_CONST_OBJ_TYPE( + mp_type_code, + MP_QSTR_code, + MP_TYPE_FLAG_NONE + ); STATIC mp_obj_t code_execute(mp_obj_code_t *self, mp_obj_dict_t *globals, mp_obj_dict_t *locals) { // save context and set new context diff --git a/py/builtinhelp.c b/py/builtinhelp.c index c26a1a5490..fc922935ed 100644 --- a/py/builtinhelp.c +++ b/py/builtinhelp.c @@ -32,8 +32,6 @@ #include "py/mpconfig.h" #include "py/objmodule.h" -#include "supervisor/shared/translate/translate.h" - #if MICROPY_PY_BUILTINS_HELP const char mp_help_default_text[] = @@ -153,14 +151,20 @@ STATIC void mp_help_print_obj(const mp_obj_t obj) { if (type == &mp_type_type) { type = MP_OBJ_TO_PTR(obj); } - if (type->locals_dict != NULL) { - map = &type->locals_dict->map; + if (MP_OBJ_TYPE_HAS_SLOT(type, locals_dict)) { + map = &MP_OBJ_TYPE_GET_SLOT(type, locals_dict)->map; } } if (map != NULL) { for (uint i = 0; i < map->alloc; i++) { - if (map->table[i].key != MP_OBJ_NULL) { - mp_help_print_info_about_object(map->table[i].key, map->table[i].value); + mp_obj_t key = map->table[i].key; + if (key != MP_OBJ_NULL + #if MICROPY_MODULE_ATTR_DELEGATION + // MP_MODULE_ATTR_DELEGATION_ENTRY entries have MP_QSTRnull as qstr key. + && key != MP_OBJ_NEW_QSTR(MP_QSTRnull) + #endif + ) { + mp_help_print_info_about_object(key, map->table[i].value); } } } diff --git a/py/builtinimport.c b/py/builtinimport.c index bf47b2567e..a57841cd8c 100644 --- a/py/builtinimport.c +++ b/py/builtinimport.c @@ -38,8 +38,6 @@ #include "py/builtin.h" #include "py/frozenmod.h" -#include "supervisor/shared/translate/translate.h" - #if MICROPY_DEBUG_VERBOSE // print debugging info #define DEBUG_PRINT (1) #define DEBUG_printf DEBUG_printf @@ -48,6 +46,15 @@ #define DEBUG_printf(...) (void)0 #endif +#if MICROPY_MODULE_WEAK_LINKS +STATIC qstr make_weak_link_name(vstr_t *buffer, qstr name) { + vstr_reset(buffer); + vstr_add_char(buffer, 'u'); + vstr_add_str(buffer, qstr_str(name)); + return qstr_from_strn(buffer->buf, buffer->len); +} +#endif + #if MICROPY_ENABLE_EXTERNAL_IMPORT // Must be a string of one byte. @@ -162,7 +169,7 @@ 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(mp_module_context_t *context, const mp_raw_code_t *rc, const mp_module_context_t *mc, const char *source_name) { +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; #if MICROPY_PY___FILE__ @@ -182,7 +189,7 @@ STATIC void do_execute_raw_code(mp_module_context_t *context, const mp_raw_code_ nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { - mp_obj_t module_fun = mp_make_function_from_raw_code(rc, mc, NULL); + mp_obj_t module_fun = mp_make_function_from_raw_code(rc, context, NULL); mp_call_function_0(module_fun); // finish nlr block, restore context @@ -227,7 +234,7 @@ 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, module_obj, file_str + frozen_path_prefix_len); + do_execute_raw_code(module_obj, frozen->rc, file_str + frozen_path_prefix_len); return; } #endif @@ -239,8 +246,10 @@ STATIC void do_load(mp_module_context_t *module_obj, vstr_t *file) { // 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 = mp_raw_code_load_file(file_str, module_obj); - do_execute_raw_code(module_obj, cm.rc, cm.context, file_str); + 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); return; } #endif @@ -370,10 +379,7 @@ STATIC mp_obj_t process_import_at_level(qstr full_mod_name, qstr level_mod_name, // formerly known as "weak links"). #if MICROPY_MODULE_WEAK_LINKS if (stat == MP_IMPORT_STAT_NO_EXIST && module_obj == MP_OBJ_NULL) { - char *umodule_buf = vstr_str(path); - umodule_buf[0] = 'u'; - strcpy(umodule_buf + 1, qstr_str(level_mod_name)); - qstr umodule_name = qstr_from_str(umodule_buf); + qstr umodule_name = make_weak_link_name(path, level_mod_name); module_obj = mp_module_get_builtin(umodule_name); } #elif MICROPY_PY_SYS @@ -475,7 +481,7 @@ STATIC mp_obj_t process_import_at_level(qstr full_mod_name, qstr level_mod_name, return module_obj; } -mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) { +mp_obj_t mp_builtin___import___default(size_t n_args, const mp_obj_t *args) { #if DEBUG_PRINT DEBUG_printf("__import__:\n"); for (size_t i = 0; i < n_args; i++) { @@ -574,7 +580,11 @@ mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) { #else // MICROPY_ENABLE_EXTERNAL_IMPORT -mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) { +bool mp_obj_is_package(mp_obj_t module) { + return false; +} + +mp_obj_t mp_builtin___import___default(size_t n_args, const mp_obj_t *args) { // Check that it's not a relative import if (n_args >= 5 && MP_OBJ_SMALL_INT_VALUE(args[4]) != 0) { mp_raise_NotImplementedError(MP_ERROR_TEXT("relative import")); @@ -589,10 +599,8 @@ mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) { #if MICROPY_MODULE_WEAK_LINKS // Check if there is a weak link to this module - char umodule_buf[MICROPY_ALLOC_PATH_MAX]; - umodule_buf[0] = 'u'; - strcpy(umodule_buf + 1, args[0]); - qstr umodule_name_qstr = qstr_from_str(umodule_buf); + VSTR_FIXED(umodule_path, MICROPY_ALLOC_PATH_MAX); + qstr umodule_name_qstr = make_weak_link_name(&umodule_path, module_name_qstr); module_obj = mp_module_get_loaded_or_builtin(umodule_name_qstr); if (module_obj != MP_OBJ_NULL) { return module_obj; diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index ab72eddd40..8ee5b2c9ec 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -108,6 +108,7 @@ extern void common_hal_mcu_enable_interrupts(void); #define MICROPY_PY_ARRAY_SLICE_ASSIGN (1) #define MICROPY_PY_ATTRTUPLE (1) #define MICROPY_PY_BUILTINS_BYTEARRAY (1) +#define MICROPY_PY_BUILTINS_BYTES_HEX (1) #define MICROPY_PY_BUILTINS_ENUMERATE (1) #define MICROPY_PY_BUILTINS_FILTER (1) #define MICROPY_PY_BUILTINS_HELP (1) @@ -444,7 +445,7 @@ struct _supervisor_allocation_node; vstr_t *repl_line; \ mp_obj_t pew_singleton; \ mp_obj_t rtc_time_source; \ - const char *readline_hist[8]; \ + const char *readline_hist[MICROPY_READLINE_HISTORY_SIZE]; \ struct _supervisor_allocation_node *first_embedded_allocation; \ void background_callback_run_all(void); diff --git a/py/compile.c b/py/compile.c index 82ab354eb4..b1addc76fc 100644 --- a/py/compile.c +++ b/py/compile.c @@ -39,8 +39,6 @@ #include "py/persistentcode.h" #include "py/smallint.h" -#include "supervisor/shared/translate/translate.h" - #if MICROPY_ENABLE_COMPILER // TODO need to mangle __attr names @@ -1288,6 +1286,14 @@ STATIC void compile_declare_nonlocal(compiler_t *comp, mp_parse_node_t pn, id_in } } +STATIC void compile_declare_global_or_nonlocal(compiler_t *comp, mp_parse_node_t pn, id_info_t *id_info, bool is_global) { + if (is_global) { + compile_declare_global(comp, pn, id_info); + } else { + compile_declare_nonlocal(comp, pn, id_info); + } +} + STATIC void compile_global_nonlocal_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { if (comp->pass == MP_PASS_SCOPE) { bool is_global = MP_PARSE_NODE_STRUCT_KIND(pns) == PN_global_stmt; @@ -1302,11 +1308,7 @@ STATIC void compile_global_nonlocal_stmt(compiler_t *comp, mp_parse_node_struct_ for (size_t i = 0; i < n; i++) { qstr qst = MP_PARSE_NODE_LEAF_ARG(nodes[i]); id_info_t *id_info = scope_find_or_add_id(comp->scope_cur, qst, ID_INFO_KIND_UNDECIDED); - if (is_global) { - compile_declare_global(comp, (mp_parse_node_t)pns, id_info); - } else { - compile_declare_nonlocal(comp, (mp_parse_node_t)pns, id_info); - } + compile_declare_global_or_nonlocal(comp, (mp_parse_node_t)pns, id_info, is_global); } } } @@ -1344,12 +1346,8 @@ STATIC void compile_if_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { goto done; } - if ( - // optimisation: don't jump over non-existent elif/else blocks - !(MP_PARSE_NODE_IS_NULL(pns->nodes[2]) && MP_PARSE_NODE_IS_NULL(pns->nodes[3])) - // optimisation: don't jump if last instruction was return - && !EMIT(last_emit_was_return_value) - ) { + // optimisation: don't jump over non-existent elif/else blocks + if (!(MP_PARSE_NODE_IS_NULL(pns->nodes[2]) && MP_PARSE_NODE_IS_NULL(pns->nodes[3]))) { // jump over elif/else blocks EMIT_ARG(jump, l_end); } @@ -1376,10 +1374,7 @@ STATIC void compile_if_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { goto done; } - // optimisation: don't jump if last instruction was return - if (!EMIT(last_emit_was_return_value)) { - EMIT_ARG(jump, l_end); - } + EMIT_ARG(jump, l_end); EMIT_ARG(label_assign, l_fail); } } @@ -1594,9 +1589,7 @@ STATIC void compile_for_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { EMIT_ARG(for_iter, pop_label); c_assign(comp, pns->nodes[0], ASSIGN_STORE); // variable compile_node(comp, pns->nodes[2]); // body - if (!EMIT(last_emit_was_return_value)) { - EMIT_ARG(jump, continue_label); - } + EMIT_ARG(jump, continue_label); EMIT_ARG(label_assign, pop_label); EMIT(for_iter_end); @@ -2155,13 +2148,30 @@ STATIC void compile_namedexpr_helper(compiler_t *comp, mp_parse_node_t pn_name, } compile_node(comp, pn_expr); EMIT(dup_top); - scope_t *old_scope = comp->scope_cur; - if (SCOPE_IS_COMP_LIKE(comp->scope_cur->kind)) { - // Use parent's scope for assigned value so it can "escape" - comp->scope_cur = comp->scope_cur->parent; + + qstr target = MP_PARSE_NODE_LEAF_ARG(pn_name); + + // When a variable is assigned via := in a comprehension then that variable is bound to + // the parent scope. Any global or nonlocal declarations in the parent scope are honoured. + // For details see: https://peps.python.org/pep-0572/#scope-of-the-target + if (comp->pass == MP_PASS_SCOPE && SCOPE_IS_COMP_LIKE(comp->scope_cur->kind)) { + id_info_t *id_info_parent = mp_emit_common_get_id_for_modification(comp->scope_cur->parent, target); + if (id_info_parent->kind == ID_INFO_KIND_GLOBAL_EXPLICIT) { + scope_find_or_add_id(comp->scope_cur, target, ID_INFO_KIND_GLOBAL_EXPLICIT); + } else { + id_info_t *id_info = scope_find_or_add_id(comp->scope_cur, target, ID_INFO_KIND_UNDECIDED); + bool is_global = comp->scope_cur->parent->parent == NULL; // comprehension is defined in outer scope + if (!is_global && id_info->kind == ID_INFO_KIND_GLOBAL_IMPLICIT) { + // Variable was already referenced but now needs to be closed over, so reset the kind + // such that scope_check_to_close_over() is called in compile_declare_nonlocal(). + id_info->kind = ID_INFO_KIND_UNDECIDED; + } + compile_declare_global_or_nonlocal(comp, pn_name, id_info, is_global); + } } - compile_store_id(comp, MP_PARSE_NODE_LEAF_ARG(pn_name)); - comp->scope_cur = old_scope; + + // Do the store to the target variable. + compile_store_id(comp, target); } STATIC void compile_namedexpr(compiler_t *comp, mp_parse_node_struct_t *pns) { @@ -3075,11 +3085,8 @@ STATIC bool compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) { } compile_node(comp, pns->nodes[3]); // 3 is function body - // emit return if it wasn't the last opcode - if (!EMIT(last_emit_was_return_value)) { - EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE); - EMIT(return_value); - } + EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE); + EMIT(return_value); } else if (scope->kind == SCOPE_LAMBDA) { assert(MP_PARSE_NODE_IS_STRUCT(scope->pn)); mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)scope->pn; @@ -3325,12 +3332,13 @@ STATIC void compile_scope_inline_asm(compiler_t *comp, scope_t *scope, pass_kind if (pass > MP_PASS_SCOPE) { mp_int_t bytesize = MP_PARSE_NODE_LEAF_SMALL_INT(pn_arg[0]); for (uint j = 1; j < n_args; j++) { - if (!MP_PARSE_NODE_IS_SMALL_INT(pn_arg[j])) { + mp_obj_t int_obj; + if (!mp_parse_node_get_int_maybe(pn_arg[j], &int_obj)) { compile_syntax_error(comp, nodes[i], MP_ERROR_TEXT("'data' requires integer arguments")); return; } mp_asm_base_data((mp_asm_base_t *)comp->emit_inline_asm, - bytesize, MP_PARSE_NODE_LEAF_SMALL_INT(pn_arg[j])); + bytesize, mp_obj_int_get_truncated(int_obj)); } } } else { @@ -3465,7 +3473,7 @@ STATIC void scope_compute_things(scope_t *scope) { #if !MICROPY_PERSISTENT_CODE_SAVE STATIC #endif -mp_compiled_module_t mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl, mp_module_context_t *context) { +void mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl, mp_compiled_module_t *cm) { // put compiler state on the stack, it's relatively small compiler_t comp_state = {0}; compiler_t *comp = &comp_state; @@ -3606,26 +3614,24 @@ mp_compiled_module_t mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr so } // construct the global qstr/const table for this module - mp_compiled_module_t cm; - cm.rc = module_scope->raw_code; - cm.context = context; + cm->rc = module_scope->raw_code; #if MICROPY_PERSISTENT_CODE_SAVE - cm.has_native = false; + cm->has_native = false; #if MICROPY_EMIT_NATIVE if (emit_native != NULL) { - cm.has_native = true; + cm->has_native = true; } #endif #if MICROPY_EMIT_INLINE_ASM if (comp->emit_inline_asm != NULL) { - cm.has_native = true; + cm->has_native = true; } #endif - cm.n_qstr = comp->emit_common.qstr_map.used; - cm.n_obj = comp->emit_common.const_obj_list.len; + cm->n_qstr = comp->emit_common.qstr_map.used; + cm->n_obj = comp->emit_common.const_obj_list.len; #endif if (comp->compile_error == MP_OBJ_NULL) { - mp_emit_common_populate_module_context(&comp->emit_common, source_file, context); + mp_emit_common_populate_module_context(&comp->emit_common, source_file, cm->context); #if MICROPY_DEBUG_PRINTERS // now that the module context is valid, the raw codes can be printed @@ -3633,7 +3639,7 @@ mp_compiled_module_t mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr so for (scope_t *s = comp->scope_head; s != NULL; s = s->next) { mp_raw_code_t *rc = s->raw_code; if (rc->kind == MP_CODE_BYTECODE) { - mp_bytecode_print(&mp_plat_print, rc, &cm.context->constants); + mp_bytecode_print(&mp_plat_print, rc, &cm->context->constants); } } } @@ -3667,14 +3673,13 @@ mp_compiled_module_t mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr so if (comp->compile_error != MP_OBJ_NULL) { nlr_raise(comp->compile_error); } - - return cm; } mp_obj_t mp_compile(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl) { - mp_module_context_t *context = m_new_obj(mp_module_context_t); - context->module.globals = mp_globals_get(); - mp_compiled_module_t cm = mp_compile_to_raw_code(parse_tree, source_file, is_repl, context); + mp_compiled_module_t cm; + cm.context = m_new_obj(mp_module_context_t); + cm.context->module.globals = mp_globals_get(); + mp_compile_to_raw_code(parse_tree, source_file, is_repl, &cm); // return function that executes the outer module return mp_make_function_from_raw_code(cm.rc, cm.context, NULL); } diff --git a/py/compile.h b/py/compile.h index ae87bf2a04..5e0fd8b31c 100644 --- a/py/compile.h +++ b/py/compile.h @@ -37,7 +37,7 @@ mp_obj_t mp_compile(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl) #if MICROPY_PERSISTENT_CODE_SAVE // this has the same semantics as mp_compile -mp_compiled_module_t mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl, mp_module_context_t *globals); +void mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl, mp_compiled_module_t *cm); #endif // this is implemented in runtime.c diff --git a/py/dynruntime.h b/py/dynruntime.h index b7a9a89156..9303638055 100644 --- a/py/dynruntime.h +++ b/py/dynruntime.h @@ -105,7 +105,7 @@ static inline void *m_realloc_dyn(void *ptr, size_t new_num_bytes) { #define mp_const_none ((mp_obj_t)mp_fun_table.const_none) #define mp_const_false ((mp_obj_t)mp_fun_table.const_false) #define mp_const_true ((mp_obj_t)mp_fun_table.const_true) -#define mp_const_empty_bytes (mp_type_bytes.make_new(NULL, 0, 0, NULL)) +#define mp_const_empty_bytes (MP_OBJ_TYPE_GET_SLOT(&mp_type_bytes, make_new)(NULL, 0, 0, NULL)) #define mp_const_empty_tuple (mp_fun_table.new_tuple(0, NULL)) #define mp_obj_new_bool(b) ((b) ? (mp_obj_t)mp_fun_table.const_true : (mp_obj_t)mp_fun_table.const_false) @@ -127,6 +127,7 @@ static inline void *m_realloc_dyn(void *ptr, size_t new_num_bytes) { #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_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)) #define mp_obj_len(o) (mp_obj_len_dyn(o)) #define mp_obj_subscr(base, index, val) (mp_fun_table.obj_subscr((base), (index), (val))) @@ -151,7 +152,7 @@ static inline mp_obj_t mp_obj_cast_to_native_base_dyn(mp_obj_t self_in, mp_const if (MP_OBJ_FROM_PTR(self_type) == native_type) { return self_in; - } else if (self_type->parent != native_type) { + } else if (MP_OBJ_TYPE_GET_SLOT_OR_NULL(self_type, parent) != native_type) { // The self_in object is not a direct descendant of native_type, so fail the cast. // This is a very simple version of mp_obj_is_subclass_fast that could be improved. return MP_OBJ_NULL; diff --git a/py/dynruntime.mk b/py/dynruntime.mk index 09cbb2dd37..62db43ad14 100644 --- a/py/dynruntime.mk +++ b/py/dynruntime.mk @@ -7,7 +7,7 @@ ECHO = @echo RM = /bin/rm MKDIR = /bin/mkdir PYTHON = python3 -MPY_CROSS = $(MPY_DIR)/mpy-cross/mpy-cross +MPY_CROSS = $(MPY_DIR)/mpy-cross/build/mpy-cross MPY_TOOL = $(PYTHON) $(MPY_DIR)/tools/mpy-tool.py MPY_LD = $(PYTHON) $(MPY_DIR)/tools/mpy_ld.py @@ -35,7 +35,7 @@ CFLAGS += -U _FORTIFY_SOURCE # prevent use of __*_chk libc functions MPY_CROSS_FLAGS += -march=$(ARCH) -SRC_O += $(addprefix $(BUILD)/, $(patsubst %.c,%.o,$(filter %.c,$(SRC)))) +SRC_O += $(addprefix $(BUILD)/, $(patsubst %.c,%.o,$(filter %.c,$(SRC))) $(patsubst %.S,%.o,$(filter %.S,$(SRC)))) SRC_MPY += $(addprefix $(BUILD)/, $(patsubst %.py,%.mpy,$(filter %.py,$(SRC)))) ################################################################################ @@ -134,6 +134,11 @@ $(BUILD)/%.o: %.c $(CONFIG_H) Makefile $(ECHO) "CC $<" $(Q)$(CROSS)gcc $(CFLAGS) -o $@ -c $< +# Build .o from .S source files +$(BUILD)/%.o: %.S $(CONFIG_H) Makefile + $(ECHO) "AS $<" + $(Q)$(CROSS)gcc $(CFLAGS) -o $@ -c $< + # Build .mpy from .py source files $(BUILD)/%.mpy: %.py $(ECHO) "MPY $<" diff --git a/py/emit.h b/py/emit.h index 608734552a..26f978ba59 100644 --- a/py/emit.h +++ b/py/emit.h @@ -115,7 +115,6 @@ typedef struct _emit_method_table_t { void (*start_pass)(emit_t *emit, pass_kind_t pass, scope_t *scope); bool (*end_pass)(emit_t *emit); - bool (*last_emit_was_return_value)(emit_t *emit); void (*adjust_stack_size)(emit_t *emit, mp_int_t delta); void (*set_source_line)(emit_t *emit, mp_uint_t line); @@ -192,7 +191,7 @@ static inline void mp_emit_common_get_id_for_load(scope_t *scope, qstr qst) { scope_find_or_add_id(scope, qst, ID_INFO_KIND_GLOBAL_IMPLICIT); } -void mp_emit_common_get_id_for_modification(scope_t *scope, qstr qst); +id_info_t *mp_emit_common_get_id_for_modification(scope_t *scope, qstr qst); void mp_emit_common_id_op(emit_t *emit, const mp_emit_method_table_id_ops_t *emit_method_table, scope_t *scope, qstr qst); extern const emit_method_table_t emit_bc_method_table; @@ -227,7 +226,6 @@ void emit_native_xtensawin_free(emit_t *emit); void mp_emit_bc_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope); bool mp_emit_bc_end_pass(emit_t *emit); -bool mp_emit_bc_last_emit_was_return_value(emit_t *emit); void mp_emit_bc_adjust_stack_size(emit_t *emit, mp_int_t delta); void mp_emit_bc_set_source_line(emit_t *emit, mp_uint_t line); diff --git a/py/emitbc.c b/py/emitbc.c index 2007975c5e..70a4d8b12e 100644 --- a/py/emitbc.c +++ b/py/emitbc.c @@ -47,7 +47,11 @@ struct _emit_t { byte dummy_data[DUMMY_DATA_SIZE]; pass_kind_t pass : 8; - mp_uint_t last_emit_was_return_value : 8; + + // Set to true if the code generator should suppress emitted code due to it + // being dead code. This can happen when opcodes immediately follow an + // unconditional flow control (eg jump or raise). + bool suppress; int stack_size; @@ -141,6 +145,9 @@ STATIC void emit_write_code_info_bytes_lines(emit_t *emit, mp_uint_t bytes_to_sk // all functions must go through this one to emit byte code STATIC uint8_t *emit_get_cur_to_write_bytecode(void *emit_in, size_t num_bytes_to_write) { emit_t *emit = emit_in; + if (emit->suppress) { + return emit->dummy_data; + } if (emit->pass < MP_PASS_EMIT) { emit->bytecode_offset += num_bytes_to_write; return emit->dummy_data; @@ -223,6 +230,10 @@ STATIC void emit_write_bytecode_byte_child(emit_t *emit, int stack_adj, byte b, STATIC void emit_write_bytecode_byte_label(emit_t *emit, int stack_adj, byte b1, mp_uint_t label) { mp_emit_bc_adjust_stack_size(emit, stack_adj); + if (emit->suppress) { + return; + } + // Determine if the jump offset is signed or unsigned, based on the opcode. const bool is_signed = b1 <= MP_BC_POP_JUMP_IF_FALSE; @@ -272,7 +283,7 @@ STATIC void emit_write_bytecode_byte_label(emit_t *emit, int stack_adj, byte b1, void mp_emit_bc_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope) { emit->pass = pass; emit->stack_size = 0; - emit->last_emit_was_return_value = false; + emit->suppress = false; emit->scope = scope; emit->last_source_line_offset = 0; emit->last_source_line = 1; @@ -397,10 +408,6 @@ bool mp_emit_bc_end_pass(emit_t *emit) { return true; } -bool mp_emit_bc_last_emit_was_return_value(emit_t *emit) { - return emit->last_emit_was_return_value; -} - void mp_emit_bc_adjust_stack_size(emit_t *emit, mp_int_t delta) { if (emit->pass == MP_PASS_SCOPE) { return; @@ -410,7 +417,6 @@ void mp_emit_bc_adjust_stack_size(emit_t *emit, mp_int_t delta) { if (emit->stack_size > emit->scope->stack_size) { emit->scope->stack_size = emit->stack_size; } - emit->last_emit_was_return_value = false; } void mp_emit_bc_set_source_line(emit_t *emit, mp_uint_t source_line) { @@ -433,6 +439,10 @@ void mp_emit_bc_set_source_line(emit_t *emit, mp_uint_t source_line) { } void mp_emit_bc_label_assign(emit_t *emit, mp_uint_t l) { + // Assiging a label ends any dead-code region, and all following opcodes + // should be emitted (until another unconditional flow control). + emit->suppress = false; + mp_emit_bc_adjust_stack_size(emit, 0); if (emit->pass == MP_PASS_SCOPE) { return; @@ -596,6 +606,7 @@ void mp_emit_bc_rot_three(emit_t *emit) { void mp_emit_bc_jump(emit_t *emit, mp_uint_t label) { emit_write_bytecode_byte_label(emit, 0, MP_BC_JUMP, label); + emit->suppress = true; } void mp_emit_bc_pop_jump_if(emit_t *emit, bool cond, mp_uint_t label) { @@ -629,6 +640,7 @@ void mp_emit_bc_unwind_jump(emit_t *emit, mp_uint_t label, mp_uint_t except_dept emit_write_bytecode_byte_label(emit, 0, MP_BC_UNWIND_JUMP, label & ~MP_EMIT_BREAK_FROM_FOR); emit_write_bytecode_raw_byte(emit, ((label & MP_EMIT_BREAK_FROM_FOR) ? 0x80 : 0) | except_depth); } + emit->suppress = true; } void mp_emit_bc_setup_block(emit_t *emit, mp_uint_t label, int kind) { @@ -670,6 +682,7 @@ void mp_emit_bc_for_iter_end(emit_t *emit) { void mp_emit_bc_pop_except_jump(emit_t *emit, mp_uint_t label, bool within_exc_handler) { (void)within_exc_handler; emit_write_bytecode_byte_label(emit, 0, MP_BC_POP_EXCEPT_JUMP, label); + emit->suppress = true; } void mp_emit_bc_unary_op(emit_t *emit, mp_unary_op_t op) { @@ -773,7 +786,7 @@ void mp_emit_bc_call_method(emit_t *emit, mp_uint_t n_positional, mp_uint_t n_ke void mp_emit_bc_return_value(emit_t *emit) { emit_write_bytecode_byte(emit, -1, MP_BC_RETURN_VALUE); - emit->last_emit_was_return_value = true; + emit->suppress = true; } void mp_emit_bc_raise_varargs(emit_t *emit, mp_uint_t n_args) { @@ -781,6 +794,7 @@ void mp_emit_bc_raise_varargs(emit_t *emit, mp_uint_t n_args) { MP_STATIC_ASSERT(MP_BC_RAISE_LAST + 2 == MP_BC_RAISE_FROM); assert(n_args <= 2); emit_write_bytecode_byte(emit, -n_args, MP_BC_RAISE_LAST + n_args); + emit->suppress = true; } void mp_emit_bc_yield(emit_t *emit, int kind) { @@ -806,7 +820,6 @@ const emit_method_table_t emit_bc_method_table = { mp_emit_bc_start_pass, mp_emit_bc_end_pass, - mp_emit_bc_last_emit_was_return_value, mp_emit_bc_adjust_stack_size, mp_emit_bc_set_source_line, diff --git a/py/emitcommon.c b/py/emitcommon.c index 679ef1d973..a9eb6e2021 100644 --- a/py/emitcommon.c +++ b/py/emitcommon.c @@ -86,7 +86,7 @@ size_t mp_emit_common_use_const_obj(mp_emit_common_t *emit, mp_obj_t const_obj) return emit->const_obj_list.len - 1; } -void mp_emit_common_get_id_for_modification(scope_t *scope, qstr qst) { +id_info_t *mp_emit_common_get_id_for_modification(scope_t *scope, qstr qst) { // name adding/lookup id_info_t *id = scope_find_or_add_id(scope, qst, ID_INFO_KIND_GLOBAL_IMPLICIT); if (id->kind == ID_INFO_KIND_GLOBAL_IMPLICIT) { @@ -98,6 +98,7 @@ void mp_emit_common_get_id_for_modification(scope_t *scope, qstr qst) { id->kind = ID_INFO_KIND_GLOBAL_IMPLICIT_ASSIGNED; } } + return id; } void mp_emit_common_id_op(emit_t *emit, const mp_emit_method_table_id_ops_t *emit_method_table, scope_t *scope, qstr qst) { diff --git a/py/emitnative.c b/py/emitnative.c index 9fafa1104a..619f75bdbb 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -305,8 +305,6 @@ struct _emit_t { uint16_t n_info; uint16_t n_cell; - bool last_emit_was_return_value; - scope_t *scope; ASM_T *as; @@ -399,7 +397,6 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop emit->pass = pass; emit->do_viper_types = scope->emit_options == MP_EMIT_OPT_VIPER; emit->stack_size = 0; - emit->last_emit_was_return_value = false; emit->scope = scope; // allocate memory for keeping track of the types of locals @@ -433,7 +430,7 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop // local variables begin unbound, and have unknown type for (mp_uint_t i = num_args; i < emit->local_vtype_alloc; i++) { - emit->local_vtype[i] = VTYPE_UNBOUND; + emit->local_vtype[i] = emit->do_viper_types ? VTYPE_UNBOUND : VTYPE_PYOBJ; } // values on stack begin unbound @@ -762,10 +759,6 @@ STATIC bool emit_native_end_pass(emit_t *emit) { return true; } -STATIC bool emit_native_last_emit_was_return_value(emit_t *emit) { - return emit->last_emit_was_return_value; -} - STATIC void ensure_extra_stack(emit_t *emit, size_t delta) { if (emit->stack_size + delta > emit->stack_info_alloc) { size_t new_alloc = (emit->stack_size + delta + 8) & ~3; @@ -822,7 +815,7 @@ STATIC void emit_native_set_source_line(emit_t *emit, mp_uint_t source_line) { // this must be called at start of emit functions STATIC void emit_native_pre(emit_t *emit) { - emit->last_emit_was_return_value = false; + (void)emit; } // depth==0 is top, depth==1 is before top, etc @@ -946,7 +939,6 @@ STATIC void emit_fold_stack_top(emit_t *emit, int reg_dest) { // If stacked value is in a register and the register is not r1 or r2, then // *reg_dest is set to that register. Otherwise the value is put in *reg_dest. STATIC void emit_pre_pop_reg_flexible(emit_t *emit, vtype_kind_t *vtype, int *reg_dest, int not_r1, int not_r2) { - emit->last_emit_was_return_value = false; stack_info_t *si = peek_stack(emit, 0); if (si->kind == STACK_REG && si->data.u_reg != not_r1 && si->data.u_reg != not_r2) { *vtype = si->vtype; @@ -959,12 +951,10 @@ STATIC void emit_pre_pop_reg_flexible(emit_t *emit, vtype_kind_t *vtype, int *re } STATIC void emit_pre_pop_discard(emit_t *emit) { - emit->last_emit_was_return_value = false; adjust_stack(emit, -1); } STATIC void emit_pre_pop_reg(emit_t *emit, vtype_kind_t *vtype, int reg_dest) { - emit->last_emit_was_return_value = false; emit_access_stack(emit, 1, vtype, reg_dest); adjust_stack(emit, -1); } @@ -1566,6 +1556,7 @@ STATIC void emit_native_load_subscr(emit_t *emit) { break; } #endif + need_reg_single(emit, reg_index, 0); ASM_MOV_REG_IMM(emit->as, reg_index, index_value); ASM_ADD_REG_REG(emit->as, reg_index, reg_base); // add index to base reg_base = reg_index; @@ -1583,6 +1574,7 @@ STATIC void emit_native_load_subscr(emit_t *emit) { break; } #endif + need_reg_single(emit, reg_index, 0); ASM_MOV_REG_IMM(emit->as, reg_index, index_value << 1); ASM_ADD_REG_REG(emit->as, reg_index, reg_base); // add 2*index to base reg_base = reg_index; @@ -1600,6 +1592,7 @@ STATIC void emit_native_load_subscr(emit_t *emit) { break; } #endif + need_reg_single(emit, reg_index, 0); ASM_MOV_REG_IMM(emit->as, reg_index, index_value << 2); ASM_ADD_REG_REG(emit->as, reg_index, reg_base); // add 4*index to base reg_base = reg_index; @@ -1722,10 +1715,18 @@ STATIC void emit_native_store_global(emit_t *emit, qstr qst, int kind) { } STATIC void emit_native_store_attr(emit_t *emit, qstr qst) { - vtype_kind_t vtype_base, vtype_val; - emit_pre_pop_reg_reg(emit, &vtype_base, REG_ARG_1, &vtype_val, REG_ARG_3); // arg1 = base, arg3 = value + vtype_kind_t vtype_base; + vtype_kind_t vtype_val = peek_vtype(emit, 1); + if (vtype_val == VTYPE_PYOBJ) { + emit_pre_pop_reg_reg(emit, &vtype_base, REG_ARG_1, &vtype_val, REG_ARG_3); // arg1 = base, arg3 = value + } else { + emit_access_stack(emit, 2, &vtype_val, REG_ARG_1); // arg1 = value + emit_call_with_imm_arg(emit, MP_F_CONVERT_NATIVE_TO_OBJ, vtype_val, REG_ARG_2); // arg2 = type + ASM_MOV_REG_REG(emit->as, REG_ARG_3, REG_RET); // arg3 = value (converted) + emit_pre_pop_reg(emit, &vtype_base, REG_ARG_1); // arg1 = base + adjust_stack(emit, -1); // pop value + } assert(vtype_base == VTYPE_PYOBJ); - assert(vtype_val == VTYPE_PYOBJ); emit_call_with_qstr_arg(emit, MP_F_STORE_ATTR, qst, REG_ARG_2); // arg2 = attribute name emit_post(emit); } @@ -2005,6 +2006,7 @@ STATIC void emit_native_jump(emit_t *emit, mp_uint_t label) { need_stack_settled(emit); ASM_JUMP(emit->as, label); emit_post(emit); + mp_asm_base_suppress_code(&emit->as->base); } STATIC void emit_native_jump_helper(emit_t *emit, bool cond, mp_uint_t label, bool pop) { @@ -2392,14 +2394,13 @@ STATIC void emit_native_binary_op(emit_t *emit, mp_binary_op_t op) { } else if (op == MP_BINARY_OP_MULTIPLY) { ASM_MUL_REG_REG(emit->as, REG_ARG_2, reg_rhs); emit_post_push_reg(emit, vtype_lhs, REG_ARG_2); - } else if (MP_BINARY_OP_LESS <= op && op <= MP_BINARY_OP_NOT_EQUAL) { - // comparison ops are (in enum order): - // MP_BINARY_OP_LESS - // MP_BINARY_OP_MORE - // MP_BINARY_OP_EQUAL - // MP_BINARY_OP_LESS_EQUAL - // MP_BINARY_OP_MORE_EQUAL - // MP_BINARY_OP_NOT_EQUAL + } else if (op == MP_BINARY_OP_LESS + || op == MP_BINARY_OP_MORE + || op == MP_BINARY_OP_EQUAL + || op == MP_BINARY_OP_LESS_EQUAL + || op == MP_BINARY_OP_MORE_EQUAL + || op == MP_BINARY_OP_NOT_EQUAL) { + // comparison ops if (vtype_lhs != vtype_rhs) { EMIT_NATIVE_VIPER_TYPE_ERROR(emit, MP_ERROR_TEXT("comparison of int and uint")); @@ -2800,7 +2801,6 @@ STATIC void emit_native_return_value(emit_t *emit) { // Do the unwinding jump to get to the return handler emit_native_unwind_jump(emit, emit->exit_label, emit->exc_stack_size); - emit->last_emit_was_return_value = true; return; } @@ -2838,7 +2838,6 @@ STATIC void emit_native_return_value(emit_t *emit) { ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_RET_VAL(emit), REG_PARENT_RET); } emit_native_unwind_jump(emit, emit->exit_label, emit->exc_stack_size); - emit->last_emit_was_return_value = true; } STATIC void emit_native_raise_varargs(emit_t *emit, mp_uint_t n_args) { @@ -2851,6 +2850,7 @@ STATIC void emit_native_raise_varargs(emit_t *emit, mp_uint_t n_args) { } // TODO probably make this 1 call to the runtime (which could even call convert, native_raise(obj, type)) emit_call(emit, MP_F_NATIVE_RAISE); + mp_asm_base_suppress_code(&emit->as->base); } STATIC void emit_native_yield(emit_t *emit, int kind) { @@ -2957,7 +2957,6 @@ const emit_method_table_t EXPORT_FUN(method_table) = { emit_native_start_pass, emit_native_end_pass, - emit_native_last_emit_was_return_value, emit_native_adjust_stack_size, emit_native_set_source_line, diff --git a/py/enum.c b/py/enum.c index 22b9090196..0d1868576f 100644 --- a/py/enum.c +++ b/py/enum.c @@ -27,8 +27,6 @@ #include "py/enum.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" - mp_obj_t cp_enum_find(const mp_obj_type_t *type, int value) { const mp_obj_dict_t *dict = type->locals_dict; for (size_t i = 0; i < dict->map.used; i++) { diff --git a/py/formatfloat.c b/py/formatfloat.c index 854944976f..050d3a9dfb 100644 --- a/py/formatfloat.c +++ b/py/formatfloat.c @@ -1,5 +1,3 @@ -// CIRCUITPY: https://github.com/adafruit/circuitpython/pull/6722 -// post v1.19.1. Remove this comment during v1.20 merge. /* * This file is part of the MicroPython project, http://micropython.org/ * @@ -64,26 +62,20 @@ #define FPMIN_BUF_SIZE 6 // +9e+99 #define FLT_SIGN_MASK 0x80000000 -#define FLT_EXP_MASK 0x7F800000 -#define FLT_MAN_MASK 0x007FFFFF -union floatbits { - float f; - uint32_t u; -}; static inline int fp_signbit(float x) { - union floatbits fb = {x}; - return fb.u & FLT_SIGN_MASK; + mp_float_union_t fb = {x}; + return fb.i & FLT_SIGN_MASK; } #define fp_isnan(x) isnan(x) #define fp_isinf(x) isinf(x) static inline int fp_iszero(float x) { - union floatbits fb = {x}; - return fb.u == 0; + mp_float_union_t fb = {x}; + return fb.i == 0; } static inline int fp_isless1(float x) { - union floatbits fb = {x}; - return fb.u < 0x3f800000; + mp_float_union_t fb = {x}; + return fb.i < 0x3f800000; } #elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE @@ -101,28 +93,11 @@ static inline int fp_isless1(float x) { #endif // MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT/DOUBLE -static inline int fp_ge_eps(FPTYPE x, FPTYPE y) { - mp_float_union_t fb_y = {y}; - // Back off 2 eps. - // This is valid for almost all values, but in practice - // it's only used when y = 1eX for X>=0. - fb_y.i -= 2; - return x >= fb_y.f; +static inline int fp_expval(FPTYPE x) { + mp_float_union_t fb = {x}; + return (int)((fb.i >> MP_FLOAT_FRAC_BITS) & (~(0xFFFFFFFF << MP_FLOAT_EXP_BITS))) - MP_FLOAT_EXP_OFFSET; } -static const FPTYPE g_pos_pow[] = { - #if FPDECEXP > 32 - MICROPY_FLOAT_CONST(1e256), MICROPY_FLOAT_CONST(1e128), MICROPY_FLOAT_CONST(1e64), - #endif - MICROPY_FLOAT_CONST(1e32), MICROPY_FLOAT_CONST(1e16), MICROPY_FLOAT_CONST(1e8), MICROPY_FLOAT_CONST(1e4), MICROPY_FLOAT_CONST(1e2), MICROPY_FLOAT_CONST(1e1) -}; -static const FPTYPE g_neg_pow[] = { - #if FPDECEXP > 32 - MICROPY_FLOAT_CONST(1e-256), MICROPY_FLOAT_CONST(1e-128), MICROPY_FLOAT_CONST(1e-64), - #endif - MICROPY_FLOAT_CONST(1e-32), MICROPY_FLOAT_CONST(1e-16), MICROPY_FLOAT_CONST(1e-8), MICROPY_FLOAT_CONST(1e-4), MICROPY_FLOAT_CONST(1e-2), MICROPY_FLOAT_CONST(1e-1) -}; - int mp_format_float(FPTYPE f, char *buf, size_t buf_size, char fmt, int prec, char sign) { char *s = buf; @@ -179,14 +154,15 @@ int mp_format_float(FPTYPE f, char *buf, size_t buf_size, char fmt, int prec, ch if (fmt == 'g' && prec == 0) { prec = 1; } - int e, e1; + int e; int dec = 0; char e_sign = '\0'; int num_digits = 0; - const FPTYPE *pos_pow = g_pos_pow; - const FPTYPE *neg_pow = g_neg_pow; int signed_e = 0; + // Approximate power of 10 exponent from binary exponent. + // abs(e_guess) is lower bound on abs(power of 10 exponent). + int e_guess = (int)(fp_expval(f) * FPCONST(0.3010299956639812)); // 1/log2(10). if (fp_iszero(f)) { e = 0; if (fmt == 'f') { @@ -205,25 +181,18 @@ int mp_format_float(FPTYPE f, char *buf, size_t buf_size, char fmt, int prec, ch } } } else if (fp_isless1(f)) { - FPTYPE f_mod = f; + FPTYPE f_entry = f; // Save f in case we go to 'f' format. // Build negative exponent - for (e = 0, e1 = FPDECEXP; e1; e1 >>= 1, pos_pow++, neg_pow++) { - if (*neg_pow > f_mod) { - e += e1; - f_mod *= *pos_pow; - } - } - - char e_sign_char = '-'; - if (fp_isless1(f_mod) && f_mod >= FPROUND_TO_ONE) { - f_mod = FPCONST(1.0); - if (e == 0) { - e_sign_char = '+'; - } - } else if (fp_isless1(f_mod)) { - e++; - f_mod *= FPCONST(10.0); + e = -e_guess; + FPTYPE u_base = MICROPY_FLOAT_C_FUN(pow)(10, -e); + while (u_base > f) { + ++e; + u_base = MICROPY_FLOAT_C_FUN(pow)(10, -e); } + // Normalize out the inferred unit. Use divide because + // pow(10, e) * pow(10, -e) is slightly < 1 for some e in float32 + // (e.g. print("%.12f" % ((1e13) * (1e-13)))) + f /= u_base; // If the user specified 'g' format, and e is <= 4, then we'll switch // to the fixed format ('f') @@ -243,11 +212,12 @@ int mp_format_float(FPTYPE f, char *buf, size_t buf_size, char fmt, int prec, ch num_digits = prec; signed_e = 0; + f = f_entry; ++num_digits; } else { // For e & g formats, we'll be printing the exponent, so set the // sign. - e_sign = e_sign_char; + e_sign = '-'; dec = 0; if (prec > (buf_remaining - FPMIN_BUF_SIZE)) { @@ -264,19 +234,11 @@ int mp_format_float(FPTYPE f, char *buf, size_t buf_size, char fmt, int prec, ch // scaling it. Instead, we find the product of powers of 10 // that is not greater than it, and use that to start the // mantissa. - FPTYPE u_base = FPCONST(1.0); - for (e = 0, e1 = FPDECEXP; e1; e1 >>= 1, pos_pow++) { - FPTYPE next_u = u_base * *pos_pow; - // fp_ge_eps performs "f >= (next_u - 2eps)" so that if, for - // numerical reasons, f is very close to a power of ten but - // not strictly equal, we still treat it as that power of 10. - // The comparison was failing for maybe 10% of 1eX values, but - // although rounding fixed many of them, there were still some - // rendering as 9.99999998e(X-1). - if (fp_ge_eps(f, next_u)) { - u_base = next_u; - e += e1; - } + e = e_guess; + FPTYPE next_u = MICROPY_FLOAT_C_FUN(pow)(10, e + 1); + while (f >= next_u) { + ++e; + next_u = MICROPY_FLOAT_C_FUN(pow)(10, e + 1); } // If the user specified fixed format (fmt == 'f') and e makes the @@ -343,46 +305,22 @@ int mp_format_float(FPTYPE f, char *buf, size_t buf_size, char fmt, int prec, ch num_digits = prec; } - if (signed_e < 0) { - // The algorithm below treats numbers smaller than 1 by scaling them - // repeatedly by 10 to bring the new digit to the top. Our input number - // was smaller than 1, so scale it up to be 1 <= f < 10. - FPTYPE u_base = FPCONST(1.0); - const FPTYPE *pow_u = g_pos_pow; - for (int m = FPDECEXP; m; m >>= 1, pow_u++) { - if (m & e) { - u_base *= *pow_u; - } - } - f *= u_base; - } - int d = 0; - int num_digits_left = num_digits; - for (int digit_index = signed_e; num_digits_left >= 0; --digit_index) { + for (int digit_index = signed_e; num_digits >= 0; --digit_index) { FPTYPE u_base = FPCONST(1.0); if (digit_index > 0) { // Generate 10^digit_index for positive digit_index. - const FPTYPE *pow_u = g_pos_pow; - int target_index = digit_index; - for (int m = FPDECEXP; m; m >>= 1, pow_u++) { - if (m & target_index) { - u_base *= *pow_u; - } - } + u_base = MICROPY_FLOAT_C_FUN(pow)(10, digit_index); } for (d = 0; d < 9; ++d) { - // This is essentially "if (f < u_base)", but with 2eps margin - // so that if f is just a tiny bit smaller, we treat it as - // equal (and accept the additional digit value). - if (!fp_ge_eps(f, u_base)) { + if (f < u_base) { break; } f -= u_base; } // We calculate one more digit than we display, to use in rounding // below. So only emit the digit if it's one that we display. - if (num_digits_left > 0) { + if (num_digits > 0) { // Emit this number (the leading digit). *s++ = '0' + d; if (dec == 0 && prec > 0) { @@ -390,7 +328,7 @@ int mp_format_float(FPTYPE f, char *buf, size_t buf_size, char fmt, int prec, ch } } --dec; - --num_digits_left; + --num_digits; if (digit_index <= 0) { // Once we get below 1.0, we scale up f instead of calculating // negative powers of 10 in u_base. This provides better diff --git a/py/gc.c b/py/gc.c index d83fe2dfa8..603aceb5a0 100644 --- a/py/gc.c +++ b/py/gc.c @@ -36,6 +36,7 @@ #include #endif +// CIRCUITPY #include "supervisor/shared/safe_mode.h" #if CIRCUITPY_MEMORYMONITOR @@ -52,6 +53,7 @@ #define DEBUG_printf(...) (void)0 #endif +// CIRCUITPY // Uncomment this if you want to use a debugger to capture state at every allocation and free. // #define LOG_HEAP_ACTIVITY 1 @@ -87,17 +89,28 @@ #define ATB_2_IS_FREE(a) (((a) & ATB_MASK_2) == 0) #define ATB_3_IS_FREE(a) (((a) & ATB_MASK_3) == 0) -#define BLOCK_SHIFT(block) (2 * ((block) & (BLOCKS_PER_ATB - 1))) -#define ATB_GET_KIND(block) ((MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] >> BLOCK_SHIFT(block)) & 3) -#define ATB_ANY_TO_FREE(block) do { MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] &= (~(AT_MARK << BLOCK_SHIFT(block))); } while (0) -#define ATB_FREE_TO_HEAD(block) do { MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] |= (AT_HEAD << BLOCK_SHIFT(block)); } while (0) -#define ATB_FREE_TO_TAIL(block) do { MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] |= (AT_TAIL << BLOCK_SHIFT(block)); } while (0) -#define ATB_HEAD_TO_MARK(block) do { MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] |= (AT_MARK << BLOCK_SHIFT(block)); } while (0) -#define ATB_MARK_TO_HEAD(block) do { MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] &= (~(AT_TAIL << BLOCK_SHIFT(block))); } while (0) +#if MICROPY_GC_SPLIT_HEAP +#define NEXT_AREA(area) (area->next) +#else +#define NEXT_AREA(area) (NULL) +#endif -#define BLOCK_FROM_PTR(ptr) (((byte *)(ptr) - MP_STATE_MEM(gc_pool_start)) / BYTES_PER_BLOCK) -#define PTR_FROM_BLOCK(block) (((block) * BYTES_PER_BLOCK + (uintptr_t)MP_STATE_MEM(gc_pool_start))) -#define ATB_FROM_BLOCK(bl) ((bl) / BLOCKS_PER_ATB) +#define BLOCK_SHIFT(block) (2 * ((block) & (BLOCKS_PER_ATB - 1))) +#define ATB_GET_KIND(area, block) (((area)->gc_alloc_table_start[(block) / BLOCKS_PER_ATB] >> BLOCK_SHIFT(block)) & 3) +#define ATB_ANY_TO_FREE(area, block) do { area->gc_alloc_table_start[(block) / BLOCKS_PER_ATB] &= (~(AT_MARK << BLOCK_SHIFT(block))); } while (0) +#define ATB_FREE_TO_HEAD(area, block) do { area->gc_alloc_table_start[(block) / BLOCKS_PER_ATB] |= (AT_HEAD << BLOCK_SHIFT(block)); } while (0) +#define ATB_FREE_TO_TAIL(area, block) do { area->gc_alloc_table_start[(block) / BLOCKS_PER_ATB] |= (AT_TAIL << BLOCK_SHIFT(block)); } while (0) +#define ATB_HEAD_TO_MARK(area, block) do { area->gc_alloc_table_start[(block) / BLOCKS_PER_ATB] |= (AT_MARK << BLOCK_SHIFT(block)); } while (0) +#define ATB_MARK_TO_HEAD(area, block) do { area->gc_alloc_table_start[(block) / BLOCKS_PER_ATB] &= (~(AT_TAIL << BLOCK_SHIFT(block))); } while (0) + +#define BLOCK_FROM_PTR(area, ptr) (((byte *)(ptr) - area->gc_pool_start) / BYTES_PER_BLOCK) +#define PTR_FROM_BLOCK(area, block) (((block) * BYTES_PER_BLOCK + (uintptr_t)area->gc_pool_start)) + +// After the ATB, there must be a byte filled with AT_FREE so that gc_mark_tree +// cannot erroneously conclude that a block extends past the end of the GC heap +// due to bit patterns in the FTB (or first block, if finalizers are disabled) +// being interpreted as AT_TAIL. +#define ALLOC_TABLE_GAP_BYTE (1) #if MICROPY_ENABLE_FINALISER // FTB = finaliser table byte @@ -105,9 +118,9 @@ #define BLOCKS_PER_FTB (8) -#define FTB_GET(block) ((MP_STATE_MEM(gc_finaliser_table_start)[(block) / BLOCKS_PER_FTB] >> ((block) & 7)) & 1) -#define FTB_SET(block) do { MP_STATE_MEM(gc_finaliser_table_start)[(block) / BLOCKS_PER_FTB] |= (1 << ((block) & 7)); } while (0) -#define FTB_CLEAR(block) do { MP_STATE_MEM(gc_finaliser_table_start)[(block) / BLOCKS_PER_FTB] &= (~(1 << ((block) & 7))); } while (0) +#define FTB_GET(area, block) ((area->gc_finaliser_table_start[(block) / BLOCKS_PER_FTB] >> ((block) & 7)) & 1) +#define FTB_SET(area, block) do { area->gc_finaliser_table_start[(block) / BLOCKS_PER_FTB] |= (1 << ((block) & 7)); } while (0) +#define FTB_CLEAR(area, block) do { area->gc_finaliser_table_start[(block) / BLOCKS_PER_FTB] &= (~(1 << ((block) & 7))); } while (0) #endif #if MICROPY_PY_THREAD && !MICROPY_PY_THREAD_GIL @@ -130,11 +143,7 @@ void __attribute__ ((noinline)) gc_log_change(uint32_t start_block, uint32_t len #endif // TODO waste less memory; currently requires that all entries in alloc_table have a corresponding block in pool -void gc_init(void *start, void *end) { - // align end pointer on block boundary - end = (void *)((uintptr_t)end & (~(BYTES_PER_BLOCK - 1))); - DEBUG_printf("Initializing GC heap: %p..%p = " UINT_FMT " bytes\n", start, end, (byte *)end - (byte *)start); - +STATIC void gc_setup_area(mp_state_mem_area_t *area, void *start, void *end) { // calculate parameters for GC (T=total, A=alloc table, F=finaliser table, P=pool; all in bytes): // T = A + F + P // F = A * BLOCKS_PER_ATB / BLOCKS_PER_FTB @@ -142,39 +151,59 @@ void gc_init(void *start, void *end) { // => T = A * (1 + BLOCKS_PER_ATB / BLOCKS_PER_FTB + BLOCKS_PER_ATB * BYTES_PER_BLOCK) size_t total_byte_len = (byte *)end - (byte *)start; #if MICROPY_ENABLE_FINALISER - // CIRCUITPY: https://github.com/adafruit/circuitpython/pull/5245 (bug fix) - MP_STATE_MEM(gc_alloc_table_byte_len) = (total_byte_len - 1) * MP_BITS_PER_BYTE / (MP_BITS_PER_BYTE + MP_BITS_PER_BYTE * BLOCKS_PER_ATB / BLOCKS_PER_FTB + MP_BITS_PER_BYTE * BLOCKS_PER_ATB * BYTES_PER_BLOCK); + area->gc_alloc_table_byte_len = (total_byte_len - ALLOC_TABLE_GAP_BYTE) * MP_BITS_PER_BYTE / (MP_BITS_PER_BYTE + MP_BITS_PER_BYTE * BLOCKS_PER_ATB / BLOCKS_PER_FTB + MP_BITS_PER_BYTE * BLOCKS_PER_ATB * BYTES_PER_BLOCK); #else - MP_STATE_MEM(gc_alloc_table_byte_len) = total_byte_len / (1 + MP_BITS_PER_BYTE / 2 * BYTES_PER_BLOCK); + area->gc_alloc_table_byte_len = (total_byte_len - ALLOC_TABLE_GAP_BYTE) / (1 + MP_BITS_PER_BYTE / 2 * BYTES_PER_BLOCK); #endif - MP_STATE_MEM(gc_alloc_table_start) = (byte *)start; + area->gc_alloc_table_start = (byte *)start; #if MICROPY_ENABLE_FINALISER - size_t gc_finaliser_table_byte_len = (MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB + BLOCKS_PER_FTB - 1) / BLOCKS_PER_FTB; - MP_STATE_MEM(gc_finaliser_table_start) = MP_STATE_MEM(gc_alloc_table_start) + MP_STATE_MEM(gc_alloc_table_byte_len); + size_t gc_finaliser_table_byte_len = (area->gc_alloc_table_byte_len * BLOCKS_PER_ATB + BLOCKS_PER_FTB - 1) / BLOCKS_PER_FTB; + area->gc_finaliser_table_start = area->gc_alloc_table_start + area->gc_alloc_table_byte_len + ALLOC_TABLE_GAP_BYTE; #endif - size_t gc_pool_block_len = MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB; - MP_STATE_MEM(gc_pool_start) = (byte *)end - gc_pool_block_len * BYTES_PER_BLOCK; - MP_STATE_MEM(gc_pool_end) = end; + size_t gc_pool_block_len = area->gc_alloc_table_byte_len * BLOCKS_PER_ATB; + area->gc_pool_start = (byte *)end - gc_pool_block_len * BYTES_PER_BLOCK; + area->gc_pool_end = end; #if MICROPY_ENABLE_FINALISER - // CIRCUITPY: https://github.com/adafruit/circuitpython/pull/6397 (compiler diagnostic) - (void)gc_finaliser_table_byte_len; // avoid unused variable diagnostic if asserts are disabled - assert(MP_STATE_MEM(gc_pool_start) >= MP_STATE_MEM(gc_finaliser_table_start) + gc_finaliser_table_byte_len); + assert(area->gc_pool_start >= area->gc_finaliser_table_start + gc_finaliser_table_byte_len); #endif - // clear ATB's - memset(MP_STATE_MEM(gc_alloc_table_start), 0, MP_STATE_MEM(gc_alloc_table_byte_len)); - #if MICROPY_ENABLE_FINALISER - // clear FTB's - memset(MP_STATE_MEM(gc_finaliser_table_start), 0, gc_finaliser_table_byte_len); + // clear ATBs and FTBs + memset(area->gc_alloc_table_start, 0, gc_finaliser_table_byte_len + area->gc_alloc_table_byte_len + ALLOC_TABLE_GAP_BYTE); + #else + // clear ATBs + memset(area->gc_alloc_table_start, 0, area->gc_alloc_table_byte_len + ALLOC_TABLE_GAP_BYTE); #endif + area->gc_last_free_atb_index = 0; + + #if MICROPY_GC_SPLIT_HEAP + area->next = NULL; + #endif + + DEBUG_printf("GC layout:\n"); + DEBUG_printf(" alloc table at %p, length " UINT_FMT " bytes, " UINT_FMT " blocks\n", MP_STATE_MEM(area).gc_alloc_table_start, MP_STATE_MEM(area).gc_alloc_table_byte_len, MP_STATE_MEM(area).gc_alloc_table_byte_len * BLOCKS_PER_ATB); + #if MICROPY_ENABLE_FINALISER + DEBUG_printf(" finaliser table at %p, length " UINT_FMT " bytes, " UINT_FMT " blocks\n", MP_STATE_MEM(area).gc_finaliser_table_start, gc_finaliser_table_byte_len, gc_finaliser_table_byte_len * BLOCKS_PER_FTB); + #endif + DEBUG_printf(" pool at %p, length " UINT_FMT " bytes, " UINT_FMT " blocks\n", MP_STATE_MEM(area).gc_pool_start, gc_pool_block_len * BYTES_PER_BLOCK, gc_pool_block_len); +} + +void gc_init(void *start, void *end) { + // align end pointer on block boundary + end = (void *)((uintptr_t)end & (~(BYTES_PER_BLOCK - 1))); + DEBUG_printf("Initializing GC heap: %p..%p = " UINT_FMT " bytes\n", start, end, (byte *)end - (byte *)start); + + gc_setup_area(&MP_STATE_MEM(area), start, end); + // set last free ATB index to start of heap - MP_STATE_MEM(gc_last_free_atb_index) = 0; + #if MICROPY_GC_SPLIT_HEAP + MP_STATE_MEM(gc_last_free_area) = &MP_STATE_MEM(area); + #endif // unlock the GC MP_STATE_THREAD(gc_lock_depth) = 0; @@ -191,22 +220,38 @@ void gc_init(void *start, void *end) { #if MICROPY_PY_THREAD && !MICROPY_PY_THREAD_GIL mp_thread_mutex_init(&MP_STATE_MEM(gc_mutex)); #endif - - MP_STATE_MEM(permanent_pointers) = NULL; - - DEBUG_printf("GC layout:\n"); - DEBUG_printf(" alloc table at %p, length " UINT_FMT " bytes, " UINT_FMT " blocks\n", MP_STATE_MEM(gc_alloc_table_start), MP_STATE_MEM(gc_alloc_table_byte_len), MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB); - #if MICROPY_ENABLE_FINALISER - DEBUG_printf(" finaliser table at %p, length " UINT_FMT " bytes, " UINT_FMT " blocks\n", MP_STATE_MEM(gc_finaliser_table_start), gc_finaliser_table_byte_len, gc_finaliser_table_byte_len * BLOCKS_PER_FTB); - #endif - DEBUG_printf(" pool at %p, length " UINT_FMT " bytes, " UINT_FMT " blocks\n", MP_STATE_MEM(gc_pool_start), gc_pool_block_len * BYTES_PER_BLOCK, gc_pool_block_len); } +#if MICROPY_GC_SPLIT_HEAP +void gc_add(void *start, void *end) { + // Place the area struct at the start of the area. + mp_state_mem_area_t *area = (mp_state_mem_area_t *)start; + start = (void *)((uintptr_t)start + sizeof(mp_state_mem_area_t)); + + end = (void *)((uintptr_t)end & (~(BYTES_PER_BLOCK - 1))); + DEBUG_printf("Adding GC heap: %p..%p = " UINT_FMT " bytes\n", start, end, (byte *)end - (byte *)start); + + // Init this area + gc_setup_area(area, start, end); + + // Find the last registered area in the linked list + mp_state_mem_area_t *prev_area = &MP_STATE_MEM(area); + while (prev_area->next != NULL) { + prev_area = prev_area->next; + } + + // Add this area to the linked list + prev_area->next = area; +} +#endif + +// CIRCUITPY +// TODO FOR MERGE: fix this for multiple areas?? void gc_deinit(void) { // Run any finalisers before we stop using the heap. gc_sweep_all(); - MP_STATE_MEM(gc_pool_start) = 0; + MP_STATE_MEM(area) = 0; } void gc_lock(void) { @@ -226,6 +271,23 @@ bool gc_is_locked(void) { return MP_STATE_THREAD(gc_lock_depth) != 0; } +#if MICROPY_GC_SPLIT_HEAP +// Returns the area to which this pointer belongs, or NULL if it isn't +// allocated on the GC-managed heap. +STATIC inline mp_state_mem_area_t *gc_get_ptr_area(const void *ptr) { + if (((uintptr_t)(ptr) & (BYTES_PER_BLOCK - 1)) != 0) { // must be aligned on a block + return NULL; + } + for (mp_state_mem_area_t *area = &MP_STATE_MEM(area); area != NULL; area = NEXT_AREA(area)) { + if (ptr >= (void *)area->gc_pool_start // must be above start of pool + && ptr < (void *)area->gc_pool_end) { // must be below end of pool + return area; + } + } + return NULL; +} +#endif + // CIRCUITPY: VERIFY_PTR moved to gc.h to make it available elsewhere. #ifndef TRACE_MARK @@ -241,36 +303,65 @@ bool gc_is_locked(void) { // blocks on the stack. When all children have been checked, pop off the // topmost block on the stack and repeat with that one. // CIRCUITPY: We don't instrument these functions because they occur a lot during GC and -// fill up the output buffer quickly. -STATIC void MP_NO_INSTRUMENT PLACE_IN_ITCM(gc_mark_subtree)(size_t block) { +#if MICROPY_GC_SPLIT_HEAP +STATIC void MP_NO_INSTRUMENT PLACE_IN_ITCM(gc_mark_subtree)(mp_state_mem_area_t *area, size_t block) +#else +STATIC void MP_NO_INSTRUMENT PLACE_IN_ITCM(gc_mark_subtree)(size_t block) +#endif +{ // Start with the block passed in the argument. size_t sp = 0; for (;;) { MICROPY_GC_HOOK_LOOP + + #if !MICROPY_GC_SPLIT_HEAP + mp_state_mem_area_t * area = &MP_STATE_MEM(area); + #endif + // work out number of consecutive blocks in the chain starting with this one size_t n_blocks = 0; do { n_blocks += 1; - } while (ATB_GET_KIND(block + n_blocks) == AT_TAIL); + } while (ATB_GET_KIND(area, block + n_blocks) == AT_TAIL); + + // check that the consecutive blocks didn't overflow past the end of the area + assert(area->gc_pool_start + (block + n_blocks) * BYTES_PER_BLOCK <= area->gc_pool_end); // check this block's children - void **ptrs = (void **)PTR_FROM_BLOCK(block); + void **ptrs = (void **)PTR_FROM_BLOCK(area, block); for (size_t i = n_blocks * BYTES_PER_BLOCK / sizeof(void *); i > 0; i--, ptrs++) { MICROPY_GC_HOOK_LOOP void *ptr = *ptrs; - if (VERIFY_PTR(ptr)) { - // Mark and push this pointer - size_t childblock = BLOCK_FROM_PTR(ptr); - if (ATB_GET_KIND(childblock) == AT_HEAD) { - // an unmarked head, mark it, and push it on gc stack - TRACE_MARK(childblock, ptr); - ATB_HEAD_TO_MARK(childblock); - if (sp < MICROPY_ALLOC_GC_STACK_SIZE) { - MP_STATE_MEM(gc_stack)[sp++] = childblock; - } else { - MP_STATE_MEM(gc_stack_overflow) = 1; - } - } + // If this is a heap pointer that hasn't been marked, mark it and push + // it's children to the stack. + #if MICROPY_GC_SPLIT_HEAP + mp_state_mem_area_t *ptr_area = gc_get_ptr_area(ptr); + if (!ptr_area) { + // Not a heap-allocated pointer (might even be random data). + continue; + } + #else + if (!VERIFY_PTR(ptr)) { + continue; + } + mp_state_mem_area_t *ptr_area = area; + #endif + size_t ptr_block = BLOCK_FROM_PTR(ptr_area, ptr); + if (ATB_GET_KIND(ptr_area, ptr_block) != AT_HEAD) { + // This block is already marked. + continue; + } + // An unmarked head. Mark it, and push it on gc stack. + TRACE_MARK(ptr_block, ptr); + ATB_HEAD_TO_MARK(ptr_area, ptr_block); + if (sp < MICROPY_ALLOC_GC_STACK_SIZE) { + MP_STATE_MEM(gc_block_stack)[sp] = ptr_block; + #if MICROPY_GC_SPLIT_HEAP + MP_STATE_MEM(gc_area_stack)[sp] = ptr_area; + #endif + sp += 1; + } else { + MP_STATE_MEM(gc_stack_overflow) = 1; } } @@ -280,7 +371,11 @@ STATIC void MP_NO_INSTRUMENT PLACE_IN_ITCM(gc_mark_subtree)(size_t block) { } // pop the next block off the stack - block = MP_STATE_MEM(gc_stack)[--sp]; + sp -= 1; + block = MP_STATE_MEM(gc_block_stack)[sp]; + #if MICROPY_GC_SPLIT_HEAP + area = MP_STATE_MEM(gc_area_stack)[sp]; + #endif } } @@ -289,11 +384,17 @@ STATIC void gc_deal_with_stack_overflow(void) { MP_STATE_MEM(gc_stack_overflow) = 0; // scan entire memory looking for blocks which have been marked but not their children - for (size_t block = 0; block < MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB; block++) { - MICROPY_GC_HOOK_LOOP - // trace (again) if mark bit set - if (ATB_GET_KIND(block) == AT_MARK) { - gc_mark_subtree(block); + for (mp_state_mem_area_t *area = &MP_STATE_MEM(area); area != NULL; area = NEXT_AREA(area)) { + for (size_t block = 0; block < area->gc_alloc_table_byte_len * BLOCKS_PER_ATB; block++) { + MICROPY_GC_HOOK_LOOP + // trace (again) if mark bit set + if (ATB_GET_KIND(area, block) == AT_MARK) { + #if MICROPY_GC_SPLIT_HEAP + gc_mark_subtree(area, block); + #else + gc_mark_subtree(block); + #endif + } } } } @@ -305,70 +406,55 @@ STATIC void gc_sweep(void) { #endif // free unmarked heads and their tails int free_tail = 0; - for (size_t block = 0; block < MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB; block++) { - MICROPY_GC_HOOK_LOOP - switch (ATB_GET_KIND(block)) { - case AT_HEAD: - #if MICROPY_ENABLE_FINALISER - if (FTB_GET(block)) { - mp_obj_base_t *obj = (mp_obj_base_t *)PTR_FROM_BLOCK(block); - if (obj->type != NULL) { - // if the object has a type then see if it has a __del__ method - mp_obj_t dest[2]; - mp_load_method_maybe(MP_OBJ_FROM_PTR(obj), MP_QSTR___del__, dest); - if (dest[0] != MP_OBJ_NULL) { - // load_method returned a method, execute it in a protected environment - #if MICROPY_ENABLE_SCHEDULER - mp_sched_lock(); - #endif - mp_call_function_1_protected(dest[0], dest[1]); - #if MICROPY_ENABLE_SCHEDULER - mp_sched_unlock(); - #endif + for (mp_state_mem_area_t *area = &MP_STATE_MEM(area); area != NULL; area = NEXT_AREA(area)) { + for (size_t block = 0; block < area->gc_alloc_table_byte_len * BLOCKS_PER_ATB; block++) { + MICROPY_GC_HOOK_LOOP + switch (ATB_GET_KIND(area, block)) { + case AT_HEAD: + #if MICROPY_ENABLE_FINALISER + if (FTB_GET(area, block)) { + mp_obj_base_t *obj = (mp_obj_base_t *)PTR_FROM_BLOCK(area, block); + if (obj->type != NULL) { + // if the object has a type then see if it has a __del__ method + mp_obj_t dest[2]; + mp_load_method_maybe(MP_OBJ_FROM_PTR(obj), MP_QSTR___del__, dest); + if (dest[0] != MP_OBJ_NULL) { + // load_method returned a method, execute it in a protected environment + #if MICROPY_ENABLE_SCHEDULER + mp_sched_lock(); + #endif + mp_call_function_1_protected(dest[0], dest[1]); + #if MICROPY_ENABLE_SCHEDULER + mp_sched_unlock(); + #endif + } } + // clear finaliser flag + FTB_CLEAR(area, block); } - // clear finaliser flag - FTB_CLEAR(block); - } - #endif - free_tail = 1; - DEBUG_printf("gc_sweep(%p)\n", (void *)PTR_FROM_BLOCK(block)); - - #ifdef LOG_HEAP_ACTIVITY - gc_log_change(block, 0); - #endif - #if MICROPY_PY_GC_COLLECT_RETVAL - MP_STATE_MEM(gc_collected)++; - #endif - // fall through to free the head - MP_FALLTHROUGH - - case AT_TAIL: - if (free_tail) { - ATB_ANY_TO_FREE(block); - #if CLEAR_ON_SWEEP - memset((void *)PTR_FROM_BLOCK(block), 0, BYTES_PER_BLOCK); #endif - } - break; + free_tail = 1; + DEBUG_printf("gc_sweep(%p)\n", (void *)PTR_FROM_BLOCK(area, block)); + #if MICROPY_PY_GC_COLLECT_RETVAL + MP_STATE_MEM(gc_collected)++; + #endif + // fall through to free the head + MP_FALLTHROUGH - case AT_MARK: - ATB_MARK_TO_HEAD(block); - free_tail = 0; - break; - } - } -} + case AT_TAIL: + if (free_tail) { + ATB_ANY_TO_FREE(area, block); + #if CLEAR_ON_SWEEP + memset((void *)PTR_FROM_BLOCK(area, block), 0, BYTES_PER_BLOCK); + #endif + } + break; -// Mark can handle NULL pointers because it verifies the pointer is within the heap bounds. -STATIC void MP_NO_INSTRUMENT PLACE_IN_ITCM(gc_mark)(void *ptr) { - if (VERIFY_PTR(ptr)) { - size_t block = BLOCK_FROM_PTR(ptr); - if (ATB_GET_KIND(block) == AT_HEAD) { - // An unmarked head: mark it, and mark all its children - TRACE_MARK(block, ptr); - ATB_HEAD_TO_MARK(block); - gc_mark_subtree(block); + case AT_MARK: + ATB_MARK_TO_HEAD(area, block); + free_tail = 0; + break; + } } } } @@ -389,8 +475,6 @@ void gc_collect_start(void) { size_t root_end = offsetof(mp_state_ctx_t, vm.qstr_last_chunk); gc_collect_root(ptrs + root_start / sizeof(void *), (root_end - root_start) / sizeof(void *)); - gc_mark(MP_STATE_MEM(permanent_pointers)); - #if MICROPY_ENABLE_PYSTACK // Trace root pointers from the Python stack. ptrs = (void **)(void *)MP_STATE_THREAD(pystack_start); @@ -398,17 +482,13 @@ void gc_collect_start(void) { #endif } -void gc_collect_ptr(void *ptr) { - gc_mark(ptr); -} - // Address sanitizer needs to know that the access to ptrs[i] must always be // considered OK, even if it's a load from an address that would normally be // prohibited (due to being undefined, in a red zone, etc). #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) __attribute__((no_sanitize_address)) #endif -static void *MP_NO_INSTRUMENT PLACE_IN_ITCM(gc_get_ptr)(void **ptrs, int i) { +static void *gc_get_ptr(void **ptrs, int i) { #if MICROPY_DEBUG_VALGRIND if (!VALGRIND_CHECK_MEM_IS_ADDRESSABLE(&ptrs[i], sizeof(*ptrs))) { return NULL; @@ -418,18 +498,44 @@ static void *MP_NO_INSTRUMENT PLACE_IN_ITCM(gc_get_ptr)(void **ptrs, int i) { } void gc_collect_root(void **ptrs, size_t len) { + #if !MICROPY_GC_SPLIT_HEAP + mp_state_mem_area_t *area = &MP_STATE_MEM(area); + #endif for (size_t i = 0; i < len; i++) { MICROPY_GC_HOOK_LOOP void *ptr = gc_get_ptr(ptrs, i); - // CIRCUITPY changed in PR #1816 - gc_mark(ptr); + #if MICROPY_GC_SPLIT_HEAP + mp_state_mem_area_t *area = gc_get_ptr_area(ptr); + if (!area) { + continue; + } + #else + if (!VERIFY_PTR(ptr)) { + continue; + } + #endif + size_t block = BLOCK_FROM_PTR(area, ptr); + if (ATB_GET_KIND(area, block) == AT_HEAD) { + // An unmarked head: mark it, and mark all its children + ATB_HEAD_TO_MARK(area, block); + #if MICROPY_GC_SPLIT_HEAP + gc_mark_subtree(area, block); + #else + gc_mark_subtree(block); + #endif + } } } void gc_collect_end(void) { gc_deal_with_stack_overflow(); gc_sweep(); - MP_STATE_MEM(gc_last_free_atb_index) = 0; + #if MICROPY_GC_SPLIT_HEAP + MP_STATE_MEM(gc_last_free_area) = &MP_STATE_MEM(area); + #endif + for (mp_state_mem_area_t *area = &MP_STATE_MEM(area); area != NULL; area = NEXT_AREA(area)) { + area->gc_last_free_atb_index = 0; + } MP_STATE_THREAD(gc_lock_depth)--; GC_EXIT(); } @@ -443,59 +549,62 @@ void gc_sweep_all(void) { void gc_info(gc_info_t *info) { GC_ENTER(); - info->total = MP_STATE_MEM(gc_pool_end) - MP_STATE_MEM(gc_pool_start); + info->total = 0; info->used = 0; info->free = 0; info->max_free = 0; info->num_1block = 0; info->num_2block = 0; info->max_block = 0; - bool finish = false; - for (size_t block = 0, len = 0, len_free = 0; !finish;) { - size_t kind = ATB_GET_KIND(block); - switch (kind) { - case AT_FREE: - info->free += 1; - len_free += 1; - len = 0; - break; + for (mp_state_mem_area_t *area = &MP_STATE_MEM(area); area != NULL; area = NEXT_AREA(area)) { + bool finish = false; + info->total += area->gc_pool_end - area->gc_pool_start; + for (size_t block = 0, len = 0, len_free = 0; !finish;) { + size_t kind = ATB_GET_KIND(area, block); + switch (kind) { + case AT_FREE: + info->free += 1; + len_free += 1; + len = 0; + break; - case AT_HEAD: - info->used += 1; - len = 1; - break; + case AT_HEAD: + info->used += 1; + len = 1; + break; - case AT_TAIL: - info->used += 1; - len += 1; - break; + case AT_TAIL: + info->used += 1; + len += 1; + break; - case AT_MARK: - // shouldn't happen - break; - } - - block++; - finish = (block == MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB); - // Get next block type if possible - if (!finish) { - kind = ATB_GET_KIND(block); - } - - if (finish || kind == AT_FREE || kind == AT_HEAD) { - if (len == 1) { - info->num_1block += 1; - } else if (len == 2) { - info->num_2block += 1; + case AT_MARK: + // shouldn't happen + break; } - if (len > info->max_block) { - info->max_block = len; + + block++; + finish = (block == area->gc_alloc_table_byte_len * BLOCKS_PER_ATB); + // Get next block type if possible + if (!finish) { + kind = ATB_GET_KIND(area, block); } - if (finish || kind == AT_HEAD) { - if (len_free > info->max_free) { - info->max_free = len_free; + + if (finish || kind == AT_FREE || kind == AT_HEAD) { + if (len == 1) { + info->num_1block += 1; + } else if (len == 2) { + info->num_2block += 1; + } + if (len > info->max_block) { + info->max_block = len; + } + if (finish || kind == AT_HEAD) { + if (len_free > info->max_free) { + info->max_free = len_free; + } + len_free = 0; } - len_free = 0; } } } @@ -506,7 +615,11 @@ void gc_info(gc_info_t *info) { } bool gc_alloc_possible(void) { - return MP_STATE_MEM(gc_pool_start) != 0; + #if MICROPY_GC_SPLIT_HEAP + return MP_STATE_MEM(gc_last_free_area) != 0; + #else + area = &MP_STATE_MEM(area) != 0; + #endif } void *gc_alloc(size_t n_bytes, unsigned int alloc_flags) { @@ -524,12 +637,9 @@ void *gc_alloc(size_t n_bytes, unsigned int alloc_flags) { return NULL; } - if (MP_STATE_MEM(gc_pool_start) == 0) { - reset_into_safe_mode(SAFE_MODE_GC_ALLOC_OUTSIDE_VM); - } - GC_ENTER(); + mp_state_mem_area_t *area; size_t i; size_t end_block; size_t start_block; @@ -547,16 +657,37 @@ void *gc_alloc(size_t n_bytes, unsigned int alloc_flags) { for (;;) { + #if MICROPY_GC_SPLIT_HEAP + area = MP_STATE_MEM(gc_last_free_area); + #else + area = &MP_STATE_MEM(area); + #endif + + if (area == 0) { + reset_into_safe_mode(SAFE_MODE_GC_ALLOC_OUTSIDE_VM); + } + // look for a run of n_blocks available blocks - n_free = 0; - for (i = MP_STATE_MEM(gc_last_free_atb_index); i < MP_STATE_MEM(gc_alloc_table_byte_len); i++) { - byte a = MP_STATE_MEM(gc_alloc_table_start)[i]; - // *FORMAT-OFF* - if (ATB_0_IS_FREE(a)) { if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 0; goto found; } } else { n_free = 0; } - if (ATB_1_IS_FREE(a)) { if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 1; goto found; } } else { n_free = 0; } - if (ATB_2_IS_FREE(a)) { if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 2; goto found; } } else { n_free = 0; } - if (ATB_3_IS_FREE(a)) { if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 3; goto found; } } else { n_free = 0; } - // *FORMAT-ON* + for (; area != NULL; area = NEXT_AREA(area), i = 0) { + n_free = 0; + for (i = area->gc_last_free_atb_index; i < area->gc_alloc_table_byte_len; i++) { + byte a = area->gc_alloc_table_start[i]; + // *FORMAT-OFF* + if (ATB_0_IS_FREE(a)) { if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 0; goto found; } } else { n_free = 0; } + if (ATB_1_IS_FREE(a)) { if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 1; goto found; } } else { n_free = 0; } + if (ATB_2_IS_FREE(a)) { if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 2; goto found; } } else { n_free = 0; } + if (ATB_3_IS_FREE(a)) { if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 3; goto found; } } else { n_free = 0; } + // *FORMAT-ON* + } + + // No free blocks found on this heap. Mark this heap as + // filled, so we won't try to find free space here again until + // space is freed. + #if MICROPY_GC_SPLIT_HEAP + if (n_blocks == 1) { + area->gc_last_free_atb_index = (i + 1) / BLOCKS_PER_ATB; // or (size_t)-1 + } + #endif } GC_EXIT(); @@ -582,26 +713,29 @@ found: // before this one. Also, whenever we free or shink a block we must check // if this index needs adjusting (see gc_realloc and gc_free). if (n_free == 1) { - MP_STATE_MEM(gc_last_free_atb_index) = (i + 1) / BLOCKS_PER_ATB; + #if MICROPY_GC_SPLIT_HEAP + MP_STATE_MEM(gc_last_free_area) = area; + #endif + area->gc_last_free_atb_index = (i + 1) / BLOCKS_PER_ATB; } - + // CIRCUITPY #ifdef LOG_HEAP_ACTIVITY gc_log_change(start_block, end_block - start_block + 1); #endif // mark first block as used head - ATB_FREE_TO_HEAD(start_block); + ATB_FREE_TO_HEAD(area, start_block); // mark rest of blocks as used tail // TODO for a run of many blocks can make this more efficient for (size_t bl = start_block + 1; bl <= end_block; bl++) { - ATB_FREE_TO_TAIL(bl); + ATB_FREE_TO_TAIL(area, bl); } // get pointer to first block // we must create this pointer before unlocking the GC so a collection can find it - void *ret_ptr = (void *)(MP_STATE_MEM(gc_pool_start) + start_block * BYTES_PER_BLOCK); + void *ret_ptr = (void *)(area->gc_pool_start + start_block * BYTES_PER_BLOCK); DEBUG_printf("gc_alloc(%p)\n", ret_ptr); #if MICROPY_GC_ALLOC_THRESHOLD @@ -628,7 +762,7 @@ found: ((mp_obj_base_t *)ret_ptr)->type = NULL; // set mp_obj flag only if it has a finaliser GC_ENTER(); - FTB_SET(start_block); + FTB_SET(area, start_block); GC_EXIT(); } #else @@ -636,12 +770,11 @@ found: #endif #if EXTENSIVE_HEAP_PROFILING - gc_dump_alloc_table(); - #endif + gc_dump_alloc_table(&mp_plat_print); #if CIRCUITPY_MEMORYMONITOR memorymonitor_track_allocation(end_block - start_block + 1); - #endif + #endif return ret_ptr; } @@ -660,7 +793,9 @@ void *gc_alloc_with_finaliser(mp_uint_t n_bytes) { // TODO: freeing here does not call finaliser void gc_free(void *ptr) { if (MP_STATE_THREAD(gc_lock_depth) > 0) { - // TODO how to deal with this error? + // Cannot free while the GC is locked. However free is an optimisation + // to reclaim the memory immediately, this means it will now be left + // until the next collection. return; } @@ -669,54 +804,91 @@ void gc_free(void *ptr) { DEBUG_printf("gc_free(%p)\n", ptr); if (ptr == NULL) { + // free(NULL) is a no-op GC_EXIT(); - } else { - // CIRCUITPY extra checking - if (MP_STATE_MEM(gc_pool_start) == 0) { - reset_into_safe_mode(SAFE_MODE_GC_ALLOC_OUTSIDE_VM); - } - // get the GC block number corresponding to this pointer - assert(VERIFY_PTR(ptr)); - size_t block = BLOCK_FROM_PTR(ptr); - assert(ATB_GET_KIND(block) == AT_HEAD); - - #if MICROPY_ENABLE_FINALISER - FTB_CLEAR(block); - #endif - - // set the last_free pointer to this block if it's earlier in the heap - if (block / BLOCKS_PER_ATB < MP_STATE_MEM(gc_last_free_atb_index)) { - MP_STATE_MEM(gc_last_free_atb_index) = block / BLOCKS_PER_ATB; - } - - #ifdef LOG_HEAP_ACTIVITY - gc_log_change(start_block, 0); - #endif - - // free head and all of its tail blocks - do { - ATB_ANY_TO_FREE(block); - block += 1; - } while (ATB_GET_KIND(block) == AT_TAIL); - - GC_EXIT(); - - #if EXTENSIVE_HEAP_PROFILING - gc_dump_alloc_table(); - #endif + return; } + + // get the GC block number corresponding to this pointer + mp_state_mem_area_t *area; + #if MICROPY_GC_SPLIT_HEAP + area = gc_get_ptr_area(ptr); + // CIRCUITPY extra checking + if (MP_STATE_MEM(gc_pool_start) == 0) { + reset_into_safe_mode(SAFE_MODE_GC_ALLOC_OUTSIDE_VM); + } + // assert(area); + #else + assert(VERIFY_PTR(ptr)); + area = &MP_STATE_MEM(area); + #endif + + size_t block = BLOCK_FROM_PTR(area, ptr); + assert(ATB_GET_KIND(area, block) == AT_HEAD); + + #if MICROPY_ENABLE_FINALISER + FTB_CLEAR(area, block); + #endif + + #if MICROPY_GC_SPLIT_HEAP + if (MP_STATE_MEM(gc_last_free_area) != area) { + // We freed something but it isn't the current area. Reset the + // last free area to the start for a rescan. Note that this won't + // give much of a performance hit, since areas that are completely + // filled will likely be skipped (the gc_last_free_atb_index + // points to the last block). + // The reason why this is necessary is because it is not possible + // to see which area came first (like it is possible to adjust + // gc_last_free_atb_index based on whether the freed block is + // before the last free block). + MP_STATE_MEM(gc_last_free_area) = &MP_STATE_MEM(area); + } + #endif + + // set the last_free pointer to this block if it's earlier in the heap + if (block / BLOCKS_PER_ATB < area->gc_last_free_atb_index) { + area->gc_last_free_atb_index = block / BLOCKS_PER_ATB; + } + + #ifdef LOG_HEAP_ACTIVITY + gc_log_change(start_block, 0); + #endif + + // free head and all of its tail blocks + do { + ATB_ANY_TO_FREE(area, block); + block += 1; + } while (ATB_GET_KIND(area, block) == AT_TAIL); + + GC_EXIT(); + + #if EXTENSIVE_HEAP_PROFILING + gc_dump_alloc_table(&mp_plat_print); + #endif } size_t gc_nbytes(const void *ptr) { GC_ENTER(); + + mp_state_mem_area_t *area; + #if MICROPY_GC_SPLIT_HEAP + area = gc_get_ptr_area(ptr); + #else if (VERIFY_PTR(ptr)) { - size_t block = BLOCK_FROM_PTR(ptr); - if (ATB_GET_KIND(block) == AT_HEAD) { + area = &MP_STATE_MEM(area); + } else { + area = NULL; + } + #endif + + if (area) { + size_t block = BLOCK_FROM_PTR(area, ptr); + if (ATB_GET_KIND(area, block) == AT_HEAD) { // work out number of consecutive blocks in the chain starting with this on size_t n_blocks = 0; do { n_blocks += 1; - } while (ATB_GET_KIND(block + n_blocks) == AT_TAIL); + } while (ATB_GET_KIND(area, block + n_blocks) == AT_TAIL); GC_EXIT(); return n_blocks * BYTES_PER_BLOCK; } @@ -727,23 +899,6 @@ size_t gc_nbytes(const void *ptr) { return 0; } -bool gc_has_finaliser(const void *ptr) { - #if MICROPY_ENABLE_FINALISER - GC_ENTER(); - if (VERIFY_PTR(ptr)) { - bool has_finaliser = FTB_GET(BLOCK_FROM_PTR(ptr)); - GC_EXIT(); - return has_finaliser; - } - - // invalid pointer - GC_EXIT(); - #else - (void)ptr; - #endif - return false; -} - #if 0 // old, simple realloc that didn't expand memory in place void *gc_realloc(void *ptr, mp_uint_t n_bytes) { @@ -794,9 +949,16 @@ void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) { GC_ENTER(); // get the GC block number corresponding to this pointer + mp_state_mem_area_t *area; + #if MICROPY_GC_SPLIT_HEAP + area = gc_get_ptr_area(ptr); + assert(area); + #else assert(VERIFY_PTR(ptr)); - size_t block = BLOCK_FROM_PTR(ptr); - assert(ATB_GET_KIND(block) == AT_HEAD); + area = &MP_STATE_MEM(area); + #endif + size_t block = BLOCK_FROM_PTR(area, ptr); + assert(ATB_GET_KIND(area, block) == AT_HEAD); // compute number of new blocks that are requested size_t new_blocks = (n_bytes + BYTES_PER_BLOCK - 1) / BYTES_PER_BLOCK; @@ -809,9 +971,9 @@ void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) { // efficiently shrink it (see below for shrinking code). size_t n_free = 0; size_t n_blocks = 1; // counting HEAD block - size_t max_block = MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB; + size_t max_block = area->gc_alloc_table_byte_len * BLOCKS_PER_ATB; for (size_t bl = block + n_blocks; bl < max_block; bl++) { - byte block_type = ATB_GET_KIND(bl); + byte block_type = ATB_GET_KIND(area, bl); if (block_type == AT_TAIL) { n_blocks++; continue; @@ -837,18 +999,25 @@ void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) { if (new_blocks < n_blocks) { // free unneeded tail blocks for (size_t bl = block + new_blocks, count = n_blocks - new_blocks; count > 0; bl++, count--) { - ATB_ANY_TO_FREE(bl); + ATB_ANY_TO_FREE(area, bl); } + #if MICROPY_GC_SPLIT_HEAP + if (MP_STATE_MEM(gc_last_free_area) != area) { + // See comment in gc_free. + MP_STATE_MEM(gc_last_free_area) = &MP_STATE_MEM(area); + } + #endif + // set the last_free pointer to end of this block if it's earlier in the heap - if ((block + new_blocks) / BLOCKS_PER_ATB < MP_STATE_MEM(gc_last_free_atb_index)) { - MP_STATE_MEM(gc_last_free_atb_index) = (block + new_blocks) / BLOCKS_PER_ATB; + if ((block + new_blocks) / BLOCKS_PER_ATB < area->gc_last_free_atb_index) { + area->gc_last_free_atb_index = (block + new_blocks) / BLOCKS_PER_ATB; } GC_EXIT(); #if EXTENSIVE_HEAP_PROFILING - gc_dump_alloc_table(); + gc_dump_alloc_table(&mp_plat_print); #endif #ifdef LOG_HEAP_ACTIVITY @@ -866,8 +1035,8 @@ void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) { if (new_blocks <= n_blocks + n_free) { // mark few more blocks as used tail for (size_t bl = block + n_blocks; bl < block + new_blocks; bl++) { - assert(ATB_GET_KIND(bl) == AT_FREE); - ATB_FREE_TO_TAIL(bl); + assert(ATB_GET_KIND(area, bl) == AT_FREE); + ATB_FREE_TO_TAIL(area, bl); } GC_EXIT(); @@ -881,7 +1050,7 @@ void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) { #endif #if EXTENSIVE_HEAP_PROFILING - gc_dump_alloc_table(); + gc_dump_alloc_table(&mp_plat_print); #endif #ifdef LOG_HEAP_ACTIVITY @@ -896,7 +1065,7 @@ void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) { } #if MICROPY_ENABLE_FINALISER - bool ftb_state = FTB_GET(block); + bool ftb_state = FTB_GET(area, block); #else bool ftb_state = false; #endif @@ -923,174 +1092,141 @@ void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) { } #endif // Alternative gc_realloc impl -bool gc_never_free(void *ptr) { - // Check to make sure the pointer is on the heap in the first place. - if (gc_nbytes(ptr) == 0) { - return false; - } - // Pointers are stored in a linked list where each block is BYTES_PER_BLOCK long and the first - // pointer is the next block of pointers. - void **current_reference_block = MP_STATE_MEM(permanent_pointers); - void **last_reference_block = NULL; - while (current_reference_block != NULL) { - for (size_t i = 1; i < BYTES_PER_BLOCK / sizeof(void *); i++) { - if (current_reference_block[i] == NULL) { - current_reference_block[i] = ptr; - return true; - } - } - last_reference_block = current_reference_block; // keep a record of last "proper" reference block - current_reference_block = current_reference_block[0]; - } - void **next_block = gc_alloc(BYTES_PER_BLOCK, false); - if (next_block == NULL) { - return false; - } - if (MP_STATE_MEM(permanent_pointers) == NULL) { - MP_STATE_MEM(permanent_pointers) = next_block; - } else { - last_reference_block[0] = next_block; - } - next_block[1] = ptr; - return true; -} - -void gc_dump_info(void) { +void gc_dump_info(const mp_print_t *print) { gc_info_t info; gc_info(&info); - mp_printf(&mp_plat_print, "GC: total: %u, used: %u, free: %u\n", + mp_printf(print, "GC: total: %u, used: %u, free: %u\n", (uint)info.total, (uint)info.used, (uint)info.free); - mp_printf(&mp_plat_print, " No. of 1-blocks: %u, 2-blocks: %u, max blk sz: %u, max free sz: %u\n", + mp_printf(print, " No. of 1-blocks: %u, 2-blocks: %u, max blk sz: %u, max free sz: %u\n", (uint)info.num_1block, (uint)info.num_2block, (uint)info.max_block, (uint)info.max_free); } -void gc_dump_alloc_table(void) { +void gc_dump_alloc_table(const mp_print_t *print) { GC_ENTER(); static const size_t DUMP_BYTES_PER_LINE = 64; - #if !EXTENSIVE_HEAP_PROFILING - // When comparing heap output we don't want to print the starting - // pointer of the heap because it changes from run to run. - mp_printf(&mp_plat_print, "GC memory layout; from %p:", MP_STATE_MEM(gc_pool_start)); - #endif - for (size_t bl = 0; bl < MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB; bl++) { - if (bl % DUMP_BYTES_PER_LINE == 0) { - // a new line of blocks - { - // check if this line contains only free blocks - size_t bl2 = bl; - while (bl2 < MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB && ATB_GET_KIND(bl2) == AT_FREE) { - bl2++; - } - if (bl2 - bl >= 2 * DUMP_BYTES_PER_LINE) { - // there are at least 2 lines containing only free blocks, so abbreviate their printing - mp_printf(&mp_plat_print, "\n (%u lines all free)", (uint)(bl2 - bl) / DUMP_BYTES_PER_LINE); - bl = bl2 & (~(DUMP_BYTES_PER_LINE - 1)); - if (bl >= MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB) { - // got to end of heap - break; + for (mp_state_mem_area_t *area = &MP_STATE_MEM(area); area != NULL; area = NEXT_AREA(area)) { + #if !EXTENSIVE_HEAP_PROFILING + // When comparing heap output we don't want to print the starting + // pointer of the heap because it changes from run to run. + mp_printf(print, "GC memory layout; from %p:", area->gc_pool_start); + #endif + for (size_t bl = 0; bl < area->gc_alloc_table_byte_len * BLOCKS_PER_ATB; bl++) { + if (bl % DUMP_BYTES_PER_LINE == 0) { + // a new line of blocks + { + // check if this line contains only free blocks + size_t bl2 = bl; + while (bl2 < area->gc_alloc_table_byte_len * BLOCKS_PER_ATB && ATB_GET_KIND(area, bl2) == AT_FREE) { + bl2++; + } + if (bl2 - bl >= 2 * DUMP_BYTES_PER_LINE) { + // there are at least 2 lines containing only free blocks, so abbreviate their printing + mp_printf(print, "\n (%u lines all free)", (uint)(bl2 - bl) / DUMP_BYTES_PER_LINE); + bl = bl2 & (~(DUMP_BYTES_PER_LINE - 1)); + if (bl >= area->gc_alloc_table_byte_len * BLOCKS_PER_ATB) { + // got to end of heap + break; + } } } + // print header for new line of blocks + // (the cast to uint32_t is for 16-bit ports) + mp_printf(print, "\n%08x: ", (uint)(bl * BYTES_PER_BLOCK)); } - // print header for new line of blocks - // (the cast to uint32_t is for 16-bit ports) - // mp_printf(&mp_plat_print, "\n%05x: ", (uint)(PTR_FROM_BLOCK(bl) & (uint32_t)0xfffff)); - mp_printf(&mp_plat_print, "\n%05x: ", (uint)((bl * BYTES_PER_BLOCK) & (uint32_t)0xfffff)); - } - int c = ' '; - switch (ATB_GET_KIND(bl)) { - case AT_FREE: - c = '.'; - break; - /* this prints out if the object is reachable from BSS or STACK (for unix only) - case AT_HEAD: { - c = 'h'; - void **ptrs = (void**)(void*)&mp_state_ctx; - mp_uint_t len = offsetof(mp_state_ctx_t, vm.stack_top) / sizeof(mp_uint_t); - for (mp_uint_t i = 0; i < len; i++) { - mp_uint_t ptr = (mp_uint_t)ptrs[i]; - if (VERIFY_PTR(ptr) && BLOCK_FROM_PTR(ptr) == bl) { - c = 'B'; - break; - } - } - if (c == 'h') { - ptrs = (void**)&c; - len = ((mp_uint_t)MP_STATE_THREAD(stack_top) - (mp_uint_t)&c) / sizeof(mp_uint_t); + int c = ' '; + switch (ATB_GET_KIND(area, bl)) { + case AT_FREE: + c = '.'; + break; + /* this prints out if the object is reachable from BSS or STACK (for unix only) + case AT_HEAD: { + c = 'h'; + void **ptrs = (void**)(void*)&mp_state_ctx; + mp_uint_t len = offsetof(mp_state_ctx_t, vm.stack_top) / sizeof(mp_uint_t); for (mp_uint_t i = 0; i < len; i++) { mp_uint_t ptr = (mp_uint_t)ptrs[i]; - if (VERIFY_PTR(ptr) && BLOCK_FROM_PTR(ptr) == bl) { - c = 'S'; + if (gc_get_ptr_area(ptr) && BLOCK_FROM_PTR(ptr) == bl) { + c = 'B'; break; } } - } - break; - } - */ - /* this prints the uPy object type of the head block */ - case AT_HEAD: { - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wcast-align" - void **ptr = (void **)(MP_STATE_MEM(gc_pool_start) + bl * BYTES_PER_BLOCK); - #pragma GCC diagnostic pop - if (*ptr == &mp_type_tuple) { - c = 'T'; - } else if (*ptr == &mp_type_list) { - c = 'L'; - } else if (*ptr == &mp_type_dict) { - c = 'D'; - } else if (*ptr == &mp_type_str || *ptr == &mp_type_bytes) { - c = 'S'; - } - #if MICROPY_PY_BUILTINS_BYTEARRAY - else if (*ptr == &mp_type_bytearray) { - c = 'A'; - } - #endif - #if MICROPY_PY_ARRAY - else if (*ptr == &mp_type_array) { - c = 'A'; - } - #endif - #if MICROPY_PY_BUILTINS_FLOAT - else if (*ptr == &mp_type_float) { - c = 'F'; - } - #endif - else if (*ptr == &mp_type_fun_bc) { - c = 'B'; - } else if (*ptr == &mp_type_module) { - c = 'M'; - } else { - c = 'h'; - #if 0 - // This code prints "Q" for qstr-pool data, and "q" for qstr-str - // data. It can be useful to see how qstrs are being allocated, - // but is disabled by default because it is very slow. - for (const qstr_pool_t *pool = MP_STATE_VM(last_pool); c == 'h' && pool != NULL; pool = pool->prev) { - if ((const qstr_pool_t *)ptr == pool) { - c = 'Q'; - break; - } - for (const char *const *q = pool->qstrs, *const *q_top = pool->qstrs + pool->len; q < q_top; q++) { - if ((const char *)ptr == *q) { - c = 'q'; + if (c == 'h') { + ptrs = (void**)&c; + len = ((mp_uint_t)MP_STATE_THREAD(stack_top) - (mp_uint_t)&c) / sizeof(mp_uint_t); + for (mp_uint_t i = 0; i < len; i++) { + mp_uint_t ptr = (mp_uint_t)ptrs[i]; + if (gc_get_ptr_area(ptr) && BLOCK_FROM_PTR(ptr) == bl) { + c = 'S'; break; } } } - #endif + break; } - break; + */ + /* this prints the uPy object type of the head block */ + case AT_HEAD: { + void **ptr = (void **)(area->gc_pool_start + bl * BYTES_PER_BLOCK); + if (*ptr == &mp_type_tuple) { + c = 'T'; + } else if (*ptr == &mp_type_list) { + c = 'L'; + } else if (*ptr == &mp_type_dict) { + c = 'D'; + } else if (*ptr == &mp_type_str || *ptr == &mp_type_bytes) { + c = 'S'; + } + #if MICROPY_PY_BUILTINS_BYTEARRAY + else if (*ptr == &mp_type_bytearray) { + c = 'A'; + } + #endif + #if MICROPY_PY_ARRAY + else if (*ptr == &mp_type_array) { + c = 'A'; + } + #endif + #if MICROPY_PY_BUILTINS_FLOAT + else if (*ptr == &mp_type_float) { + c = 'F'; + } + #endif + else if (*ptr == &mp_type_fun_bc) { + c = 'B'; + } else if (*ptr == &mp_type_module) { + c = 'M'; + } else { + c = 'h'; + #if 0 + // This code prints "Q" for qstr-pool data, and "q" for qstr-str + // data. It can be useful to see how qstrs are being allocated, + // but is disabled by default because it is very slow. + for (qstr_pool_t *pool = MP_STATE_VM(last_pool); c == 'h' && pool != NULL; pool = pool->prev) { + if ((qstr_pool_t *)ptr == pool) { + c = 'Q'; + break; + } + for (const byte **q = pool->qstrs, **q_top = pool->qstrs + pool->len; q < q_top; q++) { + if ((const byte *)ptr == *q) { + c = 'q'; + break; + } + } + } + #endif + } + break; + } + case AT_TAIL: + c = '='; + break; + case AT_MARK: + c = 'm'; + break; } - case AT_TAIL: - c = '='; - break; - case AT_MARK: - c = 'm'; - break; + mp_printf(print, "%c", c); } - mp_printf(&mp_plat_print, "%c", c); + mp_print_str(print, "\n"); } mp_print_str(&mp_plat_print, "\n"); GC_EXIT(); @@ -1123,13 +1259,13 @@ void gc_test(void) { } printf("Before GC:\n"); - gc_dump_alloc_table(); + gc_dump_alloc_table(&mp_plat_print); printf("Starting GC...\n"); gc_collect_start(); gc_collect_root(ptrs, sizeof(ptrs) / sizeof(void *)); gc_collect_end(); printf("After GC:\n"); - gc_dump_alloc_table(); + gc_dump_alloc_table(&mp_plat_print); } #endif diff --git a/py/gc.h b/py/gc.h index c88ac78006..57995bcc1d 100644 --- a/py/gc.h +++ b/py/gc.h @@ -28,6 +28,7 @@ #include #include +#include "py/mpprint.h" #include "py/mpconfig.h" #include "py/mpstate.h" @@ -48,6 +49,11 @@ void gc_init(void *start, void *end); void gc_deinit(void); +#if MICROPY_GC_SPLIT_HEAP +// Used to add additional memory areas to the heap. +void gc_add(void *start, void *end); +#endif + // These lock/unlock functions can be nested. // They can be used to prevent the GC from allocating/freeing. void gc_lock(void); @@ -92,7 +98,7 @@ typedef struct _gc_info_t { } gc_info_t; void gc_info(gc_info_t *info); -void gc_dump_info(void); -void gc_dump_alloc_table(void); +void gc_dump_info(const mp_print_t *print); +void gc_dump_alloc_table(const mp_print_t *print); #endif // MICROPY_INCLUDED_PY_GC_H diff --git a/py/lexer.c b/py/lexer.c index 39e9662f63..e7d0e81440 100644 --- a/py/lexer.c +++ b/py/lexer.c @@ -356,6 +356,9 @@ STATIC void parse_string_literal(mp_lexer_t *lex, bool is_raw, bool is_fstring) vstr_add_byte(&lex->vstr, '{'); next_char(lex); } else { + // wrap each argument in (), e.g. + // f"{a,b,}, {c}" --> "{}".format((a,b), (c),) + vstr_add_byte(&lex->fstring_args, '('); // remember the start of this argument (if we need it for f'{a=}'). size_t i = lex->fstring_args.len; // extract characters inside the { until we reach the @@ -382,7 +385,9 @@ STATIC void parse_string_literal(mp_lexer_t *lex, bool is_raw, bool is_fstring) // remove the trailing '=' lex->fstring_args.len--; } - // comma-separate args + // close the paren-wrapped arg to .format(). + vstr_add_byte(&lex->fstring_args, ')'); + // comma-separate args to .format(). vstr_add_byte(&lex->fstring_args, ','); } vstr_add_byte(&lex->vstr, '{'); diff --git a/py/make_root_pointers.py b/py/make_root_pointers.py new file mode 100644 index 0000000000..efe398b822 --- /dev/null +++ b/py/make_root_pointers.py @@ -0,0 +1,55 @@ +""" +This pre-processor parses a single file containing a list of +MP_REGISTER_ROOT_POINTER(variable declaration) items. + +These are used to generate a header with the required entries for +"struct _mp_state_vm_t" in py/mpstate.h +""" + +from __future__ import print_function + +import argparse +import io +import re + + +PATTERN = re.compile(r"MP_REGISTER_ROOT_POINTER\((.*?)\);") + + +def find_root_pointer_registrations(filename): + """Find any MP_REGISTER_ROOT_POINTER definitions in the provided file. + + :param str filename: path to file to check + :return: List[variable_declaration] + """ + with io.open(filename, encoding="utf-8") as c_file_obj: + return set(re.findall(PATTERN, c_file_obj.read())) + + +def generate_root_pointer_header(root_pointers): + """Generate header with root pointer entries. + + :param List[variable_declaration] root_pointers: root pointer declarations + :return: None + """ + + # Print header file for all external modules. + print("// Automatically generated by make_root_pointers.py.") + print() + + for item in root_pointers: + print(item, end=";") + print() + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("file", nargs=1, help="file with MP_REGISTER_ROOT_POINTER definitions") + args = parser.parse_args() + + root_pointers = find_root_pointer_registrations(args.file[0]) + generate_root_pointer_header(sorted(root_pointers)) + + +if __name__ == "__main__": + main() diff --git a/py/makeqstrdefs.py b/py/makeqstrdefs.py index adf6195389..c489158f2b 100644 --- a/py/makeqstrdefs.py +++ b/py/makeqstrdefs.py @@ -2,7 +2,7 @@ This script processes the output from the C preprocessor and extracts all qstr. Each qstr is transformed into a qstr definition of the form 'Q(...)'. -This script works with Python 3.x +This script works with Python 3.x (CIRCUITPY: not 2.x) """ from __future__ import print_function @@ -17,10 +17,6 @@ import multiprocessing, multiprocessing.dummy from html.entities import name2codepoint -# Blocklist of qstrings that are specially handled in further -# processing and should be ignored -QSTRING_BLOCK_LIST = set(["NULL", "number_of"]) - # add some custom names to map characters that aren't in HTML name2codepoint["hyphen"] = ord("-") name2codepoint["space"] = ord(" ") @@ -64,6 +60,9 @@ _MODE_COMPRESS = "compress" # Extract MP_REGISTER_MODULE(...) macros. _MODE_MODULE = "module" +# Extract MP_REGISTER_ROOT_POINTER(...) macros. +_MODE_ROOT_POINTER = "root_pointer" + def is_c_source(fname): return os.path.splitext(fname)[1] in [".c"] @@ -132,25 +131,26 @@ def qstr_unescape(qstr): def process_file(f): - re_line = re.compile(r"#[line]*\s\d+\s\"([^\"]+)\"") + # match gcc-like output (# n "file") and msvc-like output (#line n "file") + re_line = re.compile(r"^#(?:line)?\s+\d+\s\"([^\"]+)\"") if args.mode == _MODE_QSTR: re_match = re.compile(r"MP_QSTR_[_a-zA-Z0-9]+") elif args.mode == _MODE_COMPRESS: re_match = re.compile(r'MP_COMPRESSED_ROM_TEXT\("([^"]*)"\)') elif args.mode == _MODE_MODULE: re_match = re.compile(r"MP_REGISTER_MODULE\(.*?,\s*.*?\);") + elif args.mode == _MODE_ROOT_POINTER: + re_match = re.compile(r"MP_REGISTER_ROOT_POINTER\(.*?\);") re_translate = re.compile(r"translate\(\"((?:(?=(\\?))\2.)*?)\"\)") output = [] last_fname = None for line in f: if line.isspace(): continue - # match gcc-like output (# n "file") and msvc-like output (#line n "file") - if line.startswith(("# ", "#line")): - m = re_line.match(line) - assert m is not None + m = re_line.match(line) + if m: fname = m.group(1) - if os.path.splitext(fname)[1] not in [".c", ".cpp"]: + if not is_c_source(fname) and not is_cxx_source(fname): continue if fname != last_fname: write_out(last_fname, output) @@ -160,9 +160,8 @@ def process_file(f): for match in re_match.findall(line): if args.mode == _MODE_QSTR: name = match.replace("MP_QSTR_", "") - if name not in QSTRING_BLOCK_LIST: - output.append("Q(" + qstr_unescape(name) + ")") - elif args.mode in (_MODE_COMPRESS, _MODE_MODULE): + output.append("Q(" + name + ")") + elif args.mode in (_MODE_COMPRESS, _MODE_MODULE, _MODE_ROOT_POINTER): output.append(match) for match in re_translate.findall(line): @@ -202,6 +201,8 @@ def cat_together(): mode_full = "Compressed data" elif args.mode == _MODE_MODULE: mode_full = "Module registrations" + elif args.mode == _MODE_ROOT_POINTER: + mode_full = "Root pointer registrations" if old_hash != new_hash: print(mode_full, "updated") try: @@ -262,7 +263,7 @@ if __name__ == "__main__": args.output_dir = sys.argv[4] args.output_file = None if len(sys.argv) == 5 else sys.argv[5] # Unused for command=split - if args.mode not in (_MODE_QSTR, _MODE_COMPRESS, _MODE_MODULE): + if args.mode not in (_MODE_QSTR, _MODE_COMPRESS, _MODE_MODULE, _MODE_ROOT_POINTER): print("error: mode %s unrecognised" % sys.argv[2]) sys.exit(2) diff --git a/py/makeversionhdr.py b/py/makeversionhdr.py index 11d162ecea..075e02a992 100644 --- a/py/makeversionhdr.py +++ b/py/makeversionhdr.py @@ -6,16 +6,25 @@ This script works with Python 3.7 and newer from __future__ import print_function +import argparse import sys import os import pathlib import datetime import subprocess +// CIRCUITPY: use external script tools_describe = str(pathlib.Path(__file__).parent.parent / "tools/describe") def get_version_info_from_git(): + # Python 2.6 doesn't have check_output, so check for that + try: + subprocess.check_output + subprocess.check_call + except AttributeError: + return None + # Note: git describe doesn't work if no tag is available try: git_tag = subprocess.check_output( @@ -26,11 +35,12 @@ def get_version_info_from_git(): # git exit code of 128 means no repository found return None git_tag = "" - except OSError as e: + except OSError: return None try: git_hash = subprocess.check_output( ["git", "rev-parse", "--short", "HEAD"], + cwd=repo_path, stderr=subprocess.STDOUT, universal_newlines=True, ).strip() @@ -42,17 +52,22 @@ def get_version_info_from_git(): try: # Check if there are any modified files. subprocess.check_call( - ["git", "diff", "--no-ext-diff", "--quiet", "--exit-code"], stderr=subprocess.STDOUT + ["git", "diff", "--no-ext-diff", "--quiet", "--exit-code"], + cwd=repo_path, + stderr=subprocess.STDOUT, ) # Check if there are any staged files. subprocess.check_call( - ["git", "diff-index", "--cached", "--quiet", "HEAD", "--"], stderr=subprocess.STDOUT + ["git", "diff-index", "--cached", "--quiet", "HEAD", "--"], + cwd=repo_path, + stderr=subprocess.STDOUT, ) except subprocess.CalledProcessError: git_hash += "-dirty" except OSError: return None + # CIRCUITPY # Try to extract MicroPython version from git tag ver = git_tag.split("-")[0].split(".") @@ -120,9 +135,26 @@ def make_version_header(filename): # Only write the file if we need to if write_file: + print("GEN %s" % filename) with open(filename, "w") as f: f.write(file_data) +def main(): + parser = argparse.ArgumentParser() + # makeversionheader.py lives in repo/py, so default repo_path to the + # parent of sys.argv[0]'s directory. + parser.add_argument( + "-r", + "--repo-path", + default=os.path.join(os.path.dirname(sys.argv[0]), ".."), + help="path to MicroPython Git repo to query for version", + ) + parser.add_argument("dest", nargs=1, help="output file path") + args = parser.parse_args() + + make_version_header(args.repo_path, args.dest[0]) + + if __name__ == "__main__": - make_version_header(sys.argv[1]) + main() diff --git a/py/map.c b/py/map.c index b194250cb4..c18df5a9f3 100644 --- a/py/map.c +++ b/py/map.c @@ -174,7 +174,7 @@ mp_map_elem_t *MICROPY_WRAP_MP_MAP_LOOKUP(mp_map_lookup)(mp_map_t * map, mp_obj_ if (compare_only_ptrs) { if (mp_obj_is_qstr(index)) { // Index is a qstr, so can just do ptr comparison. - } else if (mp_obj_is_type(index, &mp_type_str)) { + } else if (mp_obj_is_exact_type(index, &mp_type_str)) { // Index is a non-interned string. // We can either intern the string, or force a full equality comparison. // We chose the latter, since interning costs time and potentially RAM, @@ -221,6 +221,7 @@ mp_map_elem_t *MICROPY_WRAP_MP_MAP_LOOKUP(mp_map_lookup)(mp_map_t * map, mp_obj_ } mp_map_elem_t *elem = map->table + map->used++; elem->key = index; + elem->value = MP_OBJ_NULL; if (!mp_obj_is_qstr(index)) { map->all_keys_are_qstrs = 0; } diff --git a/py/misc.h b/py/misc.h index 816d6446b1..863ee371a0 100644 --- a/py/misc.h +++ b/py/misc.h @@ -54,6 +54,11 @@ 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) +#else +#define MP_STATIC_ASSERT_NOT_MSC(cond) MP_STATIC_ASSERT(cond) +#endif // Round-up integer division #define MP_CEIL_DIVIDE(a, b) (((a) + (b) - 1) / (b)) @@ -70,6 +75,7 @@ typedef unsigned int uint; #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))) #if MICROPY_ENABLE_FINALISER #define m_new_obj_with_finaliser(type) ((type *)(m_malloc_with_finaliser(sizeof(type)))) @@ -179,7 +185,7 @@ typedef struct _vstr_t { size_t alloc; size_t len; char *buf; - bool fixed_buf : 1; + bool fixed_buf; } vstr_t; // convenience macro to declare a vstr with a fixed size buffer on the stack @@ -244,10 +250,12 @@ extern mp_uint_t mp_verbose_flag; #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE #define MP_FLOAT_EXP_BITS (11) +#define MP_FLOAT_EXP_OFFSET (1023) #define MP_FLOAT_FRAC_BITS (52) typedef uint64_t mp_float_uint_t; #elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT #define MP_FLOAT_EXP_BITS (8) +#define MP_FLOAT_EXP_OFFSET (127) #define MP_FLOAT_FRAC_BITS (23) typedef uint32_t mp_float_uint_t; #endif @@ -294,15 +302,16 @@ typedef union _mp_float_union_t { // Force usage of the MP_ERROR_TEXT macro by requiring an opaque type. typedef struct { - #ifdef __clang__ - // Fix "error: empty struct has size 0 in C, size 1 in C++". + #if defined(__clang__) || defined(_MSC_VER) + // Fix "error: empty struct has size 0 in C, size 1 in C++", and the msvc counterpart + // "C requires that a struct or union have at least one member" char dummy; #endif } *mp_rom_error_text_t; #include -inline __attribute__((always_inline)) const char *MP_COMPRESSED_ROM_TEXT(const char *msg) { +inline MP_ALWAYSINLINE const char *MP_COMPRESSED_ROM_TEXT(const char *msg) { // "genhdr/compressed.data.h" contains an invocation of the MP_MATCH_COMPRESSED macro for each compressed string. // The giant if(strcmp) tree is optimized by the compiler, which turns this into a direct return of the compressed data. #define MP_MATCH_COMPRESSED(a, b) if (strcmp(msg, a) == 0) { return b; } else diff --git a/py/mkenv.mk b/py/mkenv.mk index a53e030aed..ee2a96e258 100644 --- a/py/mkenv.mk +++ b/py/mkenv.mk @@ -67,6 +67,7 @@ NPROC = $(PYTHON) -c 'import multiprocessing as mp; print(mp.cpu_count())' AS = $(CROSS_COMPILE)as CC = $(CROSS_COMPILE)gcc +CPP = $(CC) -E CXX = $(CROSS_COMPILE)g++ GDB = $(CROSS_COMPILE)gdb LD = $(CROSS_COMPILE)ld @@ -80,10 +81,11 @@ MAKE_FROZEN = $(PYTHON) $(TOP)/tools/make-frozen.py MPY_TOOL = $(PYTHON) $(TOP)/tools/mpy-tool.py PREPROCESS_FROZEN_MODULES = PYTHONPATH=$(TOP)/tools/python-semver $(TOP)/tools/preprocess_frozen_modules.py -MPY_LIB_DIR = $(TOP)/../micropython-lib +MPY_LIB_SUBMODULE_DIR = $(TOP)/lib/micropython-lib +MPY_LIB_DIR = $(MPY_LIB_SUBMODULE_DIR) ifeq ($(MICROPY_MPYCROSS),) -MICROPY_MPYCROSS = $(TOP)/mpy-cross/mpy-cross +MICROPY_MPYCROSS = $(TOP)/mpy-cross/build/mpy-cross MICROPY_MPYCROSS_DEPENDENCY = $(MICROPY_MPYCROSS) endif diff --git a/py/mkrules.cmake b/py/mkrules.cmake index d0dc019625..7eb5fcf018 100644 --- a/py/mkrules.cmake +++ b/py/mkrules.cmake @@ -11,6 +11,9 @@ set(MICROPY_QSTRDEFS_GENERATED "${MICROPY_GENHDR_DIR}/qstrdefs.generated.h") set(MICROPY_MODULEDEFS_SPLIT "${MICROPY_GENHDR_DIR}/moduledefs.split") set(MICROPY_MODULEDEFS_COLLECTED "${MICROPY_GENHDR_DIR}/moduledefs.collected") set(MICROPY_MODULEDEFS "${MICROPY_GENHDR_DIR}/moduledefs.h") +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") # Need to do this before extracting MICROPY_CPP_DEF below. Rest of frozen # manifest handling is at the end of this file. @@ -46,23 +49,18 @@ target_sources(${MICROPY_TARGET} PRIVATE ${MICROPY_MPVERSION} ${MICROPY_QSTRDEFS_GENERATED} ${MICROPY_MODULEDEFS} + ${MICROPY_ROOT_POINTERS} ) # Command to force the build of another command -add_custom_command( - OUTPUT MICROPY_FORCE_BUILD - COMMENT "" - COMMAND echo -n -) - # Generate mpversion.h -add_custom_command( - OUTPUT ${MICROPY_MPVERSION} +add_custom_target( + BUILD_VERSION_HEADER ALL + BYPRODUCTS ${MICROPY_MPVERSION} COMMAND ${CMAKE_COMMAND} -E make_directory ${MICROPY_GENHDR_DIR} COMMAND ${Python3_EXECUTABLE} ${MICROPY_DIR}/py/makeversionhdr.py ${MICROPY_MPVERSION} - DEPENDS MICROPY_FORCE_BUILD ) # Generate qstrs @@ -139,6 +137,31 @@ add_custom_command( DEPENDS ${MICROPY_MODULEDEFS_COLLECTED} ) +# Generate root_pointers.h + +add_custom_command( + OUTPUT ${MICROPY_ROOT_POINTERS_SPLIT} + COMMAND ${Python3_EXECUTABLE} ${MICROPY_PY_DIR}/makeqstrdefs.py split root_pointer ${MICROPY_GENHDR_DIR}/qstr.i.last ${MICROPY_GENHDR_DIR}/root_pointer _ + COMMAND touch ${MICROPY_ROOT_POINTERS_SPLIT} + DEPENDS ${MICROPY_QSTRDEFS_LAST} + VERBATIM + COMMAND_EXPAND_LISTS +) + +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} + DEPENDS ${MICROPY_ROOT_POINTERS_SPLIT} + VERBATIM + COMMAND_EXPAND_LISTS +) + +add_custom_command( + OUTPUT ${MICROPY_ROOT_POINTERS} + COMMAND ${Python3_EXECUTABLE} ${MICROPY_PY_DIR}/make_root_pointers.py ${MICROPY_ROOT_POINTERS_COLLECTED} > ${MICROPY_ROOT_POINTERS} + DEPENDS ${MICROPY_ROOT_POINTERS_COLLECTED} ${MICROPY_PY_DIR}/make_root_pointers.py +) + # Build frozen code if enabled if(MICROPY_FROZEN_MANIFEST) @@ -151,7 +174,12 @@ if(MICROPY_FROZEN_MANIFEST) # Note: target_compile_definitions already added earlier. if(NOT MICROPY_LIB_DIR) - set(MICROPY_LIB_DIR ${MICROPY_DIR}/../micropython-lib) + string(CONCAT GIT_SUBMODULES "${GIT_SUBMODULES} " lib/micropython-lib) + set(MICROPY_LIB_DIR ${MICROPY_DIR}/lib/micropython-lib) + endif() + + if(NOT (${ECHO_SUBMODULES}) AND NOT EXISTS ${MICROPY_LIB_DIR}/README.md) + message(FATAL_ERROR " micropython-lib not initialized.\n Run 'make BOARD=${MICROPY_BOARD} submodules'") endif() # If MICROPY_MPYCROSS is not explicitly defined in the environment (which @@ -159,7 +187,7 @@ if(MICROPY_FROZEN_MANIFEST) # to automatically build mpy-cross if needed. set(MICROPY_MPYCROSS $ENV{MICROPY_MPYCROSS}) if(NOT MICROPY_MPYCROSS) - set(MICROPY_MPYCROSS_DEPENDENCY ${MICROPY_DIR}/mpy-cross/mpy-cross) + set(MICROPY_MPYCROSS_DEPENDENCY ${MICROPY_DIR}/mpy-cross/build/mpy-cross) if(NOT MICROPY_MAKE_EXECUTABLE) set(MICROPY_MAKE_EXECUTABLE make) endif() @@ -169,11 +197,13 @@ if(MICROPY_FROZEN_MANIFEST) ) endif() - add_custom_command( - OUTPUT ${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} ${MICROPY_FROZEN_MANIFEST} - DEPENDS MICROPY_FORCE_BUILD + 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} + DEPENDS ${MICROPY_QSTRDEFS_GENERATED} + ${MICROPY_ROOT_POINTERS} ${MICROPY_MPYCROSS_DEPENDENCY} VERBATIM ) @@ -182,6 +212,15 @@ endif() # Update submodules if(ECHO_SUBMODULES) # If cmake is run with GIT_SUBMODULES defined on command line, process the port / board - # settings then print the final GIT_SUBMODULES variable as a fatal error and exit. - message(FATAL_ERROR "GIT_SUBMODULES=${GIT_SUBMODULES}") + # settings then print the final GIT_SUBMODULES variable and exit. + # Note: the GIT_SUBMODULES is done via echo rather than message, as message splits + # the output onto multiple lines + execute_process(COMMAND ${CMAKE_COMMAND} -E echo "GIT_SUBMODULES=${GIT_SUBMODULES}") + message(FATAL_ERROR "Done") +endif() + +# Display BOARD_VARIANTS +if(ECHO_BOARD_VARIANTS) + execute_process(COMMAND ${CMAKE_COMMAND} -E echo "BOARD_VARIANTS=${BOARD_VARIANTS}") + message(FATAL_ERROR "Done") endif() diff --git a/py/mkrules.mk b/py/mkrules.mk index 5b871af62b..77eb36a918 100644 --- a/py/mkrules.mk +++ b/py/mkrules.mk @@ -4,11 +4,14 @@ THIS_MAKEFILE = $(lastword $(MAKEFILE_LIST)) include $(dir $(THIS_MAKEFILE))mkenv.mk 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'" + # Extra deps that need to happen before object compilation. OBJ_EXTRA_ORDER_DEPS = -# Generate moduledefs.h. -OBJ_EXTRA_ORDER_DEPS += $(HEADER_BUILD)/moduledefs.h +# Generate header files. +OBJ_EXTRA_ORDER_DEPS += $(HEADER_BUILD)/moduledefs.h $(HEADER_BUILD)/root_pointers.h ifeq ($(MICROPY_ROM_TEXT_COMPRESSION),1) # If compression is enabled, trigger the build of compressed.data.h... @@ -65,7 +68,7 @@ endef define compile_cxx $(ECHO) "CXX $<" -$(Q)$(CXX) $(CXXFLAGS) -c -MD -o $@ $< +$(Q)$(CXX) $(CXXFLAGS) -c -MD -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. @@ -75,8 +78,7 @@ $(Q)$(CXX) $(CXXFLAGS) -c -MD -o $@ $< $(RM) -f $(@:.o=.d) endef -# frozen.c and frozen_mpy.c are created in $(BUILD), so add it to the vpath as well. -vpath %.c . $(TOP) $(USER_C_MODULES) $(DEVICES_MODULES) $(BUILD) +vpath %.c . $(TOP) $(USER_C_MODULES) $(BUILD)/%.o: %.c $(call compile_c) @@ -127,6 +129,16 @@ $(HEADER_BUILD)/moduledefs.collected: $(HEADER_BUILD)/moduledefs.split $(STEPECHO) "GEN $@" $(Q)$(PYTHON) $(PY_SRC)/makeqstrdefs.py cat module _ $(HEADER_BUILD)/module $@ +# Module definitions via MP_REGISTER_ROOT_POINTER. +$(HEADER_BUILD)/root_pointers.split: $(HEADER_BUILD)/qstr.i.last + $(STEPECHO) "GEN $@" + $(Q)$(PYTHON) $(PY_SRC)/makeqstrdefs.py split root_pointer $< $(HEADER_BUILD)/root_pointer _ + $(Q)$(TOUCH) $@ + +$(HEADER_BUILD)/root_pointers.collected: $(HEADER_BUILD)/root_pointers.split + $(STEPECHO) "GEN $@" + $(Q)$(PYTHON) $(PY_SRC)/makeqstrdefs.py cat root_pointer _ $(HEADER_BUILD)/root_pointer $@ + # Compressed error strings. $(HEADER_BUILD)/compressed.split: $(HEADER_BUILD)/qstr.i.last $(STEPECHO) "GEN $@" @@ -165,37 +177,44 @@ $(error Support for FROZEN_MPY_DIR was removed. Please use manifest.py instead, endif ifneq ($(FROZEN_MANIFEST),) +# If we're using the default submodule path for micropython-lib, then make +# sure it's included in "make submodules". +ifeq ($(MPY_LIB_DIR),$(MPY_LIB_SUBMODULE_DIR)) +GIT_SUBMODULES += lib/micropython-lib +endif + # to build frozen_content.c from a manifest -$(BUILD)/frozen_content.c: $(BUILD)/genhdr/qstrdefs.generated.h $(FROZEN_MANIFEST) | $(MICROPY_MPYCROSS_DEPENDENCY) +$(BUILD)/frozen_content.c: FORCE $(BUILD)/genhdr/qstrdefs.generated.h $(BUILD)/genhdr/root_pointers.h | $(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) endif ifneq ($(PROG),) # Build a standalone executable (unix does this) -# The executable should have an .exe extension for builds targeting 'pure' +# The executable should have an .exe extension for builds targetting 'pure' # Windows, i.e. msvc or mingw builds, but not when using msys or cygwin's gcc. COMPILER_TARGET := $(shell $(CC) -dumpmachine) ifneq (,$(findstring mingw,$(COMPILER_TARGET))) PROG := $(PROG).exe endif -all: $(PROG) +all: $(BUILD)/$(PROG) -$(PROG): $(OBJ) - $(STEPECHO) "LINK $@" +$(BUILD)/$(PROG): $(OBJ) + $(ECHO) "LINK $@" # Do not pass COPT here - it's *C* compiler optimizations. For example, # we may want to compile using Thumb, but link with non-Thumb libc. $(Q)$(CC) -o $@ $^ $(LIB) $(LDFLAGS) -ifdef STRIP_CIRCUITPYTHON +ifndef DEBUG $(Q)$(STRIP) $(STRIPFLAGS_EXTRA) $@ endif $(Q)$(SIZE) $$(find $(BUILD) -path "$(BUILD)/build/frozen*.o") $@ clean: clean-prog clean-prog: - $(RM) -f $(PROG) - $(RM) -f $(PROG).map + $(RM) -f $(BUILD)/$(PROG) + $(RM) -f $(BUILD)/$(PROG).map .PHONY: clean-prog endif @@ -215,8 +234,8 @@ LIBMICROPYTHON = libmicropython.a # with 3rd-party projects which don't have proper dependency # tracking. Then LIBMICROPYTHON_EXTRA_CMD can e.g. touch some # other file to cause needed effect, e.g. relinking with new lib. -lib $(LIBMICROPYTHON): $(OBJ) - $(Q)$(AR) rcs $(LIBMICROPYTHON) $^ +lib $(BUILD)/$(LIBMICROPYTHON): $(OBJ) + $(Q)$(AR) rcs $(BUILD)/$(LIBMICROPYTHON) $^ $(LIBMICROPYTHON_EXTRA_CMD) clean: @@ -236,8 +255,3 @@ print-def: @$(RM) -f __empty__.c -include $(OBJ:.o=.P) - -# Print out the value of a make variable. -# https://stackoverflow.com/questions/16467718/how-to-print-out-a-variable-in-makefile -print-%: - @echo $* = $($*) diff --git a/py/modbuiltins.c b/py/modbuiltins.c index 870b19d7b2..e5695bfb53 100644 --- a/py/modbuiltins.c +++ b/py/modbuiltins.c @@ -35,8 +35,6 @@ #include "py/builtin.h" #include "py/stream.h" -#include "supervisor/shared/translate/translate.h" - #if MICROPY_PY_BUILTINS_FLOAT #include #endif @@ -253,7 +251,7 @@ STATIC mp_obj_t mp_builtin_input(size_t n_args, const mp_obj_t *args) { if (line.len == 0 && ret == CHAR_CTRL_D) { mp_raise_type(&mp_type_EOFError); } - return mp_obj_new_str_from_vstr(&mp_type_str, &line); + return mp_obj_new_str_from_vstr(&line); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_input_obj, 0, 1, mp_builtin_input); @@ -462,7 +460,7 @@ STATIC mp_obj_t mp_builtin___repl_print__(mp_obj_t o) { #if MICROPY_CAN_OVERRIDE_BUILTINS // Set "_" special variable mp_obj_t dest[2] = {MP_OBJ_SENTINEL, o}; - mp_type_module.attr(MP_OBJ_FROM_PTR(&mp_module_builtins), MP_QSTR__, dest); + MP_OBJ_TYPE_GET_SLOT(&mp_type_module, attr)(MP_OBJ_FROM_PTR(&mp_module_builtins), MP_QSTR__, dest); #endif } return mp_const_none; @@ -474,7 +472,7 @@ STATIC mp_obj_t mp_builtin_repr(mp_obj_t o_in) { mp_print_t print; vstr_init_print(&vstr, 16, &print); mp_obj_print_helper(&print, o_in, PRINT_REPR); - return mp_obj_new_str_from_vstr(&mp_type_str, &vstr); + return mp_obj_new_str_from_utf8_vstr(&vstr); } MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_repr_obj, mp_builtin_repr); @@ -554,7 +552,7 @@ STATIC mp_obj_t mp_builtin_sorted(size_t n_args, const mp_obj_t *args, mp_map_t if (n_args > 1) { mp_raise_TypeError(MP_ERROR_TEXT("must use keyword argument for key function")); } - mp_obj_t self = mp_type_list.make_new(&mp_type_list, 1, 0, args); + mp_obj_t self = mp_obj_list_make_new(&mp_type_list, 1, 0, args); mp_obj_list_sort(1, &self, kwargs); return self; diff --git a/py/modio.c b/py/modio.c index 836d04fb11..9ec6bbcc4e 100644 --- a/py/modio.c +++ b/py/modio.c @@ -37,9 +37,6 @@ #if MICROPY_PY_IO -extern const mp_obj_type_t mp_type_fileio; -extern const mp_obj_type_t mp_type_textio; - #if MICROPY_PY_IO_IOBASE STATIC const mp_obj_type_t mp_type_iobase; @@ -95,21 +92,18 @@ STATIC mp_uint_t iobase_ioctl(mp_obj_t obj, mp_uint_t request, uintptr_t arg, in } STATIC const mp_stream_p_t iobase_p = { - MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream) .read = iobase_read, .write = iobase_write, .ioctl = iobase_ioctl, }; -STATIC const mp_obj_type_t mp_type_iobase = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_IOBase, - .make_new = iobase_make_new, - MP_TYPE_EXTENDED_FIELDS( - .protocol = &iobase_p, - ), -}; +STATIC MP_DEFINE_CONST_OBJ_TYPE( + mp_type_iobase, + MP_QSTR_IOBase, + MP_TYPE_FLAG_NONE, + make_new, iobase_make_new, + protocol, &iobase_p + ); #endif // MICROPY_PY_IO_IOBASE @@ -195,40 +189,27 @@ STATIC const mp_rom_map_elem_t bufwriter_locals_dict_table[] = { STATIC MP_DEFINE_CONST_DICT(bufwriter_locals_dict, bufwriter_locals_dict_table); STATIC const mp_stream_p_t bufwriter_stream_p = { - MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream) .write = bufwriter_write, }; -STATIC const mp_obj_type_t mp_type_bufwriter = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_BufferedWriter, - .make_new = bufwriter_make_new, - .locals_dict = (mp_obj_dict_t *)&bufwriter_locals_dict, - MP_TYPE_EXTENDED_FIELDS( - .protocol = &bufwriter_stream_p, - ), -}; +STATIC MP_DEFINE_CONST_OBJ_TYPE( + mp_type_bufwriter, + MP_QSTR_BufferedWriter, + MP_TYPE_FLAG_NONE, + make_new, bufwriter_make_new, + protocol, &bufwriter_stream_p, + locals_dict, &bufwriter_locals_dict + ); #endif // MICROPY_PY_IO_BUFFEREDWRITER STATIC const mp_rom_map_elem_t mp_module_io_globals_table[] = { - #if CIRCUITPY - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_io) }, - #else { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uio) }, - #endif // Note: mp_builtin_open_obj should be defined by port, it's not // part of the core. { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) }, #if MICROPY_PY_IO_IOBASE { MP_ROM_QSTR(MP_QSTR_IOBase), MP_ROM_PTR(&mp_type_iobase) }, #endif - #if MICROPY_PY_IO_FILEIO - { MP_ROM_QSTR(MP_QSTR_FileIO), MP_ROM_PTR(&mp_type_fileio) }, - #if MICROPY_CPYTHON_COMPAT - { MP_ROM_QSTR(MP_QSTR_TextIOWrapper), MP_ROM_PTR(&mp_type_textio) }, - #endif - #endif { MP_ROM_QSTR(MP_QSTR_StringIO), MP_ROM_PTR(&mp_type_stringio) }, #if MICROPY_PY_IO_BYTESIO { MP_ROM_QSTR(MP_QSTR_BytesIO), MP_ROM_PTR(&mp_type_bytesio) }, diff --git a/py/modmath.c b/py/modmath.c index 0567e5dd94..81cf0cb8fa 100644 --- a/py/modmath.c +++ b/py/modmath.c @@ -27,8 +27,6 @@ #include "py/builtin.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" - #if MICROPY_PY_BUILTINS_FLOAT && MICROPY_PY_MATH #include @@ -56,7 +54,7 @@ STATIC mp_obj_t math_generic_2(mp_obj_t x_obj, mp_obj_t y_obj, mp_float_t (*f)(m mp_float_t x = mp_obj_get_float(x_obj); mp_float_t y = mp_obj_get_float(y_obj); mp_float_t ans = f(x, y); - if ((isnan(ans) && !isnan(x) && !isnan(y)) || (isinf(ans) && !isinf(x))) { + if ((isnan(ans) && !isnan(x) && !isnan(y)) || (isinf(ans) && !isinf(x) && !isinf(y))) { math_error(); } return mp_obj_new_float(ans); diff --git a/py/modmicropython.c b/py/modmicropython.c index ee4d57d94b..7c097ff487 100644 --- a/py/modmicropython.c +++ b/py/modmicropython.c @@ -32,7 +32,7 @@ #include "py/gc.h" #include "py/mphal.h" -#include "supervisor/shared/translate/translate.h" +#if MICROPY_PY_MICROPYTHON // Various builtins specific to MicroPython runtime, // living in micropython module @@ -81,10 +81,10 @@ mp_obj_t mp_micropython_mem_info(size_t n_args, const mp_obj_t *args) { mp_printf(&mp_plat_print, "stack: " UINT_FMT "\n", mp_stack_usage()); #endif #if MICROPY_ENABLE_GC - gc_dump_info(); + gc_dump_info(&mp_plat_print); if (n_args == 1) { // arg given means dump gc allocation table - gc_dump_alloc_table(); + gc_dump_alloc_table(&mp_plat_print); } #else (void)n_args; @@ -213,3 +213,5 @@ const mp_obj_module_t mp_module_micropython = { }; MP_REGISTER_MODULE(MP_QSTR_micropython, mp_module_micropython); + +#endif // MICROPY_PY_MICROPYTHON diff --git a/py/modstruct.c b/py/modstruct.c index a2dfa91655..c772977013 100644 --- a/py/modstruct.c +++ b/py/modstruct.c @@ -34,8 +34,6 @@ #include "py/binary.h" #include "py/parsenum.h" -#include "supervisor/shared/translate/translate.h" - #if MICROPY_PY_STRUCT /* @@ -237,7 +235,7 @@ STATIC mp_obj_t struct_pack(size_t n_args, const mp_obj_t *args) { byte *p = (byte *)vstr.buf; memset(p, 0, size); struct_pack_into_internal(args[0], p, n_args - 1, &args[1]); - return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); + return mp_obj_new_bytes_from_vstr(&vstr); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(struct_pack_obj, 1, MP_OBJ_FUN_ARGS_MAX, struct_pack); diff --git a/py/modsys.c b/py/modsys.c index 63fb654f05..5137ff6e24 100644 --- a/py/modsys.c +++ b/py/modsys.c @@ -113,6 +113,12 @@ STATIC const mp_rom_obj_tuple_t mp_sys_implementation_obj = { STATIC const MP_DEFINE_STR_OBJ(mp_sys_platform_obj, MICROPY_PY_SYS_PLATFORM); #endif +#ifdef MICROPY_PY_SYS_EXECUTABLE +// executable - the path to the micropython binary +// This object is non-const and is populated at startup in main() +MP_DEFINE_STR_OBJ(mp_sys_executable_obj, ""); +#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) { @@ -260,6 +266,10 @@ STATIC const mp_rom_map_elem_t mp_module_sys_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_getsizeof), MP_ROM_PTR(&mp_sys_getsizeof_obj) }, #endif + #if MICROPY_PY_SYS_EXECUTABLE + { MP_ROM_QSTR(MP_QSTR_executable), MP_ROM_PTR(&mp_sys_executable_obj) }, + #endif + /* * Extensions to CPython */ @@ -284,4 +294,24 @@ const mp_obj_module_t mp_module_sys = { MP_REGISTER_MODULE(MP_QSTR_usys, mp_module_sys); +// If MICROPY_PY_SYS_PATH_ARGV_DEFAULTS is not enabled then these two lists +// must be initialised after the call to mp_init. +MP_REGISTER_ROOT_POINTER(mp_obj_list_t mp_sys_path_obj); +MP_REGISTER_ROOT_POINTER(mp_obj_list_t mp_sys_argv_obj); + +#if MICROPY_PY_SYS_EXC_INFO +// current exception being handled, for sys.exc_info() +MP_REGISTER_ROOT_POINTER(mp_obj_base_t * cur_exception); #endif + +#if MICROPY_PY_SYS_ATEXIT +// exposed through sys.atexit function +MP_REGISTER_ROOT_POINTER(mp_obj_t sys_exitfunc); +#endif + +#if MICROPY_PY_SYS_ATTR_DELEGATION +// Contains mutable sys attributes. +MP_REGISTER_ROOT_POINTER(mp_obj_t sys_mutable[MP_SYS_MUTABLE_NUM]); +#endif + +#endif // MICROPY_PY_SYS diff --git a/py/modthread.c b/py/modthread.c index bad94fbf2f..51d63e4703 100644 --- a/py/modthread.c +++ b/py/modthread.c @@ -116,11 +116,12 @@ STATIC const mp_rom_map_elem_t thread_lock_locals_dict_table[] = { STATIC MP_DEFINE_CONST_DICT(thread_lock_locals_dict, thread_lock_locals_dict_table); -STATIC const mp_obj_type_t mp_type_thread_lock = { - { &mp_type_type }, - .name = MP_QSTR_lock, - .locals_dict = (mp_obj_dict_t *)&thread_lock_locals_dict, -}; +STATIC MP_DEFINE_CONST_OBJ_TYPE( + mp_type_thread_lock, + MP_QSTR_lock, + MP_TYPE_FLAG_NONE, + locals_dict, &thread_lock_locals_dict + ); /****************************************************************/ // _thread module diff --git a/py/moduerrno.c b/py/moduerrno.c index cf3cbe064f..19e7926493 100644 --- a/py/moduerrno.c +++ b/py/moduerrno.c @@ -30,8 +30,6 @@ #include "py/obj.h" #include "py/mperrno.h" -#include "supervisor/shared/translate/translate.h" - // This list can be defined per port in mpconfigport.h to tailor it to a // specific port's needs. If it's not defined then we provide a default. #ifndef MICROPY_PY_UERRNO_LIST diff --git a/py/mpconfig.h b/py/mpconfig.h index 0c4b924ef9..304caf5dce 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -32,8 +32,8 @@ #else // Current version of MicroPython #define MICROPY_VERSION_MAJOR 1 -#define MICROPY_VERSION_MINOR 19 -#define MICROPY_VERSION_MICRO 1 +#define MICROPY_VERSION_MINOR 20 +#define MICROPY_VERSION_MICRO 0 // Combined version as a 32-bit number for convenience #define MICROPY_VERSION ( \ @@ -86,6 +86,12 @@ // Enable everything (e.g. coverage) #define MICROPY_CONFIG_ROM_LEVEL_EVERYTHING (50) +#ifdef MP_CONFIGFILE +#include MP_CONFIGFILE +#else +#include +#endif + // Ports/boards should set this, but default to level=core. #ifndef MICROPY_CONFIG_ROM_LEVEL #define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_CORE_FEATURES) @@ -521,7 +527,6 @@ #define MICROPY_DEBUG_VM_STACK_OVERFLOW (0) #endif -// CIRCUITPY // Whether to enable extra instrumentation for valgrind #ifndef MICROPY_DEBUG_VALGRIND #define MICROPY_DEBUG_VALGRIND (0) @@ -638,6 +643,11 @@ #define MICROPY_ENABLE_GC (0) #endif +// Whether the garbage-collected heap can be split over multiple memory areas. +#ifndef MICROPY_GC_SPLIT_HEAP +#define MICROPY_GC_SPLIT_HEAP (0) +#endif + // Hook to run code during time consuming garbage collector operations #ifndef MICROPY_GC_HOOK_LOOP #define MICROPY_GC_HOOK_LOOP @@ -710,7 +720,7 @@ // This adds Alt+F, Alt+B, Alt+D and Alt+Backspace for forward-word, backward-word, forward-kill-word // and backward-kill-word, respectively. #ifndef MICROPY_REPL_EMACS_WORDS_MOVE -#define MICROPY_REPL_EMACS_WORDS_MOVE (0) +#define MICROPY_REPL_EMACS_WORDS_MOVE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) #endif // Whether to include extra convenience keys for word movement/kill in readline REPL. @@ -718,7 +728,7 @@ // respectively. Ctrl+Delete is not implemented because it's a very different escape sequence. // Depends on MICROPY_REPL_EMACS_WORDS_MOVE. #ifndef MICROPY_REPL_EMACS_EXTRA_WORDS_MOVE -#define MICROPY_REPL_EMACS_EXTRA_WORDS_MOVE (0) +#define MICROPY_REPL_EMACS_EXTRA_WORDS_MOVE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) #endif // Whether to implement auto-indent in REPL @@ -731,6 +741,11 @@ #define MICROPY_REPL_EVENT_DRIVEN (0) #endif +// The number of items to keep in the readline history. +#ifndef MICROPY_READLINE_HISTORY_SIZE +#define MICROPY_READLINE_HISTORY_SIZE (8) +#endif + // Whether to include lexer helper function for unix #ifndef MICROPY_HELPER_LEXER_UNIX #define MICROPY_HELPER_LEXER_UNIX (0) @@ -784,18 +799,6 @@ typedef long long mp_longint_impl_t; #define MICROPY_WARNINGS (0) #endif -// CIRCUITPY -// Whether to support chained exceptions -#ifndef MICROPY_CPYTHON_EXCEPTION_CHAIN -#define MICROPY_CPYTHON_EXCEPTION_CHAIN (0) -#endif - -// CIRCUITPY -// Whether the statically allocated GeneratorExit exception may be const -#ifndef MICROPY_CONST_GENERATOREXIT_OBJ -#define MICROPY_CONST_GENERATOREXIT_OBJ (!MICROPY_CPYTHON_EXCEPTION_CHAIN) -#endif - // Whether to support warning categories #ifndef MICROPY_WARNINGS_CATEGORY #define MICROPY_WARNINGS_CATEGORY (0) @@ -836,7 +839,7 @@ typedef double mp_float_t; // Whether to provide a high-quality hash for float and complex numbers. // Otherwise the default is a very simple but correct hashing function. #ifndef MICROPY_FLOAT_HIGH_QUALITY_HASH -#define MICROPY_FLOAT_HIGH_QUALITY_HASH (0) +#define MICROPY_FLOAT_HIGH_QUALITY_HASH (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) #endif // Enable features which improve CPython compatibility @@ -933,6 +936,16 @@ typedef double mp_float_t; #define MICROPY_USE_INTERNAL_PRINTF (1) #endif +// The mp_print_t printer used for printf output when MICROPY_USE_INTERNAL_PRINTF is enabled +#ifndef MICROPY_INTERNAL_PRINTF_PRINTER +#define MICROPY_INTERNAL_PRINTF_PRINTER (&mp_plat_print) +#endif + +// Whether to support mp_sched_vm_abort to asynchronously abort to the top level. +#ifndef MICROPY_ENABLE_VM_ABORT +#define MICROPY_ENABLE_VM_ABORT (0) +#endif + // Support for internal scheduler #ifndef MICROPY_ENABLE_SCHEDULER #define MICROPY_ENABLE_SCHEDULER (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) @@ -954,15 +967,25 @@ typedef double mp_float_t; #endif // Support for VFS POSIX component, to mount a POSIX filesystem within VFS -#ifndef MICROPY_VFS +#ifndef MICROPY_VFS_POSIX #define MICROPY_VFS_POSIX (0) #endif // Support for VFS FAT component, to mount a FAT filesystem within VFS -#ifndef MICROPY_VFS +#ifndef MICROPY_VFS_FAT #define MICROPY_VFS_FAT (0) #endif +// Support for VFS LittleFS v1 component, to mount a LFSv1 filesystem within VFS +#ifndef MICROPY_VFS_LFS1 +#define MICROPY_VFS_LFS1 (0) +#endif + +// Support for VFS LittleFS v2 component, to mount a LFSv2 filesystem within VFS +#ifndef MICROPY_VFS_LFS2 +#define MICROPY_VFS_LFS2 (0) +#endif + /*****************************************************************************/ /* Fine control over Python builtins, classes, modules, etc */ @@ -1021,6 +1044,11 @@ typedef double mp_float_t; #define MICROPY_PY_STR_BYTES_CMP_WARN (0) #endif +// Add bytes.hex and bytes.fromhex +#ifndef MICROPY_PY_BUILTINS_BYTES_HEX +#define MICROPY_PY_BUILTINS_BYTES_HEX (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + // Whether str object is proper unicode #ifndef MICROPY_PY_BUILTINS_STR_UNICODE #define MICROPY_PY_BUILTINS_STR_UNICODE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) @@ -1073,7 +1101,7 @@ typedef double mp_float_t; // Whether to support memoryview.itemsize attribute #ifndef MICROPY_PY_BUILTINS_MEMORYVIEW_ITEMSIZE -#define MICROPY_PY_BUILTINS_MEMORYVIEW_ITEMSIZE (0) +#define MICROPY_PY_BUILTINS_MEMORYVIEW_ITEMSIZE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) #endif // Whether to support set object @@ -1107,11 +1135,6 @@ typedef double mp_float_t; #define MICROPY_PY_BUILTINS_PROPERTY (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) #endif -// Whether to optimize property flash storage size (requires linker script support) -#ifndef MICROPY_PY_BUILTINS_PROPERTY -#define MICROPY_PY_BUILTINS_PROPERTY (0) -#endif - // Whether to implement the start/stop/step attributes (readback) on // the "range" builtin type. Rarely used, and costs ~60 bytes (x86). #ifndef MICROPY_PY_BUILTINS_RANGE_ATTRS @@ -1123,12 +1146,12 @@ typedef double mp_float_t; // the same object will compare as not-equal. With it enabled the semantics // match CPython and ranges are equal if they yield the same sequence of items. #ifndef MICROPY_PY_BUILTINS_RANGE_BINOP -#define MICROPY_PY_BUILTINS_RANGE_BINOP (0) +#define MICROPY_PY_BUILTINS_RANGE_BINOP (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) #endif // Support for callling next() with second argument #ifndef MICROPY_PY_BUILTINS_NEXT2 -#define MICROPY_PY_BUILTINS_NEXT2 (0) +#define MICROPY_PY_BUILTINS_NEXT2 (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) #endif // Whether to support rounding of integers (incl bignum); eg round(123,-1)=120 @@ -1148,7 +1171,7 @@ typedef double mp_float_t; // Whether to support all inplace arithmetic operarion methods // (__imul__, etc.) #ifndef MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS -#define MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS (0) +#define MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) #endif // Whether to support reverse arithmetic operarion methods @@ -1243,7 +1266,7 @@ typedef double mp_float_t; // Whether to provide the "micropython.heap_locked" function #ifndef MICROPY_PY_MICROPYTHON_HEAP_LOCKED -#define MICROPY_PY_MICROPYTHON_HEAP_LOCKED (0) +#define MICROPY_PY_MICROPYTHON_HEAP_LOCKED (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) #endif // Whether to provide "array" module. Note that large chunk of the @@ -1259,12 +1282,6 @@ typedef double mp_float_t; #define MICROPY_PY_ARRAY_SLICE_ASSIGN (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) #endif -// Whether to support nonstandard typecodes "O", "P" and "S" -// in array and struct modules. -#ifndef MICROPY_NONSTANDARD_TYPECODES -#define MICROPY_NONSTANDARD_TYPECODES (1) -#endif - // Whether to support attrtuple type (MicroPython extension) // It provides space-efficient tuples with attribute access #ifndef MICROPY_PY_ATTRTUPLE @@ -1288,7 +1305,7 @@ typedef double mp_float_t; // Whether to provide the _asdict function for namedtuple #ifndef MICROPY_PY_COLLECTIONS_NAMEDTUPLE__ASDICT -#define MICROPY_PY_COLLECTIONS_NAMEDTUPLE__ASDICT (0) +#define MICROPY_PY_COLLECTIONS_NAMEDTUPLE__ASDICT (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) #endif // Whether to provide "math" module @@ -1341,6 +1358,11 @@ typedef double mp_float_t; #define MICROPY_PY_CMATH (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) #endif +// Whether to provide "micropython" module +#ifndef MICROPY_PY_MICROPYTHON +#define MICROPY_PY_MICROPYTHON (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + // Whether to provide "gc" module #ifndef MICROPY_PY_GC #define MICROPY_PY_GC (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) @@ -1361,11 +1383,6 @@ typedef double mp_float_t; #define MICROPY_PY_IO_IOBASE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) #endif -// Whether to provide "io.FileIO" class -#ifndef MICROPY_PY_IO_FILEIO -#define MICROPY_PY_IO_FILEIO (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) -#endif - // Whether to provide "io.BytesIO" class #ifndef MICROPY_PY_IO_BYTESIO #define MICROPY_PY_IO_BYTESIO (1) @@ -1373,7 +1390,7 @@ typedef double mp_float_t; // Whether to provide "io.BufferedWriter" class #ifndef MICROPY_PY_IO_BUFFEREDWRITER -#define MICROPY_PY_IO_BUFFEREDWRITER (0) +#define MICROPY_PY_IO_BUFFEREDWRITER (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) #endif // Whether to provide "struct" module @@ -1407,6 +1424,13 @@ typedef double mp_float_t; #define MICROPY_PY_SYS_EXC_INFO (0) #endif +// Whether to provide "sys.executable", which is the absolute path to the +// micropython binary +// Intended for use on the "OS" ports (e.g. Unix) +#ifndef MICROPY_PY_SYS_EXECUTABLE +#define MICROPY_PY_SYS_EXECUTABLE (0) +#endif + // Whether to provide "sys.exit" function #ifndef MICROPY_PY_SYS_EXIT #define MICROPY_PY_SYS_EXIT (1) @@ -1429,7 +1453,7 @@ typedef double mp_float_t; // Whether to provide "sys.getsizeof" function #ifndef MICROPY_PY_SYS_GETSIZEOF -#define MICROPY_PY_SYS_GETSIZEOF (0) +#define MICROPY_PY_SYS_GETSIZEOF (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) #endif // Whether to provide sys.{stdin,stdout,stderr} objects @@ -1555,15 +1579,15 @@ typedef double mp_float_t; #endif #ifndef MICROPY_PY_URE_DEBUG -#define MICROPY_PY_URE_DEBUG (0) +#define MICROPY_PY_URE_DEBUG (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) #endif #ifndef MICROPY_PY_URE_MATCH_GROUPS -#define MICROPY_PY_URE_MATCH_GROUPS (0) +#define MICROPY_PY_URE_MATCH_GROUPS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) #endif #ifndef MICROPY_PY_URE_MATCH_SPAN_START_END -#define MICROPY_PY_URE_MATCH_SPAN_START_END (0) +#define MICROPY_PY_URE_MATCH_SPAN_START_END (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) #endif #ifndef MICROPY_PY_URE_SUB @@ -1663,6 +1687,11 @@ typedef double mp_float_t; #define MICROPY_PY_MACHINE_SOFTSPI (0) #endif +// Whether to provide the "machine.Timer" class +#ifndef MICROPY_PY_MACHINE_TIMER +#define MICROPY_PY_MACHINE_TIMER (0) +#endif + // The default backlog value for socket.listen(backlog) #ifndef MICROPY_PY_USOCKET_LISTEN_BACKLOG_DEFAULT #define MICROPY_PY_USOCKET_LISTEN_BACKLOG_DEFAULT (2) @@ -1761,6 +1790,10 @@ typedef double mp_float_t; #define MICROPY_WRAP_MP_SCHED_SCHEDULE(f) f #endif +#ifndef MICROPY_WRAP_MP_SCHED_VM_ABORT +#define MICROPY_WRAP_MP_SCHED_VM_ABORT(f) f +#endif + /*****************************************************************************/ /* Miscellaneous settings */ diff --git a/py/mpprint.h b/py/mpprint.h index fbfa3d9914..562afdad44 100644 --- a/py/mpprint.h +++ b/py/mpprint.h @@ -56,7 +56,7 @@ typedef struct _mp_print_ext_t { mp_print_t base; const char *item_separator; const char *key_separator; -}mp_print_ext_t; +} mp_print_ext_t; #define MP_PRINT_GET_EXT(print) ((mp_print_ext_t *)print) diff --git a/py/mpstate.h b/py/mpstate.h index 6871e2b40a..1519efb71d 100644 --- a/py/mpstate.h +++ b/py/mpstate.h @@ -77,12 +77,11 @@ typedef struct _mp_sched_item_t { mp_obj_t arg; } mp_sched_item_t; -// This structure hold information about the memory allocation system. -typedef struct _mp_state_mem_t { - #if MICROPY_MEM_STATS - size_t total_bytes_allocated; - size_t current_bytes_allocated; - size_t peak_bytes_allocated; +// This structure holds information about a single contiguous area of +// memory reserved for the memory manager. +typedef struct _mp_state_mem_area_t { + #if MICROPY_GC_SPLIT_HEAP + struct _mp_state_mem_area_t *next; #endif byte *gc_alloc_table_start; @@ -93,8 +92,25 @@ typedef struct _mp_state_mem_t { byte *gc_pool_start; byte *gc_pool_end; + size_t gc_last_free_atb_index; +} mp_state_mem_area_t; + +// This structure hold information about the memory allocation system. +typedef struct _mp_state_mem_t { + #if MICROPY_MEM_STATS + size_t total_bytes_allocated; + size_t current_bytes_allocated; + size_t peak_bytes_allocated; + #endif + + mp_state_mem_area_t area; + int gc_stack_overflow; - MICROPY_GC_STACK_ENTRY_TYPE gc_stack[MICROPY_ALLOC_GC_STACK_SIZE]; + MICROPY_GC_STACK_ENTRY_TYPE gc_block_stack[MICROPY_ALLOC_GC_STACK_SIZE]; + #if MICROPY_GC_SPLIT_HEAP + // Array that tracks the area for each block on gc_block_stack. + mp_state_mem_area_t *gc_area_stack[MICROPY_ALLOC_GC_STACK_SIZE]; + #endif // This variable controls auto garbage collection. If set to 0 then the // GC won't automatically run when gc_alloc can't find enough blocks. But @@ -106,7 +122,9 @@ typedef struct _mp_state_mem_t { size_t gc_alloc_threshold; #endif - size_t gc_last_free_atb_index; + #if MICROPY_GC_SPLIT_HEAP + mp_state_mem_area_t *gc_last_free_area; + #endif #if MICROPY_PY_GC_COLLECT_RETVAL size_t gc_collected; @@ -165,69 +183,19 @@ typedef struct _mp_state_vm_t { // dictionary with loaded modules (may be exposed as sys.modules) mp_obj_dict_t mp_loaded_modules_dict; - #if MICROPY_ENABLE_SCHEDULER - mp_sched_item_t sched_queue[MICROPY_SCHEDULER_DEPTH]; - #endif - - // current exception being handled, for sys.exc_info() - #if MICROPY_PY_SYS_EXC_INFO - mp_obj_base_t *cur_exception; - #endif - - #if MICROPY_PY_SYS_ATEXIT - // exposed through sys.atexit function - mp_obj_t sys_exitfunc; - #endif - // dictionary for the __main__ module mp_obj_dict_t dict_main; - #if MICROPY_PY_SYS - // If MICROPY_PY_SYS_PATH_ARGV_DEFAULTS is not enabled then these two lists - // must be initialised after the call to mp_init. - mp_obj_list_t mp_sys_path_obj; - mp_obj_list_t mp_sys_argv_obj; - - #if MICROPY_PY_SYS_ATTR_DELEGATION - // Contains mutable sys attributes. - mp_obj_t sys_mutable[MP_SYS_MUTABLE_NUM]; - #endif - #endif - // dictionary for overridden builtins #if MICROPY_CAN_OVERRIDE_BUILTINS mp_obj_dict_t *mp_module_builtins_override_dict; #endif - #if MICROPY_PERSISTENT_CODE_TRACK_RELOC_CODE - // An mp_obj_list_t that tracks relocated native code to prevent the GC from reclaiming them. - mp_obj_t track_reloc_code_list; - #endif - - // include any root pointers defined by a port - MICROPY_PORT_ROOT_POINTERS - - // root pointers for extmod - - #if MICROPY_REPL_EVENT_DRIVEN - vstr_t *repl_line; - #endif - - #if MICROPY_PY_OS_DUPTERM - mp_obj_t dupterm_objs[MICROPY_PY_OS_DUPTERM]; - #endif - - #if MICROPY_PY_LWIP_SLIP - mp_obj_t lwip_slip_stream; - #endif - - #if MICROPY_VFS - struct _mp_vfs_mount_t *vfs_cur; - struct _mp_vfs_mount_t *vfs_mount_table; - #endif - - #if MICROPY_PY_BLUETOOTH - mp_obj_t bluetooth; + // Include any root pointers registered with MP_REGISTER_ROOT_POINTER(). + #ifndef NO_QSTR + // Only include root pointer definitions when not doing qstr extraction, because + // the qstr extraction stage also generates the root pointers header file. + #include "genhdr/root_pointers.h" #endif // @@ -273,6 +241,11 @@ typedef struct _mp_state_vm_t { uint8_t sched_idx; #endif + #if MICROPY_ENABLE_VM_ABORT + bool vm_abort; + nlr_buf_t *nlr_abort; + #endif + #if MICROPY_PY_THREAD_GIL // This is a global mutex used to make the VM/runtime thread-safe. mp_thread_mutex_t gil_mutex; @@ -349,8 +322,10 @@ extern mp_state_ctx_t mp_state_ctx; #if MICROPY_PY_THREAD extern mp_state_thread_t *mp_thread_get_state(void); #define MP_STATE_THREAD(x) (mp_thread_get_state()->x) +#define mp_thread_is_main_thread() (mp_thread_get_state() == &mp_state_ctx.thread) #else #define MP_STATE_THREAD(x) MP_STATE_MAIN_THREAD(x) +#define mp_thread_is_main_thread() (true) #endif #endif // MICROPY_INCLUDED_PY_MPSTATE_H diff --git a/py/nlr.c b/py/nlr.c index 32e2b42c30..6435df66fb 100644 --- a/py/nlr.c +++ b/py/nlr.c @@ -49,3 +49,10 @@ void nlr_pop(void) { nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); *top = (*top)->prev; } + +#if MICROPY_ENABLE_VM_ABORT +NORETURN void nlr_jump_abort(void) { + MP_STATE_THREAD(nlr_top) = MP_STATE_VM(nlr_abort); + nlr_jump(NULL); +} +#endif diff --git a/py/nlr.h b/py/nlr.h index b7b016b1e6..2b8ec4b64e 100644 --- a/py/nlr.h +++ b/py/nlr.h @@ -40,6 +40,7 @@ #define MICROPY_NLR_NUM_REGS_ARM_THUMB (10) #define MICROPY_NLR_NUM_REGS_ARM_THUMB_FP (10 + 6) #define MICROPY_NLR_NUM_REGS_AARCH64 (13) +#define MICROPY_NLR_NUM_REGS_MIPS (13) #define MICROPY_NLR_NUM_REGS_XTENSA (10) #define MICROPY_NLR_NUM_REGS_XTENSAWIN (17) @@ -88,6 +89,9 @@ #define MICROPY_NLR_POWERPC (1) // this could be less but using 128 for safety #define MICROPY_NLR_NUM_REGS (128) +#elif defined(__mips__) + #define MICROPY_NLR_MIPS (1) + #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" @@ -107,9 +111,16 @@ typedef struct _nlr_buf_t nlr_buf_t; struct _nlr_buf_t { - // the entries here must all be machine word size + // The entries in this struct must all be machine word size. + + // Pointer to the previous nlr_buf_t in the chain. + // Or NULL if it's the top-level one. nlr_buf_t *prev; - void *ret_val; // always a concrete object (an exception instance) + + // The exception that is being raised: + // - NULL means the jump is because of a VM abort (only if MICROPY_ENABLE_VM_ABORT enabled) + // - otherwise it's always a concrete object (an exception instance) + void *ret_val; #if MICROPY_NLR_SETJMP jmp_buf jmpbuf; @@ -155,6 +166,12 @@ unsigned int nlr_push_tail(nlr_buf_t *top); void nlr_pop(void); NORETURN void nlr_jump(void *val); +#if MICROPY_ENABLE_VM_ABORT +#define nlr_set_abort(buf) MP_STATE_VM(nlr_abort) = buf +#define nlr_get_abort() MP_STATE_VM(nlr_abort) +NORETURN void nlr_jump_abort(void); +#endif + // This must be implemented by a port. It's called by nlr_jump // if no nlr buf has been pushed. It must not return, but rather // should bail out with a fatal error. diff --git a/py/nlrmips.c b/py/nlrmips.c new file mode 100644 index 0000000000..bd5d73b6f7 --- /dev/null +++ b/py/nlrmips.c @@ -0,0 +1,86 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2017 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/mpstate.h" + +#if MICROPY_NLR_MIPS + +__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr); + +__asm( + ".globl nlr_push \n" + "nlr_push: \n" + ".ent nlr_push \n" + ".frame $29, 0, $31 \n" + ".set noreorder \n" + ".cpload $25 \n" + ".set reorder \n" + "sw $31, 8($4) \n" /* is the offset of regs in nlr_buf_t */ + "sw $30, 12($4) \n" + "sw $29, 16($4) \n" + "sw $28, 20($4) \n" + "sw $23, 24($4) \n" + "sw $22, 28($4) \n" + "sw $21, 32($4) \n" + "sw $20, 36($4) \n" + "sw $19, 40($4) \n" + "sw $18, 44($4) \n" + "sw $17, 48($4) \n" + "sw $16, 52($4) \n" +#ifdef __pic__ + "la $25, nlr_push_tail \n" +#endif + "j nlr_push_tail \n" + ".end nlr_push \n" + ); + +NORETURN void nlr_jump(void *val) { + MP_NLR_JUMP_HEAD(val, top) + __asm( + "move $4, %0 \n" + "lw $31, 8($4) \n" + "lw $30, 12($4) \n" + "lw $29, 16($4) \n" + "lw $28, 20($4) \n" + "lw $23, 24($4) \n" + "lw $22, 28($4) \n" + "lw $21, 32($4) \n" + "lw $20, 36($4) \n" + "lw $19, 40($4) \n" + "lw $18, 44($4) \n" + "lw $17, 48($4) \n" + "lw $16, 52($4) \n" + "lui $2,1 \n" // set return value 1 + "j $31 \n" + "nop \n" + : + : "r" (top) + : + ); + MP_UNREACHABLE +} + +#endif // MICROPY_NLR_MIPS diff --git a/py/nlrpowerpc.c b/py/nlrpowerpc.c index 43eb34583d..a1b44efef1 100644 --- a/py/nlrpowerpc.c +++ b/py/nlrpowerpc.c @@ -32,6 +32,8 @@ // Saving all ABI non-vol registers here +#ifdef __LP64__ + unsigned int nlr_push(nlr_buf_t *nlr) { __asm__ volatile ( @@ -118,4 +120,95 @@ NORETURN void nlr_jump(void *val) { MP_UNREACHABLE; } +#else +// Saving all ABI non-vol registers here + +unsigned int nlr_push(nlr_buf_t *nlr) { + + __asm__ volatile ( + "li 4, 0x4eed ; " // Store canary + "stw 4, 0x00(%0) ;" + "stw 0, 0x04(%0) ;" + "stw 1, 0x08(%0) ;" + "stw 2, 0x0c(%0) ;" + "stw 14, 0x10(%0) ;" + "stw 15, 0x14(%0) ;" + "stw 16, 0x18(%0) ;" + "stw 17, 0x1c(%0) ;" + "stw 18, 0x20(%0) ;" + "stw 19, 0x24(%0) ;" + "stw 20, 0x28(%0) ;" + "stw 21, 0x2c(%0) ;" + "stw 22, 0x30(%0) ;" + "stw 23, 0x34(%0) ;" + "stw 24, 0x38(%0) ;" + "stw 25, 0x3c(%0) ;" + "stw 26, 0x40(%0) ;" + "stw 27, 0x44(%0) ;" + "stw 28, 0x48(%0) ;" + "stw 29, 0x4c(%0) ;" + "stw 30, 0x50(%0) ;" + "stw 31, 0x54(%0) ;" + + "mfcr 4 ; " + "stw 4, 0x58(%0) ;" + "mflr 4 ;" + "stw 4, 0x5c(%0) ;" + "li 4, nlr_push_tail@l ;" + "oris 4, 4, nlr_push_tail@h ;" + "mtctr 4 ;" + "mr 3, %1 ; " + "bctr ;" + : + : "r" (&nlr->regs), "r" (nlr) + : + ); + + return 0; +} + +NORETURN void nlr_jump(void *val) { + MP_NLR_JUMP_HEAD(val, top) + + __asm__ volatile ( + "l 3, 0x0(%0) ;" + "cmpdi 3, 0x4eed ; " // Check canary + "bne . ; " + "l 0, 0x04(%0) ;" + "l 1, 0x08(%0) ;" + "l 2, 0x0c(%0) ;" + "l 14, 0x10(%0) ;" + "l 15, 0x14(%0) ;" + "l 16, 0x18(%0) ;" + "l 17, 0x1c(%0) ;" + "l 18, 0x20(%0) ;" + "l 19, 0x24(%0) ;" + "l 20, 0x28(%0) ;" + "l 21, 0x2c(%0) ;" + "l 22, 0x30(%0) ;" + "l 23, 0x34(%0) ;" + "l 24, 0x38(%0) ;" + "l 25, 0x3c(%0) ;" + "l 26, 0x40(%0) ;" + "l 27, 0x44(%0) ;" + "l 28, 0x48(%0) ;" + "l 29, 0x4c(%0) ;" + "l 30, 0x50(%0) ;" + "l 31, 0x54(%0) ;" + "l 3, 0x58(%0) ;" + "mtcr 3 ;" + "l 3, 0x5c(%0) ;" + "mtlr 3 ; " + "li 3, 1;" + "blr ;" + : + : "r" (&top->regs) + : + ); + + MP_UNREACHABLE; +} + +#endif // __LP64__ + #endif // MICROPY_NLR_POWERPC diff --git a/py/obj.c b/py/obj.c index 31d469770c..9c1cffc728 100644 --- a/py/obj.c +++ b/py/obj.c @@ -39,12 +39,10 @@ #include "py/stackctrl.h" #include "py/stream.h" // for mp_obj_print -#include "supervisor/linker.h" #include "supervisor/shared/stack.h" -#include "supervisor/shared/translate/translate.h" // Allocates an object and also sets type, for mp_obj_malloc{,_var} macros. -void *mp_obj_malloc_helper(size_t num_bytes, const mp_obj_type_t *type) { +MP_NOINLINE void *mp_obj_malloc_helper(size_t num_bytes, const mp_obj_type_t *type) { mp_obj_base_t *base = (mp_obj_base_t *)m_malloc(num_bytes); base->type = type; return base; @@ -108,12 +106,6 @@ const mp_obj_type_t *MICROPY_WRAP_MP_OBJ_GET_TYPE(mp_obj_get_type)(mp_const_obj_ #endif } -const mp_obj_full_type_t *mp_obj_get_full_type(mp_const_obj_t o_in) { - const mp_obj_type_t *type = mp_obj_get_type(o_in); - assert(type->flags & MP_TYPE_FLAG_EXTENDED); - return (mp_obj_full_type_t *)type; -} - const char *mp_obj_get_type_str(mp_const_obj_t o_in) { return qstr_str(mp_obj_get_type_qstr(o_in)); } @@ -138,8 +130,8 @@ void mp_obj_print_helper(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t } #endif const mp_obj_type_t *type = mp_obj_get_type(o_in); - if (type->print != NULL) { - type->print((mp_print_t *)print, o_in, kind); + if (MP_OBJ_TYPE_HAS_SLOT(type, print)) { + MP_OBJ_TYPE_GET_SLOT(type, print)((mp_print_t *)print, o_in, kind); } else { mp_printf(print, "<%q>", type->name); } @@ -250,9 +242,8 @@ bool PLACE_IN_ITCM(mp_obj_is_true)(mp_obj_t arg) { } } else { const mp_obj_type_t *type = mp_obj_get_type(arg); - mp_unary_op_fun_t unary_op = mp_type_get_unary_op_slot(type); - if (unary_op) { - mp_obj_t result = unary_op(MP_UNARY_OP_BOOL, arg); + if (MP_OBJ_TYPE_HAS_SLOT(type, unary_op)) { + mp_obj_t result = MP_OBJ_TYPE_GET_SLOT(type, unary_op)(MP_UNARY_OP_BOOL, arg); if (result != MP_OBJ_NULL) { return result == mp_const_true; } @@ -270,7 +261,7 @@ bool PLACE_IN_ITCM(mp_obj_is_true)(mp_obj_t arg) { } bool mp_obj_is_callable(mp_obj_t o_in) { - const mp_call_fun_t call = mp_type_get_call_slot(mp_obj_get_type(o_in)); + const mp_call_fun_t call = MP_OBJ_TYPE_GET_SLOT_OR_NULL(mp_obj_get_type(o_in), call); if (call != mp_obj_instance_call) { return call != NULL; } @@ -337,20 +328,19 @@ mp_obj_t mp_obj_equal_not_equal(mp_binary_op_t op, mp_obj_t o1, mp_obj_t o2) { const mp_obj_type_t *type = mp_obj_get_type(o1); // If a full equality test is not needed and the other object is a different // type then we don't need to bother trying the comparison. - mp_binary_op_fun_t binary_op = mp_type_get_binary_op_slot(type); - if (binary_op != NULL && + if (MP_OBJ_TYPE_HAS_SLOT(type, binary_op) && ((type->flags & MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE) || mp_obj_get_type(o2) == type)) { // CPython is asymmetric: it will try __eq__ if there's no __ne__ but not the // other way around. If the class doesn't need a full test we can skip __ne__. if (op == MP_BINARY_OP_NOT_EQUAL && (type->flags & MP_TYPE_FLAG_EQ_HAS_NEQ_TEST)) { - mp_obj_t r = binary_op(MP_BINARY_OP_NOT_EQUAL, o1, o2); + mp_obj_t r = MP_OBJ_TYPE_GET_SLOT(type, binary_op)(MP_BINARY_OP_NOT_EQUAL, o1, o2); if (r != MP_OBJ_NULL) { return r; } } // Try calling __eq__. - mp_obj_t r = binary_op(MP_BINARY_OP_EQUAL, o1, o2); + mp_obj_t r = MP_OBJ_TYPE_GET_SLOT(type, binary_op)(MP_BINARY_OP_EQUAL, o1, o2); if (r != MP_OBJ_NULL) { if (op == MP_BINARY_OP_EQUAL) { return r; @@ -386,7 +376,7 @@ mp_int_t mp_obj_get_int(mp_const_obj_t arg) { return 1; } else if (mp_obj_is_small_int(arg)) { return MP_OBJ_SMALL_INT_VALUE(arg); - } else if (mp_obj_is_type(arg, &mp_type_int)) { + } else if (mp_obj_is_exact_type(arg, &mp_type_int)) { return mp_obj_int_get_checked(arg); } else { mp_obj_t res = mp_unary_op(MP_UNARY_OP_INT, (mp_obj_t)arg); @@ -412,7 +402,7 @@ bool mp_obj_get_int_maybe(mp_const_obj_t arg, mp_int_t *value) { *value = 1; } else if (mp_obj_is_small_int(arg)) { *value = MP_OBJ_SMALL_INT_VALUE(arg); - } else if (mp_obj_is_type(arg, &mp_type_int)) { + } else if (mp_obj_is_exact_type(arg, &mp_type_int)) { *value = mp_obj_int_get_checked(arg); } else { return false; @@ -431,15 +421,19 @@ bool mp_obj_get_float_maybe(mp_obj_t arg, mp_float_t *value) { } else if (mp_obj_is_small_int(arg)) { val = (mp_float_t)MP_OBJ_SMALL_INT_VALUE(arg); #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE - } else if (mp_obj_is_type(arg, &mp_type_int)) { + } else if (mp_obj_is_exact_type(arg, &mp_type_int)) { val = mp_obj_int_as_float_impl(arg); #endif } else if (mp_obj_is_float(arg)) { val = mp_obj_float_get(arg); } else { - return false; + arg = mp_unary_op(MP_UNARY_OP_FLOAT_MAYBE, (mp_obj_t)arg); + if (arg != MP_OBJ_NULL && mp_obj_is_float(arg)) { + val = mp_obj_float_get(arg); + } else { + return false; + } } - *value = val; return true; } @@ -461,27 +455,17 @@ mp_float_t mp_obj_get_float(mp_obj_t arg) { #if MICROPY_PY_BUILTINS_COMPLEX bool mp_obj_get_complex_maybe(mp_obj_t arg, mp_float_t *real, mp_float_t *imag) { - if (arg == mp_const_false) { - *real = 0; - *imag = 0; - } else if (arg == mp_const_true) { - *real = 1; - *imag = 0; - } else if (mp_obj_is_small_int(arg)) { - *real = (mp_float_t)MP_OBJ_SMALL_INT_VALUE(arg); - *imag = 0; - #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE - } else if (mp_obj_is_type(arg, &mp_type_int)) { - *real = mp_obj_int_as_float_impl(arg); - *imag = 0; - #endif - } else if (mp_obj_is_float(arg)) { - *real = mp_obj_float_get(arg); + if (mp_obj_get_float_maybe(arg, real)) { *imag = 0; } else if (mp_obj_is_type(arg, &mp_type_complex)) { mp_obj_complex_get(arg, real, imag); } else { - return false; + arg = mp_unary_op(MP_UNARY_OP_COMPLEX_MAYBE, (mp_obj_t)arg); + if (arg != MP_OBJ_NULL && mp_obj_is_type(arg, &mp_type_complex)) { + mp_obj_complex_get(arg, real, imag); + } else { + return false; + } } return true; } @@ -606,9 +590,8 @@ mp_obj_t mp_obj_len_maybe(mp_obj_t o_in) { return MP_OBJ_NEW_SMALL_INT(l); } else { const mp_obj_type_t *type = mp_obj_get_type(o_in); - mp_unary_op_fun_t unary_op = mp_type_get_unary_op_slot(type); - if (unary_op != NULL) { - return unary_op(MP_UNARY_OP_LEN, o_in); + if (MP_OBJ_TYPE_HAS_SLOT(type, unary_op)) { + return MP_OBJ_TYPE_GET_SLOT(type, unary_op)(MP_UNARY_OP_LEN, o_in); } else { return MP_OBJ_NULL; } @@ -617,14 +600,14 @@ mp_obj_t mp_obj_len_maybe(mp_obj_t o_in) { 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); - mp_subscr_fun_t subscr = mp_type_get_subscr_slot(type); - if (subscr != NULL) { - mp_obj_t ret = subscr(base, index, value); + if (MP_OBJ_TYPE_HAS_SLOT(type, subscr)) { + mp_obj_t ret = MP_OBJ_TYPE_GET_SLOT(type, subscr)(base, index, value); // May have called port specific C code. Make sure it didn't mess up the heap. assert_heap_ok(); if (ret != MP_OBJ_NULL) { return ret; } + // TODO: call base classes here? } if (value == MP_OBJ_NULL) { #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE @@ -657,48 +640,17 @@ mp_obj_t mp_identity(mp_obj_t self) { } MP_DEFINE_CONST_FUN_OBJ_1(mp_identity_obj, mp_identity); -mp_obj_t mp_identity_getiter(mp_obj_t self, mp_obj_iter_buf_t *iter_buf) { - (void)iter_buf; - return self; -} - -typedef struct { - mp_obj_base_t base; - mp_fun_1_t iternext; - mp_obj_t obj; - mp_int_t cur; -} mp_obj_generic_it_t; - -STATIC mp_obj_t generic_it_iternext(mp_obj_t self_in) { - mp_obj_generic_it_t *self = MP_OBJ_TO_PTR(self_in); - const mp_obj_full_type_t *type = mp_obj_get_full_type(self->obj); - mp_obj_t current_length = type->MP_TYPE_UNARY_OP(MP_UNARY_OP_LEN, self->obj); - if (self->cur < MP_OBJ_SMALL_INT_VALUE(current_length)) { - mp_obj_t o_out = type->ext[0].subscr(self->obj, MP_OBJ_NEW_SMALL_INT(self->cur), MP_OBJ_SENTINEL); - self->cur += 1; - return o_out; - } else { - return MP_OBJ_STOP_ITERATION; - } -} - -mp_obj_t mp_obj_new_generic_iterator(mp_obj_t obj, mp_obj_iter_buf_t *iter_buf) { - assert(sizeof(mp_obj_generic_it_t) <= sizeof(mp_obj_iter_buf_t)); - mp_obj_generic_it_t *o = (mp_obj_generic_it_t *)iter_buf; - o->base.type = &mp_type_polymorph_iter; - o->iternext = generic_it_iternext; - o->obj = obj; - o->cur = 0; - return MP_OBJ_FROM_PTR(o); -} +// mp_obj_t mp_identity_getiter(mp_obj_t self, mp_obj_iter_buf_t *iter_buf) { +// (void)iter_buf; +// return self; +// } bool mp_get_buffer(mp_obj_t obj, mp_buffer_info_t *bufinfo, mp_uint_t flags) { const mp_obj_type_t *type = mp_obj_get_type(obj); - const mp_getbuffer_fun_t get_buffer = mp_type_get_getbuffer_slot(type); - if (get_buffer == NULL) { + if (!MP_OBJ_TYPE_HAS_SLOT(type, buffer)) { return false; } - int ret = get_buffer(obj, bufinfo, flags); + int ret = MP_OBJ_TYPE_GET_SLOT(type, buffer)(obj, bufinfo, flags); if (ret != 0) { return false; } @@ -719,82 +671,3 @@ mp_obj_t mp_generic_unary_op(mp_unary_op_t op, mp_obj_t o_in) { return MP_OBJ_NULL; // op not supported } } - -mp_call_fun_t mp_type_get_call_slot(const mp_obj_type_t *type) { - if (!(type->flags & MP_TYPE_FLAG_EXTENDED)) { - return NULL; - } - return type->ext[0].call; -} - -mp_unary_op_fun_t mp_type_get_unary_op_slot(const mp_obj_type_t *type) { - if (!(type->flags & MP_TYPE_FLAG_EXTENDED)) { - return NULL; - } - return type->ext[0].unary_op; -} - - -mp_binary_op_fun_t mp_type_get_binary_op_slot(const mp_obj_type_t *type) { - if (!(type->flags & MP_TYPE_FLAG_EXTENDED)) { - return NULL; - } - return type->ext[0].binary_op; -} - - -mp_attr_fun_t mp_type_get_attr_slot(const mp_obj_type_t *type) { - return type->attr; -} - - -mp_subscr_fun_t mp_type_get_subscr_slot(const mp_obj_type_t *type) { - if (!(type->flags & MP_TYPE_FLAG_EXTENDED)) { - return NULL; - } - return type->MP_TYPE_SUBSCR; -} - - -mp_getiter_fun_t mp_type_get_getiter_slot(const mp_obj_type_t *type) { - if (!(type->flags & MP_TYPE_FLAG_EXTENDED)) { - return NULL; - } - return type->MP_TYPE_GETITER; -} - - -mp_fun_1_t mp_type_get_iternext_slot(const mp_obj_type_t *type) { - if (!(type->flags & MP_TYPE_FLAG_EXTENDED)) { - return NULL; - } - return type->MP_TYPE_ITERNEXT; -} - - -mp_getbuffer_fun_t mp_type_get_getbuffer_slot(const mp_obj_type_t *type) { - if (!(type->flags & MP_TYPE_FLAG_EXTENDED)) { - return NULL; - } - return type->MP_TYPE_GET_BUFFER; -} - - -const void *mp_type_get_protocol_slot(const mp_obj_type_t *type) { - if (!(type->flags & MP_TYPE_FLAG_EXTENDED)) { - return NULL; - } - return type->MP_TYPE_PROTOCOL; -} - - -const void *mp_type_get_parent_slot(const mp_obj_type_t *type) { - return type->parent; -} - -size_t mp_type_size(const mp_obj_type_t *type) { - if (!(type->flags & MP_TYPE_FLAG_EXTENDED)) { - return sizeof(mp_obj_type_t); - } - return sizeof(mp_obj_full_type_t); -} diff --git a/py/obj.h b/py/obj.h index d1874a8cd2..538343c4b1 100644 --- a/py/obj.h +++ b/py/obj.h @@ -50,7 +50,6 @@ typedef const void *mp_const_obj_t; // This mp_obj_type_t struct is a concrete MicroPython object which holds info // about a type. See below for actual definition of the struct. typedef struct _mp_obj_type_t mp_obj_type_t; -typedef struct _mp_obj_full_type_t mp_obj_full_type_t; // Anything that wants to be a concrete MicroPython object must have mp_obj_base_t // as its first member (small ints, qstr objs and inline floats are not concrete). @@ -86,19 +85,19 @@ typedef struct _mp_obj_base_t mp_obj_base_t; #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_A -static MP_INLINE bool mp_obj_is_small_int(mp_const_obj_t o) { +static inline bool mp_obj_is_small_int(mp_const_obj_t o) { return (((mp_int_t)(o)) & 1) != 0; } #define MP_OBJ_SMALL_INT_VALUE(o) (((mp_int_t)(o)) >> 1) #define MP_OBJ_NEW_SMALL_INT(small_int) ((mp_obj_t)((((mp_uint_t)(small_int)) << 1) | 1)) -static MP_INLINE bool mp_obj_is_qstr(mp_const_obj_t o) { +static inline bool mp_obj_is_qstr(mp_const_obj_t o) { return (((mp_int_t)(o)) & 7) == 2; } #define MP_OBJ_QSTR_VALUE(o) (((mp_uint_t)(o)) >> 3) #define MP_OBJ_NEW_QSTR(qst) ((mp_obj_t)((((mp_uint_t)(qst)) << 3) | 2)) -static MP_INLINE bool mp_obj_is_immediate_obj(mp_const_obj_t o) { +static inline bool mp_obj_is_immediate_obj(mp_const_obj_t o) { return (((mp_int_t)(o)) & 7) == 6; } #define MP_OBJ_IMMEDIATE_OBJ_VALUE(o) (((mp_uint_t)(o)) >> 3) @@ -125,25 +124,25 @@ mp_float_t mp_obj_float_get(mp_obj_t self_in); mp_obj_t mp_obj_new_float(mp_float_t value); #endif -static MP_INLINE bool mp_obj_is_obj(mp_const_obj_t o) { +static inline bool mp_obj_is_obj(mp_const_obj_t o) { return (((mp_int_t)(o)) & 3) == 0; } #elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_B -static MP_INLINE bool mp_obj_is_small_int(mp_const_obj_t o) { +static inline bool mp_obj_is_small_int(mp_const_obj_t o) { return (((mp_int_t)(o)) & 3) == 1; } #define MP_OBJ_SMALL_INT_VALUE(o) (((mp_int_t)(o)) >> 2) #define MP_OBJ_NEW_SMALL_INT(small_int) ((mp_obj_t)((((mp_uint_t)(small_int)) << 2) | 1)) -static MP_INLINE bool mp_obj_is_qstr(mp_const_obj_t o) { +static inline bool mp_obj_is_qstr(mp_const_obj_t o) { return (((mp_int_t)(o)) & 7) == 3; } #define MP_OBJ_QSTR_VALUE(o) (((mp_uint_t)(o)) >> 3) #define MP_OBJ_NEW_QSTR(qst) ((mp_obj_t)((((mp_uint_t)(qst)) << 3) | 3)) -static MP_INLINE bool mp_obj_is_immediate_obj(mp_const_obj_t o) { +static inline bool mp_obj_is_immediate_obj(mp_const_obj_t o) { return (((mp_int_t)(o)) & 7) == 7; } #define MP_OBJ_IMMEDIATE_OBJ_VALUE(o) (((mp_uint_t)(o)) >> 3) @@ -170,13 +169,17 @@ mp_float_t mp_obj_float_get(mp_obj_t self_in); mp_obj_t mp_obj_new_float(mp_float_t value); #endif -static MP_INLINE bool mp_obj_is_obj(mp_const_obj_t o) { +static inline bool mp_obj_is_obj(mp_const_obj_t o) { return (((mp_int_t)(o)) & 1) == 0; } #elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C -static MP_INLINE bool mp_obj_is_small_int(mp_const_obj_t o) { +#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_NONE +#error "MICROPY_OBJ_REPR_C requires float to be enabled." +#endif + +static inline bool mp_obj_is_small_int(mp_const_obj_t o) { return (((mp_int_t)(o)) & 1) != 0; } #define MP_OBJ_SMALL_INT_VALUE(o) (((mp_int_t)(o)) >> 1) @@ -191,17 +194,20 @@ static MP_INLINE bool mp_obj_is_small_int(mp_const_obj_t o) { #define mp_const_float_nan MP_ROM_PTR((mp_obj_t)(((0xffc00000 & ~3) | 2) + 0x80800000)) #endif -static MP_INLINE bool mp_obj_is_float(mp_const_obj_t o) { +static inline bool mp_obj_is_float(mp_const_obj_t o) { + // Ensure that 32-bit arch can only use single precision. + MP_STATIC_ASSERT(sizeof(mp_float_t) <= sizeof(mp_obj_t)); + return (((mp_uint_t)(o)) & 3) == 2 && (((mp_uint_t)(o)) & 0xff800007) != 0x00000006; } -static MP_INLINE mp_float_t mp_obj_float_get(mp_const_obj_t o) { +static inline mp_float_t mp_obj_float_get(mp_const_obj_t o) { union { mp_float_t f; mp_uint_t u; } num = {.u = ((mp_uint_t)o - 0x80800000) & ~3}; return num.f; } -static MP_INLINE mp_obj_t mp_obj_new_float(mp_float_t f) { +static inline mp_obj_t mp_obj_new_float(mp_float_t f) { union { mp_float_t f; mp_uint_t u; @@ -210,37 +216,37 @@ static MP_INLINE mp_obj_t mp_obj_new_float(mp_float_t f) { } #endif -static MP_INLINE bool mp_obj_is_qstr(mp_const_obj_t o) { +static inline bool mp_obj_is_qstr(mp_const_obj_t o) { return (((mp_uint_t)(o)) & 0xff80000f) == 0x00000006; } #define MP_OBJ_QSTR_VALUE(o) (((mp_uint_t)(o)) >> 4) #define MP_OBJ_NEW_QSTR(qst) ((mp_obj_t)((((mp_uint_t)(qst)) << 4) | 0x00000006)) -static MP_INLINE bool mp_obj_is_immediate_obj(mp_const_obj_t o) { +static inline bool mp_obj_is_immediate_obj(mp_const_obj_t o) { return (((mp_uint_t)(o)) & 0xff80000f) == 0x0000000e; } #define MP_OBJ_IMMEDIATE_OBJ_VALUE(o) (((mp_uint_t)(o)) >> 4) #define MP_OBJ_NEW_IMMEDIATE_OBJ(val) ((mp_obj_t)(((val) << 4) | 0xe)) -static MP_INLINE bool mp_obj_is_obj(mp_const_obj_t o) { +static inline bool mp_obj_is_obj(mp_const_obj_t o) { return (((mp_int_t)(o)) & 3) == 0; } #elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D -static MP_INLINE bool mp_obj_is_small_int(mp_const_obj_t o) { +static inline bool mp_obj_is_small_int(mp_const_obj_t o) { return (((uint64_t)(o)) & 0xffff000000000000) == 0x0001000000000000; } #define MP_OBJ_SMALL_INT_VALUE(o) (((mp_int_t)((o) << 16)) >> 17) #define MP_OBJ_NEW_SMALL_INT(small_int) (((((uint64_t)(small_int)) & 0x7fffffffffff) << 1) | 0x0001000000000001) -static MP_INLINE bool mp_obj_is_qstr(mp_const_obj_t o) { +static inline bool mp_obj_is_qstr(mp_const_obj_t o) { return (((uint64_t)(o)) & 0xffff000000000000) == 0x0002000000000000; } #define MP_OBJ_QSTR_VALUE(o) ((((uint32_t)(o)) >> 1) & 0xffffffff) #define MP_OBJ_NEW_QSTR(qst) ((mp_obj_t)(((uint64_t)(((uint32_t)(qst)) << 1)) | 0x0002000000000001)) -static MP_INLINE bool mp_obj_is_immediate_obj(mp_const_obj_t o) { +static inline bool mp_obj_is_immediate_obj(mp_const_obj_t o) { return (((uint64_t)(o)) & 0xffff000000000000) == 0x0003000000000000; } #define MP_OBJ_IMMEDIATE_OBJ_VALUE(o) ((((uint32_t)(o)) >> 46) & 3) @@ -260,17 +266,17 @@ static MP_INLINE bool mp_obj_is_immediate_obj(mp_const_obj_t o) { #define mp_const_float_nan {((mp_obj_t)((uint64_t)0xfff8000000000000 + 0x8004000000000000))} #endif -static MP_INLINE bool mp_obj_is_float(mp_const_obj_t o) { +static inline bool mp_obj_is_float(mp_const_obj_t o) { return ((uint64_t)(o) & 0xfffc000000000000) != 0; } -static MP_INLINE mp_float_t mp_obj_float_get(mp_const_obj_t o) { +static inline mp_float_t mp_obj_float_get(mp_const_obj_t o) { union { mp_float_t f; uint64_t r; } num = {.r = o - 0x8004000000000000}; return num.f; } -static MP_INLINE mp_obj_t mp_obj_new_float(mp_float_t f) { +static inline mp_obj_t mp_obj_new_float(mp_float_t f) { union { mp_float_t f; uint64_t r; @@ -279,16 +285,18 @@ static MP_INLINE mp_obj_t mp_obj_new_float(mp_float_t f) { } #endif -static MP_INLINE bool mp_obj_is_obj(mp_const_obj_t o) { +static inline bool mp_obj_is_obj(mp_const_obj_t o) { return (((uint64_t)(o)) & 0xffff000000000000) == 0x0000000000000000; } #define MP_OBJ_TO_PTR(o) ((void *)(uintptr_t)(o)) #define MP_OBJ_FROM_PTR(p) ((mp_obj_t)((uintptr_t)(p))) // rom object storage needs special handling to widen 32-bit pointer to 64-bits -typedef union _mp_rom_obj_t { uint64_t u64; - struct { const void *lo, *hi; - } u32; +typedef union _mp_rom_obj_t { + uint64_t u64; + struct { + const void *lo, *hi; + } u32; } mp_rom_obj_t; #define MP_ROM_INT(i) {MP_OBJ_NEW_SMALL_INT(i)} #define MP_ROM_QSTR(q) {MP_OBJ_NEW_QSTR(q)} @@ -307,12 +315,12 @@ typedef union _mp_rom_obj_t { uint64_t u64; // Cast mp_obj_t to object pointer #ifndef MP_OBJ_TO_PTR -#define MP_OBJ_TO_PTR(o) ((void *)o) +#define MP_OBJ_TO_PTR(o) ((void *)(o)) #endif // Cast object pointer to mp_obj_t #ifndef MP_OBJ_FROM_PTR -#define MP_OBJ_FROM_PTR(p) ((mp_obj_t)p) +#define MP_OBJ_FROM_PTR(p) ((mp_obj_t)(p)) #endif // Macros to create objects that are stored in ROM. @@ -389,6 +397,7 @@ typedef struct _mp_rom_obj_t { mp_const_obj_t o; } mp_rom_obj_t; MP_PROPERTY_GETTER(obj_name, (mp_obj_t)&fun_name##_obj); // 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) \ @@ -401,19 +410,21 @@ typedef struct _mp_rom_obj_t { mp_const_obj_t o; } mp_rom_obj_t; .table = (mp_map_elem_t *)(mp_rom_map_elem_t *)table_name, \ } -#define MP_DEFINE_CONST_DICT(dict_name, table_name) \ +#define MP_DEFINE_CONST_DICT_WITH_SIZE(dict_name, table_name, n) \ const mp_obj_dict_t dict_name = { \ .base = {&mp_type_dict}, \ .map = { \ .all_keys_are_qstrs = 1, \ .is_fixed = 1, \ .is_ordered = 1, \ - .used = MP_ARRAY_SIZE(table_name), \ - .alloc = MP_ARRAY_SIZE(table_name), \ + .used = n, \ + .alloc = n, \ .table = (mp_map_elem_t *)(mp_rom_map_elem_t *)table_name, \ }, \ } +#define MP_DEFINE_CONST_DICT(dict_name, table_name) MP_DEFINE_CONST_DICT_WITH_SIZE(dict_name, table_name, MP_ARRAY_SIZE(table_name)) + #define MP_DEFINE_MUTABLE_MAP(map_name, table_name) \ mp_map_t map_name = { \ .all_keys_are_qstrs = 1, \ @@ -438,6 +449,7 @@ typedef struct _mp_rom_obj_t { mp_const_obj_t o; } mp_rom_obj_t; } // These macros are used to declare and define constant staticmethond and classmethod objects +// These macros are used to declare and define constant staticmethod and classmethod objects // You can put "static" in front of the definitions to make them local #define MP_DECLARE_CONST_STATICMETHOD_OBJ(obj_name) extern const mp_rom_obj_static_class_method_t obj_name @@ -446,13 +458,18 @@ typedef struct _mp_rom_obj_t { mp_const_obj_t o; } mp_rom_obj_t; #define MP_DEFINE_CONST_STATICMETHOD_OBJ(obj_name, fun_name) const mp_rom_obj_static_class_method_t obj_name = {{&mp_type_staticmethod}, fun_name} #define MP_DEFINE_CONST_CLASSMETHOD_OBJ(obj_name, fun_name) const mp_rom_obj_static_class_method_t obj_name = {{&mp_type_classmethod}, fun_name} +#ifndef NO_QSTR + // Declare a module as a builtin, processed by makemoduledefs.py // param module_name: MP_QSTR_ // param obj_module: mp_obj_module_t instance - -#ifndef NO_QSTR #define MP_REGISTER_MODULE(module_name, obj_module) -#endif + +// Declare a root pointer (to avoid garbage collection of a global static variable). +// param variable_declaration: a valid C variable declaration +#define MP_REGISTER_ROOT_POINTER(variable_declaration) + +#endif // NO_QSTR // Underlying map/hash table implementation (not dict object or map function) @@ -483,7 +500,7 @@ typedef enum _mp_map_lookup_kind_t { MP_MAP_LOOKUP_ADD_IF_NOT_FOUND_OR_REMOVE_IF_FOUND = 3, // only valid for mp_set_lookup } mp_map_lookup_kind_t; -static MP_INLINE bool mp_map_slot_is_filled(const mp_map_t *map, size_t pos) { +static inline bool mp_map_slot_is_filled(const mp_map_t *map, size_t pos) { assert(pos < map->alloc); return (map)->table[pos].key != MP_OBJ_NULL && (map)->table[pos].key != MP_OBJ_SENTINEL; } @@ -505,7 +522,7 @@ typedef struct _mp_set_t { mp_obj_t *table; } mp_set_t; -static MP_INLINE bool mp_set_slot_is_filled(const mp_set_t *set, size_t pos) { +static inline bool mp_set_slot_is_filled(const mp_set_t *set, size_t pos) { return (set)->table[pos] != MP_OBJ_NULL && (set)->table[pos] != MP_OBJ_SENTINEL; } @@ -528,12 +545,22 @@ typedef mp_obj_t (*mp_fun_kw_t)(size_t n, const mp_obj_t *, mp_map_t *); // Flags for type behaviour (mp_obj_type_t.flags) // If MP_TYPE_FLAG_EQ_NOT_REFLEXIVE is clear then __eq__ is reflexive (A==A returns True). // If MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE is clear then the type can't be equal to an -// instance of any different class that also clears this flag. If this flag is set -// then the type may check for equality against a different type. +// instance of any different class that also clears this flag. If this flag is set +// then the type may check for equality against a different type. // If MP_TYPE_FLAG_EQ_HAS_NEQ_TEST is clear then the type only implements the __eq__ -// operator and not the __ne__ operator. If it's set then __ne__ may be implemented. +// operator and not the __ne__ operator. If it's set then __ne__ may be implemented. // If MP_TYPE_FLAG_BINDS_SELF is set then the type as a method binds self as the first arg. // If MP_TYPE_FLAG_BUILTIN_FUN is set then the type is a built-in function type. +// MP_TYPE_FLAG_ITER_IS_GETITER is a no-op flag that means the default behaviour for the +// iter slot and it's the getiter function. +// If MP_TYPE_FLAG_ITER_IS_ITERNEXT is set then the "iter" slot is the iternext +// function and getiter will be automatically implemented as "return self". +// If MP_TYPE_FLAG_ITER_IS_CUSTOM is set then the "iter" slot is a pointer to a +// mp_getiter_iternext_custom_t struct instance (with both .getiter and .iternext set). +// If MP_TYPE_FLAG_ITER_IS_STREAM is set then the type implicitly gets a "return self" +// getiter, and mp_stream_unbuffered_iter for iternext. +// If MP_TYPE_FLAG_INSTANCE_TYPE is set then this is an instance type (i.e. defined in Python). +#define MP_TYPE_FLAG_NONE (0x0000) #define MP_TYPE_FLAG_IS_SUBCLASSED (0x0001) #define MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS (0x0002) #define MP_TYPE_FLAG_EQ_NOT_REFLEXIVE (0x0004) @@ -541,7 +568,11 @@ typedef mp_obj_t (*mp_fun_kw_t)(size_t n, const mp_obj_t *, mp_map_t *); #define MP_TYPE_FLAG_EQ_HAS_NEQ_TEST (0x0010) #define MP_TYPE_FLAG_BINDS_SELF (0x0020) #define MP_TYPE_FLAG_BUILTIN_FUN (0x0040) -#define MP_TYPE_FLAG_EXTENDED (0x0080) // contains the 'ext' fields +#define MP_TYPE_FLAG_ITER_IS_GETITER (0x0000) +#define MP_TYPE_FLAG_ITER_IS_ITERNEXT (0x0080) +#define MP_TYPE_FLAG_ITER_IS_CUSTOM (0x0100) +#define MP_TYPE_FLAG_ITER_IS_STREAM (MP_TYPE_FLAG_ITER_IS_ITERNEXT | MP_TYPE_FLAG_ITER_IS_CUSTOM) +#define MP_TYPE_FLAG_INSTANCE_TYPE (0x0200) typedef enum { PRINT_STR = 0, @@ -561,8 +592,6 @@ typedef struct _mp_obj_iter_buf_t { // It's rounded up in case mp_obj_base_t is smaller than mp_obj_t (eg for OBJ_REPR_D). #define MP_OBJ_ITER_BUF_NSLOTS ((sizeof(mp_obj_iter_buf_t) + sizeof(mp_obj_t) - 1) / sizeof(mp_obj_t)) -struct _mp_buffer_info_t; - typedef void (*mp_print_fun_t)(const mp_print_t *print, mp_obj_t o, mp_print_kind_t kind); typedef mp_obj_t (*mp_make_new_fun_t)(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args); typedef mp_obj_t (*mp_call_fun_t)(mp_obj_t fun, size_t n_args, size_t n_kw, const mp_obj_t *args); @@ -571,7 +600,13 @@ typedef mp_obj_t (*mp_binary_op_fun_t)(mp_binary_op_t op, mp_obj_t, mp_obj_t); typedef void (*mp_attr_fun_t)(mp_obj_t self_in, qstr attr, mp_obj_t *dest); typedef mp_obj_t (*mp_subscr_fun_t)(mp_obj_t self_in, mp_obj_t index, mp_obj_t value); typedef mp_obj_t (*mp_getiter_fun_t)(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf); -typedef mp_int_t (*mp_getbuffer_fun_t)(mp_obj_t obj, struct _mp_buffer_info_t *bufinfo, mp_uint_t flags); +typedef mp_fun_1_t mp_iternext_fun_t; + +// For MP_TYPE_FLAG_ITER_IS_CUSTOM, the "getiter" slot points to an instance of this type. +typedef struct _mp_getiter_iternext_custom_t { + mp_getiter_fun_t getiter; + mp_iternext_fun_t iternext; +} mp_getiter_iternext_custom_t; // Buffer protocol typedef struct _mp_buffer_info_t { @@ -582,61 +617,42 @@ typedef struct _mp_buffer_info_t { #define MP_BUFFER_READ (1) #define MP_BUFFER_WRITE (2) #define MP_BUFFER_RW (MP_BUFFER_READ | MP_BUFFER_WRITE) -typedef struct _mp_buffer_p_t { - mp_getbuffer_fun_t get_buffer; -} mp_buffer_p_t; +typedef mp_int_t (*mp_buffer_fun_t)(mp_obj_t obj, mp_buffer_info_t *bufinfo, mp_uint_t flags); bool mp_get_buffer(mp_obj_t obj, mp_buffer_info_t *bufinfo, mp_uint_t flags); void mp_get_buffer_raise(mp_obj_t obj, mp_buffer_info_t *bufinfo, mp_uint_t flags); -struct _mp_obj_type_ext { - // Corresponds to __call__ special method, ie T(...). - mp_call_fun_t call; - - // Implements unary and binary operations. - // Can return MP_OBJ_NULL if the operation is not supported. - mp_unary_op_fun_t unary_op; - mp_binary_op_fun_t binary_op; - - // Implements load, store and delete subscripting: - // - value = MP_OBJ_SENTINEL means load - // - value = MP_OBJ_NULL means delete - // - all other values mean store the value - // Can return MP_OBJ_NULL if operation not supported. - mp_subscr_fun_t subscr; - - // Corresponds to __iter__ special method. - // Can use the given mp_obj_iter_buf_t to store iterator object, - // otherwise can return a pointer to an object on the heap. - mp_getiter_fun_t getiter; - - // Corresponds to __next__ special method. May return MP_OBJ_STOP_ITERATION - // as an optimisation instead of raising StopIteration() with no args. - mp_fun_1_t iternext; - - // Implements the buffer protocol if supported by this type. - mp_buffer_p_t buffer_p; - - // One of disjoint protocols (interfaces), like mp_stream_p_t, etc. - const void *protocol; -}; - +// This struct will be updated to become a variable sized struct. In order to +// use this as a member, or allocate dynamically, use the mp_obj_empty_type_t +// or mp_obj_full_type_t structs below (which must be kept in sync). struct _mp_obj_type_t { // A type is an object so must start with this entry, which points to mp_type_type. mp_obj_base_t base; // Flags associated with this type. uint16_t flags; + // The name of this type, a qstr. uint16_t name; - // A dict mapping qstrs to objects local methods/constants/etc. - struct _mp_obj_dict_t *locals_dict; + // Slots: For the rest of the fields, the slot index points to the + // relevant function in the variable-length "slots" field. Ideally these + // would be only 4 bits, but the extra overhead of accessing them adds + // more code, and we also need to be able to take the address of them for + // mp_obj_class_lookup. // Corresponds to __new__ and __init__ special methods, to make an instance of the type. - mp_make_new_fun_t make_new; + uint8_t slot_index_make_new; // Corresponds to __repr__ and __str__ special methods. - mp_print_fun_t print; + uint8_t slot_index_print; + + // Corresponds to __call__ special method, ie T(...). + uint8_t slot_index_call; + + // Implements unary and binary operations. + // Can return MP_OBJ_NULL if the operation is not supported. + uint8_t slot_index_unary_op; + uint8_t slot_index_binary_op; // Implements load, store and delete attribute. // @@ -650,62 +666,152 @@ struct _mp_obj_type_t { // dest[0,1] = {MP_OBJ_SENTINEL, object} means store // return: for fail, do nothing // for success set dest[0] = MP_OBJ_NULL - mp_attr_fun_t attr; + uint8_t slot_index_attr; + + // Implements load, store and delete subscripting: + // - value = MP_OBJ_SENTINEL means load + // - value = MP_OBJ_NULL means delete + // - all other values mean store the value + // Can return MP_OBJ_NULL if operation not supported. + uint8_t slot_index_subscr; + + // This slot's behaviour depends on the MP_TYPE_FLAG_ITER_IS_* flags above. + // - If MP_TYPE_FLAG_ITER_IS_GETITER flag is set, then this corresponds to the __iter__ + // special method (of type mp_getiter_fun_t). Can use the given mp_obj_iter_buf_t + // to store the iterator object, otherwise can return a pointer to an object on the heap. + // - If MP_TYPE_FLAG_ITER_IS_ITERNEXT is set, then this corresponds to __next__ special method. + // May return MP_OBJ_STOP_ITERATION as an optimisation instead of raising StopIteration() + // with no args. The type will implicitly implement getiter as "return self". + // - If MP_TYPE_FLAG_ITER_IS_CUSTOM is set, then this slot must point to an + // mp_getiter_iternext_custom_t instance with both the getiter and iternext fields set. + // - If MP_TYPE_FLAG_ITER_IS_STREAM is set, this this slot should be unset. + uint8_t slot_index_iter; + + // Implements the buffer protocol if supported by this type. + uint8_t slot_index_buffer; + + // One of disjoint protocols (interfaces), like mp_stream_p_t, etc. + uint8_t slot_index_protocol; // A pointer to the parents of this type: // - 0 parents: pointer is NULL (object is implicitly the single parent) // - 1 parent: a pointer to the type of that parent // - 2 or more parents: pointer to a tuple object containing the parent types - const void *parent; + uint8_t slot_index_parent; -#define MP_TYPE_EXTENDED_FIELDS(...) .ext = {{ __VA_ARGS__ }} - struct _mp_obj_type_ext ext[]; + // A dict mapping qstrs to objects local methods/constants/etc. + uint8_t slot_index_locals_dict; + + const void *slots[]; }; -// _mp_obj_full_type_t must match _mp_obj_type_t exactly, except that the `ext` field -// is a 1-element array rather than a flexible array member. -struct _mp_obj_full_type_t { +// Non-variable sized versions of mp_obj_type_t to be used as a member +// in other structs or for dynamic allocation. The fields are exactly +// as in mp_obj_type_t, but with a fixed size for the flexible array +// members. +typedef struct _mp_obj_empty_type_t { mp_obj_base_t base; uint16_t flags; uint16_t name; - // A dict mapping qstrs to objects local methods/constants/etc. - struct _mp_obj_dict_t *locals_dict; - mp_make_new_fun_t make_new; - mp_print_fun_t print; - mp_attr_fun_t attr; - const void *parent; - struct _mp_obj_type_ext ext[1]; -}; + uint8_t slot_index_make_new; + uint8_t slot_index_print; + uint8_t slot_index_call; + uint8_t slot_index_unary_op; + uint8_t slot_index_binary_op; + uint8_t slot_index_attr; + uint8_t slot_index_subscr; + uint8_t slot_index_iter; + uint8_t slot_index_buffer; + uint8_t slot_index_protocol; + uint8_t slot_index_parent; + uint8_t slot_index_locals_dict; -// If the type object in question is known to have the extended fields, you can -// refer to type->MP_TYPE_CALL. Otherwise, you have to use mp_type_get_call_slot(type) -// The same goes for other fields within the extended region. -#define MP_TYPE_CALL ext[0].call -#define MP_TYPE_UNARY_OP ext[0].unary_op -#define MP_TYPE_BINARY_OP ext[0].binary_op -#define MP_TYPE_SUBSCR ext[0].subscr -#define MP_TYPE_GETITER ext[0].getiter -#define MP_TYPE_ITERNEXT ext[0].iternext -#define MP_TYPE_GET_BUFFER ext[0].buffer_p.get_buffer -#define MP_TYPE_PROTOCOL ext[0].protocol -extern mp_call_fun_t mp_type_get_call_slot(const mp_obj_type_t *); -extern mp_unary_op_fun_t mp_type_get_unary_op_slot(const mp_obj_type_t *); -extern mp_binary_op_fun_t mp_type_get_binary_op_slot(const mp_obj_type_t *); -extern mp_subscr_fun_t mp_type_get_subscr_slot(const mp_obj_type_t *); -extern mp_getiter_fun_t mp_type_get_getiter_slot(const mp_obj_type_t *); -extern mp_fun_1_t mp_type_get_iternext_slot(const mp_obj_type_t *); -extern mp_getbuffer_fun_t mp_type_get_getbuffer_slot(const mp_obj_type_t *); -extern const void *mp_type_get_protocol_slot(const mp_obj_type_t *); + // No slots member. +} mp_obj_empty_type_t; -// These fields ended up not being placed in the extended area, but accessors -// were created for them anyway. -extern mp_attr_fun_t mp_type_get_attr_slot(const mp_obj_type_t *); -extern const void *mp_type_get_parent_slot(const mp_obj_type_t *); +typedef struct _mp_obj_full_type_t { + mp_obj_base_t base; + uint16_t flags; + uint16_t name; -// Return the size of a type object, which can be one of two lengths depending whether it has -// the extended fields or not. -extern size_t mp_type_size(const mp_obj_type_t *); + uint8_t slot_index_make_new; + uint8_t slot_index_print; + uint8_t slot_index_call; + uint8_t slot_index_unary_op; + uint8_t slot_index_binary_op; + uint8_t slot_index_attr; + uint8_t slot_index_subscr; + uint8_t slot_index_iter; + uint8_t slot_index_buffer; + uint8_t slot_index_protocol; + uint8_t slot_index_parent; + uint8_t slot_index_locals_dict; + + // Explicitly add 12 slots. + const void *slots[11]; +} mp_obj_full_type_t; + +#define _MP_OBJ_TYPE_SLOT_TYPE_make_new (mp_make_new_fun_t) +#define _MP_OBJ_TYPE_SLOT_TYPE_print (mp_print_fun_t) +#define _MP_OBJ_TYPE_SLOT_TYPE_call (mp_call_fun_t) +#define _MP_OBJ_TYPE_SLOT_TYPE_unary_op (mp_unary_op_fun_t) +#define _MP_OBJ_TYPE_SLOT_TYPE_binary_op (mp_binary_op_fun_t) +#define _MP_OBJ_TYPE_SLOT_TYPE_attr (mp_attr_fun_t) +#define _MP_OBJ_TYPE_SLOT_TYPE_subscr (mp_subscr_fun_t) +#define _MP_OBJ_TYPE_SLOT_TYPE_iter (const void *) +#define _MP_OBJ_TYPE_SLOT_TYPE_buffer (mp_buffer_fun_t) +#define _MP_OBJ_TYPE_SLOT_TYPE_protocol (const void *) +#define _MP_OBJ_TYPE_SLOT_TYPE_parent (const void *) +#define _MP_OBJ_TYPE_SLOT_TYPE_locals_dict (struct _mp_obj_dict_t *) + +// Implementation of MP_DEFINE_CONST_OBJ_TYPE for each number of arguments. +// Do not use these directly, instead use MP_DEFINE_CONST_OBJ_TYPE. +// Generated with: +// for i in range(13): +// print(f"#define MP_DEFINE_CONST_OBJ_TYPE_NARGS_{i}(_struct_type, _typename, _name, _flags{''.join(f', f{j+1}, v{j+1}' for j in range(i))}) const _struct_type _typename = {{ .base = {{ &mp_type_type }}, .name = _name, .flags = _flags{''.join(f', .slot_index_##f{j+1} = {j+1}' for j in range(i))}{', .slots = { ' + ''.join(f'v{j+1}, ' for j in range(i)) + '}' if i else '' } }}") +#define MP_DEFINE_CONST_OBJ_TYPE_NARGS_0(_struct_type, _typename, _name, _flags) const _struct_type _typename = { .base = { &mp_type_type }, .name = _name, .flags = _flags } +#define MP_DEFINE_CONST_OBJ_TYPE_NARGS_1(_struct_type, _typename, _name, _flags, f1, v1) const _struct_type _typename = { .base = { &mp_type_type }, .name = _name, .flags = _flags, .slot_index_##f1 = 1, .slots = { v1, } } +#define MP_DEFINE_CONST_OBJ_TYPE_NARGS_2(_struct_type, _typename, _name, _flags, f1, v1, f2, v2) const _struct_type _typename = { .base = { &mp_type_type }, .name = _name, .flags = _flags, .slot_index_##f1 = 1, .slot_index_##f2 = 2, .slots = { v1, v2, } } +#define MP_DEFINE_CONST_OBJ_TYPE_NARGS_3(_struct_type, _typename, _name, _flags, f1, v1, f2, v2, f3, v3) const _struct_type _typename = { .base = { &mp_type_type }, .name = _name, .flags = _flags, .slot_index_##f1 = 1, .slot_index_##f2 = 2, .slot_index_##f3 = 3, .slots = { v1, v2, v3, } } +#define MP_DEFINE_CONST_OBJ_TYPE_NARGS_4(_struct_type, _typename, _name, _flags, f1, v1, f2, v2, f3, v3, f4, v4) const _struct_type _typename = { .base = { &mp_type_type }, .name = _name, .flags = _flags, .slot_index_##f1 = 1, .slot_index_##f2 = 2, .slot_index_##f3 = 3, .slot_index_##f4 = 4, .slots = { v1, v2, v3, v4, } } +#define MP_DEFINE_CONST_OBJ_TYPE_NARGS_5(_struct_type, _typename, _name, _flags, f1, v1, f2, v2, f3, v3, f4, v4, f5, v5) const _struct_type _typename = { .base = { &mp_type_type }, .name = _name, .flags = _flags, .slot_index_##f1 = 1, .slot_index_##f2 = 2, .slot_index_##f3 = 3, .slot_index_##f4 = 4, .slot_index_##f5 = 5, .slots = { v1, v2, v3, v4, v5, } } +#define MP_DEFINE_CONST_OBJ_TYPE_NARGS_6(_struct_type, _typename, _name, _flags, f1, v1, f2, v2, f3, v3, f4, v4, f5, v5, f6, v6) const _struct_type _typename = { .base = { &mp_type_type }, .name = _name, .flags = _flags, .slot_index_##f1 = 1, .slot_index_##f2 = 2, .slot_index_##f3 = 3, .slot_index_##f4 = 4, .slot_index_##f5 = 5, .slot_index_##f6 = 6, .slots = { v1, v2, v3, v4, v5, v6, } } +#define MP_DEFINE_CONST_OBJ_TYPE_NARGS_7(_struct_type, _typename, _name, _flags, f1, v1, f2, v2, f3, v3, f4, v4, f5, v5, f6, v6, f7, v7) const _struct_type _typename = { .base = { &mp_type_type }, .name = _name, .flags = _flags, .slot_index_##f1 = 1, .slot_index_##f2 = 2, .slot_index_##f3 = 3, .slot_index_##f4 = 4, .slot_index_##f5 = 5, .slot_index_##f6 = 6, .slot_index_##f7 = 7, .slots = { v1, v2, v3, v4, v5, v6, v7, } } +#define MP_DEFINE_CONST_OBJ_TYPE_NARGS_8(_struct_type, _typename, _name, _flags, f1, v1, f2, v2, f3, v3, f4, v4, f5, v5, f6, v6, f7, v7, f8, v8) const _struct_type _typename = { .base = { &mp_type_type }, .name = _name, .flags = _flags, .slot_index_##f1 = 1, .slot_index_##f2 = 2, .slot_index_##f3 = 3, .slot_index_##f4 = 4, .slot_index_##f5 = 5, .slot_index_##f6 = 6, .slot_index_##f7 = 7, .slot_index_##f8 = 8, .slots = { v1, v2, v3, v4, v5, v6, v7, v8, } } +#define MP_DEFINE_CONST_OBJ_TYPE_NARGS_9(_struct_type, _typename, _name, _flags, f1, v1, f2, v2, f3, v3, f4, v4, f5, v5, f6, v6, f7, v7, f8, v8, f9, v9) const _struct_type _typename = { .base = { &mp_type_type }, .name = _name, .flags = _flags, .slot_index_##f1 = 1, .slot_index_##f2 = 2, .slot_index_##f3 = 3, .slot_index_##f4 = 4, .slot_index_##f5 = 5, .slot_index_##f6 = 6, .slot_index_##f7 = 7, .slot_index_##f8 = 8, .slot_index_##f9 = 9, .slots = { v1, v2, v3, v4, v5, v6, v7, v8, v9, } } +#define MP_DEFINE_CONST_OBJ_TYPE_NARGS_10(_struct_type, _typename, _name, _flags, f1, v1, f2, v2, f3, v3, f4, v4, f5, v5, f6, v6, f7, v7, f8, v8, f9, v9, f10, v10) const _struct_type _typename = { .base = { &mp_type_type }, .name = _name, .flags = _flags, .slot_index_##f1 = 1, .slot_index_##f2 = 2, .slot_index_##f3 = 3, .slot_index_##f4 = 4, .slot_index_##f5 = 5, .slot_index_##f6 = 6, .slot_index_##f7 = 7, .slot_index_##f8 = 8, .slot_index_##f9 = 9, .slot_index_##f10 = 10, .slots = { v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, } } +#define MP_DEFINE_CONST_OBJ_TYPE_NARGS_11(_struct_type, _typename, _name, _flags, f1, v1, f2, v2, f3, v3, f4, v4, f5, v5, f6, v6, f7, v7, f8, v8, f9, v9, f10, v10, f11, v11) const _struct_type _typename = { .base = { &mp_type_type }, .name = _name, .flags = _flags, .slot_index_##f1 = 1, .slot_index_##f2 = 2, .slot_index_##f3 = 3, .slot_index_##f4 = 4, .slot_index_##f5 = 5, .slot_index_##f6 = 6, .slot_index_##f7 = 7, .slot_index_##f8 = 8, .slot_index_##f9 = 9, .slot_index_##f10 = 10, .slot_index_##f11 = 11, .slots = { v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, } } +#define MP_DEFINE_CONST_OBJ_TYPE_NARGS_12(_struct_type, _typename, _name, _flags, f1, v1, f2, v2, f3, v3, f4, v4, f5, v5, f6, v6, f7, v7, f8, v8, f9, v9, f10, v10, f11, v11, f12, v12) const _struct_type _typename = { .base = { &mp_type_type }, .name = _name, .flags = _flags, .slot_index_##f1 = 1, .slot_index_##f2 = 2, .slot_index_##f3 = 3, .slot_index_##f4 = 4, .slot_index_##f5 = 5, .slot_index_##f6 = 6, .slot_index_##f7 = 7, .slot_index_##f8 = 8, .slot_index_##f9 = 9, .slot_index_##f10 = 10, .slot_index_##f11 = 11, .slot_index_##f12 = 12, .slots = { v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, } } + +// Because the mp_obj_type_t instances are in (zero-initialised) ROM, we take +// slot_index_foo=0 to mean that the slot is unset. This also simplifies checking +// if the slot is set. That means that we need to store index+1 in slot_index_foo +// though and then access it as slots[slot_index_foo - 1]. This is an implementation +// detail, the user of these macros doesn't need to be aware of it, and when using +// MP_OBJ_TYPE_OFFSETOF_SLOT you should use zero-based indexing. +#define MP_OBJ_TYPE_HAS_SLOT(t, f) ((t)->slot_index_##f) +#define MP_OBJ_TYPE_GET_SLOT(t, f) (_MP_OBJ_TYPE_SLOT_TYPE_##f(t)->slots[(t)->slot_index_##f - 1]) +#define MP_OBJ_TYPE_GET_SLOT_OR_NULL(t, f) (_MP_OBJ_TYPE_SLOT_TYPE_##f(MP_OBJ_TYPE_HAS_SLOT(t, f) ? MP_OBJ_TYPE_GET_SLOT(t, f) : NULL)) +#define MP_OBJ_TYPE_SET_SLOT(t, f, v, n) ((t)->slot_index_##f = (n) + 1, (t)->slots[(n)] = (void *)v) +#define MP_OBJ_TYPE_OFFSETOF_SLOT(f) (offsetof(mp_obj_type_t, slot_index_##f)) +#define MP_OBJ_TYPE_HAS_SLOT_BY_OFFSET(t, offset) (*(uint8_t *)((char *)(t) + (offset)) != 0) + +// Workaround for https://docs.microsoft.com/en-us/cpp/preprocessor/preprocessor-experimental-overview?view=msvc-160#macro-arguments-are-unpacked +#define MP_DEFINE_CONST_OBJ_TYPE_EXPAND(x) x + +// This macro evaluates to MP_DEFINE_CONST_OBJ_TYPE_NARGS_##N, where N is the value +// of the 29th argument (29 is 13*2 + 3). +#define MP_DEFINE_CONST_OBJ_TYPE_NARGS(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, N, ...) MP_DEFINE_CONST_OBJ_TYPE_NARGS_##N + +// This macros is used to define a object type in ROM. +// Invoke as MP_DEFINE_CONST_OBJ_TYPE(_typename, _name, _flags, _make_new [, slot, func]*) +// It uses the number of arguments to select which MP_DEFINE_CONST_OBJ_TYPE_* +// macro to use based on the number of arguments. It works by shifting the +// numeric values 12, 11, ... 0 by the number of arguments, such that the +// 29th argument ends up being the number to use. The _INV values are +// placeholders because the slot arguments come in pairs. +#define MP_DEFINE_CONST_OBJ_TYPE(...) MP_DEFINE_CONST_OBJ_TYPE_EXPAND(MP_DEFINE_CONST_OBJ_TYPE_NARGS(__VA_ARGS__, _INV, 12, _INV, 11, _INV, 10, _INV, 9, _INV, 8, _INV, 7, _INV, 6, _INV, 5, _INV, 4, _INV, 3, _INV, 2, _INV, 1, _INV, 0)(mp_obj_type_t, __VA_ARGS__)) // Constant types, globally accessible extern const mp_obj_type_t mp_type_type; @@ -719,7 +825,6 @@ extern const mp_obj_type_t mp_type_bytearray; extern const mp_obj_type_t mp_type_memoryview; extern const mp_obj_type_t mp_type_float; extern const mp_obj_type_t mp_type_complex; -extern const mp_obj_type_t mp_type_traceback; extern const mp_obj_type_t mp_type_tuple; extern const mp_obj_type_t mp_type_list; extern const mp_obj_type_t mp_type_map; // map (the python builtin, not the dict implementation detail) @@ -736,18 +841,8 @@ extern const mp_obj_type_t mp_type_zip; extern const mp_obj_type_t mp_type_array; extern const mp_obj_type_t mp_type_super; extern const mp_obj_type_t mp_type_gen_wrap; -#if MICROPY_EMIT_NATIVE extern const mp_obj_type_t mp_type_native_gen_wrap; -#endif extern const mp_obj_type_t mp_type_gen_instance; -// CIRCUITPY -#if MICROPY_PY_ASYNC_AWAIT -extern const mp_obj_type_t mp_type_coro_wrap; -#if MICROPY_EMIT_NATIVE -extern const mp_obj_type_t mp_type_native_coro_wrap; -#endif -extern const mp_obj_type_t mp_type_coro_instance; -#endif extern const mp_obj_type_t mp_type_fun_builtin_0; extern const mp_obj_type_t mp_type_fun_builtin_1; extern const mp_obj_type_t mp_type_fun_builtin_2; @@ -762,6 +857,9 @@ extern const mp_obj_type_t mp_type_stringio; extern const mp_obj_type_t mp_type_bytesio; extern const mp_obj_type_t mp_type_reversed; extern const mp_obj_type_t mp_type_polymorph_iter; +#if MICROPY_ENABLE_FINALISER +extern const mp_obj_type_t mp_type_polymorph_iter_with_finaliser; +#endif // Exceptions extern const mp_obj_type_t mp_type_BaseException; @@ -775,16 +873,12 @@ extern const mp_obj_type_t mp_type_ImportError; extern const mp_obj_type_t mp_type_IndentationError; extern const mp_obj_type_t mp_type_IndexError; extern const mp_obj_type_t mp_type_KeyboardInterrupt; -extern const mp_obj_type_t mp_type_ReloadException; extern const mp_obj_type_t mp_type_KeyError; extern const mp_obj_type_t mp_type_LookupError; extern const mp_obj_type_t mp_type_MemoryError; extern const mp_obj_type_t mp_type_NameError; extern const mp_obj_type_t mp_type_NotImplementedError; extern const mp_obj_type_t mp_type_OSError; -extern const mp_obj_type_t mp_type_TimeoutError; -extern const mp_obj_type_t mp_type_ConnectionError; -extern const mp_obj_type_t mp_type_BrokenPipeError; extern const mp_obj_type_t mp_type_OverflowError; extern const mp_obj_type_t mp_type_RuntimeError; extern const mp_obj_type_t mp_type_StopAsyncIteration; @@ -796,10 +890,6 @@ 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; -#if CIRCUITPY_ALARM -extern const mp_obj_type_t mp_type_DeepSleepRequest; -#endif - // Constant objects, globally accessible: None, False, True // These should always be accessed via the below macros. @@ -825,12 +915,9 @@ extern const struct _mp_obj_bool_t mp_const_true_obj; extern const struct _mp_obj_str_t mp_const_empty_bytes_obj; extern const struct _mp_obj_tuple_t mp_const_empty_tuple_obj; extern const struct _mp_obj_dict_t mp_const_empty_dict_obj; -extern const struct _mp_obj_traceback_t mp_const_empty_traceback_obj; extern const struct _mp_obj_singleton_t mp_const_ellipsis_obj; extern const struct _mp_obj_singleton_t mp_const_notimplemented_obj; -#if MICROPY_CONST_GENERATOREXIT_OBJ -extern const struct _mp_obj_exception_t mp_static_GeneratorExit_obj; -#endif +extern const struct _mp_obj_exception_t mp_const_GeneratorExit_obj; // Fixed empty map. Useful when calling keyword-receiving functions // without any keywords from C, etc. @@ -848,23 +935,35 @@ void *mp_obj_malloc_helper(size_t num_bytes, const mp_obj_type_t *type); // check for more specific object types. // Note: these are kept as macros because inline functions sometimes use much // more code space than the equivalent macros, depending on the compiler. -#define mp_obj_is_type(o, t) (mp_obj_is_obj(o) && (&(((mp_obj_base_t *)MP_OBJ_TO_PTR(o))->type->name) == &((t)->name))) // this does not work for checking int, str or fun; use below macros for that +// don't use mp_obj_is_exact_type directly; use mp_obj_is_type which provides additional safety checks. +// use the former only if you need to bypass these checks (because you've already checked everything else) +#define mp_obj_is_exact_type(o, t) (mp_obj_is_obj(o) && (((mp_obj_base_t *)MP_OBJ_TO_PTR(o))->type == (t))) + +// Type checks are split to a separate, constant result macro. This is so it doesn't hinder the compilers's +// 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), \ + 1) + +#define mp_obj_is_type(o, t) (mp_type_assert_not_bool_int_str_nonetype(t) && mp_obj_is_exact_type(o, t)) #if MICROPY_OBJ_IMMEDIATE_OBJS // bool's are immediates, not real objects, so test for the 2 possible values. #define mp_obj_is_bool(o) ((o) == mp_const_false || (o) == mp_const_true) #else -#define mp_obj_is_bool(o) mp_obj_is_type(o, &mp_type_bool) +#define mp_obj_is_bool(o) mp_obj_is_exact_type(o, &mp_type_bool) #endif -#define mp_obj_is_int(o) (mp_obj_is_small_int(o) || mp_obj_is_type(o, &mp_type_int)) -#define mp_obj_is_str(o) (mp_obj_is_qstr(o) || mp_obj_is_type(o, &mp_type_str)) -#define mp_obj_is_str_or_bytes(o) (mp_obj_is_qstr(o) || (mp_obj_is_obj(o) && mp_type_get_binary_op_slot(((mp_obj_base_t *)MP_OBJ_TO_PTR(o))->type) == mp_obj_str_binary_op)) -#define mp_obj_is_dict_or_ordereddict(o) (mp_obj_is_obj(o) && ((mp_obj_base_t *)MP_OBJ_TO_PTR(o))->type->make_new == mp_obj_dict_make_new) +#define mp_obj_is_int(o) (mp_obj_is_small_int(o) || mp_obj_is_exact_type(o, &mp_type_int)) +#define mp_obj_is_str(o) (mp_obj_is_qstr(o) || mp_obj_is_exact_type(o, &mp_type_str)) +#define mp_obj_is_str_or_bytes(o) (mp_obj_is_qstr(o) || (mp_obj_is_obj(o) && MP_OBJ_TYPE_GET_SLOT_OR_NULL(((mp_obj_base_t *)MP_OBJ_TO_PTR(o))->type, binary_op) == mp_obj_str_binary_op)) +bool mp_obj_is_dict_or_ordereddict(mp_obj_t o); #define mp_obj_is_fun(o) (mp_obj_is_obj(o) && (((mp_obj_base_t *)MP_OBJ_TO_PTR(o))->type->name == MP_QSTR_function)) -// type check is done on getiter method to allow tuple, namedtuple, attrtuple -#define mp_obj_is_tuple_compatible(o) (mp_type_get_getiter_slot(mp_obj_get_type(o)) == mp_obj_tuple_getiter) mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict); -static MP_INLINE mp_obj_t mp_obj_new_bool(mp_int_t x) { +static inline mp_obj_t mp_obj_new_bool(mp_int_t x) { return x ? mp_const_true : mp_const_false; } mp_obj_t mp_obj_new_cell(mp_obj_t obj); @@ -873,19 +972,21 @@ mp_obj_t mp_obj_new_int_from_uint(mp_uint_t value); mp_obj_t mp_obj_new_int_from_str_len(const char **str, size_t len, bool neg, unsigned int base); mp_obj_t mp_obj_new_int_from_ll(long long val); // this must return a multi-precision integer object (or raise an overflow exception) mp_obj_t mp_obj_new_int_from_ull(unsigned long long val); // this must return a multi-precision integer object (or raise an overflow exception) -mp_obj_t mp_obj_new_str(const char *data, size_t len); -mp_obj_t mp_obj_new_str_via_qstr(const char *data, size_t len); -mp_obj_t mp_obj_new_str_from_vstr(const mp_obj_type_t *type, vstr_t *vstr); +mp_obj_t mp_obj_new_str(const char *data, size_t len); // will check utf-8 (raises UnicodeError) +mp_obj_t mp_obj_new_str_via_qstr(const char *data, size_t len); // input data must be valid utf-8 +mp_obj_t mp_obj_new_str_from_vstr(vstr_t *vstr); // will check utf-8 (raises UnicodeError) +#if MICROPY_PY_BUILTINS_STR_UNICODE && MICROPY_PY_BUILTINS_STR_UNICODE_CHECK +mp_obj_t mp_obj_new_str_from_utf8_vstr(vstr_t *vstr); // input data must be valid utf-8 +#else +#define mp_obj_new_str_from_utf8_vstr mp_obj_new_str_from_vstr +#endif +mp_obj_t mp_obj_new_bytes_from_vstr(vstr_t *vstr); mp_obj_t mp_obj_new_bytes(const byte *data, size_t len); -mp_obj_t mp_obj_new_bytes_of_zeros(size_t len); -mp_obj_t mp_obj_new_bytearray(size_t n, void *items); -mp_obj_t mp_obj_new_bytearray_of_zeros(size_t n); +mp_obj_t mp_obj_new_bytearray(size_t n, const void *items); 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); -extern mp_float_t uint64_to_float(uint64_t ui64); -extern uint64_t float_to_uint64(float f); #endif mp_obj_t mp_obj_new_exception(const mp_obj_type_t *exc_type); mp_obj_t mp_obj_new_exception_args(const mp_obj_type_t *exc_type, size_t n_args, const mp_obj_t *args); @@ -893,19 +994,16 @@ mp_obj_t mp_obj_new_exception_args(const mp_obj_type_t *exc_type, size_t n_args, #define mp_obj_new_exception_msg(exc_type, msg) mp_obj_new_exception(exc_type) #define mp_obj_new_exception_msg_varg(exc_type, ...) mp_obj_new_exception(exc_type) #else -mp_obj_t mp_obj_new_exception_msg(const mp_obj_type_t *exc_type, const compressed_string_t *msg); -mp_obj_t mp_obj_new_exception_msg_varg(const mp_obj_type_t *exc_type, const compressed_string_t *fmt, ...); // counts args by number of % symbols in fmt, excluding %%; can only handle void* sizes (ie no float/double!) +mp_obj_t mp_obj_new_exception_msg(const mp_obj_type_t *exc_type, mp_rom_error_text_t msg); +mp_obj_t mp_obj_new_exception_msg_varg(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, ...); // counts args by number of % symbols in fmt, excluding %%; can only handle void* sizes (ie no float/double!) #endif #ifdef va_start -mp_obj_t mp_obj_new_exception_msg_vlist(const mp_obj_type_t *exc_type, const compressed_string_t *fmt, va_list ap); // counts args by number of % symbols in fmt, excluding %%; can only handle void* sizes (ie no float/double!) +mp_obj_t mp_obj_new_exception_msg_vlist(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, va_list arg); // same fmt restrictions as above #endif -// Only use this string version from native MPY files with static error strings. -mp_obj_t mp_obj_new_exception_msg_str(const mp_obj_type_t *exc_type, const char *msg); -mp_obj_t mp_obj_new_gen_wrap(mp_obj_t fun, bool is_coroutine); +mp_obj_t mp_obj_new_gen_wrap(mp_obj_t fun); mp_obj_t mp_obj_new_closure(mp_obj_t fun, size_t n_closed, const mp_obj_t *closed); mp_obj_t mp_obj_new_tuple(size_t n, const mp_obj_t *items); mp_obj_t mp_obj_new_list(size_t n, mp_obj_t *items); -mp_obj_t mp_obj_new_list_from_iter(mp_obj_t iterable); mp_obj_t mp_obj_new_dict(size_t n_args); mp_obj_t mp_obj_new_set(size_t n_args, mp_obj_t *items); mp_obj_t mp_obj_new_slice(mp_obj_t start, mp_obj_t stop, mp_obj_t step); @@ -915,16 +1013,13 @@ mp_obj_t mp_obj_new_module(qstr module_name); mp_obj_t mp_obj_new_memoryview(byte typecode, size_t nitems, void *items); const mp_obj_type_t *mp_obj_get_type(mp_const_obj_t o_in); -const mp_obj_full_type_t *mp_obj_get_full_type(mp_const_obj_t o_in); const char *mp_obj_get_type_str(mp_const_obj_t o_in); -#define mp_obj_get_type_qstr(o_in) (mp_obj_get_type((o_in))->name) bool mp_obj_is_subclass_fast(mp_const_obj_t object, mp_const_obj_t classinfo); // arguments should be type objects mp_obj_t mp_obj_cast_to_native_base(mp_obj_t self_in, mp_const_obj_t native_type); void mp_obj_print_helper(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind); void mp_obj_print(mp_obj_t o, mp_print_kind_t kind); void mp_obj_print_exception(const mp_print_t *print, mp_obj_t exc); -void mp_obj_print_exception_with_limit(const mp_print_t *print, mp_obj_t exc, mp_int_t limit); bool mp_obj_is_true(mp_obj_t arg); bool mp_obj_is_callable(mp_obj_t o_in); @@ -932,7 +1027,7 @@ mp_obj_t mp_obj_equal_not_equal(mp_binary_op_t op, mp_obj_t o1, mp_obj_t o2); bool mp_obj_equal(mp_obj_t o1, mp_obj_t o2); // returns true if o is bool, small int or long int -static MP_INLINE bool mp_obj_is_integer(mp_const_obj_t o) { +static inline bool mp_obj_is_integer(mp_const_obj_t o) { return mp_obj_is_int(o) || mp_obj_is_bool(o); } @@ -955,8 +1050,21 @@ mp_obj_t mp_obj_subscr(mp_obj_t base, mp_obj_t index, mp_obj_t val); mp_obj_t mp_generic_unary_op(mp_unary_op_t op, mp_obj_t o_in); // cell -mp_obj_t mp_obj_cell_get(mp_obj_t self_in); -void mp_obj_cell_set(mp_obj_t self_in, mp_obj_t obj); + +typedef struct _mp_obj_cell_t { + mp_obj_base_t base; + mp_obj_t obj; +} mp_obj_cell_t; + +static inline mp_obj_t mp_obj_cell_get(mp_obj_t self_in) { + mp_obj_cell_t *self = (mp_obj_cell_t *)MP_OBJ_TO_PTR(self_in); + return self->obj; +} + +static inline void mp_obj_cell_set(mp_obj_t self_in, mp_obj_t obj) { + mp_obj_cell_t *self = (mp_obj_cell_t *)MP_OBJ_TO_PTR(self_in); + self->obj = obj; +} // int // For long int, returns value truncated to mp_int_t @@ -967,7 +1075,7 @@ mp_int_t mp_obj_int_get_checked(mp_const_obj_t self_in); mp_uint_t mp_obj_int_get_uint_checked(mp_const_obj_t self_in); // exception -#define mp_obj_is_native_exception_instance(o) (mp_obj_get_type(o)->make_new == mp_obj_exception_make_new) +bool mp_obj_is_native_exception_instance(mp_obj_t self_in); bool mp_obj_is_exception_type(mp_obj_t self_in); bool mp_obj_is_exception_instance(mp_obj_t self_in); bool mp_obj_exception_match(mp_obj_t exc, mp_const_obj_t exc_type); @@ -979,8 +1087,8 @@ mp_obj_t mp_obj_exception_get_value(mp_obj_t self_in); mp_obj_t mp_obj_exception_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args); mp_obj_t mp_alloc_emergency_exception_buf(mp_obj_t size_in); void mp_init_emergency_exception_buf(void); -static MP_INLINE mp_obj_t mp_obj_new_exception_arg1(const mp_obj_type_t *exc_type, mp_obj_t arg) { - assert(exc_type->make_new == mp_obj_exception_make_new); +static inline mp_obj_t mp_obj_new_exception_arg1(const mp_obj_type_t *exc_type, mp_obj_t arg) { + assert(MP_OBJ_TYPE_GET_SLOT_OR_NULL(exc_type, make_new) == mp_obj_exception_make_new); return mp_obj_exception_make_new(exc_type, 1, 0, &arg); } @@ -996,42 +1104,42 @@ void mp_str_print_quoted(const mp_print_t *print, const byte *str_data, size_t s #if MICROPY_PY_BUILTINS_FLOAT // float #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT -static MP_INLINE float mp_obj_get_float_to_f(mp_obj_t o) { +static inline float mp_obj_get_float_to_f(mp_obj_t o) { return mp_obj_get_float(o); } -static MP_INLINE double mp_obj_get_float_to_d(mp_obj_t o) { +static inline double mp_obj_get_float_to_d(mp_obj_t o) { return (double)mp_obj_get_float(o); } -static MP_INLINE mp_obj_t mp_obj_new_float_from_f(float o) { +static inline mp_obj_t mp_obj_new_float_from_f(float o) { return mp_obj_new_float(o); } -static MP_INLINE mp_obj_t mp_obj_new_float_from_d(double o) { +static inline mp_obj_t mp_obj_new_float_from_d(double o) { return mp_obj_new_float((mp_float_t)o); } #elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE -static MP_INLINE float mp_obj_get_float_to_f(mp_obj_t o) { +static inline float mp_obj_get_float_to_f(mp_obj_t o) { return (float)mp_obj_get_float(o); } -static MP_INLINE double mp_obj_get_float_to_d(mp_obj_t o) { +static inline double mp_obj_get_float_to_d(mp_obj_t o) { return mp_obj_get_float(o); } -static MP_INLINE mp_obj_t mp_obj_new_float_from_f(float o) { +static inline mp_obj_t mp_obj_new_float_from_f(float o) { return mp_obj_new_float((mp_float_t)o); } -static MP_INLINE mp_obj_t mp_obj_new_float_from_d(double o) { +static inline mp_obj_t mp_obj_new_float_from_d(double o) { return mp_obj_new_float(o); } #endif #if MICROPY_FLOAT_HIGH_QUALITY_HASH mp_int_t mp_float_hash(mp_float_t val); #else -static MP_INLINE mp_int_t mp_float_hash(mp_float_t val) { +static inline mp_int_t mp_float_hash(mp_float_t val) { return (mp_int_t)val; } #endif @@ -1070,7 +1178,7 @@ mp_obj_t mp_obj_dict_get(mp_obj_t self_in, mp_obj_t index); mp_obj_t mp_obj_dict_store(mp_obj_t self_in, mp_obj_t key, mp_obj_t value); mp_obj_t mp_obj_dict_delete(mp_obj_t self_in, mp_obj_t key); mp_obj_t mp_obj_dict_copy(mp_obj_t self_in); -static MP_INLINE mp_map_t *mp_obj_dict_get_map(mp_obj_t dict) { +static inline mp_map_t *mp_obj_dict_get_map(mp_obj_t dict) { return &((mp_obj_dict_t *)MP_OBJ_TO_PTR(dict))->map; } @@ -1129,8 +1237,9 @@ typedef struct _mp_obj_module_t { mp_obj_base_t base; mp_obj_dict_t *globals; } mp_obj_module_t; -mp_obj_dict_t *mp_obj_module_get_globals(mp_obj_t self_in); -void mp_obj_module_set_globals(mp_obj_t self_in, mp_obj_dict_t *globals); +static inline mp_obj_dict_t *mp_obj_module_get_globals(mp_obj_t module) { + return ((mp_obj_module_t *)MP_OBJ_TO_PTR(module))->globals; +} // check if given module object is a package bool mp_obj_is_package(mp_obj_t module); @@ -1177,4 +1286,16 @@ mp_obj_t mp_seq_extract_slice(size_t len, const mp_obj_t *seq, mp_bound_slice_t memmove(((char *)dest) + (beg + slice_len) * (item_sz), ((char *)dest) + (end) * (item_sz), ((dest_len) + (len_adj) - ((beg) + (slice_len))) * (item_sz)); \ memmove(((char *)dest) + (beg) * (item_sz), slice, slice_len * (item_sz)); +// Provide translation for legacy API +#define MP_OBJ_IS_SMALL_INT mp_obj_is_small_int +#define MP_OBJ_IS_QSTR mp_obj_is_qstr +#define MP_OBJ_IS_OBJ mp_obj_is_obj +#define MP_OBJ_IS_INT mp_obj_is_int +#define MP_OBJ_IS_TYPE mp_obj_is_type +#define MP_OBJ_IS_STR mp_obj_is_str +#define MP_OBJ_IS_STR_OR_BYTES mp_obj_is_str_or_bytes +#define MP_OBJ_IS_FUN mp_obj_is_fun +#define MP_MAP_SLOT_IS_FILLED mp_map_slot_is_filled +#define MP_SET_SLOT_IS_FILLED mp_set_slot_is_filled + #endif // MICROPY_INCLUDED_PY_OBJ_H diff --git a/py/objarray.c b/py/objarray.c index b8ea9992bb..48374c4445 100644 --- a/py/objarray.c +++ b/py/objarray.c @@ -31,12 +31,9 @@ #include "py/runtime.h" #include "py/binary.h" -#include "py/objproperty.h" #include "py/objstr.h" #include "py/objarray.h" -#include "supervisor/shared/translate/translate.h" - #if MICROPY_PY_ARRAY || MICROPY_PY_BUILTINS_BYTEARRAY || MICROPY_PY_BUILTINS_MEMORYVIEW // About memoryview object: We want to reuse as much code as possible from @@ -56,6 +53,7 @@ #if MICROPY_PY_BUILTINS_MEMORYVIEW #define TYPECODE_MASK (0x7f) #define memview_offset free +#define memview_offset_max ((1LL << MP_OBJ_ARRAY_FREE_SIZE_BITS) - 1) #else // make (& TYPECODE_MASK) a null operation if memorview not enabled #define TYPECODE_MASK (~(size_t)0) @@ -190,12 +188,14 @@ STATIC mp_obj_t array_make_new(const mp_obj_type_t *type_in, size_t n_args, size #if MICROPY_PY_BUILTINS_BYTEARRAY STATIC mp_obj_t bytearray_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { (void)type_in; + // Can take 2nd/3rd arg if constructs from str mp_arg_check_num(n_args, n_kw, 0, 3, false); if (n_args == 0) { // no args: construct an empty bytearray return MP_OBJ_FROM_PTR(array_new(BYTEARRAY_TYPECODE, 0)); } else if (mp_obj_is_int(args[0])) { + // CIRCUITPY error checks this if (n_args > 1) { mp_raise_TypeError(MP_ERROR_TEXT("wrong number of arguments")); } @@ -206,6 +206,14 @@ STATIC mp_obj_t bytearray_make_new(const mp_obj_type_t *type_in, size_t n_args, return MP_OBJ_FROM_PTR(o); } else { // 1 arg: construct the bytearray from that + if (mp_obj_is_str(args[0]) && n_args == 1) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + // Match bytes_make_new. + mp_raise_TypeError(MP_ERROR_TEXT("wrong number of arguments")); + #else + mp_raise_TypeError(MP_ERROR_TEXT("string argument without an encoding")); + #endif + } return array_construct(BYTEARRAY_TYPECODE, args[0]); } } @@ -250,6 +258,7 @@ STATIC mp_obj_t memoryview_make_new(const mp_obj_type_t *type_in, size_t n_args, return MP_OBJ_FROM_PTR(self); } +// CIRCUITPY adds cast #if MICROPY_CPYTHON_COMPAT STATIC mp_obj_t memoryview_cast(const mp_obj_t self_in, const mp_obj_t typecode_in) { mp_obj_array_t *self = MP_OBJ_TO_PTR(self_in); @@ -273,13 +282,21 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(memoryview_cast_obj, memoryview_cast); #endif #if MICROPY_PY_BUILTINS_MEMORYVIEW_ITEMSIZE -STATIC mp_obj_t memoryview_itemsize_get(mp_obj_t self_in) { - mp_obj_array_t *self = MP_OBJ_TO_PTR(self_in); - return MP_OBJ_NEW_SMALL_INT(mp_binary_get_size('@', self->typecode & TYPECODE_MASK, NULL)); +STATIC void memoryview_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + if (dest[0] != MP_OBJ_NULL) { + return; + } + if (attr == MP_QSTR_itemsize) { + 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)); + } + #if MICROPY_PY_BUILTINS_BYTES_HEX || MICROPY_CPYTHON_COMPAT + else { + // Need to forward to locals dict. + dest[1] = MP_OBJ_SENTINEL; + } + #endif } -MP_DEFINE_CONST_FUN_OBJ_1(memoryview_itemsize_get_obj, memoryview_itemsize_get); - -MP_PROPERTY_GETTER(memoryview_itemsize_obj, (mp_obj_t)&memoryview_itemsize_get_obj); #endif #endif @@ -310,6 +327,7 @@ STATIC int typecode_for_comparison(int typecode, bool *is_unsigned) { STATIC mp_obj_t array_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { mp_obj_array_t *lhs = MP_OBJ_TO_PTR(lhs_in); switch (op) { + // CIRCUITPY does multiply case MP_BINARY_OP_MULTIPLY: case MP_BINARY_OP_INPLACE_MULTIPLY: { if (!mp_obj_is_int(rhs_in)) { @@ -449,7 +467,7 @@ STATIC mp_obj_t array_append(mp_obj_t self_in, mp_obj_t arg) { self->free--; return mp_const_none; // return None, as per CPython } -STATIC MP_DEFINE_CONST_FUN_OBJ_2(array_append_obj, array_append); +MP_DEFINE_CONST_FUN_OBJ_2(mp_obj_array_append_obj, array_append); STATIC mp_obj_t array_extend(mp_obj_t self_in, mp_obj_t arg_in) { // self is not a memoryview, so we don't need to use (& TYPECODE_MASK) @@ -459,6 +477,7 @@ STATIC mp_obj_t array_extend(mp_obj_t self_in, mp_obj_t arg_in) { // allow to extend by anything that has the buffer protocol (extension to CPython) mp_buffer_info_t arg_bufinfo; + // CIRCUITPY: allow appending an iterable if (mp_get_buffer(arg_in, &arg_bufinfo, MP_BUFFER_READ)) { size_t sz = mp_binary_get_size('@', self->typecode, NULL); @@ -529,6 +548,7 @@ STATIC mp_obj_t buffer_finder(size_t n_args, const mp_obj_t *args, int direction return MP_OBJ_NEW_SMALL_INT(p - (const byte *)haystack_bufinfo.buf); } +// CIRCUITPY provides find, rfind, index STATIC mp_obj_t buffer_find(size_t n_args, const mp_obj_t *args) { return buffer_finder(n_args, args, 1, false); } @@ -571,7 +591,7 @@ STATIC mp_obj_t array_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value size_t src_len; void *src_items; size_t item_sz = mp_binary_get_size('@', o->typecode & TYPECODE_MASK, NULL); - if (mp_obj_is_obj(value) && mp_type_get_subscr_slot(((mp_obj_base_t *)MP_OBJ_TO_PTR(value))->type) == array_subscr) { + if (mp_obj_is_obj(value) && MP_OBJ_TYPE_GET_SLOT_OR_NULL(((mp_obj_base_t *)MP_OBJ_TO_PTR(value))->type, subscr) == array_subscr) { // value is array, bytearray or memoryview mp_obj_array_t *src_slice = MP_OBJ_TO_PTR(value); if (item_sz != mp_binary_get_size('@', src_slice->typecode & TYPECODE_MASK, NULL)) { @@ -644,6 +664,9 @@ STATIC mp_obj_t array_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value assert(sz > 0); #if MICROPY_PY_BUILTINS_MEMORYVIEW if (o->base.type == &mp_type_memoryview) { + if (slice.start > memview_offset_max) { + mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("memoryview offset too large")); + } res = m_new_obj(mp_obj_array_t); *res = *o; res->memview_offset += slice.start; @@ -749,74 +772,65 @@ STATIC MP_DEFINE_CONST_DICT(bytearray_locals_dict, bytearray_locals_dict_table); #if MICROPY_PY_ARRAY -const mp_obj_type_t mp_type_array = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_array, - .print = array_print, - .make_new = array_make_new, - .locals_dict = (mp_obj_dict_t *)&array_locals_dict, - MP_TYPE_EXTENDED_FIELDS( - .getiter = array_iterator_new, - .unary_op = array_unary_op, - .binary_op = array_binary_op, - .subscr = array_subscr, - .buffer_p = { .get_buffer = array_get_buffer }, - ), -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_array, + MP_QSTR_array, + MP_TYPE_FLAG_ITER_IS_GETITER, + make_new, array_make_new, + print, array_print, + iter, array_iterator_new, + unary_op, array_unary_op, + binary_op, array_binary_op, + subscr, array_subscr, + buffer, array_get_buffer, + locals_dict, &mp_obj_array_locals_dict + ); #endif #if MICROPY_PY_BUILTINS_BYTEARRAY -const mp_obj_type_t mp_type_bytearray = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_bytearray, - .print = array_print, - .make_new = bytearray_make_new, - .locals_dict = (mp_obj_dict_t *)&bytearray_locals_dict, - MP_TYPE_EXTENDED_FIELDS( - .getiter = array_iterator_new, - .unary_op = array_unary_op, - .binary_op = array_binary_op, - .subscr = array_subscr, - .buffer_p = { .get_buffer = array_get_buffer }, - ), -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_bytearray, + MP_QSTR_bytearray, + MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_ITER_IS_GETITER, + make_new, bytearray_make_new, + print, array_print, + iter, array_iterator_new, + unary_op, array_unary_op, + binary_op, array_binary_op, + subscr, array_subscr, + buffer, array_get_buffer, + locals_dict, &mp_obj_bytearray_locals_dict + ); #endif #if MICROPY_PY_BUILTINS_MEMORYVIEW - -#if MICROPY_CPYTHON_COMPAT || MICROPY_PY_BUILTINS_MEMORYVIEW_ITEMSIZE -STATIC const mp_rom_map_elem_t memoryview_locals_dict_table[] = { - #if MICROPY_CPYTHON_COMPAT - { MP_ROM_QSTR(MP_QSTR_cast), MP_ROM_PTR(&memoryview_cast_obj) }, - #endif - #if MICROPY_PY_BUILTINS_MEMORYVIEW_ITEMSIZE - { MP_ROM_QSTR(MP_QSTR_itemsize), MP_ROM_PTR(&memoryview_itemsize_obj) }, - #endif -}; - -STATIC MP_DEFINE_CONST_DICT(memoryview_locals_dict, memoryview_locals_dict_table); +#if MICROPY_PY_BUILTINS_MEMORYVIEW_ITEMSIZE +#define MEMORYVIEW_TYPE_ATTR attr, memoryview_attr, +#else +#define MEMORYVIEW_TYPE_ATTR #endif -const mp_obj_type_t mp_type_memoryview = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_memoryview, - .make_new = memoryview_make_new, - #if MICROPY_CPYTHON_COMPAT || MICROPY_PY_BUILTINS_MEMORYVIEW_ITEMSIZE - .locals_dict = (mp_obj_dict_t *)&memoryview_locals_dict, - #endif - MP_TYPE_EXTENDED_FIELDS( - .getiter = array_iterator_new, - .unary_op = array_unary_op, - .binary_op = array_binary_op, - .subscr = array_subscr, - .buffer_p = { .get_buffer = array_get_buffer }, - ), -}; +#if MICROPY_PY_BUILTINS_BYTES_HEX +#define MEMORYVIEW_TYPE_LOCALS_DICT locals_dict, &mp_obj_memoryview_locals_dict, +#else +#define MEMORYVIEW_TYPE_LOCALS_DICT #endif +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_memoryview, + MP_QSTR_memoryview, + MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_ITER_IS_GETITER, + make_new, memoryview_make_new, + iter, array_iterator_new, + unary_op, array_unary_op, + binary_op, array_binary_op, + MEMORYVIEW_TYPE_LOCALS_DICT + MEMORYVIEW_TYPE_ATTR + subscr, array_subscr, + buffer, array_get_buffer + ); +#endif // MICROPY_PY_BUILTINS_MEMORYVIEW + /* unused size_t mp_obj_array_len(mp_obj_t self_in) { return ((mp_obj_array_t *)self_in)->len; @@ -824,7 +838,7 @@ size_t mp_obj_array_len(mp_obj_t self_in) { */ #if MICROPY_PY_BUILTINS_BYTEARRAY -mp_obj_t mp_obj_new_bytearray(size_t n, void *items) { +mp_obj_t mp_obj_new_bytearray(size_t n, const void *items) { mp_obj_array_t *o = array_new(BYTEARRAY_TYPECODE, n); memcpy(o->items, items, n); return MP_OBJ_FROM_PTR(o); @@ -866,15 +880,12 @@ STATIC mp_obj_t array_it_iternext(mp_obj_t self_in) { } } -STATIC const mp_obj_type_t mp_type_array_it = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_iterator, - MP_TYPE_EXTENDED_FIELDS( - .getiter = mp_identity_getiter, - .iternext = array_it_iternext, - ), -}; +STATIC MP_DEFINE_CONST_OBJ_TYPE( + mp_type_array_it, + MP_QSTR_iterator, + MP_TYPE_FLAG_ITER_IS_ITERNEXT, + iter, array_it_iternext + ); STATIC mp_obj_t array_iterator_new(mp_obj_t array_in, mp_obj_iter_buf_t *iter_buf) { assert(sizeof(mp_obj_array_t) <= sizeof(mp_obj_iter_buf_t)); diff --git a/py/objarray.h b/py/objarray.h index 94c31c9693..4a0e8a983f 100644 --- a/py/objarray.h +++ b/py/objarray.h @@ -32,6 +32,9 @@ // Used only for memoryview types, set in "typecode" to indicate a writable memoryview #define MP_OBJ_ARRAY_TYPECODE_FLAG_RW (0x80) +// Bit size used for mp_obj_array_t.free member. +#define MP_OBJ_ARRAY_FREE_SIZE_BITS (8 * sizeof(size_t) - 8) + // This structure is used for all of bytearray, array.array, memoryview // objects. Note that memoryview has different meaning for some fields, // see comment at the beginning of objarray.c. @@ -44,7 +47,7 @@ typedef struct _mp_obj_array_t { // parent object. (Union is not used to not go into a complication of // union-of-bitfields with different toolchains). See comments in // objarray.c. - size_t free : (8 * sizeof(size_t) - 8); + size_t free : MP_OBJ_ARRAY_FREE_SIZE_BITS; size_t len; // in elements void *items; } mp_obj_array_t; @@ -59,4 +62,9 @@ static inline void mp_obj_memoryview_init(mp_obj_array_t *self, size_t typecode, } #endif +#if MICROPY_PY_ARRAY || MICROPY_PY_BUILTINS_BYTEARRAY +MP_DECLARE_CONST_FUN_OBJ_2(mp_obj_array_append_obj); +MP_DECLARE_CONST_FUN_OBJ_2(mp_obj_array_extend_obj); +#endif + #endif // MICROPY_INCLUDED_PY_OBJARRAY_H diff --git a/py/objattrtuple.c b/py/objattrtuple.c index c288727d48..fbe04bedb8 100644 --- a/py/objattrtuple.c +++ b/py/objattrtuple.c @@ -80,18 +80,17 @@ mp_obj_t mp_obj_new_attrtuple(const qstr *fields, size_t n, const mp_obj_t *item return MP_OBJ_FROM_PTR(o); } -const mp_obj_type_t mp_type_attrtuple = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_tuple, // reuse tuple to save on a qstr - .print = mp_obj_attrtuple_print, - .attr = mp_obj_attrtuple_attr, - MP_TYPE_EXTENDED_FIELDS( - .unary_op = mp_obj_tuple_unary_op, - .binary_op = mp_obj_tuple_binary_op, - .subscr = mp_obj_tuple_subscr, - .getiter = mp_obj_tuple_getiter, - ), -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_attrtuple, + MP_QSTR_tuple, + MP_TYPE_FLAG_ITER_IS_GETITER, + // reuse tuple to save on a qstr + print, mp_obj_attrtuple_print, + unary_op, mp_obj_tuple_unary_op, + binary_op, mp_obj_tuple_binary_op, + attr, mp_obj_attrtuple_attr, + subscr, mp_obj_tuple_subscr, + iter, mp_obj_tuple_getiter + ); #endif // MICROPY_PY_ATTRTUPLE diff --git a/py/objbool.c b/py/objbool.c index e450270fd9..3267ff98bb 100644 --- a/py/objbool.c +++ b/py/objbool.c @@ -84,17 +84,16 @@ STATIC mp_obj_t bool_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_ return mp_binary_op(op, MP_OBJ_NEW_SMALL_INT(value), rhs_in); } -const mp_obj_type_t mp_type_bool = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_EXTENDED, // can match all numeric types - .name = MP_QSTR_bool, - .print = bool_print, - .make_new = bool_make_new, - MP_TYPE_EXTENDED_FIELDS( - .unary_op = bool_unary_op, - .binary_op = bool_binary_op, - ), -}; +MP_DEFINE_CONST_OBJ_TYPE( + // can match all numeric types + mp_type_bool, + MP_QSTR_bool, + MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE, + make_new, bool_make_new, + print, bool_print, + unary_op, bool_unary_op, + binary_op, bool_binary_op + ); #if !MICROPY_OBJ_IMMEDIATE_OBJS const mp_obj_bool_t mp_const_false_obj = {{&mp_type_bool}, false}; diff --git a/py/objboundmeth.c b/py/objboundmeth.c index edc37462dd..8486f876f9 100644 --- a/py/objboundmeth.c +++ b/py/objboundmeth.c @@ -95,20 +95,26 @@ STATIC void bound_meth_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { } #endif -STATIC const mp_obj_type_t mp_type_bound_meth = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_bound_method, - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED - .print = bound_meth_print, - #endif - MP_TYPE_EXTENDED_FIELDS( - .call = bound_meth_call, - ), - #if MICROPY_PY_FUNCTION_ATTRS - .attr = bound_meth_attr, - #endif -}; +#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED +#define BOUND_METH_TYPE_PRINT print, bound_meth_print, +#else +#define BOUND_METH_TYPE_PRINT +#endif + +#if MICROPY_PY_FUNCTION_ATTRS +#define BOUND_METH_TYPE_ATTR attr, bound_meth_attr, +#else +#define BOUND_METH_TYPE_ATTR +#endif + +STATIC 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 + ); mp_obj_t mp_obj_new_bound_meth(mp_obj_t meth, mp_obj_t self) { mp_obj_bound_meth_t *o = mp_obj_malloc(mp_obj_bound_meth_t, &mp_type_bound_meth); diff --git a/py/objcell.c b/py/objcell.c index 2702ca5350..0a74e29d20 100644 --- a/py/objcell.c +++ b/py/objcell.c @@ -26,21 +26,6 @@ #include "py/obj.h" -typedef struct _mp_obj_cell_t { - mp_obj_base_t base; - mp_obj_t obj; -} mp_obj_cell_t; - -mp_obj_t mp_obj_cell_get(mp_obj_t self_in) { - mp_obj_cell_t *self = MP_OBJ_TO_PTR(self_in); - return self->obj; -} - -void mp_obj_cell_set(mp_obj_t self_in, mp_obj_t obj) { - mp_obj_cell_t *self = MP_OBJ_TO_PTR(self_in); - self->obj = obj; -} - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED STATIC void cell_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { (void)kind; @@ -55,13 +40,17 @@ STATIC void cell_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t k } #endif -STATIC const mp_obj_type_t mp_type_cell = { - { &mp_type_type }, - .name = MP_QSTR_, // cell representation is just value in < > - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED - .print = cell_print, - #endif -}; +#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED +#define CELL_TYPE_PRINT , print, cell_print +#else +#define CELL_TYPE_PRINT +#endif + +STATIC MP_DEFINE_CONST_OBJ_TYPE( + // cell representation is just value in < > + mp_type_cell, MP_QSTR_, MP_TYPE_FLAG_NONE + CELL_TYPE_PRINT + ); mp_obj_t mp_obj_new_cell(mp_obj_t obj) { mp_obj_cell_t *o = mp_obj_malloc(mp_obj_cell_t, &mp_type_cell); diff --git a/py/objclosure.c b/py/objclosure.c index 309dd2b826..6059d18100 100644 --- a/py/objclosure.c +++ b/py/objclosure.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2013, 2014 Damien P. George * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -78,17 +78,31 @@ STATIC void closure_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_ } #endif -const mp_obj_type_t mp_type_closure = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_closure, - #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED - .print = closure_print, - #endif - MP_TYPE_EXTENDED_FIELDS( - .call = closure_call, - ) -}; +#if MICROPY_PY_FUNCTION_ATTRS +STATIC void mp_obj_closure_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + // forward to self_in->fun + mp_obj_closure_t *o = MP_OBJ_TO_PTR(self_in); + mp_load_method_maybe(o->fun, attr, dest); +} +#define CLOSURE_TYPE_ATTR attr, mp_obj_closure_attr, +#else +#define CLOSURE_TYPE_ATTR +#endif + +#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED +#define CLOSURE_TYPE_PRINT print, closure_print, +#else +#define CLOSURE_TYPE_PRINT +#endif + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_closure, + MP_QSTR_closure, + MP_TYPE_FLAG_BINDS_SELF, + CLOSURE_TYPE_ATTR + CLOSURE_TYPE_PRINT + call, closure_call + ); mp_obj_t mp_obj_new_closure(mp_obj_t fun, size_t n_closed_over, const mp_obj_t *closed) { mp_obj_closure_t *o = mp_obj_malloc_var(mp_obj_closure_t, mp_obj_t, n_closed_over, &mp_type_closure); diff --git a/py/objcomplex.c b/py/objcomplex.c index 570f9f746f..9c71455a97 100644 --- a/py/objcomplex.c +++ b/py/objcomplex.c @@ -31,8 +31,6 @@ #include "py/parsenum.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" - #if MICROPY_PY_BUILTINS_COMPLEX #include @@ -88,13 +86,14 @@ STATIC mp_obj_t complex_make_new(const mp_obj_type_t *type_in, size_t n_args, si // a string, parse it size_t l; const char *s = mp_obj_str_get_data(args[0], &l); - return mp_parse_num_decimal(s, l, true, true, NULL); + return mp_parse_num_complex(s, l, NULL); } else if (mp_obj_is_type(args[0], &mp_type_complex)) { // a complex, just return it return args[0]; } else { - // something else, try to cast it to a complex - return mp_obj_new_complex(mp_obj_get_float(args[0]), 0); + mp_float_t real, imag; + mp_obj_get_complex(args[0], &real, &imag); + return mp_obj_new_complex(real, imag); } case 2: @@ -155,18 +154,14 @@ STATIC void complex_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { } } -const mp_obj_type_t mp_type_complex = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EQ_NOT_REFLEXIVE | MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_complex, - .print = complex_print, - .make_new = complex_make_new, - .attr = complex_attr, - MP_TYPE_EXTENDED_FIELDS( - .unary_op = complex_unary_op, - .binary_op = complex_binary_op, - ), -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_complex, MP_QSTR_complex, MP_TYPE_FLAG_EQ_NOT_REFLEXIVE | MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE, + make_new, complex_make_new, + print, complex_print, + unary_op, complex_unary_op, + binary_op, complex_binary_op, + attr, complex_attr + ); mp_obj_t mp_obj_new_complex(mp_float_t real, mp_float_t imag) { mp_obj_complex_t *o = mp_obj_malloc(mp_obj_complex_t, &mp_type_complex); @@ -210,13 +205,13 @@ mp_obj_t mp_obj_complex_binary_op(mp_binary_op_t op, mp_float_t lhs_real, mp_flo } case MP_BINARY_OP_FLOOR_DIVIDE: case MP_BINARY_OP_INPLACE_FLOOR_DIVIDE: - mp_raise_TypeError(MP_ERROR_TEXT("can't do truncated division of a complex number")); + mp_raise_TypeError(MP_ERROR_TEXT("can't truncate-divide a complex number")); case MP_BINARY_OP_TRUE_DIVIDE: case MP_BINARY_OP_INPLACE_TRUE_DIVIDE: if (rhs_imag == 0) { if (rhs_real == 0) { - mp_raise_ZeroDivisionError(); + mp_raise_msg(&mp_type_ZeroDivisionError, MP_ERROR_TEXT("complex divide by zero")); } lhs_real /= rhs_real; lhs_imag /= rhs_real; @@ -267,6 +262,4 @@ mp_obj_t mp_obj_complex_binary_op(mp_binary_op_t op, mp_float_t lhs_real, mp_flo return mp_obj_new_complex(lhs_real, lhs_imag); } -#pragma GCC diagnostic pop - #endif diff --git a/py/objdeque.c b/py/objdeque.c index 621b7b60b0..67a5baa257 100644 --- a/py/objdeque.c +++ b/py/objdeque.c @@ -28,7 +28,6 @@ #include #include "py/mpconfig.h" -#include "supervisor/shared/translate/translate.h" #if MICROPY_PY_COLLECTIONS_DEQUE @@ -157,15 +156,13 @@ STATIC const mp_rom_map_elem_t deque_locals_dict_table[] = { STATIC MP_DEFINE_CONST_DICT(deque_locals_dict, deque_locals_dict_table); -const mp_obj_type_t mp_type_deque = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_deque, - .make_new = deque_make_new, - .locals_dict = (mp_obj_dict_t *)&deque_locals_dict, - MP_TYPE_EXTENDED_FIELDS( - .unary_op = deque_unary_op, - ), -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_deque, + MP_QSTR_deque, + MP_TYPE_FLAG_NONE, + make_new, deque_make_new, + unary_op, deque_unary_op, + locals_dict, &deque_locals_dict + ); #endif // MICROPY_PY_COLLECTIONS_DEQUE diff --git a/py/objdict.c b/py/objdict.c index e4e9711c60..05248494ce 100644 --- a/py/objdict.c +++ b/py/objdict.c @@ -33,8 +33,9 @@ #include "py/objtype.h" #include "py/objstr.h" -#include "supervisor/linker.h" -#include "supervisor/shared/translate/translate.h" +bool mp_obj_is_dict_or_ordereddict(mp_obj_t o) { + return mp_obj_is_obj(o) && MP_OBJ_TYPE_GET_SLOT_OR_NULL(((mp_obj_base_t *)MP_OBJ_TO_PTR(o))->type, make_new) == mp_obj_dict_make_new; +} const mp_obj_dict_t mp_const_empty_dict_obj = { .base = { .type = &mp_type_dict }, @@ -125,7 +126,14 @@ STATIC mp_obj_t dict_new_typed(const mp_obj_type_t *type, const size_t n) { } mp_obj_t mp_obj_dict_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { - mp_obj_t dict_out = dict_new_typed(type, 0); + mp_obj_t dict_out = mp_obj_new_dict(0); + mp_obj_dict_t *dict = MP_OBJ_TO_PTR(dict_out); + dict->base.type = type; + #if MICROPY_PY_COLLECTIONS_ORDEREDDICT + if (type == &mp_type_ordereddict) { + dict->map.is_ordered = 1; + } + #endif if (n_args > 0 || n_kw > 0) { mp_obj_t args2[2] = {dict_out, args[0]}; // args[0] is always valid, even if it's not a positional arg mp_map_t kwargs; @@ -175,6 +183,7 @@ STATIC mp_obj_t dict_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_ return e1 == NULL && e2 == NULL ? mp_const_true : mp_const_false; } #endif + if (mp_obj_is_type(rhs_in, &mp_type_dict)) { mp_obj_dict_t *rhs = MP_OBJ_TO_PTR(rhs_in); if (o->map.used != rhs->map.used) { @@ -195,6 +204,17 @@ STATIC mp_obj_t dict_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_ return mp_const_false; } } + #if MICROPY_CPYTHON_COMPAT + case MP_BINARY_OP_INPLACE_OR: + case MP_BINARY_OP_OR: { + if (op == MP_BINARY_OP_OR) { + lhs_in = mp_obj_dict_copy(lhs_in); + } + mp_obj_t dicts[2] = {lhs_in, rhs_in}; + dict_update(2, dicts, (mp_map_t *)&mp_const_empty_map); + return lhs_in; + } + #endif default: // op not supported return MP_OBJ_NULL; @@ -271,7 +291,6 @@ 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) { - 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; mp_obj_t next = MP_OBJ_NULL; @@ -421,53 +440,6 @@ STATIC mp_obj_t dict_update(size_t n_args, const mp_obj_t *args, mp_map_t *kwarg } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(dict_update_obj, 1, dict_update); -#if MICROPY_PY_COLLECTIONS_ORDEREDDICT -STATIC mp_obj_t dict_move_to_end(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - mp_obj_dict_t *self = MP_OBJ_TO_PTR(pos_args[0]); - mp_arg_validate_type(self, &mp_type_ordereddict, MP_QSTR_self); - - // parse args - enum { ARG_key, ARG_last }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_key, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } }, - { MP_QSTR_last, MP_ARG_BOOL, {.u_bool = true } } - }; - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - mp_obj_t *key = args[ARG_key].u_obj; - bool last = args[ARG_last].u_bool; - - mp_map_elem_t *elem = mp_map_lookup(&self->map, key, MP_MAP_LOOKUP); - if (!elem) { - mp_raise_type_arg(&mp_type_KeyError, key); - } - - mp_map_elem_t tmp = *elem; - mp_map_elem_t *table = self->map.table; - mp_map_elem_t *dest, *move_begin, *move_dest; - size_t move_count; - - if (last) { - mp_map_elem_t *top = &table[self->map.used]; - dest = top - 1; - move_begin = elem + 1; - move_dest = elem; - move_count = top - move_begin; - } else { - dest = &table[0]; - move_begin = table; - move_dest = table + 1; - move_count = elem - table; - } - memmove(move_dest, move_begin, move_count * sizeof(*elem)); - *dest = tmp; - - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(dict_move_to_end_obj, 1, dict_move_to_end); -#endif - /******************************************************************************/ /* dict views */ @@ -518,15 +490,12 @@ STATIC mp_obj_t dict_view_it_iternext(mp_obj_t self_in) { } } -STATIC const mp_obj_type_t mp_type_dict_view_it = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_iterator, - MP_TYPE_EXTENDED_FIELDS( - .getiter = mp_identity_getiter, - .iternext = dict_view_it_iternext, - ), -}; +STATIC MP_DEFINE_CONST_OBJ_TYPE( + mp_type_dict_view_it, + MP_QSTR_iterator, + MP_TYPE_FLAG_ITER_IS_ITERNEXT, + iter, dict_view_it_iternext + ); STATIC mp_obj_t dict_view_getiter(mp_obj_t view_in, mp_obj_iter_buf_t *iter_buf) { assert(sizeof(mp_obj_dict_view_it_t) <= sizeof(mp_obj_iter_buf_t)); @@ -572,16 +541,14 @@ STATIC mp_obj_t dict_view_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t return dict_binary_op(op, o->dict, rhs_in); } -STATIC const mp_obj_type_t mp_type_dict_view = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_dict_view, - .print = dict_view_print, - MP_TYPE_EXTENDED_FIELDS( - .binary_op = dict_view_binary_op, - .getiter = dict_view_getiter, - ), -}; +STATIC MP_DEFINE_CONST_OBJ_TYPE( + mp_type_dict_view, + MP_QSTR_dict_view, + MP_TYPE_FLAG_ITER_IS_GETITER, + print, dict_view_print, + binary_op, dict_view_binary_op, + iter, dict_view_getiter + ); STATIC mp_obj_t mp_obj_new_dict_view(mp_obj_t dict, mp_dict_view_kind_t kind) { mp_obj_dict_view_t *o = mp_obj_malloc(mp_obj_dict_view_t, &mp_type_dict_view); @@ -636,9 +603,6 @@ STATIC const mp_rom_map_elem_t dict_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_get), MP_ROM_PTR(&dict_get_obj) }, { MP_ROM_QSTR(MP_QSTR_items), MP_ROM_PTR(&dict_items_obj) }, { MP_ROM_QSTR(MP_QSTR_keys), MP_ROM_PTR(&dict_keys_obj) }, - #if MICROPY_PY_COLLECTIONS_ORDEREDDICT - { MP_ROM_QSTR(MP_QSTR_move_to_end), MP_ROM_PTR(&dict_move_to_end_obj) }, - #endif { MP_ROM_QSTR(MP_QSTR_pop), MP_ROM_PTR(&dict_pop_obj) }, { MP_ROM_QSTR(MP_QSTR_popitem), MP_ROM_PTR(&dict_popitem_obj) }, { MP_ROM_QSTR(MP_QSTR_setdefault), MP_ROM_PTR(&dict_setdefault_obj) }, @@ -651,37 +615,33 @@ STATIC const mp_rom_map_elem_t dict_locals_dict_table[] = { STATIC MP_DEFINE_CONST_DICT(dict_locals_dict, dict_locals_dict_table); -const mp_obj_type_t mp_type_dict = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_dict, - .print = dict_print, - .make_new = mp_obj_dict_make_new, - .locals_dict = (mp_obj_dict_t *)&dict_locals_dict, - MP_TYPE_EXTENDED_FIELDS( - .unary_op = dict_unary_op, - .binary_op = dict_binary_op, - .subscr = dict_subscr, - .getiter = dict_getiter, - ), -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_dict, + MP_QSTR_dict, + MP_TYPE_FLAG_ITER_IS_GETITER, + make_new, mp_obj_dict_make_new, + print, dict_print, + unary_op, dict_unary_op, + binary_op, dict_binary_op, + subscr, dict_subscr, + iter, dict_getiter, + locals_dict, &dict_locals_dict + ); #if MICROPY_PY_COLLECTIONS_ORDEREDDICT -const mp_obj_type_t mp_type_ordereddict = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_OrderedDict, - .print = dict_print, - .make_new = mp_obj_dict_make_new, - .parent = &mp_type_dict, - .locals_dict = (mp_obj_dict_t *)&dict_locals_dict, - MP_TYPE_EXTENDED_FIELDS( - .unary_op = dict_unary_op, - .binary_op = dict_binary_op, - .subscr = dict_subscr, - .getiter = dict_getiter, - ), -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_ordereddict, + MP_QSTR_OrderedDict, + MP_TYPE_FLAG_ITER_IS_GETITER, + make_new, mp_obj_dict_make_new, + print, dict_print, + unary_op, dict_unary_op, + binary_op, dict_binary_op, + subscr, dict_subscr, + iter, dict_getiter, + parent, &mp_type_dict, + locals_dict, &dict_locals_dict + ); #endif void mp_obj_dict_init(mp_obj_dict_t *dict, size_t n_args) { @@ -700,7 +660,7 @@ size_t mp_obj_dict_len(mp_obj_t self_in) { return self->map.used; } -mp_obj_t PLACE_IN_ITCM(mp_obj_dict_store)(mp_obj_t self_in, mp_obj_t key, mp_obj_t value) { +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)); mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_in); mp_ensure_not_fixed(self); diff --git a/py/objenumerate.c b/py/objenumerate.c index 2ef553adb5..40bed919ed 100644 --- a/py/objenumerate.c +++ b/py/objenumerate.c @@ -67,16 +67,13 @@ STATIC mp_obj_t enumerate_make_new(const mp_obj_type_t *type, size_t n_args, siz return MP_OBJ_FROM_PTR(o); } -const mp_obj_type_t mp_type_enumerate = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_enumerate, - .make_new = enumerate_make_new, - MP_TYPE_EXTENDED_FIELDS( - .iternext = enumerate_iternext, - .getiter = mp_identity_getiter, - ) -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_enumerate, + MP_QSTR_enumerate, + MP_TYPE_FLAG_ITER_IS_ITERNEXT, + make_new, enumerate_make_new, + iter, enumerate_iternext + ); STATIC mp_obj_t enumerate_iternext(mp_obj_t self_in) { assert(mp_obj_is_type(self_in, &mp_type_enumerate)); diff --git a/py/objexcept.c b/py/objexcept.c index 282740ae44..a96c1d362f 100644 --- a/py/objexcept.c +++ b/py/objexcept.c @@ -31,7 +31,6 @@ #include #include "py/objlist.h" -#include "py/objnamedtuple.h" #include "py/objstr.h" #include "py/objtuple.h" #include "py/objtype.h" @@ -39,7 +38,15 @@ #include "py/gc.h" #include "py/mperrno.h" -#include "supervisor/shared/translate/translate.h" +#if MICROPY_ROM_TEXT_COMPRESSION && !defined(NO_QSTR) +// Extract the MP_MAX_UNCOMPRESSED_TEXT_LEN macro from "genhdr/compressed.data.h". +// Only need this if compression enabled and in a regular build (i.e. not during QSTR extraction). +#define MP_MATCH_COMPRESSED(...) // Ignore +#define MP_COMPRESSED_DATA(...) // Ignore +#include "genhdr/compressed.data.h" +#undef MP_MATCH_COMPRESSED +#undef MP_COMPRESSED_DATA +#endif // Number of items per traceback entry (file, line, block) #define TRACEBACK_ENTRY_LEN (3) @@ -104,7 +111,11 @@ mp_obj_t mp_alloc_emergency_exception_buf(mp_obj_t size_in) { #endif #endif // MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF -mp_obj_exception_t *mp_obj_exception_get_native(mp_obj_t self_in) { +bool mp_obj_is_native_exception_instance(mp_obj_t self_in) { + return MP_OBJ_TYPE_GET_SLOT_OR_NULL(mp_obj_get_type(self_in), make_new) == mp_obj_exception_make_new; +} + +mp_obj_exception_t *get_native_exception(mp_obj_t self_in) { assert(mp_obj_is_exception_instance(self_in)); if (mp_obj_is_native_exception_instance(self_in)) { return MP_OBJ_TO_PTR(self_in); @@ -113,6 +124,40 @@ mp_obj_exception_t *mp_obj_exception_get_native(mp_obj_t self_in) { } } +STATIC void decompress_error_text_maybe(mp_obj_exception_t *o) { + #if MICROPY_ROM_TEXT_COMPRESSION + if (o->args->len == 1 && mp_obj_is_exact_type(o->args->items[0], &mp_type_str)) { + mp_obj_str_t *o_str = MP_OBJ_TO_PTR(o->args->items[0]); + if (MP_IS_COMPRESSED_ROM_STRING(o_str->data)) { + byte *buf = m_new_maybe(byte, MP_MAX_UNCOMPRESSED_TEXT_LEN + 1); + if (!buf) { + #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF + // Try and use the emergency exception buf if enough space is available. + buf = (byte *)((uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + EMG_BUF_STR_BUF_OFFSET); + size_t avail = (uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + mp_emergency_exception_buf_size - buf; + if (avail < MP_MAX_UNCOMPRESSED_TEXT_LEN + 1) { + // No way to decompress, fallback to no message text. + o->args = (mp_obj_tuple_t *)&mp_const_empty_tuple_obj; + return; + } + #else + o->args = (mp_obj_tuple_t *)&mp_const_empty_tuple_obj; + return; + #endif + } + mp_decompress_rom_string(buf, (mp_rom_error_text_t)o_str->data); + o_str->data = buf; + o_str->len = strlen((const char *)buf); + o_str->hash = 0; + } + // Lazily compute the string hash. + if (o_str->hash == 0) { + o_str->hash = qstr_compute_hash(o_str->data, o_str->len); + } + } + #endif +} + void mp_obj_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { mp_obj_exception_t *o = MP_OBJ_TO_PTR(o_in); mp_print_kind_t k = kind & ~PRINT_EXC_SUBCLASS; @@ -125,14 +170,18 @@ void mp_obj_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kin mp_print_str(print, ": "); } + decompress_error_text_maybe(o); + if (k == PRINT_STR || k == PRINT_EXC) { if (o->args == NULL || o->args->len == 0) { mp_print_str(print, ""); return; } + #if MICROPY_PY_UERRNO // try to provide a nice OSError error message if (o->base.type == &mp_type_OSError && o->args->len > 0 && o->args->len < 3 && mp_obj_is_small_int(o->args->items[0])) { + // CIRCUITPY can print a whole string, not just the errno qstr char decompressed[50]; const char *msg = mp_common_errno_to_str(o->args->items[0], decompressed, sizeof(decompressed)); if (msg != NULL) { @@ -155,6 +204,7 @@ void mp_obj_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kin mp_obj_tuple_print(print, MP_OBJ_FROM_PTR(o->args), kind); } +// CIRCUITPY void mp_obj_exception_initialize0(mp_obj_exception_t *o_exc, const mp_obj_type_t *type) { o_exc->base.type = type; o_exc->args = (mp_obj_tuple_t *)&mp_const_empty_tuple_obj; @@ -212,10 +262,11 @@ mp_obj_t mp_obj_exception_make_new(const mp_obj_type_t *type, size_t n_args, siz // Get exception "value" - that is, first argument, or None mp_obj_t mp_obj_exception_get_value(mp_obj_t self_in) { - mp_obj_exception_t *self = mp_obj_exception_get_native(self_in); + mp_obj_exception_t *self = get_native_exception(self_in); if (self->args->len == 0) { return mp_const_none; } else { + decompress_error_text_maybe(self); return self->args->items[0]; } } @@ -267,6 +318,7 @@ void mp_obj_exception_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { return; } if (attr == MP_QSTR_args) { + decompress_error_text_maybe(self); dest[0] = MP_OBJ_FROM_PTR(self->args); } else if (attr == MP_QSTR_value && self->base.type == &mp_type_StopIteration) { dest[0] = mp_obj_exception_get_value(self_in); @@ -308,13 +360,14 @@ void mp_obj_exception_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { } } -const mp_obj_type_t mp_type_BaseException = { - { &mp_type_type }, - .name = MP_QSTR_BaseException, - .print = mp_obj_exception_print, - .make_new = mp_obj_exception_make_new, - .attr = mp_obj_exception_attr, -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_BaseException, + MP_QSTR_BaseException, + MP_TYPE_FLAG_NONE, + make_new, mp_obj_exception_make_new, + print, mp_obj_exception_print, + attr, mp_obj_exception_attr + ); // *FORMAT-OFF* @@ -322,51 +375,58 @@ const mp_obj_type_t mp_type_BaseException = { // http://docs.python.org/3/library/exceptions.html MP_DEFINE_EXCEPTION(SystemExit, BaseException) MP_DEFINE_EXCEPTION(KeyboardInterrupt, BaseException) +// CIRCUITPY MP_DEFINE_EXCEPTION(ReloadException, BaseException) MP_DEFINE_EXCEPTION(GeneratorExit, BaseException) MP_DEFINE_EXCEPTION(Exception, BaseException) -#if MICROPY_PY_ASYNC_AWAIT + #if MICROPY_PY_ASYNC_AWAIT MP_DEFINE_EXCEPTION(StopAsyncIteration, Exception) -#endif + #endif MP_DEFINE_EXCEPTION(StopIteration, Exception) MP_DEFINE_EXCEPTION(ArithmeticError, Exception) - // MP_DEFINE_EXCEPTION(FloatingPointError, ArithmeticError) + //MP_DEFINE_EXCEPTION(FloatingPointError, ArithmeticError) MP_DEFINE_EXCEPTION(OverflowError, ArithmeticError) MP_DEFINE_EXCEPTION(ZeroDivisionError, ArithmeticError) MP_DEFINE_EXCEPTION(AssertionError, Exception) MP_DEFINE_EXCEPTION(AttributeError, Exception) - // MP_DEFINE_EXCEPTION(BufferError, Exception) - // MP_DEFINE_EXCEPTION(EnvironmentError, Exception) use OSError instead + //MP_DEFINE_EXCEPTION(BufferError, Exception) MP_DEFINE_EXCEPTION(EOFError, Exception) MP_DEFINE_EXCEPTION(ImportError, Exception) - // MP_DEFINE_EXCEPTION(IOError, Exception) use OSError instead MP_DEFINE_EXCEPTION(LookupError, Exception) MP_DEFINE_EXCEPTION(IndexError, LookupError) MP_DEFINE_EXCEPTION(KeyError, LookupError) MP_DEFINE_EXCEPTION(MemoryError, Exception) MP_DEFINE_EXCEPTION(NameError, Exception) - // MP_DEFINE_EXCEPTION(UnboundLocalError, NameError) + /* + MP_DEFINE_EXCEPTION(UnboundLocalError, NameError) + */ MP_DEFINE_EXCEPTION(OSError, Exception) + /* + MP_DEFINE_EXCEPTION(BlockingIOError, OSError) + MP_DEFINE_EXCEPTION(ChildProcessError, OSError) MP_DEFINE_EXCEPTION(ConnectionError, OSError) MP_DEFINE_EXCEPTION(BrokenPipeError, ConnectionError) - // MP_DEFINE_EXCEPTION(ConnectionAbortedError, ConnectionError) - // MP_DEFINE_EXCEPTION(ConnectionRefusedError, ConnectionError) - // MP_DEFINE_EXCEPTION(ConnectionResetError, ConnectionError) - // MP_DEFINE_EXCEPTION(FileExistsError, OSError) - MP_DEFINE_EXCEPTION(FileNotFoundError, OSError) - // MP_DEFINE_EXCEPTION(InterruptedError, OSError) - // MP_DEFINE_EXCEPTION(IsADirectoryError, OSError) - // MP_DEFINE_EXCEPTION(NotADirectoryError, OSError) - // MP_DEFINE_EXCEPTION(PermissionError, OSError) - // MP_DEFINE_EXCEPTION(ProcessLookupError, OSError) - // MP_DEFINE_EXCEPTION(ReferenceError, Exception) + MP_DEFINE_EXCEPTION(ConnectionAbortedError, ConnectionError) + MP_DEFINE_EXCEPTION(ConnectionRefusedError, ConnectionError) + MP_DEFINE_EXCEPTION(ConnectionResetError, ConnectionError) + MP_DEFINE_EXCEPTION(InterruptedError, OSError) + MP_DEFINE_EXCEPTION(IsADirectoryError, OSError) + MP_DEFINE_EXCEPTION(NotADirectoryError, OSError) + MP_DEFINE_EXCEPTION(PermissionError, OSError) + MP_DEFINE_EXCEPTION(ProcessLookupError, OSError) MP_DEFINE_EXCEPTION(TimeoutError, OSError) + MP_DEFINE_EXCEPTION(FileExistsError, OSError) + MP_DEFINE_EXCEPTION(FileNotFoundError, OSError) + MP_DEFINE_EXCEPTION(ReferenceError, Exception) + */ MP_DEFINE_EXCEPTION(RuntimeError, Exception) MP_DEFINE_EXCEPTION(NotImplementedError, RuntimeError) MP_DEFINE_EXCEPTION(SyntaxError, Exception) MP_DEFINE_EXCEPTION(IndentationError, SyntaxError) - // MP_DEFINE_EXCEPTION(TabError, IndentationError) - // MP_DEFINE_EXCEPTION(SystemError, Exception) + /* + MP_DEFINE_EXCEPTION(TabError, IndentationError) + */ + //MP_DEFINE_EXCEPTION(SystemError, Exception) MP_DEFINE_EXCEPTION(TypeError, Exception) #if MICROPY_EMIT_NATIVE MP_DEFINE_EXCEPTION(ViperTypeError, TypeError) @@ -396,12 +456,12 @@ MP_DEFINE_EXCEPTION(DeepSleepRequest, BaseException) // *FORMAT-ON* mp_obj_t mp_obj_new_exception(const mp_obj_type_t *exc_type) { - assert(exc_type->make_new == mp_obj_exception_make_new); + assert(MP_OBJ_TYPE_GET_SLOT_OR_NULL(exc_type, make_new) == mp_obj_exception_make_new); return mp_obj_exception_make_new(exc_type, 0, 0, NULL); } mp_obj_t mp_obj_new_exception_args(const mp_obj_type_t *exc_type, size_t n_args, const mp_obj_t *args) { - assert(exc_type->make_new == mp_obj_exception_make_new); + assert(MP_OBJ_TYPE_GET_SLOT_OR_NULL(exc_type, make_new) == mp_obj_exception_make_new); return mp_obj_exception_make_new(exc_type, n_args, 0, args); } @@ -409,7 +469,6 @@ mp_obj_t mp_obj_new_exception_args(const mp_obj_type_t *exc_type, size_t n_args, mp_obj_t mp_obj_new_exception_msg(const mp_obj_type_t *exc_type, const compressed_string_t *msg) { return mp_obj_new_exception_msg_varg(exc_type, msg); } -#endif // The following struct and function implement a simple printer that conservatively // allocates memory and truncates the output data if no more memory can be obtained. @@ -444,20 +503,19 @@ STATIC void exc_add_strn(void *data, const char *str, size_t len) { pr->len += len; } - -mp_obj_t mp_obj_new_exception_msg_varg(const mp_obj_type_t *exc_type, const compressed_string_t *fmt, ...) { - va_list ap; - va_start(ap, fmt); - mp_obj_t exception = mp_obj_new_exception_msg_vlist(exc_type, fmt, ap); - va_end(ap); - return exception; +mp_obj_t mp_obj_new_exception_msg_varg(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, ...) { + va_list args; + va_start(args, fmt); + mp_obj_t exc = mp_obj_new_exception_msg_vlist(exc_type, fmt, args); + va_end(args); + return exc; } -mp_obj_t mp_obj_new_exception_msg_vlist(const mp_obj_type_t *exc_type, const compressed_string_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 - assert(exc_type->make_new == mp_obj_exception_make_new); + assert(MP_OBJ_TYPE_GET_SLOT_OR_NULL(exc_type, make_new) == mp_obj_exception_make_new); // Try to allocate memory for the message mp_obj_str_t *o_str = m_new_obj_maybe(mp_obj_str_t); @@ -502,7 +560,11 @@ mp_obj_t mp_obj_new_exception_msg_vlist(const mp_obj_type_t *exc_type, const com // Create the string object and call mp_obj_exception_make_new to create the exception o_str->base.type = &mp_type_str; + #if MICROPY_ROM_TEXT_COMPRESSION + o_str->hash = 0; // will be computed only if string object is accessed + #else o_str->hash = qstr_compute_hash(o_str->data, o_str->len); + #endif mp_obj_t arg = MP_OBJ_FROM_PTR(o_str); return mp_obj_exception_make_new(exc_type, 1, 0, &arg); } @@ -543,12 +605,14 @@ mp_obj_t mp_obj_new_exception_msg_str(const mp_obj_type_t *exc_type, const char return mp_obj_exception_make_new(exc_type, 1, 0, &arg); } +#endif + // return true if the given object is an exception type bool mp_obj_is_exception_type(mp_obj_t self_in) { if (mp_obj_is_type(self_in, &mp_type_type)) { // optimisation when self_in is a builtin exception mp_obj_type_t *self = MP_OBJ_TO_PTR(self_in); - if (self->make_new == mp_obj_exception_make_new) { + if (MP_OBJ_TYPE_GET_SLOT_OR_NULL(self, make_new) == mp_obj_exception_make_new) { return true; } } @@ -574,7 +638,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) { - mp_obj_exception_t *self = mp_obj_exception_get_native(self_in); + mp_obj_exception_t *self = get_native_exception(self_in); // just set the traceback to the empty traceback object // we don't want to call any memory management functions here self->traceback = (mp_obj_traceback_t *)&mp_const_empty_traceback_obj; @@ -587,7 +651,10 @@ void mp_obj_exception_clear_traceback(mp_obj_t self_in) { } 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); + mp_obj_exception_t *self = get_native_exception(self_in); + + // append this traceback info to traceback data + // if memory allocation fails (eg because gc is locked), just return #if MICROPY_PY_SYS_TRACEBACKLIMIT mp_int_t max_traceback = MP_OBJ_SMALL_INT_VALUE(MP_STATE_VM(sys_mutable[MP_SYS_MUTABLE_TRACEBACKLIMIT])); @@ -669,172 +736,3 @@ void mp_obj_exception_get_traceback(mp_obj_t self_in, size_t *n, size_t **values *values = self->traceback->data; } } - -#if MICROPY_PY_SYS_EXC_INFO -STATIC const mp_obj_namedtuple_type_t code_type_obj = { - .base = { - .base = { - .type = &mp_type_type - }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_code, - .print = namedtuple_print, - .make_new = namedtuple_make_new, - .parent = &mp_type_tuple, - .attr = namedtuple_attr, - MP_TYPE_EXTENDED_FIELDS( - .unary_op = mp_obj_tuple_unary_op, - .binary_op = mp_obj_tuple_binary_op, - .subscr = mp_obj_tuple_subscr, - .getiter = mp_obj_tuple_getiter, - ), - }, - .n_fields = 15, - .fields = { - MP_QSTR_co_argcount, - MP_QSTR_co_kwonlyargcount, - MP_QSTR_co_nlocals, - MP_QSTR_co_stacksize, - MP_QSTR_co_flags, - MP_QSTR_co_code, - MP_QSTR_co_consts, - MP_QSTR_co_names, - MP_QSTR_co_varnames, - MP_QSTR_co_freevars, - MP_QSTR_co_cellvars, - MP_QSTR_co_filename, - MP_QSTR_co_name, - MP_QSTR_co_firstlineno, - MP_QSTR_co_lnotab, - }, -}; - -STATIC mp_obj_t code_make_new(qstr file, qstr block) { - mp_obj_t elems[15] = { - mp_obj_new_int(0), // co_argcount - mp_obj_new_int(0), // co_kwonlyargcount - mp_obj_new_int(0), // co_nlocals - mp_obj_new_int(0), // co_stacksize - mp_obj_new_int(0), // co_flags - mp_obj_new_bytearray(0, NULL), // co_code - mp_obj_new_tuple(0, NULL), // co_consts - mp_obj_new_tuple(0, NULL), // co_names - mp_obj_new_tuple(0, NULL), // co_varnames - mp_obj_new_tuple(0, NULL), // co_freevars - mp_obj_new_tuple(0, NULL), // co_cellvars - MP_OBJ_NEW_QSTR(file), // co_filename - MP_OBJ_NEW_QSTR(block), // co_name - mp_obj_new_int(1), // co_firstlineno - mp_obj_new_bytearray(0, NULL), // co_lnotab - }; - - return namedtuple_make_new((const mp_obj_type_t *)&code_type_obj, 15, 0, elems); -} - -STATIC const mp_obj_namedtuple_type_t frame_type_obj = { - .base = { - .base = { - .type = &mp_type_type - }, - .name = MP_QSTR_frame, - .print = namedtuple_print, - .make_new = namedtuple_make_new, - .parent = &mp_type_tuple, - .attr = namedtuple_attr, - MP_TYPE_EXTENDED_FIELDS( - .unary_op = mp_obj_tuple_unary_op, - .binary_op = mp_obj_tuple_binary_op, - .subscr = mp_obj_tuple_subscr, - .getiter = mp_obj_tuple_getiter, - ), - }, - .n_fields = 8, - .fields = { - MP_QSTR_f_back, - MP_QSTR_f_builtins, - MP_QSTR_f_code, - MP_QSTR_f_globals, - MP_QSTR_f_lasti, - MP_QSTR_f_lineno, - MP_QSTR_f_locals, - MP_QSTR_f_trace, - }, -}; - -STATIC mp_obj_t frame_make_new(mp_obj_t f_code, int f_lineno) { - mp_obj_t elems[8] = { - mp_const_none, // f_back - mp_obj_new_dict(0), // f_builtins - f_code, // f_code - mp_obj_new_dict(0), // f_globals - mp_obj_new_int(0), // f_lasti - mp_obj_new_int(f_lineno), // f_lineno - mp_obj_new_dict(0), // f_locals - mp_const_none, // f_trace - }; - - return namedtuple_make_new((const mp_obj_type_t *)&frame_type_obj, 8, 0, elems); -} - -STATIC const mp_obj_namedtuple_type_t traceback_type_obj = { - .base = { - .base = { - .type = &mp_type_type - }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_traceback, - .print = namedtuple_print, - .make_new = namedtuple_make_new, - .parent = &mp_type_tuple, - .attr = namedtuple_attr, - MP_TYPE_EXTENDED_FIELDS( - .unary_op = mp_obj_tuple_unary_op, - .binary_op = mp_obj_tuple_binary_op, - .subscr = mp_obj_tuple_subscr, - .getiter = mp_obj_tuple_getiter, - ), - }, - .n_fields = 4, - .fields = { - MP_QSTR_tb_frame, - MP_QSTR_tb_lasti, - MP_QSTR_tb_lineno, - MP_QSTR_tb_next, - }, -}; - -STATIC mp_obj_t traceback_from_values(size_t *values, mp_obj_t tb_next) { - int lineno = values[1]; - - mp_obj_t elems[4] = { - frame_make_new(code_make_new(values[0], values[2]), lineno), - mp_obj_new_int(0), - mp_obj_new_int(lineno), - tb_next, - }; - - return namedtuple_make_new((const mp_obj_type_t *)&traceback_type_obj, 4, 0, elems); -}; - -mp_obj_t mp_obj_exception_get_traceback_obj(mp_obj_t self_in) { - mp_obj_exception_t *self = MP_OBJ_TO_PTR(self_in); - - if (!mp_obj_is_exception_instance(self)) { - return mp_const_none; - } - - size_t n, *values; - mp_obj_exception_get_traceback(self, &n, &values); - if (n == 0) { - return mp_const_none; - } - - mp_obj_t tb_next = mp_const_none; - - for (size_t i = 0; i < n; i += 3) { - tb_next = traceback_from_values(&values[i], tb_next); - } - - return tb_next; -} -#endif diff --git a/py/objexcept.h b/py/objexcept.h index 80c06e90e0..c7e1d89f2d 100644 --- a/py/objexcept.h +++ b/py/objexcept.h @@ -47,13 +47,11 @@ void mp_obj_exception_initialize0(mp_obj_exception_t *o_exc, const mp_obj_type_t mp_obj_exception_t *mp_obj_exception_get_native(mp_obj_t self_in); #define MP_DEFINE_EXCEPTION(exc_name, base_name) \ - const mp_obj_type_t mp_type_##exc_name = { \ - { &mp_type_type }, \ - .name = MP_QSTR_##exc_name, \ - .print = mp_obj_exception_print, \ - .make_new = mp_obj_exception_make_new, \ - .attr = mp_obj_exception_attr, \ - .parent = &mp_type_##base_name, \ - }; + MP_DEFINE_CONST_OBJ_TYPE(mp_type_##exc_name, MP_QSTR_##exc_name, MP_TYPE_FLAG_NONE, \ + make_new, mp_obj_exception_make_new, \ + print, mp_obj_exception_print, \ + attr, mp_obj_exception_attr, \ + parent, &mp_type_##base_name \ + ); #endif // MICROPY_INCLUDED_PY_OBJEXCEPT_H diff --git a/py/objfilter.c b/py/objfilter.c index 5292cfc550..2a657fde4b 100644 --- a/py/objfilter.c +++ b/py/objfilter.c @@ -60,15 +60,12 @@ STATIC mp_obj_t filter_iternext(mp_obj_t self_in) { return MP_OBJ_STOP_ITERATION; } -const mp_obj_type_t mp_type_filter = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_filter, - .make_new = filter_make_new, - MP_TYPE_EXTENDED_FIELDS( - .getiter = mp_identity_getiter, - .iternext = filter_iternext, - ), -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_filter, + MP_QSTR_filter, + MP_TYPE_FLAG_ITER_IS_ITERNEXT, + make_new, filter_make_new, + iter, filter_iternext + ); #endif // MICROPY_PY_BUILTINS_FILTER diff --git a/py/objfloat.c b/py/objfloat.c index 5666f3f5c2..14c4140e51 100644 --- a/py/objfloat.c +++ b/py/objfloat.c @@ -32,8 +32,6 @@ #include "py/parsenum.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" - #if MICROPY_PY_BUILTINS_FLOAT #include @@ -142,7 +140,7 @@ STATIC mp_obj_t float_make_new(const mp_obj_type_t *type_in, size_t n_args, size mp_buffer_info_t bufinfo; if (mp_get_buffer(args[0], &bufinfo, MP_BUFFER_READ)) { // a textual representation, parse it - return mp_parse_num_decimal(bufinfo.buf, bufinfo.len, false, false, NULL); + return mp_parse_num_float(bufinfo.buf, bufinfo.len, false, NULL); } else if (mp_obj_is_float(args[0])) { // a float, just return it return args[0]; @@ -187,17 +185,13 @@ STATIC mp_obj_t float_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs return mp_obj_float_binary_op(op, lhs_val, rhs_in); } -const mp_obj_type_t mp_type_float = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EQ_NOT_REFLEXIVE | MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_float, - .print = float_print, - .make_new = float_make_new, - MP_TYPE_EXTENDED_FIELDS( - .unary_op = float_unary_op, - .binary_op = float_binary_op, - ), -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_float, MP_QSTR_float, MP_TYPE_FLAG_EQ_NOT_REFLEXIVE | MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE, + make_new, float_make_new, + print, float_print, + unary_op, float_unary_op, + binary_op, float_binary_op + ); #if MICROPY_OBJ_REPR != MICROPY_OBJ_REPR_C && MICROPY_OBJ_REPR != MICROPY_OBJ_REPR_D @@ -352,22 +346,4 @@ mp_obj_t mp_obj_float_binary_op(mp_binary_op_t op, mp_float_t lhs_val, mp_obj_t return mp_obj_new_float(lhs_val); } -// Convert a uint64_t to a 32-bit float without invoking the double-precision math routines, -// which are large. -mp_float_t uint64_to_float(uint64_t ui64) { - // 4294967296 = 2^32 - return (mp_float_t)((uint32_t)(ui64 >> 32) * 4294967296.0f + (uint32_t)(ui64 & 0xffffffff)); -} - -// Convert a uint64_t to a 32-bit float to a uint64_t without invoking extra math routines. -// which are large. -// Assume f >= 0. -uint64_t float_to_uint64(float f) { - // 4294967296 = 2^32 - const uint32_t upper_half = (uint32_t)(f / 4294967296.0f); - const uint32_t lower_half = (uint32_t)f; - return (((uint64_t)upper_half) << 32) + lower_half; -} -#pragma GCC diagnostic pop - #endif // MICROPY_PY_BUILTINS_FLOAT diff --git a/py/objfun.c b/py/objfun.c index eb4a2bfe25..e10b79d089 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -34,8 +34,6 @@ #include "py/bc.h" #include "py/stackctrl.h" -#include "supervisor/linker.h" - #if MICROPY_DEBUG_VERBOSE // print debugging info #define DEBUG_PRINT (1) #else // don't print debugging info @@ -58,15 +56,11 @@ STATIC mp_obj_t PLACE_IN_ITCM(fun_builtin_0_call)(mp_obj_t self_in, size_t n_arg return self->fun._0(); } -const mp_obj_type_t mp_type_fun_builtin_0 = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_BUILTIN_FUN | MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_function, - MP_TYPE_EXTENDED_FIELDS( - .call = fun_builtin_0_call, - .unary_op = mp_generic_unary_op, - ), -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_fun_builtin_0, MP_QSTR_function, MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_BUILTIN_FUN, + call, fun_builtin_0_call, + unary_op, mp_generic_unary_op + ); 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)); @@ -75,32 +69,24 @@ STATIC mp_obj_t PLACE_IN_ITCM(fun_builtin_1_call)(mp_obj_t self_in, size_t n_arg return self->fun._1(args[0]); } -const mp_obj_type_t mp_type_fun_builtin_1 = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_BUILTIN_FUN | MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_function, - MP_TYPE_EXTENDED_FIELDS( - .call = fun_builtin_1_call, - .unary_op = mp_generic_unary_op, - ), -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_fun_builtin_1, MP_QSTR_function, MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_BUILTIN_FUN, + call, fun_builtin_1_call, + unary_op, mp_generic_unary_op + ); -STATIC mp_obj_t PLACE_IN_ITCM(fun_builtin_2_call)(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { +STATIC mp_obj_t fun_builtin_2_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_2)); mp_obj_fun_builtin_fixed_t *self = MP_OBJ_TO_PTR(self_in); mp_arg_check_num(n_args, n_kw, 2, 2, false); return self->fun._2(args[0], args[1]); } -const mp_obj_type_t mp_type_fun_builtin_2 = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_BUILTIN_FUN | MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_function, - MP_TYPE_EXTENDED_FIELDS( - .call = fun_builtin_2_call, - .unary_op = mp_generic_unary_op, - ), -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_fun_builtin_2, MP_QSTR_function, MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_BUILTIN_FUN, + call, fun_builtin_2_call, + unary_op, mp_generic_unary_op + ); 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)); @@ -109,17 +95,13 @@ STATIC mp_obj_t PLACE_IN_ITCM(fun_builtin_3_call)(mp_obj_t self_in, size_t n_arg return self->fun._3(args[0], args[1], args[2]); } -const mp_obj_type_t mp_type_fun_builtin_3 = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_BUILTIN_FUN | MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_function, - MP_TYPE_EXTENDED_FIELDS( - .call = fun_builtin_3_call, - .unary_op = mp_generic_unary_op, - ), -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_fun_builtin_3, MP_QSTR_function, MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_BUILTIN_FUN, + call, fun_builtin_3_call, + unary_op, mp_generic_unary_op + ); -STATIC mp_obj_t PLACE_IN_ITCM(fun_builtin_var_call)(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { +STATIC mp_obj_t fun_builtin_var_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_var)); mp_obj_fun_builtin_var_t *self = MP_OBJ_TO_PTR(self_in); @@ -142,15 +124,11 @@ STATIC mp_obj_t PLACE_IN_ITCM(fun_builtin_var_call)(mp_obj_t self_in, size_t n_a } } -const mp_obj_type_t mp_type_fun_builtin_var = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_BUILTIN_FUN | MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_function, - MP_TYPE_EXTENDED_FIELDS( - .call = fun_builtin_var_call, - .unary_op = mp_generic_unary_op, - ), -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_fun_builtin_var, MP_QSTR_function, MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_BUILTIN_FUN, + call, fun_builtin_var_call, + unary_op, mp_generic_unary_op + ); /******************************************************************************/ /* byte code functions */ @@ -374,21 +352,27 @@ void mp_obj_fun_bc_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { } #endif -const mp_obj_type_t mp_type_fun_bc = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_function, - #if MICROPY_CPYTHON_COMPAT - .print = fun_bc_print, - #endif - #if MICROPY_PY_FUNCTION_ATTRS - .attr = mp_obj_fun_bc_attr, - #endif - MP_TYPE_EXTENDED_FIELDS( - .call = fun_bc_call, - .unary_op = mp_generic_unary_op, - ), -}; +#if MICROPY_CPYTHON_COMPAT +#define FUN_BC_TYPE_PRINT print, fun_bc_print, +#else +#define FUN_BC_TYPE_PRINT +#endif + +#if MICROPY_PY_FUNCTION_ATTRS +#define FUN_BC_TYPE_ATTR attr, mp_obj_fun_bc_attr, +#else +#define FUN_BC_TYPE_ATTR +#endif + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_fun_bc, + MP_QSTR_function, + MP_TYPE_FLAG_BINDS_SELF, + FUN_BC_TYPE_PRINT + FUN_BC_TYPE_ATTR + call, fun_bc_call, + unary_op, mp_generic_unary_op + ); mp_obj_t mp_obj_new_fun_bc(const mp_obj_t *def_args, const byte *code, const mp_module_context_t *context, struct _mp_raw_code_t *const *child_table) { size_t n_def_args = 0; @@ -431,15 +415,26 @@ STATIC mp_obj_t PLACE_IN_ITCM(fun_native_call)(mp_obj_t self_in, size_t n_args, return fun(self_in, n_args, n_kw, args); } -STATIC const mp_obj_type_t mp_type_fun_native = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_function, - MP_TYPE_EXTENDED_FIELDS( - .call = fun_native_call, - .unary_op = mp_generic_unary_op, - ), -}; +#if MICROPY_CPYTHON_COMPAT +#define FUN_BC_TYPE_PRINT print, fun_bc_print, +#else +#define FUN_BC_TYPE_PRINT +#endif +#if MICROPY_PY_FUNCTION_ATTRS +#define FUN_BC_TYPE_ATTR attr, mp_obj_fun_bc_attr, +#else +#define FUN_BC_TYPE_ATTR +#endif + +STATIC MP_DEFINE_CONST_OBJ_TYPE( + mp_type_fun_native, + MP_QSTR_function, + MP_TYPE_FLAG_BINDS_SELF, + FUN_BC_TYPE_PRINT + FUN_BC_TYPE_ATTR + call, fun_native_call, + unary_op, mp_generic_unary_op + ); mp_obj_t mp_obj_new_fun_native(const mp_obj_t *def_args, const void *fun_data, const mp_module_context_t *mc, struct _mp_raw_code_t *const *child_table) { mp_obj_fun_bc_t *o = MP_OBJ_TO_PTR(mp_obj_new_fun_bc(def_args, (const byte *)fun_data, mc, child_table)); @@ -478,7 +473,7 @@ STATIC mp_uint_t convert_obj_for_inline_asm(mp_obj_t obj) { return 0; } else if (obj == mp_const_true) { return 1; - } else if (mp_obj_is_type(obj, &mp_type_int)) { + } else if (mp_obj_is_exact_type(obj, &mp_type_int)) { return mp_obj_int_get_truncated(obj); } else if (mp_obj_is_str(obj)) { // pointer to the string (it's probably constant though!) @@ -541,15 +536,13 @@ STATIC mp_obj_t PLACE_IN_ITCM(fun_asm_call)(mp_obj_t self_in, size_t n_args, siz return mp_native_to_obj(ret, self->type_sig); } -STATIC const mp_obj_type_t mp_type_fun_asm = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_function, - MP_TYPE_EXTENDED_FIELDS( - .call = fun_asm_call, - .unary_op = mp_generic_unary_op, - ), -}; +STATIC MP_DEFINE_CONST_OBJ_TYPE( + mp_type_fun_asm, + MP_QSTR_function, + MP_TYPE_FLAG_BINDS_SELF, + call, fun_asm_call, + unary_op, mp_generic_unary_op + ); mp_obj_t mp_obj_new_fun_asm(size_t n_args, const void *fun_data, mp_uint_t type_sig) { mp_obj_fun_asm_t *o = mp_obj_malloc(mp_obj_fun_asm_t, &mp_type_fun_asm); diff --git a/py/objgenerator.c b/py/objgenerator.c index 4443f81ce1..9c4e31a998 100644 --- a/py/objgenerator.c +++ b/py/objgenerator.c @@ -35,8 +35,6 @@ #include "py/objfun.h" #include "py/stackctrl.h" -#include "supervisor/shared/translate/translate.h" - // Instance of GeneratorExit exception - needed by generator.close() // CIRCUITPY: https://github.com/adafruit/circuitpython/pull/7069 fix #if MICROPY_CONST_GENERATOREXIT_OBJ @@ -85,32 +83,30 @@ STATIC mp_obj_t gen_wrap_call(mp_obj_t self_in, size_t n_args, size_t n_kw, cons return MP_OBJ_FROM_PTR(o); } -const mp_obj_type_t mp_type_gen_wrap = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_generator, - #if MICROPY_PY_FUNCTION_ATTRS - .attr = mp_obj_fun_bc_attr, - #endif - MP_TYPE_EXTENDED_FIELDS( - .call = gen_wrap_call, - .unary_op = mp_generic_unary_op, - ), -}; +#if MICROPY_PY_FUNCTION_ATTRS +#define GEN_WRAP_TYPE_ATTR attr, mp_obj_fun_bc_attr, +#else +#define GEN_WRAP_TYPE_ATTR +#endif + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_gen_wrap, + MP_QSTR_generator, + MP_TYPE_FLAG_BINDS_SELF, + GEN_WRAP_TYPE_ATTR + call, gen_wrap_call, + unary_op, mp_generic_unary_op + ); #if MICROPY_PY_ASYNC_AWAIT -const mp_obj_type_t mp_type_coro_wrap = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_coroutine, - #if MICROPY_PY_FUNCTION_ATTRS - .attr = mp_obj_fun_bc_attr, - #endif - MP_TYPE_EXTENDED_FIELDS( - .call = gen_wrap_call, - .unary_op = mp_generic_unary_op, - ), -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_coro_wrap, + MP_QSTR_coroutine, + MP_TYPE_FLAG_BINDS_SELF, + GEN_WRAP_TYPE_ATTR + call, gen_wrap_call, + unary_op, mp_generic_unary_op + ); #endif /******************************************************************************/ @@ -175,32 +171,31 @@ STATIC mp_obj_t native_gen_wrap_call(mp_obj_t self_in, size_t n_args, size_t n_k return MP_OBJ_FROM_PTR(o); } -const mp_obj_type_t mp_type_native_gen_wrap = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_generator, - #if MICROPY_PY_FUNCTION_ATTRS - .attr = mp_obj_fun_bc_attr, - #endif - MP_TYPE_EXTENDED_FIELDS( - .call = native_gen_wrap_call, - .unary_op = mp_generic_unary_op, - ), -}; +#if MICROPY_PY_FUNCTION_ATTRS +#define NATIVE_GEN_WRAP_TYPE_ATTR attr, mp_obj_fun_bc_attr, +#else +#define NATIVE_GEN_WRAP_TYPE_ATTR +#endif + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_native_gen_wrap, + MP_QSTR_generator, + MP_TYPE_FLAG_BINDS_SELF, + call, native_gen_wrap_call, + NATIVE_GEN_WRAP_TYPE_ATTR + unary_op, mp_generic_unary_op + ); + #if MICROPY_PY_ASYNC_AWAIT -const mp_obj_type_t mp_type_native_coro_wrap = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_coroutine, - #if MICROPY_PY_FUNCTION_ATTRS - .attr = mp_obj_fun_bc_attr, - #endif - MP_TYPE_EXTENDED_FIELDS( - .call = native_gen_wrap_call, - .unary_op = mp_generic_unary_op, - ), -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_native_coro_wrap, + MP_QSTR_coroutine, + MP_TYPE_FLAG_BINDS_SELF, + GEN_WRAP_TYPE_ATTR + call, native_gen_wrap_call, + unary_op, mp_generic_unary_op + ); #endif #endif // MICROPY_EMIT_NATIVE @@ -241,6 +236,7 @@ mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_ *ret_val = mp_const_none; return MP_VM_RETURN_NORMAL; } + // Ensure the generator cannot be reentered during execution if (self->pend_exc == MP_OBJ_NULL) { mp_raise_ValueError(MP_ERROR_TEXT("generator already executing")); @@ -337,7 +333,6 @@ mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_ STATIC mp_obj_t gen_resume_and_raise(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t throw_value, bool raise_stop_iteration) { mp_obj_t ret; switch (mp_obj_gen_resume(self_in, send_value, throw_value, &ret)) { - case MP_VM_RETURN_NORMAL: default: // A normal return is a StopIteration, either raise it or return @@ -450,47 +445,12 @@ STATIC const mp_rom_map_elem_t gen_instance_locals_dict_table[] = { STATIC MP_DEFINE_CONST_DICT(gen_instance_locals_dict, gen_instance_locals_dict_table); -const mp_obj_type_t mp_type_gen_instance = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_generator, - .print = gen_instance_print, - .locals_dict = (mp_obj_dict_t *)&gen_instance_locals_dict, - MP_TYPE_EXTENDED_FIELDS( - .unary_op = mp_generic_unary_op, - .getiter = mp_identity_getiter, - .iternext = gen_instance_iternext, - ), -}; - -#if MICROPY_PY_ASYNC_AWAIT -// CIRCUITPY -// 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[] = { - { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&gen_instance_close_obj) }, - { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&gen_instance_send_obj) }, - { MP_ROM_QSTR(MP_QSTR_throw), MP_ROM_PTR(&gen_instance_throw_obj) }, - #if MICROPY_PY_GENERATOR_PEND_THROW - { MP_ROM_QSTR(MP_QSTR_pend_throw), MP_ROM_PTR(&gen_instance_pend_throw_obj) }, - #endif - #if MICROPY_PY_ASYNC_AWAIT - { MP_ROM_QSTR(MP_QSTR___await__), MP_ROM_PTR(&coro_instance_await_obj) }, - #endif -}; - -STATIC MP_DEFINE_CONST_DICT(coro_instance_locals_dict, coro_instance_locals_dict_table); - -const mp_obj_type_t mp_type_coro_instance = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_coroutine, - .print = coro_instance_print, - .locals_dict = (mp_obj_dict_t *)&coro_instance_locals_dict, - MP_TYPE_EXTENDED_FIELDS( - .unary_op = mp_generic_unary_op, - .getiter = mp_identity_getiter, - .iternext = gen_instance_iternext, - ), -}; -#endif +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_gen_instance, + MP_QSTR_generator, + MP_TYPE_FLAG_ITER_IS_ITERNEXT, + print, gen_instance_print, + unary_op, mp_generic_unary_op, + iter, gen_instance_iternext, + locals_dict, &gen_instance_locals_dict + ); diff --git a/py/objgetitemiter.c b/py/objgetitemiter.c index d841212649..c598d1daac 100644 --- a/py/objgetitemiter.c +++ b/py/objgetitemiter.c @@ -56,15 +56,12 @@ STATIC mp_obj_t it_iternext(mp_obj_t self_in) { } } -STATIC const mp_obj_type_t mp_type_it = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_iterator, - MP_TYPE_EXTENDED_FIELDS( - .getiter = mp_identity_getiter, - .iternext = it_iternext, - ), -}; +STATIC MP_DEFINE_CONST_OBJ_TYPE( + mp_type_it, + MP_QSTR_iterator, + MP_TYPE_FLAG_ITER_IS_ITERNEXT, + iter, it_iternext + ); // args are those returned from mp_load_method_maybe (ie either an attribute or a method) mp_obj_t mp_obj_new_getitem_iter(mp_obj_t *args, mp_obj_iter_buf_t *iter_buf) { diff --git a/py/objint.c b/py/objint.c index 83987d690a..fac8db173d 100644 --- a/py/objint.c +++ b/py/objint.c @@ -35,8 +35,6 @@ #include "py/runtime.h" #include "py/binary.h" -#include "supervisor/shared/translate/translate.h" - #if MICROPY_PY_BUILTINS_FLOAT #include #endif @@ -142,9 +140,9 @@ 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) { - mp_raise_OverflowError_varg(MP_ERROR_TEXT("can't convert %q to %q"), MP_QSTR_inf, MP_QSTR_int); + mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("can't convert inf to int")); } else { - mp_raise_ValueError_varg(MP_ERROR_TEXT("can't convert %q to %q"), MP_QSTR_NaN, MP_QSTR_int); + mp_raise_ValueError(MP_ERROR_TEXT("can't convert NaN to int")); } } else { mp_fp_as_int_class_t icl = mp_classify_fp_as_int(val); @@ -234,7 +232,7 @@ char *mp_obj_int_formatted(char **buf, size_t *buf_size, size_t *fmt_size, mp_co // A small int; get the integer value to format. num = MP_OBJ_SMALL_INT_VALUE(self_in); } else { - assert(mp_obj_is_type(self_in, &mp_type_int)); + assert(mp_obj_is_exact_type(self_in, &mp_type_int)); // Not a small int. #if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_LONGLONG const mp_obj_int_t *self = self_in; @@ -582,15 +580,13 @@ STATIC const mp_rom_map_elem_t int_locals_dict_table[] = { STATIC MP_DEFINE_CONST_DICT(int_locals_dict, int_locals_dict_table); -const mp_obj_type_t mp_type_int = { - { &mp_type_type }, - .name = MP_QSTR_int, - .flags = MP_TYPE_FLAG_EXTENDED, - .print = mp_obj_int_print, - .make_new = mp_obj_int_make_new, - .locals_dict = (mp_obj_dict_t *)&int_locals_dict, - MP_TYPE_EXTENDED_FIELDS( - .unary_op = mp_obj_int_unary_op, - .binary_op = mp_obj_int_binary_op, - ), -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_int, + MP_QSTR_int, + MP_TYPE_FLAG_NONE, + make_new, mp_obj_int_make_new, + print, mp_obj_int_print, + unary_op, mp_obj_int_unary_op, + binary_op, mp_obj_int_binary_op, + locals_dict, &int_locals_dict + ); diff --git a/py/objint_longlong.c b/py/objint_longlong.c index ba23825abf..a2d6e8382b 100644 --- a/py/objint_longlong.c +++ b/py/objint_longlong.c @@ -32,8 +32,6 @@ #include "py/objint.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" - #if MICROPY_PY_BUILTINS_FLOAT #include #endif @@ -71,7 +69,7 @@ mp_obj_t mp_obj_int_from_bytes_impl(bool big_endian, size_t len, const byte *buf } void mp_obj_int_to_bytes_impl(mp_obj_t self_in, bool big_endian, size_t len, byte *buf) { - assert(mp_obj_is_type(self_in, &mp_type_int)); + assert(mp_obj_is_exact_type(self_in, &mp_type_int)); mp_obj_int_t *self = self_in; long long val = self->val; if (big_endian) { @@ -144,13 +142,13 @@ mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i if (mp_obj_is_small_int(lhs_in)) { lhs_val = MP_OBJ_SMALL_INT_VALUE(lhs_in); } else { - assert(mp_obj_is_type(lhs_in, &mp_type_int)); + assert(mp_obj_is_exact_type(lhs_in, &mp_type_int)); lhs_val = ((mp_obj_int_t *)lhs_in)->val; } if (mp_obj_is_small_int(rhs_in)) { rhs_val = MP_OBJ_SMALL_INT_VALUE(rhs_in); - } else if (mp_obj_is_type(rhs_in, &mp_type_int)) { + } else if (mp_obj_is_exact_type(rhs_in, &mp_type_int)) { rhs_val = ((mp_obj_int_t *)rhs_in)->val; } else { // delegate to generic function to check for extra cases @@ -297,7 +295,7 @@ mp_int_t mp_obj_int_get_checked(mp_const_obj_t self_in) { #if MICROPY_PY_BUILTINS_FLOAT mp_float_t mp_obj_int_as_float_impl(mp_obj_t self_in) { - assert(mp_obj_is_type(self_in, &mp_type_int)); + assert(mp_obj_is_exact_type(self_in, &mp_type_int)); mp_obj_int_t *self = self_in; return self->val; } diff --git a/py/objint_mpz.c b/py/objint_mpz.c index 603a5b986c..cfb00e6580 100644 --- a/py/objint_mpz.c +++ b/py/objint_mpz.c @@ -33,8 +33,6 @@ #include "py/objint.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" - #if MICROPY_PY_BUILTINS_FLOAT #include #endif @@ -93,7 +91,7 @@ mp_obj_int_t *mp_obj_int_new_mpz(void) { // This particular routine should only be called for the mpz representation of the int. char *mp_obj_int_formatted_impl(char **buf, size_t *buf_size, size_t *fmt_size, mp_const_obj_t self_in, int base, const char *prefix, char base_char, char comma) { - assert(mp_obj_is_type(self_in, &mp_type_int)); + assert(mp_obj_is_exact_type(self_in, &mp_type_int)); const mp_obj_int_t *self = MP_OBJ_TO_PTR(self_in); size_t needed_size = mp_int_format_size(mpz_max_num_bits(&self->mpz), base, prefix, comma); @@ -121,7 +119,7 @@ mp_obj_t mp_obj_int_from_bytes_impl(bool big_endian, size_t len, const byte *buf } void mp_obj_int_to_bytes_impl(mp_obj_t self_in, bool big_endian, size_t len, byte *buf) { - assert(mp_obj_is_type(self_in, &mp_type_int)); + assert(mp_obj_is_exact_type(self_in, &mp_type_int)); mp_obj_int_t *self = MP_OBJ_TO_PTR(self_in); mpz_as_bytes(&self->mpz, big_endian, len, buf); } @@ -189,7 +187,7 @@ mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i mpz_init_fixed_from_int(&z_int, z_int_dig, MPZ_NUM_DIG_FOR_INT, MP_OBJ_SMALL_INT_VALUE(lhs_in)); zlhs = &z_int; } else { - assert(mp_obj_is_type(lhs_in, &mp_type_int)); + assert(mp_obj_is_exact_type(lhs_in, &mp_type_int)); zlhs = &((mp_obj_int_t *)MP_OBJ_TO_PTR(lhs_in))->mpz; } @@ -197,7 +195,7 @@ mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i if (mp_obj_is_small_int(rhs_in)) { mpz_init_fixed_from_int(&z_int, z_int_dig, MPZ_NUM_DIG_FOR_INT, MP_OBJ_SMALL_INT_VALUE(rhs_in)); zrhs = &z_int; - } else if (mp_obj_is_type(rhs_in, &mp_type_int)) { + } else if (mp_obj_is_exact_type(rhs_in, &mp_type_int)) { zrhs = &((mp_obj_int_t *)MP_OBJ_TO_PTR(rhs_in))->mpz; #if MICROPY_PY_BUILTINS_FLOAT } else if (mp_obj_is_float(rhs_in)) { @@ -304,8 +302,7 @@ mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i mpz_pow_inpl(&res->mpz, zlhs, zrhs); break; - default: { - assert(op == MP_BINARY_OP_DIVMOD); + case MP_BINARY_OP_DIVMOD: { if (mpz_is_zero(zrhs)) { goto zero_division_error; } @@ -314,6 +311,9 @@ mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i mp_obj_t tuple[2] = {MP_OBJ_FROM_PTR(quo), MP_OBJ_FROM_PTR(res)}; return mp_obj_new_tuple(2, tuple); } + + default: + return MP_OBJ_NULL; // op not supported } return MP_OBJ_FROM_PTR(res); @@ -459,7 +459,7 @@ mp_uint_t mp_obj_int_get_uint_checked(mp_const_obj_t self_in) { #if MICROPY_PY_BUILTINS_FLOAT mp_float_t mp_obj_int_as_float_impl(mp_obj_t self_in) { - assert(mp_obj_is_type(self_in, &mp_type_int)); + assert(mp_obj_is_exact_type(self_in, &mp_type_int)); mp_obj_int_t *self = MP_OBJ_TO_PTR(self_in); return mpz_as_float(&self->mpz); } diff --git a/py/objlist.c b/py/objlist.c index f95c322bd5..36b4d962ef 100644 --- a/py/objlist.c +++ b/py/objlist.c @@ -31,8 +31,6 @@ #include "py/runtime.h" #include "py/stackctrl.h" -#include "supervisor/shared/translate/translate.h" - STATIC mp_obj_t mp_obj_new_list_iterator(mp_obj_t list, size_t cur, mp_obj_iter_buf_t *iter_buf); STATIC mp_obj_list_t *list_new(size_t n); STATIC mp_obj_t list_extend(mp_obj_t self_in, mp_obj_t arg_in); @@ -468,20 +466,19 @@ STATIC const mp_rom_map_elem_t list_locals_dict_table[] = { STATIC MP_DEFINE_CONST_DICT(list_locals_dict, list_locals_dict_table); -const mp_obj_type_t mp_type_list = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_list, - .print = list_print, - .make_new = list_make_new, - .locals_dict = (mp_obj_dict_t *)&list_locals_dict, - MP_TYPE_EXTENDED_FIELDS( - .unary_op = list_unary_op, - .binary_op = list_binary_op, - .subscr = list_subscr, - .getiter = list_getiter, - ), -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_list, + MP_QSTR_list, + MP_TYPE_FLAG_ITER_IS_GETITER, + make_new, mp_obj_list_make_new, + print, list_print, + unary_op, list_unary_op, + binary_op, list_binary_op, + subscr, list_subscr, + iter, list_getiter, + locals_dict, &list_locals_dict + ); + void mp_obj_list_init(mp_obj_list_t *o, size_t n) { o->base.type = &mp_type_list; diff --git a/py/objlist.h b/py/objlist.h index 79ed6c288c..b015261f61 100644 --- a/py/objlist.h +++ b/py/objlist.h @@ -36,6 +36,7 @@ typedef struct _mp_obj_list_t { } 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); 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/objmap.c b/py/objmap.c index d87105c089..e7e594cd2c 100644 --- a/py/objmap.c +++ b/py/objmap.c @@ -63,13 +63,10 @@ STATIC mp_obj_t map_iternext(mp_obj_t self_in) { return mp_call_function_n_kw(self->fun, self->n_iters, 0, nextses); } -const mp_obj_type_t mp_type_map = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_map, - .make_new = map_make_new, - MP_TYPE_EXTENDED_FIELDS( - .getiter = mp_identity_getiter, - .iternext = map_iternext, - ), -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_map, + MP_QSTR_map, + MP_TYPE_FLAG_ITER_IS_ITERNEXT, + make_new, map_make_new, + iter, map_iternext + ); diff --git a/py/objmodule.c b/py/objmodule.c index 1c281793d6..5916f63b74 100644 --- a/py/objmodule.c +++ b/py/objmodule.c @@ -89,6 +89,10 @@ STATIC void module_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { mp_map_elem_t *elem = mp_map_lookup(&self->globals->map, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP); if (elem != NULL) { dest[0] = elem->value; + #if MICROPY_CPYTHON_COMPAT + } else if (attr == MP_QSTR___dict__) { + dest[0] = MP_OBJ_FROM_PTR(self->globals); + #endif #if MICROPY_MODULE_GETATTR } else if (attr != MP_QSTR___getattr__) { elem = mp_map_lookup(&self->globals->map, MP_OBJ_NEW_QSTR(MP_QSTR___getattr__), MP_MAP_LOOKUP); @@ -130,12 +134,13 @@ STATIC void module_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { } } -const mp_obj_type_t mp_type_module = { - { &mp_type_type }, - .name = MP_QSTR_module, - .print = module_print, - .attr = module_attr, -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_module, + MP_QSTR_module, + MP_TYPE_FLAG_NONE, + print, module_print, + attr, module_attr + ); mp_obj_t mp_obj_new_module(qstr module_name) { mp_map_t *mp_loaded_modules_map = &MP_STATE_VM(mp_loaded_modules_dict).map; diff --git a/py/objnamedtuple.c b/py/objnamedtuple.c index 1139ab2b54..1b0169b63f 100644 --- a/py/objnamedtuple.c +++ b/py/objnamedtuple.c @@ -33,8 +33,6 @@ #include "py/objnamedtuple.h" #include "py/objtype.h" -#include "supervisor/shared/translate/translate.h" - #if MICROPY_PY_COLLECTIONS size_t mp_obj_namedtuple_find_field(const mp_obj_namedtuple_type_t *type, qstr name) { @@ -54,6 +52,7 @@ STATIC mp_obj_t namedtuple_asdict(mp_obj_t self_in) { mp_obj_dict_t *dictObj = MP_OBJ_TO_PTR(dict); #if MICROPY_PY_COLLECTIONS_ORDEREDDICT // make it an OrderedDict + mp_obj_dict_t *dictObj = MP_OBJ_TO_PTR(dict); dictObj->base.type = &mp_type_ordereddict; dictObj->map.is_ordered = 1; #else @@ -151,8 +150,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_var(mp_obj_namedtuple_type_t, qstr, n_fields); - memset(&o->base, 0, sizeof(o->base)); + mp_obj_namedtuple_type_t *o = m_new_obj_var0(mp_obj_namedtuple_type_t, 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]); @@ -162,17 +160,18 @@ mp_obj_namedtuple_type_t *mp_obj_new_namedtuple_base(size_t n_fields, mp_obj_t * STATIC mp_obj_t mp_obj_new_namedtuple_type(qstr name, size_t n_fields, mp_obj_t *fields) { mp_obj_namedtuple_type_t *o = mp_obj_new_namedtuple_base(n_fields, fields); - o->base.base.type = &mp_type_type; - o->base.flags = MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_EXTENDED; // can match tuple - o->base.name = name; - o->base.print = namedtuple_print; - o->base.make_new = namedtuple_make_new; - o->base.MP_TYPE_UNARY_OP = mp_obj_tuple_unary_op; - o->base.MP_TYPE_BINARY_OP = mp_obj_tuple_binary_op; - o->base.attr = namedtuple_attr; - o->base.MP_TYPE_SUBSCR = mp_obj_tuple_subscr; - o->base.MP_TYPE_GETITER = mp_obj_tuple_getiter; - o->base.parent = &mp_type_tuple; + mp_obj_type_t *type = (mp_obj_type_t *)&o->base; + type->base.type = &mp_type_type; + type->flags = MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE; // can match tuple + type->name = name; + MP_OBJ_TYPE_SET_SLOT(type, make_new, namedtuple_make_new, 0); + MP_OBJ_TYPE_SET_SLOT(type, print, namedtuple_print, 1); + MP_OBJ_TYPE_SET_SLOT(type, unary_op, mp_obj_tuple_unary_op, 2); + MP_OBJ_TYPE_SET_SLOT(type, binary_op, mp_obj_tuple_binary_op, 3); + MP_OBJ_TYPE_SET_SLOT(type, attr, namedtuple_attr, 4); + MP_OBJ_TYPE_SET_SLOT(type, subscr, mp_obj_tuple_subscr, 5); + MP_OBJ_TYPE_SET_SLOT(type, iter, mp_obj_tuple_getiter, 6); + MP_OBJ_TYPE_SET_SLOT(type, parent, &mp_type_tuple, 7); return MP_OBJ_FROM_PTR(o); } diff --git a/py/objnamedtuple.h b/py/objnamedtuple.h index b6c37f3909..865db526e1 100644 --- a/py/objnamedtuple.h +++ b/py/objnamedtuple.h @@ -28,7 +28,6 @@ #include -#include "py/nlr.h" #include "py/objtuple.h" #include "py/runtime.h" #include "py/objstr.h" @@ -36,7 +35,9 @@ #if MICROPY_PY_COLLECTIONS typedef struct _mp_obj_namedtuple_type_t { - mp_obj_full_type_t base; + // This is a mp_obj_type_t with eight slots. + mp_obj_empty_type_t base; + void *slots[8]; size_t n_fields; qstr fields[]; } mp_obj_namedtuple_type_t; diff --git a/py/objnone.c b/py/objnone.c index 9f6960669a..4f8996e897 100644 --- a/py/objnone.c +++ b/py/objnone.c @@ -43,15 +43,13 @@ STATIC void none_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_ } } -const mp_obj_type_t mp_type_NoneType = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_NoneType, - .print = none_print, - MP_TYPE_EXTENDED_FIELDS( - .unary_op = mp_generic_unary_op, - ), -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_NoneType, + MP_QSTR_NoneType, + MP_TYPE_FLAG_NONE, + print, none_print, + unary_op, mp_generic_unary_op + ); #if !MICROPY_OBJ_IMMEDIATE_OBJS const mp_obj_none_t mp_const_none_obj = {{&mp_type_NoneType}}; diff --git a/py/objobject.c b/py/objobject.c index 32552775aa..4f296106ce 100644 --- a/py/objobject.c +++ b/py/objobject.c @@ -29,8 +29,6 @@ #include "py/objtype.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" - typedef struct _mp_obj_object_t { mp_obj_base_t base; } mp_obj_object_t; @@ -113,11 +111,16 @@ STATIC const mp_rom_map_elem_t object_locals_dict_table[] = { STATIC MP_DEFINE_CONST_DICT(object_locals_dict, object_locals_dict_table); #endif -const mp_obj_type_t mp_type_object = { - { &mp_type_type }, - .name = MP_QSTR_object, - .make_new = object_make_new, - #if MICROPY_CPYTHON_COMPAT - .locals_dict = (mp_obj_dict_t *)&object_locals_dict, - #endif -}; +#if MICROPY_CPYTHON_COMPAT +#define OBJECT_TYPE_LOCALS_DICT , locals_dict, &object_locals_dict +#else +#define OBJECT_TYPE_LOCALS_DICT +#endif + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_object, + MP_QSTR_object, + MP_TYPE_FLAG_NONE, + make_new, object_make_new + OBJECT_TYPE_LOCALS_DICT + ); diff --git a/py/objpolyiter.c b/py/objpolyiter.c index 20f12381d6..78b600abac 100644 --- a/py/objpolyiter.c +++ b/py/objpolyiter.c @@ -45,12 +45,42 @@ STATIC mp_obj_t polymorph_it_iternext(mp_obj_t self_in) { return self->iternext(self_in); } -const mp_obj_type_t mp_type_polymorph_iter = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_iterator, - MP_TYPE_EXTENDED_FIELDS( - .getiter = mp_identity_getiter, - .iternext = polymorph_it_iternext, - ), +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_polymorph_iter, + MP_QSTR_iterator, + MP_TYPE_FLAG_ITER_IS_ITERNEXT, + iter, polymorph_it_iternext + ); + +#if MICROPY_ENABLE_FINALISER +// mp_type_polymorph_iter_with_finaliser is a variant of the universal iterator +// above which has a finaliser function attached. This function will be run when +// the GC collects the iter object and can be used to close file handles etc. + +// Any instance should have these 3 fields at the beginning +typedef struct _mp_obj_polymorph_iter_with_finaliser_t { + mp_obj_base_t base; + mp_fun_1_t iternext; + mp_fun_1_t finaliser; +} mp_obj_polymorph_with_finaliser_iter_t; + +STATIC mp_obj_t mp_obj_polymorph_iter_del(mp_obj_t self_in) { + mp_obj_polymorph_with_finaliser_iter_t *self = MP_OBJ_TO_PTR(self_in); + // Redirect call to object instance's finaliser method + return self->finaliser(self_in); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_obj_polymorph_iter_del_obj, mp_obj_polymorph_iter_del); + +STATIC const mp_rom_map_elem_t mp_obj_polymorph_iter_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mp_obj_polymorph_iter_del_obj) }, }; +STATIC MP_DEFINE_CONST_DICT(mp_obj_polymorph_iter_locals_dict, mp_obj_polymorph_iter_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_polymorph_iter_with_finaliser, + MP_QSTR_iterator, + MP_TYPE_FLAG_ITER_IS_ITERNEXT, + iter, polymorph_it_iternext, + locals_dict, &mp_obj_polymorph_iter_locals_dict + ); +#endif diff --git a/py/objproperty.c b/py/objproperty.c index b0436e5c1f..98011f2782 100644 --- a/py/objproperty.c +++ b/py/objproperty.c @@ -87,12 +87,13 @@ STATIC const mp_rom_map_elem_t property_locals_dict_table[] = { STATIC MP_DEFINE_CONST_DICT(property_locals_dict, property_locals_dict_table); -const mp_obj_type_t mp_type_property = { - { &mp_type_type }, - .name = MP_QSTR_property, - .make_new = property_make_new, - .locals_dict = (mp_obj_dict_t *)&property_locals_dict, -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_property, + MP_QSTR_property, + MP_TYPE_FLAG_NONE, + make_new, property_make_new, + locals_dict, &property_locals_dict + ); #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; diff --git a/py/objrange.c b/py/objrange.c index d83b55d826..bfe95b1f72 100644 --- a/py/objrange.c +++ b/py/objrange.c @@ -28,8 +28,6 @@ #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" - /******************************************************************************/ /* range iterator */ @@ -52,15 +50,12 @@ STATIC mp_obj_t range_it_iternext(mp_obj_t o_in) { } } -STATIC const mp_obj_type_t mp_type_range_it = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_iterator, - MP_TYPE_EXTENDED_FIELDS( - .getiter = mp_identity_getiter, - .iternext = range_it_iternext, - ), -}; +STATIC MP_DEFINE_CONST_OBJ_TYPE( + mp_type_range_it, + MP_QSTR_iterator, + MP_TYPE_FLAG_ITER_IS_ITERNEXT, + iter, range_it_iternext + ); STATIC mp_obj_t mp_obj_new_range_iterator(mp_int_t cur, mp_int_t stop, mp_int_t step, mp_obj_iter_buf_t *iter_buf) { assert(sizeof(mp_obj_range_it_t) <= sizeof(mp_obj_iter_buf_t)); @@ -213,21 +208,27 @@ STATIC void range_attr(mp_obj_t o_in, qstr attr, mp_obj_t *dest) { } #endif -const mp_obj_type_t mp_type_range = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_range, - .print = range_print, - .make_new = range_make_new, - #if MICROPY_PY_BUILTINS_RANGE_ATTRS - .attr = range_attr, - #endif - MP_TYPE_EXTENDED_FIELDS( - .unary_op = range_unary_op, - #if MICROPY_PY_BUILTINS_RANGE_BINOP - .binary_op = range_binary_op, - #endif - .subscr = range_subscr, - .getiter = range_getiter, - ), -}; +#if MICROPY_PY_BUILTINS_RANGE_BINOP +#define RANGE_TYPE_BINOP binary_op, range_binary_op, +#else +#define RANGE_TYPE_BINOP +#endif + +#if MICROPY_PY_BUILTINS_RANGE_ATTRS +#define RANGE_TYPE_ATTR attr, range_attr, +#else +#define RANGE_TYPE_ATTR +#endif + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_range, + MP_QSTR_range, + MP_TYPE_FLAG_NONE, + make_new, range_make_new, + RANGE_TYPE_BINOP + RANGE_TYPE_ATTR + print, range_print, + unary_op, range_unary_op, + subscr, range_subscr, + iter, range_getiter + ); diff --git a/py/objreversed.c b/py/objreversed.c index c9b36bf2a4..c66698f028 100644 --- a/py/objreversed.c +++ b/py/objreversed.c @@ -68,15 +68,12 @@ STATIC mp_obj_t reversed_iternext(mp_obj_t self_in) { return mp_obj_subscr(self->seq, MP_OBJ_NEW_SMALL_INT(self->cur_index), MP_OBJ_SENTINEL); } -const mp_obj_type_t mp_type_reversed = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_reversed, - .make_new = reversed_make_new, - MP_TYPE_EXTENDED_FIELDS( - .getiter = mp_identity_getiter, - .iternext = reversed_iternext, - ), -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_reversed, + MP_QSTR_reversed, + MP_TYPE_FLAG_ITER_IS_ITERNEXT, + make_new, reversed_make_new, + iter, reversed_iternext + ); #endif // MICROPY_PY_BUILTINS_REVERSED diff --git a/py/objset.c b/py/objset.c index 9557ec76ab..4965eca3b0 100644 --- a/py/objset.c +++ b/py/objset.c @@ -31,8 +31,6 @@ #include "py/runtime.h" #include "py/builtin.h" -#include "supervisor/shared/translate/translate.h" - #if MICROPY_PY_BUILTINS_SET typedef struct _mp_obj_set_t { @@ -542,19 +540,17 @@ STATIC const mp_rom_map_elem_t set_locals_dict_table[] = { }; STATIC MP_DEFINE_CONST_DICT(set_locals_dict, set_locals_dict_table); -const mp_obj_type_t mp_type_set = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_set, - .print = set_print, - .make_new = set_make_new, - .locals_dict = (mp_obj_dict_t *)&set_locals_dict, - MP_TYPE_EXTENDED_FIELDS( - .unary_op = set_unary_op, - .binary_op = set_binary_op, - .getiter = set_getiter, - ), -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_set, + MP_QSTR_set, + MP_TYPE_FLAG_ITER_IS_GETITER, + make_new, set_make_new, + print, set_print, + unary_op, set_unary_op, + binary_op, set_binary_op, + iter, set_getiter, + locals_dict, &set_locals_dict + ); #if MICROPY_PY_BUILTINS_FROZENSET STATIC const mp_rom_map_elem_t frozenset_locals_dict_table[] = { @@ -570,19 +566,17 @@ STATIC const mp_rom_map_elem_t frozenset_locals_dict_table[] = { }; STATIC MP_DEFINE_CONST_DICT(frozenset_locals_dict, frozenset_locals_dict_table); -const mp_obj_type_t mp_type_frozenset = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_frozenset, - .print = set_print, - .make_new = set_make_new, - .locals_dict = (mp_obj_dict_t *)&frozenset_locals_dict, - MP_TYPE_EXTENDED_FIELDS( - .unary_op = set_unary_op, - .binary_op = set_binary_op, - .getiter = set_getiter, - ), -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_frozenset, + MP_QSTR_frozenset, + MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_ITER_IS_GETITER, + make_new, set_make_new, + print, set_print, + unary_op, set_unary_op, + binary_op, set_binary_op, + iter, set_getiter, + locals_dict, &frozenset_locals_dict + ); #endif mp_obj_t mp_obj_new_set(size_t n_args, mp_obj_t *items) { diff --git a/py/objsingleton.c b/py/objsingleton.c index 34a6ebd028..dc73d28c27 100644 --- a/py/objsingleton.c +++ b/py/objsingleton.c @@ -43,15 +43,11 @@ STATIC void singleton_print(const mp_print_t *print, mp_obj_t self_in, mp_print_ mp_printf(print, "%q", self->name); } -const mp_obj_type_t mp_type_singleton = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_, - .print = singleton_print, - MP_TYPE_EXTENDED_FIELDS( - .unary_op = mp_generic_unary_op, - ), -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_singleton, MP_QSTR_, MP_TYPE_FLAG_NONE, + print, singleton_print, + unary_op, mp_generic_unary_op + ); const mp_obj_singleton_t mp_const_ellipsis_obj = {{&mp_type_singleton}, MP_QSTR_Ellipsis}; #if MICROPY_PY_BUILTINS_NOTIMPLEMENTED diff --git a/py/objslice.c b/py/objslice.c index fe272cfa9b..1ec7532f9b 100644 --- a/py/objslice.c +++ b/py/objslice.c @@ -30,8 +30,6 @@ #include "py/obj.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" - /******************************************************************************/ /* slice object */ @@ -122,19 +120,21 @@ STATIC const mp_rom_map_elem_t slice_locals_dict_table[] = { STATIC MP_DEFINE_CONST_DICT(slice_locals_dict, slice_locals_dict_table); #endif -const mp_obj_type_t mp_type_slice = { - { &mp_type_type }, - .name = MP_QSTR_slice, - .print = slice_print, - #if MICROPY_PY_BUILTINS_SLICE_INDICES || MICROPY_PY_BUILTINS_SLICE_ATTRS - .make_new = slice_make_new, - #endif - #if MICROPY_PY_BUILTINS_SLICE_ATTRS - .attr = slice_attr, - #elif MICROPY_PY_BUILTINS_SLICE_INDICES - .locals_dict = (mp_obj_dict_t *)&slice_locals_dict, - #endif -}; +#if MICROPY_PY_BUILTINS_SLICE_ATTRS +#define SLICE_TYPE_ATTR_OR_LOCALS_DICT attr, slice_attr, +#elif MICROPY_PY_BUILTINS_SLICE_INDICES +#define SLICE_TYPE_ATTR_OR_LOCALS_DICT locals_dict, &slice_locals_dict, +#else +#define SLICE_TYPE_ATTR_OR_LOCALS_DICT +#endif + +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_slice, + MP_QSTR_slice, + MP_TYPE_FLAG_NONE, + SLICE_TYPE_ATTR_OR_LOCALS_DICT + print, slice_print + ); mp_obj_t mp_obj_new_slice(mp_obj_t ostart, mp_obj_t ostop, mp_obj_t ostep) { mp_obj_slice_t *o = mp_obj_malloc(mp_obj_slice_t, &mp_type_slice); diff --git a/py/objstr.c b/py/objstr.c index 72d44d537c..ee2f3f56a4 100644 --- a/py/objstr.c +++ b/py/objstr.c @@ -31,12 +31,9 @@ #include "py/unicode.h" #include "py/objstr.h" #include "py/objlist.h" -#include "py/objtype.h" #include "py/runtime.h" #include "py/stackctrl.h" -#include "supervisor/shared/translate/translate.h" - #if MICROPY_PY_BUILTINS_STR_OP_MODULO STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, size_t n_args, const mp_obj_t *args, mp_obj_t dict); #endif @@ -44,11 +41,31 @@ STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, size_t n_args, const mp_obj_ STATIC mp_obj_t mp_obj_new_bytes_iterator(mp_obj_t str, mp_obj_iter_buf_t *iter_buf); STATIC NORETURN void bad_implicit_conversion(mp_obj_t self_in); -const char nibble_to_hex_upper[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'A', 'B', 'C', 'D', 'E', 'F'}; +STATIC mp_obj_t mp_obj_new_str_type_from_vstr(const mp_obj_type_t *type, vstr_t *vstr); -const char nibble_to_hex_lower[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'a', 'b', 'c', 'd', 'e', 'f'}; +STATIC void str_check_arg_type(const mp_obj_type_t *self_type, const mp_obj_t arg) { + // String operations generally need the args type to match the object they're called on, + // e.g. str.find(str), byte.startswith(byte) + // with the exception that bytes may be used for bytearray and vice versa. + const mp_obj_type_t *arg_type = mp_obj_get_type(arg); + + #if MICROPY_PY_BUILTINS_BYTEARRAY + if (arg_type == &mp_type_bytearray) { + arg_type = &mp_type_bytes; + } + if (self_type == &mp_type_bytearray) { + self_type = &mp_type_bytes; + } + #endif + + if (arg_type != self_type) { + bad_implicit_conversion(arg); + } +} + +STATIC void check_is_str_or_bytes(mp_obj_t self_in) { + mp_check_self(mp_obj_is_str_or_bytes(self_in)); +} /******************************************************************************/ /* str */ @@ -142,6 +159,12 @@ STATIC void str_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t } mp_obj_t mp_obj_str_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + #if MICROPY_CPYTHON_COMPAT + if (n_kw != 0) { + mp_arg_error_unimpl_kw(); + } + #endif + mp_arg_check_num(n_args, n_kw, 0, 3, false); switch (n_args) { @@ -153,7 +176,7 @@ mp_obj_t mp_obj_str_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_ mp_print_t print; vstr_init_print(&vstr, 16, &print); mp_obj_print_helper(&print, args[0], PRINT_STR); - return mp_obj_new_str_from_vstr(type, &vstr); + return mp_obj_new_str_type_from_vstr(type, &vstr); } default: // 2 or 3 args @@ -183,11 +206,7 @@ mp_obj_t mp_obj_str_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_ } else { mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ); - #if MICROPY_PY_BUILTINS_STR_UNICODE_CHECK - if (!utf8_check(bufinfo.buf, bufinfo.len)) { - mp_raise_msg(&mp_type_UnicodeError, NULL); - } - #endif + // This will utf-8 check the input. return mp_obj_new_str(bufinfo.buf, bufinfo.len); } } @@ -214,7 +233,11 @@ STATIC mp_obj_t bytes_make_new(const mp_obj_type_t *type_in, size_t n_args, size if (mp_obj_is_str(args[0])) { if (n_args < 2 || n_args > 3) { + #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE goto wrong_args; + #else + mp_raise_TypeError(MP_ERROR_TEXT("string argument without an encoding")); + #endif } GET_STR_DATA_LEN(args[0], str_data, str_len); GET_STR_HASH(args[0], str_hash); @@ -239,9 +262,10 @@ STATIC mp_obj_t bytes_make_new(const mp_obj_type_t *type_in, size_t n_args, size vstr_t vstr; vstr_init_len(&vstr, len); memset(vstr.buf, 0, len); - return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); + return mp_obj_new_bytes_from_vstr(&mp_type_bytes, &vstr); } + // CIRCUITPY // check if __bytes__ exists, and if so delegate to it mp_obj_t dest[2]; mp_load_method_maybe(args[0], MP_QSTR___bytes__, dest); @@ -278,7 +302,7 @@ STATIC mp_obj_t bytes_make_new(const mp_obj_type_t *type_in, size_t n_args, size vstr_add_byte(&vstr, val); } - return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); + return mp_obj_new_bytes_from_vstr(&vstr); wrong_args: mp_raise_TypeError(MP_ERROR_TEXT("wrong number of arguments")); @@ -350,6 +374,7 @@ mp_obj_t mp_obj_str_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i return mp_const_empty_bytes; } } + // CIRCUITPY: more careful checking of length size_t new_len = mp_seq_multiply_len(lhs_len, n); vstr_t vstr; vstr_init_len(&vstr, new_len); @@ -405,7 +430,7 @@ mp_obj_t mp_obj_str_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i vstr_init_len(&vstr, lhs_len + rhs_len); memcpy(vstr.buf, lhs_data, lhs_len); memcpy(vstr.buf + lhs_len, rhs_data, rhs_len); - return mp_obj_new_str_from_vstr(lhs_type, &vstr); + return mp_obj_new_str_type_from_vstr(lhs_type, &vstr); } case MP_BINARY_OP_CONTAINS: @@ -426,15 +451,6 @@ mp_obj_t mp_obj_str_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i #if !MICROPY_PY_BUILTINS_STR_UNICODE // objstrunicode defines own version -size_t str_offset_to_index(const mp_obj_type_t *type, const byte *self_data, size_t self_len, - size_t offset) { - if (offset > self_len) { - mp_raise_ValueError(MP_ERROR_TEXT("offset out of bounds")); - } - - return offset; -} - const byte *str_index_to_ptr(const mp_obj_type_t *type, const byte *self_data, size_t self_len, mp_obj_t index, bool is_slice) { size_t index_val = mp_get_index(type, self_len, index, is_slice); @@ -470,8 +486,9 @@ STATIC mp_obj_t bytes_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { } STATIC mp_obj_t str_join(mp_obj_t self_in, mp_obj_t arg) { - mp_check_self(mp_obj_is_str_or_bytes(self_in)); + check_is_str_or_bytes(self_in); const mp_obj_type_t *self_type = mp_obj_get_type(self_in); + const mp_obj_type_t *ret_type = self_type; // get separation string GET_STR_DATA_LEN(self_in, sep_str, sep_len); @@ -483,14 +500,25 @@ STATIC mp_obj_t str_join(mp_obj_t self_in, mp_obj_t arg) { if (!mp_obj_is_type(arg, &mp_type_list) && !mp_obj_is_type(arg, &mp_type_tuple)) { // arg is not a list nor a tuple, try to convert it to a list // TODO: Try to optimize? - arg = mp_type_list.make_new(&mp_type_list, 1, 0, &arg); + arg = mp_obj_list_make_new(&mp_type_list, 1, 0, &arg); } mp_obj_get_array(arg, &seq_len, &seq_items); // count required length size_t required_len = 0; + #if MICROPY_PY_BUILTINS_BYTEARRAY + if (self_type == &mp_type_bytearray) { + self_type = &mp_type_bytes; + } + #endif for (size_t i = 0; i < seq_len; i++) { - if (mp_obj_get_type(seq_items[i]) != self_type) { + const mp_obj_type_t *seq_type = mp_obj_get_type(seq_items[i]); + #if MICROPY_PY_BUILTINS_BYTEARRAY + if (seq_type == &mp_type_bytearray) { + seq_type = &mp_type_bytes; + } + #endif + if (seq_type != self_type) { mp_raise_TypeError( MP_ERROR_TEXT("join expects a list of str/bytes objects consistent with self object")); } @@ -516,7 +544,7 @@ STATIC mp_obj_t str_join(mp_obj_t self_in, mp_obj_t arg) { } // return joined string - return mp_obj_new_str_from_vstr(self_type, &vstr); + return mp_obj_new_str_type_from_vstr(ret_type, &vstr); } MP_DEFINE_CONST_FUN_OBJ_2(str_join_obj, str_join); @@ -565,9 +593,7 @@ mp_obj_t mp_obj_str_split(size_t n_args, const mp_obj_t *args) { } else { // sep given - if (mp_obj_get_type(sep) != self_type) { - bad_implicit_conversion(sep); - } + str_check_arg_type(self_type, sep); size_t sep_len; const char *sep_str = mp_obj_str_get_data(sep, &sep_len); @@ -716,12 +742,10 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_rsplit_obj, 1, 3, str_rsplit); STATIC mp_obj_t str_finder(size_t n_args, const mp_obj_t *args, int direction, bool is_index) { const mp_obj_type_t *self_type = mp_obj_get_type(args[0]); - mp_check_self(mp_obj_is_str_or_bytes(args[0])); + check_is_str_or_bytes(args[0]); // check argument type - if (mp_obj_get_type(args[1]) != self_type) { - bad_implicit_conversion(args[1]); - } + str_check_arg_type(self_type, args[1]); GET_STR_DATA_LEN(args[0], haystack, haystack_len); GET_STR_DATA_LEN(args[1], needle, needle_len); @@ -814,7 +838,7 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_endswith_obj, 2, 3, str_endswith); enum { LSTRIP, RSTRIP, STRIP }; STATIC mp_obj_t str_uni_strip(int type, size_t n_args, const mp_obj_t *args) { - mp_check_self(mp_obj_is_str_or_bytes(args[0])); + check_is_str_or_bytes(args[0]); const mp_obj_type_t *self_type = mp_obj_get_type(args[0]); const byte *chars_to_del; @@ -825,9 +849,7 @@ STATIC mp_obj_t str_uni_strip(int type, size_t n_args, const mp_obj_t *args) { chars_to_del = whitespace; chars_to_del_len = sizeof(whitespace) - 1; } else { - if (mp_obj_get_type(args[1]) != self_type) { - bad_implicit_conversion(args[1]); - } + str_check_arg_type(self_type, args[1]); GET_STR_DATA_LEN(args[1], s, l); chars_to_del = s; chars_to_del_len = l; @@ -912,7 +934,7 @@ STATIC mp_obj_t str_center(mp_obj_t str_in, mp_obj_t width_in) { memset(vstr.buf, ' ', width); int left = (width - str_len) / 2; memcpy(vstr.buf + left, str, str_len); - return mp_obj_new_str_from_vstr(mp_obj_get_type(str_in), &vstr); + return mp_obj_new_str_type_from_vstr(mp_obj_get_type(str_in), &vstr); } MP_DEFINE_CONST_FUN_OBJ_2(str_center_obj, str_center); #endif @@ -1106,7 +1128,7 @@ STATIC vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *ar arg = key_elem->value; } if (field_name < field_name_top) { - mp_raise_NotImplementedError(MP_ERROR_TEXT("attributes not supported yet")); + mp_raise_NotImplementedError(MP_ERROR_TEXT("attributes not supported")); } } else { if (*arg_i < 0) { @@ -1138,7 +1160,7 @@ STATIC vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *ar mp_print_t arg_print; vstr_init_print(&arg_vstr, 16, &arg_print); mp_obj_print_helper(&arg_print, arg, print_kind); - arg = mp_obj_new_str_from_vstr(&mp_type_str, &arg_vstr); + arg = mp_obj_new_str_type_from_vstr(&mp_type_str, &arg_vstr); } char fill = '\0'; @@ -1418,18 +1440,18 @@ STATIC vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *ar } mp_obj_t mp_obj_str_format(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { - mp_check_self(mp_obj_is_str_or_bytes(args[0])); + check_is_str_or_bytes(args[0]); GET_STR_DATA_LEN(args[0], str, len); int arg_i = 0; vstr_t vstr = mp_obj_str_format_helper((const char *)str, (const char *)str + len, &arg_i, n_args, args, kwargs); - return mp_obj_new_str_from_vstr(mp_obj_get_type(args[0]), &vstr); + return mp_obj_new_str_type_from_vstr(mp_obj_get_type(args[0]), &vstr); } MP_DEFINE_CONST_FUN_OBJ_KW(str_format_obj, 1, mp_obj_str_format); #if MICROPY_PY_BUILTINS_STR_OP_MODULO STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, size_t n_args, const mp_obj_t *args, mp_obj_t dict) { - mp_check_self(mp_obj_is_str_or_bytes(pattern)); + check_is_str_or_bytes(pattern); GET_STR_DATA_LEN(pattern, str, len); #if MICROPY_ERROR_REPORTING > MICROPY_ERROR_REPORTING_TERSE @@ -1628,14 +1650,14 @@ STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, size_t n_args, const mp_obj_ mp_raise_TypeError(MP_ERROR_TEXT("not all arguments converted during string formatting")); } - return mp_obj_new_str_from_vstr(is_bytes ? &mp_type_bytes : &mp_type_str, &vstr); + return mp_obj_new_str_type_from_vstr(is_bytes ? &mp_type_bytes : &mp_type_str, &vstr); } #endif // The implementation is optimized, returning the original string if there's // nothing to replace. STATIC mp_obj_t str_replace(size_t n_args, const mp_obj_t *args) { - mp_check_self(mp_obj_is_str_or_bytes(args[0])); + check_is_str_or_bytes(args[0]); mp_int_t max_rep = -1; if (n_args == 4) { @@ -1653,13 +1675,8 @@ STATIC mp_obj_t str_replace(size_t n_args, const mp_obj_t *args) { const mp_obj_type_t *self_type = mp_obj_get_type(args[0]); - if (mp_obj_get_type(args[1]) != self_type) { - bad_implicit_conversion(args[1]); - } - - if (mp_obj_get_type(args[2]) != self_type) { - bad_implicit_conversion(args[2]); - } + str_check_arg_type(self_type, args[1]); + str_check_arg_type(self_type, args[2]); // extract string data @@ -1736,19 +1753,17 @@ STATIC mp_obj_t str_replace(size_t n_args, const mp_obj_t *args) { } } - return mp_obj_new_str_from_vstr(self_type, &vstr); + return mp_obj_new_str_type_from_vstr(self_type, &vstr); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_replace_obj, 3, 4, str_replace); #if MICROPY_PY_BUILTINS_STR_COUNT STATIC mp_obj_t str_count(size_t n_args, const mp_obj_t *args) { const mp_obj_type_t *self_type = mp_obj_get_type(args[0]); - mp_check_self(mp_obj_is_str_or_bytes(args[0])); + check_is_str_or_bytes(args[0]); // check argument type - if (mp_obj_get_type(args[1]) != self_type) { - bad_implicit_conversion(args[1]); - } + str_check_arg_type(self_type, args[1]); GET_STR_DATA_LEN(args[0], haystack, haystack_len); GET_STR_DATA_LEN(args[1], needle, needle_len); @@ -1767,6 +1782,8 @@ STATIC mp_obj_t str_count(size_t n_args, const mp_obj_t *args) { return MP_OBJ_NEW_SMALL_INT(utf8_charlen(start, end - start) + 1); } + bool is_str = self_type == &mp_type_str; + // count the occurrences mp_int_t num_occurrences = 0; for (const byte *haystack_ptr = start; haystack_ptr + needle_len <= end;) { @@ -1774,7 +1791,7 @@ STATIC mp_obj_t str_count(size_t n_args, const mp_obj_t *args) { num_occurrences++; haystack_ptr += needle_len; } else { - haystack_ptr = utf8_next_char(haystack_ptr); + haystack_ptr = is_str ? utf8_next_char(haystack_ptr) : haystack_ptr + 1; } } @@ -1785,11 +1802,9 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_count_obj, 2, 4, str_count); #if MICROPY_PY_BUILTINS_STR_PARTITION STATIC mp_obj_t str_partitioner(mp_obj_t self_in, mp_obj_t arg, int direction) { - mp_check_self(mp_obj_is_str_or_bytes(self_in)); + check_is_str_or_bytes(self_in); const mp_obj_type_t *self_type = mp_obj_get_type(self_in); - if (self_type != mp_obj_get_type(arg)) { - bad_implicit_conversion(arg); - } + str_check_arg_type(self_type, arg); GET_STR_DATA_LEN(self_in, str, str_len); GET_STR_DATA_LEN(arg, sep, sep_len); @@ -1815,6 +1830,12 @@ STATIC mp_obj_t str_partitioner(mp_obj_t self_in, mp_obj_t arg, int direction) { result[2] = self_in; } + #if MICROPY_PY_BUILTINS_BYTEARRAY + if (mp_obj_get_type(arg) != self_type) { + arg = mp_obj_new_str_of_type(self_type, sep, sep_len); + } + #endif + const byte *position_ptr = find_subbytes(str, str_len, sep, sep_len, direction); if (position_ptr != NULL) { size_t position = position_ptr - str; @@ -1846,7 +1867,7 @@ STATIC mp_obj_t str_caseconv(unichar (*op)(unichar), mp_obj_t self_in) { for (size_t i = 0; i < self_len; i++) { *data++ = op(*self_data++); } - return mp_obj_new_str_from_vstr(mp_obj_get_type(self_in), &vstr); + return mp_obj_new_str_type_from_vstr(mp_obj_get_type(self_in), &vstr); } STATIC mp_obj_t str_lower(mp_obj_t self_in) { @@ -1947,6 +1968,84 @@ STATIC mp_obj_t str_encode(size_t n_args, const mp_obj_t *args) { MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_encode_obj, 1, 3, str_encode); #endif +#if MICROPY_PY_BUILTINS_BYTES_HEX +mp_obj_t mp_obj_bytes_hex(size_t n_args, const mp_obj_t *args, const mp_obj_type_t *type) { + // First argument is the data to convert. + // Second argument is an optional separator to be used between values. + const char *sep = NULL; + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ); + + // Code below assumes non-zero buffer length when computing size with + // separator, so handle the zero-length case here. + if (bufinfo.len == 0) { + return mp_const_empty_bytes; + } + + vstr_t vstr; + size_t out_len = bufinfo.len * 2; + if (n_args > 1) { + // 1-char separator between hex numbers + out_len += bufinfo.len - 1; + sep = mp_obj_str_get_str(args[1]); + } + vstr_init_len(&vstr, out_len); + byte *in = bufinfo.buf, *out = (byte *)vstr.buf; + for (mp_uint_t i = bufinfo.len; i--;) { + byte d = (*in >> 4); + if (d > 9) { + d += 'a' - '9' - 1; + } + *out++ = d + '0'; + d = (*in++ & 0xf); + if (d > 9) { + d += 'a' - '9' - 1; + } + *out++ = d + '0'; + if (sep != NULL && i != 0) { + *out++ = *sep; + } + } + return mp_obj_new_str_type_from_vstr(type, &vstr); +} + +mp_obj_t mp_obj_bytes_fromhex(mp_obj_t type_in, mp_obj_t data) { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_READ); + + if ((bufinfo.len & 1) != 0) { + mp_raise_ValueError(MP_ERROR_TEXT("odd-length string")); + } + vstr_t vstr; + vstr_init_len(&vstr, bufinfo.len / 2); + byte *in = bufinfo.buf, *out = (byte *)vstr.buf; + byte hex_byte = 0; + for (mp_uint_t i = bufinfo.len; i--;) { + byte hex_ch = *in++; + if (unichar_isxdigit(hex_ch)) { + hex_byte += unichar_xdigit_value(hex_ch); + } else { + mp_raise_ValueError(MP_ERROR_TEXT("non-hex digit found")); + } + if (i & 1) { + hex_byte <<= 4; + } else { + *out++ = hex_byte; + hex_byte = 0; + } + } + return mp_obj_new_str_type_from_vstr(MP_OBJ_TO_PTR(type_in), &vstr); +} + +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); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(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); +STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(bytes_fromhex_classmethod_obj, MP_ROM_PTR(&bytes_fromhex_obj)); +#endif // MICROPY_PY_BUILTINS_BYTES_HEX + mp_int_t mp_obj_str_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { if (flags == MP_BUFFER_READ) { GET_STR_DATA_LEN(self_in, str_data, str_len); @@ -1960,17 +2059,25 @@ mp_int_t mp_obj_str_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_u } } -STATIC const mp_rom_map_elem_t str8_locals_dict_table[] = { +void mp_obj_str_set_data(mp_obj_str_t *str, const byte *data, size_t len) { + str->data = data; + str->len = len; + str->hash = qstr_compute_hash(data, len); +} + +// This locals table is used for the following types: str, bytes, bytearray, array.array. +// Each type takes a different section (start to end offset) of this table. +STATIC const mp_rom_map_elem_t array_bytearray_str_bytes_locals_table[] = { + #if MICROPY_PY_ARRAY || MICROPY_PY_BUILTINS_BYTEARRAY + { MP_ROM_QSTR(MP_QSTR_append), MP_ROM_PTR(&mp_obj_array_append_obj) }, + { MP_ROM_QSTR(MP_QSTR_extend), MP_ROM_PTR(&mp_obj_array_extend_obj) }, + #endif + #if MICROPY_PY_BUILTINS_BYTES_HEX + { MP_ROM_QSTR(MP_QSTR_hex), MP_ROM_PTR(&bytes_hex_as_str_obj) }, + { MP_ROM_QSTR(MP_QSTR_fromhex), MP_ROM_PTR(&bytes_fromhex_classmethod_obj) }, + #endif #if MICROPY_CPYTHON_COMPAT { MP_ROM_QSTR(MP_QSTR_decode), MP_ROM_PTR(&bytes_decode_obj) }, - #if !MICROPY_PY_BUILTINS_STR_UNICODE - // If we have separate unicode type, then here we have methods only - // for bytes type, and it should not have encode() methods. Otherwise, - // we have non-compliant-but-practical bytestring type, which shares - // method table with bytes, so they both have encode() and decode() - // methods (which should do type checking at runtime). - { MP_ROM_QSTR(MP_QSTR_encode), MP_ROM_PTR(&str_encode_obj) }, - #endif #endif { MP_ROM_QSTR(MP_QSTR_find), MP_ROM_PTR(&str_find_obj) }, { MP_ROM_QSTR(MP_QSTR_rfind), MP_ROM_PTR(&str_rfind_obj) }, @@ -2006,41 +2113,89 @@ STATIC const mp_rom_map_elem_t str8_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_isdigit), MP_ROM_PTR(&str_isdigit_obj) }, { MP_ROM_QSTR(MP_QSTR_isupper), MP_ROM_PTR(&str_isupper_obj) }, { MP_ROM_QSTR(MP_QSTR_islower), MP_ROM_PTR(&str_islower_obj) }, + #if MICROPY_CPYTHON_COMPAT + { MP_ROM_QSTR(MP_QSTR_encode), MP_ROM_PTR(&str_encode_obj) }, + #endif }; -STATIC MP_DEFINE_CONST_DICT(str8_locals_dict, str8_locals_dict_table); +#if MICROPY_CPYTHON_COMPAT +#define TABLE_ENTRIES_COMPAT 1 +#else +#define TABLE_ENTRIES_COMPAT 0 +#endif + +#if MICROPY_PY_BUILTINS_BYTES_HEX +#define TABLE_ENTRIES_HEX 2 +#else +#define TABLE_ENTRIES_HEX 0 +#endif + +#if MICROPY_PY_ARRAY || MICROPY_PY_BUILTINS_BYTEARRAY +#define TABLE_ENTRIES_ARRAY 2 +#else +#define TABLE_ENTRIES_ARRAY 0 +#endif + +MP_DEFINE_CONST_DICT_WITH_SIZE(mp_obj_str_locals_dict, + array_bytearray_str_bytes_locals_table + TABLE_ENTRIES_ARRAY + TABLE_ENTRIES_HEX + TABLE_ENTRIES_COMPAT, + MP_ARRAY_SIZE(array_bytearray_str_bytes_locals_table) - (TABLE_ENTRIES_ARRAY + TABLE_ENTRIES_HEX + TABLE_ENTRIES_COMPAT)); + +#if TABLE_ENTRIES_COMPAT == 0 +#define mp_obj_bytes_locals_dict mp_obj_str_locals_dict +#else +MP_DEFINE_CONST_DICT_WITH_SIZE(mp_obj_bytes_locals_dict, + array_bytearray_str_bytes_locals_table + TABLE_ENTRIES_ARRAY, + MP_ARRAY_SIZE(array_bytearray_str_bytes_locals_table) - (TABLE_ENTRIES_ARRAY + TABLE_ENTRIES_COMPAT)); +#endif + +#if MICROPY_PY_BUILTINS_BYTEARRAY +MP_DEFINE_CONST_DICT_WITH_SIZE(mp_obj_bytearray_locals_dict, + array_bytearray_str_bytes_locals_table, + MP_ARRAY_SIZE(array_bytearray_str_bytes_locals_table) - TABLE_ENTRIES_COMPAT); +#endif + +#if MICROPY_PY_ARRAY +MP_DEFINE_CONST_DICT_WITH_SIZE(mp_obj_array_locals_dict, + array_bytearray_str_bytes_locals_table, + TABLE_ENTRIES_ARRAY); +#endif + +#if MICROPY_PY_BUILTINS_MEMORYVIEW && MICROPY_PY_BUILTINS_BYTES_HEX +MP_DEFINE_CONST_DICT_WITH_SIZE(mp_obj_memoryview_locals_dict, + array_bytearray_str_bytes_locals_table + TABLE_ENTRIES_ARRAY, + 1); // Just the "hex" entry. +#endif #if !MICROPY_PY_BUILTINS_STR_UNICODE STATIC mp_obj_t mp_obj_new_str_iterator(mp_obj_t str, mp_obj_iter_buf_t *iter_buf); -const mp_obj_type_t mp_type_str = { - { &mp_type_type }, - .name = MP_QSTR_str, - .print = str_print, - .make_new = mp_obj_str_make_new, - .binary_op = mp_obj_str_binary_op, - .subscr = bytes_subscr, - .getiter = mp_obj_new_str_iterator, - .buffer_p = { .get_buffer = mp_obj_str_get_buffer }, - .locals_dict = (mp_obj_dict_t *)&str8_locals_dict, -}; -#endif +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_str, + MP_QSTR_str, + MP_TYPE_FLAG_NONE, + make_new, mp_obj_str_make_new, + print, str_print, + binary_op, mp_obj_str_binary_op, + subscr, bytes_subscr, + iter, mp_obj_new_str_iterator, + buffer, mp_obj_str_get_buffer, + locals_dict, &mp_obj_str_locals_dict + ); +#endif // !MICROPY_PY_BUILTINS_STR_UNICODE -// Reuses most of methods from str -const mp_obj_type_t mp_type_bytes = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_bytes, - .print = str_print, - .make_new = bytes_make_new, - .locals_dict = (mp_obj_dict_t *)&str8_locals_dict, - MP_TYPE_EXTENDED_FIELDS( - .binary_op = mp_obj_str_binary_op, - .subscr = bytes_subscr, - .getiter = mp_obj_new_bytes_iterator, - .buffer_p = { .get_buffer = mp_obj_str_get_buffer }, - ), -}; +// Reuses most methods from str +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_bytes, + MP_QSTR_bytes, + MP_TYPE_FLAG_NONE, + make_new, bytes_make_new, + print, str_print, + binary_op, mp_obj_str_binary_op, + subscr, bytes_subscr, + iter, mp_obj_new_bytes_iterator, + buffer, mp_obj_str_get_buffer, + locals_dict, &mp_obj_bytes_locals_dict + ); // The zero-length bytes object, with data that includes a null-terminating byte const mp_obj_str_t mp_const_empty_bytes_obj = {{&mp_type_bytes}, 0, 0, (const byte *)""}; @@ -2067,6 +2222,10 @@ mp_obj_t mp_obj_new_str_copy(const mp_obj_type_t *type, const byte *data, size_t mp_obj_t mp_obj_new_str_of_type(const mp_obj_type_t *type, const byte *data, size_t len) { if (type == &mp_type_str) { return mp_obj_new_str((const char *)data, len); + #if MICROPY_PY_BUILTINS_BYTEARRAY + } else if (type == &mp_type_bytearray) { + return mp_obj_new_bytearray(len, data); + #endif } else { return mp_obj_new_bytes(data, len); } @@ -2080,7 +2239,7 @@ mp_obj_t mp_obj_new_str_via_qstr(const char *data, size_t len) { // Create a str/bytes object from the given vstr. The vstr buffer is resized to // the exact length required and then reused for the str/bytes object. The vstr // is cleared and can safely be passed to vstr_free if it was heap allocated. -mp_obj_t mp_obj_new_str_from_vstr(const mp_obj_type_t *type, vstr_t *vstr) { +STATIC mp_obj_t mp_obj_new_str_type_from_vstr(const mp_obj_type_t *type, vstr_t *vstr) { // if not a bytes object, look if a qstr with this data already exists if (type == &mp_type_str) { qstr q = qstr_find_strn(vstr->buf, vstr->len); @@ -2091,22 +2250,53 @@ mp_obj_t mp_obj_new_str_from_vstr(const mp_obj_type_t *type, vstr_t *vstr) { } } - // make a new str/bytes object - mp_obj_str_t *o = mp_obj_malloc(mp_obj_str_t, type); - o->len = vstr->len; - o->hash = qstr_compute_hash((byte *)vstr->buf, vstr->len); + byte *data; if (vstr->len + 1 == vstr->alloc) { - o->data = (byte *)vstr->buf; + data = (byte *)vstr->buf; } else { - o->data = (byte *)m_renew(char, vstr->buf, vstr->alloc, vstr->len + 1); + data = (byte *)m_renew(char, vstr->buf, vstr->alloc, vstr->len + 1); } - ((byte *)o->data)[o->len] = '\0'; // add null byte + data[vstr->len] = '\0'; // add null byte vstr->buf = NULL; vstr->alloc = 0; + #if MICROPY_PY_BUILTINS_BYTEARRAY + if (type == &mp_type_bytearray) { + return mp_obj_new_bytearray_by_ref(vstr->len, data); + } + #endif + mp_obj_str_t *o = mp_obj_malloc(mp_obj_str_t, type); + o->len = vstr->len; + o->hash = qstr_compute_hash(data, vstr->len); + o->data = data; return MP_OBJ_FROM_PTR(o); } +mp_obj_t mp_obj_new_str_from_vstr(vstr_t *vstr) { + #if MICROPY_PY_BUILTINS_STR_UNICODE && MICROPY_PY_BUILTINS_STR_UNICODE_CHECK + if (!utf8_check((byte *)vstr->buf, vstr->len)) { + mp_raise_msg(&mp_type_UnicodeError, NULL); + } + #endif // MICROPY_PY_BUILTINS_STR_UNICODE && MICROPY_PY_BUILTINS_STR_UNICODE_CHECK + return mp_obj_new_str_type_from_vstr(&mp_type_str, vstr); +} + +#if MICROPY_PY_BUILTINS_STR_UNICODE && MICROPY_PY_BUILTINS_STR_UNICODE_CHECK +mp_obj_t mp_obj_new_str_from_utf8_vstr(vstr_t *vstr) { + // bypasses utf8_check. + return mp_obj_new_str_type_from_vstr(&mp_type_str, vstr); +} +#endif // MICROPY_PY_BUILTINS_STR_UNICODE && MICROPY_PY_BUILTINS_STR_UNICODE_CHECK + +mp_obj_t mp_obj_new_bytes_from_vstr(vstr_t *vstr) { + return mp_obj_new_str_type_from_vstr(&mp_type_bytes, vstr); +} + mp_obj_t mp_obj_new_str(const char *data, size_t len) { + #if MICROPY_PY_BUILTINS_STR_UNICODE && MICROPY_PY_BUILTINS_STR_UNICODE_CHECK + if (!utf8_check((byte *)data, len)) { + mp_raise_msg(&mp_type_UnicodeError, NULL); + } + #endif qstr q = qstr_find_strn(data, len); if (q != MP_QSTRnull) { // qstr with this data already exists @@ -2132,14 +2322,6 @@ mp_obj_t mp_obj_new_bytes(const byte *data, size_t len) { return mp_obj_new_str_copy(&mp_type_bytes, data, len); } -mp_obj_t mp_obj_new_bytes_of_zeros(size_t len) { - vstr_t vstr; - vstr_init_len(&vstr, len); - memset(vstr.buf, 0, len); - return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); -} - - bool mp_obj_str_equal(mp_obj_t s1, mp_obj_t s2) { if (mp_obj_is_qstr(s1) && mp_obj_is_qstr(s2)) { return s1 == s2; @@ -2174,7 +2356,7 @@ STATIC NORETURN void bad_implicit_conversion(mp_obj_t self_in) { qstr mp_obj_str_get_qstr(mp_obj_t self_in) { if (mp_obj_is_qstr(self_in)) { return MP_OBJ_QSTR_VALUE(self_in); - } else if (mp_obj_is_type(self_in, &mp_type_str)) { + } else if (mp_obj_is_exact_type(self_in, &mp_type_str)) { mp_obj_str_t *self = MP_OBJ_TO_PTR(self_in); return qstr_from_strn((char *)self->data, self->len); } else { @@ -2209,6 +2391,7 @@ const byte *mp_obj_str_get_data_no_check(mp_obj_t self_in, size_t *len) { if (mp_obj_is_qstr(self_in)) { return qstr_data(MP_OBJ_QSTR_VALUE(self_in), len); } else { + MP_STATIC_ASSERT_STR_ARRAY_COMPATIBLE; *len = ((mp_obj_str_t *)MP_OBJ_TO_PTR(self_in))->len; return ((mp_obj_str_t *)MP_OBJ_TO_PTR(self_in))->data; } diff --git a/py/objstr.h b/py/objstr.h index bae32cbffe..72fe1cfef0 100644 --- a/py/objstr.h +++ b/py/objstr.h @@ -27,86 +27,94 @@ #define MICROPY_INCLUDED_PY_OBJSTR_H #include "py/obj.h" +#include "py/objarray.h" typedef struct _mp_obj_str_t { mp_obj_base_t base; - mp_uint_t hash; + size_t hash; // len == number of bytes used in data, alloc = len + 1 because (at the moment) we also append a null byte size_t len; const byte *data; } mp_obj_str_t; +// This static assert is used to ensure that mp_obj_str_t and mp_obj_array_t are compatible, +// meaning that their len and data/items entries are at the same offsets in the struct. +// This allows the same code to be used for str/bytes and bytearray. +#define MP_STATIC_ASSERT_STR_ARRAY_COMPATIBLE \ + MP_STATIC_ASSERT(offsetof(mp_obj_str_t, len) == offsetof(mp_obj_array_t, len) \ + && offsetof(mp_obj_str_t, data) == offsetof(mp_obj_array_t, items)) + #define MP_DEFINE_STR_OBJ(obj_name, str) mp_obj_str_t obj_name = {{&mp_type_str}, 0, sizeof(str) - 1, (const byte *)str} // use this macro to extract the string hash // warning: the hash can be 0, meaning invalid, and must then be explicitly computed from the data #define GET_STR_HASH(str_obj_in, str_hash) \ - mp_uint_t str_hash; if (mp_obj_is_qstr(str_obj_in)) \ - { str_hash = qstr_hash(MP_OBJ_QSTR_VALUE(str_obj_in)); } else { str_hash = ((mp_obj_str_t *)MP_OBJ_TO_PTR(str_obj_in))->hash; } + size_t str_hash; \ + if (mp_obj_is_qstr(str_obj_in)) { \ + str_hash = qstr_hash(MP_OBJ_QSTR_VALUE(str_obj_in)); \ + } else { \ + str_hash = ((mp_obj_str_t *)MP_OBJ_TO_PTR(str_obj_in))->hash; \ + } // use this macro to extract the string length #define GET_STR_LEN(str_obj_in, str_len) \ - size_t str_len; if (mp_obj_is_qstr(str_obj_in)) \ - { str_len = qstr_len(MP_OBJ_QSTR_VALUE(str_obj_in)); } else { str_len = ((mp_obj_str_t *)MP_OBJ_TO_PTR(str_obj_in))->len; } + size_t str_len; \ + if (mp_obj_is_qstr(str_obj_in)) { \ + str_len = qstr_len(MP_OBJ_QSTR_VALUE(str_obj_in)); \ + } else { \ + str_len = ((mp_obj_str_t *)MP_OBJ_TO_PTR(str_obj_in))->len; \ + } // use this macro to extract the string data and length #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C || MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D const byte *mp_obj_str_get_data_no_check(mp_obj_t self_in, size_t *len); #define GET_STR_DATA_LEN(str_obj_in, str_data, str_len) \ - size_t str_len; const byte *str_data = mp_obj_str_get_data_no_check(str_obj_in, &str_len); + size_t str_len; \ + const byte *str_data = mp_obj_str_get_data_no_check(str_obj_in, &str_len); #else #define GET_STR_DATA_LEN(str_obj_in, str_data, str_len) \ - const byte *str_data; size_t str_len; if (mp_obj_is_qstr(str_obj_in)) \ - { str_data = qstr_data(MP_OBJ_QSTR_VALUE(str_obj_in), &str_len); } \ - else { str_len = ((mp_obj_str_t *)MP_OBJ_TO_PTR(str_obj_in))->len; str_data = ((mp_obj_str_t *)MP_OBJ_TO_PTR(str_obj_in))->data; } + const byte *str_data; \ + size_t str_len; \ + if (mp_obj_is_qstr(str_obj_in)) { \ + str_data = qstr_data(MP_OBJ_QSTR_VALUE(str_obj_in), &str_len); \ + } else { \ + MP_STATIC_ASSERT_STR_ARRAY_COMPATIBLE; \ + str_len = ((mp_obj_str_t *)MP_OBJ_TO_PTR(str_obj_in))->len; \ + str_data = ((mp_obj_str_t *)MP_OBJ_TO_PTR(str_obj_in))->data; \ + } #endif mp_obj_t mp_obj_str_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args); void mp_str_print_json(const mp_print_t *print, const byte *str_data, size_t str_len); mp_obj_t mp_obj_str_format(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs); mp_obj_t mp_obj_str_split(size_t n_args, const mp_obj_t *args); -mp_obj_t mp_obj_new_str_copy(const mp_obj_type_t *type, const byte *data, size_t len); -mp_obj_t mp_obj_new_str_of_type(const mp_obj_type_t *type, const byte *data, size_t len); +mp_obj_t mp_obj_new_str_copy(const mp_obj_type_t *type, const byte *data, size_t len); // for type=str, input data must be valid utf-8 +mp_obj_t mp_obj_new_str_of_type(const mp_obj_type_t *type, const byte *data, size_t len); // for type=str, will check utf-8 (raises UnicodeError) mp_obj_t mp_obj_str_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in); mp_int_t mp_obj_str_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags); -size_t str_offset_to_index(const mp_obj_type_t *type, const byte *self_data, size_t self_len, - size_t offset); +void mp_obj_str_set_data(mp_obj_str_t *str, const byte *data, size_t len); + const byte *str_index_to_ptr(const mp_obj_type_t *type, const byte *self_data, size_t self_len, mp_obj_t index, bool is_slice); const byte *find_subbytes(const byte *haystack, size_t hlen, const byte *needle, size_t nlen, int direction); -extern const char nibble_to_hex_upper[16]; -extern const char nibble_to_hex_lower[16]; +mp_obj_t mp_obj_bytes_hex(size_t n_args, const mp_obj_t *args, const mp_obj_type_t *type); +mp_obj_t mp_obj_bytes_fromhex(mp_obj_t type_in, mp_obj_t data); -MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(str_encode_obj); -MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(str_find_obj); -MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(str_rfind_obj); -MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(str_index_obj); -MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(str_rindex_obj); -MP_DECLARE_CONST_FUN_OBJ_2(str_join_obj); -MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(str_split_obj); -MP_DECLARE_CONST_FUN_OBJ_KW(str_splitlines_obj); -MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(str_rsplit_obj); -MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(str_startswith_obj); -MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(str_endswith_obj); -MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(str_strip_obj); -MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(str_lstrip_obj); -MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(str_rstrip_obj); -MP_DECLARE_CONST_FUN_OBJ_KW(str_format_obj); -MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(str_replace_obj); -MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(str_count_obj); -MP_DECLARE_CONST_FUN_OBJ_2(str_partition_obj); -MP_DECLARE_CONST_FUN_OBJ_2(str_rpartition_obj); -MP_DECLARE_CONST_FUN_OBJ_2(str_center_obj); -MP_DECLARE_CONST_FUN_OBJ_1(str_lower_obj); -MP_DECLARE_CONST_FUN_OBJ_1(str_upper_obj); -MP_DECLARE_CONST_FUN_OBJ_1(str_isspace_obj); -MP_DECLARE_CONST_FUN_OBJ_1(str_isalpha_obj); -MP_DECLARE_CONST_FUN_OBJ_1(str_isdigit_obj); -MP_DECLARE_CONST_FUN_OBJ_1(str_isupper_obj); -MP_DECLARE_CONST_FUN_OBJ_1(str_islower_obj); -MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(bytes_decode_obj); +extern const mp_obj_dict_t mp_obj_str_locals_dict; + +#if MICROPY_PY_BUILTINS_MEMORYVIEW && MICROPY_PY_BUILTINS_BYTES_HEX +extern const mp_obj_dict_t mp_obj_memoryview_locals_dict; +#endif + +#if MICROPY_PY_BUILTINS_BYTEARRAY +extern const mp_obj_dict_t mp_obj_bytearray_locals_dict; +#endif + +#if MICROPY_PY_ARRAY +extern const mp_obj_dict_t mp_obj_array_locals_dict; +#endif #endif // MICROPY_INCLUDED_PY_OBJSTR_H diff --git a/py/objstringio.c b/py/objstringio.c index 563dd149bc..1a083449ba 100644 --- a/py/objstringio.c +++ b/py/objstringio.c @@ -33,8 +33,6 @@ #include "py/runtime.h" #include "py/stream.h" -#include "supervisor/shared/translate/translate.h" - #if MICROPY_PY_IO #if MICROPY_CPYTHON_COMPAT @@ -186,7 +184,7 @@ STATIC mp_obj_stringio_t *stringio_new(const mp_obj_type_t *type) { } STATIC mp_obj_t stringio_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { - mp_arg_check_num(n_args, n_kw, 0, 1, false); + (void)n_kw; // TODO check n_kw==0 mp_uint_t sz = 16; bool initdata = false; @@ -195,18 +193,22 @@ STATIC mp_obj_t stringio_make_new(const mp_obj_type_t *type_in, size_t n_args, s mp_obj_stringio_t *o = stringio_new(type_in); if (n_args > 0) { - mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ); + if (mp_obj_is_int(args[0])) { + sz = mp_obj_get_int(args[0]); + } else { + mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ); - if (mp_obj_is_str_or_bytes(args[0])) { - o->vstr = m_new_obj(vstr_t); - vstr_init_fixed_buf(o->vstr, bufinfo.len, bufinfo.buf); - o->vstr->len = bufinfo.len; - o->ref_obj = args[0]; - return MP_OBJ_FROM_PTR(o); + if (mp_obj_is_str_or_bytes(args[0])) { + o->vstr = m_new_obj(vstr_t); + vstr_init_fixed_buf(o->vstr, bufinfo.len, bufinfo.buf); + o->vstr->len = bufinfo.len; + o->ref_obj = args[0]; + return MP_OBJ_FROM_PTR(o); + } + + sz = bufinfo.len; + initdata = true; } - - sz = bufinfo.len; - initdata = true; } o->vstr = vstr_new(sz); @@ -236,48 +238,38 @@ STATIC const mp_rom_map_elem_t stringio_locals_dict_table[] = { STATIC MP_DEFINE_CONST_DICT(stringio_locals_dict, stringio_locals_dict_table); STATIC const mp_stream_p_t stringio_stream_p = { - MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream) .read = stringio_read, .write = stringio_write, .ioctl = stringio_ioctl, .is_text = true, }; -const mp_obj_type_t mp_type_stringio = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_StringIO, - .print = stringio_print, - .make_new = stringio_make_new, - .locals_dict = (mp_obj_dict_t *)&stringio_locals_dict, - MP_TYPE_EXTENDED_FIELDS( - .getiter = mp_identity_getiter, - .iternext = mp_stream_unbuffered_iter, - .protocol = &stringio_stream_p, - ), -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_stringio, + MP_QSTR_StringIO, + MP_TYPE_FLAG_ITER_IS_STREAM, + make_new, stringio_make_new, + print, stringio_print, + protocol, &stringio_stream_p, + locals_dict, &stringio_locals_dict + ); #if MICROPY_PY_IO_BYTESIO STATIC const mp_stream_p_t bytesio_stream_p = { - MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream) .read = stringio_read, .write = stringio_write, .ioctl = stringio_ioctl, }; -const mp_obj_type_t mp_type_bytesio = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_BytesIO, - .print = stringio_print, - .make_new = stringio_make_new, - .locals_dict = (mp_obj_dict_t *)&stringio_locals_dict, - MP_TYPE_EXTENDED_FIELDS( - .getiter = mp_identity_getiter, - .iternext = mp_stream_unbuffered_iter, - .protocol = &bytesio_stream_p, - ), -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_bytesio, + MP_QSTR_BytesIO, + MP_TYPE_FLAG_ITER_IS_STREAM, + make_new, stringio_make_new, + print, stringio_print, + protocol, &bytesio_stream_p, + locals_dict, &stringio_locals_dict + ); #endif #endif diff --git a/py/objstrunicode.c b/py/objstrunicode.c index 93277e630d..93383b3c19 100644 --- a/py/objstrunicode.c +++ b/py/objstrunicode.c @@ -32,8 +32,6 @@ #include "py/objlist.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" - #if MICROPY_PY_BUILTINS_STR_UNICODE STATIC mp_obj_t mp_obj_new_str_iterator(mp_obj_t str, mp_obj_iter_buf_t *iter_buf); @@ -41,13 +39,6 @@ STATIC mp_obj_t mp_obj_new_str_iterator(mp_obj_t str, mp_obj_iter_buf_t *iter_bu /******************************************************************************/ /* str */ - -// These settings approximate CPython's printability. It is not -// exhaustive and may print "unprintable" characters. All ASCII control codes -// are escaped along with variable space widths and paragraph designators. -// Unlike CPython, we do not escape private use codes or reserved characters. -// We assume that the unicode is well formed. -// CPython policy is documented here: https://github.com/python/cpython/blob/bb3e0c240bc60fe08d332ff5955d54197f79751c/Objects/unicodectype.c#L147-L159 STATIC void uni_print_quoted(const mp_print_t *print, const byte *str_data, uint str_len) { // this escapes characters, but it will be very slow to print (calling print many times) bool has_single_quote = false; @@ -68,26 +59,25 @@ 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); - const byte *start = s; s = utf8_next_char(s); if (ch == quote_char) { mp_printf(print, "\\%c", quote_char); } else if (ch == '\\') { mp_print_str(print, "\\\\"); + } else if (32 <= ch && ch <= 126) { + mp_printf(print, "%c", ch); } else if (ch == '\n') { mp_print_str(print, "\\n"); } else if (ch == '\r') { mp_print_str(print, "\\r"); } else if (ch == '\t') { mp_print_str(print, "\\t"); - } else if (ch <= 0x1f || (0x7f <= ch && ch <= 0xa0) || ch == 0xad) { + } else if (ch < 0x100) { mp_printf(print, "\\x%02x", ch); - } else if ((0x2000 <= ch && ch <= 0x200f) || ch == 0x2028 || ch == 0x2029) { + } else if (ch < 0x10000) { mp_printf(print, "\\u%04x", ch); } else { - // Print the full character out. - int width = s - start; - mp_print_strn(print, (const char *)start, width, 0, ' ', width); + mp_printf(print, "\\U%08x", ch); } } mp_printf(print, "%c", quote_char); @@ -120,33 +110,17 @@ STATIC mp_obj_t uni_unary_op(mp_unary_op_t op, mp_obj_t self_in) { } } -size_t str_offset_to_index(const mp_obj_type_t *type, const byte *self_data, size_t self_len, - size_t offset) { - if (offset > self_len) { - mp_raise_ValueError(MP_ERROR_TEXT("offset out of bounds")); - } - - if (type == &mp_type_bytes) { - return offset; - } - - size_t index_val = 0; - const byte *s = self_data; - for (size_t i = 0; i < offset; i++, s++) { - if (!UTF8_IS_CONT(*s)) { - ++index_val; - } - } - return index_val; -} - // Convert an index into a pointer to its lead byte. Out of bounds indexing will raise IndexError or // be capped to the first/last character of the string, depending on is_slice. const byte *str_index_to_ptr(const mp_obj_type_t *type, const byte *self_data, size_t self_len, mp_obj_t index, bool is_slice) { // All str functions also handle bytes objects, and they call str_index_to_ptr(), // so it must handle bytes. - if (type == &mp_type_bytes) { + if (type == &mp_type_bytes + #if MICROPY_PY_BUILTINS_BYTEARRAY + || type == &mp_type_bytearray + #endif + ) { // Taken from objstr.c:str_index_to_ptr() size_t index_val = mp_get_index(type, self_len, index, is_slice); return self_data + index_val; @@ -159,7 +133,7 @@ const byte *str_index_to_ptr(const mp_obj_type_t *type, const byte *self_data, s if (mp_obj_is_small_int(index)) { i = MP_OBJ_SMALL_INT_VALUE(index); } else if (!mp_obj_get_int_maybe(index, &i)) { - mp_raise_TypeError_varg(MP_ERROR_TEXT("%q must be of type %q, not %q"), MP_QSTR_index, MP_QSTR_int, mp_obj_get_type(index)->name); + mp_raise_msg_varg(&mp_type_TypeError, MP_ERROR_TEXT("string indices must be integers, not %s"), mp_obj_get_type_str(index)); } const byte *s, *top = self_data + self_len; if (i < 0) { @@ -169,7 +143,7 @@ const byte *str_index_to_ptr(const mp_obj_type_t *type, const byte *self_data, s if (is_slice) { return self_data; } - mp_raise_IndexError_varg(MP_ERROR_TEXT("%q index out of range"), MP_QSTR_str); + mp_raise_msg(&mp_type_IndexError, MP_ERROR_TEXT("string index out of range")); } if (!UTF8_IS_CONT(*s)) { ++i; @@ -188,7 +162,7 @@ const byte *str_index_to_ptr(const mp_obj_type_t *type, const byte *self_data, s if (is_slice) { return top; } - mp_raise_IndexError_varg(MP_ERROR_TEXT("%q index out of range"), MP_QSTR_str); + mp_raise_msg(&mp_type_IndexError, MP_ERROR_TEXT("string index out of range")); } // Then check completion if (i-- == 0) { @@ -255,63 +229,19 @@ STATIC mp_obj_t str_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { } } -STATIC const mp_rom_map_elem_t struni_locals_dict_table[] = { - #if MICROPY_CPYTHON_COMPAT - { MP_ROM_QSTR(MP_QSTR_encode), MP_ROM_PTR(&str_encode_obj) }, - #endif - { MP_ROM_QSTR(MP_QSTR_find), MP_ROM_PTR(&str_find_obj) }, - { MP_ROM_QSTR(MP_QSTR_rfind), MP_ROM_PTR(&str_rfind_obj) }, - { MP_ROM_QSTR(MP_QSTR_index), MP_ROM_PTR(&str_index_obj) }, - { MP_ROM_QSTR(MP_QSTR_rindex), MP_ROM_PTR(&str_rindex_obj) }, - { MP_ROM_QSTR(MP_QSTR_join), MP_ROM_PTR(&str_join_obj) }, - { MP_ROM_QSTR(MP_QSTR_split), MP_ROM_PTR(&str_split_obj) }, - #if MICROPY_PY_BUILTINS_STR_SPLITLINES - { MP_ROM_QSTR(MP_QSTR_splitlines), MP_ROM_PTR(&str_splitlines_obj) }, - #endif - { MP_ROM_QSTR(MP_QSTR_rsplit), MP_ROM_PTR(&str_rsplit_obj) }, - { MP_ROM_QSTR(MP_QSTR_startswith), MP_ROM_PTR(&str_startswith_obj) }, - { MP_ROM_QSTR(MP_QSTR_endswith), MP_ROM_PTR(&str_endswith_obj) }, - { MP_ROM_QSTR(MP_QSTR_strip), MP_ROM_PTR(&str_strip_obj) }, - { MP_ROM_QSTR(MP_QSTR_lstrip), MP_ROM_PTR(&str_lstrip_obj) }, - { MP_ROM_QSTR(MP_QSTR_rstrip), MP_ROM_PTR(&str_rstrip_obj) }, - { MP_ROM_QSTR(MP_QSTR_format), MP_ROM_PTR(&str_format_obj) }, - { MP_ROM_QSTR(MP_QSTR_replace), MP_ROM_PTR(&str_replace_obj) }, - #if MICROPY_PY_BUILTINS_STR_COUNT - { MP_ROM_QSTR(MP_QSTR_count), MP_ROM_PTR(&str_count_obj) }, - #endif - #if MICROPY_PY_BUILTINS_STR_PARTITION - { MP_ROM_QSTR(MP_QSTR_partition), MP_ROM_PTR(&str_partition_obj) }, - { MP_ROM_QSTR(MP_QSTR_rpartition), MP_ROM_PTR(&str_rpartition_obj) }, - #endif - #if MICROPY_PY_BUILTINS_STR_CENTER - { MP_ROM_QSTR(MP_QSTR_center), MP_ROM_PTR(&str_center_obj) }, - #endif - { MP_ROM_QSTR(MP_QSTR_lower), MP_ROM_PTR(&str_lower_obj) }, - { MP_ROM_QSTR(MP_QSTR_upper), MP_ROM_PTR(&str_upper_obj) }, - { MP_ROM_QSTR(MP_QSTR_isspace), MP_ROM_PTR(&str_isspace_obj) }, - { MP_ROM_QSTR(MP_QSTR_isalpha), MP_ROM_PTR(&str_isalpha_obj) }, - { MP_ROM_QSTR(MP_QSTR_isdigit), MP_ROM_PTR(&str_isdigit_obj) }, - { MP_ROM_QSTR(MP_QSTR_isupper), MP_ROM_PTR(&str_isupper_obj) }, - { MP_ROM_QSTR(MP_QSTR_islower), MP_ROM_PTR(&str_islower_obj) }, -}; - -STATIC MP_DEFINE_CONST_DICT(struni_locals_dict, struni_locals_dict_table); - -const mp_obj_type_t mp_type_str = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_str, - .print = uni_print, - .make_new = mp_obj_str_make_new, - .locals_dict = (mp_obj_dict_t *)&struni_locals_dict, - MP_TYPE_EXTENDED_FIELDS( - .unary_op = uni_unary_op, - .binary_op = mp_obj_str_binary_op, - .subscr = str_subscr, - .getiter = mp_obj_new_str_iterator, - .buffer_p = { .get_buffer = mp_obj_str_get_buffer }, - ), -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_str, + MP_QSTR_str, + MP_TYPE_FLAG_ITER_IS_GETITER, + make_new, mp_obj_str_make_new, + print, uni_print, + unary_op, uni_unary_op, + binary_op, mp_obj_str_binary_op, + subscr, str_subscr, + iter, mp_obj_new_str_iterator, + buffer, mp_obj_str_get_buffer, + locals_dict, &mp_obj_str_locals_dict + ); /******************************************************************************/ /* str iterator */ diff --git a/py/objtuple.c b/py/objtuple.c index 275a3e3ed9..d219a2a92d 100644 --- a/py/objtuple.c +++ b/py/objtuple.c @@ -3,8 +3,8 @@ * * The MIT License (MIT) * - * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George - * SPDX-FileCopyrightText: Copyright (c) 2014-2017 Paul Sokolovsky + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2014-2017 Paul Sokolovsky * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -30,9 +30,9 @@ #include "py/objtuple.h" #include "py/runtime.h" -#include "py/objtype.h" -#include "supervisor/shared/translate/translate.h" +// type check is done on getiter method to allow tuple, namedtuple, attrtuple +#define mp_obj_is_tuple_compatible(o) (mp_obj_get_type(o)->getiter == mp_obj_tuple_getiter) /******************************************************************************/ /* tuple */ @@ -111,7 +111,7 @@ STATIC mp_obj_t tuple_cmp_helper(mp_uint_t op, mp_obj_t self_in, mp_obj_t anothe mp_check_self(mp_obj_is_tuple_compatible(self_in)); const mp_obj_type_t *another_type = mp_obj_get_type(another_in); mp_obj_tuple_t *self = MP_OBJ_TO_PTR(self_in); - if (mp_type_get_getiter_slot(another_type) != mp_obj_tuple_getiter) { + if (MP_OBJ_TYPE_GET_SLOT_OR_NULL(another_type, iter) != mp_obj_tuple_getiter) { // Slow path for user subclasses another_in = mp_obj_cast_to_native_base(another_in, MP_OBJ_FROM_PTR(&mp_type_tuple)); if (another_in == MP_OBJ_NULL) { @@ -165,6 +165,7 @@ mp_obj_t mp_obj_tuple_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) { if (n <= 0) { return mp_const_empty_tuple; } + // CIRCUITPY size_t new_len = mp_seq_multiply_len(o->len, n); mp_obj_tuple_t *s = MP_OBJ_TO_PTR(mp_obj_new_tuple(new_len, NULL)); mp_seq_multiply(o->items, sizeof(*o->items), o->len, n, s->items); @@ -230,20 +231,18 @@ STATIC const mp_rom_map_elem_t tuple_locals_dict_table[] = { STATIC MP_DEFINE_CONST_DICT(tuple_locals_dict, tuple_locals_dict_table); -const mp_obj_type_t mp_type_tuple = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_tuple, - .print = mp_obj_tuple_print, - .make_new = mp_obj_tuple_make_new, - .locals_dict = (mp_obj_dict_t *)&tuple_locals_dict, - MP_TYPE_EXTENDED_FIELDS( - .unary_op = mp_obj_tuple_unary_op, - .binary_op = mp_obj_tuple_binary_op, - .subscr = mp_obj_tuple_subscr, - .getiter = mp_obj_tuple_getiter, - ), -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_tuple, + MP_QSTR_tuple, + MP_TYPE_FLAG_ITER_IS_GETITER, + make_new, mp_obj_tuple_make_new, + print, mp_obj_tuple_print, + unary_op, mp_obj_tuple_unary_op, + binary_op, mp_obj_tuple_binary_op, + subscr, mp_obj_tuple_subscr, + iter, mp_obj_tuple_getiter, + locals_dict, &tuple_locals_dict + ); // the zero-length tuple const mp_obj_tuple_t mp_const_empty_tuple_obj = {{&mp_type_tuple}, 0}; diff --git a/py/objtype.c b/py/objtype.c index 0d25aac4e9..20c8a286d2 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -3,8 +3,8 @@ * * The MIT License (MIT) * - * SPDX-FileCopyrightText: Copyright (c) 2013-2018 Damien P. George - * SPDX-FileCopyrightText: Copyright (c) 2014-2018 Paul Sokolovsky + * Copyright (c) 2013-2018 Damien P. George + * Copyright (c) 2014-2018 Paul Sokolovsky * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -33,9 +33,6 @@ #include "py/objtype.h" #include "py/runtime.h" -#include "supervisor/shared/stack.h" -#include "supervisor/shared/translate/translate.h" - #if MICROPY_DEBUG_VERBOSE // print debugging info #define DEBUG_PRINT (1) #define DEBUG_printf DEBUG_printf @@ -47,7 +44,7 @@ #define ENABLE_SPECIAL_ACCESSORS \ (MICROPY_PY_DESCRIPTORS || MICROPY_PY_DELATTR_SETATTR || MICROPY_PY_BUILTINS_PROPERTY) -STATIC mp_obj_t static_class_method_make_new(const mp_obj_type_t *self, size_t n_args, size_t n_kw, const mp_obj_t *args); +STATIC mp_obj_t static_class_method_make_new(const mp_obj_type_t *self_in, size_t n_args, size_t n_kw, const mp_obj_t *args); /******************************************************************************/ // instance object @@ -58,20 +55,17 @@ STATIC int instance_count_native_bases(const mp_obj_type_t *type, const mp_obj_t if (type == &mp_type_object) { // Not a "real" type, end search here. return count; - } - if (mp_obj_is_native_type(type)) { + } else if (mp_obj_is_native_type(type)) { // Native types don't have parents (at least not from our perspective) so end. *last_native_base = type; return count + 1; - } - const void *parent = mp_type_get_parent_slot(type); - if (parent == NULL) { + } else if (!MP_OBJ_TYPE_HAS_SLOT(type, parent)) { // No parents so end search here. return count; #if MICROPY_MULTIPLE_INHERITANCE - } else if (((mp_obj_base_t *)parent)->type == &mp_type_tuple) { + } else if (((mp_obj_base_t *)MP_OBJ_TYPE_GET_SLOT(type, parent))->type == &mp_type_tuple) { // Multiple parents, search through them all recursively. - const mp_obj_tuple_t *parent_tuple = parent; + const mp_obj_tuple_t *parent_tuple = MP_OBJ_TYPE_GET_SLOT(type, parent); const mp_obj_t *item = parent_tuple->items; const mp_obj_t *top = item + parent_tuple->len; for (; item < top; ++item) { @@ -83,7 +77,7 @@ STATIC int instance_count_native_bases(const mp_obj_type_t *type, const mp_obj_t #endif } else { // A single parent, use iteration to continue the search. - type = parent; + type = MP_OBJ_TYPE_GET_SLOT(type, parent); } } } @@ -108,7 +102,7 @@ STATIC mp_obj_t native_base_init_wrapper(size_t n_args, const mp_obj_t *pos_args if (n_kw) { memcpy(args2 + n_args, kw_args->table, 2 * n_kw * sizeof(mp_obj_t)); } - self->subobj[0] = native_base->make_new(native_base, n_args, n_kw, args2); + self->subobj[0] = MP_OBJ_TYPE_GET_SLOT(native_base, make_new)(native_base, n_args - 1, 0, args + 1); m_del(mp_obj_t, args2, n_args + 2 * n_kw); return mp_const_none; @@ -151,7 +145,7 @@ void mp_obj_assert_native_inited(mp_obj_t native_object) { // will keep lookup->dest[0]'s value (should be MP_OBJ_NULL on invocation) if attribute // is not found // will set lookup->dest[0] to MP_OBJ_SENTINEL if special method was found in a native -// type base via slot id (as specified by lookup->meth_offset). As there can be only one +// type base via slot id (as specified by lookup->slot_offset). As there can be only one // native base, it's known that it applies to instance->subobj[0]. In most cases, we also // don't need to know which type it was - because instance->subobj[0] is of that type. // The only exception is when object is not yet constructed, then we need to know base @@ -160,7 +154,7 @@ void mp_obj_assert_native_inited(mp_obj_t native_object) { struct class_lookup_data { mp_obj_instance_t *obj; qstr attr; - size_t meth_offset; + size_t slot_offset; mp_obj_t *dest; bool is_type; }; @@ -174,23 +168,22 @@ STATIC void mp_obj_class_lookup(struct class_lookup_data *lookup, const mp_obj_t // This avoids extra method_name => slot lookup. On the other hand, // this should not be applied to class types, as will result in extra // lookup either. - if (lookup->meth_offset != 0 && mp_obj_is_native_type(type)) { - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wcast-align" - size_t sz = mp_type_size(type); - if (lookup->meth_offset < sz && *(void **)((char *)type + lookup->meth_offset) != NULL) { - #pragma GCC diagnostic pop + if (lookup->slot_offset != 0 && mp_obj_is_native_type(type)) { + // Check if there is a non-zero value in the specified slot index, + // with a special case for getiter where the slot won't be set + // for MP_TYPE_FLAG_ITER_IS_STREAM. + if (MP_OBJ_TYPE_HAS_SLOT_BY_OFFSET(type, lookup->slot_offset) || (lookup->slot_offset == MP_OBJ_TYPE_OFFSETOF_SLOT(iter) && type->flags & MP_TYPE_FLAG_ITER_IS_STREAM)) { DEBUG_printf("mp_obj_class_lookup: Matched special meth slot (off=%d) for %s\n", - lookup->meth_offset, qstr_str(lookup->attr)); + lookup->slot_offset, qstr_str(lookup->attr)); lookup->dest[0] = MP_OBJ_SENTINEL; return; } } - if (type->locals_dict != NULL) { + if (MP_OBJ_TYPE_HAS_SLOT(type, locals_dict)) { // search locals_dict (the set of methods/attributes) - assert(mp_obj_is_dict_or_ordereddict(MP_OBJ_FROM_PTR(type->locals_dict))); // MicroPython restriction, for now - mp_map_t *locals_map = &type->locals_dict->map; + assert(mp_obj_is_dict_or_ordereddict(MP_OBJ_FROM_PTR(MP_OBJ_TYPE_GET_SLOT(type, locals_dict)))); // MicroPython restriction, for now + mp_map_t *locals_map = &MP_OBJ_TYPE_GET_SLOT(type, locals_dict)->map; mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(lookup->attr), MP_MAP_LOOKUP); if (elem != NULL) { if (lookup->is_type) { @@ -198,12 +191,16 @@ STATIC void mp_obj_class_lookup(struct class_lookup_data *lookup, const mp_obj_t // do a lookup, not a (base) type in which we found the class method. const mp_obj_type_t *org_type = (const mp_obj_type_t *)lookup->obj; mp_convert_member_lookup(MP_OBJ_NULL, org_type, elem->value, lookup->dest); - } else if (mp_obj_is_type(elem->value, &mp_type_property)) { - lookup->dest[0] = elem->value; - return; } else { mp_obj_instance_t *obj = lookup->obj; - mp_convert_member_lookup(MP_OBJ_FROM_PTR(obj), type, elem->value, lookup->dest); + mp_obj_t obj_obj; + if (obj != NULL && mp_obj_is_native_type(type) && type != &mp_type_object /* object is not a real type */) { + // If we're dealing with native base class, then it applies to native sub-object + obj_obj = obj->subobj[0]; + } else { + obj_obj = MP_OBJ_FROM_PTR(obj); + } + mp_convert_member_lookup(obj_obj, type, elem->value, lookup->dest); } #if DEBUG_PRINT DEBUG_printf("mp_obj_class_lookup: Returning: "); @@ -230,14 +227,12 @@ STATIC void mp_obj_class_lookup(struct class_lookup_data *lookup, const mp_obj_t // attribute not found, keep searching base classes - const void *parent = mp_type_get_parent_slot(type); - if (parent == NULL) { + if (!MP_OBJ_TYPE_HAS_SLOT(type, parent)) { DEBUG_printf("mp_obj_class_lookup: No more parents\n"); return; - } #if MICROPY_MULTIPLE_INHERITANCE - if (((mp_obj_base_t *)parent)->type == &mp_type_tuple) { - const mp_obj_tuple_t *parent_tuple = parent; + } else if (((mp_obj_base_t *)MP_OBJ_TYPE_GET_SLOT(type, parent))->type == &mp_type_tuple) { + const mp_obj_tuple_t *parent_tuple = MP_OBJ_TYPE_GET_SLOT(type, parent); const mp_obj_t *item = parent_tuple->items; const mp_obj_t *top = item + parent_tuple->len - 1; for (; item < top; ++item) { @@ -256,10 +251,10 @@ STATIC void mp_obj_class_lookup(struct class_lookup_data *lookup, const mp_obj_t // search last base (simple tail recursion elimination) assert(mp_obj_is_type(*item, &mp_type_type)); type = (mp_obj_type_t *)MP_OBJ_TO_PTR(*item); - continue; - } #endif - type = parent; + } else { + type = MP_OBJ_TYPE_GET_SLOT(type, parent); + } if (type == &mp_type_object) { // Not a "real" type return; @@ -274,7 +269,7 @@ STATIC void instance_print(const mp_print_t *print, mp_obj_t self_in, mp_print_k struct class_lookup_data lookup = { .obj = self, .attr = meth, - .meth_offset = offsetof(mp_obj_type_t, print), + .slot_offset = MP_OBJ_TYPE_OFFSETOF_SLOT(print), .dest = member, .is_type = false, }; @@ -282,7 +277,7 @@ STATIC void instance_print(const mp_print_t *print, mp_obj_t self_in, mp_print_k if (member[0] == MP_OBJ_NULL && kind == PRINT_STR) { // If there's no __str__, fall back to __repr__ lookup.attr = MP_QSTR___repr__; - lookup.meth_offset = 0; + lookup.slot_offset = 0; mp_obj_class_lookup(&lookup, self->base.type); } @@ -309,7 +304,7 @@ STATIC void instance_print(const mp_print_t *print, mp_obj_t self_in, mp_print_k mp_printf(print, "<%q object at %p>", mp_obj_get_type_qstr(self_in), self); } -mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self, size_t n_args, size_t n_kw, const mp_obj_t *args) { +STATIC mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self, size_t n_args, size_t n_kw, const mp_obj_t *args) { assert(mp_obj_is_instance_type(self)); // look for __new__ function @@ -317,7 +312,7 @@ mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self, size_t n_args, size struct class_lookup_data lookup = { .obj = NULL, .attr = MP_QSTR___new__, - .meth_offset = offsetof(mp_obj_type_t, make_new), + .slot_offset = MP_OBJ_TYPE_OFFSETOF_SLOT(make_new), .dest = init_fn, .is_type = false, }; @@ -343,7 +338,6 @@ mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self, size_t n_args, size mp_obj_t args2[1] = {MP_OBJ_FROM_PTR(self)}; new_ret = mp_call_function_n_kw(init_fn[0], 1, 0, args2); } else { - // TODO(tannewt): Could this be on the stack? It's deleted below. mp_obj_t *args2 = m_new(mp_obj_t, 1 + n_args + 2 * n_kw); args2[0] = MP_OBJ_FROM_PTR(self); memcpy(args2 + 1, args, (n_args + 2 * n_kw) * sizeof(mp_obj_t)); @@ -368,18 +362,16 @@ mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self, size_t n_args, size init_fn[0] = init_fn[1] = MP_OBJ_NULL; lookup.obj = o; lookup.attr = MP_QSTR___init__; - lookup.meth_offset = 0; + lookup.slot_offset = 0; mp_obj_class_lookup(&lookup, self); if (init_fn[0] != MP_OBJ_NULL) { mp_obj_t init_ret; if (n_args == 0 && n_kw == 0) { init_ret = mp_call_method_n_kw(0, 0, init_fn); } else { - // TODO(tannewt): Could this be on the stack? It's deleted below. mp_obj_t *args2 = m_new(mp_obj_t, 2 + n_args + 2 * n_kw); args2[0] = init_fn[0]; args2[1] = init_fn[1]; - // copy in kwargs memcpy(args2 + 2, args, (n_args + 2 * n_kw) * sizeof(mp_obj_t)); init_ret = mp_call_method_n_kw(n_args, n_kw, args2); m_del(mp_obj_t, args2, 2 + n_args + 2 * n_kw); @@ -388,8 +380,8 @@ mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self, size_t n_args, size #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("__init__() should return None")); #else - mp_raise_TypeError_varg(MP_ERROR_TEXT("__init__() should return None, not '%q'"), - mp_obj_get_type_qstr(init_ret)); + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("__init__() should return None, not '%s'"), mp_obj_get_type_str(init_ret)); #endif } } @@ -397,7 +389,7 @@ mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self, size_t n_args, size // If the type had a native base that was not explicitly initialised // (constructed) by the Python __init__() method then construct it now. if (native_base != NULL && o->subobj[0] == MP_OBJ_FROM_PTR(&native_base_init_wrapper_obj)) { - o->subobj[0] = native_base->make_new(native_base, n_args, n_kw, args); + o->subobj[0] = MP_OBJ_TYPE_GET_SLOT(native_base, make_new)(native_base, n_args, n_kw, args); } return MP_OBJ_FROM_PTR(o); @@ -416,6 +408,12 @@ const byte mp_unary_op_method_name[MP_UNARY_OP_NUM_RUNTIME] = { [MP_UNARY_OP_INVERT] = MP_QSTR___invert__, [MP_UNARY_OP_ABS] = MP_QSTR___abs__, #endif + #if MICROPY_PY_BUILTINS_FLOAT + [MP_UNARY_OP_FLOAT_MAYBE] = MP_QSTR___float__, + #if MICROPY_PY_BUILTINS_COMPLEX + [MP_UNARY_OP_COMPLEX_MAYBE] = MP_QSTR___complex__, + #endif + #endif #if MICROPY_PY_SYS_GETSIZEOF [MP_UNARY_OP_SIZEOF] = MP_QSTR___sizeof__, #endif @@ -446,7 +444,7 @@ STATIC mp_obj_t instance_unary_op(mp_unary_op_t op, mp_obj_t self_in) { struct class_lookup_data lookup = { .obj = self, .attr = op_name, - .meth_offset = offsetof(mp_obj_type_t, ext[0].unary_op), + .slot_offset = MP_OBJ_TYPE_OFFSETOF_SLOT(unary_op), .dest = member, .is_type = false, }; @@ -574,7 +572,7 @@ retry:; struct class_lookup_data lookup = { .obj = lhs, .attr = op_name, - .meth_offset = offsetof(mp_obj_type_t, ext[0].binary_op), + .slot_offset = MP_OBJ_TYPE_OFFSETOF_SLOT(binary_op), .dest = dest, .is_type = false, }; @@ -640,7 +638,7 @@ STATIC void mp_obj_instance_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *des struct class_lookup_data lookup = { .obj = self, .attr = attr, - .meth_offset = 0, + .slot_offset = 0, .dest = dest, .is_type = false, }; @@ -726,7 +724,7 @@ STATIC bool mp_obj_instance_store_attr(mp_obj_t self_in, qstr attr, mp_obj_t val struct class_lookup_data lookup = { .obj = self, .attr = attr, - .meth_offset = 0, + .slot_offset = 0, .dest = member, .is_type = false, }; @@ -849,7 +847,7 @@ STATIC mp_obj_t instance_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value mp_obj_t member[4] = {MP_OBJ_NULL, MP_OBJ_NULL, index, value}; struct class_lookup_data lookup = { .obj = self, - .meth_offset = offsetof(mp_obj_type_t, ext[0].subscr), + .slot_offset = MP_OBJ_TYPE_OFFSETOF_SLOT(subscr), .dest = member, .is_type = false, }; @@ -865,11 +863,7 @@ STATIC mp_obj_t instance_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value } mp_obj_class_lookup(&lookup, self->base.type); if (member[0] == MP_OBJ_SENTINEL) { - const mp_obj_type_t *subobj_type = mp_obj_get_type(self->subobj[0]); - mp_obj_t ret = subobj_type->ext[0].subscr(self_in, index, value); - // May have called port specific C code. Make sure it didn't mess up the heap. - assert_heap_ok(); - return ret; + return mp_obj_subscr(self->subobj[0], index, value); } else if (member[0] != MP_OBJ_NULL) { size_t n_args = value == MP_OBJ_NULL || value == MP_OBJ_SENTINEL ? 1 : 2; mp_obj_t ret = mp_call_method_n_kw(n_args, 0, member); @@ -888,7 +882,7 @@ STATIC mp_obj_t mp_obj_instance_get_call(mp_obj_t self_in, mp_obj_t *member) { struct class_lookup_data lookup = { .obj = self, .attr = MP_QSTR___call__, - .meth_offset = offsetof(mp_obj_type_t, ext[0].call), + .slot_offset = MP_OBJ_TYPE_OFFSETOF_SLOT(call), .dest = member, .is_type = false, }; @@ -927,7 +921,7 @@ mp_obj_t mp_obj_instance_getiter(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) struct class_lookup_data lookup = { .obj = self, .attr = MP_QSTR___iter__, - .meth_offset = offsetof(mp_obj_type_t, ext[0].getiter), + .slot_offset = MP_OBJ_TYPE_OFFSETOF_SLOT(iter), .dest = member, .is_type = false, }; @@ -936,10 +930,14 @@ mp_obj_t mp_obj_instance_getiter(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) return MP_OBJ_NULL; } else if (member[0] == MP_OBJ_SENTINEL) { const mp_obj_type_t *type = mp_obj_get_type(self->subobj[0]); - if (iter_buf == NULL) { - iter_buf = m_new_obj(mp_obj_iter_buf_t); + if (type->flags & MP_TYPE_FLAG_ITER_IS_ITERNEXT) { + return self->subobj[0]; + } else { + if (iter_buf == NULL) { + iter_buf = m_new_obj(mp_obj_iter_buf_t); + } + return ((mp_getiter_fun_t)MP_OBJ_TYPE_GET_SLOT(type, iter))(self->subobj[0], iter_buf); } - return type->ext[0].getiter(self->subobj[0], iter_buf); } else { return mp_call_method_n_kw(0, 0, member); } @@ -951,14 +949,14 @@ STATIC mp_int_t instance_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, struct class_lookup_data lookup = { .obj = self, .attr = MP_QSTR_, // don't actually look for a method - .meth_offset = offsetof(mp_obj_type_t, ext[0].buffer_p.get_buffer), + .slot_offset = MP_OBJ_TYPE_OFFSETOF_SLOT(buffer), .dest = member, .is_type = false, }; mp_obj_class_lookup(&lookup, self->base.type); if (member[0] == MP_OBJ_SENTINEL) { const mp_obj_type_t *type = mp_obj_get_type(self->subobj[0]); - return type->ext[0].buffer_p.get_buffer(self->subobj[0], bufinfo, flags); + return MP_OBJ_TYPE_GET_SLOT(type, buffer)(self->subobj[0], bufinfo, flags); } else { return 1; // object does not support buffer protocol } @@ -1044,7 +1042,7 @@ STATIC mp_obj_t type_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp mp_obj_type_t *self = MP_OBJ_TO_PTR(self_in); - if (self->make_new == NULL) { + if (!MP_OBJ_TYPE_HAS_SLOT(self, make_new)) { #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("cannot create instance")); #else @@ -1052,7 +1050,8 @@ STATIC mp_obj_t type_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp #endif } - mp_obj_t o = self->make_new(self, n_args, n_kw, args); + // make new instance + mp_obj_t o = MP_OBJ_TYPE_GET_SLOT(self, make_new)(self, n_args, n_kw, args); // return new instance return o; @@ -1073,7 +1072,7 @@ STATIC void type_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { if (attr == MP_QSTR___dict__) { // Returns a read-only dict of the class attributes. // If the internal locals is not fixed, a copy will be created. - const mp_obj_dict_t *dict = self->locals_dict; + const mp_obj_dict_t *dict = MP_OBJ_TYPE_GET_SLOT_OR_NULL(self, locals_dict); if (!dict) { dict = &mp_const_empty_dict_obj; } @@ -1092,8 +1091,7 @@ STATIC void type_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { dest[0] = mp_const_empty_tuple; return; } - const void *parent = mp_type_get_parent_slot(self); - mp_obj_t parent_obj = parent ? MP_OBJ_FROM_PTR(parent) : MP_OBJ_FROM_PTR(&mp_type_object); + mp_obj_t parent_obj = MP_OBJ_TYPE_HAS_SLOT(self, parent) ? MP_OBJ_FROM_PTR(MP_OBJ_TYPE_GET_SLOT(self, parent)) : MP_OBJ_FROM_PTR(&mp_type_object); #if MICROPY_MULTIPLE_INHERITANCE if (mp_obj_is_type(parent_obj, &mp_type_tuple)) { dest[0] = parent_obj; @@ -1107,7 +1105,7 @@ STATIC void type_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { struct class_lookup_data lookup = { .obj = (mp_obj_instance_t *)self, .attr = attr, - .meth_offset = 0, + .slot_offset = 0, .dest = dest, .is_type = true, }; @@ -1115,9 +1113,9 @@ STATIC void type_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { } else { // delete/store attribute - if (self->locals_dict != NULL) { - assert(mp_obj_is_dict_or_ordereddict(MP_OBJ_FROM_PTR(self->locals_dict))); // MicroPython restriction, for now - mp_map_t *locals_map = &self->locals_dict->map; + if (MP_OBJ_TYPE_HAS_SLOT(self, locals_dict)) { + assert(mp_obj_is_dict_or_ordereddict(MP_OBJ_FROM_PTR(MP_OBJ_TYPE_GET_SLOT(self, locals_dict)))); // MicroPython restriction, for now + mp_map_t *locals_map = &MP_OBJ_TYPE_GET_SLOT(self, locals_dict)->map; if (locals_map->is_fixed) { // can't apply delete/store to a fixed map return; @@ -1151,18 +1149,16 @@ STATIC void type_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { } } -const mp_obj_type_t mp_type_type = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_type, - .print = type_print, - .make_new = type_make_new, - .attr = type_attr, - MP_TYPE_EXTENDED_FIELDS( - .call = type_call, - .unary_op = mp_generic_unary_op, - ), -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_type, + MP_QSTR_type, + MP_TYPE_FLAG_NONE, + make_new, type_make_new, + print, type_print, + call, type_call, + unary_op, mp_generic_unary_op, + attr, type_attr + ); mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict) { // Verify input objects have expected type @@ -1177,15 +1173,20 @@ mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict) // Basic validation of base classes uint16_t base_flags = MP_TYPE_FLAG_EQ_NOT_REFLEXIVE - | MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_EQ_HAS_NEQ_TEST | MP_TYPE_FLAG_EXTENDED; + | MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE + | MP_TYPE_FLAG_EQ_HAS_NEQ_TEST + | MP_TYPE_FLAG_ITER_IS_GETITER + | MP_TYPE_FLAG_INSTANCE_TYPE; size_t bases_len; mp_obj_t *bases_items; mp_obj_tuple_get(bases_tuple, &bases_len, &bases_items); for (size_t i = 0; i < bases_len; i++) { - mp_arg_validate_type(bases_items[i], &mp_type_type, MP_QSTR___class__); + if (!mp_obj_is_type(bases_items[i], &mp_type_type)) { + mp_raise_TypeError(NULL); + } mp_obj_type_t *t = MP_OBJ_TO_PTR(bases_items[i]); // TODO: Verify with CPy, tested on function type - if (t->make_new == NULL) { + if (!MP_OBJ_TYPE_HAS_SLOT(t, make_new)) { #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("type is not an acceptable base type")); #else @@ -1201,47 +1202,58 @@ mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict) #endif } - mp_obj_full_type_t *o = m_new0(mp_obj_full_type_t, 1); + const void *base_protocol = NULL; + if (bases_len > 0) { + base_protocol = MP_OBJ_TYPE_GET_SLOT_OR_NULL(((mp_obj_type_t *)MP_OBJ_TO_PTR(bases_items[0])), protocol); + } + + // Allocate a variable-sized mp_obj_type_t with as many slots as we need + // (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)); o->base.type = &mp_type_type; o->flags = base_flags; o->name = name; - o->print = instance_print; - o->make_new = mp_obj_instance_make_new; - o->attr = mp_obj_instance_attr; - o->MP_TYPE_CALL = mp_obj_instance_call; - o->MP_TYPE_UNARY_OP = instance_unary_op; - o->MP_TYPE_BINARY_OP = instance_binary_op; - o->MP_TYPE_SUBSCR = instance_subscr; - o->MP_TYPE_GETITER = mp_obj_instance_getiter; - // o->iternext = ; not implemented - o->MP_TYPE_GET_BUFFER = instance_get_buffer; + MP_OBJ_TYPE_SET_SLOT(o, make_new, mp_obj_instance_make_new, 0); + MP_OBJ_TYPE_SET_SLOT(o, print, instance_print, 1); + MP_OBJ_TYPE_SET_SLOT(o, call, mp_obj_instance_call, 2); + MP_OBJ_TYPE_SET_SLOT(o, unary_op, instance_unary_op, 3); + MP_OBJ_TYPE_SET_SLOT(o, binary_op, instance_binary_op, 4); + MP_OBJ_TYPE_SET_SLOT(o, attr, mp_obj_instance_attr, 5); + MP_OBJ_TYPE_SET_SLOT(o, subscr, instance_subscr, 6); + MP_OBJ_TYPE_SET_SLOT(o, iter, mp_obj_instance_getiter, 7); + MP_OBJ_TYPE_SET_SLOT(o, buffer, instance_get_buffer, 8); + + mp_obj_dict_t *locals_ptr = MP_OBJ_TO_PTR(locals_dict); + MP_OBJ_TYPE_SET_SLOT(o, locals_dict, locals_ptr, 9); if (bases_len > 0) { - // Inherit protocol from a base class. This allows to define an - // abstract base class which would translate C-level protocol to - // Python method calls, and any subclass inheriting from it will - // support this feature. - o->MP_TYPE_PROTOCOL = mp_type_get_protocol_slot((mp_obj_type_t *)MP_OBJ_TO_PTR(bases_items[0])); - if (bases_len >= 2) { #if MICROPY_MULTIPLE_INHERITANCE - o->parent = MP_OBJ_TO_PTR(bases_tuple); + MP_OBJ_TYPE_SET_SLOT(o, parent, MP_OBJ_TO_PTR(bases_tuple), 10); #else mp_raise_NotImplementedError(MP_ERROR_TEXT("multiple inheritance not supported")); #endif } else { - o->parent = MP_OBJ_TO_PTR(bases_items[0]); + MP_OBJ_TYPE_SET_SLOT(o, parent, MP_OBJ_TO_PTR(bases_items[0]), 10); + } + + // Inherit protocol from a base class. This allows to define an + // abstract base class which would translate C-level protocol to + // Python method calls, and any subclass inheriting from it will + // support this feature. + if (base_protocol) { + MP_OBJ_TYPE_SET_SLOT(o, protocol, base_protocol, 11); } } - o->locals_dict = MP_OBJ_TO_PTR(locals_dict); - #if ENABLE_SPECIAL_ACCESSORS // Check if the class has any special accessor methods if (!(o->flags & MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS)) { - for (size_t i = 0; i < o->locals_dict->map.alloc; i++) { - if (mp_map_slot_is_filled(&o->locals_dict->map, i)) { - const mp_map_elem_t *elem = &o->locals_dict->map.table[i]; + for (size_t i = 0; i < locals_ptr->map.alloc; i++) { + if (mp_map_slot_is_filled(&locals_ptr->map, i)) { + const mp_map_elem_t *elem = &locals_ptr->map.table[i]; if (check_for_special_accessors(elem->key, elem->value)) { o->flags |= MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS; break; @@ -1252,23 +1264,12 @@ mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict) #endif const mp_obj_type_t *native_base; - size_t num_native_bases = instance_count_native_bases((mp_obj_type_t *)o, &native_base); + size_t num_native_bases = instance_count_native_bases(o, &native_base); if (num_native_bases > 1) { mp_raise_TypeError(MP_ERROR_TEXT("multiple bases have instance lay-out conflict")); } - mp_map_t *locals_map = &o->locals_dict->map; - #if ENABLE_SPECIAL_ACCESSORS - // Check if the class has any special accessor methods - if (!(o->flags & MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS) && - (map_has_special_accessors(locals_map) || - (num_native_bases == 1 && - native_base->locals_dict != NULL && - map_has_special_accessors(&native_base->locals_dict->map)))) { - o->flags |= MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS; - } - #endif - + mp_map_t *locals_map = &MP_OBJ_TYPE_GET_SLOT(o, locals_dict)->map; mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(MP_QSTR___new__), MP_MAP_LOOKUP); if (elem != NULL) { // __new__ slot exists; check if it is a function @@ -1329,22 +1330,21 @@ STATIC void super_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { struct class_lookup_data lookup = { .obj = MP_OBJ_TO_PTR(self->obj), .attr = attr, - .meth_offset = 0, + .slot_offset = 0, .dest = dest, .is_type = false, }; - // Allow a call super().__init__() to reach any native base classes + // Allow a call super().__init__() to reach any native base classes. if (attr == MP_QSTR___init__) { - lookup.meth_offset = offsetof(mp_obj_type_t, make_new); + lookup.slot_offset = MP_OBJ_TYPE_OFFSETOF_SLOT(make_new); } - const void *parent = mp_type_get_parent_slot(type); - if (parent == NULL) { + if (!MP_OBJ_TYPE_HAS_SLOT(type, parent)) { // no parents, do nothing #if MICROPY_MULTIPLE_INHERITANCE - } else if (((mp_obj_base_t *)parent)->type == &mp_type_tuple) { - const mp_obj_tuple_t *parent_tuple = parent; + } else if (((mp_obj_base_t *)MP_OBJ_TYPE_GET_SLOT(type, parent))->type == &mp_type_tuple) { + const mp_obj_tuple_t *parent_tuple = MP_OBJ_TYPE_GET_SLOT(type, parent); size_t len = parent_tuple->len; const mp_obj_t *items = parent_tuple->items; for (size_t i = 0; i < len; i++) { @@ -1354,14 +1354,15 @@ STATIC void super_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { // and we don't want to lookup native methods in object. continue; } + mp_obj_class_lookup(&lookup, (mp_obj_type_t *)MP_OBJ_TO_PTR(items[i])); if (dest[0] != MP_OBJ_NULL) { break; } } #endif - } else if (parent != &mp_type_object) { - mp_obj_class_lookup(&lookup, parent); + } else if (MP_OBJ_TYPE_GET_SLOT(type, parent) != &mp_type_object) { + mp_obj_class_lookup(&lookup, MP_OBJ_TYPE_GET_SLOT(type, parent)); } if (dest[0] != MP_OBJ_NULL) { @@ -1369,6 +1370,7 @@ STATIC void super_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { // Looked up native __init__ so defer to it dest[0] = MP_OBJ_FROM_PTR(&native_base_init_wrapper_obj); dest[1] = self->obj; + // CIRCUITPY better support for properties } else { mp_obj_t member = dest[0]; // changes to mp_obj_instance_load_attr may require changes @@ -1397,20 +1399,21 @@ STATIC void super_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { return; } - // Reset meth_offset so we don't look up any native methods in object, + // Reset slot_offset so we don't look up any native methods in object, // because object never takes up the native base-class slot. - lookup.meth_offset = 0; + lookup.slot_offset = 0; mp_obj_class_lookup(&lookup, &mp_type_object); } -const mp_obj_type_t mp_type_super = { - { &mp_type_type }, - .name = MP_QSTR_super, - .print = super_print, - .make_new = super_make_new, - .attr = super_attr, -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_super, + MP_QSTR_super, + MP_TYPE_FLAG_NONE, + make_new, super_make_new, + print, super_print, + attr, super_attr + ); void mp_load_super_method(qstr attr, mp_obj_t *dest) { mp_obj_super_t super = {{&mp_type_super}, dest[1], dest[2]}; @@ -1436,15 +1439,14 @@ bool mp_obj_is_subclass_fast(mp_const_obj_t object, mp_const_obj_t classinfo) { } const mp_obj_type_t *self = MP_OBJ_TO_PTR(object); - const void *parent = mp_type_get_parent_slot(self); - if (parent == NULL) { + if (!MP_OBJ_TYPE_HAS_SLOT(self, parent)) { // type has no parents return false; #if MICROPY_MULTIPLE_INHERITANCE - } else if (((mp_obj_base_t *)parent)->type == &mp_type_tuple) { + } else if (((mp_obj_base_t *)MP_OBJ_TYPE_GET_SLOT(self, parent))->type == &mp_type_tuple) { // get the base objects (they should be type objects) - const mp_obj_tuple_t *parent_tuple = parent; + const mp_obj_tuple_t *parent_tuple = MP_OBJ_TYPE_GET_SLOT(self, parent); const mp_obj_t *item = parent_tuple->items; const mp_obj_t *top = item + parent_tuple->len - 1; @@ -1460,7 +1462,7 @@ bool mp_obj_is_subclass_fast(mp_const_obj_t object, mp_const_obj_t classinfo) { #endif } else { // type has 1 parent - object = MP_OBJ_FROM_PTR(parent); + object = MP_OBJ_FROM_PTR(MP_OBJ_TYPE_GET_SLOT(self, parent)); } } } @@ -1527,14 +1529,16 @@ STATIC mp_obj_t static_class_method_make_new(const mp_obj_type_t *self, size_t n return MP_OBJ_FROM_PTR(o); } -const mp_obj_type_t mp_type_staticmethod = { - { &mp_type_type }, - .name = MP_QSTR_staticmethod, - .make_new = static_class_method_make_new, -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_staticmethod, + MP_QSTR_staticmethod, + MP_TYPE_FLAG_NONE, + make_new, static_class_method_make_new + ); -const mp_obj_type_t mp_type_classmethod = { - { &mp_type_type }, - .name = MP_QSTR_classmethod, - .make_new = static_class_method_make_new, -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_classmethod, + MP_QSTR_classmethod, + MP_TYPE_FLAG_NONE, + make_new, static_class_method_make_new + ); diff --git a/py/objtype.h b/py/objtype.h index 17b8048c19..839cc6d146 100644 --- a/py/objtype.h +++ b/py/objtype.h @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2013, 2014 Damien P. George * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -37,8 +37,6 @@ typedef struct _mp_obj_instance_t { // TODO maybe cache __getattr__ and __setattr__ for efficient lookup of them } mp_obj_instance_t; -void mp_obj_assert_native_inited(mp_obj_t native_object); - #if MICROPY_CPYTHON_COMPAT // this is needed for object.__new__ mp_obj_instance_t *mp_obj_new_instance(const mp_obj_type_t *cls, const mp_obj_type_t **native_base); @@ -48,10 +46,8 @@ mp_obj_instance_t *mp_obj_new_instance(const mp_obj_type_t *cls, const mp_obj_ty bool mp_obj_instance_is_callable(mp_obj_t self_in); mp_obj_t mp_obj_instance_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args); -#define mp_obj_is_instance_type(type) ((type)->make_new == mp_obj_instance_make_new) -#define mp_obj_is_native_type(type) ((type)->make_new != mp_obj_instance_make_new) -// this needs to be exposed for the above macros to work correctly -mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args); +#define mp_obj_is_instance_type(type) ((type)->flags & MP_TYPE_FLAG_INSTANCE_TYPE) +#define mp_obj_is_native_type(type) (!((type)->flags & MP_TYPE_FLAG_INSTANCE_TYPE)) // this needs to be exposed for mp_getiter mp_obj_t mp_obj_instance_getiter(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf); diff --git a/py/objzip.c b/py/objzip.c index 69aca29e9a..3c3138180a 100644 --- a/py/objzip.c +++ b/py/objzip.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2013, 2014 Damien P. George * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -66,13 +66,10 @@ STATIC mp_obj_t zip_iternext(mp_obj_t self_in) { return MP_OBJ_FROM_PTR(tuple); } -const mp_obj_type_t mp_type_zip = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_zip, - .make_new = zip_make_new, - MP_TYPE_EXTENDED_FIELDS( - .getiter = mp_identity_getiter, - .iternext = zip_iternext, - ), -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_zip, + MP_QSTR_zip, + MP_TYPE_FLAG_ITER_IS_ITERNEXT, + make_new, zip_make_new, + iter, zip_iternext + ); diff --git a/py/opmethods.c b/py/opmethods.c index 6ebd469113..c3931fd358 100644 --- a/py/opmethods.c +++ b/py/opmethods.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2013, 2014 Damien P. George * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,25 +29,28 @@ STATIC mp_obj_t op_getitem(mp_obj_t self_in, mp_obj_t key_in) { const mp_obj_type_t *type = mp_obj_get_type(self_in); - return type->ext[0].subscr(self_in, key_in, MP_OBJ_SENTINEL); + // Note: assumes type must have subscr (only used by dict). + return MP_OBJ_TYPE_GET_SLOT(type, subscr)(self_in, key_in, MP_OBJ_SENTINEL); } MP_DEFINE_CONST_FUN_OBJ_2(mp_op_getitem_obj, op_getitem); STATIC mp_obj_t op_setitem(mp_obj_t self_in, mp_obj_t key_in, mp_obj_t value_in) { const mp_obj_type_t *type = mp_obj_get_type(self_in); - return type->ext[0].subscr(self_in, key_in, value_in); + // Note: assumes type must have subscr (only used by dict). + return MP_OBJ_TYPE_GET_SLOT(type, subscr)(self_in, key_in, value_in); } MP_DEFINE_CONST_FUN_OBJ_3(mp_op_setitem_obj, op_setitem); STATIC mp_obj_t op_delitem(mp_obj_t self_in, mp_obj_t key_in) { const mp_obj_type_t *type = mp_obj_get_type(self_in); - return type->ext[0].subscr(self_in, key_in, MP_OBJ_NULL); + // Note: assumes type must have subscr (only used by dict). + return MP_OBJ_TYPE_GET_SLOT(type, subscr)(self_in, key_in, MP_OBJ_NULL); } MP_DEFINE_CONST_FUN_OBJ_2(mp_op_delitem_obj, op_delitem); STATIC mp_obj_t op_contains(mp_obj_t lhs_in, mp_obj_t rhs_in) { const mp_obj_type_t *type = mp_obj_get_type(lhs_in); - mp_binary_op_fun_t binary_op = mp_type_get_binary_op_slot(type); - return binary_op(MP_BINARY_OP_CONTAINS, lhs_in, rhs_in); + // Note: assumes type must have binary_op (only used by set/frozenset). + return MP_OBJ_TYPE_GET_SLOT(type, binary_op)(MP_BINARY_OP_CONTAINS, lhs_in, rhs_in); } MP_DEFINE_CONST_FUN_OBJ_2(mp_op_contains_obj, op_contains); diff --git a/py/parse.c b/py/parse.c index c8a3b29bb5..50f9586835 100644 --- a/py/parse.c +++ b/py/parse.c @@ -39,8 +39,6 @@ #include "py/objstr.h" #include "py/builtin.h" -#include "supervisor/shared/translate/translate.h" - #if MICROPY_ENABLE_COMPILER #define RULE_ACT_ARG_MASK (0x0f) @@ -340,16 +338,6 @@ STATIC uint8_t peek_rule(parser_t *parser, size_t n) { } #endif -bool mp_parse_node_is_const_false(mp_parse_node_t pn) { - return MP_PARSE_NODE_IS_TOKEN_KIND(pn, MP_TOKEN_KW_FALSE) - || (MP_PARSE_NODE_IS_SMALL_INT(pn) && MP_PARSE_NODE_LEAF_SMALL_INT(pn) == 0); -} - -bool mp_parse_node_is_const_true(mp_parse_node_t pn) { - return MP_PARSE_NODE_IS_TOKEN_KIND(pn, MP_TOKEN_KW_TRUE) - || (MP_PARSE_NODE_IS_SMALL_INT(pn) && MP_PARSE_NODE_LEAF_SMALL_INT(pn) != 0); -} - bool mp_parse_node_get_int_maybe(mp_parse_node_t pn, mp_obj_t *o) { if (MP_PARSE_NODE_IS_SMALL_INT(pn)) { *o = MP_OBJ_NEW_SMALL_INT(MP_PARSE_NODE_LEAF_SMALL_INT(pn)); @@ -433,6 +421,24 @@ STATIC mp_obj_t mp_parse_node_convert_to_obj(mp_parse_node_t pn) { } #endif +STATIC bool parse_node_is_const_bool(mp_parse_node_t pn, bool value) { + // Returns true if 'pn' is a constant whose boolean value is equivalent to 'value' + #if MICROPY_COMP_CONST_TUPLE || MICROPY_COMP_CONST + return mp_parse_node_is_const(pn) && mp_obj_is_true(mp_parse_node_convert_to_obj(pn)) == value; + #else + return MP_PARSE_NODE_IS_TOKEN_KIND(pn, value ? MP_TOKEN_KW_TRUE : MP_TOKEN_KW_FALSE) + || (MP_PARSE_NODE_IS_SMALL_INT(pn) && !!MP_PARSE_NODE_LEAF_SMALL_INT(pn) == value); + #endif +} + +bool mp_parse_node_is_const_false(mp_parse_node_t pn) { + return parse_node_is_const_bool(pn, false); +} + +bool mp_parse_node_is_const_true(mp_parse_node_t pn) { + return parse_node_is_const_bool(pn, true); +} + size_t mp_parse_node_extract_list(mp_parse_node_t *pn, size_t pn_kind, mp_parse_node_t **nodes) { if (MP_PARSE_NODE_IS_NULL(*pn)) { *nodes = NULL; @@ -597,7 +603,7 @@ STATIC void push_result_token(parser_t *parser, uint8_t rule_id) { mp_obj_t o = mp_parse_num_integer(lex->vstr.buf, lex->vstr.len, 0, lex); pn = make_node_const_object_optimised(parser, lex->tok_line, o); } else if (lex->tok_kind == MP_TOKEN_FLOAT_OR_IMAG) { - mp_obj_t o = mp_parse_num_decimal(lex->vstr.buf, lex->vstr.len, true, false, lex); + mp_obj_t o = mp_parse_num_float(lex->vstr.buf, lex->vstr.len, true, lex); pn = make_node_const_object(parser, lex->tok_line, o); } else if (lex->tok_kind == MP_TOKEN_STRING) { // Don't automatically intern all strings. Doc strings (which are usually large) diff --git a/py/parsenum.c b/py/parsenum.c index 24d46705a7..b2658eac77 100644 --- a/py/parsenum.c +++ b/py/parsenum.c @@ -32,8 +32,6 @@ #include "py/parsenum.h" #include "py/smallint.h" -#include "supervisor/shared/translate/translate.h" - #if MICROPY_PY_BUILTINS_FLOAT #include #endif @@ -162,43 +160,79 @@ value_error: mp_printf(&print, "invalid syntax for integer with base %d: ", base); mp_str_print_quoted(&print, str_val_start, top - str_val_start, true); mp_obj_t exc = mp_obj_new_exception_arg1(&mp_type_ValueError, - mp_obj_new_str_from_vstr(&mp_type_str, &vstr)); + mp_obj_new_str_from_utf8_vstr(&vstr)); raise_exc(exc, lex); #endif } } +enum { + REAL_IMAG_STATE_START = 0, + REAL_IMAG_STATE_HAVE_REAL = 1, + REAL_IMAG_STATE_HAVE_IMAG = 2, +}; + typedef enum { PARSE_DEC_IN_INTG, PARSE_DEC_IN_FRAC, PARSE_DEC_IN_EXP, } parse_dec_in_t; -mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool force_complex, mp_lexer_t *lex) { - #if MICROPY_PY_BUILTINS_FLOAT - +#if MICROPY_PY_BUILTINS_FLOAT // DEC_VAL_MAX only needs to be rough and is used to retain precision while not overflowing // SMALL_NORMAL_VAL is the smallest power of 10 that is still a normal float // EXACT_POWER_OF_10 is the largest value of x so that 10^x can be stored exactly in a float // Note: EXACT_POWER_OF_10 is at least floor(log_5(2^mantissa_length)). Indeed, 10^n = 2^n * 5^n // so we only have to store the 5^n part in the mantissa (the 2^n part will go into the float's // exponent). - #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT +#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT #define DEC_VAL_MAX 1e20F #define SMALL_NORMAL_VAL (1e-37F) #define SMALL_NORMAL_EXP (-37) #define EXACT_POWER_OF_10 (9) - #elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE +#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE #define DEC_VAL_MAX 1e200 #define SMALL_NORMAL_VAL (1e-307) #define SMALL_NORMAL_EXP (-307) #define EXACT_POWER_OF_10 (22) - #endif +#endif + +// Break out inner digit accumulation routine to ease trailing zero deferral. +static void accept_digit(mp_float_t *p_dec_val, int dig, int *p_exp_extra, int in) { + // Core routine to ingest an additional digit. + if (*p_dec_val < DEC_VAL_MAX) { + // dec_val won't overflow so keep accumulating + *p_dec_val = 10 * *p_dec_val + dig; + if (in == PARSE_DEC_IN_FRAC) { + --(*p_exp_extra); + } + } else { + // dec_val might overflow and we anyway can't represent more digits + // of precision, so ignore the digit and just adjust the exponent + if (in == PARSE_DEC_IN_INTG) { + ++(*p_exp_extra); + } + } +} +#endif // MICROPY_BUILTINS_FLOAT + +#if MICROPY_PY_BUILTINS_COMPLEX +mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool force_complex, mp_lexer_t *lex) +#else +mp_obj_t mp_parse_num_float(const char *str, size_t len, bool allow_imag, mp_lexer_t *lex) +#endif +{ + #if MICROPY_PY_BUILTINS_FLOAT const char *top = str + len; mp_float_t dec_val = 0; bool dec_neg = false; - bool imag = false; + + #if MICROPY_PY_BUILTINS_COMPLEX + unsigned int real_imag_state = REAL_IMAG_STATE_START; + mp_float_t dec_real = 0; +parse_start: + #endif // skip leading space for (; str < top && unichar_isspace(*str); str++) { @@ -241,6 +275,7 @@ mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool bool exp_neg = false; int exp_val = 0; int exp_extra = 0; + int trailing_zeros_intg = 0, trailing_zeros_frac = 0; while (str < top) { unsigned int dig = *str++; if ('0' <= dig && dig <= '9') { @@ -253,18 +288,25 @@ mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool exp_val = 10 * exp_val + dig; } } else { - if (dec_val < DEC_VAL_MAX) { - // dec_val won't overflow so keep accumulating - dec_val = 10 * dec_val + dig; - if (in == PARSE_DEC_IN_FRAC) { - --exp_extra; + if (dig == 0 || dec_val >= DEC_VAL_MAX) { + // Defer treatment of zeros in fractional part. If nothing comes afterwards, ignore them. + // Also, once we reach DEC_VAL_MAX, treat every additional digit as a trailing zero. + if (in == PARSE_DEC_IN_INTG) { + ++trailing_zeros_intg; + } else { + ++trailing_zeros_frac; } } else { - // dec_val might overflow and we anyway can't represent more digits - // of precision, so ignore the digit and just adjust the exponent - if (in == PARSE_DEC_IN_INTG) { - ++exp_extra; + // Time to un-defer any trailing zeros. Intg zeros first. + while (trailing_zeros_intg) { + accept_digit(&dec_val, 0, &exp_extra, PARSE_DEC_IN_INTG); + --trailing_zeros_intg; } + while (trailing_zeros_frac) { + accept_digit(&dec_val, 0, &exp_extra, PARSE_DEC_IN_FRAC); + --trailing_zeros_frac; + } + accept_digit(&dec_val, dig, &exp_extra, in); } } } else if (in == PARSE_DEC_IN_INTG && dig == '.') { @@ -282,9 +324,6 @@ mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool if (str == top) { goto value_error; } - } else if (allow_imag && (dig | 0x20) == 'j') { - imag = true; - break; } else if (dig == '_') { continue; } else { @@ -300,7 +339,7 @@ mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool } // apply the exponent, making sure it's not a subnormal value - exp_val += exp_extra; + exp_val += exp_extra + trailing_zeros_intg; if (exp_val < SMALL_NORMAL_EXP) { exp_val -= SMALL_NORMAL_EXP; dec_val *= SMALL_NORMAL_VAL; @@ -318,6 +357,19 @@ mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool } } + if (allow_imag && str < top && (*str | 0x20) == 'j') { + #if MICROPY_PY_BUILTINS_COMPLEX + if (str == str_val_start) { + // Convert "j" to "1j". + dec_val = 1; + } + ++str; + real_imag_state |= REAL_IMAG_STATE_HAVE_IMAG; + #else + raise_exc(mp_obj_new_exception_msg(&mp_type_ValueError, MP_ERROR_TEXT("complex values not supported")), lex); + #endif + } + // negate value if needed if (dec_neg) { dec_val = -dec_val; @@ -334,24 +386,36 @@ mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool // check we reached the end of the string if (str != top) { + #if MICROPY_PY_BUILTINS_COMPLEX + if (force_complex && real_imag_state == REAL_IMAG_STATE_START) { + // If we've only seen a real so far, keep parsing for the imaginary part. + dec_real = dec_val; + dec_val = 0; + real_imag_state |= REAL_IMAG_STATE_HAVE_REAL; + goto parse_start; + } + #endif goto value_error; } - // return the object #if MICROPY_PY_BUILTINS_COMPLEX - if (imag) { - return mp_obj_new_complex(0, dec_val); + if (real_imag_state == REAL_IMAG_STATE_HAVE_REAL) { + // We're on the second part, but didn't get the expected imaginary number. + goto value_error; + } + #endif + + // return the object + + #if MICROPY_PY_BUILTINS_COMPLEX + if (real_imag_state != REAL_IMAG_STATE_START) { + return mp_obj_new_complex(dec_real, dec_val); } else if (force_complex) { return mp_obj_new_complex(dec_val, 0); } - #else - if (imag || force_complex) { - raise_exc(mp_obj_new_exception_msg(&mp_type_ValueError, MP_ERROR_TEXT("complex values not supported")), lex); - } #endif - else { - return mp_obj_new_float(dec_val); - } + + return mp_obj_new_float(dec_val); value_error: raise_exc(mp_obj_new_exception_msg(&mp_type_ValueError, MP_ERROR_TEXT("invalid syntax for number")), lex); diff --git a/py/parsenum.h b/py/parsenum.h index a91ca532da..a0c738659c 100644 --- a/py/parsenum.h +++ b/py/parsenum.h @@ -31,7 +31,21 @@ #include "py/obj.h" // these functions raise a SyntaxError if lex!=NULL, else a ValueError + mp_obj_t mp_parse_num_integer(const char *restrict str, size_t len, int base, mp_lexer_t *lex); + +#if MICROPY_PY_BUILTINS_COMPLEX mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool force_complex, mp_lexer_t *lex); +static inline mp_obj_t mp_parse_num_float(const char *str, size_t len, bool allow_imag, mp_lexer_t *lex) { + return mp_parse_num_decimal(str, len, allow_imag, false, lex); +} + +static inline mp_obj_t mp_parse_num_complex(const char *str, size_t len, mp_lexer_t *lex) { + return mp_parse_num_decimal(str, len, true, true, lex); +} +#else +mp_obj_t mp_parse_num_float(const char *str, size_t len, bool allow_imag, mp_lexer_t *lex); +#endif + #endif // MICROPY_INCLUDED_PY_PARSENUM_H diff --git a/py/persistentcode.c b/py/persistentcode.c index 272298274f..4cc72d63c9 100644 --- a/py/persistentcode.c +++ b/py/persistentcode.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * SPDX-FileCopyrightText: Copyright (c) 2013-2020 Damien P. George + * Copyright (c) 2013-2020 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 @@ -208,12 +208,16 @@ STATIC mp_obj_t load_obj(mp_reader_t *reader) { read_bytes(reader, (byte *)vstr.buf, len); if (obj_type == MP_PERSISTENT_OBJ_STR || obj_type == MP_PERSISTENT_OBJ_BYTES) { read_byte(reader); // skip null terminator - return mp_obj_new_str_from_vstr(obj_type == MP_PERSISTENT_OBJ_STR ? &mp_type_str : &mp_type_bytes, &vstr); + if (obj_type == MP_PERSISTENT_OBJ_STR) { + return mp_obj_new_str_from_utf8_vstr(&vstr); + } else { + return mp_obj_new_bytes_from_vstr(&vstr); + } } else if (obj_type == MP_PERSISTENT_OBJ_INT) { return mp_parse_num_integer(vstr.buf, vstr.len, 10, NULL); } else { assert(obj_type == MP_PERSISTENT_OBJ_FLOAT || obj_type == MP_PERSISTENT_OBJ_COMPLEX); - return mp_parse_num_decimal(vstr.buf, vstr.len, obj_type == MP_PERSISTENT_OBJ_COMPLEX, false, NULL); + return mp_parse_num_float(vstr.buf, vstr.len, obj_type == MP_PERSISTENT_OBJ_COMPLEX, NULL); } } } @@ -392,45 +396,49 @@ STATIC mp_raw_code_t *load_raw_code(mp_reader_t *reader, mp_module_context_t *co return rc; } -mp_compiled_module_t mp_raw_code_load(mp_reader_t *reader, mp_module_context_t *context) { +void mp_raw_code_load(mp_reader_t *reader, mp_compiled_module_t *cm) { byte header[4]; read_bytes(reader, header, sizeof(header)); - if (header[0] != 'C' + if (header[0] != 'M' || header[1] != MPY_VERSION - || MPY_FEATURE_DECODE_FLAGS(header[2]) != MPY_FEATURE_FLAGS + || (arch != MP_NATIVE_ARCH_NONE && MPY_FEATURE_DECODE_SUB_VERSION(header[2]) != MPY_SUB_VERSION) || header[3] > MP_SMALL_INT_BITS) { mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy file")); } if (MPY_FEATURE_DECODE_ARCH(header[2]) != MP_NATIVE_ARCH_NONE) { byte arch = MPY_FEATURE_DECODE_ARCH(header[2]); if (!MPY_FEATURE_ARCH_TEST(arch)) { - mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy arch")); + if (MPY_FEATURE_ARCH_TEST(MP_NATIVE_ARCH_NONE)) { + // On supported ports this can be resolved by enabling feature, eg + // mpconfigboard.h: MICROPY_EMIT_THUMB (1) + mp_raise_ValueError(MP_ERROR_TEXT("native code in .mpy unsupported")); + } else { + mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy arch")); + } } } size_t n_qstr = read_uint(reader); size_t n_obj = read_uint(reader); - mp_module_context_alloc_tables(context, n_qstr, n_obj); + mp_module_context_alloc_tables(cm->context, n_qstr, n_obj); // Load qstrs. for (size_t i = 0; i < n_qstr; ++i) { - context->constants.qstr_table[i] = load_qstr(reader); + cm->context->constants.qstr_table[i] = load_qstr(reader); } // Load constant objects. for (size_t i = 0; i < n_obj; ++i) { - context->constants.obj_table[i] = load_obj(reader); + cm->context->constants.obj_table[i] = load_obj(reader); } // Load top-level module. - mp_compiled_module_t cm2; - cm2.rc = load_raw_code(reader, context); - cm2.context = context; + cm->rc = load_raw_code(reader, cm->context); #if MICROPY_PERSISTENT_CODE_SAVE - cm2.has_native = MPY_FEATURE_DECODE_ARCH(header[2]) != MP_NATIVE_ARCH_NONE; - cm2.n_qstr = n_qstr; - cm2.n_obj = n_obj; + cm->has_native = MPY_FEATURE_DECODE_ARCH(header[2]) != MP_NATIVE_ARCH_NONE; + cm->n_qstr = n_qstr; + cm->n_obj = n_obj; #endif reader->close(reader->data); @@ -438,18 +446,18 @@ mp_compiled_module_t mp_raw_code_load(mp_reader_t *reader, mp_module_context_t * return cm2; } -mp_compiled_module_t mp_raw_code_load_mem(const byte *buf, size_t len, mp_module_context_t *context) { +void mp_raw_code_load_mem(const byte *buf, size_t len, mp_compiled_module_t *context) { mp_reader_t reader; mp_reader_new_mem(&reader, buf, len, 0); - return mp_raw_code_load(&reader, context); + mp_raw_code_load(&reader, context); } #if MICROPY_HAS_FILE_READER -mp_compiled_module_t mp_raw_code_load_file(const char *filename, mp_module_context_t *context) { +void mp_raw_code_load_file(const char *filename, mp_compiled_module_t *context) { mp_reader_t reader; mp_reader_new_file(&reader, filename); - return mp_raw_code_load(&reader, context); + mp_raw_code_load(&reader, context); } #endif // MICROPY_HAS_FILE_READER @@ -585,23 +593,20 @@ 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: - // byte 'C' + // byte 'M' // byte version - // byte feature flags + // byte native arch (and sub-version if native) // byte number of bits in a small int byte header[4] = { - 'C', + 'M', MPY_VERSION, - MPY_FEATURE_ENCODE_FLAGS(MPY_FEATURE_FLAGS_DYNAMIC), + cm->has_native ? MPY_FEATURE_ENCODE_SUB_VERSION(MPY_SUB_VERSION) | MPY_FEATURE_ENCODE_ARCH(MPY_FEATURE_ARCH_DYNAMIC) : 0, #if MICROPY_DYNAMIC_COMPILER mp_dynamic_compiler.small_int_bits, #else MP_SMALL_INT_BITS, #endif }; - if (cm->has_native) { - header[2] |= MPY_FEATURE_ENCODE_ARCH(MPY_FEATURE_ARCH_DYNAMIC); - } mp_print_bytes(print, header, sizeof(header)); // Number of entries in constant table. @@ -640,6 +645,9 @@ void mp_raw_code_save_file(mp_compiled_module_t *cm, const char *filename) { MP_THREAD_GIL_EXIT(); int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644); MP_THREAD_GIL_ENTER(); + if (fd < 0) { + mp_raise_OSError_with_filename(errno, filename); + } mp_print_t fd_print = {(void *)(intptr_t)fd, fd_print_strn}; mp_raw_code_save(cm, &fd_print); MP_THREAD_GIL_EXIT(); @@ -650,3 +658,8 @@ void mp_raw_code_save_file(mp_compiled_module_t *cm, const char *filename) { #endif // MICROPY_PERSISTENT_CODE_SAVE_FILE #endif // MICROPY_PERSISTENT_CODE_SAVE + +#if MICROPY_PERSISTENT_CODE_TRACK_RELOC_CODE +// An mp_obj_list_t that tracks relocated native code to prevent the GC from reclaiming them. +MP_REGISTER_ROOT_POINTER(mp_obj_t track_reloc_code_list); +#endif diff --git a/py/persistentcode.h b/py/persistentcode.h index 7ba86c1eb7..f68ce1a7e1 100644 --- a/py/persistentcode.h +++ b/py/persistentcode.h @@ -30,24 +30,23 @@ #include "py/reader.h" #include "py/emitglue.h" -// The current version of .mpy files +// The current version of .mpy files. A bytecode-only .mpy file can be loaded +// as long as MPY_VERSION matches, but a native .mpy (i.e. one with an arch +// 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 -// Macros to encode/decode flags to/from the feature byte -#define MPY_FEATURE_ENCODE_FLAGS(flags) (flags) -#define MPY_FEATURE_DECODE_FLAGS(feat) ((feat) & 3) +// 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) +// which are no longer used starting at .mpy version 6. +#define MPY_FEATURE_ENCODE_SUB_VERSION(version) (version) +#define MPY_FEATURE_DECODE_SUB_VERSION(feat) ((feat) & 3) // Macros to encode/decode native architecture to/from the feature byte #define MPY_FEATURE_ENCODE_ARCH(arch) ((arch) << 2) #define MPY_FEATURE_DECODE_ARCH(feat) ((feat) >> 2) -// The feature flag bits encode the compile-time config options that affect -// the generate bytecode. Note: no longer used. -// (formerly MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE and MICROPY_PY_BUILTINS_STR_UNICODE). -#define MPY_FEATURE_FLAGS (0) -// This is a version of the flags that can be configured at runtime. -#define MPY_FEATURE_FLAGS_DYNAMIC (0) - // Define the host architecture #if MICROPY_EMIT_X86 #define MPY_FEATURE_ARCH (MP_NATIVE_ARCH_X86) @@ -82,7 +81,7 @@ // 16-bit little-endian integer with the second and third bytes of supported .mpy files #define MPY_FILE_HEADER_INT (MPY_VERSION \ - | (MPY_FEATURE_ENCODE_FLAGS(MPY_FEATURE_FLAGS) | MPY_FEATURE_ENCODE_ARCH(MPY_FEATURE_ARCH)) << 8) + | (MPY_FEATURE_ENCODE_SUB_VERSION(MPY_SUB_VERSION) | MPY_FEATURE_ENCODE_ARCH(MPY_FEATURE_ARCH)) << 8) enum { MP_NATIVE_ARCH_NONE = 0, @@ -112,9 +111,9 @@ enum { MP_PERSISTENT_OBJ_TUPLE, }; -mp_compiled_module_t mp_raw_code_load(mp_reader_t *reader, mp_module_context_t *ctx); -mp_compiled_module_t mp_raw_code_load_mem(const byte *buf, size_t len, mp_module_context_t *ctx); -mp_compiled_module_t mp_raw_code_load_file(const char *filename, mp_module_context_t *ctx); +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_save(mp_compiled_module_t *cm, mp_print_t *print); void mp_raw_code_save_file(mp_compiled_module_t *cm, const char *filename); diff --git a/py/profile.c b/py/profile.c index 4e23e9eac4..fd2d61caa9 100644 --- a/py/profile.c +++ b/py/profile.c @@ -172,13 +172,14 @@ STATIC void code_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { } } -const mp_obj_type_t mp_type_settrace_codeobj = { - { &mp_type_type }, - .name = MP_QSTR_code, - .print = code_print, - .unary_op = mp_generic_unary_op, - .attr = code_attr, -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_settrace_codeobj, + MP_QSTR_code, + MP_TYPE_FLAG_NONE, + print, code_print, + unary_op, mp_generic_unary_op, + attr, code_attr + ); mp_obj_t mp_obj_new_code(const mp_module_context_t *context, const mp_raw_code_t *rc) { mp_obj_code_t *o = m_new_obj_maybe(mp_obj_code_t); @@ -241,13 +242,14 @@ STATIC void frame_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { } } -const mp_obj_type_t mp_type_frame = { - { &mp_type_type }, - .name = MP_QSTR_frame, - .print = frame_print, - .unary_op = mp_generic_unary_op, - .attr = frame_attr, -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_type_frame, + MP_QSTR_frame, + MP_TYPE_FLAG_NONE, + print, frame_print, + unary_op, mp_generic_unary_op, + attr, frame_attr + ); mp_obj_t mp_obj_new_frame(const mp_code_state_t *code_state) { if (gc_is_locked()) { diff --git a/py/proto.c b/py/proto.c index d70a9d2aac..9f110b9f9a 100644 --- a/py/proto.c +++ b/py/proto.c @@ -28,8 +28,6 @@ #include "py/proto.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" - #ifndef MICROPY_UNSAFE_PROTO const void *mp_proto_get(uint16_t name, mp_const_obj_t obj) { const mp_obj_type_t *type = mp_obj_get_type(obj); diff --git a/py/py.cmake b/py/py.cmake index 6f0514e0e3..d33e8a801f 100644 --- a/py/py.cmake +++ b/py/py.cmake @@ -53,6 +53,7 @@ set(MICROPY_SOURCE_PY ${MICROPY_PY_DIR}/mpz.c ${MICROPY_PY_DIR}/nativeglue.c ${MICROPY_PY_DIR}/nlr.c + ${MICROPY_PY_DIR}/nlrmips.c ${MICROPY_PY_DIR}/nlrpowerpc.c ${MICROPY_PY_DIR}/nlrsetjmp.c ${MICROPY_PY_DIR}/nlrthumb.c diff --git a/py/py.mk b/py/py.mk index 81da81ba0f..66ba7f466f 100644 --- a/py/py.mk +++ b/py/py.mk @@ -34,24 +34,46 @@ endif ifneq ($(USER_C_MODULES),) # pre-define USERMOD variables as expanded so that variables are immediate # expanded as they're added to them -SRC_USERMOD := + +# C/C++ files that are included in the QSTR/module build +SRC_USERMOD_C := SRC_USERMOD_CXX := +# Other C/C++ files (e.g. libraries or helpers) +SRC_USERMOD_LIB_C := +SRC_USERMOD_LIB_CXX := +# Optionally set flags CFLAGS_USERMOD := CXXFLAGS_USERMOD := LDFLAGS_USERMOD := + +# Backwards compatibility with older user c modules that set SRC_USERMOD +# added to SRC_USERMOD_C below +SRC_USERMOD := + $(foreach module, $(wildcard $(USER_C_MODULES)/*/micropython.mk), \ $(eval USERMOD_DIR = $(patsubst %/,%,$(dir $(module))))\ $(info Including User C Module from $(USERMOD_DIR))\ $(eval include $(module))\ ) -SRC_MOD += $(patsubst $(USER_C_MODULES)/%.c,%.c,$(SRC_USERMOD)) -SRC_MOD_CXX += $(patsubst $(USER_C_MODULES)/%.cpp,%.cpp,$(SRC_USERMOD_CXX)) -CFLAGS_MOD += $(CFLAGS_USERMOD) -CXXFLAGS_MOD += $(CXXFLAGS_USERMOD) -LDFLAGS_MOD += $(LDFLAGS_USERMOD) -endif +SRC_USERMOD_C += $(SRC_USERMOD) +SRC_USERMOD_PATHFIX_C += $(patsubst $(USER_C_MODULES)/%.c,%.c,$(SRC_USERMOD_C)) +SRC_USERMOD_PATHFIX_CXX += $(patsubst $(USER_C_MODULES)/%.cpp,%.cpp,$(SRC_USERMOD_CXX)) +SRC_USERMOD_PATHFIX_LIB_C += $(patsubst $(USER_C_MODULES)/%.c,%.c,$(SRC_USERMOD_LIB_C)) +SRC_USERMOD_PATHFIX_LIB_CXX += $(patsubst $(USER_C_MODULES)/%.cpp,%.cpp,$(SRC_USERMOD_LIB_CXX)) + +CFLAGS += $(CFLAGS_USERMOD) +CXXFLAGS += $(CXXFLAGS_USERMOD) +LDFLAGS += $(LDFLAGS_USERMOD) + +SRC_QSTR += $(SRC_USERMOD_PATHFIX_C) $(SRC_USERMOD_PATHFIX_CXX) +PY_O += $(addprefix $(BUILD)/, $(SRC_USERMOD_PATHFIX_C:.c=.o)) +PY_O += $(addprefix $(BUILD)/, $(SRC_USERMOD_PATHFIX_CXX:.cpp=.o)) +PY_O += $(addprefix $(BUILD)/, $(SRC_USERMOD_PATHFIX_LIB_C:.c=.o)) +PY_O += $(addprefix $(BUILD)/, $(SRC_USERMOD_PATHFIX_LIB_CXX:.cpp=.o)) + +// CIRCUITPY ifeq ($(CIRCUITPY_ULAB),1) ULAB_SRCS := $(shell find $(TOP)/extmod/ulab/code -type f -name "*.c") SRC_MOD += $(patsubst $(TOP)/%,%,$(ULAB_SRCS)) @@ -70,6 +92,7 @@ PY_CORE_O_BASENAME = $(addprefix py/,\ nlrx64.o \ nlrthumb.o \ nlraarch64.o \ + nlrmips.o \ nlrpowerpc.o \ nlrxtensa.o \ nlrsetjmp.o \ @@ -118,7 +141,6 @@ PY_CORE_O_BASENAME = $(addprefix py/,\ warning.o \ profile.o \ map.o \ - enum.o \ obj.o \ objarray.o \ objattrtuple.o \ @@ -155,12 +177,10 @@ PY_CORE_O_BASENAME = $(addprefix py/,\ objstr.o \ objstrunicode.o \ objstringio.o \ - objtraceback.o \ objtuple.o \ objtype.o \ objzip.o \ opmethods.o \ - proto.o \ sequence.o \ stream.o \ binary.o \ @@ -187,38 +207,11 @@ PY_CORE_O_BASENAME = $(addprefix py/,\ frozenmod.o \ ) -PY_EXTMOD_O_BASENAME = \ - extmod/moduasyncio.o \ - extmod/moductypes.o \ - extmod/modujson.o \ - extmod/moduos.o \ - extmod/modure.o \ - extmod/moduzlib.o \ - extmod/moduheapq.o \ - extmod/modutimeq.o \ - extmod/moduhashlib.o \ - extmod/modubinascii.o \ - extmod/modurandom.o \ - extmod/moduselect.o \ - extmod/vfs.o \ - extmod/vfs_blockdev.o \ - extmod/vfs_reader.o \ - extmod/vfs_posix.o \ - extmod/vfs_posix_file.o \ - extmod/vfs_fat.o \ - extmod/vfs_fat_diskio.o \ - extmod/vfs_fat_file.o \ - extmod/vfs_lfs.o \ - extmod/utime_mphal.o \ - shared/libc/abort_.o \ - shared/libc/printf.o \ - # prepend the build destination prefix to the py object files PY_CORE_O = $(addprefix $(BUILD)/, $(PY_CORE_O_BASENAME)) -PY_EXTMOD_O = $(addprefix $(BUILD)/, $(PY_EXTMOD_O_BASENAME)) # this is a convenience variable for ports that want core, extmod and frozen code -PY_O = $(PY_CORE_O) $(PY_EXTMOD_O) +PY_O += $(PY_CORE_O) # object file for frozen code specified via a manifest ifneq ($(FROZEN_MANIFEST),) @@ -227,14 +220,13 @@ endif # Sources that may contain qstrings SRC_QSTR_IGNORE = py/nlr% -SRC_QSTR += $(SRC_MOD) $(filter-out $(SRC_QSTR_IGNORE),$(PY_CORE_O_BASENAME:.o=.c)) $(PY_EXTMOD_O_BASENAME:.o=.c) +SRC_QSTR += $(filter-out $(SRC_QSTR_IGNORE),$(PY_CORE_O_BASENAME:.o=.c)) # Anything that depends on FORCE will be considered out-of-date FORCE: .PHONY: FORCE $(HEADER_BUILD)/mpversion.h: FORCE | $(HEADER_BUILD) - $(STEPECHO) "GEN $@" $(Q)$(PYTHON) $(PY_SRC)/makeversionhdr.py $@ # mpconfigport.mk is optional, but changes to it may drastically change @@ -256,30 +248,16 @@ $(HEADER_BUILD)/compressed.data.h: $(HEADER_BUILD)/compressed.collected $(ECHO) "GEN $@" $(Q)$(PYTHON) $(PY_SRC)/makecompresseddata.py $< > $@ -// CIRCUITPY: for translations -$(HEADER_BUILD)/$(TRANSLATION).mo: $(TOP)/locale/$(TRANSLATION).po | $(HEADER_BUILD) - $(Q)$(PYTHON) $(TOP)/tools/msgfmt.py -o $@ $^ - -# translations-*.c is generated as a side-effect of building compressed_translations.generated.h -# Specifying both in a single rule actually causes the rule to be run twice! -# This alternative makes it run just once. -# Another alternative is "grouped targets" (`a b &: c`), available in GNU make 4.3 and later. -# TODO: use grouped targets when we expect GNU make >= 4.3 is pervasive. -$(PY_BUILD)/translations-$(TRANSLATION).c: $(HEADER_BUILD)/compressed_translations.generated.h - @true - -$(HEADER_BUILD)/compressed_translations.generated.h: $(PY_SRC)/maketranslationdata.py $(HEADER_BUILD)/$(TRANSLATION).mo $(HEADER_BUILD)/qstrdefs.generated.h - $(STEPECHO) "GEN $@" - $(Q)mkdir -p $(PY_BUILD) - $(Q)$(PYTHON) $(PY_SRC)/maketranslationdata.py --compression_filename $(HEADER_BUILD)/compressed_translations.generated.h --translation $(HEADER_BUILD)/$(TRANSLATION).mo --translation_filename $(PY_BUILD)/translations-$(TRANSLATION).c $(HEADER_BUILD)/qstrdefs.preprocessed.h - -PY_CORE_O += $(PY_BUILD)/translations-$(TRANSLATION).o - # build a list of registered modules for py/objmodule.c. $(HEADER_BUILD)/moduledefs.h: $(HEADER_BUILD)/moduledefs.collected - @$(STEPECHO) "GEN $@" + @$(ECHO) "GEN $@" $(Q)$(PYTHON) $(PY_SRC)/makemoduledefs.py $< > $@ +# build a list of registered root pointers for py/mpstate.h. +$(HEADER_BUILD)/root_pointers.h: $(HEADER_BUILD)/root_pointers.collected $(PY_SRC)/make_root_pointers.py + @$(ECHO) "GEN $@" + $(Q)$(PYTHON) $(PY_SRC)/make_root_pointers.py $< > $@ + # Standard C functions like memset need to be compiled with special flags so # the compiler does not optimise these functions in terms of themselves. CFLAGS_BUILTIN ?= -ffreestanding -fno-builtin -fno-lto @@ -289,25 +267,11 @@ $(BUILD)/shared/libc/string0.o: CFLAGS += $(CFLAGS_BUILTIN) # that the function preludes are of a minimal and predictable form. $(PY_BUILD)/nlr%.o: CFLAGS += -Os -# CIRCUITPY: separate SUPEROPT for gc.o and vm.o # optimising gc for speed; 5ms down to 4ms on pybv2 -ifndef SUPEROPT_GC - SUPEROPT_GC = 1 -endif - -ifeq ($(SUPEROPT_GC),1) $(PY_BUILD)/gc.o: CFLAGS += $(CSUPEROPT) -endif # optimising vm for speed, adds only a small amount to code size but makes a huge difference to speed (20% faster) -ifndef SUPEROPT_VM - SUPEROPT_VM = 1 -endif - -ifeq ($(SUPEROPT_VM),1) $(PY_BUILD)/vm.o: CFLAGS += $(CSUPEROPT) -endif - # Optimizing vm.o for modern deeply pipelined CPUs with branch predictors # may require disabling tail jump optimization. This will make sure that # each opcode has its own dispatching jump which will improve branch diff --git a/py/qstr.c b/py/qstr.c index 5ff8b3bd54..7a30892906 100644 --- a/py/qstr.c +++ b/py/qstr.c @@ -33,9 +33,6 @@ #include "py/gc.h" #include "py/runtime.h" -#include "supervisor/linker.h" -#include "supervisor/shared/translate/translate.h" - // 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 @@ -63,9 +60,9 @@ #define MICROPY_ALLOC_QSTR_ENTRIES_INIT (10) // this must match the equivalent function in makeqstrdata.py -mp_uint_t qstr_compute_hash(const byte *data, size_t len) { +size_t qstr_compute_hash(const byte *data, size_t len) { // djb2 algorithm; see http://www.cse.yorku.ca/~oz/hash.html - mp_uint_t hash = 5381; + size_t hash = 5381; for (const byte *top = data + len; data < top; data++) { hash = ((hash << 5) + hash) ^ (*data); // hash * 33 ^ data } @@ -190,7 +187,7 @@ 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) { // work out hash of str - mp_uint_t str_hash = qstr_compute_hash((const byte *)str, str_len); + 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) { @@ -263,7 +260,7 @@ qstr qstr_from_strn(const char *str, size_t len) { MP_STATE_VM(qstr_last_used) += n_bytes; // store the interned strings' data - mp_uint_t hash = qstr_compute_hash((const byte *)str, len); + size_t hash = qstr_compute_hash((const byte *)str, len); memcpy(q_ptr, str, len); q_ptr[len] = '\0'; q = qstr_add(hash, len, q_ptr); diff --git a/py/qstr.h b/py/qstr.h index c6c9768d05..6782497e21 100644 --- a/py/qstr.h +++ b/py/qstr.h @@ -80,7 +80,7 @@ typedef struct _qstr_pool_t { void qstr_init(void); -mp_uint_t qstr_compute_hash(const byte *data, size_t len); +size_t qstr_compute_hash(const byte *data, size_t len); qstr qstr_find_strn(const char *str, size_t str_len); // returns MP_QSTRnull if not found qstr qstr_from_str(const char *str); @@ -95,7 +95,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 d68406b1c6..57b12b3b65 100644 --- a/py/reader.c +++ b/py/reader.c @@ -139,7 +139,7 @@ void mp_reader_new_file(mp_reader_t *reader, const char *filename) { int fd = open(filename, O_RDONLY, 0644); MP_THREAD_GIL_ENTER(); if (fd < 0) { - mp_raise_OSError(errno); + mp_raise_OSError_with_filename(errno, filename); } mp_reader_new_file_from_fd(reader, fd, true); } diff --git a/py/runtime.c b/py/runtime.c index eaeb9eb0ac..1d945c4f27 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -33,7 +33,6 @@ #include "py/parsenum.h" #include "py/compile.h" -#include "py/mperrno.h" #include "py/objstr.h" #include "py/objtuple.h" #include "py/objlist.h" @@ -46,8 +45,6 @@ #include "py/stackctrl.h" #include "py/gc.h" -#include "supervisor/shared/translate/translate.h" - #if MICROPY_DEBUG_VERBOSE // print debugging info #define DEBUG_PRINT (1) #define DEBUG_printf DEBUG_printf @@ -64,6 +61,8 @@ const mp_obj_module_t mp_module___main__ = { MP_REGISTER_MODULE(MP_QSTR___main__, mp_module___main__); +#define TYPE_HAS_ITERNEXT(type) (type->flags & (MP_TYPE_FLAG_ITER_IS_ITERNEXT | MP_TYPE_FLAG_ITER_IS_CUSTOM | MP_TYPE_FLAG_ITER_IS_STREAM)) + void mp_init(void) { qstr_init(); @@ -89,6 +88,7 @@ void mp_init(void) { #if MICROPY_KBD_EXCEPTION // initialise the exception object for raising KeyboardInterrupt + // CIRCUITPY chained exception support mp_obj_exception_initialize0(&MP_STATE_VM(mp_kbd_exception), &mp_type_KeyboardInterrupt); #endif @@ -133,6 +133,19 @@ void mp_init(void) { sizeof(MP_STATE_VM(fs_user_mount)) - MICROPY_FATFS_NUM_PERSISTENT); #endif + #if MICROPY_PY_OS_DUPTERM + for (size_t i = 0; i < MICROPY_PY_OS_DUPTERM; ++i) { + MP_STATE_VM(dupterm_objs[i]) = MP_OBJ_NULL; + } + #endif + + // CIRCUITPY: do not unmount / + #if MICROPY_VFS && 0 + // initialise the VFS sub-system + MP_STATE_VM(vfs_cur) = NULL; + MP_STATE_VM(vfs_mount_table) = NULL; + #endif + #if MICROPY_PY_SYS_PATH_ARGV_DEFAULTS mp_obj_list_init(MP_OBJ_TO_PTR(mp_sys_path), 0); mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR_)); // current dir (or base dir of the script) @@ -307,9 +320,8 @@ mp_obj_t mp_unary_op(mp_unary_op_t op, mp_obj_t arg) { return MP_OBJ_NEW_SMALL_INT(h); } else { const mp_obj_type_t *type = mp_obj_get_type(arg); - mp_unary_op_fun_t unary_op = mp_type_get_unary_op_slot(type); - if (unary_op != NULL) { - mp_obj_t result = unary_op(op, arg); + if (MP_OBJ_TYPE_HAS_SLOT(type, unary_op)) { + mp_obj_t result = MP_OBJ_TYPE_GET_SLOT(type, unary_op)(op, arg); if (result != MP_OBJ_NULL) { return result; } @@ -320,6 +332,15 @@ mp_obj_t mp_unary_op(mp_unary_op_t op, mp_obj_t arg) { // if arg==mp_const_none. return mp_const_true; } + #if MICROPY_PY_BUILTINS_FLOAT + if (op == MP_UNARY_OP_FLOAT_MAYBE + #if MICROPY_PY_BUILTINS_COMPLEX + || op == MP_UNARY_OP_COMPLEX_MAYBE + #endif + ) { + return MP_OBJ_NULL; + } + #endif // With MP_UNARY_OP_INT, mp_unary_op() becomes a fallback for mp_obj_get_int(). // In this case provide a more focused error message to not confuse, e.g. chr(1.0) #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE @@ -603,9 +624,8 @@ mp_obj_t MICROPY_WRAP_MP_BINARY_OP(mp_binary_op)(mp_binary_op_t op, mp_obj_t lhs const mp_obj_type_t *type; generic_binary_op: type = mp_obj_get_type(lhs); - mp_binary_op_fun_t binary_op = mp_type_get_binary_op_slot(type); - if (binary_op != NULL) { - mp_obj_t result = binary_op(op, lhs, rhs); + if (MP_OBJ_TYPE_HAS_SLOT(type, binary_op)) { + mp_obj_t result = MP_OBJ_TYPE_GET_SLOT(type, binary_op)(op, lhs, rhs); if (result != MP_OBJ_NULL) { return result; } @@ -680,9 +700,8 @@ mp_obj_t mp_call_function_n_kw(mp_obj_t fun_in, size_t n_args, size_t n_kw, cons const mp_obj_type_t *type = mp_obj_get_type(fun_in); // do the call - mp_call_fun_t call = mp_type_get_call_slot(type); - if (call) { - return call(fun_in, n_args, n_kw, args); + if (MP_OBJ_TYPE_HAS_SLOT(type, call)) { + return MP_OBJ_TYPE_GET_SLOT(type, call)(fun_in, n_args, n_kw, args); } #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE @@ -805,7 +824,7 @@ void PLACE_IN_ITCM(mp_call_prepare_args_n_kw_var)(bool have_self, size_t n_args_ mp_obj_t iterable = mp_getiter(arg, &iter_buf); mp_obj_t item; while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { - if (args2_len >= args2_alloc) { + if (args2_len + (n_args - i) >= args2_alloc) { args2 = mp_nonlocal_realloc(args2, args2_alloc * sizeof(mp_obj_t), args2_alloc * 2 * sizeof(mp_obj_t)); args2_alloc *= 2; @@ -1064,14 +1083,12 @@ STATIC mp_obj_t checked_fun_call(mp_obj_t self_in, size_t n_args, size_t n_kw, c return mp_call_function_n_kw(self->fun, n_args, n_kw, args); } -STATIC const mp_obj_type_t mp_type_checked_fun = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_function, - MP_TYPE_EXTENDED_FIELDS( - .call = checked_fun_call, - ) -}; +STATIC MP_DEFINE_CONST_OBJ_TYPE( + mp_type_checked_fun, + MP_QSTR_function, + MP_TYPE_FLAG_BINDS_SELF, + call, checked_fun_call + ); STATIC mp_obj_t mp_obj_new_checked_fun(const mp_obj_type_t *type, mp_obj_t fun) { mp_obj_checked_fun_t *o = mp_obj_malloc(mp_obj_checked_fun_t, &mp_type_checked_fun); @@ -1180,16 +1197,14 @@ void mp_load_method_maybe(mp_obj_t obj, qstr attr, mp_obj_t *dest) { } #endif - if (attr == MP_QSTR___next__ && mp_type_get_iternext_slot(type) != NULL) { + if (attr == MP_QSTR___next__ && TYPE_HAS_ITERNEXT(type)) { dest[0] = MP_OBJ_FROM_PTR(&mp_builtin_next_obj); dest[1] = obj; return; } - mp_attr_fun_t attr_fun = mp_type_get_attr_slot(type); - if (attr_fun != NULL) { + if (MP_OBJ_TYPE_HAS_SLOT(type, attr)) { // this type can do its own load, so call it - attr_fun(obj, attr, dest); - + MP_OBJ_TYPE_GET_SLOT(type, attr)(obj, attr, dest); // If type->attr has set dest[1] = MP_OBJ_SENTINEL, we should proceed // with lookups below (i.e. in locals_dict). If not, return right away. if (dest[1] != MP_OBJ_SENTINEL) { @@ -1198,11 +1213,11 @@ void mp_load_method_maybe(mp_obj_t obj, qstr attr, mp_obj_t *dest) { // Clear the fail flag set by type->attr so it's like it never ran. dest[1] = MP_OBJ_NULL; } - if (type->locals_dict != NULL) { + if (MP_OBJ_TYPE_HAS_SLOT(type, locals_dict)) { // generic method lookup // this is a lookup in the object (ie not class or type) - assert(type->locals_dict->base.type == &mp_type_dict); // MicroPython restriction, for now - mp_map_t *locals_map = &type->locals_dict->map; + assert(MP_OBJ_TYPE_GET_SLOT(type, locals_dict)->base.type == &mp_type_dict); // MicroPython restriction, for now + mp_map_t *locals_map = &MP_OBJ_TYPE_GET_SLOT(type, locals_dict)->map; mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP); if (elem != NULL) { mp_convert_member_lookup(obj, type, elem->value, dest); @@ -1254,10 +1269,9 @@ void mp_load_method_protected(mp_obj_t obj, qstr attr, mp_obj_t *dest, bool catc void mp_store_attr(mp_obj_t base, qstr attr, mp_obj_t value) { DEBUG_OP_printf("store attr %p.%s <- %p\n", base, qstr_str(attr), value); const mp_obj_type_t *type = mp_obj_get_type(base); - mp_attr_fun_t attr_fun = mp_type_get_attr_slot(type); - if (attr_fun != NULL) { + if (MP_OBJ_TYPE_HAS_SLOT(type, attr)) { mp_obj_t dest[2] = {MP_OBJ_SENTINEL, value}; - attr_fun(base, attr, dest); + MP_OBJ_TYPE_GET_SLOT(type, attr)(base, attr, dest); if (dest[0] == MP_OBJ_NULL) { // success return; @@ -1306,20 +1320,26 @@ void mp_store_attr(mp_obj_t base, qstr attr, mp_obj_t value) { mp_obj_t mp_getiter(mp_obj_t o_in, mp_obj_iter_buf_t *iter_buf) { assert(o_in); const mp_obj_type_t *type = mp_obj_get_type(o_in); - mp_getiter_fun_t getiter = mp_type_get_getiter_slot(type); - // Check for native getiter which is the identity. We handle this case explicitly + + // Most types that use iternext just use the identity getiter. We handle this case explicitly // so we don't unnecessarily allocate any RAM for the iter_buf, which won't be used. - if (getiter == mp_identity_getiter) { + if ((type->flags & MP_TYPE_FLAG_ITER_IS_ITERNEXT) == MP_TYPE_FLAG_ITER_IS_ITERNEXT || (type->flags & MP_TYPE_FLAG_ITER_IS_STREAM) == MP_TYPE_FLAG_ITER_IS_STREAM) { return o_in; } - // check for native getiter (corresponds to __iter__) - if (getiter != NULL) { - if (iter_buf == NULL && getiter != mp_obj_instance_getiter) { + if (MP_OBJ_TYPE_HAS_SLOT(type, iter)) { + // check for native getiter (corresponds to __iter__) + if (iter_buf == NULL && MP_OBJ_TYPE_GET_SLOT(type, iter) != mp_obj_instance_getiter) { // if caller did not provide a buffer then allocate one on the heap // mp_obj_instance_getiter is special, it will allocate only if needed iter_buf = m_new_obj(mp_obj_iter_buf_t); } + mp_getiter_fun_t getiter; + if (type->flags & MP_TYPE_FLAG_ITER_IS_CUSTOM) { + getiter = ((mp_getiter_iternext_custom_t *)MP_OBJ_TYPE_GET_SLOT(type, iter))->getiter; + } else { + getiter = (mp_getiter_fun_t)MP_OBJ_TYPE_GET_SLOT(type, iter); + } mp_obj_t iter = getiter(o_in, iter_buf); if (iter != MP_OBJ_NULL) { return iter; @@ -1345,16 +1365,29 @@ mp_obj_t mp_getiter(mp_obj_t o_in, mp_obj_iter_buf_t *iter_buf) { mp_raise_TypeError_varg( MP_ERROR_TEXT("'%q' object is not iterable"), mp_obj_get_type_qstr(o_in)); #endif + +} + +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) { + 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); + } else if (type->flags & MP_TYPE_FLAG_ITER_IS_CUSTOM) { + return ((mp_getiter_iternext_custom_t *)MP_OBJ_TYPE_GET_SLOT(type, iter))->iternext; + } else { + return NULL; + } } // may return MP_OBJ_STOP_ITERATION as an optimisation instead of raise StopIteration() // may also raise StopIteration() mp_obj_t mp_iternext_allow_raise(mp_obj_t o_in) { const mp_obj_type_t *type = mp_obj_get_type(o_in); - mp_fun_1_t iternext = mp_type_get_iternext_slot(type); - if (iternext != NULL) { + if (TYPE_HAS_ITERNEXT(type)) { MP_STATE_THREAD(stop_iteration_arg) = MP_OBJ_NULL; - return iternext(o_in); + return type_get_iternext(type)(o_in); } else { // check for __next__ method mp_obj_t dest[2]; @@ -1378,10 +1411,9 @@ mp_obj_t mp_iternext_allow_raise(mp_obj_t o_in) { mp_obj_t mp_iternext(mp_obj_t o_in) { MP_STACK_CHECK(); // enumerate, filter, map and zip can recursively call mp_iternext const mp_obj_type_t *type = mp_obj_get_type(o_in); - mp_fun_1_t iternext = mp_type_get_iternext_slot(type); - if (iternext != NULL) { + if (TYPE_HAS_ITERNEXT(type)) { MP_STATE_THREAD(stop_iteration_arg) = MP_OBJ_NULL; - return iternext(o_in); + return type_get_iternext(type)(o_in); } else { // check for __next__ method mp_obj_t dest[2]; @@ -1424,10 +1456,9 @@ mp_vm_return_kind_t mp_resume(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t th return mp_obj_gen_resume(self_in, send_value, throw_value, ret_val); } - mp_fun_1_t iternext = mp_type_get_iternext_slot(type); - if (iternext != NULL && send_value == mp_const_none) { + if (TYPE_HAS_ITERNEXT(type) && send_value == mp_const_none) { MP_STATE_THREAD(stop_iteration_arg) = MP_OBJ_NULL; - mp_obj_t ret = iternext(self_in); + mp_obj_t ret = type_get_iternext(type)(self_in); *ret_val = ret; if (ret != MP_OBJ_STOP_ITERATION) { return MP_VM_RETURN_YIELD; @@ -1551,7 +1582,8 @@ mp_obj_t __attribute__((noinline, )) mp_import_from(mp_obj_t module, qstr name) if (dest[1] != MP_OBJ_NULL) { // Hopefully we can't import bound method from an object - mp_raise_msg_varg(&mp_type_ImportError, MP_ERROR_TEXT("cannot import name %q"), name); + import_error: + mp_raise_msg_varg(&mp_type_ImportError, MP_ERROR_TEXT("can't import name %q"), name); } if (dest[0] != MP_OBJ_NULL) { @@ -1562,7 +1594,7 @@ mp_obj_t __attribute__((noinline, )) mp_import_from(mp_obj_t module, qstr name) // See if it's a package, then can try FS import if (!mp_obj_is_package(module)) { - mp_raise_msg_varg(&mp_type_ImportError, MP_ERROR_TEXT("cannot import name %q"), name); + goto import_error; } mp_load_method_maybe(module, MP_QSTR___name__, dest); @@ -1583,12 +1615,12 @@ mp_obj_t __attribute__((noinline, )) mp_import_from(mp_obj_t module, qstr name) #else // Package import not supported with external imports disabled - mp_raise_msg_varg(&mp_type_ImportError, MP_ERROR_TEXT("cannot import name %q"), name); + goto import_error; #endif } -void __attribute__((noinline)) mp_import_all(mp_obj_t module) { +void mp_import_all(mp_obj_t module) { DEBUG_printf("import all %p\n", module); // TODO: Support __all__ @@ -1822,8 +1854,18 @@ NORETURN MP_COLD void mp_raise_OSError(int errno_) { mp_raise_type_arg(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(errno_)); } -#endif +NORETURN MP_COLD void mp_raise_OSError_with_filename(int errno_, const char *filename) { + vstr_t vstr; + vstr_init(&vstr, 32); + vstr_printf(&vstr, "can't open %s", filename); + mp_obj_t o_str = mp_obj_new_str_from_vstr(&vstr); + mp_obj_t args[2] = { MP_OBJ_NEW_SMALL_INT(errno_), MP_OBJ_FROM_PTR(o_str)}; + nlr_raise(mp_obj_exception_make_new(&mp_type_OSError, 2, 0, args)); +} +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 NORETURN MP_COLD void mp_raise_recursion_depth(void) { mp_raise_RuntimeError(MP_ERROR_TEXT("maximum recursion depth exceeded")); @@ -1833,3 +1875,4 @@ NORETURN MP_COLD void mp_raise_recursion_depth(void) { NORETURN MP_COLD void mp_raise_ZeroDivisionError(void) { mp_raise_msg(&mp_type_ZeroDivisionError, MP_ERROR_TEXT("division by zero")); } +#endif diff --git a/py/runtime.h b/py/runtime.h index 5ecab91e99..2812432275 100644 --- a/py/runtime.h +++ b/py/runtime.h @@ -26,15 +26,9 @@ #ifndef MICROPY_INCLUDED_PY_RUNTIME_H #define MICROPY_INCLUDED_PY_RUNTIME_H -#include - #include "py/mpstate.h" #include "py/pystack.h" -#include "supervisor/linker.h" - -#include "supervisor/shared/translate/translate.h" - typedef enum { MP_VM_RETURN_NORMAL, MP_VM_RETURN_YIELD, @@ -96,14 +90,12 @@ bool mp_sched_schedule_node(mp_sched_node_t *node, mp_sched_callback_t callback) 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); void mp_arg_check_num_sig(size_t n_args, size_t n_kw, uint32_t sig); -static MP_INLINE void mp_arg_check_num(size_t n_args, size_t n_kw, size_t n_args_min, size_t n_args_max, bool takes_kw) { +static inline void mp_arg_check_num(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)); } void mp_arg_parse_all(size_t n_pos, const mp_obj_t *pos, mp_map_t *kws, size_t n_allowed, const mp_arg_t *allowed, mp_arg_val_t *out_vals); void mp_arg_parse_all_kw_array(size_t n_pos, size_t n_kw, const mp_obj_t *args, size_t n_allowed, const mp_arg_t *allowed, mp_arg_val_t *out_vals); -#if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE NORETURN void mp_arg_error_terse_mismatch(void); -#endif NORETURN void mp_arg_error_unimpl_kw(void); NORETURN void mp_arg_error_invalid(qstr arg_name); @@ -129,15 +121,16 @@ mp_int_t mp_arg_validate_type_int(mp_obj_t obj, qstr arg_name); mp_obj_t mp_arg_validate_type_string(mp_obj_t obj, qstr arg_name); static MP_INLINE mp_obj_dict_t *mp_locals_get(void) { +static inline mp_obj_dict_t *mp_locals_get(void) { return MP_STATE_THREAD(dict_locals); } -static MP_INLINE void mp_locals_set(mp_obj_dict_t *d) { +static inline void mp_locals_set(mp_obj_dict_t *d) { MP_STATE_THREAD(dict_locals) = d; } -static MP_INLINE mp_obj_dict_t *mp_globals_get(void) { +static inline mp_obj_dict_t *mp_globals_get(void) { return MP_STATE_THREAD(dict_globals); } -static MP_INLINE void mp_globals_set(mp_obj_dict_t *d) { +static inline void mp_globals_set(mp_obj_dict_t *d) { MP_STATE_THREAD(dict_globals) = d; } @@ -194,7 +187,7 @@ mp_obj_t mp_iternext_allow_raise(mp_obj_t o); // may return MP_OBJ_STOP_ITERATIO mp_obj_t mp_iternext(mp_obj_t o); // will always return MP_OBJ_STOP_ITERATION instead of raising StopIteration(...) mp_vm_return_kind_t mp_resume(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t throw_value, mp_obj_t *ret_val); -static MP_INLINE mp_obj_t mp_make_stop_iteration(mp_obj_t o) { +static inline mp_obj_t mp_make_stop_iteration(mp_obj_t o) { MP_STATE_THREAD(stop_iteration_arg) = o; return MP_OBJ_STOP_ITERATION; } @@ -217,35 +210,25 @@ NORETURN void mp_raise_NotImplementedError_no_msg(void); #define mp_raise_NotImplementedError(msg) mp_raise_NotImplementedError_no_msg() #else #define mp_raise_type(exc_type) mp_raise_msg(exc_type, NULL) +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, ...); +NORETURN void mp_raise_ValueError(mp_rom_error_text_t msg); +NORETURN void mp_raise_TypeError(mp_rom_error_text_t msg); +NORETURN void mp_raise_NotImplementedError(mp_rom_error_text_t msg); +#endif + 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, const compressed_string_t *msg); -NORETURN void mp_raise_msg_varg(const mp_obj_type_t *exc_type, const compressed_string_t *fmt, ...); -NORETURN void mp_raise_msg_vlist(const mp_obj_type_t *exc_type, const compressed_string_t *fmt, va_list argptr); -// Only use this string version in native mpy files. Otherwise, use the compressed string version. -NORETURN void mp_raise_msg_str(const mp_obj_type_t *exc_type, const char *msg); -NORETURN void mp_raise_ValueError(const compressed_string_t *msg); -NORETURN void mp_raise_ValueError_varg(const compressed_string_t *fmt, ...); -NORETURN void mp_raise_TypeError(const compressed_string_t *msg); -NORETURN void mp_raise_TypeError_varg(const compressed_string_t *fmt, ...); -NORETURN void mp_raise_AttributeError(const compressed_string_t *msg); -NORETURN void mp_raise_RuntimeError(const compressed_string_t *msg); -NORETURN void mp_raise_RuntimeError_varg(const compressed_string_t *fmt, ...); -NORETURN void mp_raise_ImportError(const compressed_string_t *msg); -NORETURN void mp_raise_IndexError(const compressed_string_t *msg); -NORETURN void mp_raise_IndexError_varg(const compressed_string_t *msg, ...); NORETURN void mp_raise_StopIteration(mp_obj_t arg); NORETURN void mp_raise_OSError(int errno_); NORETURN void mp_raise_OSError_errno_str(int errno_, mp_obj_t str); NORETURN void mp_raise_OSError_msg(const compressed_string_t *msg); NORETURN void mp_raise_OSError_msg_varg(const compressed_string_t *fmt, ...); -NORETURN void mp_raise_ConnectionError(const compressed_string_t *msg); +NORETURN void mp_raise_OSError_with_filename(int errno_, const char *filename);NORETURN void mp_raise_ConnectionError(const compressed_string_t *msg); NORETURN void mp_raise_BrokenPipeError(void); NORETURN void mp_raise_NotImplementedError(const compressed_string_t *msg); NORETURN void mp_raise_NotImplementedError_varg(const compressed_string_t *fmt, ...); NORETURN void mp_raise_OverflowError_varg(const compressed_string_t *fmt, ...); NORETURN void mp_raise_recursion_depth(void); -NORETURN void mp_raise_ZeroDivisionError(void); -#endif #if MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG #undef mp_check_self diff --git a/py/runtime0.h b/py/runtime0.h index 218820fd70..994c7d9324 100644 --- a/py/runtime0.h +++ b/py/runtime0.h @@ -79,6 +79,8 @@ typedef enum { MP_UNARY_OP_HASH, // __hash__; must return a small int MP_UNARY_OP_ABS, // __abs__ MP_UNARY_OP_INT, // __int__ + MP_UNARY_OP_FLOAT_MAYBE, // __float__ + MP_UNARY_OP_COMPLEX_MAYBE, // __complex__ MP_UNARY_OP_SIZEOF, // for sys.getsizeof() } mp_unary_op_t; diff --git a/py/scheduler.c b/py/scheduler.c index 2aa28bd335..41001d6af9 100644 --- a/py/scheduler.c +++ b/py/scheduler.c @@ -32,7 +32,11 @@ // sources such as interrupts and UNIX signal handlers). void MICROPY_WRAP_MP_SCHED_EXCEPTION(mp_sched_exception)(mp_obj_t exc) { MP_STATE_MAIN_THREAD(mp_pending_exception) = exc; - #if MICROPY_ENABLE_SCHEDULER + + #if MICROPY_ENABLE_SCHEDULER && !MICROPY_PY_THREAD + // Optimisation for the case where we have scheduler but no threading. + // Allows the VM to do a single check to exclude both pending exception + // and queued tasks. if (MP_STATE_VM(sched_state) == MP_SCHED_IDLE) { MP_STATE_VM(sched_state) = MP_SCHED_PENDING; } @@ -47,6 +51,12 @@ void MICROPY_WRAP_MP_SCHED_KEYBOARD_INTERRUPT(mp_sched_keyboard_interrupt)(void) } #endif +#if MICROPY_ENABLE_VM_ABORT +void MICROPY_WRAP_MP_SCHED_VM_ABORT(mp_sched_vm_abort)(void) { + MP_STATE_VM(vm_abort) = true; +} +#endif + #if MICROPY_ENABLE_SCHEDULER #define IDX_MASK(i) ((i) & (MICROPY_SCHEDULER_DEPTH - 1)) @@ -62,33 +72,17 @@ static inline bool mp_sched_empty(void) { return mp_sched_num_pending() == 0; } -// A variant of this is inlined in the VM at the pending exception check -void mp_handle_pending(bool raise_exc) { - if (MP_STATE_VM(sched_state) == MP_SCHED_PENDING) { - mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); - // Re-check state is still pending now that we're in the atomic section. - if (MP_STATE_VM(sched_state) == MP_SCHED_PENDING) { - mp_obj_t obj = MP_STATE_THREAD(mp_pending_exception); - if (obj != MP_OBJ_NULL) { - MP_STATE_THREAD(mp_pending_exception) = MP_OBJ_NULL; - if (!mp_sched_num_pending()) { - MP_STATE_VM(sched_state) = MP_SCHED_IDLE; - } - if (raise_exc) { - MICROPY_END_ATOMIC_SECTION(atomic_state); - nlr_raise(obj); - } - } - mp_handle_pending_tail(atomic_state); - } else { - MICROPY_END_ATOMIC_SECTION(atomic_state); - } +static inline void mp_sched_run_pending(void) { + mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); + if (MP_STATE_VM(sched_state) != MP_SCHED_PENDING) { + // Something else (e.g. hard IRQ) locked the scheduler while we + // acquired the lock. + MICROPY_END_ATOMIC_SECTION(atomic_state); + return; } -} -// This function should only be called by mp_handle_pending, -// or by the VM's inlined version of that function. -void mp_handle_pending_tail(mp_uint_t atomic_state) { + // Equivalent to mp_sched_lock(), but we're already in the atomic + // section and know that we're pending. MP_STATE_VM(sched_state) = MP_SCHED_LOCKED; #if MICROPY_SCHEDULER_STATIC_NODES @@ -118,14 +112,21 @@ void mp_handle_pending_tail(mp_uint_t atomic_state) { MICROPY_END_ATOMIC_SECTION(atomic_state); } + // Restore MP_STATE_VM(sched_state) to idle (or pending if there are still + // tasks in the queue). mp_sched_unlock(); } +// Locking the scheduler prevents tasks from executing (does not prevent new +// tasks from being added). We lock the scheduler while executing scheduled +// tasks and also in hard interrupts or GC finalisers. void mp_sched_lock(void) { mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); if (MP_STATE_VM(sched_state) < 0) { + // Already locked, increment lock (recursive lock). --MP_STATE_VM(sched_state); } else { + // Pending or idle. MP_STATE_VM(sched_state) = MP_SCHED_LOCKED; } MICROPY_END_ATOMIC_SECTION(atomic_state); @@ -135,12 +136,17 @@ void mp_sched_unlock(void) { mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); assert(MP_STATE_VM(sched_state) < 0); if (++MP_STATE_VM(sched_state) == 0) { - // vm became unlocked - if (MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL - #if MICROPY_SCHEDULER_STATIC_NODES - || MP_STATE_VM(sched_head) != NULL + // Scheduler became unlocked. Check if there are still tasks in the + // queue and set sched_state accordingly. + if ( + #if !MICROPY_PY_THREAD + // See optimisation in mp_sched_exception. + MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL || #endif - || mp_sched_num_pending()) { + #if MICROPY_SCHEDULER_STATIC_NODES + MP_STATE_VM(sched_head) != NULL || + #endif + mp_sched_num_pending()) { MP_STATE_VM(sched_state) = MP_SCHED_PENDING; } else { MP_STATE_VM(sched_state) = MP_SCHED_IDLE; @@ -196,17 +202,41 @@ bool mp_sched_schedule_node(mp_sched_node_t *node, mp_sched_callback_t callback) } #endif -#else // MICROPY_ENABLE_SCHEDULER - -// A variant of this is inlined in the VM at the pending exception check -void mp_handle_pending(bool raise_exc) { - if (MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL) { - mp_obj_t obj = MP_STATE_THREAD(mp_pending_exception); - MP_STATE_THREAD(mp_pending_exception) = MP_OBJ_NULL; - if (raise_exc) { - nlr_raise(obj); - } - } -} +MP_REGISTER_ROOT_POINTER(mp_sched_item_t sched_queue[MICROPY_SCHEDULER_DEPTH]); #endif // MICROPY_ENABLE_SCHEDULER + +// Called periodically from the VM or from "waiting" code (e.g. sleep) to +// process background tasks and pending exceptions (e.g. KeyboardInterrupt). +void mp_handle_pending(bool raise_exc) { + // Handle pending VM abort. + #if MICROPY_ENABLE_VM_ABORT + if (MP_STATE_VM(vm_abort) && mp_thread_is_main_thread()) { + MP_STATE_VM(vm_abort) = false; + if (raise_exc && nlr_get_abort() != NULL) { + nlr_jump_abort(); + } + } + #endif + + // Handle any pending exception. + if (MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL) { + mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); + mp_obj_t obj = MP_STATE_THREAD(mp_pending_exception); + if (obj != MP_OBJ_NULL) { + MP_STATE_THREAD(mp_pending_exception) = MP_OBJ_NULL; + if (raise_exc) { + MICROPY_END_ATOMIC_SECTION(atomic_state); + nlr_raise(obj); + } + } + MICROPY_END_ATOMIC_SECTION(atomic_state); + } + + // Handle any pending callbacks. + #if MICROPY_ENABLE_SCHEDULER + if (MP_STATE_VM(sched_state) == MP_SCHED_PENDING) { + mp_sched_run_pending(); + } + #endif +} diff --git a/py/sequence.c b/py/sequence.c index faed558723..2d42e1c6c4 100644 --- a/py/sequence.c +++ b/py/sequence.c @@ -28,7 +28,6 @@ #include #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" // Helpers for sequence types diff --git a/py/stream.c b/py/stream.c index a008e2f969..36325e7d41 100644 --- a/py/stream.c +++ b/py/stream.c @@ -31,8 +31,6 @@ #include "py/objstr.h" #include "py/stream.h" #include "py/runtime.h" -#include "py/unicode.h" -#include "supervisor/shared/translate/translate.h" // This file defines generic Python stream read/write methods which // dispatch to the underlying stream interface of an object. @@ -42,15 +40,6 @@ STATIC mp_obj_t stream_readall(mp_obj_t self_in); -#define STREAM_CONTENT_TYPE(stream) (((stream)->is_text) ? &mp_type_str : &mp_type_bytes) - -static mp_obj_t mp_obj_new_str_from_vstr_check(const mp_obj_type_t *type, vstr_t *vstr) { - if (type == &mp_type_str && !utf8_check((void *)vstr->buf, vstr->len)) { - mp_raise_msg(&mp_type_UnicodeError, NULL); - } - return mp_obj_new_str_from_vstr(type, vstr); -} - // Returns error condition in *errcode, if non-zero, return value is number of bytes written // before error condition occurred. If *errcode == 0, returns total bytes written (which will // be equal to input size). @@ -93,41 +82,31 @@ mp_uint_t mp_stream_rw(mp_obj_t stream, void *buf_, mp_uint_t size, int *errcode return done; } -const mp_stream_p_t *mp_get_stream(mp_const_obj_t self) { - return mp_proto_get(MP_QSTR_protocol_stream, self); -} - const mp_stream_p_t *mp_get_stream_raise(mp_obj_t self_in, int flags) { - const mp_stream_p_t *stream_p = mp_proto_get(MP_QSTR_protocol_stream, self_in); - if (stream_p == NULL - || ((flags & MP_STREAM_OP_READ) && stream_p->read == NULL) - || ((flags & MP_STREAM_OP_WRITE) && stream_p->write == NULL) - || ((flags & MP_STREAM_OP_IOCTL) && stream_p->ioctl == NULL)) { - // CPython: io.UnsupportedOperation, OSError subclass - mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("stream operation not supported")); + const mp_obj_type_t *type = mp_obj_get_type(self_in); + if (MP_OBJ_TYPE_HAS_SLOT(type, protocol)) { + const mp_stream_p_t *stream_p = MP_OBJ_TYPE_GET_SLOT(type, protocol); + if (!((flags & MP_STREAM_OP_READ) && stream_p->read == NULL) + && !((flags & MP_STREAM_OP_WRITE) && stream_p->write == NULL) + && !((flags & MP_STREAM_OP_IOCTL) && stream_p->ioctl == NULL)) { + return stream_p; + } } - return stream_p; + // CPython: io.UnsupportedOperation, OSError subclass + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("stream operation not supported")); } STATIC mp_obj_t stream_read_generic(size_t n_args, const mp_obj_t *args, byte flags) { // What to do if sz < -1? Python docs don't specify this case. - // CPython does a readall, let's do the same. + // CPython does a readall, but here we silently let negatives through, + // and they will cause a MemoryError. mp_int_t sz; - const mp_stream_p_t *stream_p = mp_get_stream(args[0]); - if (stream_p->pyserial_read_compatibility) { - // Pyserial defaults to sz=1 if not specified. - if (n_args == 1) { - sz = 1; - } else { - // Pyserial treats negative size as 0. - sz = MAX(0, mp_obj_get_int(args[1])); - } - } else { - if (n_args == 1 || args[1] == mp_const_none || (sz = mp_obj_get_int(args[1])) <= -1) { - return stream_readall(args[0]); - } + if (n_args == 1 || ((sz = mp_obj_get_int(args[1])) == -1)) { + return stream_readall(args[0]); } + const mp_stream_p_t *stream_p = mp_get_stream(args[0]); + #if MICROPY_PY_BUILTINS_STR_UNICODE if (stream_p->is_text) { // We need to read sz number of unicode characters. Because we don't have any @@ -209,7 +188,8 @@ STATIC mp_obj_t stream_read_generic(size_t n_args, const mp_obj_t *args, byte fl } } } - return mp_obj_new_str_from_vstr_check(&mp_type_str, &vstr); + + return mp_obj_new_str_from_vstr(&vstr); } #endif @@ -230,7 +210,11 @@ STATIC mp_obj_t stream_read_generic(size_t n_args, const mp_obj_t *args, byte fl mp_raise_OSError(error); } else { vstr.len = out_sz; - return mp_obj_new_str_from_vstr_check(STREAM_CONTENT_TYPE(stream_p), &vstr); + if (stream_p->is_text) { + return mp_obj_new_str_from_vstr(&vstr); + } else { + return mp_obj_new_bytes_from_vstr(&vstr); + } } } @@ -268,9 +252,6 @@ void mp_stream_write_adaptor(void *self, const char *buf, size_t len) { STATIC mp_obj_t stream_write_method(size_t n_args, const mp_obj_t *args) { mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_READ); - if (!mp_get_stream(args[0])->is_text && mp_obj_is_str(args[1])) { - mp_raise_ValueError(MP_ERROR_TEXT("string not supported; use bytes or bytearray")); - } size_t max_len = (size_t)-1; size_t off = 0; if (n_args == 3) { @@ -303,9 +284,6 @@ STATIC mp_obj_t stream_readinto(size_t n_args, const mp_obj_t *args) { // https://docs.python.org/3/library/socket.html#socket.socket.recv_into mp_uint_t len = bufinfo.len; if (n_args > 2) { - if (mp_get_stream(args[0])->pyserial_readinto_compatibility) { - mp_raise_ValueError(MP_ERROR_TEXT("length argument not allowed for this type")); - } len = mp_obj_get_int(args[2]); if (len > bufinfo.len) { len = bufinfo.len; @@ -316,10 +294,7 @@ STATIC mp_obj_t stream_readinto(size_t n_args, const mp_obj_t *args) { mp_uint_t out_sz = mp_stream_read_exactly(args[0], bufinfo.buf, len, &error); if (error != 0) { if (mp_is_nonblocking_error(error)) { - // pyserial readinto never returns None, just 0. - return mp_get_stream(args[0])->pyserial_dont_return_none_compatibility - ? MP_OBJ_NEW_SMALL_INT(0) - : mp_const_none; + return mp_const_none; } mp_raise_OSError(error); } else { @@ -345,10 +320,7 @@ STATIC mp_obj_t stream_readall(mp_obj_t self_in) { // If we read nothing, return None, just like read(). // Otherwise, return data read so far. if (total_size == 0) { - // pyserial read() never returns None, just b''. - return stream_p->pyserial_dont_return_none_compatibility - ? mp_const_empty_bytes - : mp_const_none; + return mp_const_none; } break; } @@ -365,13 +337,14 @@ STATIC mp_obj_t stream_readall(mp_obj_t self_in) { p = vstr_extend(&vstr, DEFAULT_BUFFER_SIZE); current_read = DEFAULT_BUFFER_SIZE; } - #ifdef RUN_BACKGROUND_TASKS - RUN_BACKGROUND_TASKS; - #endif } vstr.len = total_size; - return mp_obj_new_str_from_vstr_check(STREAM_CONTENT_TYPE(stream_p), &vstr); + if (stream_p->is_text) { + return mp_obj_new_str_from_vstr(&vstr); + } else { + return mp_obj_new_bytes_from_vstr(&vstr); + } } // Unbuffered, inefficient implementation of readline() for raw I/O files. @@ -424,7 +397,11 @@ STATIC mp_obj_t stream_unbuffered_readline(size_t n_args, const mp_obj_t *args) } } - return mp_obj_new_str_from_vstr_check(STREAM_CONTENT_TYPE(stream_p), &vstr); + if (stream_p->is_text) { + return mp_obj_new_str_from_vstr(&vstr); + } else { + return mp_obj_new_bytes_from_vstr(&vstr); + } } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_unbuffered_readline_obj, 1, 2, stream_unbuffered_readline); @@ -495,19 +472,16 @@ STATIC mp_obj_t stream_tell(mp_obj_t self) { } MP_DEFINE_CONST_FUN_OBJ_1(mp_stream_tell_obj, stream_tell); -mp_obj_t mp_stream_flush(mp_obj_t self) { +STATIC mp_obj_t stream_flush(mp_obj_t self) { const mp_stream_p_t *stream_p = mp_get_stream(self); int error; - if (stream_p->ioctl == NULL) { - mp_raise_OSError(MP_EINVAL); - } mp_uint_t res = stream_p->ioctl(self, MP_STREAM_FLUSH, 0, &error); if (res == MP_STREAM_ERROR) { mp_raise_OSError(error); } return mp_const_none; } -MP_DEFINE_CONST_FUN_OBJ_1(mp_stream_flush_obj, mp_stream_flush); +MP_DEFINE_CONST_FUN_OBJ_1(mp_stream_flush_obj, stream_flush); STATIC mp_obj_t stream_ioctl(size_t n_args, const mp_obj_t *args) { mp_buffer_info_t bufinfo; @@ -542,10 +516,10 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_ioctl_obj, 2, 3, stream_ioctl); #include -ssize_t mp_stream_posix_write(mp_obj_t stream, const void *buf, size_t len) { - mp_obj_base_t *o = (mp_obj_base_t *)MP_OBJ_TO_PTR(stream); - const mp_stream_p_t *stream_p = mp_get_stream(o); - mp_uint_t out_sz = stream_p->write(stream, buf, len, &errno); +ssize_t mp_stream_posix_write(void *stream, const void *buf, size_t len) { + mp_obj_base_t *o = stream; + const mp_stream_p_t *stream_p = MP_OBJ_TYPE_GET_SLOT(o->type, protocol); + mp_uint_t out_sz = stream_p->write(MP_OBJ_FROM_PTR(stream), buf, len, &errno); if (out_sz == MP_STREAM_ERROR) { return -1; } else { @@ -553,10 +527,10 @@ ssize_t mp_stream_posix_write(mp_obj_t stream, const void *buf, size_t len) { } } -ssize_t mp_stream_posix_read(mp_obj_t stream, void *buf, size_t len) { - mp_obj_base_t *o = (mp_obj_base_t *)MP_OBJ_TO_PTR(stream); - const mp_stream_p_t *stream_p = mp_get_stream(o); - mp_uint_t out_sz = stream_p->read(stream, buf, len, &errno); +ssize_t mp_stream_posix_read(void *stream, void *buf, size_t len) { + mp_obj_base_t *o = stream; + const mp_stream_p_t *stream_p = MP_OBJ_TYPE_GET_SLOT(o->type, protocol); + mp_uint_t out_sz = stream_p->read(MP_OBJ_FROM_PTR(stream), buf, len, &errno); if (out_sz == MP_STREAM_ERROR) { return -1; } else { @@ -564,9 +538,9 @@ ssize_t mp_stream_posix_read(mp_obj_t stream, void *buf, size_t len) { } } -off_t mp_stream_posix_lseek(mp_obj_t stream, off_t offset, int whence) { - const mp_obj_base_t *o = (mp_obj_base_t *)MP_OBJ_TO_PTR(stream); - const mp_stream_p_t *stream_p = mp_get_stream(o); +off_t mp_stream_posix_lseek(void *stream, off_t offset, int whence) { + const mp_obj_base_t *o = stream; + const mp_stream_p_t *stream_p = MP_OBJ_TYPE_GET_SLOT(o->type, protocol); struct mp_stream_seek_t seek_s; seek_s.offset = offset; seek_s.whence = whence; @@ -577,10 +551,10 @@ off_t mp_stream_posix_lseek(mp_obj_t stream, off_t offset, int whence) { return seek_s.offset; } -int mp_stream_posix_fsync(mp_obj_t stream) { - mp_obj_base_t *o = (mp_obj_base_t *)MP_OBJ_TO_PTR(stream); - const mp_stream_p_t *stream_p = mp_get_stream(o); - mp_uint_t res = stream_p->ioctl(stream, MP_STREAM_FLUSH, 0, &errno); +int mp_stream_posix_fsync(void *stream) { + mp_obj_base_t *o = stream; + const mp_stream_p_t *stream_p = MP_OBJ_TYPE_GET_SLOT(o->type, protocol); + mp_uint_t res = stream_p->ioctl(MP_OBJ_FROM_PTR(stream), MP_STREAM_FLUSH, 0, &errno); if (res == MP_STREAM_ERROR) { return -1; } diff --git a/py/stream.h b/py/stream.h index e9e8770127..d71b669e90 100644 --- a/py/stream.h +++ b/py/stream.h @@ -99,7 +99,9 @@ MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_ioctl_obj); #define MP_STREAM_OP_IOCTL (4) // Object is assumed to have a non-NULL stream protocol with valid r/w/ioctl methods -const mp_stream_p_t *mp_get_stream(mp_const_obj_t self); +static inline const mp_stream_p_t *mp_get_stream(mp_const_obj_t self) { + return (const mp_stream_p_t *)MP_OBJ_TYPE_GET_SLOT(((const mp_obj_base_t *)MP_OBJ_TO_PTR(self))->type, protocol); +} const mp_stream_p_t *mp_get_stream_raise(mp_obj_t self_in, int flags); mp_obj_t mp_stream_close(mp_obj_t stream); @@ -118,7 +120,6 @@ 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); -mp_obj_t mp_stream_flush(mp_obj_t self); #if MICROPY_STREAMS_POSIX_API #include diff --git a/py/vm.c b/py/vm.c index 7f1ea1ee4b..863ea5885b 100644 --- a/py/vm.c +++ b/py/vm.c @@ -36,9 +36,6 @@ #include "py/bc0.h" #include "py/profile.h" -#include "supervisor/linker.h" -#include "supervisor/shared/translate/translate.h" - // *FORMAT-OFF* #if 0 @@ -214,13 +211,28 @@ STATIC mp_obj_t get_active_exception(mp_exc_stack_t *exc_sp, mp_exc_stack_t *exc // MP_VM_RETURN_YIELD, ip, sp valid, yielded value in *sp // MP_VM_RETURN_EXCEPTION, exception in state[0] mp_vm_return_kind_t MICROPY_WRAP_MP_EXECUTE_BYTECODE(mp_execute_bytecode)(mp_code_state_t *code_state, volatile mp_obj_t inject_exc) { + #define SELECTIVE_EXC_IP (0) +// When disabled, code_state->ip is updated unconditionally during op +// dispatch, and this is subsequently used in the exception handler +// (either NLR jump or direct RAISE). This is good for code size because it +// happens in a single place but is more work than necessary, as many opcodes +// cannot raise. Enabling SELECTIVE_EXC_IP means that code_state->ip +// is "selectively" updated only during handling of opcodes that might raise. +// This costs about 360 bytes on PYBV11 for a 1-3% performance gain (e.g. 3% +// in bm_fft.py). On rp2040, there is zero code size diff for a 0-1% gain. +// (Both with computed goto enabled). #if SELECTIVE_EXC_IP -#define MARK_EXC_IP_SELECTIVE() { code_state->ip = ip; } /* stores ip 1 byte past last opcode */ +// Note: Because ip has already been advanced by one byte in the dispatch, the +// value of ip here is one byte past the last opcode. +#define MARK_EXC_IP_SELECTIVE() { code_state->ip = ip; } +// No need to update in dispatch. #define MARK_EXC_IP_GLOBAL() #else #define MARK_EXC_IP_SELECTIVE() -#define MARK_EXC_IP_GLOBAL() { code_state->ip = ip; } /* stores ip pointing to last opcode */ +// Immediately before dispatch, save the current ip, which will be the opcode +// about to be dispatched. +#define MARK_EXC_IP_GLOBAL() { code_state->ip = ip; } #endif #if MICROPY_OPT_COMPUTED_GOTO #include "py/vmentrytable.h" @@ -313,14 +325,14 @@ outer_dispatch_loop: // loop to execute byte code for (;;) { dispatch_loop: -#if MICROPY_OPT_COMPUTED_GOTO - ONE_TRUE_DISPATCH(); -#else + #if MICROPY_OPT_COMPUTED_GOTO + DISPATCH(); + #else TRACE(ip); MARK_EXC_IP_GLOBAL(); TRACE_TICK(ip, sp, false); switch (*ip++) { -#endif + #endif ENTRY(MP_BC_LOAD_CONST_FALSE): PUSH(mp_const_false); @@ -680,7 +692,7 @@ unwind_jump:; assert(exc_sp >= exc_stack); if (MP_TAGPTR_TAG1(exc_sp->val_sp)) { - if (exc_sp->handler > ip) { + if (exc_sp->handler >= ip) { // Found a finally handler that isn't active; run it. // Getting here the stack looks like: // (..., X, dest_ip) @@ -797,7 +809,7 @@ unwind_jump:; } #endif } - DISPATCH_WITH_PEND_EXC_CHECK(); + DISPATCH(); } ENTRY(MP_BC_POP_EXCEPT_JUMP): { @@ -837,7 +849,7 @@ unwind_jump:; mp_obj_dict_store(sp[0], sp[2], sp[1]); DISPATCH(); -#if MICROPY_PY_BUILTINS_SET + #if MICROPY_PY_BUILTINS_SET ENTRY(MP_BC_BUILD_SET): { MARK_EXC_IP_SELECTIVE(); DECODE_UINT; @@ -845,9 +857,9 @@ unwind_jump:; SET_TOP(mp_obj_new_set(unum, sp)); DISPATCH(); } -#endif + #endif -#if MICROPY_PY_BUILTINS_SLICE + #if MICROPY_PY_BUILTINS_SLICE ENTRY(MP_BC_BUILD_SLICE): { MARK_EXC_IP_SELECTIVE(); mp_obj_t step = mp_const_none; @@ -860,7 +872,7 @@ unwind_jump:; SET_TOP(mp_obj_new_slice(start, stop, step)); DISPATCH(); } -#endif + #endif ENTRY(MP_BC_STORE_COMP): { MARK_EXC_IP_SELECTIVE(); @@ -1046,6 +1058,7 @@ unwind_jump:; } #endif SET_TOP(mp_call_method_n_kw(unum & 0xff, (unum >> 8) & 0xff, sp)); + // CIRCUITPY DISPATCH_WITH_PEND_EXC_CHECK(); } @@ -1101,7 +1114,7 @@ unwind_return: // Search for and execute finally handlers that aren't already active while (exc_sp >= exc_stack) { if (MP_TAGPTR_TAG1(exc_sp->val_sp)) { - if (exc_sp->handler > ip) { + if (exc_sp->handler >= ip) { // Found a finally handler that isn't active; run it. // Getting here the stack looks like: // (..., X, [iter0, iter1, ...,] ret_val) @@ -1156,7 +1169,13 @@ unwind_return: ENTRY(MP_BC_RAISE_LAST): { MARK_EXC_IP_SELECTIVE(); // search for the inner-most previous exception, to reraise it - mp_obj_t obj = get_active_exception(exc_sp, exc_stack); + mp_obj_t obj = MP_OBJ_NULL; + for (mp_exc_stack_t *e = exc_sp; e >= exc_stack; --e) { + if (e->prev_exc != NULL) { + obj = MP_OBJ_FROM_PTR(e->prev_exc); + break; + } + } if (obj == MP_OBJ_NULL) { obj = mp_obj_new_exception_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("no active exception to reraise")); } @@ -1166,30 +1185,14 @@ unwind_return: ENTRY(MP_BC_RAISE_OBJ): { MARK_EXC_IP_SELECTIVE(); mp_obj_t obj = mp_make_raise_obj(TOP()); - #if MICROPY_CPYTHON_EXCEPTION_CHAIN - mp_obj_t active_exception = get_active_exception(exc_sp, exc_stack); - if (active_exception != MP_OBJ_NULL && active_exception != obj) { - mp_store_attr(obj, MP_QSTR___context__, active_exception); - } - #endif RAISE(obj); } ENTRY(MP_BC_RAISE_FROM): { MARK_EXC_IP_SELECTIVE(); - mp_obj_t cause = POP(); - mp_obj_t obj = mp_make_raise_obj(TOP()); - #if MICROPY_CPYTHON_EXCEPTION_CHAIN - // search for the inner-most previous exception, to chain it - mp_obj_t active_exception = get_active_exception(exc_sp, exc_stack); - if (active_exception != MP_OBJ_NULL && active_exception != obj) { - mp_store_attr(obj, MP_QSTR___context__, active_exception); - } - mp_store_attr(obj, MP_QSTR___cause__, cause); - #else - (void)cause; mp_warning(NULL, "exception chaining not supported"); - #endif + sp--; // ignore (pop) "from" argument + mp_obj_t obj = mp_make_raise_obj(TOP()); RAISE(obj); } @@ -1204,9 +1207,6 @@ yield: ENTRY(MP_BC_YIELD_FROM): { MARK_EXC_IP_SELECTIVE(); -//#define EXC_MATCH(exc, type) mp_obj_is_type(exc, type) -#define EXC_MATCH(exc, type) mp_obj_exception_match(exc, type) -#define GENERATOR_EXIT_IF_NEEDED(t) if (t != MP_OBJ_NULL && EXC_MATCH(t, MP_OBJ_FROM_PTR(&mp_type_GeneratorExit))) { mp_obj_t raise_t = mp_make_raise_obj(t); RAISE(raise_t); } mp_vm_return_kind_t ret_kind; mp_obj_t send_value = POP(); mp_obj_t t_exc = MP_OBJ_NULL; @@ -1230,11 +1230,14 @@ yield: SET_TOP(ret_value); // If we injected GeneratorExit downstream, then even // if it was swallowed, we re-raise GeneratorExit - GENERATOR_EXIT_IF_NEEDED(t_exc); + if (t_exc != MP_OBJ_NULL && mp_obj_exception_match(t_exc, MP_OBJ_FROM_PTR(&mp_type_GeneratorExit))) { + mp_obj_t raise_t = mp_make_raise_obj(t_exc); + RAISE(raise_t); + } DISPATCH(); } else { assert(ret_kind == MP_VM_RETURN_EXCEPTION); - assert(!EXC_MATCH(ret_value, MP_OBJ_FROM_PTR(&mp_type_StopIteration))); + assert(!mp_obj_exception_match(ret_value, MP_OBJ_FROM_PTR(&mp_type_StopIteration))); // Pop exhausted gen sp--; RAISE(ret_value); @@ -1264,7 +1267,7 @@ yield: mp_import_all(POP()); DISPATCH(); -#if MICROPY_OPT_COMPUTED_GOTO + #if MICROPY_OPT_COMPUTED_GOTO ENTRY(MP_BC_LOAD_CONST_SMALL_INT_MULTI): PUSH(MP_OBJ_NEW_SMALL_INT((mp_int_t)ip[-1] - MP_BC_LOAD_CONST_SMALL_INT_MULTI - MP_BC_LOAD_CONST_SMALL_INT_MULTI_EXCESS)); DISPATCH(); @@ -1292,7 +1295,7 @@ yield: ENTRY_DEFAULT: MARK_EXC_IP_SELECTIVE(); -#else + #else ENTRY_DEFAULT: if (ip[-1] < MP_BC_LOAD_CONST_SMALL_INT_MULTI + MP_BC_LOAD_CONST_SMALL_INT_MULTI_NUM) { PUSH(MP_OBJ_NEW_SMALL_INT((mp_int_t)ip[-1] - MP_BC_LOAD_CONST_SMALL_INT_MULTI - MP_BC_LOAD_CONST_SMALL_INT_MULTI_EXCESS)); @@ -1312,9 +1315,8 @@ yield: SET_TOP(mp_binary_op(ip[-1] - MP_BC_BINARY_OP_MULTI, lhs, rhs)); DISPATCH(); } else -#endif + #endif // MICROPY_OPT_COMPUTED_GOTO { - mp_obj_t obj = mp_obj_new_exception_msg(&mp_type_NotImplementedError, MP_ERROR_TEXT("opcode")); nlr_pop(); code_state->state[0] = obj; @@ -1322,46 +1324,47 @@ yield: return MP_VM_RETURN_EXCEPTION; } -#if !MICROPY_OPT_COMPUTED_GOTO - } // switch -#endif + #if !MICROPY_OPT_COMPUTED_GOTO + } // switch + #endif pending_exception_check: + // We've just done a branch, use this as a convenient point to + // run periodic code/checks and/or bounce the GIL.. i.e. + // not _every_ instruction but on average a branch should + // occur every few instructions. MICROPY_VM_HOOK_LOOP + // Check for pending exceptions or scheduled tasks to run. + // Note: it's safe to just call mp_handle_pending(true), but + // we can inline the check for the common case where there is + // neither. + if ( #if MICROPY_ENABLE_SCHEDULER - // This is an inlined variant of mp_handle_pending - if (MP_STATE_VM(sched_state) == MP_SCHED_PENDING) { - mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); - // Re-check state is still pending now that we're in the atomic section. - if (MP_STATE_VM(sched_state) == MP_SCHED_PENDING) { - MARK_EXC_IP_SELECTIVE(); - mp_obj_t obj = MP_STATE_THREAD(mp_pending_exception); - if (obj != MP_OBJ_NULL) { - MP_STATE_THREAD(mp_pending_exception) = MP_OBJ_NULL; - if (!mp_sched_num_pending()) { - MP_STATE_VM(sched_state) = MP_SCHED_IDLE; - } - MICROPY_END_ATOMIC_SECTION(atomic_state); - RAISE(obj); - } - mp_handle_pending_tail(atomic_state); - } else { - MICROPY_END_ATOMIC_SECTION(atomic_state); - } - } + #if MICROPY_PY_THREAD + // Scheduler + threading: Scheduler and pending exceptions are independent, check both. + MP_STATE_VM(sched_state) == MP_SCHED_PENDING || MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL #else - // This is an inlined variant of mp_handle_pending - if (MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL) { + // Scheduler + non-threading: Optimisation: pending exception sets sched_state, only check sched_state. + MP_STATE_VM(sched_state) == MP_SCHED_PENDING + #endif + #else + // No scheduler: Just check pending exception. + MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL + #endif + #if MICROPY_ENABLE_VM_ABORT + // Check if the VM should abort execution. + || MP_STATE_VM(vm_abort) + #endif + ) { MARK_EXC_IP_SELECTIVE(); - mp_obj_t obj = MP_STATE_THREAD(mp_pending_exception); - MP_STATE_THREAD(mp_pending_exception) = MP_OBJ_NULL; - RAISE(obj); + mp_handle_pending(true); } #endif #if MICROPY_PY_THREAD_GIL #if MICROPY_PY_THREAD_GIL_VM_DIVISOR + // Don't bounce the GIL too frequently (default every 32 branches). if (--gil_divisor == 0) #endif { @@ -1373,8 +1376,8 @@ pending_exception_check: if (MP_STATE_VM(sched_state) == MP_SCHED_IDLE) #endif { - MP_THREAD_GIL_EXIT(); - MP_THREAD_GIL_ENTER(); + MP_THREAD_GIL_EXIT(); + MP_THREAD_GIL_ENTER(); } } #endif @@ -1395,22 +1398,20 @@ exception_handler: #endif if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(((mp_obj_base_t*)nlr.ret_val)->type), MP_OBJ_FROM_PTR(&mp_type_StopIteration))) { - if (code_state->ip) { - // check if it's a StopIteration within a for block - if (*code_state->ip == MP_BC_FOR_ITER) { - const byte *ip = code_state->ip + 1; - DECODE_ULABEL; // the jump offset if iteration finishes; for labels are always forward - code_state->ip = ip + ulab; // jump to after for-block - code_state->sp -= MP_OBJ_ITER_BUF_NSLOTS; // pop the exhausted iterator - goto outer_dispatch_loop; // continue with dispatch loop - } else if (*code_state->ip == MP_BC_YIELD_FROM) { - // StopIteration inside yield from call means return a value of - // yield from, so inject exception's value as yield from's result - // (Instead of stack pop then push we just replace exhausted gen with value) - *code_state->sp = mp_obj_exception_get_value(MP_OBJ_FROM_PTR(nlr.ret_val)); - code_state->ip++; // yield from is over, move to next instruction - goto outer_dispatch_loop; // continue with dispatch loop - } + // check if it's a StopIteration within a for block + if (*code_state->ip == MP_BC_FOR_ITER) { + const byte *ip = code_state->ip + 1; + DECODE_ULABEL; // the jump offset if iteration finishes; for labels are always forward + code_state->ip = ip + ulab; // jump to after for-block + code_state->sp -= MP_OBJ_ITER_BUF_NSLOTS; // pop the exhausted iterator + goto outer_dispatch_loop; // continue with dispatch loop + } else if (*code_state->ip == MP_BC_YIELD_FROM) { + // StopIteration inside yield from call means return a value of + // yield from, so inject exception's value as yield from's result + // (Instead of stack pop then push we just replace exhausted gen with value) + *code_state->sp = mp_obj_exception_get_value(MP_OBJ_FROM_PTR(nlr.ret_val)); + code_state->ip++; // yield from is over, move to next instruction + goto outer_dispatch_loop; // continue with dispatch loop } } @@ -1429,6 +1430,7 @@ unwind_loop: // - exceptions re-raised by END_FINALLY // - exceptions re-raised explicitly by "raise" if ( true + // CIRCUITPY #if MICROPY_CONST_GENERATOREXIT_OBJ && nlr.ret_val != &mp_static_GeneratorExit_obj #endif @@ -1471,6 +1473,7 @@ unwind_loop: // 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 #if MICROPY_CPYTHON_EXCEPTION_CHAIN mp_obj_t active_exception = get_active_exception(exc_sp, exc_stack); #endif diff --git a/shared-bindings/_bleio/ScanResults.h b/shared-bindings/_bleio/ScanResults.h index a8c88c215d..f3102eb2ce 100644 --- a/shared-bindings/_bleio/ScanResults.h +++ b/shared-bindings/_bleio/ScanResults.h @@ -3,9 +3,13 @@ * * The MIT License (MIT) * +<<<<<<<< HEAD:shared-bindings/_bleio/ScanResults.h * Copyright (c) 2019 Dan Halbert for Adafruit Industries * Copyright (c) 2018 Artur Pacholec * Copyright (c) 2017 Glenn Ruben Bakke +======== + * Copyright (c) 2022 Blake W. Felt & Angus Gratton +>>>>>>>> v1.20.0:shared/tinyusb/mp_usbd.h * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -26,6 +30,7 @@ * THE SOFTWARE. */ +<<<<<<<< HEAD:shared-bindings/_bleio/ScanResults.h #ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SCANRESULTS_H #define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SCANRESULTS_H @@ -37,3 +42,18 @@ extern const mp_obj_type_t bleio_scanresults_type; mp_obj_t common_hal_bleio_scanresults_next(bleio_scanresults_obj_t *self); #endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SCANRESULTS_H +======== +#ifndef MICROPY_INCLUDED_SHARED_TINYUSB_MP_USBD_H +#define MICROPY_INCLUDED_SHARED_TINYUSB_MP_USBD_H + +#include "py/obj.h" + +// Call instead of tud_task() +void mp_usbd_task(void); + +// Function to be implemented in port code. +// Can write a string up to MICROPY_HW_USB_DESC_STR_MAX characters long, plus terminating byte. +extern void mp_usbd_port_get_serial_number(char *buf); + +#endif // MICROPY_INCLUDED_SHARED_TINYUSB_USBD_H +>>>>>>>> v1.20.0:shared/tinyusb/mp_usbd.h diff --git a/shared-bindings/_pew/PewPew.c b/shared-bindings/_pew/PewPew.c index 229fb24381..794d4b207c 100644 --- a/shared-bindings/_pew/PewPew.c +++ b/shared-bindings/_pew/PewPew.c @@ -32,7 +32,6 @@ #include "shared-bindings/util.h" #include "PewPew.h" #include "common-hal/_pew/PewPew.h" -#include "supervisor/shared/translate/translate.h" //| class PewPew: //| """This is an internal module to be used by the ``pew.py`` library from diff --git a/shared-bindings/_stage/Layer.c b/shared-bindings/_stage/Layer.c index a5dd130dac..e92e950fb1 100644 --- a/shared-bindings/_stage/Layer.c +++ b/shared-bindings/_stage/Layer.c @@ -24,11 +24,10 @@ * THE SOFTWARE. */ -#include +#include "py/runtime.h" #include "__init__.h" #include "Layer.h" -#include "supervisor/shared/translate/translate.h" //| class Layer: //| """Keep information about a single layer of graphics""" diff --git a/shared-bindings/_stage/Text.c b/shared-bindings/_stage/Text.c index 0c54f123d7..b53dccfc65 100644 --- a/shared-bindings/_stage/Text.c +++ b/shared-bindings/_stage/Text.c @@ -28,7 +28,6 @@ #include "__init__.h" #include "Text.h" -#include "supervisor/shared/translate/translate.h" //| class Text: //| """Keep information about a single grid of text""" diff --git a/shared-bindings/adafruit_bus_device/i2c_device/I2CDevice.c b/shared-bindings/adafruit_bus_device/i2c_device/I2CDevice.c index 7dd5e09868..dfc77fde16 100644 --- a/shared-bindings/adafruit_bus_device/i2c_device/I2CDevice.c +++ b/shared-bindings/adafruit_bus_device/i2c_device/I2CDevice.c @@ -36,7 +36,6 @@ #include "shared/runtime/context_manager_helpers.h" #include "py/runtime.h" #include "py/smallint.h" -#include "supervisor/shared/translate/translate.h" //| class I2CDevice: diff --git a/shared-bindings/adafruit_bus_device/spi_device/SPIDevice.c b/shared-bindings/adafruit_bus_device/spi_device/SPIDevice.c index 47f93f3230..cb148143e4 100644 --- a/shared-bindings/adafruit_bus_device/spi_device/SPIDevice.c +++ b/shared-bindings/adafruit_bus_device/spi_device/SPIDevice.c @@ -35,8 +35,6 @@ #include "shared/runtime/buffer_helper.h" #include "shared/runtime/context_manager_helpers.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" - //| class SPIDevice: //| """SPI Device Manager""" diff --git a/shared-bindings/alarm/SleepMemory.c b/shared-bindings/alarm/SleepMemory.c index 0304abfbcc..c51c15ad46 100644 --- a/shared-bindings/alarm/SleepMemory.c +++ b/shared-bindings/alarm/SleepMemory.c @@ -31,7 +31,6 @@ #include "py/runtime0.h" #include "shared-bindings/alarm/SleepMemory.h" -#include "supervisor/shared/translate/translate.h" //| class SleepMemory: //| """Store raw bytes in RAM that persists during deep sleep. diff --git a/shared-bindings/alarm/pin/PinAlarm.c b/shared-bindings/alarm/pin/PinAlarm.c index b0e21771c5..3eeb83c835 100644 --- a/shared-bindings/alarm/pin/PinAlarm.c +++ b/shared-bindings/alarm/pin/PinAlarm.c @@ -33,7 +33,6 @@ #include "py/obj.h" #include "py/objproperty.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" //| class PinAlarm: //| """Trigger an alarm when a pin changes state.""" diff --git a/shared-bindings/alarm/time/TimeAlarm.c b/shared-bindings/alarm/time/TimeAlarm.c index aed14613ea..0a886e6680 100644 --- a/shared-bindings/alarm/time/TimeAlarm.c +++ b/shared-bindings/alarm/time/TimeAlarm.c @@ -33,8 +33,6 @@ #include "shared-bindings/rtc/__init__.h" #include "shared-bindings/time/__init__.h" -#include "supervisor/shared/translate/translate.h" - #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE mp_obj_t MP_WEAK rtc_get_time_source_time(void) { mp_raise_RuntimeError(translate("RTC is not supported on this board")); diff --git a/shared-bindings/analogio/AnalogOut.c b/shared-bindings/analogio/AnalogOut.c index 36a6532b7c..7261bc61f6 100644 --- a/shared-bindings/analogio/AnalogOut.c +++ b/shared-bindings/analogio/AnalogOut.c @@ -34,7 +34,6 @@ #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/analogio/AnalogOut.h" #include "shared-bindings/util.h" -#include "supervisor/shared/translate/translate.h" //| class AnalogOut: //| """Output analog values (a specific voltage). diff --git a/shared-bindings/audiobusio/I2SOut.c b/shared-bindings/audiobusio/I2SOut.c index 8c6f1a3a7a..eeabde281e 100644 --- a/shared-bindings/audiobusio/I2SOut.c +++ b/shared-bindings/audiobusio/I2SOut.c @@ -33,7 +33,6 @@ #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/audiobusio/I2SOut.h" #include "shared-bindings/util.h" -#include "supervisor/shared/translate/translate.h" //| class I2SOut: //| """Output an I2S audio signal""" diff --git a/shared-bindings/audiobusio/PDMIn.c b/shared-bindings/audiobusio/PDMIn.c index f5105b367a..f3062bd223 100644 --- a/shared-bindings/audiobusio/PDMIn.c +++ b/shared-bindings/audiobusio/PDMIn.c @@ -34,7 +34,6 @@ #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/audiobusio/PDMIn.h" #include "shared-bindings/util.h" -#include "supervisor/shared/translate/translate.h" //| class PDMIn: //| """Record an input PDM audio stream""" diff --git a/shared-bindings/audiocore/RawSample.c b/shared-bindings/audiocore/RawSample.c index 58ec83fe35..cf593de603 100644 --- a/shared-bindings/audiocore/RawSample.c +++ b/shared-bindings/audiocore/RawSample.c @@ -32,7 +32,6 @@ #include "py/runtime.h" #include "shared-bindings/util.h" #include "shared-bindings/audiocore/RawSample.h" -#include "supervisor/shared/translate/translate.h" //| class RawSample: //| """A raw audio sample buffer in memory""" diff --git a/shared-bindings/audiocore/WaveFile.c b/shared-bindings/audiocore/WaveFile.c index aa50321040..4831a104f8 100644 --- a/shared-bindings/audiocore/WaveFile.c +++ b/shared-bindings/audiocore/WaveFile.c @@ -31,7 +31,6 @@ #include "py/runtime.h" #include "shared-bindings/audiocore/WaveFile.h" #include "shared-bindings/util.h" -#include "supervisor/shared/translate/translate.h" #include "extmod/vfs_posix.h" //| class WaveFile: diff --git a/shared-bindings/audioio/AudioOut.c b/shared-bindings/audioio/AudioOut.c index c66f109f9a..39ceb8cefb 100644 --- a/shared-bindings/audioio/AudioOut.c +++ b/shared-bindings/audioio/AudioOut.c @@ -34,7 +34,6 @@ #include "shared-bindings/audioio/AudioOut.h" #include "shared-bindings/audiocore/RawSample.h" #include "shared-bindings/util.h" -#include "supervisor/shared/translate/translate.h" //| class AudioOut: //| """Output an analog audio signal""" diff --git a/shared-bindings/audiomixer/Mixer.c b/shared-bindings/audiomixer/Mixer.c index 6ddc5c3641..35b21f9341 100644 --- a/shared-bindings/audiomixer/Mixer.c +++ b/shared-bindings/audiomixer/Mixer.c @@ -34,7 +34,6 @@ #include "py/objproperty.h" #include "py/runtime.h" #include "shared-bindings/util.h" -#include "supervisor/shared/translate/translate.h" //| class Mixer: //| """Mixes one or more audio samples together into one sample.""" diff --git a/shared-bindings/audiomixer/MixerVoice.c b/shared-bindings/audiomixer/MixerVoice.c index 74b9c6745d..637e91f053 100644 --- a/shared-bindings/audiomixer/MixerVoice.c +++ b/shared-bindings/audiomixer/MixerVoice.c @@ -33,7 +33,6 @@ #include "py/objproperty.h" #include "py/runtime.h" #include "shared-bindings/util.h" -#include "supervisor/shared/translate/translate.h" //| class MixerVoice: //| """Voice objects used with Mixer diff --git a/shared-bindings/audiomp3/MP3Decoder.c b/shared-bindings/audiomp3/MP3Decoder.c index ef5445a1de..01496d1d90 100644 --- a/shared-bindings/audiomp3/MP3Decoder.c +++ b/shared-bindings/audiomp3/MP3Decoder.c @@ -32,7 +32,6 @@ #include "py/runtime.h" #include "shared-bindings/audiomp3/MP3Decoder.h" #include "shared-bindings/util.h" -#include "supervisor/shared/translate/translate.h" //| class MP3Decoder: //| """Load a mp3 file for audio playback diff --git a/shared-bindings/audiopwmio/PWMAudioOut.c b/shared-bindings/audiopwmio/PWMAudioOut.c index ce35374618..24aa01e678 100644 --- a/shared-bindings/audiopwmio/PWMAudioOut.c +++ b/shared-bindings/audiopwmio/PWMAudioOut.c @@ -34,7 +34,6 @@ #include "shared-bindings/audiopwmio/PWMAudioOut.h" #include "shared-bindings/audiocore/RawSample.h" #include "shared-bindings/util.h" -#include "supervisor/shared/translate/translate.h" //| class PWMAudioOut: //| """Output an analog audio signal by varying the PWM duty cycle.""" diff --git a/shared-bindings/bitbangio/I2C.c b/shared-bindings/bitbangio/I2C.c index 390e1c0c98..26f85fa54f 100644 --- a/shared-bindings/bitbangio/I2C.c +++ b/shared-bindings/bitbangio/I2C.c @@ -36,7 +36,6 @@ #include "py/binary.h" #include "py/mperrno.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" //| class I2C: //| """Two wire serial protocol""" diff --git a/shared-bindings/bitbangio/SPI.c b/shared-bindings/bitbangio/SPI.c index 2ec2220bef..14af508032 100644 --- a/shared-bindings/bitbangio/SPI.c +++ b/shared-bindings/bitbangio/SPI.c @@ -38,7 +38,6 @@ #include "py/binary.h" #include "py/mperrno.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" //| class SPI: //| """A 3-4 wire serial protocol diff --git a/shared-bindings/busio/I2C.c b/shared-bindings/busio/I2C.c index 14ec7093a5..4ebbe03184 100644 --- a/shared-bindings/busio/I2C.c +++ b/shared-bindings/busio/I2C.c @@ -35,7 +35,6 @@ #include "shared/runtime/context_manager_helpers.h" #include "py/binary.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" //| class I2C: //| """Two wire serial protocol""" diff --git a/shared-bindings/busio/SPI.c b/shared-bindings/busio/SPI.c index 9d843a7500..f9061450ae 100644 --- a/shared-bindings/busio/SPI.c +++ b/shared-bindings/busio/SPI.c @@ -39,7 +39,6 @@ #include "py/mperrno.h" #include "py/objproperty.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" //| class SPI: diff --git a/shared-bindings/busio/UART.c b/shared-bindings/busio/UART.c index 314a012d01..6cedfad64a 100644 --- a/shared-bindings/busio/UART.c +++ b/shared-bindings/busio/UART.c @@ -38,7 +38,6 @@ #include "py/objtype.h" #include "py/runtime.h" #include "py/stream.h" -#include "supervisor/shared/translate/translate.h" #define STREAM_DEBUG(...) (void)0 // #define STREAM_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__) diff --git a/shared-bindings/digitalio/DigitalInOut.c b/shared-bindings/digitalio/DigitalInOut.c index 832f16558a..500c9b7253 100644 --- a/shared-bindings/digitalio/DigitalInOut.c +++ b/shared-bindings/digitalio/DigitalInOut.c @@ -41,7 +41,6 @@ #include "shared-bindings/digitalio/DriveMode.h" #include "shared-bindings/digitalio/Pull.h" #include "shared-bindings/util.h" -#include "supervisor/shared/translate/translate.h" #if CIRCUITPY_CYW43 #include "bindings/cyw43/__init__.h" diff --git a/shared-bindings/digitalio/Pull.c b/shared-bindings/digitalio/Pull.c index 3c7a036d1e..5f89c9dc26 100644 --- a/shared-bindings/digitalio/Pull.c +++ b/shared-bindings/digitalio/Pull.c @@ -26,7 +26,6 @@ #include "py/runtime.h" #include "shared-bindings/digitalio/Pull.h" -#include "supervisor/shared/translate/translate.h" //| class Pull: //| """Defines the pull of a digital input pin""" diff --git a/shared-bindings/displayio/Bitmap.c b/shared-bindings/displayio/Bitmap.c index 7a314f0098..6b758a4c75 100644 --- a/shared-bindings/displayio/Bitmap.c +++ b/shared-bindings/displayio/Bitmap.c @@ -34,7 +34,6 @@ #include "py/objproperty.h" #include "py/runtime.h" #include "shared-bindings/util.h" -#include "supervisor/shared/translate/translate.h" //| class Bitmap: //| """Stores values of a certain size in a 2D array diff --git a/shared-bindings/displayio/ColorConverter.c b/shared-bindings/displayio/ColorConverter.c index 0ec78779ee..fe891b38a0 100644 --- a/shared-bindings/displayio/ColorConverter.c +++ b/shared-bindings/displayio/ColorConverter.c @@ -34,7 +34,6 @@ #include "py/objproperty.h" #include "py/runtime.h" #include "shared-bindings/util.h" -#include "supervisor/shared/translate/translate.h" //| class ColorConverter: //| """Converts one color format to another.""" diff --git a/shared-bindings/displayio/Display.c b/shared-bindings/displayio/Display.c index c7fb6dadf4..9d33081815 100644 --- a/shared-bindings/displayio/Display.c +++ b/shared-bindings/displayio/Display.c @@ -37,7 +37,6 @@ #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/util.h" #include "shared-module/displayio/__init__.h" -#include "supervisor/shared/translate/translate.h" //| _DisplayBus = Union["FourWire", "paralleldisplay.ParallelBus", "I2CDisplay"] //| """:py:class:`FourWire`, :py:class:`paralleldisplay.ParallelBus` or :py:class:`I2CDisplay`""" diff --git a/shared-bindings/displayio/EPaperDisplay.c b/shared-bindings/displayio/EPaperDisplay.c index cca8c919cc..59d394b7fb 100644 --- a/shared-bindings/displayio/EPaperDisplay.c +++ b/shared-bindings/displayio/EPaperDisplay.c @@ -37,7 +37,6 @@ #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/util.h" #include "shared-module/displayio/__init__.h" -#include "supervisor/shared/translate/translate.h" //| class EPaperDisplay: //| """Manage updating an epaper display over a display bus diff --git a/shared-bindings/displayio/FourWire.c b/shared-bindings/displayio/FourWire.c index 29cdf09299..cca473d392 100644 --- a/shared-bindings/displayio/FourWire.c +++ b/shared-bindings/displayio/FourWire.c @@ -37,7 +37,6 @@ #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/util.h" #include "shared-module/displayio/__init__.h" -#include "supervisor/shared/translate/translate.h" //| class FourWire: //| """Manage updating a display over SPI four wire protocol in the background while Python code runs. diff --git a/shared-bindings/displayio/Group.c b/shared-bindings/displayio/Group.c index 0800f4c861..1fd3d1f154 100644 --- a/shared-bindings/displayio/Group.c +++ b/shared-bindings/displayio/Group.c @@ -33,7 +33,6 @@ #include "py/objproperty.h" #include "py/objtype.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" //| class Group: //| """Manage a group of sprites and groups and how they are inter-related.""" diff --git a/shared-bindings/displayio/I2CDisplay.c b/shared-bindings/displayio/I2CDisplay.c index ca5968421c..28c80071e8 100644 --- a/shared-bindings/displayio/I2CDisplay.c +++ b/shared-bindings/displayio/I2CDisplay.c @@ -37,7 +37,6 @@ #include "shared-bindings/busio/I2C.h" #include "shared-bindings/util.h" #include "shared-module/displayio/__init__.h" -#include "supervisor/shared/translate/translate.h" //| class I2CDisplay: //| """Manage updating a display over I2C in the background while Python code runs. diff --git a/shared-bindings/displayio/OnDiskBitmap.c b/shared-bindings/displayio/OnDiskBitmap.c index d0dcba2339..f922020650 100644 --- a/shared-bindings/displayio/OnDiskBitmap.c +++ b/shared-bindings/displayio/OnDiskBitmap.c @@ -30,7 +30,7 @@ #include "py/runtime.h" #include "py/objproperty.h" -#include "supervisor/shared/translate/translate.h" + #include "shared-bindings/displayio/OnDiskBitmap.h" //| class OnDiskBitmap: diff --git a/shared-bindings/displayio/Palette.c b/shared-bindings/displayio/Palette.c index abe5e369dd..039af2a730 100644 --- a/shared-bindings/displayio/Palette.c +++ b/shared-bindings/displayio/Palette.c @@ -34,7 +34,6 @@ #include "py/runtime.h" #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/util.h" -#include "supervisor/shared/translate/translate.h" //| class Palette: //| """Map a pixel palette_index to a full color. Colors are transformed to the display's format internally to diff --git a/shared-bindings/displayio/Shape.c b/shared-bindings/displayio/Shape.c index 572ed216b8..db35e8e277 100644 --- a/shared-bindings/displayio/Shape.c +++ b/shared-bindings/displayio/Shape.c @@ -32,7 +32,6 @@ #include "py/objproperty.h" #include "py/runtime.h" #include "shared-bindings/util.h" -#include "supervisor/shared/translate/translate.h" //| class Shape: //| """Represents a shape made by defining boundaries that may be mirrored.""" diff --git a/shared-bindings/displayio/TileGrid.c b/shared-bindings/displayio/TileGrid.c index 5d27580fdf..dbade39e51 100644 --- a/shared-bindings/displayio/TileGrid.c +++ b/shared-bindings/displayio/TileGrid.c @@ -38,7 +38,6 @@ #include "shared-bindings/displayio/OnDiskBitmap.h" #include "shared-bindings/displayio/Palette.h" #include "shared-bindings/displayio/Shape.h" -#include "supervisor/shared/translate/translate.h" //| class TileGrid: //| """A grid of tiles sourced out of one bitmap diff --git a/shared-bindings/fontio/BuiltinFont.c b/shared-bindings/fontio/BuiltinFont.c index cf28b6f223..8e47640730 100644 --- a/shared-bindings/fontio/BuiltinFont.c +++ b/shared-bindings/fontio/BuiltinFont.c @@ -34,7 +34,6 @@ #include "py/runtime.h" #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/util.h" -#include "supervisor/shared/translate/translate.h" //| from typing_extensions import Protocol # for compat with python < 3.8 //| diff --git a/shared-bindings/framebufferio/FramebufferDisplay.c b/shared-bindings/framebufferio/FramebufferDisplay.c index a1ad6a0bac..3c0bf4407d 100644 --- a/shared-bindings/framebufferio/FramebufferDisplay.c +++ b/shared-bindings/framebufferio/FramebufferDisplay.c @@ -38,7 +38,6 @@ #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/util.h" #include "shared-module/displayio/__init__.h" -#include "supervisor/shared/translate/translate.h" //| class FramebufferDisplay: //| """Manage updating a display with framebuffer in RAM diff --git a/shared-bindings/frequencyio/FrequencyIn.c b/shared-bindings/frequencyio/FrequencyIn.c index c3ce5e94a7..832948d6ae 100644 --- a/shared-bindings/frequencyio/FrequencyIn.c +++ b/shared-bindings/frequencyio/FrequencyIn.c @@ -33,7 +33,6 @@ #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/frequencyio/FrequencyIn.h" #include "shared-bindings/util.h" -#include "supervisor/shared/translate/translate.h" //| class FrequencyIn: //| """Read a frequency signal diff --git a/shared-bindings/gifio/OnDiskGif.c b/shared-bindings/gifio/OnDiskGif.c index 840d58e410..c36b725202 100644 --- a/shared-bindings/gifio/OnDiskGif.c +++ b/shared-bindings/gifio/OnDiskGif.c @@ -32,7 +32,6 @@ #include "py/objproperty.h" #include "shared/runtime/context_manager_helpers.h" #include "shared-bindings/util.h" -#include "supervisor/shared/translate/translate.h" #include "shared-bindings/gifio/OnDiskGif.h" //| class OnDiskGif: diff --git a/shared-bindings/hashlib/__init__.c b/shared-bindings/hashlib/__init__.c index 87f6df495a..4179926ea5 100644 --- a/shared-bindings/hashlib/__init__.c +++ b/shared-bindings/hashlib/__init__.c @@ -31,7 +31,6 @@ #include "py/runtime.h" #include "shared-bindings/hashlib/__init__.h" #include "shared-bindings/hashlib/Hash.h" -#include "supervisor/shared/translate/translate.h" //| """Hashing related functions //| diff --git a/shared-bindings/math/__init__.c b/shared-bindings/math/__init__.c index 2c17532197..1ab07170f3 100644 --- a/shared-bindings/math/__init__.c +++ b/shared-bindings/math/__init__.c @@ -27,7 +27,6 @@ #include "py/builtin.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" #if MICROPY_PY_BUILTINS_FLOAT diff --git a/shared-bindings/memorymap/AddressRange.c b/shared-bindings/memorymap/AddressRange.c index 7fd1062534..ea814214b8 100644 --- a/shared-bindings/memorymap/AddressRange.c +++ b/shared-bindings/memorymap/AddressRange.c @@ -29,7 +29,6 @@ #include "py/runtime.h" #include "py/runtime0.h" #include "shared-bindings/memorymap/AddressRange.h" -#include "supervisor/shared/translate/translate.h" //| class AddressRange: //| r"""Presents a range of addresses as a bytearray. diff --git a/shared-bindings/memorymonitor/AllocationAlarm.c b/shared-bindings/memorymonitor/AllocationAlarm.c index a21a14b2f0..8cf810d3b8 100644 --- a/shared-bindings/memorymonitor/AllocationAlarm.c +++ b/shared-bindings/memorymonitor/AllocationAlarm.c @@ -31,7 +31,6 @@ #include "py/runtime0.h" #include "shared-bindings/memorymonitor/AllocationAlarm.h" #include "shared-bindings/util.h" -#include "supervisor/shared/translate/translate.h" //| class AllocationAlarm: //| def __init__(self, *, minimum_block_count: int = 1) -> None: diff --git a/shared-bindings/memorymonitor/AllocationSize.c b/shared-bindings/memorymonitor/AllocationSize.c index 3340f51615..9ac0e9617e 100644 --- a/shared-bindings/memorymonitor/AllocationSize.c +++ b/shared-bindings/memorymonitor/AllocationSize.c @@ -31,7 +31,6 @@ #include "py/runtime0.h" #include "shared-bindings/memorymonitor/AllocationSize.h" #include "shared-bindings/util.h" -#include "supervisor/shared/translate/translate.h" //| class AllocationSize: //| def __init__(self) -> None: diff --git a/shared-bindings/microcontroller/Pin.c b/shared-bindings/microcontroller/Pin.c index ea27df9812..06aacea8f1 100644 --- a/shared-bindings/microcontroller/Pin.c +++ b/shared-bindings/microcontroller/Pin.c @@ -31,7 +31,6 @@ #include "py/nlr.h" #include "py/obj.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" //| class Pin: //| """Identifies an IO pin on the microcontroller.""" diff --git a/shared-bindings/microcontroller/Processor.c b/shared-bindings/microcontroller/Processor.c index e4819047da..d57fbc2dd0 100644 --- a/shared-bindings/microcontroller/Processor.c +++ b/shared-bindings/microcontroller/Processor.c @@ -38,7 +38,6 @@ #include "py/objtype.h" #include "py/objproperty.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" //| class Processor: diff --git a/shared-bindings/microcontroller/__init__.c b/shared-bindings/microcontroller/__init__.c index 3858cad776..51940a3dab 100644 --- a/shared-bindings/microcontroller/__init__.c +++ b/shared-bindings/microcontroller/__init__.c @@ -39,8 +39,6 @@ #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/microcontroller/Processor.h" -#include "supervisor/shared/translate/translate.h" - //| """Pin references and cpu functionality //| //| The `microcontroller` module defines the pins and other bare-metal hardware diff --git a/shared-bindings/neopixel_write/__init__.c b/shared-bindings/neopixel_write/__init__.c index 8a471f26ff..3c307f5c71 100644 --- a/shared-bindings/neopixel_write/__init__.c +++ b/shared-bindings/neopixel_write/__init__.c @@ -30,7 +30,6 @@ #include "py/runtime.h" #include "shared-bindings/digitalio/DigitalInOut.h" #include "shared-bindings/util.h" -#include "supervisor/shared/translate/translate.h" // RGB LED timing information: diff --git a/shared-bindings/nvm/ByteArray.c b/shared-bindings/nvm/ByteArray.c index e7c3fc37e8..4b74a2a48f 100644 --- a/shared-bindings/nvm/ByteArray.c +++ b/shared-bindings/nvm/ByteArray.c @@ -29,7 +29,6 @@ #include "py/runtime.h" #include "py/runtime0.h" #include "shared-bindings/nvm/ByteArray.h" -#include "supervisor/shared/translate/translate.h" //| class ByteArray: //| r"""Presents a stretch of non-volatile memory as a bytearray. diff --git a/shared-bindings/os/__init__.c b/shared-bindings/os/__init__.c index 87b77a2d6d..d1b657a580 100644 --- a/shared-bindings/os/__init__.c +++ b/shared-bindings/os/__init__.c @@ -36,7 +36,6 @@ #include "py/objstr.h" #include "py/runtime.h" #include "shared-bindings/os/__init__.h" -#include "supervisor/shared/translate/translate.h" //| """functions that an OS normally provides //| diff --git a/shared-bindings/paralleldisplay/ParallelBus.c b/shared-bindings/paralleldisplay/ParallelBus.c index f8ab6470e4..be75e57018 100644 --- a/shared-bindings/paralleldisplay/ParallelBus.c +++ b/shared-bindings/paralleldisplay/ParallelBus.c @@ -35,7 +35,6 @@ #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/util.h" #include "shared-module/displayio/__init__.h" -#include "supervisor/shared/translate/translate.h" //| class ParallelBus: //| """Manage updating a display over 8-bit parallel bus in the background while Python code runs. This diff --git a/shared-bindings/ps2io/Ps2.c b/shared-bindings/ps2io/Ps2.c index 789e1173c8..3f5c1d6f00 100644 --- a/shared-bindings/ps2io/Ps2.c +++ b/shared-bindings/ps2io/Ps2.c @@ -34,7 +34,6 @@ #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/ps2io/Ps2.h" #include "shared-bindings/util.h" -#include "supervisor/shared/translate/translate.h" //| class Ps2: //| """Communicate with a PS/2 keyboard or mouse diff --git a/shared-bindings/pulseio/PulseIn.c b/shared-bindings/pulseio/PulseIn.c index d738726f1c..444b2a770f 100644 --- a/shared-bindings/pulseio/PulseIn.c +++ b/shared-bindings/pulseio/PulseIn.c @@ -33,7 +33,6 @@ #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/pulseio/PulseIn.h" #include "shared-bindings/util.h" -#include "supervisor/shared/translate/translate.h" //| class PulseIn: //| """Measure a series of active and idle pulses. This is commonly used in infrared receivers diff --git a/shared-bindings/pulseio/PulseOut.c b/shared-bindings/pulseio/PulseOut.c index 16777d9843..e9084bf4ba 100644 --- a/shared-bindings/pulseio/PulseOut.c +++ b/shared-bindings/pulseio/PulseOut.c @@ -34,7 +34,6 @@ #include "shared-bindings/pulseio/PulseOut.h" #include "shared-bindings/pwmio/PWMOut.h" #include "shared-bindings/util.h" -#include "supervisor/shared/translate/translate.h" //| class PulseOut: //| """Pulse PWM "carrier" output on and off. This is commonly used in infrared remotes. The diff --git a/shared-bindings/pwmio/PWMOut.c b/shared-bindings/pwmio/PWMOut.c index 0c99e26a7d..83a0f4ad1c 100644 --- a/shared-bindings/pwmio/PWMOut.c +++ b/shared-bindings/pwmio/PWMOut.c @@ -33,7 +33,6 @@ #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/pwmio/PWMOut.h" #include "shared-bindings/util.h" -#include "supervisor/shared/translate/translate.h" void common_hal_pwmio_pwmout_raise_error(pwmout_result_t result) { diff --git a/shared-bindings/random/__init__.c b/shared-bindings/random/__init__.c index 6fa9629f8c..16bdf95df4 100644 --- a/shared-bindings/random/__init__.c +++ b/shared-bindings/random/__init__.c @@ -31,7 +31,6 @@ #include "py/obj.h" #include "py/runtime.h" #include "shared-bindings/random/__init__.h" -#include "supervisor/shared/translate/translate.h" //| """pseudo-random numbers and choices //| diff --git a/shared-bindings/rtc/RTC.c b/shared-bindings/rtc/RTC.c index bf2b0915c4..ea323306f7 100644 --- a/shared-bindings/rtc/RTC.c +++ b/shared-bindings/rtc/RTC.c @@ -34,7 +34,6 @@ #include "shared-bindings/rtc/__init__.h" #include "shared-bindings/rtc/RTC.h" #include "shared-bindings/time/__init__.h" -#include "supervisor/shared/translate/translate.h" const rtc_rtc_obj_t rtc_rtc_obj = {{&rtc_rtc_type}}; diff --git a/shared-bindings/sdioio/SDCard.c b/shared-bindings/sdioio/SDCard.c index 7ff4fb750a..80a82a3ed3 100644 --- a/shared-bindings/sdioio/SDCard.c +++ b/shared-bindings/sdioio/SDCard.c @@ -38,7 +38,6 @@ #include "py/mperrno.h" #include "py/objproperty.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" //| class SDCard: //| """SD Card Block Interface with SDIO diff --git a/shared-bindings/storage/__init__.c b/shared-bindings/storage/__init__.c index b774c5550e..8eaf19cf0f 100644 --- a/shared-bindings/storage/__init__.c +++ b/shared-bindings/storage/__init__.c @@ -33,7 +33,6 @@ #include "py/objnamedtuple.h" #include "py/runtime.h" #include "shared-bindings/storage/__init__.h" -#include "supervisor/shared/translate/translate.h" #include "supervisor/flash.h" //| """Storage management diff --git a/shared-bindings/struct/__init__.c b/shared-bindings/struct/__init__.c index 519b1ace66..a1257c67c3 100644 --- a/shared-bindings/struct/__init__.c +++ b/shared-bindings/struct/__init__.c @@ -36,7 +36,6 @@ #include "py/parsenum.h" #include "shared-bindings/struct/__init__.h" #include "shared-module/struct/__init__.h" -#include "supervisor/shared/translate/translate.h" //| """Manipulation of c-style data //| diff --git a/shared-bindings/supervisor/__init__.c b/shared-bindings/supervisor/__init__.c index a1820e215e..8b93866655 100644 --- a/shared-bindings/supervisor/__init__.c +++ b/shared-bindings/supervisor/__init__.c @@ -33,7 +33,6 @@ #include "supervisor/shared/display.h" #include "supervisor/shared/reload.h" #include "supervisor/shared/traceback.h" -#include "supervisor/shared/translate/translate.h" #include "supervisor/shared/workflow.h" #if CIRCUITPY_USB_IDENTIFICATION diff --git a/shared-bindings/synthio/MidiTrack.c b/shared-bindings/synthio/MidiTrack.c index 79af9e9744..c7863e1cb2 100644 --- a/shared-bindings/synthio/MidiTrack.c +++ b/shared-bindings/synthio/MidiTrack.c @@ -33,7 +33,6 @@ #include "shared-bindings/util.h" #include "shared-bindings/synthio/MidiTrack.h" #include "shared-bindings/synthio/__init__.h" -#include "supervisor/shared/translate/translate.h" //| class MidiTrack: //| """Simple MIDI synth""" diff --git a/shared-bindings/synthio/MidiTrack.h b/shared-bindings/synthio/MidiTrack.h index 5c6bce46cd..cafe913430 100644 --- a/shared-bindings/synthio/MidiTrack.h +++ b/shared-bindings/synthio/MidiTrack.h @@ -1,5 +1,5 @@ /* - * This file is part of the Micro Python project, http://micropython.org/ + * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * diff --git a/shared-bindings/synthio/Synthesizer.c b/shared-bindings/synthio/Synthesizer.c index aa3d74e015..db1d6312f7 100644 --- a/shared-bindings/synthio/Synthesizer.c +++ b/shared-bindings/synthio/Synthesizer.c @@ -37,7 +37,6 @@ #include "shared-bindings/synthio/Synthesizer.h" #include "shared-bindings/synthio/LFO.h" #include "shared-bindings/synthio/__init__.h" -#include "supervisor/shared/translate/translate.h" //| NoteSequence = Sequence[Union[int, Note]] //| """A sequence of notes, which can each be integer MIDI note numbers or `Note` objects""" diff --git a/shared-bindings/terminalio/Terminal.c b/shared-bindings/terminalio/Terminal.c index 05c10f2887..fb2a8283c7 100644 --- a/shared-bindings/terminalio/Terminal.c +++ b/shared-bindings/terminalio/Terminal.c @@ -35,7 +35,6 @@ #include "py/runtime.h" #include "py/stream.h" #include "shared-bindings/fontio/BuiltinFont.h" -#include "supervisor/shared/translate/translate.h" //| class Terminal: //| """Display a character stream with a TileGrid diff --git a/shared-bindings/time/__init__.c b/shared-bindings/time/__init__.c index a4e5033def..66a3267264 100644 --- a/shared-bindings/time/__init__.c +++ b/shared-bindings/time/__init__.c @@ -34,7 +34,6 @@ #include "shared/timeutils/timeutils.h" #include "shared-bindings/rtc/__init__.h" #include "shared-bindings/time/__init__.h" -#include "supervisor/shared/translate/translate.h" //| """time and timing related functions //| diff --git a/shared-bindings/touchio/TouchIn.c b/shared-bindings/touchio/TouchIn.c index 64de6e18b0..100b22c03d 100644 --- a/shared-bindings/touchio/TouchIn.c +++ b/shared-bindings/touchio/TouchIn.c @@ -36,7 +36,6 @@ #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/touchio/TouchIn.h" #include "shared-bindings/util.h" -#include "supervisor/shared/translate/translate.h" //| class TouchIn: //| """Read the state of a capacitive touch sensor diff --git a/shared-bindings/usb_cdc/Serial.c b/shared-bindings/usb_cdc/Serial.c index 9fe6cafd5f..f7da2a8881 100644 --- a/shared-bindings/usb_cdc/Serial.c +++ b/shared-bindings/usb_cdc/Serial.c @@ -33,7 +33,6 @@ #include "py/objproperty.h" #include "py/runtime.h" #include "py/stream.h" -#include "supervisor/shared/translate/translate.h" //| class Serial: //| """Receives cdc commands over USB""" diff --git a/shared-bindings/usb_cdc/__init__.c b/shared-bindings/usb_cdc/__init__.c index e7a13a768a..8b3ca345bc 100644 --- a/shared-bindings/usb_cdc/__init__.c +++ b/shared-bindings/usb_cdc/__init__.c @@ -31,9 +31,6 @@ #include "shared-bindings/usb_cdc/__init__.h" #include "shared-bindings/usb_cdc/Serial.h" -#include "supervisor/shared/translate/translate.h" - -#include "py/runtime.h" //| """USB CDC Serial streams //| diff --git a/shared-bindings/usb_hid/Device.c b/shared-bindings/usb_hid/Device.c index 34b496a90e..d998788fd4 100644 --- a/shared-bindings/usb_hid/Device.c +++ b/shared-bindings/usb_hid/Device.c @@ -27,7 +27,6 @@ #include "py/objproperty.h" #include "shared-bindings/usb_hid/Device.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" //| class Device: //| """HID Device specification""" diff --git a/shared-bindings/usb_hid/__init__.c b/shared-bindings/usb_hid/__init__.c index 2f374084bb..412e0918e9 100644 --- a/shared-bindings/usb_hid/__init__.c +++ b/shared-bindings/usb_hid/__init__.c @@ -31,8 +31,6 @@ #include "shared-bindings/usb_hid/__init__.h" #include "shared-bindings/usb_hid/Device.h" -#include "supervisor/shared/translate/translate.h" - //| """USB Human Interface Device //| //| The `usb_hid` module allows you to output data as a HID device.""" diff --git a/shared-bindings/usb_midi/PortIn.c b/shared-bindings/usb_midi/PortIn.c index ca07c8695a..6510063e2f 100644 --- a/shared-bindings/usb_midi/PortIn.c +++ b/shared-bindings/usb_midi/PortIn.c @@ -33,7 +33,6 @@ #include "py/objproperty.h" #include "py/runtime.h" #include "py/stream.h" -#include "supervisor/shared/translate/translate.h" //| class PortIn: //| """Receives midi commands over USB""" diff --git a/shared-bindings/usb_midi/PortOut.c b/shared-bindings/usb_midi/PortOut.c index f1f61f948a..1ba1466e0f 100644 --- a/shared-bindings/usb_midi/PortOut.c +++ b/shared-bindings/usb_midi/PortOut.c @@ -33,7 +33,6 @@ #include "py/objproperty.h" #include "py/runtime.h" #include "py/stream.h" -#include "supervisor/shared/translate/translate.h" //| class PortOut: //| """Sends midi messages to a computer over USB""" diff --git a/shared-bindings/usb_midi/__init__.c b/shared-bindings/usb_midi/__init__.c index 651f4a51d1..3790ea501e 100644 --- a/shared-bindings/usb_midi/__init__.c +++ b/shared-bindings/usb_midi/__init__.c @@ -32,7 +32,6 @@ #include "shared-bindings/usb_midi/__init__.h" #include "shared-bindings/usb_midi/PortIn.h" #include "shared-bindings/usb_midi/PortOut.h" -#include "supervisor/shared/translate/translate.h" #include "py/runtime.h" diff --git a/shared-bindings/util.c b/shared-bindings/util.c index 7cb0d51176..4b882375d1 100644 --- a/shared-bindings/util.c +++ b/shared-bindings/util.c @@ -27,7 +27,6 @@ #include "py/runtime.h" #include "shared-bindings/util.h" -#include "supervisor/shared/translate/translate.h" // If so, deinit() has already been called on the object, so complain. void raise_deinited_error(void) { diff --git a/shared-bindings/vectorio/Circle.c b/shared-bindings/vectorio/Circle.c index 308e828a02..1334492317 100644 --- a/shared-bindings/vectorio/Circle.c +++ b/shared-bindings/vectorio/Circle.c @@ -8,7 +8,6 @@ #include "py/objproperty.h" #include "py/objtype.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" //| class Circle: //| def __init__( diff --git a/shared-bindings/vectorio/Polygon.c b/shared-bindings/vectorio/Polygon.c index 452631eddc..e4b0f43afe 100644 --- a/shared-bindings/vectorio/Polygon.c +++ b/shared-bindings/vectorio/Polygon.c @@ -9,7 +9,6 @@ #include "py/objproperty.h" #include "py/objtype.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" #define VECTORIO_POLYGON_DEBUG(...) (void)0 diff --git a/shared-bindings/vectorio/Rectangle.c b/shared-bindings/vectorio/Rectangle.c index 01453693f8..89e9be7d53 100644 --- a/shared-bindings/vectorio/Rectangle.c +++ b/shared-bindings/vectorio/Rectangle.c @@ -7,7 +7,6 @@ #include "py/objtype.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" //| class Rectangle: //| def __init__( diff --git a/shared-bindings/vectorio/VectorShape.c b/shared-bindings/vectorio/VectorShape.c index 06a8355e2d..0adc386a07 100644 --- a/shared-bindings/vectorio/VectorShape.c +++ b/shared-bindings/vectorio/VectorShape.c @@ -1,4 +1,3 @@ - #include "shared-module/vectorio/__init__.h" #include "shared-bindings/vectorio/VectorShape.h" #include "shared-bindings/vectorio/Circle.h" @@ -16,7 +15,6 @@ #include "py/objproperty.h" #include "py/objtype.h" #include "py/runtime.h" -#include "supervisor/shared/translate/translate.h" // shape: The shape implementation to draw. diff --git a/shared-bindings/zlib/__init__.c b/shared-bindings/zlib/__init__.c index d0fd803d3a..0aba845ba9 100644 --- a/shared-bindings/zlib/__init__.c +++ b/shared-bindings/zlib/__init__.c @@ -39,8 +39,6 @@ #include "shared-bindings/zlib/__init__.h" -#include "supervisor/shared/translate/translate.h" - //| """zlib decompression functionality //| //| The `zlib` module allows limited functionality similar to the CPython zlib library. diff --git a/shared-module/audiocore/WaveFile.c b/shared-module/audiocore/WaveFile.c index 590148f5c3..021bb63449 100644 --- a/shared-module/audiocore/WaveFile.c +++ b/shared-module/audiocore/WaveFile.c @@ -33,7 +33,6 @@ #include "py/runtime.h" #include "shared-module/audiocore/WaveFile.h" -#include "supervisor/shared/translate/translate.h" struct wave_format_chunk { uint16_t audio_format; diff --git a/shared-module/audiomp3/MP3Decoder.c b/shared-module/audiomp3/MP3Decoder.c index 21385670a2..2049c6aa1d 100644 --- a/shared-module/audiomp3/MP3Decoder.c +++ b/shared-module/audiomp3/MP3Decoder.c @@ -35,7 +35,6 @@ #include "py/runtime.h" #include "shared-module/audiomp3/MP3Decoder.h" -#include "supervisor/shared/translate/translate.h" #include "supervisor/background_callback.h" #include "lib/mp3/src/mp3common.h" diff --git a/shared-module/bitbangio/I2C.c b/shared-module/bitbangio/I2C.c index 65926b8c07..4ee33fd8f5 100644 --- a/shared-module/bitbangio/I2C.c +++ b/shared-module/bitbangio/I2C.c @@ -32,7 +32,6 @@ #include "common-hal/microcontroller/Pin.h" #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/digitalio/DigitalInOut.h" -#include "supervisor/shared/translate/translate.h" STATIC void delay(bitbangio_i2c_obj_t *self) { // We need to use an accurate delay to get acceptable I2C diff --git a/shared-module/bitbangio/SPI.c b/shared-module/bitbangio/SPI.c index c82cecff62..60d216f619 100644 --- a/shared-module/bitbangio/SPI.c +++ b/shared-module/bitbangio/SPI.c @@ -32,7 +32,6 @@ #include "shared-bindings/bitbangio/SPI.h" #include "shared-bindings/digitalio/DigitalInOut.h" #include "shared-bindings/microcontroller/__init__.h" -#include "supervisor/shared/translate/translate.h" #define MAX_BAUDRATE (common_hal_mcu_get_clock_frequency() / 48) diff --git a/shared-module/board/__init__.c b/shared-module/board/__init__.c index 2d5a8a07e4..372f014239 100644 --- a/shared-module/board/__init__.c +++ b/shared-module/board/__init__.c @@ -27,7 +27,6 @@ #include "shared-bindings/board/__init__.h" #include "shared-bindings/microcontroller/Pin.h" #include "shared-module/board/__init__.h" -#include "supervisor/shared/translate/translate.h" #include "mpconfigboard.h" #include "py/runtime.h" diff --git a/shared-module/msgpack/__init__.c b/shared-module/msgpack/__init__.c index 27416a29fd..6425ae8496 100644 --- a/shared-module/msgpack/__init__.c +++ b/shared-module/msgpack/__init__.c @@ -36,7 +36,6 @@ #include "py/runtime.h" #include "py/stream.h" -#include "supervisor/shared/translate/translate.h" #include "shared-bindings/msgpack/ExtType.h" #include "shared-bindings/msgpack/__init__.h" #include "shared-module/msgpack/__init__.h" diff --git a/shared-module/storage/__init__.c b/shared-module/storage/__init__.c index 060972d0c3..e961f83e31 100644 --- a/shared-module/storage/__init__.c +++ b/shared-module/storage/__init__.c @@ -38,7 +38,6 @@ #include "shared-bindings/storage/__init__.h" #include "supervisor/filesystem.h" #include "supervisor/flash.h" -#include "supervisor/shared/translate/translate.h" #include "supervisor/usb.h" #if CIRCUITPY_USB_MSC diff --git a/shared-module/struct/__init__.c b/shared-module/struct/__init__.c index 11e122915c..6ed1ad6c41 100644 --- a/shared-module/struct/__init__.c +++ b/shared-module/struct/__init__.c @@ -31,7 +31,6 @@ #include "py/runtime.h" #include "py/binary.h" #include "py/parsenum.h" -#include "supervisor/shared/translate/translate.h" #include "shared-bindings/struct/__init__.h" STATIC void struct_validate_format(char fmt) { diff --git a/shared-module/usb_hid/Device.c b/shared-module/usb_hid/Device.c index 8691794955..00736906c4 100644 --- a/shared-module/usb_hid/Device.c +++ b/shared-module/usb_hid/Device.c @@ -32,7 +32,6 @@ #include "shared-bindings/usb_hid/Device.h" #include "shared-module/usb_hid/__init__.h" #include "shared-module/usb_hid/Device.h" -#include "supervisor/shared/translate/translate.h" #include "supervisor/shared/tick.h" #include "tusb.h" diff --git a/shared-module/usb_hid/__init__.c b/shared-module/usb_hid/__init__.c index 11b87a78f1..d962ff855a 100644 --- a/shared-module/usb_hid/__init__.c +++ b/shared-module/usb_hid/__init__.c @@ -33,7 +33,6 @@ #include "shared-bindings/usb_hid/__init__.h" #include "shared-bindings/usb_hid/Device.h" #include "supervisor/memory.h" -#include "supervisor/shared/translate/translate.h" #include "supervisor/usb.h" static const uint8_t usb_hid_descriptor_template[] = { diff --git a/shared-module/usb_midi/__init__.c b/shared-module/usb_midi/__init__.c index ed1b8a0518..8cac2ba8af 100644 --- a/shared-module/usb_midi/__init__.c +++ b/shared-module/usb_midi/__init__.c @@ -34,7 +34,6 @@ #include "shared-bindings/usb_midi/PortIn.h" #include "shared-bindings/usb_midi/PortOut.h" #include "supervisor/memory.h" -#include "supervisor/shared/translate/translate.h" #include "supervisor/usb.h" #include "tusb.h" diff --git a/shared/libc/abort_.c b/shared/libc/abort_.c index 0c1d860aef..3051eae81e 100644 --- a/shared/libc/abort_.c +++ b/shared/libc/abort_.c @@ -1,7 +1,5 @@ #include -#include "supervisor/shared/translate/translate.h" - NORETURN void abort_(void); NORETURN void abort_(void) { diff --git a/shared/libc/printf.c b/shared/libc/printf.c index dbf0c26744..e52ed523ef 100644 --- a/shared/libc/printf.c +++ b/shared/libc/printf.c @@ -60,13 +60,13 @@ int snprintf(char *str, size_t size, const char *fmt, ...); int printf(const char *fmt, ...) { va_list ap; va_start(ap, fmt); - int ret = mp_vprintf(&mp_plat_print, fmt, ap); + int ret = mp_vprintf(MICROPY_INTERNAL_PRINTF_PRINTER, fmt, ap); va_end(ap); return ret; } int vprintf(const char *fmt, va_list ap) { - return mp_vprintf(&mp_plat_print, fmt, ap); + return mp_vprintf(MICROPY_INTERNAL_PRINTF_PRINTER, fmt, ap); } // need this because gcc optimises printf("%c", c) -> putchar(c), and printf("a") -> putchar('a') diff --git a/shared/netutils/dhcpserver.c b/shared/netutils/dhcpserver.c index 3f90a06e46..c7b1d2b254 100644 --- a/shared/netutils/dhcpserver.c +++ b/shared/netutils/dhcpserver.c @@ -65,7 +65,7 @@ #define PORT_DHCP_SERVER (67) #define PORT_DHCP_CLIENT (68) -#define DEFAULT_DNS MAKE_IP4(8, 8, 8, 8) +#define DEFAULT_DNS MAKE_IP4(192, 168, 4, 1) #define DEFAULT_LEASE_TIME_S (24 * 60 * 60) // in seconds #define MAC_LEN (6) diff --git a/shared/netutils/netutils.c b/shared/netutils/netutils.c index 030b1535cd..ab2b592427 100644 --- a/shared/netutils/netutils.c +++ b/shared/netutils/netutils.c @@ -31,7 +31,6 @@ #include "py/runtime.h" #include "shared/netutils/netutils.h" -#include "supervisor/shared/translate/translate.h" // Takes an array with a raw IPv4 address and returns something like '192.168.0.1'. mp_obj_t netutils_format_ipv4_addr(uint8_t *ip, netutils_endian_t endian) { diff --git a/shared/netutils/netutils.h b/shared/netutils/netutils.h index 3bb9212800..dfc554bf8a 100644 --- a/shared/netutils/netutils.h +++ b/shared/netutils/netutils.h @@ -27,6 +27,8 @@ #ifndef MICROPY_INCLUDED_LIB_NETUTILS_NETUTILS_H #define MICROPY_INCLUDED_LIB_NETUTILS_NETUTILS_H +#include "py/obj.h" + #define NETUTILS_IPV4ADDR_BUFSIZE 4 #define NETUTILS_TRACE_IS_TX (0x0001) diff --git a/shared/readline/readline.c b/shared/readline/readline.c index 9e1e904417..db4653b82b 100644 --- a/shared/readline/readline.c +++ b/shared/readline/readline.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2013, 2014 Damien P. George * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -419,11 +419,6 @@ right_arrow_key: // right arrow if (rl.cursor_pos < rl.line->len) { redraw_step_forward = 1; - // Check if we have moved into a UTF-8 continuation byte - while (UTF8_IS_CONT(rl.line->buf[rl.cursor_pos+redraw_step_forward]) && - rl.cursor_pos+redraw_step_forward < rl.line->len) { - redraw_step_forward++; - } } } else if (c == 'D') { #if MICROPY_REPL_EMACS_KEYS @@ -432,11 +427,6 @@ left_arrow_key: // left arrow if (rl.cursor_pos > rl.orig_line_len) { redraw_step_back = 1; - // Check if we have moved into a UTF-8 continuation byte - while (UTF8_IS_CONT(rl.line->buf[rl.cursor_pos-redraw_step_back])) { - redraw_step_back++; - cont_chars++; - } } } else if (c == 'H') { // home @@ -637,10 +627,12 @@ void readline_push_history(const char *line) { // so update the history char *most_recent_hist = str_dup_maybe(line); if (most_recent_hist != NULL) { - for (int i = READLINE_HIST_SIZE - 1; i > 0; i--) { + for (int i = MICROPY_READLINE_HISTORY_SIZE - 1; i > 0; i--) { MP_STATE_PORT(readline_hist)[i] = MP_STATE_PORT(readline_hist)[i - 1]; } MP_STATE_PORT(readline_hist)[0] = most_recent_hist; } } } + +MP_REGISTER_ROOT_POINTER(const char *readline_hist[MICROPY_READLINE_HISTORY_SIZE]); diff --git a/shared/runtime/gchelper_generic.c b/shared/runtime/gchelper_generic.c index dcd35f9c7e..272e37056a 100644 --- a/shared/runtime/gchelper_generic.c +++ b/shared/runtime/gchelper_generic.c @@ -98,7 +98,7 @@ STATIC void gc_helper_get_regs(gc_helper_regs_t arr) { #elif defined(__thumb2__) || defined(__thumb__) || defined(__arm__) -// Fallback implementation, prefer gchelper_m0.s or gchelper_m3.s +// Fallback implementation, prefer gchelper_thumb1.s or gchelper_thumb2.s STATIC void gc_helper_get_regs(gc_helper_regs_t arr) { register long r4 asm ("r4"); diff --git a/shared/runtime/gchelper_native.c b/shared/runtime/gchelper_native.c index 1e4af9c844..dbab8d6017 100644 --- a/shared/runtime/gchelper_native.c +++ b/shared/runtime/gchelper_native.c @@ -41,7 +41,7 @@ MP_NOINLINE void gc_helper_collect_regs_and_stack(void) { uintptr_t sp = gc_helper_get_regs_and_sp(regs); // trace the stack, including the registers (since they live on the stack in this function) - gc_collect_root((void **)sp, ((uint32_t)MP_STATE_THREAD(stack_top) - sp) / sizeof(uint32_t)); + gc_collect_root((void **)sp, ((uintptr_t)MP_STATE_THREAD(stack_top) - sp) / sizeof(uintptr_t)); } #endif diff --git a/shared/runtime/gchelper_m0.s b/shared/runtime/gchelper_thumb1.s similarity index 94% rename from shared/runtime/gchelper_m0.s rename to shared/runtime/gchelper_thumb1.s index db0d9738d1..a316c4fb36 100644 --- a/shared/runtime/gchelper_m0.s +++ b/shared/runtime/gchelper_thumb1.s @@ -25,7 +25,6 @@ */ .syntax unified - .cpu cortex-m0 .thumb .section .text @@ -34,6 +33,9 @@ .global gc_helper_get_regs_and_sp .type gc_helper_get_regs_and_sp, %function +@ This function will compile on processors like Cortex M0 that don't support +@ newer Thumb-2 instructions. + @ uint gc_helper_get_regs_and_sp(r0=uint regs[10]) gc_helper_get_regs_and_sp: @ store registers into given array diff --git a/shared/runtime/gchelper_m3.s b/shared/runtime/gchelper_thumb2.s similarity index 96% rename from shared/runtime/gchelper_m3.s rename to shared/runtime/gchelper_thumb2.s index 5220fa0883..bbc98459ec 100644 --- a/shared/runtime/gchelper_m3.s +++ b/shared/runtime/gchelper_thumb2.s @@ -25,7 +25,6 @@ */ .syntax unified - .cpu cortex-m3 .thumb .section .text @@ -34,6 +33,8 @@ .global gc_helper_get_regs_and_sp .type gc_helper_get_regs_and_sp, %function +@ This function requires Thumb-2 instruction support, e.g. Cortex M3/M4. + @ uint gc_helper_get_regs_and_sp(r0=uint regs[10]) gc_helper_get_regs_and_sp: @ store registers into given array diff --git a/shared/runtime/mpirq.c b/shared/runtime/mpirq.c index 8e474bf5a2..acd727403b 100644 --- a/shared/runtime/mpirq.c +++ b/shared/runtime/mpirq.c @@ -81,8 +81,8 @@ void mp_irq_handler(mp_irq_obj_t *self) { // Uncaught exception; disable the callback so that it doesn't run again self->methods->trigger(self->parent, 0); self->handler = mp_const_none; - printf("Uncaught exception in IRQ callback handler\n"); - mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val)); + mp_printf(MICROPY_ERROR_PRINTER, "Uncaught exception in IRQ callback handler\n"); + mp_obj_print_exception(MICROPY_ERROR_PRINTER, MP_OBJ_FROM_PTR(nlr.ret_val)); } gc_unlock(); mp_sched_unlock(); @@ -125,11 +125,12 @@ STATIC const mp_rom_map_elem_t mp_irq_locals_dict_table[] = { }; STATIC MP_DEFINE_CONST_DICT(mp_irq_locals_dict, mp_irq_locals_dict_table); -const mp_obj_type_t mp_irq_type = { - { &mp_type_type }, - .name = MP_QSTR_irq, - .call = mp_irq_call, - .locals_dict = (mp_obj_dict_t *)&mp_irq_locals_dict, -}; +MP_DEFINE_CONST_OBJ_TYPE( + mp_irq_type, + MP_QSTR_irq, + MP_TYPE_FLAG_NONE, + call, mp_irq_call, + locals_dict, &mp_irq_locals_dict + ); #endif // MICROPY_ENABLE_SCHEDULER diff --git a/shared/runtime/pyexec.c b/shared/runtime/pyexec.c index 0717bfc6a8..944a3f5895 100644 --- a/shared/runtime/pyexec.c +++ b/shared/runtime/pyexec.c @@ -639,7 +639,7 @@ friendly_repl_reset: // If the user gets to here and interrupts are disabled then // they'll never see the prompt, traceback etc. The USB REPL needs // interrupts to be enabled or no transfers occur. So we try to - // do the user a favor and re-enable interrupts. + // do the user a favor and reenable interrupts. if (query_irq() == IRQ_STATE_DISABLED) { enable_irq(IRQ_STATE_ENABLED); mp_hal_stdout_tx_str("MPY: enabling IRQs\r\n"); diff --git a/shared/runtime/pyexec.h b/shared/runtime/pyexec.h index 411426eaaa..960bb95ff8 100644 --- a/shared/runtime/pyexec.h +++ b/shared/runtime/pyexec.h @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2013, 2014 Damien P. George * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/shared/runtime/sys_stdio_mphal.c b/shared/runtime/sys_stdio_mphal.c index 580d97f614..236c6f3eb3 100644 --- a/shared/runtime/sys_stdio_mphal.c +++ b/shared/runtime/sys_stdio_mphal.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * SPDX-FileCopyrightText: Copyright (c) 2013-2019 Damien P. George + * Copyright (c) 2013-2019 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 @@ -120,26 +120,20 @@ STATIC const mp_rom_map_elem_t stdio_locals_dict_table[] = { STATIC MP_DEFINE_CONST_DICT(stdio_locals_dict, stdio_locals_dict_table); STATIC const mp_stream_p_t stdio_obj_stream_p = { - MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream) .read = stdio_read, .write = stdio_write, .ioctl = stdio_ioctl, .is_text = true, }; -STATIC const mp_obj_type_t stdio_obj_type = { - { &mp_type_type }, - .flags = MP_TYPE_FLAG_EXTENDED, - .name = MP_QSTR_FileIO, - // TODO .make_new? - .print = stdio_obj_print, - .locals_dict = (mp_obj_dict_t *)&stdio_locals_dict, - MP_TYPE_EXTENDED_FIELDS( - .getiter = mp_identity_getiter, - .iternext = mp_stream_unbuffered_iter, - .protocol = &stdio_obj_stream_p, - ), -}; +MP_DEFINE_CONST_OBJ_TYPE( + stdio_obj_type, + MP_QSTR_FileIO, + MP_TYPE_FLAG_ITER_IS_STREAM, + print, stdio_obj_print, + protocol, &stdio_obj_stream_p, + locals_dict, &stdio_locals_dict + ); const sys_stdio_obj_t mp_sys_stdin_obj = {{&stdio_obj_type}, .fd = STDIO_FD_IN}; const sys_stdio_obj_t mp_sys_stdout_obj = {{&stdio_obj_type}, .fd = STDIO_FD_OUT}; @@ -159,25 +153,20 @@ STATIC mp_uint_t stdio_buffer_write(mp_obj_t self_in, const void *buf, mp_uint_t } STATIC const mp_stream_p_t stdio_buffer_obj_stream_p = { - MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream) .read = stdio_buffer_read, .write = stdio_buffer_write, .ioctl = stdio_ioctl, .is_text = false, }; -STATIC const mp_obj_type_t stdio_buffer_obj_type = { - { &mp_type_type }, - .name = MP_QSTR_FileIO, - .flags = MP_TYPE_FLAG_EXTENDED, - .print = stdio_obj_print, - .locals_dict = (mp_obj_dict_t *)&stdio_locals_dict, - MP_TYPE_EXTENDED_FIELDS( - .getiter = mp_identity_getiter, - .iternext = mp_stream_unbuffered_iter, - .protocol = &stdio_buffer_obj_stream_p, - ), -}; +STATIC MP_DEFINE_CONST_OBJ_TYPE( + stdio_buffer_obj_type, + MP_QSTR_FileIO, + MP_TYPE_FLAG_ITER_IS_STREAM, + print, stdio_obj_print, + protocol, &stdio_buffer_obj_stream_p, + locals_dict, &stdio_locals_dict + ); STATIC const sys_stdio_obj_t stdio_buffer_obj = {{&stdio_buffer_obj_type}, .fd = 0}; // fd unused #endif diff --git a/ports/unix/variants/dev/mpconfigvariant.h b/shared/tinyusb/mp_cdc_common.c similarity index 57% rename from ports/unix/variants/dev/mpconfigvariant.h rename to shared/tinyusb/mp_cdc_common.c index 61092394cd..cd4f5d1013 100644 --- a/ports/unix/variants/dev/mpconfigvariant.h +++ b/shared/tinyusb/mp_cdc_common.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2019 Damien P. George + * Copyright (c) 2022 Ibrahim Abdelkader * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,16 +24,31 @@ * THE SOFTWARE. */ -// Set base feature level. -#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES) +#include "py/runtime.h" +#include "py/mphal.h" +#include "modmachine.h" -// Disable some features that come enabled by default with the feature level. -#define MICROPY_PY_BUILTINS_EXECFILE (0) -#define MICROPY_PY_SYS_STDIO_BUFFER (0) -#define MICROPY_PY_USELECT (0) +#if MICROPY_HW_USB_CDC_1200BPS_TOUCH && MICROPY_HW_ENABLE_USBDEV -// Enable some additional features. -#define MICROPY_REPL_EMACS_WORDS_MOVE (1) -#define MICROPY_REPL_EMACS_EXTRA_WORDS_MOVE (1) -#define MICROPY_PY_SYS_SETTRACE (1) -#define MICROPY_PY_URANDOM_SEED_INIT_FUNC (mp_urandom_seed_init()) +#include "tusb.h" + +static mp_sched_node_t mp_bootloader_sched_node; + +STATIC void usbd_cdc_run_bootloader_task(mp_sched_node_t *node) { + mp_hal_delay_ms(250); + machine_bootloader(0, NULL); +} + +void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) { + if (dtr == false && rts == false) { + // Device is disconnected. + cdc_line_coding_t line_coding; + tud_cdc_n_get_line_coding(itf, &line_coding); + if (line_coding.bit_rate == 1200) { + // Delay bootloader jump to allow the USB stack to service endpoints. + mp_sched_schedule_node(&mp_bootloader_sched_node, usbd_cdc_run_bootloader_task); + } + } +} + +#endif diff --git a/shared/tinyusb/mp_usbd_descriptor.c b/shared/tinyusb/mp_usbd_descriptor.c new file mode 100644 index 0000000000..8fab599b67 --- /dev/null +++ b/shared/tinyusb/mp_usbd_descriptor.c @@ -0,0 +1,133 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Damien P. George + * Copyright (c) 2022 Blake W. Felt & Angus Gratton + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/mpconfig.h" + +#if MICROPY_HW_ENABLE_USBDEV + +#include "tusb.h" +#include "mp_usbd.h" +#include "mp_usbd_internal.h" + +#define USBD_CDC_CMD_MAX_SIZE (8) +#define USBD_CDC_IN_OUT_MAX_SIZE (64) + +const tusb_desc_device_t mp_usbd_desc_device_static = { + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = 0x0200, + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + .idVendor = MICROPY_HW_USB_VID, + .idProduct = MICROPY_HW_USB_PID, + .bcdDevice = 0x0100, + .iManufacturer = USBD_STR_MANUF, + .iProduct = USBD_STR_PRODUCT, + .iSerialNumber = USBD_STR_SERIAL, + .bNumConfigurations = 1, +}; + +const uint8_t mp_usbd_desc_cfg_static[USBD_STATIC_DESC_LEN] = { + TUD_CONFIG_DESCRIPTOR(1, USBD_ITF_STATIC_MAX, USBD_STR_0, USBD_STATIC_DESC_LEN, + 0, USBD_MAX_POWER_MA), + + #if CFG_TUD_CDC + TUD_CDC_DESCRIPTOR(USBD_ITF_CDC, USBD_STR_CDC, USBD_CDC_EP_CMD, + USBD_CDC_CMD_MAX_SIZE, USBD_CDC_EP_OUT, USBD_CDC_EP_IN, USBD_CDC_IN_OUT_MAX_SIZE), + #endif + #if CFG_TUD_MSC + TUD_MSC_DESCRIPTOR(USBD_ITF_MSC, 5, EPNUM_MSC_OUT, EPNUM_MSC_IN, 64), + #endif +}; + +const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { + char serial_buf[MICROPY_HW_USB_DESC_STR_MAX + 1]; // Includes terminating NUL byte + static uint16_t desc_wstr[MICROPY_HW_USB_DESC_STR_MAX + 1]; // Includes prefix uint16_t + const char *desc_str; + uint16_t desc_len; + + switch (index) { + case 0: + desc_wstr[1] = 0x0409; // supported language is English + desc_len = 4; + break; + case USBD_STR_SERIAL: + // TODO: make a port-specific serial number callback + mp_usbd_port_get_serial_number(serial_buf); + desc_str = serial_buf; + break; + case USBD_STR_MANUF: + desc_str = MICROPY_HW_USB_MANUFACTURER_STRING; + break; + case USBD_STR_PRODUCT: + desc_str = MICROPY_HW_USB_PRODUCT_FS_STRING; + break; + #if CFG_TUD_CDC + case USBD_STR_CDC: + desc_str = MICROPY_HW_USB_CDC_INTERFACE_STRING; + break; + #endif + #if CFG_TUD_MSC + case USBD_STR_MSC: + desc_str = MICROPY_HW_USB_MSC_INTERFACE_STRING; + break; + #endif + default: + desc_str = NULL; + } + + if (index != 0) { + if (desc_str == NULL) { + return NULL; // Will STALL the endpoint + } + + // Convert from narrow string to wide string + desc_len = 2; + for (int i = 0; i < MICROPY_HW_USB_DESC_STR_MAX && desc_str[i] != 0; i++) { + desc_wstr[1 + i] = desc_str[i]; + desc_len += 2; + } + } + // first byte is length (including header), second byte is string type + desc_wstr[0] = (TUSB_DESC_STRING << 8) | desc_len; + + return desc_wstr; +} + + +const uint8_t *tud_descriptor_device_cb(void) { + return (const void *)&mp_usbd_desc_device_static; +} + +const uint8_t *tud_descriptor_configuration_cb(uint8_t index) { + (void)index; + return mp_usbd_desc_cfg_static; +} + +#endif diff --git a/ports/unix/variants/freedos/mpconfigvariant.h b/shared/tinyusb/mp_usbd_internal.h similarity index 74% rename from ports/unix/variants/freedos/mpconfigvariant.h rename to shared/tinyusb/mp_usbd_internal.h index 562c783ca3..8b8f50bae8 100644 --- a/ports/unix/variants/freedos/mpconfigvariant.h +++ b/shared/tinyusb/mp_usbd_internal.h @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2015 Damien P. George + * Copyright (c) 2022 Angus Gratton * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,16 +23,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#ifndef MICROPY_INCLUDED_SHARED_TINYUSB_MP_USBD_INTERNAL_H +#define MICROPY_INCLUDED_SHARED_TINYUSB_MP_USBD_INTERNAL_H +#include "tusb.h" -// options to control how MicroPython is built +// Static USB device descriptor values +extern const tusb_desc_device_t mp_usbd_desc_device_static; +extern const uint8_t mp_usbd_desc_cfg_static[USBD_STATIC_DESC_LEN]; -#define MICROPY_PY_USELECT_POSIX (0) - -#define MICROPY_STREAMS_NON_BLOCK (0) - -#define MICROPY_PY_SYS_PLATFORM "freedos" - -// djgpp dirent struct does not have d_ino field -#undef _DIRENT_HAVE_D_INO - -#define MICROPY_USE_INTERNAL_ERRNO (1) +#endif // MICROPY_INCLUDED_SHARED_TINYUSB_MP_USBD_INTERNAL_H diff --git a/shared/tinyusb/tusb_config.h b/shared/tinyusb/tusb_config.h new file mode 100644 index 0000000000..28bee09a59 --- /dev/null +++ b/shared/tinyusb/tusb_config.h @@ -0,0 +1,132 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * Copyright (c) 2022 Angus Gratton + * + * 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_SHARED_TINYUSB_TUSB_CONFIG_H +#define MICROPY_INCLUDED_SHARED_TINYUSB_TUSB_CONFIG_H + +#include "py/mpconfig.h" + +#if MICROPY_HW_ENABLE_USBDEV + +#ifndef MICROPY_HW_USB_MANUFACTURER_STRING +#define MICROPY_HW_USB_MANUFACTURER_STRING "MicroPython" +#endif + +#ifndef MICROPY_HW_USB_PRODUCT_FS_STRING +#define MICROPY_HW_USB_PRODUCT_FS_STRING "Board in FS mode" +#endif + +#ifndef MICROPY_HW_USB_CDC_INTERFACE_STRING +#define MICROPY_HW_USB_CDC_INTERFACE_STRING "Board CDC" +#endif + +#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE) + +#if MICROPY_HW_USB_CDC +#define CFG_TUD_CDC (1) +#else +#define CFG_TUD_CDC (0) +#endif + +#if MICROPY_HW_USB_MSC +#define CFG_TUD_MSC (1) +#else +#define CFG_TUD_MSC (0) +#endif + +// CDC Configuration +#if CFG_TUD_CDC +#define CFG_TUD_CDC_RX_BUFSIZE (256) +#define CFG_TUD_CDC_TX_BUFSIZE (256) +#endif + +// MSC Configuration +#if CFG_TUD_MSC +#ifndef MICROPY_HW_USB_MSC_INTERFACE_STRING +#define MICROPY_HW_USB_MSC_INTERFACE_STRING "Board MSC" +#endif +// Set MSC EP buffer size to FatFS block size to avoid partial read/writes (offset arg). +#define CFG_TUD_MSC_BUFSIZE (MICROPY_FATFS_MAX_SS) +#endif + +// Define static descriptor size and interface count based on the above config + +#define USBD_STATIC_DESC_LEN (TUD_CONFIG_DESC_LEN + \ + (CFG_TUD_CDC ? (TUD_CDC_DESC_LEN) : 0) + \ + (CFG_TUD_MSC ? (TUD_MSC_DESC_LEN) : 0) \ + ) + +#define USBD_STR_0 (0x00) +#define USBD_STR_MANUF (0x01) +#define USBD_STR_PRODUCT (0x02) +#define USBD_STR_SERIAL (0x03) +#define USBD_STR_CDC (0x04) +#define USBD_STR_MSC (0x05) + +#define USBD_MAX_POWER_MA (250) + +#ifndef MICROPY_HW_USB_DESC_STR_MAX +#define MICROPY_HW_USB_DESC_STR_MAX (20) +#endif + +#if CFG_TUD_CDC +#define USBD_ITF_CDC (0) // needs 2 interfaces +#define USBD_CDC_EP_CMD (0x81) +#define USBD_CDC_EP_OUT (0x02) +#define USBD_CDC_EP_IN (0x82) +#endif // CFG_TUD_CDC + +#if CFG_TUD_MSC +// Interface & Endpoint numbers for MSC come after CDC, if it is enabled +#if CFG_TUD_CDC +#define USBD_ITF_MSC (2) +#define EPNUM_MSC_OUT (0x03) +#define EPNUM_MSC_IN (0x83) +#else +#define USBD_ITF_MSC (0) +#define EPNUM_MSC_OUT (0x01) +#define EPNUM_MSC_IN (0x81) +#endif // CFG_TUD_CDC +#endif // CFG_TUD_MSC + +/* Limits of statically defined USB interfaces, endpoints, strings */ +#if CFG_TUD_MSC +#define USBD_ITF_STATIC_MAX (USBD_ITF_MSC + 1) +#define USBD_STR_STATIC_MAX (USBD_STR_MSC + 1) +#define USBD_EP_STATIC_MAX (EPNUM_MSC_OUT + 1) +#elif CFG_TUD_CDC +#define USBD_ITF_STATIC_MAX (USBD_ITF_CDC + 2) +#define USBD_STR_STATIC_MAX (USBD_STR_CDC + 1) +#define USBD_EP_STATIC_MAX (((EPNUM_CDC_EP_IN)&~TUSB_DIR_IN_MASK) + 1) +#else // !CFG_TUD_MSC && !CFG_TUD_CDC +#define USBD_ITF_STATIC_MAX (0) +#define USBD_STR_STATIC_MAX (0) +#define USBD_EP_STATIC_MAX (0) +#endif + +#endif // MICROPY_HW_ENABLE_USBDEV + +#endif // MICROPY_INCLUDED_SHARED_TINYUSB_TUSB_CONFIG_H diff --git a/supervisor/shared/tick.c b/supervisor/shared/tick.c index b91257c8c8..7c35bc2f9b 100644 --- a/supervisor/shared/tick.c +++ b/supervisor/shared/tick.c @@ -30,7 +30,6 @@ #include "py/mphal.h" #include "py/mpstate.h" #include "py/runtime.h" -#include "supervisor/linker.h" #include "supervisor/filesystem.h" #include "supervisor/background_callback.h" #include "supervisor/port.h" diff --git a/tests/README b/tests/README deleted file mode 100644 index 8794202e15..0000000000 --- a/tests/README +++ /dev/null @@ -1,27 +0,0 @@ -This directory contains tests for various functionality areas of MicroPython. -To run all stable tests, run "run-tests.py" script in this directory. - -Tests of capabilities not supported on all platforms should be written -to check for the capability being present. If it is not, the test -should merely output 'SKIP' followed by the line terminator, and call -sys.exit() to raise SystemExit, instead of attempting to test the -missing capability. The testing framework (run-tests.py in this -directory, test_main.c in qemu_arm) recognizes this as a skipped test. - -There are a few features for which this mechanism cannot be used to -condition a test. The run-tests.py script uses small scripts in the -feature_check directory to check whether each such feature is present, -and skips the relevant tests if not. - -Tests are generally verified by running the test both in MicroPython and -in CPython and comparing the outputs. If the output differs the test fails -and the outputs are saved in a .out and a .exp file respectively. -For tests that cannot be run in CPython, for example because they use -the machine module, a .exp file can be provided next to the test's .py -file. A convenient way to generate that is to run the test, let it fail -(because CPython cannot run it) and then copy the .out file (but not -before checking it manually!) - -When creating new tests, anything that relies on float support should go in the -float/ subdirectory. Anything that relies on import x, where x is not a built-in -module, should go in the import/ subdirectory. diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000000..b0bff4872e --- /dev/null +++ b/tests/README.md @@ -0,0 +1,149 @@ +# MicroPython Test Suite + +This directory contains tests for various functionality areas of MicroPython. +To run all stable tests, run "run-tests.py" script in this directory. + +Tests of capabilities not supported on all platforms should be written +to check for the capability being present. If it is not, the test +should merely output 'SKIP' followed by the line terminator, and call +sys.exit() to raise SystemExit, instead of attempting to test the +missing capability. The testing framework (run-tests.py in this +directory, test_main.c in qemu_arm) recognizes this as a skipped test. + +There are a few features for which this mechanism cannot be used to +condition a test. The run-tests.py script uses small scripts in the +feature_check directory to check whether each such feature is present, +and skips the relevant tests if not. + +Tests are generally verified by running the test both in MicroPython and +in CPython and comparing the outputs. If the output differs the test fails +and the outputs are saved in a .out and a .exp file respectively. +For tests that cannot be run in CPython, for example because they use +the machine module, a .exp file can be provided next to the test's .py +file. A convenient way to generate that is to run the test, let it fail +(because CPython cannot run it) and then copy the .out file (but not +before checking it manually!) + +When creating new tests, anything that relies on float support should go in the +float/ subdirectory. Anything that relies on import x, where x is not a built-in +module, should go in the import/ subdirectory. + +## perf_bench + +The `perf_bench` directory contains some performance benchmarks that can be used +to benchmark different MicroPython firmwares or host ports. + +The runner utility is `run-perfbench,py`. Execute `./run-perfbench.py --help` +for a full list of command line options. + +### Benchmarking a target + +To run tests on a firmware target using `pyboard.py`, run the command line like +this: + +``` +./run-perfbench.py -p -d /dev/ttyACM0 168 100 +``` + +* `-p` indicates running on a remote target via pyboard.py, not the host. +* `-d PORTNAME` is the serial port, `/dev/ttyACM0` is the default if not + provided. +* `168` is value `N`, the approximate CPU frequency in MHz (in this case Pyboard + V1.1 is 168MHz). It's possible to choose other values as well: lower values + like `10` will run much the tests much quicker, higher values like `1000` will + run much longer. +* `100` is value `M`, the approximate heap size in kilobytes (can get this from + `import micropython; micropython.mem_info()` or estimate it). It's possible to + choose other values here too: lower values like `10` will run shorter/smaller + tests, and higher values will run bigger tests. The maximum value of `M` is + limited by available heap, and the tests are written so the "recommended" + value is approximately the upper limit. + +### Benchmarking the host + +To benchmark the host build (unix/Windows), run like this: + +``` +./run-perfbench.py 2000 10000 +``` + +The output of perfbench is a list of tests and times/scores, like this: + +``` +N=2000 M=10000 n_average=8 +perf_bench/bm_chaos.py: SKIP +perf_bench/bm_fannkuch.py: 94550.38 2.9145 84.68 2.8499 +perf_bench/bm_fft.py: 79920.38 10.0771 129269.74 8.8205 +perf_bench/bm_float.py: 43844.62 17.8229 353219.64 17.7693 +perf_bench/bm_hexiom.py: 32959.12 15.0243 775.77 14.8893 +perf_bench/bm_nqueens.py: 40855.00 10.7297 247776.15 11.3647 +perf_bench/bm_pidigits.py: 64547.75 2.5609 7751.36 2.5996 +perf_bench/core_import_mpy_multi.py: 15433.38 14.2733 33065.45 14.2368 +perf_bench/core_import_mpy_single.py: 263.00 11.3910 3858.35 12.9021 +perf_bench/core_qstr.py: 4929.12 1.8434 8117.71 1.7921 +perf_bench/core_yield_from.py: 16274.25 6.2584 12334.13 5.8125 +perf_bench/misc_aes.py: 57425.25 5.5226 17888.60 5.7482 +perf_bench/misc_mandel.py: 40809.25 8.2007 158107.00 9.8864 +perf_bench/misc_pystone.py: 39821.75 6.4145 100867.62 6.5043 +perf_bench/misc_raytrace.py: 36293.75 6.8501 26906.93 6.8402 +perf_bench/viper_call0.py: 15573.00 14.9931 19644.99 13.1550 +perf_bench/viper_call1a.py: 16725.75 9.8205 18099.96 9.2752 +perf_bench/viper_call1b.py: 20752.62 8.3372 14565.60 9.0663 +perf_bench/viper_call1c.py: 20849.88 5.8783 14444.80 6.6295 +perf_bench/viper_call2a.py: 16156.25 11.2956 18818.59 11.7959 +perf_bench/viper_call2b.py: 22047.38 8.9484 13725.73 9.6800 +``` + +The numbers across each line are times and scores for the test: + +* Runtime average (microseconds, lower is better) +* Runtime standard deviation as a percentage +* Score average (units depend on the benchmark, higher is better) +* Score standard deviation as a percentage + +### Comparing performance + +Usually you want to know if something is faster or slower than a reference. To +do this, copy the output of each `run-perfbench.py` run to a text file. + +This can be done multiple ways, but one way on Linux/macOS is with the `tee` +utility: `./run-perfbench.py -p 168 100 | tee pyb-run1.txt` + +Once you have two files with output from two different runs (maybe with +different code or configuration), compare the runtimes with `./run-perfbench.py +-t pybv-run1.txt pybv-run2.txt` or compare scores with `./run-perfbench.py -s +pybv-run1.txt pybv-run2.txt`: + +``` +> ./run-perfbench.py -s pyb-run1.txt pyb-run2.txt +diff of scores (higher is better) +N=168 M=100 pyb-run1.txt -> pyb-run2.txt diff diff% (error%) +bm_chaos.py 352.90 -> 352.63 : -0.27 = -0.077% (+/-0.00%) +bm_fannkuch.py 77.52 -> 77.45 : -0.07 = -0.090% (+/-0.01%) +bm_fft.py 2516.80 -> 2519.74 : +2.94 = +0.117% (+/-0.00%) +bm_float.py 5749.27 -> 5749.65 : +0.38 = +0.007% (+/-0.00%) +bm_hexiom.py 42.22 -> 42.30 : +0.08 = +0.189% (+/-0.00%) +bm_nqueens.py 4407.55 -> 4414.44 : +6.89 = +0.156% (+/-0.00%) +bm_pidigits.py 638.09 -> 632.14 : -5.95 = -0.932% (+/-0.25%) +core_import_mpy_multi.py 477.74 -> 477.57 : -0.17 = -0.036% (+/-0.00%) +core_import_mpy_single.py 58.74 -> 58.72 : -0.02 = -0.034% (+/-0.00%) +core_qstr.py 63.11 -> 63.11 : +0.00 = +0.000% (+/-0.01%) +core_yield_from.py 357.57 -> 357.57 : +0.00 = +0.000% (+/-0.00%) +misc_aes.py 397.27 -> 396.47 : -0.80 = -0.201% (+/-0.00%) +misc_mandel.py 3375.70 -> 3375.84 : +0.14 = +0.004% (+/-0.00%) +misc_pystone.py 2265.36 -> 2265.97 : +0.61 = +0.027% (+/-0.01%) +misc_raytrace.py 367.61 -> 368.15 : +0.54 = +0.147% (+/-0.01%) +viper_call0.py 605.92 -> 605.92 : +0.00 = +0.000% (+/-0.00%) +viper_call1a.py 576.78 -> 576.78 : +0.00 = +0.000% (+/-0.00%) +viper_call1b.py 452.45 -> 452.46 : +0.01 = +0.002% (+/-0.01%) +viper_call1c.py 457.39 -> 457.39 : +0.00 = +0.000% (+/-0.00%) +viper_call2a.py 561.37 -> 561.37 : +0.00 = +0.000% (+/-0.00%) +viper_call2b.py 389.49 -> 389.50 : +0.01 = +0.003% (+/-0.01%) +``` + +Note in particular the error percentages at the end of each line. If these are +high relative to the percentage difference then it indicates high variability in +the test runs, and the absolute difference value is unreliable. High error +percentages are particularly common on PC builds, where the host OS may +influence test run times. Increasing the `N` value may help average this out by +running each test longer. diff --git a/tests/basics/assign_expr.py b/tests/basics/assign_expr.py index f243905dc2..2cf604193b 100644 --- a/tests/basics/assign_expr.py +++ b/tests/basics/assign_expr.py @@ -12,6 +12,17 @@ x = 1 print(x, x := 5, x) print(x) +# Test "while" with assignment expression as conditional, assigning to a new local. +# The while conditional is compiled after the while body, so this tests how the +# compiler handles the case of an unbound local being compiled before it is assigned. +def f(): + l = [0, 1] + while local := len(l): + print(local, l.pop()) + + +f() + def foo(): print("any", any((hit := i) % 5 == 3 and (hit % 2) == 0 for i in range(10))) diff --git a/tests/basics/assign_expr.py.exp b/tests/basics/assign_expr.py.exp index e38e1ae7a6..47da56b80d 100644 --- a/tests/basics/assign_expr.py.exp +++ b/tests/basics/assign_expr.py.exp @@ -5,6 +5,8 @@ True 5 1 5 5 5 +2 1 +1 0 any True 8 123 diff --git a/tests/basics/assign_expr_scope.py b/tests/basics/assign_expr_scope.py new file mode 100644 index 0000000000..69dc9f0d17 --- /dev/null +++ b/tests/basics/assign_expr_scope.py @@ -0,0 +1,81 @@ +# Test scoping rules for assignment expression :=. + +# Test that var is closed over (assigned to in the scope of scope0). +def scope0(): + any((var0 := i) for i in range(2)) + return var0 + + +print("scope0") +print(scope0()) +print(globals().get("var0", None)) + +# Test that var1 gets closed over correctly in the list comprehension. +def scope1(): + var1 = 0 + dummy1 = 1 + dummy2 = 1 + print([var1 := i for i in [0, 1] if i > var1]) + print(var1) + + +print("scope1") +scope1() +print(globals().get("var1", None)) + +# Test that var2 in the comprehension honours the global declaration. +def scope2(): + global var2 + print([var2 := i for i in range(2)]) + print(globals().get("var2", None)) + + +print("scope2") +scope2() +print(globals().get("var2", None)) + +# Test that var1 in the comprehension remains local to inner1. +def scope3(): + global var3 + + def inner3(): + print([var3 := i for i in range(2)]) + + inner3() + print(globals().get("var3", None)) + + +print("scope3") +scope3() +print(globals().get("var3", None)) + +# Test that var4 in the comprehension honours the global declarations. +def scope4(): + global var4 + + def inner4(): + global var4 + print([var4 := i for i in range(2)]) + + inner4() + print(var4) + + +print("scope4") +scope4() +print(globals().get("var4", None)) + +# Test that var5 in the comprehension honours the nonlocal declaration. +def scope5(): + def inner5(): + nonlocal var5 + print([var5 := i for i in range(2)]) + + inner5() + print(var5) + var5 = 0 # force var5 to be a local to scope5 + + +print("scope5") +scope5() +print(globals().get("var5", None)) diff --git a/tests/basics/assign_expr_scope.py.exp b/tests/basics/assign_expr_scope.py.exp new file mode 100644 index 0000000000..5c780b3822 --- /dev/null +++ b/tests/basics/assign_expr_scope.py.exp @@ -0,0 +1,23 @@ +scope0 +1 +None +scope1 +[1] +1 +None +scope2 +[0, 1] +1 +1 +scope3 +[0, 1] +None +None +scope4 +[0, 1] +1 +1 +scope5 +[0, 1] +1 +None diff --git a/tests/basics/assign_expr_syntaxerror.py b/tests/basics/assign_expr_syntaxerror.py index 11b3501292..0c334d075a 100644 --- a/tests/basics/assign_expr_syntaxerror.py +++ b/tests/basics/assign_expr_syntaxerror.py @@ -8,9 +8,9 @@ def test(code): test("x := 1") test("((x, y) := 1)") - -# these are currently all allowed in MicroPython, but not in CPython test("([i := i + 1 for i in range(4)])") test("([i := -1 for i, j in [(1, 2)]])") test("([[(i := j) for i in range(2)] for j in range(2)])") + +# this is currently allowed in MicroPython, but not in CPython test("([[(j := i) for i in range(2)] for j in range(2)])") diff --git a/tests/basics/assign_expr_syntaxerror.py.exp b/tests/basics/assign_expr_syntaxerror.py.exp index 2ba7d7df86..8b386b2a95 100644 --- a/tests/basics/assign_expr_syntaxerror.py.exp +++ b/tests/basics/assign_expr_syntaxerror.py.exp @@ -1,6 +1,6 @@ SyntaxError SyntaxError -[1, 2, 3, 4] -[-1] -[[0, 0], [1, 1]] +SyntaxError +SyntaxError +SyntaxError [[0, 1], [0, 1]] diff --git a/tests/basics/builtin_str_hex.py b/tests/basics/builtin_str_hex.py new file mode 100644 index 0000000000..7390c8eaee --- /dev/null +++ b/tests/basics/builtin_str_hex.py @@ -0,0 +1,24 @@ +if not hasattr(bytes, "fromhex"): + print("SKIP") + raise SystemExit + +for x in ( + b"\x00\x01\x02\x03\x04\x05\x06\x07", + b"\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + b"\x7f\x80\xff", + b"1234ABCDabcd", +): + print(x.hex()) + print(bytearray(x).hex()) + print(memoryview(x).hex()) + print(x.hex(":")) + print(bytearray(x).hex(":")) + print(memoryview(x).hex(":")) + +for x in ( + "0001020304050607", + "08090a0b0c0d0e0f", + "7f80ff", + "313233344142434461626364", +): + print(bytes.fromhex(x)) diff --git a/tests/basics/builtin_str_hex.py.exp b/tests/basics/builtin_str_hex.py.exp new file mode 100644 index 0000000000..990dd85707 --- /dev/null +++ b/tests/basics/builtin_str_hex.py.exp @@ -0,0 +1,28 @@ +0001020304050607 +0001020304050607 +0001020304050607 +00:01:02:03:04:05:06:07 +00:01:02:03:04:05:06:07 +00:01:02:03:04:05:06:07 +08090a0b0c0d0e0f +08090a0b0c0d0e0f +08090a0b0c0d0e0f +08:09:0a:0b:0c:0d:0e:0f +08:09:0a:0b:0c:0d:0e:0f +08:09:0a:0b:0c:0d:0e:0f +7f80ff +7f80ff +7f80ff +7f:80:ff +7f:80:ff +7f:80:ff +313233344142434461626364 +313233344142434461626364 +313233344142434461626364 +31:32:33:34:41:42:43:44:61:62:63:64 +31:32:33:34:41:42:43:44:61:62:63:64 +31:32:33:34:41:42:43:44:61:62:63:64 +b'\x00\x01\x02\x03\x04\x05\x06\x07' +b'\x08\t\n\x0b\x0c\r\x0e\x0f' +b'\x7f\x80\xff' +b'1234ABCDabcd' diff --git a/tests/basics/bytearray_byte_operations.py b/tests/basics/bytearray_byte_operations.py new file mode 100644 index 0000000000..7ce53cac53 --- /dev/null +++ b/tests/basics/bytearray_byte_operations.py @@ -0,0 +1,35 @@ +# test bytearray with its re-use of byte functions + +print(bytearray(b"hello world").find(b"ll")) +print(bytearray(b"hello\x00world").rfind(b"l")) + +print(bytearray(b"abc efg ").strip(b"g a")) +print(bytearray(b" spacious ").lstrip()) +print(bytearray(b"www.example.com").lstrip(b"cmowz.")) +print(bytearray(b" spacious ").rstrip()) +print(bytearray(b"mississippi").rstrip(b"ipz")) + +print(bytearray(b"abc").split(b"a")) +print(bytearray(b"abcabc").rsplit(b"bc")) + +print(bytearray(b"asdfasdf").replace(b"a", b"b")) + +print("00\x0000".index("0", 0)) +print("00\x0000".index("0", 3)) +print("00\x0000".rindex("0", 0)) +print("00\x0000".rindex("0", 3)) + +print(bytearray(b"foobar").endswith(b"bar")) +print(bytearray(b"1foo").startswith(b"foo", 1)) + +print(bytearray(b" T E \x00 S T").lower()) +print(bytearray(b" te \x00 st").upper()) + +print(bytearray(b" \t\n\r\v\f").isspace()) +print(bytearray(b"this ").isalpha()) +print(bytearray(b"0123456789").isdigit()) +print(bytearray(b"AB").isupper()) +print(bytearray(b"cheese-cake").islower()) + +print(bytearray(b",").join((bytearray(b"abc"), bytearray(b"def")))) +print(type(bytearray(b",").join((b"a", b"b", b"c")))) diff --git a/tests/basics/bytearray_center.py b/tests/basics/bytearray_center.py new file mode 100644 index 0000000000..c262a8c209 --- /dev/null +++ b/tests/basics/bytearray_center.py @@ -0,0 +1,8 @@ +try: + bytearray.center +except AttributeError: + print("SKIP") + raise SystemExit + +print(bytearray(b"foo").center(6)) +print(type(bytearray(b"foo").center(6))) diff --git a/tests/basics/bytearray_construct.py b/tests/basics/bytearray_construct.py index 75fdc41178..eb4d4e641f 100644 --- a/tests/basics/bytearray_construct.py +++ b/tests/basics/bytearray_construct.py @@ -5,3 +5,8 @@ print(bytearray('1234', 'utf-8')) print(bytearray('12345', 'utf-8', 'strict')) print(bytearray((1, 2))) print(bytearray([1, 2])) + +try: + print(bytearray('1234')) +except TypeError: + print("TypeError") diff --git a/tests/basics/bytearray_count.py b/tests/basics/bytearray_count.py new file mode 100644 index 0000000000..a151c1e818 --- /dev/null +++ b/tests/basics/bytearray_count.py @@ -0,0 +1,7 @@ +try: + bytearray.count +except AttributeError: + print("SKIP") + raise SystemExit + +print(bytearray(b"aaaa").count(b"a")) diff --git a/tests/basics/bytearray_partition.py b/tests/basics/bytearray_partition.py new file mode 100644 index 0000000000..b48ec4ed9b --- /dev/null +++ b/tests/basics/bytearray_partition.py @@ -0,0 +1,8 @@ +try: + bytearray.partition +except AttributeError: + print("SKIP") + raise SystemExit + +print(bytearray(b"asdsf").partition(b"s")) +print(bytearray(b"asdsf").rpartition(b"s")) diff --git a/tests/basics/bytes_center.py b/tests/basics/bytes_center.py new file mode 100644 index 0000000000..5d8b41d92f --- /dev/null +++ b/tests/basics/bytes_center.py @@ -0,0 +1,13 @@ +try: + bytes.center +except: + print("SKIP") + raise SystemExit + +print(b"foo".center(0)) +print(b"foo".center(1)) +print(b"foo".center(3)) +print(b"foo".center(4)) +print(b"foo".center(5)) +print(b"foo".center(6)) +print(b"foo".center(20)) diff --git a/tests/basics/bytes_count.py b/tests/basics/bytes_count.py index 5fa0730f5c..e71f09db00 100644 --- a/tests/basics/bytes_count.py +++ b/tests/basics/bytes_count.py @@ -48,6 +48,13 @@ print(b"aaaa".count(b'a', 1, 5)) print(b"aaaa".count(b'a', -1, 5)) print(b"abbabba".count(b"abba")) +print(b'\xaa \xaa'.count(b'\xaa')) +print(b'\xaa \xaa \xaa \xaa'.count(b'\xaa')) +print(b'\xaa \xaa \xaa \xaa'.count(b'\xaa'), 1) +print(b'\xaa \xaa \xaa \xaa'.count(b'\xaa'), 2) +print(b'\xaa \xaa \xaa \xaa'.count(b'\xaa'), 1, 3) +print(b'\xaa \xaa \xaa \xaa'.count(b'\xaa'), 2, 3) + def t(): return True diff --git a/tests/basics/dict_union.py b/tests/basics/dict_union.py new file mode 100644 index 0000000000..606b1c4c52 --- /dev/null +++ b/tests/basics/dict_union.py @@ -0,0 +1,36 @@ +# Test dictionary union. +# +# The tests sort the resulting dictionaries for now, since map insertion +# order is not implemented in MicroPython. + +try: + {} | {} +except TypeError: + print("SKIP") + raise SystemExit + + +def print_sorted_dict(d): + print(sorted(d.items())) + + +def test_union(a, b): + print_sorted_dict(a | b) + print_sorted_dict(b | a) + a |= a + print_sorted_dict(a) + a |= b + print_sorted_dict(a) + + +d = {} +e = {} +test_union(d, e) + +d = {1: "apple"} +e = {1: "cheese"} +test_union(d, e) + +d = {"spam": 1, "eggs": 2, "cheese": 3} +e = {"cheese": "cheddar", "aardvark": "Ethel"} +test_union(d, e) diff --git a/tests/basics/dict_union.py.exp b/tests/basics/dict_union.py.exp new file mode 100644 index 0000000000..7a8f83bd54 --- /dev/null +++ b/tests/basics/dict_union.py.exp @@ -0,0 +1,12 @@ +[] +[] +[] +[] +[(1, 'cheese')] +[(1, 'apple')] +[(1, 'apple')] +[(1, 'cheese')] +[('aardvark', 'Ethel'), ('cheese', 'cheddar'), ('eggs', 2), ('spam', 1)] +[('aardvark', 'Ethel'), ('cheese', 3), ('eggs', 2), ('spam', 1)] +[('cheese', 3), ('eggs', 2), ('spam', 1)] +[('aardvark', 'Ethel'), ('cheese', 'cheddar'), ('eggs', 2), ('spam', 1)] diff --git a/tests/basics/fun_calldblstar.py b/tests/basics/fun_calldblstar.py index d4816c5cd3..0308f42bdb 100644 --- a/tests/basics/fun_calldblstar.py +++ b/tests/basics/fun_calldblstar.py @@ -21,9 +21,12 @@ a = A() a.f(1, **{'b':2}) a.f(1, **{'b':val for val in range(1)}) +# test for duplicate keys in **kwargs not allowed + +def f1(**kwargs): + print(kwargs) + try: - f(1, **{len: 1}) + f1(**{'a': 1, 'b': 2}, **{'b': 3, 'c': 4}) except TypeError: - print(True) -else: - print(False) + print('TypeError') diff --git a/tests/basics/fun_callstardblstar.py b/tests/basics/fun_callstardblstar.py index f395df3333..c08e46f668 100644 --- a/tests/basics/fun_callstardblstar.py +++ b/tests/basics/fun_callstardblstar.py @@ -37,3 +37,7 @@ f2(*iter(range(4)), **{'a': 1}) # case where *args is not a tuple/list and takes up most of the memory allocated for **kwargs f2(*iter(range(100)), **{str(i): i for i in range(100)}) + +# regression test - iterable with unknown len() was exactly using preallocated +# memory causing args 4 and 5 to overflow the allocated arg array +print(1, *iter((1, 2, 3)), *iter((1, 2, 3)), 4, 5, sep=",") diff --git a/tests/basics/fun_globals.py b/tests/basics/fun_globals.py index 3f32e8bdb0..69f8638e8d 100644 --- a/tests/basics/fun_globals.py +++ b/tests/basics/fun_globals.py @@ -19,3 +19,18 @@ try: foo.__globals__ = None except AttributeError: print("AttributeError") + +# test closures have the __globals__ attribute + + +def outer(): + x = 1 + + def inner(): + return x + + return inner + + +print(outer.__globals__ is globals()) +print(outer().__globals__ is globals()) diff --git a/tests/basics/fun_name.py b/tests/basics/fun_name.py index bb1f14992f..b2642280a2 100644 --- a/tests/basics/fun_name.py +++ b/tests/basics/fun_name.py @@ -24,9 +24,11 @@ except AttributeError: pass # name of a function that has closed over variables +# and also the name of the inner closure def outer(): x = 1 def inner(): return x return inner print(outer.__name__) +print(outer().__name__) diff --git a/tests/basics/int_big_error.py b/tests/basics/int_big_error.py index 79809aef11..1793cf3253 100644 --- a/tests/basics/int_big_error.py +++ b/tests/basics/int_big_error.py @@ -7,6 +7,16 @@ try: except ValueError: print("ValueError") +try: + i @ 0 +except TypeError: + print("TypeError") + +try: + i @= 0 +except TypeError: + print("TypeError") + try: len(i) except TypeError: diff --git a/tests/basics/io_stringio_base.py b/tests/basics/io_stringio_base.py new file mode 100644 index 0000000000..dffc879074 --- /dev/null +++ b/tests/basics/io_stringio_base.py @@ -0,0 +1,24 @@ +# Checks that an instance type inheriting from a native base that uses +# MP_TYPE_FLAG_ITER_IS_STREAM will still have a getiter. + +try: + import uio as io +except ImportError: + import io + +a = io.StringIO() +a.write("hello\nworld\nmicro\npython\n") +a.seek(0) + +for line in a: + print(line) + +class X(io.StringIO): + pass + +b = X() +b.write("hello\nworld\nmicro\npython\n") +b.seek(0) + +for line in b: + print(line) diff --git a/tests/basics/memoryview_slice_size.py b/tests/basics/memoryview_slice_size.py new file mode 100644 index 0000000000..c56514d21f --- /dev/null +++ b/tests/basics/memoryview_slice_size.py @@ -0,0 +1,27 @@ +# test memoryview slicing beyond the limit of what memoryview can internally index + +try: + from sys import maxsize + from uctypes import bytearray_at + + memoryview +except: + print("SKIP") + raise SystemExit + +if maxsize <= 0xFFFF_FFFF: + slice_max = 0xFF_FFFF +else: + slice_max = 0xFF_FFFF_FFFF_FFFF + +buf = bytearray_at(0, slice_max + 2) +mv = memoryview(buf) + +# this should work +print(mv[slice_max : slice_max + 1]) + +# this should overflow the internal index for memoryview slicing +try: + print(mv[slice_max + 1 : slice_max + 2]) +except OverflowError: + print("OverflowError") diff --git a/tests/basics/memoryview_slice_size.py.exp b/tests/basics/memoryview_slice_size.py.exp new file mode 100644 index 0000000000..34baadd35e --- /dev/null +++ b/tests/basics/memoryview_slice_size.py.exp @@ -0,0 +1,2 @@ + +OverflowError diff --git a/tests/basics/module_dict.py b/tests/basics/module_dict.py new file mode 100644 index 0000000000..c847294f08 --- /dev/null +++ b/tests/basics/module_dict.py @@ -0,0 +1,13 @@ +# test __dict__ attribute of a built-in module +# see import/module_dict.py for the equivalent test on user modules + +import sys + +if not hasattr(sys, "__dict__"): + print("SKIP") + raise SystemExit + + +# dict of a built-in module (read-only) +print(type(sys.__dict__)) +print(sys.__dict__["__name__"]) diff --git a/tests/basics/namedtuple1.py b/tests/basics/namedtuple1.py index f55a95625e..eee8a3909c 100644 --- a/tests/basics/namedtuple1.py +++ b/tests/basics/namedtuple1.py @@ -77,3 +77,8 @@ print(t.foo, t.bar) # Not implemented so far #T2 = namedtuple("TupComma", "foo,bar") #t = T2(1, 2) + +# Creating an empty namedtuple should not segfault +T5 = namedtuple("TupEmpty", []) +t = T5() +print(t) diff --git a/tests/basics/nanbox_smallint.py b/tests/basics/nanbox_smallint.py index 167b196010..b3a502e447 100644 --- a/tests/basics/nanbox_smallint.py +++ b/tests/basics/nanbox_smallint.py @@ -1,6 +1,10 @@ # Test creating small integers without heap allocation in nan-boxing mode. -import micropython +try: + import micropython +except ImportError: + print("SKIP") + raise SystemExit try: # Test for nan-box build by allocating a float while heap is locked. diff --git a/tests/basics/string_fstring.py b/tests/basics/string_fstring.py index 7e8a97fd30..8907a5c478 100644 --- a/tests/basics/string_fstring.py +++ b/tests/basics/string_fstring.py @@ -56,3 +56,8 @@ try: except (ValueError, SyntaxError): # MicroPython incorrectly raises ValueError here. print('SyntaxError') + +# Allow literal tuples +print(f"a {1,} b") +print(f"a {x,y,} b") +print(f"a {x,1} b") diff --git a/tests/basics/string_fstring_debug.py b/tests/basics/string_fstring_debug.py index 76a448ca06..95abd4d6d0 100644 --- a/tests/basics/string_fstring_debug.py +++ b/tests/basics/string_fstring_debug.py @@ -21,3 +21,7 @@ print(f"a {x=:08x} b {y} c") print(f'a {f() + g("foo") + h()=} b') print(f'a {f() + g("foo") + h()=:08x} b') + +print(f"a {1,=} b") +print(f"a {x,y,=} b") +print(f"a {x,1=} b") diff --git a/tests/basics/string_fstring_debug.py.exp b/tests/basics/string_fstring_debug.py.exp index 563030f400..f0309e1c98 100644 --- a/tests/basics/string_fstring_debug.py.exp +++ b/tests/basics/string_fstring_debug.py.exp @@ -4,3 +4,6 @@ a x=1 b 2 c a x=00000001 b 2 c a f() + g("foo") + h()=15 b a f() + g("foo") + h()=0000000f b +a 1,=(1,) b +a x,y,=(1, 2) b +a x,1=(1, 1) b diff --git a/tests/cmdline/cmd_showbc.py b/tests/cmdline/cmd_showbc.py index e5874f990a..4a2e6500aa 100644 --- a/tests/cmdline/cmd_showbc.py +++ b/tests/cmdline/cmd_showbc.py @@ -119,12 +119,12 @@ def f(): #from sys import * # tested at module scope # raise - raise - raise 1 + if a: raise + if a: raise 1 # return - return - return 1 + if a: return + if a: return 1 # function with lots of locals def f(): diff --git a/tests/cmdline/cmd_showbc.py.exp b/tests/cmdline/cmd_showbc.py.exp index 45a1d169b6..db06de9237 100644 --- a/tests/cmdline/cmd_showbc.py.exp +++ b/tests/cmdline/cmd_showbc.py.exp @@ -47,13 +47,13 @@ arg names: 42 IMPORT_STAR 43 LOAD_CONST_NONE 44 RETURN_VALUE -File cmdline/cmd_showbc.py, code block 'f' (descriptor: \.\+, bytecode @\.\+ 45\[46\] bytes) -Raw bytecode (code_info_size=8\[46\], bytecode_size=370): +File cmdline/cmd_showbc.py, code block 'f' (descriptor: \.\+, bytecode @\.\+ 46\[68\] bytes) +Raw bytecode (code_info_size=8\[46\], bytecode_size=382): a8 12 9\[bf\] 03 05 60 60 26 22 24 64 22 24 25 25 24 26 23 63 22 22 25 23 23 2f 6c 25 65 25 25 69 68 26 65 27 6a 62 20 23 62 2a 29 69 24 25 28 67 26 ######## -\.\+81 63 +\.\+51 63 arg names: (N_STATE 22) (N_EXC_STACK 2) @@ -126,19 +126,18 @@ arg names: bc=313 line=100 bc=315 line=101 ######## - bc=321 line=103 - bc=327 line=106 - bc=331 line=107 - bc=337 line=110 - bc=340 line=111 - bc=346 line=114 - bc=346 line=117 - bc=351 line=118 - bc=363 line=121 - bc=363 line=122 - bc=364 line=123 - bc=366 line=126 - bc=368 line=127 + bc=325 line=106 + bc=329 line=107 + bc=335 line=110 + bc=338 line=111 + bc=344 line=114 + bc=344 line=117 + bc=349 line=118 + bc=361 line=121 + bc=361 line=122 + bc=365 line=123 + bc=370 line=126 + bc=375 line=127 00 LOAD_CONST_NONE 01 LOAD_CONST_FALSE 02 BINARY_OP 27 __add__ @@ -363,46 +362,55 @@ arg names: 308 LOAD_FAST 1 309 POP_TOP 310 END_FINALLY -311 JUMP 324 -313 SETUP_EXCEPT 320 -315 UNWIND_JUMP 327 1 -318 POP_EXCEPT_JUMP 324 -320 POP_TOP -321 POP_EXCEPT_JUMP 324 -323 END_FINALLY -324 LOAD_FAST 0 -325 POP_JUMP_IF_TRUE 313 -327 LOAD_FAST 0 -328 SETUP_WITH 335 -330 POP_TOP -331 LOAD_DEREF 14 -333 POP_TOP -334 LOAD_CONST_NONE -335 WITH_CLEANUP -336 END_FINALLY -337 LOAD_CONST_SMALL_INT 1 -338 STORE_DEREF 16 -340 LOAD_FAST_N 16 -342 MAKE_CLOSURE \.\+ 1 -345 STORE_FAST 13 -346 LOAD_CONST_SMALL_INT 0 -347 LOAD_CONST_NONE -348 IMPORT_NAME 'a' -350 STORE_FAST 0 -351 LOAD_CONST_SMALL_INT 0 -352 LOAD_CONST_STRING 'b' -354 BUILD_TUPLE 1 -356 IMPORT_NAME 'a' -358 IMPORT_FROM 'b' -360 STORE_DEREF 14 -362 POP_TOP -363 RAISE_LAST -364 LOAD_CONST_SMALL_INT 1 -365 RAISE_OBJ -366 LOAD_CONST_NONE -367 RETURN_VALUE +311 JUMP 322 +313 SETUP_EXCEPT 318 +315 UNWIND_JUMP 325 1 +318 POP_TOP +319 POP_EXCEPT_JUMP 322 +321 END_FINALLY +322 LOAD_FAST 0 +323 POP_JUMP_IF_TRUE 313 +325 LOAD_FAST 0 +326 SETUP_WITH 333 +328 POP_TOP +329 LOAD_DEREF 14 +331 POP_TOP +332 LOAD_CONST_NONE +333 WITH_CLEANUP +334 END_FINALLY +335 LOAD_CONST_SMALL_INT 1 +336 STORE_DEREF 16 +338 LOAD_FAST_N 16 +340 MAKE_CLOSURE \.\+ 1 +343 STORE_FAST 13 +344 LOAD_CONST_SMALL_INT 0 +345 LOAD_CONST_NONE +346 IMPORT_NAME 'a' +348 STORE_FAST 0 +349 LOAD_CONST_SMALL_INT 0 +350 LOAD_CONST_STRING 'b' +352 BUILD_TUPLE 1 +354 IMPORT_NAME 'a' +356 IMPORT_FROM 'b' +358 STORE_DEREF 14 +360 POP_TOP +361 LOAD_FAST 0 +362 POP_JUMP_IF_FALSE 365 +364 RAISE_LAST +365 LOAD_FAST 0 +366 POP_JUMP_IF_FALSE 370 368 LOAD_CONST_SMALL_INT 1 -369 RETURN_VALUE +369 RAISE_OBJ +370 LOAD_FAST 0 +371 POP_JUMP_IF_FALSE 375 +373 LOAD_CONST_NONE +374 RETURN_VALUE +375 LOAD_FAST 0 +376 POP_JUMP_IF_FALSE 380 +378 LOAD_CONST_SMALL_INT 1 +379 RETURN_VALUE +380 LOAD_CONST_NONE +381 RETURN_VALUE File cmdline/cmd_showbc.py, code block 'f' (descriptor: \.\+, bytecode @\.\+ 59 bytes) Raw bytecode (code_info_size=8, bytecode_size=51): a8 10 0a 05 80 82 34 38 81 57 c0 57 c1 57 c2 57 diff --git a/tests/cmdline/cmd_showbc_const.py b/tests/cmdline/cmd_showbc_const.py new file mode 100644 index 0000000000..54f9ec23cd --- /dev/null +++ b/tests/cmdline/cmd_showbc_const.py @@ -0,0 +1,69 @@ +# cmdline: -v -v +# Test constant-related bytecode optimisations +# (constant folding, compile-time if/while evaluation, etc.) +from micropython import const +import sys + +try: + sys.settrace + # if MICROPY_PY_SYS_SETTRACE is enabled, compile-time const optimizations + # are disabled so the bytecode output is very different + print("SKIP") + raise SystemExit +except AttributeError: + pass + +_STR = const("foo") +_EMPTY_TUPLE = const(()) +_TRUE = const(True) +_FALSE = const(False) +_SMALLINT = const(33) +_ZERO = const(0) + +# Bytecode generated for these if/while statements should contain no JUMP_IF +# and no instances of string 'Eliminated' + +if _STR or _EMPTY_TUPLE: + print("Kept") +if _STR and _EMPTY_TUPLE: + print("Eliminated") +if _TRUE: + print("Kept") +if _SMALLINT: + print("Kept") +if _ZERO and _SMALLINT: + print("Eliminated") +if _FALSE: + print("Eliminated") + +while _SMALLINT: + print("Kept") + break +while _ZERO: + print("Eliminated") +while _FALSE: + print("Eliminated") + +# These values are stored in variables, and therefore bytecode will contain JUMP_IF + +a = _EMPTY_TUPLE or _STR +if a == _STR: + print("Kept") + +b = _SMALLINT and _STR +if b == _STR: + print("Kept") + +# The compiler is also unable to optimise these expressions, even though the arguments are const, +# so these also contain JUMP_IF + +if (_EMPTY_TUPLE or _STR) == _STR: + print("Kept") + +if (_EMPTY_TUPLE and _STR) == _STR: + print("Not Eliminated") + +if (not _STR) == _FALSE: + print("Kept") + +assert True diff --git a/tests/cmdline/cmd_showbc_const.py.exp b/tests/cmdline/cmd_showbc_const.py.exp new file mode 100644 index 0000000000..6cdc3e9c96 --- /dev/null +++ b/tests/cmdline/cmd_showbc_const.py.exp @@ -0,0 +1,160 @@ +File cmdline/cmd_showbc_const.py, code block '' (descriptor: \.\+, bytecode @\.\+ 198 bytes) +Raw bytecode (code_info_size=40, bytecode_size=158): + 2c 4c 01 60 2c 46 22 65 27 4a 83 0c 20 27 40 20 + 27 20 27 40 60 20 27 24 40 60 40 24 27 47 24 27 + 67 40 27 47 27 47 26 47 80 10 02 2a 01 1b 03 1c + 02 16 02 59 80 51 1b 04 16 04 48 0f 11 04 13 05 + 59 11 09 10 06 34 01 59 11 0a 65 57 11 0b df 44 + 43 59 4a 01 5d 11 09 10 07 34 01 59 11 09 10 07 + 34 01 59 11 09 10 07 34 01 59 11 09 10 07 34 01 + 59 42 42 42 35 23 00 16 0c 11 0c 23 00 d9 44 47 + 11 09 10 07 34 01 59 23 00 16 0d 11 0d 23 00 d9 + 44 47 11 09 10 07 34 01 59 23 00 23 00 d9 44 47 + 11 09 10 07 34 01 59 23 01 23 00 d9 44 47 11 09 + 23 02 34 01 59 50 23 03 d9 44 47 11 09 10 07 34 + 01 59 42 40 51 63 +arg names: +(N_STATE 6) +(N_EXC_STACK 1) + bc=0 line=1 + bc=0 line=4 + bc=12 line=5 + bc=18 line=7 + bc=20 line=8 + bc=25 line=11 + bc=32 line=12 + bc=42 line=14 + bc=45 line=26 + bc=45 line=27 + bc=52 line=28 + bc=52 line=30 + bc=52 line=31 + bc=59 line=32 + bc=59 line=33 + bc=66 line=34 + bc=66 line=36 + bc=66 line=39 + bc=66 line=40 + bc=73 line=41 + bc=77 line=42 + bc=77 line=44 + bc=77 line=47 + bc=77 line=49 + bc=81 line=50 + bc=88 line=51 + bc=95 line=53 + bc=99 line=54 + bc=106 line=55 + bc=113 line=58 + bc=113 line=60 + bc=120 line=61 + bc=127 line=63 + bc=134 line=64 + bc=141 line=66 + bc=147 line=67 + bc=154 line=69 +00 LOAD_CONST_SMALL_INT 0 +01 LOAD_CONST_STRING 'const' +03 BUILD_TUPLE 1 +05 IMPORT_NAME 'micropython' +07 IMPORT_FROM 'const' +09 STORE_NAME const +11 POP_TOP +12 LOAD_CONST_SMALL_INT 0 +13 LOAD_CONST_NONE +14 IMPORT_NAME 'sys' +16 STORE_NAME sys +18 SETUP_EXCEPT 35 +20 LOAD_NAME sys +22 LOAD_ATTR settrace +24 POP_TOP +25 LOAD_NAME print +27 LOAD_CONST_STRING 'SKIP' +29 CALL_FUNCTION n=1 nkw=0 +31 POP_TOP +32 LOAD_NAME SystemExit +34 RAISE_OBJ +35 DUP_TOP +36 LOAD_NAME AttributeError +38 BINARY_OP 8 +39 POP_JUMP_IF_FALSE 44 +41 POP_TOP +42 POP_EXCEPT_JUMP 45 +44 END_FINALLY +45 LOAD_NAME print +47 LOAD_CONST_STRING 'Kept' +49 CALL_FUNCTION n=1 nkw=0 +51 POP_TOP +52 LOAD_NAME print +54 LOAD_CONST_STRING 'Kept' +56 CALL_FUNCTION n=1 nkw=0 +58 POP_TOP +59 LOAD_NAME print +61 LOAD_CONST_STRING 'Kept' +63 CALL_FUNCTION n=1 nkw=0 +65 POP_TOP +66 LOAD_NAME print +68 LOAD_CONST_STRING 'Kept' +70 CALL_FUNCTION n=1 nkw=0 +72 POP_TOP +73 JUMP 77 +75 JUMP 66 +77 LOAD_CONST_OBJ \.\+='foo' +79 STORE_NAME a +81 LOAD_NAME a +83 LOAD_CONST_OBJ \.\+='foo' +85 BINARY_OP 2 __eq__ +86 POP_JUMP_IF_FALSE 95 +88 LOAD_NAME print +90 LOAD_CONST_STRING 'Kept' +92 CALL_FUNCTION n=1 nkw=0 +94 POP_TOP +95 LOAD_CONST_OBJ \.\+='foo' +97 STORE_NAME b +99 LOAD_NAME b +101 LOAD_CONST_OBJ \.\+='foo' +103 BINARY_OP 2 __eq__ +104 POP_JUMP_IF_FALSE 113 +106 LOAD_NAME print +108 LOAD_CONST_STRING 'Kept' +110 CALL_FUNCTION n=1 nkw=0 +112 POP_TOP +113 LOAD_CONST_OBJ \.\+='foo' +115 LOAD_CONST_OBJ \.\+='foo' +117 BINARY_OP 2 __eq__ +118 POP_JUMP_IF_FALSE 127 +120 LOAD_NAME print +122 LOAD_CONST_STRING 'Kept' +124 CALL_FUNCTION n=1 nkw=0 +126 POP_TOP +127 LOAD_CONST_OBJ \.\+=() +129 LOAD_CONST_OBJ \.\+='foo' +131 BINARY_OP 2 __eq__ +132 POP_JUMP_IF_FALSE 141 +134 LOAD_NAME print +136 LOAD_CONST_OBJ \.\+='Not Eliminated' +138 CALL_FUNCTION n=1 nkw=0 +140 POP_TOP +141 LOAD_CONST_FALSE +142 LOAD_CONST_OBJ \.\+=False +144 BINARY_OP 2 __eq__ +145 POP_JUMP_IF_FALSE 154 +147 LOAD_NAME print +149 LOAD_CONST_STRING 'Kept' +151 CALL_FUNCTION n=1 nkw=0 +153 POP_TOP +154 JUMP 156 +156 LOAD_CONST_NONE +157 RETURN_VALUE +Kept +Kept +Kept +Kept +Kept +Kept +Kept +Kept +mem: total=\\d\+, current=\\d\+, peak=\\d\+ +stack: \\d\+ out of \\d\+ +GC: total: \\d\+, used: \\d\+, free: \\d\+ + No. of 1-blocks: \\d\+, 2-blocks: \\d\+, max blk sz: \\d\+, max free sz: \\d\+ diff --git a/tests/cmdline/cmd_showbc_opt.py b/tests/cmdline/cmd_showbc_opt.py new file mode 100644 index 0000000000..02785860d9 --- /dev/null +++ b/tests/cmdline/cmd_showbc_opt.py @@ -0,0 +1,33 @@ +# cmdline: -v -v +# test printing of bytecode when it's optimised away + + +def f0(): + return 0 + print(1) + + +def f1(x): + if x: + return + print(1) + print(2) + + +def f2(x): + raise Exception + print(1) + + +def f3(x): + while x: + break + print(1) + print(2) + + +def f4(x): + while x: + continue + print(1) + print(2) diff --git a/tests/cmdline/cmd_showbc_opt.py.exp b/tests/cmdline/cmd_showbc_opt.py.exp new file mode 100644 index 0000000000..9e4e4fae10 --- /dev/null +++ b/tests/cmdline/cmd_showbc_opt.py.exp @@ -0,0 +1,122 @@ +File cmdline/cmd_showbc_opt.py, code block '' (descriptor: \.\+, bytecode @\.\+ 35 bytes) +Raw bytecode (code_info_size=13, bytecode_size=22): + 00 16 01 60 20 64 40 84 07 64 40 84 07 32 00 16 + 02 32 01 16 03 32 02 16 04 32 03 16 05 32 04 16 + 06 51 63 +arg names: +(N_STATE 1) +(N_EXC_STACK 0) + bc=0 line=1 + bc=0 line=4 + bc=0 line=5 + bc=4 line=8 + bc=4 line=10 + bc=8 line=17 + bc=12 line=20 + bc=12 line=22 + bc=16 line=29 +00 MAKE_FUNCTION \.\+ +02 STORE_NAME f0 +04 MAKE_FUNCTION \.\+ +06 STORE_NAME f1 +08 MAKE_FUNCTION \.\+ +10 STORE_NAME f2 +12 MAKE_FUNCTION \.\+ +14 STORE_NAME f3 +16 MAKE_FUNCTION \.\+ +18 STORE_NAME f4 +20 LOAD_CONST_NONE +21 RETURN_VALUE +File cmdline/cmd_showbc_opt.py, code block 'f0' (descriptor: \.\+, bytecode @\.\+ 8 bytes) +Raw bytecode (code_info_size=6, bytecode_size=2): + 08 08 02 60 40 22 80 63 +arg names: +(N_STATE 2) +(N_EXC_STACK 0) + bc=0 line=1 + bc=0 line=4 + bc=0 line=6 + bc=2 line=7 +00 LOAD_CONST_SMALL_INT 0 +01 RETURN_VALUE +File cmdline/cmd_showbc_opt.py, code block 'f1' (descriptor: \.\+, bytecode @\.\+ 22 bytes) +Raw bytecode (code_info_size=9, bytecode_size=13): + 11 0e 03 08 80 0a 23 22 20 b0 44 42 51 63 12 07 + 82 34 01 59 51 63 +arg names: x +(N_STATE 3) +(N_EXC_STACK 0) + bc=0 line=1 + bc=0 line=11 + bc=3 line=12 + bc=5 line=13 + bc=5 line=14 +00 LOAD_FAST 0 +01 POP_JUMP_IF_FALSE 5 +03 LOAD_CONST_NONE +04 RETURN_VALUE +05 LOAD_GLOBAL print +07 LOAD_CONST_SMALL_INT 2 +08 CALL_FUNCTION n=1 nkw=0 +10 POP_TOP +11 LOAD_CONST_NONE +12 RETURN_VALUE +File cmdline/cmd_showbc_opt.py, code block 'f2' (descriptor: \.\+, bytecode @\.\+ 10 bytes) +Raw bytecode (code_info_size=7, bytecode_size=3): + 11 0a 04 08 80 11 23 12 09 65 +arg names: x +(N_STATE 3) +(N_EXC_STACK 0) + bc=0 line=1 + bc=0 line=18 + bc=3 line=19 +00 LOAD_GLOBAL Exception +02 RAISE_OBJ +File cmdline/cmd_showbc_opt.py, code block 'f3' (descriptor: \.\+, bytecode @\.\+ 24 bytes) +Raw bytecode (code_info_size=9, bytecode_size=15): + 11 0e 05 08 80 16 22 22 23 42 42 42 43 b0 43 3b + 12 07 82 34 01 59 51 63 +arg names: x +(N_STATE 3) +(N_EXC_STACK 0) + bc=0 line=1 + bc=0 line=23 + bc=2 line=24 + bc=4 line=25 + bc=7 line=26 +00 JUMP 4 +02 JUMP 7 +04 LOAD_FAST 0 +05 POP_JUMP_IF_TRUE 2 +07 LOAD_GLOBAL print +09 LOAD_CONST_SMALL_INT 2 +10 CALL_FUNCTION n=1 nkw=0 +12 POP_TOP +13 LOAD_CONST_NONE +14 RETURN_VALUE +File cmdline/cmd_showbc_opt.py, code block 'f4' (descriptor: \.\+, bytecode @\.\+ 24 bytes) +Raw bytecode (code_info_size=9, bytecode_size=15): + 11 0e 06 08 80 1d 22 22 23 42 42 42 40 b0 43 3b + 12 07 82 34 01 59 51 63 +arg names: x +(N_STATE 3) +(N_EXC_STACK 0) + bc=0 line=1 + bc=0 line=30 + bc=2 line=31 + bc=4 line=32 + bc=7 line=33 +00 JUMP 4 +02 JUMP 4 +04 LOAD_FAST 0 +05 POP_JUMP_IF_TRUE 2 +07 LOAD_GLOBAL print +09 LOAD_CONST_SMALL_INT 2 +10 CALL_FUNCTION n=1 nkw=0 +12 POP_TOP +13 LOAD_CONST_NONE +14 RETURN_VALUE +mem: total=\\d\+, current=\\d\+, peak=\\d\+ +stack: \\d\+ out of \\d\+ +GC: total: \\d\+, used: \\d\+, free: \\d\+ + No. of 1-blocks: \\d\+, 2-blocks: \\d\+, max blk sz: \\d\+, max free sz: \\d\+ diff --git a/tests/cpydiff/modules_os_getenv_argcount.py b/tests/cpydiff/modules_os_getenv_argcount.py deleted file mode 100644 index d7838a92cc..0000000000 --- a/tests/cpydiff/modules_os_getenv_argcount.py +++ /dev/null @@ -1,14 +0,0 @@ -""" -categories: Modules,os -description: ``getenv`` only allows one argument -cause: Unknown -workaround: Test that the return value is ``None`` -""" -import os - -try: - print(os.getenv("NEW_VARIABLE", "DEFAULT")) -except TypeError: - print("should not get here") - # this assumes NEW_VARIABLE is never an empty variable - print(os.getenv("NEW_VARIABLE") or "DEFAULT") diff --git a/tests/cpydiff/types_float_implicit_conversion.py b/tests/cpydiff/types_float_implicit_conversion.py new file mode 100644 index 0000000000..3726839fac --- /dev/null +++ b/tests/cpydiff/types_float_implicit_conversion.py @@ -0,0 +1,14 @@ +""" +categories: Types,float +description: uPy allows implicit conversion of objects in maths operations while CPython does not. +cause: Unknown +workaround: Objects should be wrapped in ``float(obj)`` for compatibility with CPython. +""" + + +class Test: + def __float__(self): + return 0.5 + + +print(2.0 * Test()) diff --git a/tests/extmod/btree1.py b/tests/extmod/btree1.py index 1f4853eaa3..876bce4f87 100644 --- a/tests/extmod/btree1.py +++ b/tests/extmod/btree1.py @@ -10,10 +10,13 @@ except ImportError: f = uio.BytesIO() db = btree.open(f, pagesize=512) +mv = memoryview(b"bar1foo1") + db[b"foo3"] = b"bar3" db[b"foo1"] = b"bar1" -db[b"foo2"] = b"bar2" -db[b"bar1"] = b"foo1" +# any type that implements buffer protocol works for key and value +db["foo2"] = "bar2" +db[mv[:4]] = mv[4:] dbstr = str(db) print(dbstr[:7], dbstr[-1:]) diff --git a/tests/extmod/framebuf_ellipse.py b/tests/extmod/framebuf_ellipse.py new file mode 100644 index 0000000000..a4c784aff8 --- /dev/null +++ b/tests/extmod/framebuf_ellipse.py @@ -0,0 +1,65 @@ +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 new file mode 100644 index 0000000000..ae6ad1ee7e --- /dev/null +++ b/tests/extmod/framebuf_ellipse.py.exp @@ -0,0 +1,704 @@ +--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 new file mode 100644 index 0000000000..03130b3bf0 --- /dev/null +++ b/tests/extmod/framebuf_polygon.py @@ -0,0 +1,222 @@ +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 new file mode 100644 index 0000000000..9b4801c788 --- /dev/null +++ b/tests/extmod/framebuf_polygon.py.exp @@ -0,0 +1,582 @@ + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ·· ·· ·· ff ff ff ff ·· ·· + ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· + ff ff ff ff ·· ·· ·· ·· ·· ff ff ff ff ·· ·· ff ·· ·· ·· ff ·· ·· ·· ff ·· ·· ·· ff ·· ·· + ff ·· ·· ff ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ·· ff ·· ·· ·· ff ·· ·· ·· ff ·· ·· + ff ·· ·· ·· ff ·· ·· ·· ff ·· ·· ·· ff ·· ·· ff ·· ·· ·· ·· ff ·· ff ·· ·· ·· ·· ff ·· ·· + ff ·· ·· ·· ff ·· ·· ·· ff ·· ·· ·· ff ·· ·· ff ·· ·· ·· ·· ff ·· ff ·· ·· ·· ·· ff ·· ·· + ff ·· ·· ·· ·· ff ·· ff ·· ·· ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ff ·· ·· + ff ·· ·· ·· ·· ff ·· ff ·· ·· ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· + ff ·· ·· ·· ·· ·· ff ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· + ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ff ·· ·· ·· ff ff ·· ·· ff ·· ·· + ff ·· ·· ff ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ff ·· ·· ·· ff ff ·· ·· ff ·· ·· + ff ·· ·· ff ff ·· ·· ·· ff ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ff ·· ff ·· ff ·· ·· ff ·· ·· + ff ·· ·· ff ff ·· ·· ·· ff ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ff ·· ff ·· ff ·· ·· ff ·· ·· + ff ·· ·· ff ·· ff ·· ff ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· + ff ·· ·· ff ·· ff ·· ff ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· + ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· + ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ff ·· ·· ·· ·· ·· ff ·· ·· ff ·· ·· + 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 new file mode 100644 index 0000000000..db9b6ea1e9 --- /dev/null +++ b/tests/extmod/framebuf_scroll.py @@ -0,0 +1,45 @@ +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 new file mode 100644 index 0000000000..7e99b275da --- /dev/null +++ b/tests/extmod/framebuf_scroll.py.exp @@ -0,0 +1,48 @@ +--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/ticks_add.py b/tests/extmod/ticks_add.py new file mode 100644 index 0000000000..2f1ba6c810 --- /dev/null +++ b/tests/extmod/ticks_add.py @@ -0,0 +1,42 @@ +try: + from utime import ticks_diff, ticks_add +except ImportError: + print("SKIP") + raise SystemExit + +# Maximum value returned from ticks_add, ticks_ms, etc. +TICKS_MAX = ticks_add(0, -1) +# Maximum value returned from ticks_diff. +TICKS_INTERVAL_MAX = TICKS_MAX // 2 + +# Invariants: +# - ticks_diff(ticks_add(T, delta), T) == delta +# - ticks_diff(T, ticks_add(T, delta)) == -delta + +# Check actual values of ticks_add. +print(ticks_add(20, 12)) +print(ticks_add(20, -12)) + +# Check invariant. +print(ticks_diff(ticks_add(100, 123), 100)) +print(ticks_diff(ticks_add(100, -123), 100)) +print(ticks_diff(100, ticks_add(100, 123))) +print(ticks_diff(100, ticks_add(100, -123))) + +# Check limits. +for T in (0, 10, TICKS_MAX): + for delta in ( + -TICKS_INTERVAL_MAX - 1, + -TICKS_INTERVAL_MAX, + 0, + TICKS_INTERVAL_MAX, + TICKS_INTERVAL_MAX + 1, + ): + try: + print(ticks_diff(ticks_add(T, delta), T) == delta) + except OverflowError: + print("OverflowError") + try: + print(ticks_diff(T, ticks_add(T, delta)) == -delta) + except OverflowError: + print("OverflowError") diff --git a/tests/extmod/ticks_add.py.exp b/tests/extmod/ticks_add.py.exp new file mode 100644 index 0000000000..60dc6f5afd --- /dev/null +++ b/tests/extmod/ticks_add.py.exp @@ -0,0 +1,36 @@ +32 +8 +123 +-123 +-123 +123 +OverflowError +OverflowError +True +True +True +True +True +True +OverflowError +OverflowError +OverflowError +OverflowError +True +True +True +True +True +True +OverflowError +OverflowError +OverflowError +OverflowError +True +True +True +True +True +True +OverflowError +OverflowError diff --git a/tests/extmod/uasyncio_gather.py b/tests/extmod/uasyncio_gather.py index e34176a31d..f4f1ef59ae 100644 --- a/tests/extmod/uasyncio_gather.py +++ b/tests/extmod/uasyncio_gather.py @@ -64,6 +64,11 @@ async def main(): print("====") + # Gather with no awaitables + print(await asyncio.gather()) + + print("====") + # Test return_exceptions, where one task is cancelled and the other finishes normally tasks = [asyncio.create_task(task(1)), asyncio.create_task(task(2))] tasks[0].cancel() diff --git a/tests/extmod/uasyncio_gather.py.exp b/tests/extmod/uasyncio_gather.py.exp index a5ea47ab50..371d1ae60d 100644 --- a/tests/extmod/uasyncio_gather.py.exp +++ b/tests/extmod/uasyncio_gather.py.exp @@ -9,6 +9,8 @@ Task C: Compute factorial(4)... Task C: factorial(4) = 24 [2, 6, 24] ==== +[] +==== start 2 end 2 [CancelledError(), 2] diff --git a/tests/extmod/uasyncio_heaplock.py b/tests/extmod/uasyncio_heaplock.py index 34a51cd370..9c9487357a 100644 --- a/tests/extmod/uasyncio_heaplock.py +++ b/tests/extmod/uasyncio_heaplock.py @@ -1,14 +1,26 @@ -# test that basic scheduling of tasks, and uasyncio.sleep_ms, does not use the heap +# test that the following do not use the heap: +# - basic scheduling of tasks +# - uasyncio.sleep_ms +# - StreamWriter.write, stream is blocked and data to write is a bytes object +# - StreamWriter.write, when stream is not blocked import micropython # strict stackless builds can't call functions without allocating a frame on the heap try: - f = lambda: 0 + # force bytecode (in case we're running with emit=native) and verify + # that bytecode-calling-bytecode doesn't allocate + @micropython.bytecode + def f(x): + x and f(x - 1) + micropython.heap_lock() - f() + f(1) micropython.heap_unlock() except RuntimeError: + # RuntimeError (max recursion depth) not MemoryError because effectively + # the recursion depth is at the limit while the heap is locked with + # stackless print("SKIP") raise SystemExit @@ -22,6 +34,17 @@ except ImportError: raise SystemExit +class TestStream: + def __init__(self, blocked): + self.blocked = blocked + + def write(self, data): + print("TestStream.write", data) + if self.blocked: + return None + return len(data) + + async def task(id, n, t): for i in range(n): print(id, i) @@ -32,14 +55,27 @@ async def main(): t1 = asyncio.create_task(task(1, 4, 100)) t2 = asyncio.create_task(task(2, 2, 250)) + # test scheduling tasks, and calling sleep_ms micropython.heap_lock() - print("start") await asyncio.sleep_ms(5) print("sleep") await asyncio.sleep_ms(350) print("finish") + micropython.heap_unlock() + # test writing to a stream, when the underlying stream is blocked + s = asyncio.StreamWriter(TestStream(True), None) + micropython.heap_lock() + s.write(b"12") + micropython.heap_unlock() + + # test writing to a stream, when the underlying stream is not blocked + buf = bytearray(b"56") + s = asyncio.StreamWriter(TestStream(False), None) + micropython.heap_lock() + s.write(b"34") + s.write(buf) micropython.heap_unlock() diff --git a/tests/extmod/uasyncio_heaplock.py.exp b/tests/extmod/uasyncio_heaplock.py.exp index 68c6366c6f..5c7b4e3dba 100644 --- a/tests/extmod/uasyncio_heaplock.py.exp +++ b/tests/extmod/uasyncio_heaplock.py.exp @@ -7,3 +7,6 @@ sleep 2 1 1 3 finish +TestStream.write b'12' +TestStream.write b'34' +TestStream.write bytearray(b'56') diff --git a/tests/extmod/uasyncio_micropython.py b/tests/extmod/uasyncio_micropython.py index 69e5fa3224..a6b65bb2a8 100644 --- a/tests/extmod/uasyncio_micropython.py +++ b/tests/extmod/uasyncio_micropython.py @@ -22,6 +22,12 @@ async def main(): await uasyncio.sleep_ms(1) print(utime.ticks_diff(utime.ticks_ms(), t0) < 100) + try: + # Sleep 1ms beyond maximum allowed sleep value + await uasyncio.sleep_ms(utime.ticks_add(0, -1) // 2 + 1) + except OverflowError: + print("OverflowError") + # When task finished before the timeout print(await uasyncio.wait_for_ms(task(1, 5), 50)) diff --git a/tests/extmod/uasyncio_micropython.py.exp b/tests/extmod/uasyncio_micropython.py.exp index f5be1dc75a..4d1c6d681f 100644 --- a/tests/extmod/uasyncio_micropython.py.exp +++ b/tests/extmod/uasyncio_micropython.py.exp @@ -1,4 +1,5 @@ True +OverflowError task start 1 task end 1 2 diff --git a/tests/extmod/uasyncio_threadsafeflag.py b/tests/extmod/uasyncio_threadsafeflag.py index 4e002a3d2a..a8a08d2e92 100644 --- a/tests/extmod/uasyncio_threadsafeflag.py +++ b/tests/extmod/uasyncio_threadsafeflag.py @@ -75,5 +75,25 @@ async def main(): print("wait task") await t + # Flag set, cleared, and set again. + print("----") + print("set event") + flag.set() + print("yield") + await asyncio.sleep(0) + print("clear event") + flag.clear() + print("yield") + await asyncio.sleep(0) + t = asyncio.create_task(task(4, flag)) + print("yield") + await asyncio.sleep(0) + print("set event") + flag.set() + print("yield") + await asyncio.sleep(0) + print("wait task") + await t + asyncio.run(main()) diff --git a/tests/extmod/uasyncio_threadsafeflag.py.exp b/tests/extmod/uasyncio_threadsafeflag.py.exp index aef4e479ba..757115ac4b 100644 --- a/tests/extmod/uasyncio_threadsafeflag.py.exp +++ b/tests/extmod/uasyncio_threadsafeflag.py.exp @@ -19,3 +19,14 @@ yield task 3 task 3 done wait task +---- +set event +yield +clear event +yield +yield +task 4 +set event +yield +wait task +task 4 done diff --git a/tests/extmod/ubinascii_hexlify.py b/tests/extmod/ubinascii_hexlify.py index 3dcf0c4271..3c266fb6cc 100644 --- a/tests/extmod/ubinascii_hexlify.py +++ b/tests/extmod/ubinascii_hexlify.py @@ -7,11 +7,16 @@ except ImportError: print("SKIP") raise SystemExit -print(binascii.hexlify(b"\x00\x01\x02\x03\x04\x05\x06\x07")) -print(binascii.hexlify(b"\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f")) -print(binascii.hexlify(b"\x7f\x80\xff")) -print(binascii.hexlify(b"1234ABCDabcd")) -try: - binascii.hexlify("") -except TypeError: - print("TypeError") +for x in ( + b"\x00\x01\x02\x03\x04\x05\x06\x07", + b"\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + b"\x7f\x80\xff", + b"1234ABCDabcd", +): + print(binascii.hexlify(x)) + +# Two-argument version (now supported in CPython) +print(binascii.hexlify(b"123", ":")) + +# zero length buffer +print(binascii.hexlify(b"", b":")) diff --git a/tests/extmod/ubinascii_micropython.py b/tests/extmod/ubinascii_micropython.py deleted file mode 100644 index 94e8daa557..0000000000 --- a/tests/extmod/ubinascii_micropython.py +++ /dev/null @@ -1,15 +0,0 @@ -try: - try: - import ubinascii as binascii - except ImportError: - import binascii -except ImportError: - print("SKIP") - raise SystemExit - -# two arguments supported in uPy but not CPython -a = binascii.hexlify(b"123", ":") -print(a) - -# zero length buffer -print(binascii.hexlify(b"", b":")) diff --git a/tests/extmod/ubinascii_micropython.py.exp b/tests/extmod/ubinascii_micropython.py.exp deleted file mode 100644 index a195d2602c..0000000000 --- a/tests/extmod/ubinascii_micropython.py.exp +++ /dev/null @@ -1,2 +0,0 @@ -b'31:32:33' -b'' diff --git a/tests/extmod/ubinascii_unhexlify.py b/tests/extmod/ubinascii_unhexlify.py index f656bcd758..75e188f7bc 100644 --- a/tests/extmod/ubinascii_unhexlify.py +++ b/tests/extmod/ubinascii_unhexlify.py @@ -7,10 +7,13 @@ except ImportError: print("SKIP") raise SystemExit -print(binascii.unhexlify(b"0001020304050607")) -print(binascii.unhexlify(b"08090a0b0c0d0e0f")) -print(binascii.unhexlify(b"7f80ff")) -print(binascii.unhexlify(b"313233344142434461626364")) +for x in ( + b"0001020304050607", + b"08090a0b0c0d0e0f", + b"7f80ff", + b"313233344142434461626364", +): + print(binascii.unhexlify(x)) # Unicode strings can be decoded print(binascii.unhexlify("313233344142434461626364")) diff --git a/tests/extmod/ure_namedclass.py b/tests/extmod/ure_namedclass.py index 00d58ad98a..4afc09dc0a 100644 --- a/tests/extmod/ure_namedclass.py +++ b/tests/extmod/ure_namedclass.py @@ -15,7 +15,7 @@ def print_groups(match): try: i = 0 while True: - print(m.group(i)) + print(match.group(i)) i += 1 except IndexError: pass @@ -32,3 +32,8 @@ print_groups(m) m = re.match(r"(([0-9]*)([a-z]*)\d*)", "1234hello567") 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")) diff --git a/tests/extmod/ussl_basic.py b/tests/extmod/ussl_basic.py index 9e1821dca9..dd3b6f0b91 100644 --- a/tests/extmod/ussl_basic.py +++ b/tests/extmod/ussl_basic.py @@ -7,40 +7,49 @@ except ImportError: print("SKIP") raise SystemExit + +class TestSocket(io.IOBase): + def write(self, buf): + return len(buf) + + def readinto(self, buf): + return 0 + + def ioctl(self, cmd, arg): + print("TestSocket.ioctl", cmd, arg) + return 0 + + def setblocking(self, value): + print("TestSocket.setblocking({})".format(value)) + + # create in client mode try: - ss = ssl.wrap_socket(io.BytesIO(), server_hostname="test.example.com") + ss = ssl.wrap_socket(TestSocket(), server_hostname="test.example.com") except OSError as er: - print("wrap_socket:", repr(er)) + print("OSError: client") # create in server mode (can use this object for further tests) -socket = io.BytesIO() -ss = ssl.wrap_socket(socket, server_side=1) +ss = ssl.wrap_socket(TestSocket(), server_side=1, do_handshake=0) # print print(repr(ss)[:12]) -# setblocking() propagates call to the underlying stream object, and -# io.BytesIO doesn't have setblocking() (in CPython too). -# try: -# ss.setblocking(False) -# except NotImplementedError: -# print('setblocking: NotImplementedError') -# ss.setblocking(True) +# setblocking() propagates call to the underlying stream object +ss.setblocking(False) +ss.setblocking(True) # write -print(ss.write(b"aaaa")) +try: + ss.write(b"aaaa") +except OSError: + pass # read (underlying socket has no data) -print(ss.read(8)) - -# read (underlying socket has data, but it's bad data) -socket.write(b"aaaaaaaaaaaaaaaa") -socket.seek(0) try: ss.read(8) -except OSError as er: - print("read:", repr(er)) +except OSError: + pass # close ss.close() @@ -51,10 +60,10 @@ ss.close() try: ss.read(10) except OSError as er: - print("read:", repr(er)) + print("OSError: read") # write on closed socket try: ss.write(b"aaaa") except OSError as er: - print("write:", repr(er)) + print("OSError: write") diff --git a/tests/extmod/ussl_basic.py.exp b/tests/extmod/ussl_basic.py.exp index eb7df855aa..30bfd3a436 100644 --- a/tests/extmod/ussl_basic.py.exp +++ b/tests/extmod/ussl_basic.py.exp @@ -1,7 +1,7 @@ -wrap_socket: OSError(-256, 'CONN_LOST') +OSError: client <_SSLSocket -4 -b'' -read: OSError(-261,) -read: OSError(9,) -write: OSError(9,) +TestSocket.setblocking(False) +TestSocket.setblocking(True) +TestSocket.ioctl 4 0 +OSError: read +OSError: write diff --git a/tests/extmod/ussl_poll.py b/tests/extmod/ussl_poll.py new file mode 100644 index 0000000000..8080ec5112 --- /dev/null +++ b/tests/extmod/ussl_poll.py @@ -0,0 +1,196 @@ +try: + import uselect + import ussl + import io + import ubinascii as binascii +except ImportError: + print("SKIP") + raise SystemExit + +from micropython import const + +_MP_STREAM_POLL_RD = const(0x0001) +_MP_STREAM_POLL_WR = const(0x0004) +_MP_STREAM_POLL_NVAL = const(0x0020) +_MP_STREAM_POLL = const(3) +_MP_STREAM_CLOSE = const(4) + + +# This self-signed key/cert pair is randomly generated and to be used for +# testing/demonstration only. You should always generate your own key/cert. +key = binascii.unhexlify( + b"3082013b020100024100cc20643fd3d9c21a0acba4f48f61aadd675f52175a9dcf07fbef" + b"610a6a6ba14abb891745cd18a1d4c056580d8ff1a639460f867013c8391cdc9f2e573b0f" + b"872d0203010001024100bb17a54aeb3dd7ae4edec05e775ca9632cf02d29c2a089b563b0" + b"d05cdf95aeca507de674553f28b4eadaca82d5549a86058f9996b07768686a5b02cb240d" + b"d9f1022100f4a63f5549e817547dca97b5c658038e8593cb78c5aba3c4642cc4cd031d86" + b"8f022100d598d870ffe4a34df8de57047a50b97b71f4d23e323f527837c9edae88c79483" + b"02210098560c89a70385c36eb07fd7083235c4c1184e525d838aedf7128958bedfdbb102" + b"2051c0dab7057a8176ca966f3feb81123d4974a733df0f958525f547dfd1c271f9022044" + b"6c2cafad455a671a8cf398e642e1be3b18a3d3aec2e67a9478f83c964c4f1f" +) +cert = binascii.unhexlify( + b"308201d53082017f020203e8300d06092a864886f70d01010505003075310b3009060355" + b"0406130258583114301206035504080c0b54686550726f76696e63653110300e06035504" + b"070c075468654369747931133011060355040a0c0a436f6d70616e7958595a3113301106" + b"0355040b0c0a436f6d70616e7958595a3114301206035504030c0b546865486f73744e61" + b"6d65301e170d3139313231383033333935355a170d3239313231353033333935355a3075" + b"310b30090603550406130258583114301206035504080c0b54686550726f76696e636531" + b"10300e06035504070c075468654369747931133011060355040a0c0a436f6d70616e7958" + b"595a31133011060355040b0c0a436f6d70616e7958595a3114301206035504030c0b5468" + b"65486f73744e616d65305c300d06092a864886f70d0101010500034b003048024100cc20" + b"643fd3d9c21a0acba4f48f61aadd675f52175a9dcf07fbef610a6a6ba14abb891745cd18" + b"a1d4c056580d8ff1a639460f867013c8391cdc9f2e573b0f872d0203010001300d06092a" + b"864886f70d0101050500034100b0513fe2829e9ecbe55b6dd14c0ede7502bde5d46153c8" + b"e960ae3ebc247371b525caeb41bbcf34686015a44c50d226e66aef0a97a63874ca5944ef" + b"979b57f0b3" +) + + +class _Pipe(io.IOBase): + def __init__(self): + self._other = None + self.block_reads = False + self.block_writes = False + + self.write_buffers = [] + self.last_poll_arg = None + + def readinto(self, buf): + if self.block_reads or len(self._other.write_buffers) == 0: + return None + + read_buf = self._other.write_buffers[0] + l = min(len(buf), len(read_buf)) + buf[:l] = read_buf[:l] + if l == len(read_buf): + self._other.write_buffers.pop(0) + else: + self._other.write_buffers[0] = read_buf[l:] + return l + + def write(self, buf): + if self.block_writes: + return None + + self.write_buffers.append(memoryview(bytes(buf))) + return len(buf) + + def ioctl(self, request, arg): + if request == _MP_STREAM_POLL: + self.last_poll_arg = arg + ret = 0 + if arg & _MP_STREAM_POLL_RD: + if not self.block_reads and self._other.write_buffers: + ret |= _MP_STREAM_POLL_RD + if arg & _MP_STREAM_POLL_WR: + if not self.block_writes: + ret |= _MP_STREAM_POLL_WR + return ret + + elif request == _MP_STREAM_CLOSE: + return 0 + + raise NotImplementedError() + + @classmethod + def new_pair(cls): + p1 = cls() + p2 = cls() + p1._other = p2 + p2._other = p1 + return p1, p2 + + +def assert_poll(s, i, arg, expected_arg, expected_ret): + ret = s.ioctl(_MP_STREAM_POLL, arg) + assert i.last_poll_arg == expected_arg + i.last_poll_arg = None + assert ret == expected_ret + + +def assert_raises(cb, *args, **kwargs): + try: + cb(*args, **kwargs) + raise AssertionError("should have raised") + except Exception as exc: + pass + + +client_io, server_io = _Pipe.new_pair() + +client_io.block_reads = True +client_io.block_writes = True +client_sock = ussl.wrap_socket(client_io, do_handshake=False) + +server_sock = ussl.wrap_socket(server_io, key=key, cert=cert, server_side=True, do_handshake=False) + +# Do a test read, at this point the TLS handshake wants to write, +# so it returns None: +assert client_sock.read(128) is None + +# Polling for either read or write actually check if the underlying socket can write: +assert_poll(client_sock, client_io, _MP_STREAM_POLL_RD, _MP_STREAM_POLL_WR, 0) +assert_poll(client_sock, client_io, _MP_STREAM_POLL_WR, _MP_STREAM_POLL_WR, 0) + +# Mark the socket as writable, and do another test read: +client_io.block_writes = False +assert client_sock.read(128) is None + +# The client wrote the CLIENT_HELLO message +assert len(client_io.write_buffers) == 1 + +# At this point the TLS handshake wants to read, but we don't know that yet: +assert_poll(client_sock, client_io, _MP_STREAM_POLL_RD, _MP_STREAM_POLL_RD, 0) +assert_poll(client_sock, client_io, _MP_STREAM_POLL_WR, _MP_STREAM_POLL_WR, _MP_STREAM_POLL_WR) + +# Do a test write +client_sock.write(b"foo") + +# Now we know that we want to read: +assert_poll(client_sock, client_io, _MP_STREAM_POLL_RD, _MP_STREAM_POLL_RD, 0) +assert_poll(client_sock, client_io, _MP_STREAM_POLL_WR, _MP_STREAM_POLL_RD, 0) + +# Unblock reads and nudge the two sockets: +client_io.block_reads = False +while server_io.write_buffers or client_io.write_buffers: + if server_io.write_buffers: + assert client_sock.read(128) is None + if client_io.write_buffers: + assert server_sock.read(128) is None + +# At this point, the handshake is done, try writing data: +client_sock.write(b"foo") +assert server_sock.read(3) == b"foo" + +# Test reading partial data: +client_sock.write(b"foobar") +assert server_sock.read(3) == b"foo" +server_io.block_reads = True +assert_poll( + server_sock, server_io, _MP_STREAM_POLL_RD, None, _MP_STREAM_POLL_RD +) # Did not go to the socket, just consumed buffered data +assert server_sock.read(3) == b"bar" + + +# Polling on a closed socket errors out: +client_io, _ = _Pipe.new_pair() +client_sock = ussl.wrap_socket(client_io, do_handshake=False) +client_sock.close() +assert_poll( + client_sock, client_io, _MP_STREAM_POLL_RD, None, _MP_STREAM_POLL_NVAL +) # Did not go to the socket + + +# Errors propagates to poll: +client_io, server_io = _Pipe.new_pair() +client_sock = ussl.wrap_socket(client_io, do_handshake=False) + +# The server returns garbage: +server_io.write(b"fooba") # Needs to be exactly 5 bytes + +assert_poll(client_sock, client_io, _MP_STREAM_POLL_RD, _MP_STREAM_POLL_RD, _MP_STREAM_POLL_RD) +assert_raises(client_sock.read, 128) +assert_poll( + client_sock, client_io, _MP_STREAM_POLL_RD, None, _MP_STREAM_POLL_NVAL +) # Did not go to the socket diff --git a/tests/extmod/ussl_poll.py.exp b/tests/extmod/ussl_poll.py.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/extmod/uzlib_decompio.py b/tests/extmod/uzlib_decompio.py index 6a0aae8bcd..fae901aad0 100644 --- a/tests/extmod/uzlib_decompio.py +++ b/tests/extmod/uzlib_decompio.py @@ -20,13 +20,13 @@ print(inp.read()) print(buf.seek(0, 1)) -# zlib bitstream -inp = zlib.DecompIO(io.BytesIO(b"x\x9c30\xa0=\x00\x00\xb3q\x12\xc1")) +# zlib bitstream (with 256 byte window size) +inp = zlib.DecompIO(io.BytesIO(b"\x08\x9930\xa0=\x00\x00\xb3q\x12\xc1")) print(inp.read(10)) print(inp.read()) # zlib bitstream, wrong checksum -inp = zlib.DecompIO(io.BytesIO(b"x\x9c30\xa0=\x00\x00\xb3q\x12\xc0")) +inp = zlib.DecompIO(io.BytesIO(b"\x08\x9930\xa0=\x00\x00\xb3q\x12\xc0")) try: print(inp.read()) except OSError as e: diff --git a/tests/extmod/vfs_fat_fileio1.py b/tests/extmod/vfs_fat_fileio1.py index 935f01d24f..431be4eb6d 100644 --- a/tests/extmod/vfs_fat_fileio1.py +++ b/tests/extmod/vfs_fat_fileio1.py @@ -39,11 +39,11 @@ class RAMFS: try: bdev = RAMFS(50) + uos.VfsFat.mkfs(bdev) except MemoryError: print("SKIP") raise SystemExit -uos.VfsFat.mkfs(bdev) vfs = uos.VfsFat(bdev) uos.mount(vfs, "/ramdisk") uos.chdir("/ramdisk") diff --git a/tests/extmod/vfs_fat_fileio2.py b/tests/extmod/vfs_fat_fileio2.py index 6094b5a6f1..41e3fe4731 100644 --- a/tests/extmod/vfs_fat_fileio2.py +++ b/tests/extmod/vfs_fat_fileio2.py @@ -40,11 +40,11 @@ class RAMFS: try: bdev = RAMFS(50) + uos.VfsFat.mkfs(bdev) except MemoryError: print("SKIP") raise SystemExit -uos.VfsFat.mkfs(bdev) vfs = uos.VfsFat(bdev) uos.mount(vfs, "/ramdisk") uos.chdir("/ramdisk") diff --git a/tests/extmod/vfs_fat_ilistdir_del.py b/tests/extmod/vfs_fat_ilistdir_del.py new file mode 100644 index 0000000000..ccdacc57c2 --- /dev/null +++ b/tests/extmod/vfs_fat_ilistdir_del.py @@ -0,0 +1,80 @@ +# Test ilistdir __del__ for VfsFat using a RAM device. +import gc + +try: + import uos + + uos.VfsFat +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit + + +class RAMBlockDevice: + ERASE_BLOCK_SIZE = 512 + + def __init__(self, blocks): + self.data = bytearray(blocks * self.ERASE_BLOCK_SIZE) + + def readblocks(self, block, buf, off=0): + addr = block * self.ERASE_BLOCK_SIZE + off + for i in range(len(buf)): + buf[i] = self.data[addr + i] + + def writeblocks(self, block, buf, off=0): + addr = block * self.ERASE_BLOCK_SIZE + off + for i in range(len(buf)): + self.data[addr + i] = buf[i] + + def ioctl(self, op, arg): + if op == 4: # block count + return len(self.data) // self.ERASE_BLOCK_SIZE + if op == 5: # block size + return self.ERASE_BLOCK_SIZE + if op == 6: # erase block + return 0 + + +def test(bdev, vfs_class): + vfs_class.mkfs(bdev) + vfs = vfs_class(bdev) + vfs.mkdir("/test_d1") + vfs.mkdir("/test_d2") + vfs.mkdir("/test_d3") + + for i in range(10): + print(i) + + # We want to partially iterate the ilistdir iterator to leave it in an + # open state, which will then test the finaliser when it's garbage collected. + idir = vfs.ilistdir("/") + print(any(idir)) + + # Alternate way of partially iterating the ilistdir object, modifying the + # filesystem while it's open. + for dname, *_ in vfs.ilistdir("/"): + vfs.rmdir(dname) + break + vfs.mkdir(dname) + + # Also create a fully drained iterator and ensure trying to re-use it + # throws the correct exception. + idir_emptied = vfs.ilistdir("/") + l = list(idir_emptied) + print(len(l)) + try: + next(idir_emptied) + except StopIteration: + pass + + gc.collect() + vfs.open("/test", "w").close() + + +try: + bdev = RAMBlockDevice(50) +except MemoryError: + print("SKIP") + raise SystemExit + +test(bdev, uos.VfsFat) diff --git a/tests/extmod/vfs_fat_ilistdir_del.py.exp b/tests/extmod/vfs_fat_ilistdir_del.py.exp new file mode 100644 index 0000000000..0ab2b019f4 --- /dev/null +++ b/tests/extmod/vfs_fat_ilistdir_del.py.exp @@ -0,0 +1,30 @@ +0 +True +3 +1 +True +4 +2 +True +4 +3 +True +4 +4 +True +4 +5 +True +4 +6 +True +4 +7 +True +4 +8 +True +4 +9 +True +4 diff --git a/tests/extmod/vfs_fat_ramdisk.py b/tests/extmod/vfs_fat_ramdisk.py index a22d480dfd..17aa2a5d67 100644 --- a/tests/extmod/vfs_fat_ramdisk.py +++ b/tests/extmod/vfs_fat_ramdisk.py @@ -40,12 +40,11 @@ class RAMFS: try: bdev = RAMFS(50) + uos.VfsFat.mkfs(bdev) except MemoryError: print("SKIP") raise SystemExit -uos.VfsFat.mkfs(bdev) - print(b"FOO_FILETXT" not in bdev.data) print(b"hello!" not in bdev.data) diff --git a/tests/extmod/vfs_lfs_ilistdir_del.py b/tests/extmod/vfs_lfs_ilistdir_del.py new file mode 100644 index 0000000000..073576986d --- /dev/null +++ b/tests/extmod/vfs_lfs_ilistdir_del.py @@ -0,0 +1,75 @@ +# Test ilistdir __del__ for VfsLittle using a RAM device. +import gc + +try: + import uos + + uos.VfsLfs2 +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit + + +class RAMBlockDevice: + ERASE_BLOCK_SIZE = 1024 + + def __init__(self, blocks): + self.data = bytearray(blocks * self.ERASE_BLOCK_SIZE) + + def readblocks(self, block, buf, off): + addr = block * self.ERASE_BLOCK_SIZE + off + for i in range(len(buf)): + buf[i] = self.data[addr + i] + + def writeblocks(self, block, buf, off): + addr = block * self.ERASE_BLOCK_SIZE + off + for i in range(len(buf)): + self.data[addr + i] = buf[i] + + def ioctl(self, op, arg): + if op == 4: # block count + return len(self.data) // self.ERASE_BLOCK_SIZE + if op == 5: # block size + return self.ERASE_BLOCK_SIZE + if op == 6: # erase block + return 0 + + +def test(bdev, vfs_class): + vfs_class.mkfs(bdev) + vfs = vfs_class(bdev) + vfs.mkdir("/test_d1") + vfs.mkdir("/test_d2") + vfs.mkdir("/test_d3") + + for i in range(10): + print(i) + + # We want to partially iterate the ilistdir iterator to leave it in an + # open state, which will then test the finaliser when it's garbage collected. + idir = vfs.ilistdir("/") + print(any(idir)) + + # Alternate way of partially iterating the ilistdir object, modifying the + # filesystem while it's open. + for dname, *_ in vfs.ilistdir("/"): + vfs.rmdir(dname) + break + vfs.mkdir(dname) + + # Also create a fully drained iterator and ensure trying to re-use it + # throws the correct exception. + idir_emptied = vfs.ilistdir("/") + l = list(idir_emptied) + print(len(l)) + try: + next(idir_emptied) + except StopIteration: + pass + + gc.collect() + vfs.open("/test", "w").close() + + +bdev = RAMBlockDevice(30) +test(bdev, uos.VfsLfs2) diff --git a/tests/extmod/vfs_lfs_ilistdir_del.py.exp b/tests/extmod/vfs_lfs_ilistdir_del.py.exp new file mode 100644 index 0000000000..0ab2b019f4 --- /dev/null +++ b/tests/extmod/vfs_lfs_ilistdir_del.py.exp @@ -0,0 +1,30 @@ +0 +True +3 +1 +True +4 +2 +True +4 +3 +True +4 +4 +True +4 +5 +True +4 +6 +True +4 +7 +True +4 +8 +True +4 +9 +True +4 diff --git a/tests/extmod/vfs_lfs_mtime.py b/tests/extmod/vfs_lfs_mtime.py index bacdd2246c..a67e48dd80 100644 --- a/tests/extmod/vfs_lfs_mtime.py +++ b/tests/extmod/vfs_lfs_mtime.py @@ -101,5 +101,10 @@ def test(bdev, vfs_class): vfs.umount() -bdev = RAMBlockDevice(30) +try: + bdev = RAMBlockDevice(30) +except MemoryError: + print("SKIP") + raise SystemExit + test(bdev, uos.VfsLfs2) diff --git a/tests/extmod/vfs_posix.py b/tests/extmod/vfs_posix.py index 2a14fc2076..3c23140065 100644 --- a/tests/extmod/vfs_posix.py +++ b/tests/extmod/vfs_posix.py @@ -1,6 +1,7 @@ # Test for VfsPosix try: + import gc import uos uos.VfsPosix @@ -42,8 +43,8 @@ f.close() # close on a closed file should succeed f.close() -# construct a file object using the type constructor, with a raw fileno -f = type(f)(2) +# construct a file object with a raw fileno +f = open(2) print(f) # file read @@ -51,6 +52,34 @@ f = open(temp_dir + "/test", "r") print(f.read()) f.close() +# file finaliser, also see vfs_fat_finaliser.py +names = [temp_dir + "/x%d" % i for i in range(4)] +basefd = temp_dir + "/nextfd1" +nextfd = temp_dir + "/nextfd2" + +with open(basefd, "w") as f: + base_file_no = f.fileno() + +for i in range(1024): # move GC head forwards by allocating a lot of single blocks + [] + + +def write_files_without_closing(): + for n in names: + open(n, "w").write(n) + sorted(list(range(128)), key=lambda x: x) # use up Python and C stack so f is really gone + + +write_files_without_closing() +gc.collect() + +with open(nextfd, "w") as f: + next_file_no = f.fileno() + print("next_file_no <= base_file_no", next_file_no <= base_file_no) + +for n in names + [basefd, nextfd]: + uos.remove(n) + # rename uos.rename(temp_dir + "/test", temp_dir + "/test2") print(uos.listdir(temp_dir)) diff --git a/tests/extmod/vfs_posix.py.exp b/tests/extmod/vfs_posix.py.exp index eb9ab43106..99922e621d 100644 --- a/tests/extmod/vfs_posix.py.exp +++ b/tests/extmod/vfs_posix.py.exp @@ -4,6 +4,7 @@ True hello +next_file_no <= base_file_no True ['test2'] ['test2'] diff --git a/tests/extmod/vfs_posix_ilistdir_del.py b/tests/extmod/vfs_posix_ilistdir_del.py new file mode 100644 index 0000000000..edb50dfd62 --- /dev/null +++ b/tests/extmod/vfs_posix_ilistdir_del.py @@ -0,0 +1,70 @@ +# Test ilistdir __del__ for VfsPosix. +import gc + +try: + import os + + os.VfsPosix +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit + + +def test(testdir): + vfs = os.VfsPosix(testdir) + vfs.mkdir("/test_d1") + vfs.mkdir("/test_d2") + vfs.mkdir("/test_d3") + + for i in range(10): + print(i) + + # We want to partially iterate the ilistdir iterator to leave it in an + # open state, which will then test the finaliser when it's garbage collected. + idir = vfs.ilistdir("/") + print(any(idir)) + + # Alternate way of partially iterating the ilistdir object, modifying the + # filesystem while it's open. + for dname, *_ in vfs.ilistdir("/"): + vfs.rmdir(dname) + break + vfs.mkdir(dname) + + # Also create a fully drained iterator and ensure trying to re-use it + # throws the correct exception. + idir_emptied = vfs.ilistdir("/") + l = list(idir_emptied) + print(len(l)) + try: + next(idir_emptied) + except StopIteration: + pass + + gc.collect() + + # Create and delete a file, try to flush out any filesystem + # corruption that may be caused over the loops. + vfs.open("/test", "w").close() + vfs.remove("/test") + + +# We need an empty directory for testing. +# Skip the test if it already exists. +temp_dir = "vfs_posix_ilistdir_del_test_dir" +try: + os.stat(temp_dir) + print("SKIP") + raise SystemExit +except OSError: + pass + +os.mkdir(temp_dir) + +test(temp_dir) + +# Remove tempdir. +for td in os.listdir(temp_dir): + os.rmdir("/".join((temp_dir, td))) + +os.rmdir(temp_dir) diff --git a/tests/extmod/vfs_posix_ilistdir_del.py.exp b/tests/extmod/vfs_posix_ilistdir_del.py.exp new file mode 100644 index 0000000000..c30ba41326 --- /dev/null +++ b/tests/extmod/vfs_posix_ilistdir_del.py.exp @@ -0,0 +1,30 @@ +0 +True +3 +1 +True +3 +2 +True +3 +3 +True +3 +4 +True +3 +5 +True +3 +6 +True +3 +7 +True +3 +8 +True +3 +9 +True +3 diff --git a/tests/extmod/vfs_posix_ilistdir_filter.py b/tests/extmod/vfs_posix_ilistdir_filter.py new file mode 100644 index 0000000000..c32d124971 --- /dev/null +++ b/tests/extmod/vfs_posix_ilistdir_filter.py @@ -0,0 +1,47 @@ +# Test ilistdir filter of . and .. for VfsPosix. + +try: + import os + + os.VfsPosix +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit + + +def test(testdir): + vfs = os.VfsPosix(testdir) + + dirs = [".a", "..a", "...a", "a.b", "a..b"] + + for dir in dirs: + vfs.mkdir(dir) + + dirs = [] + for entry in vfs.ilistdir("/"): + dirs.append(entry[0]) + dirs.sort() + + print(dirs) + + +# We need an empty directory for testing. +# Skip the test if it already exists. +temp_dir = "vfs_posix_ilistdir_filter_test_dir" +try: + os.stat(temp_dir) + print("SKIP") + raise SystemExit +except OSError: + pass + +os.mkdir(temp_dir) + +try: + test(temp_dir) +finally: + # Remove tempdir. + for td in os.listdir(temp_dir): + os.rmdir("/".join((temp_dir, td))) + + os.rmdir(temp_dir) diff --git a/tests/extmod/vfs_posix_ilistdir_filter.py.exp b/tests/extmod/vfs_posix_ilistdir_filter.py.exp new file mode 100644 index 0000000000..2f51073836 --- /dev/null +++ b/tests/extmod/vfs_posix_ilistdir_filter.py.exp @@ -0,0 +1 @@ +['...a', '..a', '.a', 'a..b', 'a.b'] diff --git a/tests/float/cmath_dunder.py b/tests/float/cmath_dunder.py new file mode 100644 index 0000000000..909894d9f8 --- /dev/null +++ b/tests/float/cmath_dunder.py @@ -0,0 +1,21 @@ +# test that cmath functions support user classes with __float__ and __complex__ + +try: + import cmath +except ImportError: + print("SKIP") + raise SystemExit + + +class TestFloat: + def __float__(self): + return 1.0 + + +class TestComplex: + def __complex__(self): + return complex(10, 1) + + +for clas in TestFloat, TestComplex: + print("%.5g" % cmath.phase(clas())) diff --git a/tests/float/complex1.py b/tests/float/complex1.py index 139bb0c509..f4107a1390 100644 --- a/tests/float/complex1.py +++ b/tests/float/complex1.py @@ -4,9 +4,19 @@ print(complex(1)) print(complex(1.2)) print(complex(1.2j)) +print(complex("j")) +print(complex("J")) print(complex("1")) print(complex("1.2")) print(complex("1.2j")) +print(complex("1+j")) +print(complex("1+2j")) +print(complex("-1-2j")) +print(complex("+1-2j")) +print(complex(" -1-2j ")) +print(complex(" +1-2j ")) +print(complex("nanj")) +print(complex("nan-infj")) print(complex(1, 2)) print(complex(1j, 2j)) @@ -72,6 +82,13 @@ print(float("-nan") * 1j) print(float("inf") * (1 + 1j)) print(float("-inf") * (1 + 1j)) +# malformed complex strings +for test in ("1+2", "1j+2", "1+2j+3", "1+2+3j", "1 + 2j"): + try: + complex(test) + except ValueError: + print("ValueError", test) + # can't assign to attributes try: (1j).imag = 0 diff --git a/tests/float/complex_dunder.py b/tests/float/complex_dunder.py new file mode 100644 index 0000000000..975d829b47 --- /dev/null +++ b/tests/float/complex_dunder.py @@ -0,0 +1,46 @@ +# test __complex__ function support + + +class TestFloat: + def __float__(self): + return 1.0 + + +class TestComplex: + def __complex__(self): + return 1j + 10 + + +class TestStrComplex: + def __complex__(self): + return "a" + + +class TestNonComplex: + def __complex__(self): + return 6 + + +class Test: + pass + + +print(complex(TestFloat())) +print(complex(TestComplex())) + +try: + print(complex(TestStrComplex())) +except TypeError: + print("TypeError") + + +try: + print(complex(TestNonComplex())) +except TypeError: + print("TypeError") + + +try: + print(complex(Test())) +except TypeError: + print("TypeError") diff --git a/tests/float/float_dunder.py b/tests/float/float_dunder.py new file mode 100644 index 0000000000..1cd03db524 --- /dev/null +++ b/tests/float/float_dunder.py @@ -0,0 +1,42 @@ +# test __float__ function support + + +class TestFloat: + def __float__(self): + return 10.0 + + +class TestStrFloat: + def __float__(self): + return "a" + + +class TestNonFloat: + def __float__(self): + return 6 + + +class Test: + pass + + +print("%.1f" % float(TestFloat())) +print("%.1f" % TestFloat()) + + +try: + print(float(TestStrFloat())) +except TypeError: + print("TypeError") + + +try: + print(float(TestNonFloat())) +except TypeError: + print("TypeError") + + +try: + print(float(Test())) +except TypeError: + print("TypeError") diff --git a/tests/float/float_format.py b/tests/float/float_format.py index 4c8a217567..98ed0eb096 100644 --- a/tests/float/float_format.py +++ b/tests/float/float_format.py @@ -17,3 +17,11 @@ print("%.2e" % float("9" * 40 + "e-21")) # check a case that would render negative digit values, eg ")" characters # the string is converted back to a float to check for no illegal characters float("%.23e" % 1e-80) + +# Check a problem with malformed "e" format numbers on the edge of 1.0e-X. +for r in range(38): + s = "%.12e" % float("1e-" + str(r)) + # It may format as 1e-r, or 9.999...e-(r+1), both are OK. + # But formatting as 0.999...e-r is NOT ok. + if s[0] == "0": + print("FAIL:", s) diff --git a/tests/float/float_format_ints_doubleprec.py b/tests/float/float_format_ints_doubleprec.py index 57899d6d65..67101d3e45 100644 --- a/tests/float/float_format_ints_doubleprec.py +++ b/tests/float/float_format_ints_doubleprec.py @@ -13,3 +13,6 @@ v1 = 0x54B249AD2594C37D # 1e100 v2 = 0x6974E718D7D7625A # 1e200 print("{:.12e}".format(array.array("d", v1.to_bytes(8, sys.byteorder))[0])) print("{:.12e}".format(array.array("d", v2.to_bytes(8, sys.byteorder))[0])) + +for i in range(300): + print(float("1e" + str(i))) diff --git a/tests/float/math_domain.py b/tests/float/math_domain.py index 0c25dc08b6..606a655284 100644 --- a/tests/float/math_domain.py +++ b/tests/float/math_domain.py @@ -17,6 +17,7 @@ for name, f, args in ( ("trunc", math.trunc, ()), ("sqrt", math.sqrt, (-1, 0)), ("exp", math.exp, ()), + ("log", math.log, ()), ("sin", math.sin, ()), ("cos", math.cos, ()), ("tan", math.tan, ()), @@ -27,25 +28,45 @@ for name, f, args in ( ("radians", math.radians, ()), ("degrees", math.degrees, ()), ): - for x in args + (inf, nan): + for x in args + (inf, -inf, nan): try: - ans = f(x) - print("%.4f" % ans) + ans = "%.4f" % f(x) except ValueError: - print(name, "ValueError") + ans = "ValueError" except OverflowError: - print(name, "OverflowError") + ans = "OverflowError" + print("%s(%.4f) = %s" % (name, x, ans)) # double argument functions for name, f, args in ( - ("pow", math.pow, ((0, 2), (-1, 2), (0, -1), (-1, 2.3), (nan, 0), (1, nan))), + ( + "pow", + math.pow, + ( + (0, 2), + (-1, 2), + (0, -1), + (-1, 2.3), + (0.5, inf), + (-0.5, inf), + (0.5, -inf), + (-0.5, -inf), + (1.5, inf), + (-1.5, inf), + (1.5, -inf), + (-1.5, -inf), + (nan, 0), + (1, nan), + ), + ), + ("log", math.log, ()), ("fmod", math.fmod, ((1.2, inf), (1.2, -inf), (1.2, 0), (inf, 1.2))), ("atan2", math.atan2, ((0, 0), (-inf, inf), (-inf, -inf), (inf, -inf))), ("copysign", math.copysign, ()), ): for x in args + ((0, inf), (inf, 0), (inf, inf), (inf, nan), (nan, inf), (nan, nan)): try: - ans = f(*x) - print("%.4f" % ans) + ans = "%.4f" % f(*x) except ValueError: - print(name, "ValueError") + ans = "ValueError" + print("%s(%.4f, %.4f) = %s" % (name, x[0], x[1], ans)) diff --git a/tests/float/math_domain_special.py b/tests/float/math_domain_special.py index 880594dce2..ddabf178a3 100644 --- a/tests/float/math_domain_special.py +++ b/tests/float/math_domain_special.py @@ -29,9 +29,9 @@ for name, f, args in ( ): for x in args + (inf, -inf, nan): try: - ans = f(x) - print("%.4f" % ans) + ans = "%.4f" % f(x) except ValueError: - print(name, "ValueError") + ans = "ValueError" except OverflowError: - print(name, "OverflowError") + ans = "OverflowError" + print("%s(%.4f) = %s" % (name, x, ans)) diff --git a/tests/float/math_dunder.py b/tests/float/math_dunder.py new file mode 100644 index 0000000000..33ea7f7c1c --- /dev/null +++ b/tests/float/math_dunder.py @@ -0,0 +1,15 @@ +# test that math functions support user classes with __float__ + +try: + import math +except ImportError: + print("SKIP") + raise SystemExit + + +class TestFloat: + def __float__(self): + return 1.0 + + +print("%.5g" % math.exp(TestFloat())) diff --git a/tests/float/math_fun.py b/tests/float/math_fun.py index f43216c6fb..9920caf32b 100644 --- a/tests/float/math_fun.py +++ b/tests/float/math_fun.py @@ -38,12 +38,12 @@ functions = [ ] for function_name, function, test_vals in functions: - print(function_name) for value in test_vals: try: - print("{:.5g}".format(function(value))) + ans = "{:.5g}".format(function(value)) except ValueError as e: - print(str(e)) + ans = str(e) + print("{}({:.5g}) = {}".format(function_name, value, ans)) tuple_functions = [ ("frexp", frexp, test_values), @@ -51,10 +51,9 @@ tuple_functions = [ ] for function_name, function, test_vals in tuple_functions: - print(function_name) for value in test_vals: x, y = function(value) - print("{:.5g} {:.5g}".format(x, y)) + print("{}({:.5g}) = ({:.5g}, {:.5g})".format(function_name, value, x, y)) binary_functions = [ ( @@ -123,9 +122,9 @@ binary_functions = [ ] for function_name, function, test_vals in binary_functions: - print(function_name) for value1, value2 in test_vals: try: - print("{:.5g}".format(function(value1, value2))) + ans = "{:.5g}".format(function(value1, value2)) except (ValueError, ZeroDivisionError) as e: - print(type(e)) + ans = type(e) + print("{}({:.5g}, {:.5g}) = {}".format(function_name, value1, value2, ans)) diff --git a/tests/float/math_fun_special.py b/tests/float/math_fun_special.py index 8f51b29acb..9c0fe3555f 100644 --- a/tests/float/math_fun_special.py +++ b/tests/float/math_fun_special.py @@ -52,9 +52,9 @@ functions = [ ] for function_name, function, test_vals in functions: - print(function_name) for value in test_vals: try: - print("{:.4g}".format(function(value))) + ans = "{:.4g}".format(function(value)) except ValueError as e: - print(str(e)) + ans = str(e) + print("{}({:.4g}) = {}".format(function_name, value, ans)) diff --git a/tests/float/string_format_modulo.py b/tests/float/string_format_modulo.py index 0944615381..3c206b7393 100644 --- a/tests/float/string_format_modulo.py +++ b/tests/float/string_format_modulo.py @@ -41,7 +41,10 @@ print(("%.40f" % 1e-300)[:2]) print(("%.40g" % 1e-1)[:2]) print(("%.40g" % 1e-2)[:2]) print(("%.40g" % 1e-3)[:2]) -print(("%.40g" % 1e-4)[:2]) +# Under Appveyor Release builds, 1e-4 was being formatted as 9.99999...e-5 +# instead of 0.0001. (Interestingly, it formatted correctly for the Debug +# build). Avoid the edge case. +print(("%.40g" % 1.1e-4)[:2]) print("%.0g" % 1) # 0 precision 'g' diff --git a/tests/frozen/README.md b/tests/frozen/README.md new file mode 100644 index 0000000000..bd786d5a3c --- /dev/null +++ b/tests/frozen/README.md @@ -0,0 +1,2 @@ +This is a .mpy built against the current .mpy version that can be used to test +freezing without a dependency on mpy-cross. diff --git a/tests/import/module_dict.py b/tests/import/module_dict.py new file mode 100644 index 0000000000..431d80b3b4 --- /dev/null +++ b/tests/import/module_dict.py @@ -0,0 +1,17 @@ +# test __dict__ attribute of a user module + +import sys + +if not hasattr(sys, "__dict__"): + print("SKIP") + raise SystemExit + + +import import1b + +# dict of a user module (read/write) +print(import1b.var) +print(import1b.__dict__["var"]) +import1b.__dict__["var"] = "hello" +print(import1b.var) +print(import1b.__dict__["var"]) diff --git a/tests/inlineasm/asmdata.py b/tests/inlineasm/asmdata.py new file mode 100644 index 0000000000..bbd20c9186 --- /dev/null +++ b/tests/inlineasm/asmdata.py @@ -0,0 +1,16 @@ +# test the "data" directive + + +@micropython.asm_thumb +def ret_num(r0) -> uint: + lsl(r0, r0, 2) + mov(r1, pc) + add(r0, r0, r1) + ldr(r0, [r0, 4]) + b(HERE) + data(4, 0x12345678, 0x20000000, 0x40000000, 0x7FFFFFFF + 1, (1 << 32) - 2) + label(HERE) + + +for i in range(5): + print(hex(ret_num(i))) diff --git a/tests/inlineasm/asmdata.py.exp b/tests/inlineasm/asmdata.py.exp new file mode 100644 index 0000000000..502c04f993 --- /dev/null +++ b/tests/inlineasm/asmdata.py.exp @@ -0,0 +1,5 @@ +0x12345678 +0x20000000 +0x40000000 +0x80000000 +0xfffffffe diff --git a/tests/micropython/builtin_execfile.py b/tests/micropython/builtin_execfile.py new file mode 100644 index 0000000000..8a8ce79f78 --- /dev/null +++ b/tests/micropython/builtin_execfile.py @@ -0,0 +1,70 @@ +# Test builtin execfile function using VFS. + +try: + import uio, uos + + execfile + uio.IOBase + uos.mount +except (ImportError, NameError, AttributeError): + print("SKIP") + raise SystemExit + + +class File(uio.IOBase): + def __init__(self, data): + self.data = data + self.off = 0 + + def ioctl(self, request, arg): + return 0 + + def readinto(self, buf): + buf[:] = memoryview(self.data)[self.off : self.off + len(buf)] + self.off += len(buf) + return len(buf) + + +class Filesystem: + def __init__(self, files): + self.files = files + + def mount(self, readonly, mkfs): + print("mount", readonly, mkfs) + + def umount(self): + print("umount") + + def open(self, file, mode): + print("open", file, mode) + if file not in self.files: + raise OSError(2) # ENOENT + return File(self.files[file]) + + +# First umount any existing mount points the target may have. +try: + uos.umount("/") +except OSError: + pass +for path in uos.listdir("/"): + uos.umount("/" + path) + +# Create and mount the VFS object. +files = { + "/test.py": "print(123)", +} +fs = Filesystem(files) +uos.mount(fs, "/test_mnt") + +# Test execfile with a file that doesn't exist. +try: + execfile("/test_mnt/noexist.py") +except OSError: + print("OSError") + +# Test execfile with a file that does exist. +execfile("/test_mnt/test.py") + +# Unmount the VFS object. +uos.umount(fs) diff --git a/tests/micropython/builtin_execfile.py.exp b/tests/micropython/builtin_execfile.py.exp new file mode 100644 index 0000000000..1d5d8ee571 --- /dev/null +++ b/tests/micropython/builtin_execfile.py.exp @@ -0,0 +1,6 @@ +mount False False +open /noexist.py rb +OSError +open /test.py rb +123 +umount diff --git a/tests/micropython/import_mpy_native.py b/tests/micropython/import_mpy_native.py index 0dfbca9086..8417f543ce 100644 --- a/tests/micropython/import_mpy_native.py +++ b/tests/micropython/import_mpy_native.py @@ -1,16 +1,18 @@ -# test importing of .mpy files with native code (x64 only) +# test importing of .mpy files with native code try: - import sys, uio, uos + import sys, io, os - uio.IOBase - uos.mount + sys.implementation._mpy + io.IOBase + os.mount except (ImportError, AttributeError): print("SKIP") raise SystemExit mpy_arch = sys.implementation._mpy >> 8 -if mpy_arch == 0: +if mpy_arch >> 2 == 0: + # This system does not support .mpy files containing native code print("SKIP") raise SystemExit @@ -50,11 +52,11 @@ class UserFS: # these are the test .mpy files -valid_header = bytes([ord("C"), 6, mpy_arch, 31]) +valid_header = bytes([77, 6, mpy_arch, 31]) # fmt: off user_files = { - # bad architecture - '/mod0.mpy': b'C\x06\xfc\x1f', + # bad architecture (mpy_arch needed for sub-version) + '/mod0.mpy': bytes([77, 6, 0xfc | mpy_arch, 31]), # test loading of viper and asm '/mod1.mpy': valid_header + ( @@ -99,7 +101,7 @@ user_files = { b'\x22' # 4 bytes, no children, viper code b'\x00\x00\x00\x00' # dummy machine code - b'\xe0' # scope_flags: VIPERBSS | VIPERRODATA | VIPERRELOC + b'\x70' # scope_flags: VIPERBSS | VIPERRODATA | VIPERRELOC b'\x06\x04' # rodata=6 bytes, bss=4 bytes b'rodata' # rodata content b'\x03\x01\x00' # dummy relocation of rodata @@ -109,7 +111,7 @@ user_files = { # create and mount a user filesystem uos.mount(UserFS(user_files), "/userfs") -sys.path.append("/userfs") +usys.path.append("/userfs") # import .mpy files from the user filesystem for i in range(len(user_files)): @@ -122,4 +124,4 @@ for i in range(len(user_files)): # unmount and undo path addition uos.umount("/userfs") -sys.path.pop() +usys.path.pop() diff --git a/tests/micropython/import_mpy_native_gc.py b/tests/micropython/import_mpy_native_gc.py index 1234184aed..e18720fb31 100644 --- a/tests/micropython/import_mpy_native_gc.py +++ b/tests/micropython/import_mpy_native_gc.py @@ -46,24 +46,26 @@ class UserFS: # Pre-compiled examples/natmod/features0 example for various architectures, keyed -# by the required value of sys.implementation._mpy. +# by the required value of sys.implementation._mpy (without sub-version). features0_file_contents = { # -march=x64 - 0x806: b'C\x06\x08\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'M\x06\x09\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\x10\x11$\r&\xa3 \x01"\xff', # -march=armv6m - 0x1006: b'C\x06\x14\x1f\x02\x004build/features0.native.mpy\x00\x12factorial\x00\x88"\x1a\xe0\x00\x00\x13\xb5\nK\nJ{D\x9cX\x02!\xe3h\x98G\x03F\x01 3\xb9\x02!#i\x01\x93\x02\xb0\xbd\xe8\x10@\x18GXC\x01;\xf4\xe7\x00\xbfn\x00\x00\x00\x00\x00\x00\x00\xf8\xb5\nN\nK~D\xf4XChgiXh\xb8G\x05F\x07K\x08I\xf2XyDP\x88ck\x98G(F\xb8G h\xf8\xbd\x00\xbf:\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"M\x06\x11\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\x10\x11<\r>\xa38\x01:\xff", } # Populate armv7m-derived archs based on armv6m. for arch in (0x1406, 0x1806, 0x1C06, 0x2006): features0_file_contents[arch] = features0_file_contents[0x1006] -if sys.implementation._mpy not in features0_file_contents: +# Check that a .mpy exists for the target (ignore sub-version in lookup). +sys_implementation_mpy = sys.implementation._mpy & ~(3 << 8) +if sys_implementation_mpy not in features0_file_contents: print("SKIP") raise SystemExit # These are the test .mpy files. -user_files = {"/features0.mpy": features0_file_contents[sys.implementation._mpy]} +user_files = {"/features0.mpy": features0_file_contents[sys_implementation_mpy]} # Create and mount a user filesystem. uos.mount(UserFS(user_files), "/userfs") diff --git a/tests/micropython/native_fun_attrs.py b/tests/micropython/native_fun_attrs.py new file mode 100644 index 0000000000..e61c869755 --- /dev/null +++ b/tests/micropython/native_fun_attrs.py @@ -0,0 +1,25 @@ +# test native function attributes + + +def f(): + pass + + +if not hasattr(f, "__name__"): + print("SKIP") + raise SystemExit + + +@micropython.native +def native_f(): + pass + + +print(type(native_f.__name__)) +print(type(native_f.__globals__)) +print(native_f.__globals__ is globals()) + +try: + native_f.__name__ = None +except AttributeError: + print("AttributeError") diff --git a/tests/micropython/native_fun_attrs.py.exp b/tests/micropython/native_fun_attrs.py.exp new file mode 100644 index 0000000000..8be35e2f69 --- /dev/null +++ b/tests/micropython/native_fun_attrs.py.exp @@ -0,0 +1,4 @@ + + +True +AttributeError diff --git a/tests/micropython/viper_addr.py b/tests/micropython/viper_addr.py index 84bc6c002e..8e79fadb2a 100644 --- a/tests/micropython/viper_addr.py +++ b/tests/micropython/viper_addr.py @@ -21,7 +21,7 @@ def memsum(src: ptr8, n: int) -> int: # create array and get its address -ar = bytearray("0000") +ar = bytearray(b"0000") addr = get_addr(ar) print(type(ar)) print(type(addr)) diff --git a/tests/micropython/viper_misc.py b/tests/micropython/viper_misc.py index f9267f65ca..2659032df6 100644 --- a/tests/micropython/viper_misc.py +++ b/tests/micropython/viper_misc.py @@ -1,3 +1,5 @@ +# Miscellaneous viper tests. + import micropython @@ -52,8 +54,7 @@ def viper_4args(a: int, b: int, c: int, d: int) -> int: return a + b + c + d -# viper call with 4 args not yet supported -# print(viper_4args(1, 2, 3, 4)) +print(viper_4args(1, 2, 3, 4)) # a local (should have automatic type int) @@ -73,96 +74,3 @@ def viper_no_annotation(x, y): print(viper_no_annotation(4, 5)) - - -# a for loop -@micropython.viper -def viper_for(a: int, b: int) -> int: - total = 0 - for x in range(a, b): - total += x - return total - - -print(viper_for(10, 10000)) - - -# accessing a global -@micropython.viper -def viper_access_global(): - global gl - gl = 1 - return gl - - -print(viper_access_global(), gl) - - -# calling print with object and int types -@micropython.viper -def viper_print(x, y: int): - print(x, y + 1) - - -viper_print(1, 2) - - -# convert constants to objects in tuple -@micropython.viper -def viper_tuple_consts(x): - return (x, 1, False, True) - - -print(viper_tuple_consts(0)) - - -# making a tuple from an object and an int -@micropython.viper -def viper_tuple(x, y: int): - return (x, y + 1) - - -print(viper_tuple(1, 2)) - - -# making a list from an object and an int -@micropython.viper -def viper_list(x, y: int): - return [x, y + 1] - - -print(viper_list(1, 2)) - - -# making a set from an object and an int -@micropython.viper -def viper_set(x, y: int): - return {x, y + 1} - - -print(sorted(list(viper_set(1, 2)))) - - -# raising an exception -@micropython.viper -def viper_raise(x: int): - raise OSError(x) - - -try: - viper_raise(1) -except OSError as e: - print(repr(e)) - - -# calling GC after defining the function -@micropython.viper -def viper_gc() -> int: - return 1 - - -print(viper_gc()) -import gc - -gc.collect() -print(viper_gc()) diff --git a/tests/micropython/viper_misc.py.exp b/tests/micropython/viper_misc.py.exp index e4462771b4..922ceb9fef 100644 --- a/tests/micropython/viper_misc.py.exp +++ b/tests/micropython/viper_misc.py.exp @@ -3,15 +3,6 @@ 0 Ellipsis 6 +10 7 20 -49994955 -1 1 -1 3 -(0, 1, False, True) -(1, 3) -[1, 3] -[1, 3] -OSError(1,) -1 -1 diff --git a/tests/micropython/viper_misc3.py b/tests/micropython/viper_misc3.py new file mode 100644 index 0000000000..7b211e5dd8 --- /dev/null +++ b/tests/micropython/viper_misc3.py @@ -0,0 +1,96 @@ +# Miscellaneous viper tests. + +import micropython + + +# a for loop +@micropython.viper +def viper_for(a: int, b: int) -> int: + total = 0 + for x in range(a, b): + total += x + return total + + +print(viper_for(10, 10000)) + + +# accessing a global +@micropython.viper +def viper_access_global(): + global gl + gl = 1 + return gl + + +print(viper_access_global(), gl) + + +# calling print with object and int types +@micropython.viper +def viper_print(x, y: int): + print(x, y + 1) + + +viper_print(1, 2) + + +# convert constants to objects in tuple +@micropython.viper +def viper_tuple_consts(x): + return (x, 1, False, True) + + +print(viper_tuple_consts(0)) + + +# making a tuple from an object and an int +@micropython.viper +def viper_tuple(x, y: int): + return (x, y + 1) + + +print(viper_tuple(1, 2)) + + +# making a list from an object and an int +@micropython.viper +def viper_list(x, y: int): + return [x, y + 1] + + +print(viper_list(1, 2)) + + +# making a set from an object and an int +@micropython.viper +def viper_set(x, y: int): + return {x, y + 1} + + +print(sorted(list(viper_set(1, 2)))) + + +# raising an exception +@micropython.viper +def viper_raise(x: int): + raise OSError(x) + + +try: + viper_raise(1) +except OSError as e: + print(repr(e)) + + +# calling GC after defining the function +@micropython.viper +def viper_gc() -> int: + return 1 + + +print(viper_gc()) +import gc + +gc.collect() +print(viper_gc()) diff --git a/tests/micropython/viper_misc3.py.exp b/tests/micropython/viper_misc3.py.exp new file mode 100644 index 0000000000..b12807b2f3 --- /dev/null +++ b/tests/micropython/viper_misc3.py.exp @@ -0,0 +1,10 @@ +49994955 +1 1 +1 3 +(0, 1, False, True) +(1, 3) +[1, 3] +[1, 3] +OSError(1,) +1 +1 diff --git a/tests/micropython/viper_storeattr.py b/tests/micropython/viper_storeattr.py new file mode 100644 index 0000000000..65f68b6e22 --- /dev/null +++ b/tests/micropython/viper_storeattr.py @@ -0,0 +1,25 @@ +# test storing an attribute with a value of different viper types + + +class X: + def __str__(self): + return "X" + + +x = X() + + +@micropython.viper +def a(): + x.i0 = 0 + x.i7 = 7 + x.s = "hello" + x.o = x + + +a() + +print(x.i0) +print(x.i7) +print(x.s) +print(x.o) diff --git a/tests/micropython/viper_storeattr.py.exp b/tests/micropython/viper_storeattr.py.exp new file mode 100644 index 0000000000..8e6a6cfda9 --- /dev/null +++ b/tests/micropython/viper_storeattr.py.exp @@ -0,0 +1,4 @@ +0 +7 +hello +X diff --git a/tests/micropython/viper_subscr_multi.py b/tests/micropython/viper_subscr_multi.py index 1561e5534d..a2baba2411 100644 --- a/tests/micropython/viper_subscr_multi.py +++ b/tests/micropython/viper_subscr_multi.py @@ -6,15 +6,24 @@ def f1(b: ptr8): b[0] += b[1] +b = bytearray(b"\x01\x02") +f1(b) +print(b) + + @micropython.viper def f2(b: ptr8, i: int): b[0] += b[i] -b = bytearray(b"\x01\x02") -f1(b) -print(b) - b = bytearray(b"\x01\x02") f2(b, 1) print(b) + + +@micropython.viper +def f3(b: ptr8) -> int: + return b[0] << 24 | b[1] << 16 | b[2] << 8 | b[3] + + +print(hex(f3(b"\x01\x02\x03\x04"))) diff --git a/tests/micropython/viper_subscr_multi.py.exp b/tests/micropython/viper_subscr_multi.py.exp index a2c298bb16..ea644c046d 100644 --- a/tests/micropython/viper_subscr_multi.py.exp +++ b/tests/micropython/viper_subscr_multi.py.exp @@ -1,2 +1,3 @@ bytearray(b'\x03\x02') bytearray(b'\x03\x02') +0x1020304 diff --git a/tests/misc/cexample_class.py b/tests/misc/cexample_class.py new file mode 100644 index 0000000000..6b8718ad8c --- /dev/null +++ b/tests/misc/cexample_class.py @@ -0,0 +1,24 @@ +# test custom native class + +try: + import cexample + import time +except ImportError: + print("SKIP") + raise SystemExit + + +SLEEP_MS = 100 +TOLERANCE_MS = 20 + +timer = cexample.Timer() + +t_start = timer.time() + +time.sleep_ms(100) + +t_end = timer.time() + +print(timer) +print(0 <= t_start <= TOLERANCE_MS) +print(SLEEP_MS - TOLERANCE_MS <= t_end <= SLEEP_MS + TOLERANCE_MS) diff --git a/tests/misc/cexample_class.py.exp b/tests/misc/cexample_class.py.exp new file mode 100644 index 0000000000..b9a06602a3 --- /dev/null +++ b/tests/misc/cexample_class.py.exp @@ -0,0 +1,3 @@ + +True +True diff --git a/tests/misc/cexample_module.py b/tests/misc/cexample_module.py new file mode 100644 index 0000000000..c1da2ecf7a --- /dev/null +++ b/tests/misc/cexample_module.py @@ -0,0 +1,16 @@ +# test custom builtin module + +try: + import cexample +except ImportError: + print("SKIP") + raise SystemExit + +print(cexample) +print(cexample.__name__) + +d = dir(cexample) +d.index("add_ints") +d.index("Timer") + +print(cexample.add_ints(1, 3)) diff --git a/tests/misc/cexample_module.py.exp b/tests/misc/cexample_module.py.exp new file mode 100644 index 0000000000..bb305060e9 --- /dev/null +++ b/tests/misc/cexample_module.py.exp @@ -0,0 +1,3 @@ + +cexample +4 diff --git a/tests/misc/sys_settrace_generator.py.exp b/tests/misc/sys_settrace_generator.py.exp index a83450afe3..c1d13aac86 100644 --- a/tests/misc/sys_settrace_generator.py.exp +++ b/tests/misc/sys_settrace_generator.py.exp @@ -1,195 +1,195 @@ ### trace_handler::main event: call 0: @__main__:test_generator => sys_settrace_generator.py:41 - 1: @__main__: => sys_settrace_generator.py:69 + 1: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: line 0: @__main__:test_generator => sys_settrace_generator.py:42 - 1: @__main__: => sys_settrace_generator.py:69 + 1: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: line 0: @__main__:test_generator => sys_settrace_generator.py:48 - 1: @__main__: => sys_settrace_generator.py:69 + 1: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: line 0: @__main__:test_generator => sys_settrace_generator.py:49 - 1: @__main__: => sys_settrace_generator.py:69 + 1: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: line 0: @__main__:test_generator => sys_settrace_generator.py:50 - 1: @__main__: => sys_settrace_generator.py:69 + 1: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: line - 0: @__main__:test_generator => sys_settrace_generator.py:52 - 1: @__main__: => sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:51 + 1: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: call 0: @__main__:make_gen => sys_settrace_generator.py:42 - 1: @__main__:test_generator => sys_settrace_generator.py:52 - 2: @__main__: => sys_settrace_generator.py:69 + 1: @__main__:test_generator => sys_settrace_generator.py:51 + 2: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: line 0: @__main__:make_gen => sys_settrace_generator.py:43 - 1: @__main__:test_generator => sys_settrace_generator.py:52 - 2: @__main__: => sys_settrace_generator.py:69 + 1: @__main__:test_generator => sys_settrace_generator.py:51 + 2: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: return 0: @__main__:make_gen => sys_settrace_generator.py:43 - 1: @__main__:test_generator => sys_settrace_generator.py:52 - 2: @__main__: => sys_settrace_generator.py:69 + 1: @__main__:test_generator => sys_settrace_generator.py:51 + 2: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: line - 0: @__main__:test_generator => sys_settrace_generator.py:56 - 1: @__main__: => sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:54 + 1: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: call 0: @__main__:make_gen => sys_settrace_generator.py:43 - 1: @__main__:test_generator => sys_settrace_generator.py:56 - 2: @__main__: => sys_settrace_generator.py:69 + 1: @__main__:test_generator => sys_settrace_generator.py:54 + 2: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: line 0: @__main__:make_gen => sys_settrace_generator.py:43 - 1: @__main__:test_generator => sys_settrace_generator.py:56 - 2: @__main__: => sys_settrace_generator.py:69 + 1: @__main__:test_generator => sys_settrace_generator.py:54 + 2: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: line 0: @__main__:make_gen => sys_settrace_generator.py:44 - 1: @__main__:test_generator => sys_settrace_generator.py:56 - 2: @__main__: => sys_settrace_generator.py:69 + 1: @__main__:test_generator => sys_settrace_generator.py:54 + 2: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: return 0: @__main__:make_gen => sys_settrace_generator.py:44 - 1: @__main__:test_generator => sys_settrace_generator.py:56 - 2: @__main__: => sys_settrace_generator.py:69 + 1: @__main__:test_generator => sys_settrace_generator.py:54 + 2: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: call 0: @__main__:make_gen => sys_settrace_generator.py:44 - 1: @__main__:test_generator => sys_settrace_generator.py:56 - 2: @__main__: => sys_settrace_generator.py:69 + 1: @__main__:test_generator => sys_settrace_generator.py:54 + 2: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: line 0: @__main__:make_gen => sys_settrace_generator.py:44 - 1: @__main__:test_generator => sys_settrace_generator.py:56 - 2: @__main__: => sys_settrace_generator.py:69 + 1: @__main__:test_generator => sys_settrace_generator.py:54 + 2: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: line 0: @__main__:make_gen => sys_settrace_generator.py:45 - 1: @__main__:test_generator => sys_settrace_generator.py:56 - 2: @__main__: => sys_settrace_generator.py:69 + 1: @__main__:test_generator => sys_settrace_generator.py:54 + 2: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: return 0: @__main__:make_gen => sys_settrace_generator.py:45 - 1: @__main__:test_generator => sys_settrace_generator.py:56 - 2: @__main__: => sys_settrace_generator.py:69 + 1: @__main__:test_generator => sys_settrace_generator.py:54 + 2: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: call 0: @__main__:make_gen => sys_settrace_generator.py:45 - 1: @__main__:test_generator => sys_settrace_generator.py:56 - 2: @__main__: => sys_settrace_generator.py:69 + 1: @__main__:test_generator => sys_settrace_generator.py:54 + 2: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: line 0: @__main__:make_gen => sys_settrace_generator.py:45 - 1: @__main__:test_generator => sys_settrace_generator.py:56 - 2: @__main__: => sys_settrace_generator.py:69 + 1: @__main__:test_generator => sys_settrace_generator.py:54 + 2: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: line 0: @__main__:make_gen => sys_settrace_generator.py:46 - 1: @__main__:test_generator => sys_settrace_generator.py:56 - 2: @__main__: => sys_settrace_generator.py:69 + 1: @__main__:test_generator => sys_settrace_generator.py:54 + 2: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: return 0: @__main__:make_gen => sys_settrace_generator.py:46 - 1: @__main__:test_generator => sys_settrace_generator.py:56 - 2: @__main__: => sys_settrace_generator.py:69 + 1: @__main__:test_generator => sys_settrace_generator.py:54 + 2: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: exception + 0: @__main__:test_generator => sys_settrace_generator.py:54 + 1: @__main__: => sys_settrace_generator.py:67 +### trace_handler::main event: line 0: @__main__:test_generator => sys_settrace_generator.py:56 - 1: @__main__: => sys_settrace_generator.py:69 + 1: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: line - 0: @__main__:test_generator => sys_settrace_generator.py:58 - 1: @__main__: => sys_settrace_generator.py:69 -### trace_handler::main event: line - 0: @__main__:test_generator => sys_settrace_generator.py:59 - 1: @__main__: => sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:57 + 1: @__main__: => sys_settrace_generator.py:67 test_generator 7 8 +### trace_handler::main event: line + 0: @__main__:test_generator => sys_settrace_generator.py:59 + 1: @__main__: => sys_settrace_generator.py:67 +### trace_handler::main event: line + 0: @__main__:test_generator => sys_settrace_generator.py:60 + 1: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: line 0: @__main__:test_generator => sys_settrace_generator.py:61 - 1: @__main__: => sys_settrace_generator.py:69 -### trace_handler::main event: line - 0: @__main__:test_generator => sys_settrace_generator.py:62 - 1: @__main__: => sys_settrace_generator.py:69 -### trace_handler::main event: line - 0: @__main__:test_generator => sys_settrace_generator.py:63 - 1: @__main__: => sys_settrace_generator.py:69 + 1: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: call 0: @__main__:make_gen => sys_settrace_generator.py:42 - 1: @__main__:test_generator => sys_settrace_generator.py:63 - 2: @__main__: => sys_settrace_generator.py:69 + 1: @__main__:test_generator => sys_settrace_generator.py:61 + 2: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: line 0: @__main__:make_gen => sys_settrace_generator.py:43 - 1: @__main__:test_generator => sys_settrace_generator.py:63 - 2: @__main__: => sys_settrace_generator.py:69 + 1: @__main__:test_generator => sys_settrace_generator.py:61 + 2: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: return 0: @__main__:make_gen => sys_settrace_generator.py:43 - 1: @__main__:test_generator => sys_settrace_generator.py:63 - 2: @__main__: => sys_settrace_generator.py:69 + 1: @__main__:test_generator => sys_settrace_generator.py:61 + 2: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: line - 0: @__main__:test_generator => sys_settrace_generator.py:63 - 1: @__main__: => sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:61 + 1: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: line - 0: @__main__:test_generator => sys_settrace_generator.py:64 - 1: @__main__: => sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:62 + 1: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: line - 0: @__main__:test_generator => sys_settrace_generator.py:63 - 1: @__main__: => sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:61 + 1: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: call 0: @__main__:make_gen => sys_settrace_generator.py:43 - 1: @__main__:test_generator => sys_settrace_generator.py:63 - 2: @__main__: => sys_settrace_generator.py:69 + 1: @__main__:test_generator => sys_settrace_generator.py:61 + 2: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: line 0: @__main__:make_gen => sys_settrace_generator.py:43 - 1: @__main__:test_generator => sys_settrace_generator.py:63 - 2: @__main__: => sys_settrace_generator.py:69 + 1: @__main__:test_generator => sys_settrace_generator.py:61 + 2: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: line 0: @__main__:make_gen => sys_settrace_generator.py:44 - 1: @__main__:test_generator => sys_settrace_generator.py:63 - 2: @__main__: => sys_settrace_generator.py:69 + 1: @__main__:test_generator => sys_settrace_generator.py:61 + 2: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: return 0: @__main__:make_gen => sys_settrace_generator.py:44 - 1: @__main__:test_generator => sys_settrace_generator.py:63 - 2: @__main__: => sys_settrace_generator.py:69 + 1: @__main__:test_generator => sys_settrace_generator.py:61 + 2: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: line - 0: @__main__:test_generator => sys_settrace_generator.py:63 - 1: @__main__: => sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:61 + 1: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: line - 0: @__main__:test_generator => sys_settrace_generator.py:64 - 1: @__main__: => sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:62 + 1: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: line - 0: @__main__:test_generator => sys_settrace_generator.py:63 - 1: @__main__: => sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:61 + 1: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: call 0: @__main__:make_gen => sys_settrace_generator.py:44 - 1: @__main__:test_generator => sys_settrace_generator.py:63 - 2: @__main__: => sys_settrace_generator.py:69 + 1: @__main__:test_generator => sys_settrace_generator.py:61 + 2: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: line 0: @__main__:make_gen => sys_settrace_generator.py:44 - 1: @__main__:test_generator => sys_settrace_generator.py:63 - 2: @__main__: => sys_settrace_generator.py:69 + 1: @__main__:test_generator => sys_settrace_generator.py:61 + 2: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: line 0: @__main__:make_gen => sys_settrace_generator.py:45 - 1: @__main__:test_generator => sys_settrace_generator.py:63 - 2: @__main__: => sys_settrace_generator.py:69 + 1: @__main__:test_generator => sys_settrace_generator.py:61 + 2: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: return 0: @__main__:make_gen => sys_settrace_generator.py:45 - 1: @__main__:test_generator => sys_settrace_generator.py:63 - 2: @__main__: => sys_settrace_generator.py:69 + 1: @__main__:test_generator => sys_settrace_generator.py:61 + 2: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: line - 0: @__main__:test_generator => sys_settrace_generator.py:63 - 1: @__main__: => sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:61 + 1: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: line - 0: @__main__:test_generator => sys_settrace_generator.py:64 - 1: @__main__: => sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:62 + 1: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: line - 0: @__main__:test_generator => sys_settrace_generator.py:63 - 1: @__main__: => sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:61 + 1: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: call 0: @__main__:make_gen => sys_settrace_generator.py:45 - 1: @__main__:test_generator => sys_settrace_generator.py:63 - 2: @__main__: => sys_settrace_generator.py:69 + 1: @__main__:test_generator => sys_settrace_generator.py:61 + 2: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: line 0: @__main__:make_gen => sys_settrace_generator.py:45 - 1: @__main__:test_generator => sys_settrace_generator.py:63 - 2: @__main__: => sys_settrace_generator.py:69 + 1: @__main__:test_generator => sys_settrace_generator.py:61 + 2: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: line 0: @__main__:make_gen => sys_settrace_generator.py:46 - 1: @__main__:test_generator => sys_settrace_generator.py:63 - 2: @__main__: => sys_settrace_generator.py:69 + 1: @__main__:test_generator => sys_settrace_generator.py:61 + 2: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: return 0: @__main__:make_gen => sys_settrace_generator.py:46 - 1: @__main__:test_generator => sys_settrace_generator.py:63 - 2: @__main__: => sys_settrace_generator.py:69 + 1: @__main__:test_generator => sys_settrace_generator.py:61 + 2: @__main__: => sys_settrace_generator.py:67 ### trace_handler::main event: line - 0: @__main__:test_generator => sys_settrace_generator.py:65 - 1: @__main__: => sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:63 + 1: @__main__: => sys_settrace_generator.py:67 7 ### trace_handler::main event: return - 0: @__main__:test_generator => sys_settrace_generator.py:65 - 1: @__main__: => sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:63 + 1: @__main__: => sys_settrace_generator.py:67 Total traces executed: 54 diff --git a/tests/perf_bench/benchrun.py b/tests/perf_bench/benchrun.py index 87b6489d07..6e6135fd98 100644 --- a/tests/perf_bench/benchrun.py +++ b/tests/perf_bench/benchrun.py @@ -15,7 +15,7 @@ def bm_run(N, M): cur_nm = nm param = p if param is None: - print(-1, -1, "no matching params") + print(-1, -1, "SKIP: no matching params") return # Run and time benchmark diff --git a/tests/perf_bench/bm_fannkuch.py b/tests/perf_bench/bm_fannkuch.py index 9f7ae797f0..49ab05d1e9 100644 --- a/tests/perf_bench/bm_fannkuch.py +++ b/tests/perf_bench/bm_fannkuch.py @@ -51,6 +51,7 @@ def fannkuch(n): # Benchmark interface bm_params = { + (32, 10): (3,), (50, 10): (5,), (100, 10): (6,), (500, 10): (7,), diff --git a/tests/perf_bench/bm_nqueens.py b/tests/perf_bench/bm_nqueens.py index 160f1178aa..7c6ddd59e1 100644 --- a/tests/perf_bench/bm_nqueens.py +++ b/tests/perf_bench/bm_nqueens.py @@ -47,7 +47,7 @@ def n_queens(queen_count): # Benchmark interface bm_params = { - (50, 25): (1, 5), + (32, 10): (1, 5), (100, 25): (1, 6), (1000, 100): (1, 7), (5000, 100): (1, 8), diff --git a/tests/perf_bench/bm_pidigits.py b/tests/perf_bench/bm_pidigits.py index 5949b93063..bdaa73cec7 100644 --- a/tests/perf_bench/bm_pidigits.py +++ b/tests/perf_bench/bm_pidigits.py @@ -36,6 +36,7 @@ def gen_pi_digits(n): # Benchmark interface bm_params = { + (32, 10): (1, 20), (50, 25): (1, 35), (100, 100): (1, 65), (1000, 1000): (2, 250), diff --git a/tests/perf_bench/core_import_mpy_multi.py b/tests/perf_bench/core_import_mpy_multi.py index 682c36328a..99c4721d29 100644 --- a/tests/perf_bench/core_import_mpy_multi.py +++ b/tests/perf_bench/core_import_mpy_multi.py @@ -73,7 +73,7 @@ def test(r): # Benchmark interface bm_params = { - (100, 10): (50,), + (32, 10): (50,), (1000, 10): (500,), (5000, 10): (5000,), } diff --git a/tests/perf_bench/core_qstr.py b/tests/perf_bench/core_qstr.py index b87e2c0658..64d6c59e92 100644 --- a/tests/perf_bench/core_qstr.py +++ b/tests/perf_bench/core_qstr.py @@ -10,7 +10,7 @@ def test(r): # Benchmark interface bm_params = { - (100, 10): (400,), + (32, 10): (400,), (1000, 10): (4000,), (5000, 10): (40000,), } diff --git a/tests/run-internalbench.py b/tests/run-internalbench.py index 24f6d5a771..2cf7f3443b 100755 --- a/tests/run-internalbench.py +++ b/tests/run-internalbench.py @@ -13,10 +13,12 @@ from collections import defaultdict # to the correct executable. if os.name == "nt": CPYTHON3 = os.getenv("MICROPY_CPYTHON3", "python3.exe") - MICROPYTHON = os.getenv("MICROPY_MICROPYTHON", "../ports/windows/micropython.exe") + MICROPYTHON = os.getenv( + "MICROPY_MICROPYTHON", "../ports/windows/build-standard/micropython.exe" + ) else: CPYTHON3 = os.getenv("MICROPY_CPYTHON3", "python3") - MICROPYTHON = os.getenv("MICROPY_MICROPYTHON", "../ports/unix/micropython") + MICROPYTHON = os.getenv("MICROPY_MICROPYTHON", "../ports/unix/build-standard/micropython") def run_tests(pyb, test_dict): diff --git a/tests/run-multitests.py b/tests/run-multitests.py index d8a4a48fa7..81db31ea94 100755 --- a/tests/run-multitests.py +++ b/tests/run-multitests.py @@ -3,6 +3,11 @@ # This file is part of the MicroPython project, http://micropython.org/ # The MIT License (MIT) # Copyright (c) 2020 Damien P. George +# +# run-multitests.py +# Runs a test suite that relies on two micropython instances/devices +# interacting in some way. Typically used to test networking / bluetooth etc. + import sys, os, time, re, select import argparse @@ -10,15 +15,26 @@ import itertools import subprocess import tempfile -sys.path.append("../tools") +test_dir = os.path.abspath(os.path.dirname(__file__)) + +if os.path.abspath(sys.path[0]) == test_dir: + # remove the micropython/tests dir from path to avoid + # accidentally importing tests like micropython/const.py + sys.path.pop(0) + +sys.path.insert(0, test_dir + "/../tools") import pyboard if os.name == "nt": CPYTHON3 = os.getenv("MICROPY_CPYTHON3", "python3.exe") - MICROPYTHON = os.getenv("MICROPY_MICROPYTHON", "../ports/windows/micropython.exe") + MICROPYTHON = os.getenv( + "MICROPY_MICROPYTHON", test_dir + "/../ports/windows/build-standard/micropython.exe" + ) else: CPYTHON3 = os.getenv("MICROPY_CPYTHON3", "python3") - MICROPYTHON = os.getenv("MICROPY_MICROPYTHON", "../ports/unix/micropython") + MICROPYTHON = os.getenv( + "MICROPY_MICROPYTHON", test_dir + "/../ports/unix/build-standard/micropython" + ) # For diff'ing test output DIFF = os.getenv("MICROPY_DIFF", "diff -u") @@ -74,6 +90,12 @@ class multitest: except: ip = HOST_IP return ip + @staticmethod + def expect_reboot(resume, delay_ms=0): + print("WAIT_FOR_REBOOT", resume, delay_ms) + @staticmethod + def output_metric(data): + print("OUTPUT_METRIC", data) {} @@ -293,6 +315,7 @@ def run_test_on_instances(test_file, num_instances, instances): skip = False injected_globals = "" output = [[] for _ in range(num_instances)] + output_metrics = [] # If the test calls get_network_ip() then inject HOST_IP so that devices can know # the IP address of the host. Do this lazily to not require a TCP/IP connection @@ -362,10 +385,27 @@ def run_test_on_instances(test_file, num_instances, instances): last_read_time[idx] = time.time() if out is not None and not any(m in out for m in IGNORE_OUTPUT_MATCHES): trace_instance_output(idx, out) + if out.startswith("WAIT_FOR_REBOOT"): + _, resume, delay_ms = out.split(" ") + + if wait_for_reboot(instance, delay_ms): + # Restart the test code, resuming from requested instance block + if not resume.startswith("instance{}".format(idx)): + raise SystemExit( + 'ERROR: resume function must start with "instance{}"'.format( + idx + ) + ) + append_code = APPEND_CODE_TEMPLATE.format(injected_globals, resume[8:]) + instance.start_file(test_file, append=append_code) + last_read_time[idx] = time.time() + if out.startswith("BROADCAST "): for instance2 in instances: if instance2 is not instance: instance2.write(bytes(out, "ascii") + b"\r\n") + elif out.startswith("OUTPUT_METRIC "): + output_metrics.append(out.split(" ", 1)[1]) else: output[idx].append(out) if err is not None: @@ -387,7 +427,39 @@ def run_test_on_instances(test_file, num_instances, instances): output_str += "--- instance{} ---\n".format(idx) output_str += "\n".join(lines) + "\n" - return error, skip, output_str + return error, skip, output_str, output_metrics + + +def wait_for_reboot(instance, extra_timeout_ms=0): + # Monitor device responses for reboot banner, waiting for idle. + extra_timeout = float(extra_timeout_ms) * 1000 + INITIAL_TIMEOUT = 1 + extra_timeout + FULL_TIMEOUT = 5 + extra_timeout + t_start = t_last_activity = time.monotonic() + while True: + t = time.monotonic() + out, err = instance.readline() + if err is not None: + print("Reboot: communication error", err) + return False + if out: + t_last_activity = t + # Check for reboot banner, see py/pyexec.c "reset friendly REPL" + if re.match(r"^MicroPython v\d+\.\d+\.\d+.* on .*; .* with .*$", out): + time.sleep(0.1) + break + + if t_last_activity == t_start: + if t - t_start > INITIAL_TIMEOUT: + print("Reboot: missed initial Timeout") + return False + else: + if t - t_start > FULL_TIMEOUT: + print("Reboot: Timeout") + return False + + instance.pyb.enter_raw_repl() + return True def print_diff(a, b): @@ -415,7 +487,9 @@ def run_tests(test_files, instances_truth, instances_test): sys.stdout.flush() # Run test on test instances - error, skip, output_test = run_test_on_instances(test_file, num_instances, instances_test) + error, skip, output_test, output_metrics = run_test_on_instances( + test_file, num_instances, instances_test + ) if not skip: # Check if truth exists in a file, and read it in @@ -425,7 +499,7 @@ def run_tests(test_files, instances_truth, instances_test): output_truth = f.read() else: # Run test on truth instances to get expected output - _, _, output_truth = run_test_on_instances( + _, _, output_truth, _ = run_test_on_instances( test_file, num_instances, instances_truth ) @@ -454,6 +528,11 @@ def run_tests(test_files, instances_truth, instances_test): print("### DIFF ###") print_diff(output_truth, output_test) + # Print test output metrics, if there are any. + if output_metrics: + for metric in output_metrics: + print(test_file, ": ", metric, sep="") + if cmd_args.show_output: print() @@ -471,7 +550,10 @@ def run_tests(test_files, instances_truth, instances_test): def main(): global cmd_args - cmd_parser = argparse.ArgumentParser(description="Run network tests for MicroPython") + cmd_parser = argparse.ArgumentParser( + description="Run network tests for MicroPython", + formatter_class=argparse.RawTextHelpFormatter, + ) cmd_parser.add_argument( "-s", "--show-output", action="store_true", help="show test output after running" ) @@ -488,11 +570,19 @@ def main(): default=1, help="repeat the test with this many permutations of the instance order", ) + cmd_parser.epilog = ( + "Supported instance types:\r\n" + " -i pyb: physical device (eg. pyboard) on provided repl port.\n" + " -i micropython unix micropython instance, path customised with MICROPY_MICROPYTHON env.\n" + " -i cpython desktop python3 instance, path customised with MICROPY_CPYTHON3 env.\n" + " -i exec: custom program run on provided path.\n" + "Each instance arg can optionally have custom env provided, eg. ,ENV=VAR,ENV=VAR...\n" + ) cmd_parser.add_argument("files", nargs="+", help="input test files") cmd_args = cmd_parser.parse_args() # clear search path to make sure tests use only builtin modules and those in extmod - os.environ["MICROPYPATH"] = os.pathsep + "../extmod" + os.environ["MICROPYPATH"] = os.pathsep.join(("", ".frozen", "../extmod")) test_files = prepare_test_file_list(cmd_args.files) max_instances = max(t[1] for t in test_files) diff --git a/tests/run-natmodtests.py b/tests/run-natmodtests.py index a957fbb6b6..7e19bf4ed9 100755 --- a/tests/run-natmodtests.py +++ b/tests/run-natmodtests.py @@ -11,7 +11,7 @@ import argparse # Paths for host executables CPYTHON3 = os.getenv("MICROPY_CPYTHON3", "python3") -MICROPYTHON = os.getenv("MICROPY_MICROPYTHON", "../ports/unix/micropython-coverage") +MICROPYTHON = os.getenv("MICROPY_MICROPYTHON", "../ports/unix/build-coverage/micropython") NATMOD_EXAMPLE_DIR = "../examples/natmod/" diff --git a/tests/run-perfbench.py b/tests/run-perfbench.py index a2e9e8079a..578f975bb8 100755 --- a/tests/run-perfbench.py +++ b/tests/run-perfbench.py @@ -18,10 +18,12 @@ prepare_script_for_target = __import__("run-tests").prepare_script_for_target # Paths for host executables if os.name == "nt": CPYTHON3 = os.getenv("MICROPY_CPYTHON3", "python3.exe") - MICROPYTHON = os.getenv("MICROPY_MICROPYTHON", "../ports/windows/micropython.exe") + MICROPYTHON = os.getenv( + "MICROPY_MICROPYTHON", "../ports/windows/build-standard/micropython.exe" + ) else: CPYTHON3 = os.getenv("MICROPY_CPYTHON3", "python3") - MICROPYTHON = os.getenv("MICROPY_MICROPYTHON", "../ports/unix/micropython") + MICROPYTHON = os.getenv("MICROPY_MICROPYTHON", "../ports/unix/build-standard/micropython") PYTHON_TRUTH = CPYTHON3 @@ -187,7 +189,7 @@ def parse_output(filename): m = int(m.split("=")[1]) data = [] for l in f: - if l.find(": ") != -1 and l.find(": SKIP") == -1 and l.find("CRASH: ") == -1: + if ": " in l and ": SKIP" not in l and "CRASH: " not in l: name, values = l.strip().split(": ") values = tuple(float(v) for v in values.split()) data.append((name,) + values) @@ -258,9 +260,12 @@ def main(): cmd_parser.add_argument( "--emit", default="bytecode", help="MicroPython emitter to use (bytecode or native)" ) + cmd_parser.add_argument("--heapsize", help="heapsize to use (use default if not specified)") cmd_parser.add_argument("--via-mpy", action="store_true", help="compile code to .mpy first") cmd_parser.add_argument("--mpy-cross-flags", default="", help="flags to pass to mpy-cross") - cmd_parser.add_argument("N", nargs=1, help="N parameter (approximate target CPU frequency)") + cmd_parser.add_argument( + "N", nargs=1, help="N parameter (approximate target CPU frequency in MHz)" + ) cmd_parser.add_argument("M", nargs=1, help="M parameter (approximate target heap in kbytes)") cmd_parser.add_argument("files", nargs="*", help="input test files") args = cmd_parser.parse_args() @@ -283,6 +288,8 @@ def main(): target.enter_raw_repl() else: target = [MICROPYTHON, "-X", "emit=" + args.emit] + if args.heapsize is not None: + target.extend(["-X", "heapsize=" + args.heapsize]) if len(args.files) == 0: tests_skip = ("benchrun.py",) diff --git a/tests/run-tests.py b/tests/run-tests.py index 45b4ebf536..afab133238 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -13,6 +13,9 @@ from multiprocessing.pool import ThreadPool import threading import tempfile +# Maximum time to run a PC-based test, in seconds. +TEST_TIMEOUT = 30 + # See stackoverflow.com/questions/2632199: __file__ nor sys.argv[0] # are guaranteed to always work, this one should though. BASEPATH = os.path.dirname(os.path.abspath(inspect.getsourcefile(lambda: None))) @@ -27,17 +30,23 @@ def base_path(*p): # to the correct executable. if os.name == "nt": CPYTHON3 = os.getenv("MICROPY_CPYTHON3", "python") - MICROPYTHON = os.getenv("MICROPY_MICROPYTHON", base_path("../ports/windows/micropython.exe")) + MICROPYTHON = os.getenv( + "MICROPY_MICROPYTHON", base_path("../ports/windows/build-standard/micropython.exe") + ) + # mpy-cross is only needed if --via-mpy command-line arg is passed + MPYCROSS = os.getenv("MICROPY_MPYCROSS", base_path("../mpy-cross/build/mpy-cross.exe")) else: CPYTHON3 = os.getenv("MICROPY_CPYTHON3", "python3") - MICROPYTHON = os.getenv("MICROPY_MICROPYTHON", base_path("../ports/unix/micropython")) + MICROPYTHON = os.getenv( + "MICROPY_MICROPYTHON", base_path("../ports/unix/build-standard/micropython") + ) + # mpy-cross is only needed if --via-mpy command-line arg is passed + MPYCROSS = os.getenv("MICROPY_MPYCROSS", base_path("../mpy-cross/build/mpy-cross")) # Use CPython options to not save .pyc files, to only access the core standard library # (not site packages which may clash with u-module names), and improve start up time. -CPYTHON3_CMD = [CPYTHON3, "-Wignore", "-BS"] +CPYTHON3_CMD = [CPYTHON3, "-BS"] -# mpy-cross is only needed if --via-mpy command-line arg is passed -MPYCROSS = os.getenv("MICROPY_MPYCROSS", base_path("../mpy-cross/mpy-cross")) # For diff'ing test output DIFF = os.getenv("MICROPY_DIFF", "diff -u") @@ -45,6 +54,37 @@ DIFF = os.getenv("MICROPY_DIFF", "diff -u") # Set PYTHONIOENCODING so that CPython will use utf-8 on systems which set another encoding in the locale os.environ["PYTHONIOENCODING"] = "utf-8" +# Code to allow a target MicroPython to import an .mpy from RAM +injected_import_hook_code = """\ +import usys, uos, uio +class __File(uio.IOBase): + def __init__(self): + self.off = 0 + def ioctl(self, request, arg): + return 0 + def readinto(self, buf): + buf[:] = memoryview(__buf)[self.off:self.off + len(buf)] + self.off += len(buf) + return len(buf) +class __FS: + def mount(self, readonly, mkfs): + pass + def umount(self): + pass + def chdir(self, path): + pass + def stat(self, path): + if path == '__injected_test.mpy': + return tuple(0 for _ in range(10)) + else: + raise OSError(-2) # ENOENT + def open(self, path, mode): + return __File() +uos.mount(__FS(), '/__vfstest') +uos.chdir('/__vfstest') +__import__('__injected_test') +""" + def rm_f(fname): if os.path.exists(fname): @@ -71,6 +111,68 @@ def convert_regex_escapes(line): return bytes("".join(cs), "utf8") +def prepare_script_for_target(args, *, script_filename=None, script_text=None, force_plain=False): + if force_plain or (not args.via_mpy and args.emit == "bytecode"): + if script_filename is not None: + with open(script_filename, "rb") as f: + script_text = f.read() + elif args.via_mpy: + tempname = tempfile.mktemp(dir="") + mpy_filename = tempname + ".mpy" + + if script_filename is None: + script_filename = tempname + ".py" + cleanup_script_filename = True + with open(script_filename, "wb") as f: + f.write(script_text) + else: + cleanup_script_filename = False + + try: + subprocess.check_output( + [MPYCROSS] + + args.mpy_cross_flags.split() + + ["-o", mpy_filename, "-X", "emit=" + args.emit, script_filename], + stderr=subprocess.STDOUT, + ) + except subprocess.CalledProcessError as er: + return True, b"mpy-cross crash\n" + er.output + + with open(mpy_filename, "rb") as f: + script_text = b"__buf=" + bytes(repr(f.read()), "ascii") + b"\n" + + rm_f(mpy_filename) + if cleanup_script_filename: + rm_f(script_filename) + + script_text += bytes(injected_import_hook_code, "ascii") + else: + print("error: using emit={} must go via .mpy".format(args.emit)) + sys.exit(1) + + return False, script_text + + +def run_script_on_remote_target(pyb, args, test_file, is_special): + had_crash, script = prepare_script_for_target( + args, script_filename=test_file, force_plain=is_special + ) + if had_crash: + return True, script + + try: + had_crash = False + pyb.enter_raw_repl() + output_mupy = pyb.exec_(script) + except pyboard.PyboardError as e: + had_crash = True + if not is_special and e.args[0] == "exception": + output_mupy = e.args[1] + e.args[2] + b"CRASH" + else: + output_mupy = bytes(e.args[0], "ascii") + b"\nCRASH" + return had_crash, output_mupy + + def run_micropython(pyb, args, test_file, is_special=False): special_tests = ( "micropython/meminfo.py", @@ -109,33 +211,29 @@ def run_micropython(pyb, args, test_file, is_special=False): return b"SKIP\n" import select + # Even though these might have the pty module, it's unlikely to function. + if sys.platform in ["win32", "msys", "cygwin"]: + return b"SKIP\n" + def get(required=False): rv = b"" while True: - ready = select.select([emulator], [], [emulator], 0.02) - if ready[0] == [emulator]: - rv += os.read(emulator, 1024) + ready = select.select([master], [], [], 0.02) + if ready[0] == [master]: + rv += os.read(master, 1024) else: if not required or rv: return rv def send_get(what): - os.write(emulator, what) + os.write(master, what) return get() with open(test_file, "rb") as f: # instead of: output_mupy = subprocess.check_output(args, stdin=f) - # openpty returns two read/write file descriptors. The first one is - # used by the program which provides the virtual - # terminal service, and the second one is used by the - # subprogram which requires a tty to work. - emulator, subterminal = pty.openpty() + master, slave = pty.openpty() p = subprocess.Popen( - args, - stdin=subterminal, - stdout=subterminal, - stderr=subprocess.STDOUT, - bufsize=0, + args, stdin=slave, stdout=slave, stderr=subprocess.STDOUT, bufsize=0 ) banner = get(True) output_mupy = banner + b"".join(send_get(line) for line in f) @@ -150,15 +248,14 @@ def run_micropython(pyb, args, test_file, is_special=False): p.kill() except ProcessLookupError: pass - os.close(subterminal) - os.close(emulator) + os.close(master) + os.close(slave) else: output_mupy = subprocess.check_output( args + [test_file], stderr=subprocess.STDOUT ) - - except subprocess.CalledProcessError as error: - return error.output + b"CRASH" + except subprocess.CalledProcessError: + return b"CRASH" else: # a standard test run on PC @@ -167,6 +264,8 @@ def run_micropython(pyb, args, test_file, is_special=False): cmdlist = [MICROPYTHON, "-X", "emit=" + args.emit] if args.heapsize is not None: cmdlist.extend(["-X", "heapsize=" + args.heapsize]) + if sys.platform == "darwin": + cmdlist.extend(["-X", "realtime"]) # if running via .mpy, first compile the .py file if args.via_mpy: @@ -183,20 +282,15 @@ def run_micropython(pyb, args, test_file, is_special=False): # run the actual test try: - result = subprocess.run( - cmdlist, - stderr=subprocess.STDOUT, - stdout=subprocess.PIPE, - check=True, - timeout=10, + output_mupy = subprocess.check_output( + cmdlist, stderr=subprocess.STDOUT, timeout=TEST_TIMEOUT ) - output_mupy = result.stdout - except subprocess.TimeoutExpired as er: - had_crash = True - output_mupy = (er.output or b"") + b"TIMEOUT" except subprocess.CalledProcessError as er: had_crash = True output_mupy = er.output + b"CRASH" + except subprocess.TimeoutExpired as er: + had_crash = True + output_mupy = (er.output or b"") + b"TIMEOUT" # clean up if we had an intermediate .mpy file if args.via_mpy: @@ -213,6 +307,10 @@ def run_micropython(pyb, args, test_file, is_special=False): if had_crash or output_mupy in (b"SKIP\n", b"CRASH"): return output_mupy + # skipped special tests will output "SKIP" surrounded by other interpreter debug output + if is_special and not had_crash and b"\nSKIP\n" in output_mupy: + return b"SKIP\n" + if is_special or test_file in special_tests: # convert parts of the output that are not stable across runs with open(test_file + ".exp", "rb") as f: @@ -428,18 +526,23 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): if upy_float_precision < 64: skip_tests.add("float/float_divmod.py") # tested by float/float_divmod_relaxed.py instead skip_tests.add("float/float2int_doubleprec_intbig.py") + skip_tests.add("float/float_format_ints_doubleprec.py") skip_tests.add("float/float_parse_doubleprec.py") if not has_complex: skip_tests.add("float/complex1.py") skip_tests.add("float/complex1_intbig.py") + skip_tests.add("float/complex_reverse_op.py") skip_tests.add("float/complex_special_methods.py") skip_tests.add("float/int_big_float.py") skip_tests.add("float/true_value.py") skip_tests.add("float/types.py") + skip_tests.add("float/complex_dunder.py") if not has_coverage: skip_tests.add("cmdline/cmd_parsetree.py") + skip_tests.add("cmdline/repl_sys_ps1_ps2.py") + skip_tests.add("extmod/ussl_poll.py") # Some tests shouldn't be run on a PC if args.target == "unix": @@ -514,6 +617,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): skip_tests.add("basics/del_deref.py") # requires checking for unbound local skip_tests.add("basics/del_local.py") # requires checking for unbound local skip_tests.add("basics/exception_chain.py") # raise from is not supported + skip_tests.add("basics/fun_name.py") # requires proper names for native functions skip_tests.add("basics/scope_implicit.py") # requires checking for unbound local skip_tests.add("basics/sys_tracebacklimit.py") # requires traceback info skip_tests.add("basics/try_finally_return2.py") # requires raise_varargs @@ -608,17 +712,14 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): with open(test_file_expected, "rb") as f: output_expected = f.read() else: - e = {"PYTHONPATH": os.getcwd(), "PATH": os.environ["PATH"], "LANG": "en_US.UTF-8"} # run CPython to work out expected output try: - output_expected = subprocess.check_output( - CPYTHON3_CMD + [test_file], env=e, stderr=subprocess.STDOUT - ) + output_expected = subprocess.check_output(CPYTHON3_CMD + [test_file]) if args.write_exp: with open(test_file_expected, "wb") as f: f.write(output_expected) - except subprocess.CalledProcessError as error: - output_expected = error.output + b"CPYTHON3 CRASH" + except subprocess.CalledProcessError: + output_expected = b"CPYTHON3 CRASH" # canonical form for all host platforms is to use \n for end-of-line output_expected = output_expected.replace(b"\r\n", b"\n") @@ -828,8 +929,15 @@ the last matching regex is used: "renesas-ra", "rp2", ) - if args.target in LOCAL_TARGETS or args.list_tests: + if args.list_tests: pyb = None + elif args.target in LOCAL_TARGETS: + pyb = None + if not args.mpy_cross_flags: + if args.target == "unix": + args.mpy_cross_flags = "-march=host" + elif args.target == "qemu-arm": + args.mpy_cross_flags = "-march=armv7m" elif args.target in EXTERNAL_TARGETS: global pyboard sys.path.append(base_path("../tools")) @@ -856,7 +964,6 @@ the last matching regex is used: if args.test_dirs is None: test_dirs = ( "basics", - "circuitpython", "micropython", "misc", "extmod", @@ -883,7 +990,6 @@ the last matching regex is used: "unicode", "unix", "cmdline", - "../extmod/ulab/tests", ) elif args.target == "qemu-arm": if not args.write_exp: diff --git a/tests/unicode/data/utf-8_invalid.txt b/tests/unicode/data/utf-8_invalid.txt new file mode 100644 index 0000000000..50020482bf --- /dev/null +++ b/tests/unicode/data/utf-8_invalid.txt @@ -0,0 +1 @@ +aaÿbb diff --git a/tests/unicode/file_invalid.py b/tests/unicode/file_invalid.py new file mode 100644 index 0000000000..3f7f184062 --- /dev/null +++ b/tests/unicode/file_invalid.py @@ -0,0 +1,5 @@ +try: + f = open("unicode/data/utf-8_invalid.txt", encoding="utf-8") + f.read() +except UnicodeError: + print("UnicodeError") diff --git a/tests/unicode/unicode_ure.py b/tests/unicode/unicode_ure.py new file mode 100644 index 0000000000..5a5dc60054 --- /dev/null +++ b/tests/unicode/unicode_ure.py @@ -0,0 +1,32 @@ +# test match.span() for unicode strings + +try: + import ure as re +except ImportError: + try: + import re + except ImportError: + print("SKIP") + raise SystemExit + +try: + m = re.match(".", "a") + m.span +except AttributeError: + print("SKIP") + raise SystemExit + + +def print_spans(match): + print("----") + try: + i = 0 + while True: + print(match.span(i), match.start(i), match.end(i)) + i += 1 + except IndexError: + pass + + +m = re.match(r"([0-9]*)(([a-z]*)([0-9]*))", "1234\u2764567") +print_spans(m) diff --git a/tests/unix/extra_coverage.py b/tests/unix/extra_coverage.py index bb22485026..a51b5b856a 100644 --- a/tests/unix/extra_coverage.py +++ b/tests/unix/extra_coverage.py @@ -49,11 +49,6 @@ print(buf.write(bytearray(16))) # function defined in C++ code print("cpp", extra_cpp_coverage()) -# test user C module -import cexample - -print(cexample.add_ints(3, 2)) - # test user C module mixed with C++ code import cppexample @@ -96,3 +91,8 @@ import frzmpy3 from frzqstr import returns_NULL print(returns_NULL()) + +# test for freeze_mpy +import frozentest + +print(frozentest.__file__) diff --git a/tests/unix/extra_coverage.py.exp b/tests/unix/extra_coverage.py.exp index b9742461e4..52b7b7d912 100644 --- a/tests/unix/extra_coverage.py.exp +++ b/tests/unix/extra_coverage.py.exp @@ -15,10 +15,12 @@ false true abc % # GC -0x0 -0x0 +0 +0 +# GC part 2 +pass # tracked allocation -m_tracked_head = 0x0 +m_tracked_head = 0 0 1 1 1 2 1 @@ -35,7 +37,7 @@ m_tracked_head = 0x0 5 1 6 1 7 1 -m_tracked_head = 0x0 +m_tracked_head = 0 # vstr tests sts @@ -67,10 +69,11 @@ ime utime utimeq argv atexit byteorder exc_info -exit getsizeof implementation maxsize -modules path platform print_exception -ps1 ps2 stderr stdin -stdout tracebacklimit version version_info +executable exit getsizeof implementation +maxsize modules path platform +print_exception ps1 ps2 +stderr stdin stdout tracebacklimit +version version_info ementation # attrtuple (start=1, stop=2, step=3) @@ -183,7 +186,6 @@ OSError None None cpp None -5 (3, 'hellocpp') frzstr1 frzstr1.py @@ -204,3 +206,13 @@ X '\x1b' b'\x00\xff' NULL +uPy +a long string that is not interned +a string that has unicode αβγ chars +b'bytes 1234\x01' +123456789 +0 +1 +2 +3 +frozentest.py diff --git a/tests/unix/mod_os.py b/tests/unix/mod_os.py new file mode 100644 index 0000000000..17554d9375 --- /dev/null +++ b/tests/unix/mod_os.py @@ -0,0 +1,21 @@ +# This module is not entirely compatible with CPython +import os + + +os.putenv("TEST_VARIABLE", "TEST_VALUE") + +print(os.getenv("TEST_VARIABLE")) +print(os.getenv("TEST_VARIABLE", "TEST_DEFAULT_VALUE")) + +os.unsetenv("TEST_VARIABLE") + +print(os.getenv("TEST_VARIABLE")) +print(os.getenv("TEST_VARIABLE", "TEST_DEFAULT_VALUE")) + +print(os.system("true")) + +rand = os.urandom(4) +print(type(rand) is bytes, len(rand)) + +os.errno(2) +print(os.errno()) diff --git a/tests/unix/mod_os.py.exp b/tests/unix/mod_os.py.exp new file mode 100644 index 0000000000..465163085b --- /dev/null +++ b/tests/unix/mod_os.py.exp @@ -0,0 +1,7 @@ +TEST_VALUE +TEST_VALUE +None +TEST_DEFAULT_VALUE +0 +True 4 +2 diff --git a/tools/autobuild/build-boards.sh b/tools/autobuild/build-boards.sh index 9caadc1b07..0a0e127f2e 100755 --- a/tools/autobuild/build-boards.sh +++ b/tools/autobuild/build-boards.sh @@ -83,12 +83,7 @@ function build_esp32_boards { if idf.py --version | grep -q v4.2; then if [ $mcu = esp32 ]; then # build standard esp32-based boards with IDF v4.2 - if echo $board_json | grep -q GENERIC; then - # traditionally, GENERIC and GENERIC_SPIRAM boards used manifest_release.py - MICROPY_AUTOBUILD_MAKE="$MICROPY_AUTOBUILD_MAKE FROZEN_MANIFEST=$(pwd)/boards/manifest_release.py" build_board $board_json $fw_tag $dest_dir bin elf map - else - build_board $board_json $fw_tag $dest_dir bin elf map - fi + build_board $board_json $fw_tag $dest_dir bin elf map fi else if [ $mcu != esp32 ]; then @@ -103,6 +98,10 @@ 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 hex } diff --git a/tools/ci.sh b/tools/ci.sh index 72e779c499..8127a5134c 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -18,7 +18,6 @@ function ci_gcc_arm_setup { # code formatting function ci_code_formatting_setup { - sudo apt-add-repository --yes --update ppa:pybricks/ppa sudo apt-get install uncrustify pip3 install black uncrustify --version @@ -50,6 +49,10 @@ function ci_code_size_setup { } function ci_code_size_build { + # check the following ports for the change in their code size + PORTS_TO_CHECK=bmusp + SUBMODULES="lib/berkeley-db-1.xx lib/mbedtls lib/micropython-lib lib/pico-sdk lib/stm32lib lib/tinyusb" + # starts off at either the ref/pull/N/merge FETCH_HEAD, or the current branch HEAD git checkout -b pull_request # save the current location git remote add upstream https://github.com/micropython/micropython.git @@ -57,14 +60,16 @@ function ci_code_size_build { # 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` + git submodule update --init $SUBMODULES git show -s - tools/metrics.py clean bm - tools/metrics.py build bm | tee ~/size0 || true + tools/metrics.py clean $PORTS_TO_CHECK + tools/metrics.py build $PORTS_TO_CHECK | tee ~/size0 || true # build PR/branch, save to size1 git checkout pull_request + git submodule update --init $SUBMODULES git log upstream/master..HEAD - tools/metrics.py clean bm - tools/metrics.py build bm | tee ~/size1 + tools/metrics.py clean $PORTS_TO_CHECK + tools/metrics.py build $PORTS_TO_CHECK | tee ~/size1 } ######################################################################################## @@ -76,8 +81,8 @@ function ci_mpy_format_setup { function ci_mpy_format_test { # Test mpy-tool.py dump feature on bytecode - python2 ./tools/mpy-tool.py -xd ports/minimal/frozentest.mpy - python3 ./tools/mpy-tool.py -xd ports/minimal/frozentest.mpy + python2 ./tools/mpy-tool.py -xd tests/frozen/frozentest.mpy + python3 ./tools/mpy-tool.py -xd tests/frozen/frozentest.mpy # Test mpy-tool.py dump feature on native code make -C examples/natmod/features1 @@ -173,21 +178,21 @@ function ci_esp8266_build { } ######################################################################################## -# ports/javascript +# ports/webassembly -function ci_javascript_setup { +function ci_webassembly_setup { git clone https://github.com/emscripten-core/emsdk.git (cd emsdk && ./emsdk install latest && ./emsdk activate latest) } -function ci_javascript_build { +function ci_webassembly_build { source emsdk/emsdk_env.sh - make ${MAKEOPTS} -C ports/javascript + make ${MAKEOPTS} -C ports/webassembly } -function ci_javascript_run_tests { +function ci_webassembly_run_tests { # This port is very slow at running, so only run a few of the tests. - (cd tests && MICROPY_MICROPYTHON=../ports/javascript/node_run.sh ./run-tests.py -j1 basics/builtin_*.py) + (cd tests && MICROPY_MICROPYTHON=../ports/webassembly/node_run.sh ./run-tests.py -j1 basics/builtin_*.py) } ######################################################################################## @@ -199,8 +204,9 @@ function ci_mimxrt_setup { function ci_mimxrt_build { make ${MAKEOPTS} -C mpy-cross - make ${MAKEOPTS} -C ports/mimxrt submodules + make ${MAKEOPTS} -C ports/mimxrt BOARD=MIMXRT1020_EVK submodules make ${MAKEOPTS} -C ports/mimxrt BOARD=MIMXRT1020_EVK + make ${MAKEOPTS} -C ports/mimxrt BOARD=TEENSY40 submodules make ${MAKEOPTS} -C ports/mimxrt BOARD=TEENSY40 } @@ -246,8 +252,10 @@ function ci_qemu_arm_setup { function ci_qemu_arm_build { make ${MAKEOPTS} -C mpy-cross + make ${MAKEOPTS} -C ports/qemu-arm submodules make ${MAKEOPTS} -C ports/qemu-arm CFLAGS_EXTRA=-DMP_ENDIANNESS_BIG=1 make ${MAKEOPTS} -C ports/qemu-arm clean + make ${MAKEOPTS} -C ports/qemu-arm -f Makefile.test submodules make ${MAKEOPTS} -C ports/qemu-arm -f Makefile.test test make ${MAKEOPTS} -C ports/qemu-arm -f Makefile.test clean make ${MAKEOPTS} -C ports/qemu-arm -f Makefile.test BOARD=sabrelite test @@ -282,6 +290,10 @@ function ci_rp2_build { make ${MAKEOPTS} -C ports/rp2 USER_C_MODULES=../../examples/usercmodule/micropython.cmake make ${MAKEOPTS} -C ports/rp2 BOARD=W5100S_EVB_PICO submodules make ${MAKEOPTS} -C ports/rp2 BOARD=W5100S_EVB_PICO + + # Test building ninaw10 driver and NIC interface. + make ${MAKEOPTS} -C ports/rp2 BOARD=ARDUINO_NANO_RP2040_CONNECT submodules + make ${MAKEOPTS} -C ports/rp2 BOARD=ARDUINO_NANO_RP2040_CONNECT } ######################################################################################## @@ -292,6 +304,7 @@ function ci_samd_setup { } function ci_samd_build { + make ${MAKEOPTS} -C mpy-cross make ${MAKEOPTS} -C ports/samd submodules make ${MAKEOPTS} -C ports/samd } @@ -307,10 +320,11 @@ function ci_stm32_setup { function ci_stm32_pyb_build { make ${MAKEOPTS} -C mpy-cross - make ${MAKEOPTS} -C ports/stm32 submodules + make ${MAKEOPTS} -C ports/stm32 MICROPY_PY_NETWORK_WIZNET5K=5200 submodules + make ${MAKEOPTS} -C ports/stm32 BOARD=PYBD_SF2 submodules git submodule update --init lib/btstack git submodule update --init lib/mynewt-nimble - make ${MAKEOPTS} -C ports/stm32 BOARD=PYBV11 MICROPY_PY_NETWORK_WIZNET5K=5200 MICROPY_PY_CC3K=1 USER_C_MODULES=../../examples/usercmodule + make ${MAKEOPTS} -C ports/stm32 BOARD=PYBV11 MICROPY_PY_NETWORK_WIZNET5K=5200 USER_C_MODULES=../../examples/usercmodule make ${MAKEOPTS} -C ports/stm32 BOARD=PYBD_SF2 make ${MAKEOPTS} -C ports/stm32 BOARD=PYBD_SF6 NANBOX=1 MICROPY_BLUETOOTH_NIMBLE=0 MICROPY_BLUETOOTH_BTSTACK=1 make ${MAKEOPTS} -C ports/stm32/mboot BOARD=PYBV10 CFLAGS_EXTRA='-DMBOOT_FSLOAD=1 -DMBOOT_VFS_LFS2=1' @@ -324,7 +338,7 @@ function ci_stm32_pyb_build { function ci_stm32_nucleo_build { make ${MAKEOPTS} -C mpy-cross - make ${MAKEOPTS} -C ports/stm32 submodules + make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_H743ZI submodules git submodule update --init lib/mynewt-nimble # Test building various MCU families, some with additional options. @@ -355,6 +369,8 @@ function ci_teensy_setup { } function ci_teensy_build { + make ${MAKEOPTS} -C mpy-cross + make ${MAKEOPTS} -C ports/teensy submodules make ${MAKEOPTS} -C ports/teensy } @@ -406,14 +422,10 @@ function ci_unix_run_tests_helper { function ci_unix_run_tests_full_helper { variant=$1 shift - if [ $variant = standard ]; then - micropython=micropython - else - micropython=micropython-$variant - fi + micropython=../ports/unix/build-$variant/micropython make -C ports/unix VARIANT=$variant "$@" test_full - (cd tests && MICROPY_CPYTHON3=python3 MICROPY_MICROPYTHON=../ports/unix/$micropython ./run-multitests.py multi_net/*.py) - (cd tests && MICROPY_CPYTHON3=python3 MICROPY_MICROPYTHON=../ports/unix/$micropython ./run-perfbench.py 1000 1000) + (cd tests && MICROPY_CPYTHON3=python3 MICROPY_MICROPYTHON=$micropython ./run-multitests.py multi_net/*.py) + (cd tests && MICROPY_CPYTHON3=python3 MICROPY_MICROPYTHON=$micropython ./run-perfbench.py 1000 1000) } function ci_native_mpy_modules_build { @@ -442,7 +454,7 @@ function ci_unix_minimal_build { } function ci_unix_minimal_run_tests { - (cd tests && MICROPY_CPYTHON3=python3 MICROPY_MICROPYTHON=../ports/unix/micropython-minimal ./run-tests.py -e exception_chain -e self_type_check -e subclass_native_init -d basics) + (cd tests && MICROPY_CPYTHON3=python3 MICROPY_MICROPYTHON=../ports/unix/build-minimal/micropython ./run-tests.py -e exception_chain -e self_type_check -e subclass_native_init -d basics) } function ci_unix_standard_build { @@ -454,14 +466,6 @@ function ci_unix_standard_run_tests { ci_unix_run_tests_full_helper standard } -function ci_unix_dev_build { - ci_unix_build_helper VARIANT=dev -} - -function ci_unix_dev_run_tests { - ci_unix_run_tests_helper VARIANT=dev -} - function ci_unix_coverage_setup { sudo pip3 install setuptools sudo pip3 install pyelftools @@ -489,21 +493,21 @@ function ci_unix_coverage_run_mpy_merge_tests { test=$(basename $inpy .py) echo $test outmpy=$outdir/$test.mpy - $mptop/mpy-cross/mpy-cross -o $outmpy $inpy - (cd $outdir && $mptop/ports/unix/micropython-coverage -m $test >> out-individual) + $mptop/mpy-cross/build/mpy-cross -o $outmpy $inpy + (cd $outdir && $mptop/ports/unix/build-coverage/micropython -m $test >> out-individual) allmpy+=($outmpy) done # Merge all the tests into one .mpy file, and then execute it. python3 $mptop/tools/mpy-tool.py --merge -o $outdir/merged.mpy ${allmpy[@]} - (cd $outdir && $mptop/ports/unix/micropython-coverage -m merged > out-merged) + (cd $outdir && $mptop/ports/unix/build-coverage/micropython -m merged > out-merged) # Make sure the outputs match. diff $outdir/out-individual $outdir/out-merged && /bin/rm -rf $outdir } function ci_unix_coverage_run_native_mpy_tests { - MICROPYPATH=examples/natmod/features2 ./ports/unix/micropython-coverage -m features2 + MICROPYPATH=examples/natmod/features2 ./ports/unix/build-coverage/micropython -m features2 (cd tests && ./run-natmodtests.py "$@" extmod/{btree*,framebuf*,uheapq*,urandom*,ure*,uzlib*}.py) } @@ -514,7 +518,6 @@ function ci_unix_32bit_setup { sudo pip3 install setuptools sudo pip3 install pyelftools gcc --version - python2 --version python3 --version } @@ -578,6 +581,7 @@ function ci_unix_float_clang_run_tests { function ci_unix_settrace_build { make ${MAKEOPTS} -C mpy-cross + make ${MAKEOPTS} -C ports/unix submodules make ${MAKEOPTS} -C ports/unix "${CI_UNIX_OPTS_SYS_SETTRACE[@]}" } @@ -587,6 +591,7 @@ function ci_unix_settrace_run_tests { function ci_unix_settrace_stackless_build { make ${MAKEOPTS} -C mpy-cross + make ${MAKEOPTS} -C ports/unix submodules make ${MAKEOPTS} -C ports/unix "${CI_UNIX_OPTS_SYS_SETTRACE_STACKLESS[@]}" } @@ -595,13 +600,14 @@ function ci_unix_settrace_stackless_run_tests { } function ci_unix_macos_build { + # Install pkg-config to configure libffi paths. + brew install pkg-config + make ${MAKEOPTS} -C mpy-cross make ${MAKEOPTS} -C ports/unix submodules #make ${MAKEOPTS} -C ports/unix deplibs make ${MAKEOPTS} -C ports/unix # check for additional compiler errors/warnings - make ${MAKEOPTS} -C ports/unix VARIANT=dev submodules - make ${MAKEOPTS} -C ports/unix VARIANT=dev make ${MAKEOPTS} -C ports/unix VARIANT=coverage submodules make ${MAKEOPTS} -C ports/unix VARIANT=coverage } @@ -610,7 +616,7 @@ function ci_unix_macos_run_tests { # Issues with macOS tests: # - import_pkg7 has a problem with relative imports # - urandom_basic has a problem with getrandbits(0) - (cd tests && ./run-tests.py --exclude 'import_pkg7.py' --exclude 'urandom_basic.py') + (cd tests && MICROPY_MICROPYTHON=../ports/unix/build-standard/micropython ./run-tests.py --exclude 'import_pkg7.py' --exclude 'urandom_basic.py') } function ci_unix_qemu_mips_setup { @@ -629,8 +635,8 @@ function ci_unix_qemu_mips_run_tests { # Issues with MIPS tests: # - (i)listdir does not work, it always returns the empty list (it's an issue with the underlying C call) # - ffi tests do not work - file ./ports/unix/micropython-coverage - (cd tests && MICROPY_MICROPYTHON=../ports/unix/micropython-coverage ./run-tests.py --exclude 'vfs_posix.py' --exclude 'ffi_(callback|float|float2).py') + file ./ports/unix/build-coverage/micropython + (cd tests && MICROPY_MICROPYTHON=../ports/unix/build-coverage/micropython ./run-tests.py --exclude 'vfs_posix.*\.py' --exclude 'ffi_(callback|float|float2).py') } function ci_unix_qemu_arm_setup { @@ -649,8 +655,8 @@ function ci_unix_qemu_arm_run_tests { # Issues with ARM tests: # - (i)listdir does not work, it always returns the empty list (it's an issue with the underlying C call) export QEMU_LD_PREFIX=/usr/arm-linux-gnueabi - file ./ports/unix/micropython-coverage - (cd tests && MICROPY_MICROPYTHON=../ports/unix/micropython-coverage ./run-tests.py --exclude 'vfs_posix.py') + file ./ports/unix/build-coverage/micropython + (cd tests && MICROPY_MICROPYTHON=../ports/unix/build-coverage/micropython ./run-tests.py --exclude 'vfs_posix.*\.py') } ######################################################################################## @@ -662,6 +668,7 @@ function ci_windows_setup { function ci_windows_build { make ${MAKEOPTS} -C mpy-cross + make ${MAKEOPTS} -C ports/windows submodules make ${MAKEOPTS} -C ports/windows CROSS_COMPILE=i686-w64-mingw32- } @@ -670,7 +677,7 @@ function ci_windows_build { ZEPHYR_DOCKER_VERSION=v0.21.0 ZEPHYR_SDK_VERSION=0.13.2 -ZEPHYR_VERSION=v3.0.0 +ZEPHYR_VERSION=v3.1.0 function ci_zephyr_setup { docker pull zephyrprojectrtos/ci:${ZEPHYR_DOCKER_VERSION} @@ -692,7 +699,6 @@ function ci_zephyr_install { function ci_zephyr_build { docker exec zephyr-ci west build -p auto -b qemu_x86 -- -DCONF_FILE=prj_minimal.conf - docker exec zephyr-ci west build -p auto -b qemu_x86 docker exec zephyr-ci west build -p auto -b frdm_k64f docker exec zephyr-ci west build -p auto -b mimxrt1050_evk docker exec zephyr-ci west build -p auto -b nucleo_wb55rg # for bluetooth diff --git a/tools/codestats.sh b/tools/codestats.sh index c0dd747dc6..b0780f64db 100755 --- a/tools/codestats.sh +++ b/tools/codestats.sh @@ -28,7 +28,7 @@ AWK=awk MAKE="make -j2" # these are the binaries that are built; some have 2 or 3 depending on version -bin_unix=ports/unix/micropython +bin_unix=ports/unix/build-standard/micropython bin_stm32=ports/stm32/build-PYBV10/firmware.elf bin_barearm_1=ports/bare-arm/build/flash.elf bin_barearm_2=ports/bare-arm/build/firmware.elf diff --git a/tools/gendoc.py b/tools/gendoc.py deleted file mode 100644 index e7eb011b7c..0000000000 --- a/tools/gendoc.py +++ /dev/null @@ -1,556 +0,0 @@ -# SPDX-FileCopyrightText: 2014 MicroPython & CircuitPython contributors (https://github.com/adafruit/circuitpython/graphs/contributors) -# -# SPDX-License-Identifier: MIT - -""" -Generate documentation for pyboard API from C files. -""" - -import os -import argparse -import re -import markdown - - -# given a list of (name,regex) pairs, find the first one that matches the given line -def re_match_first(regexs, line): - for name, regex in regexs: - match = re.match(regex, line) - if match: - return name, match - return None, None - - -def makedirs(d): - if not os.path.isdir(d): - os.makedirs(d) - - -class Lexer: - class LexerError(Exception): - pass - - class EOF(Exception): - pass - - class Break(Exception): - pass - - def __init__(self, file): - self.filename = file - with open(file, "rt") as f: - line_num = 0 - lines = [] - for line in f: - line_num += 1 - line = line.strip() - if line == "///": - lines.append((line_num, "")) - elif line.startswith("/// "): - lines.append((line_num, line[4:])) - elif len(lines) > 0 and lines[-1][1] is not None: - lines.append((line_num, None)) - if len(lines) > 0 and lines[-1][1] is not None: - lines.append((line_num, None)) - self.cur_line = 0 - self.lines = lines - - def opt_break(self): - if len(self.lines) > 0 and self.lines[0][1] is None: - self.lines.pop(0) - - def next(self): - if len(self.lines) == 0: - raise Lexer.EOF - else: - l = self.lines.pop(0) - self.cur_line = l[0] - if l[1] is None: - raise Lexer.Break - else: - return l[1] - - def error(self, msg): - print("({}:{}) {}".format(self.filename, self.cur_line, msg)) - raise Lexer.LexerError - - -class MarkdownWriter: - def __init__(self): - pass - - def start(self): - self.lines = [] - - def end(self): - return "\n".join(self.lines) - - def heading(self, level, text): - if len(self.lines) > 0: - self.lines.append("") - self.lines.append(level * "#" + " " + text) - self.lines.append("") - - def para(self, text): - if len(self.lines) > 0 and self.lines[-1] != "": - self.lines.append("") - if isinstance(text, list): - self.lines.extend(text) - elif isinstance(text, str): - self.lines.append(text) - else: - assert False - self.lines.append("") - - def single_line(self, text): - self.lines.append(text) - - def module(self, name, short_descr, descr): - self.heading(1, "module {}".format(name)) - self.para(descr) - - def function(self, ctx, name, args, descr): - proto = "{}.{}{}".format(ctx, self.name, self.args) - self.heading(3, "`" + proto + "`") - self.para(descr) - - def method(self, ctx, name, args, descr): - if name == "\\constructor": - proto = "{}{}".format(ctx, args) - elif name == "\\call": - proto = "{}{}".format(ctx, args) - else: - proto = "{}.{}{}".format(ctx, name, args) - self.heading(3, "`" + proto + "`") - self.para(descr) - - def constant(self, ctx, name, descr): - self.single_line("`{}.{}` - {}".format(ctx, name, descr)) - - -class ReStructuredTextWriter: - head_chars = {1: "=", 2: "-", 3: "."} - - def __init__(self): - pass - - def start(self): - self.lines = [] - - def end(self): - return "\n".join(self.lines) - - def _convert(self, text): - return text.replace("`", "``").replace("*", "\\*") - - def heading(self, level, text, convert=True): - if len(self.lines) > 0: - self.lines.append("") - if convert: - text = self._convert(text) - self.lines.append(text) - self.lines.append(len(text) * self.head_chars[level]) - self.lines.append("") - - def para(self, text, indent=""): - if len(self.lines) > 0 and self.lines[-1] != "": - self.lines.append("") - if isinstance(text, list): - for t in text: - self.lines.append(indent + self._convert(t)) - elif isinstance(text, str): - self.lines.append(indent + self._convert(text)) - else: - assert False - self.lines.append("") - - def single_line(self, text): - self.lines.append(self._convert(text)) - - def module(self, name, short_descr, descr): - self.heading(1, ":mod:`{}` --- {}".format(name, self._convert(short_descr)), convert=False) - self.lines.append(".. module:: {}".format(name)) - self.lines.append(" :synopsis: {}".format(short_descr)) - self.para(descr) - - def function(self, ctx, name, args, descr): - args = self._convert(args) - self.lines.append(".. function:: " + name + args) - self.para(descr, indent=" ") - - def method(self, ctx, name, args, descr): - args = self._convert(args) - if name == "\\constructor": - self.lines.append(".. class:: " + ctx + args) - elif name == "\\call": - self.lines.append(".. method:: " + ctx + args) - else: - self.lines.append(".. method:: " + ctx + "." + name + args) - self.para(descr, indent=" ") - - def constant(self, ctx, name, descr): - self.lines.append(".. data:: " + name) - self.para(descr, indent=" ") - - -class DocValidateError(Exception): - pass - - -class DocItem: - def __init__(self): - self.doc = [] - - def add_doc(self, lex): - try: - while True: - line = lex.next() - if len(line) > 0 or len(self.doc) > 0: - self.doc.append(line) - except Lexer.Break: - pass - - def dump(self, writer): - writer.para(self.doc) - - -class DocConstant(DocItem): - def __init__(self, name, descr): - super().__init__() - self.name = name - self.descr = descr - - def dump(self, ctx, writer): - writer.constant(ctx, self.name, self.descr) - - -class DocFunction(DocItem): - def __init__(self, name, args): - super().__init__() - self.name = name - self.args = args - - def dump(self, ctx, writer): - writer.function(ctx, self.name, self.args, self.doc) - - -class DocMethod(DocItem): - def __init__(self, name, args): - super().__init__() - self.name = name - self.args = args - - def dump(self, ctx, writer): - writer.method(ctx, self.name, self.args, self.doc) - - -class DocClass(DocItem): - def __init__(self, name, descr): - super().__init__() - self.name = name - self.descr = descr - self.constructors = {} - self.classmethods = {} - self.methods = {} - self.constants = {} - - def process_classmethod(self, lex, d): - name = d["id"] - if name == "\\constructor": - dict_ = self.constructors - else: - dict_ = self.classmethods - if name in dict_: - lex.error("multiple definition of method '{}'".format(name)) - method = dict_[name] = DocMethod(name, d["args"]) - method.add_doc(lex) - - def process_method(self, lex, d): - name = d["id"] - dict_ = self.methods - if name in dict_: - lex.error("multiple definition of method '{}'".format(name)) - method = dict_[name] = DocMethod(name, d["args"]) - method.add_doc(lex) - - def process_constant(self, lex, d): - name = d["id"] - if name in self.constants: - lex.error("multiple definition of constant '{}'".format(name)) - self.constants[name] = DocConstant(name, d["descr"]) - lex.opt_break() - - def dump(self, writer): - writer.heading(1, "class {}".format(self.name)) - super().dump(writer) - if len(self.constructors) > 0: - writer.heading(2, "Constructors") - for f in sorted(self.constructors.values(), key=lambda x: x.name): - f.dump(self.name, writer) - if len(self.classmethods) > 0: - writer.heading(2, "Class methods") - for f in sorted(self.classmethods.values(), key=lambda x: x.name): - f.dump(self.name, writer) - if len(self.methods) > 0: - writer.heading(2, "Methods") - for f in sorted(self.methods.values(), key=lambda x: x.name): - f.dump(self.name.lower(), writer) - if len(self.constants) > 0: - writer.heading(2, "Constants") - for c in sorted(self.constants.values(), key=lambda x: x.name): - c.dump(self.name, writer) - - -class DocModule(DocItem): - def __init__(self, name, descr): - super().__init__() - self.name = name - self.descr = descr - self.functions = {} - self.constants = {} - self.classes = {} - self.cur_class = None - - def new_file(self): - self.cur_class = None - - def process_function(self, lex, d): - name = d["id"] - if name in self.functions: - lex.error("multiple definition of function '{}'".format(name)) - function = self.functions[name] = DocFunction(name, d["args"]) - function.add_doc(lex) - - # def process_classref(self, lex, d): - # name = d['id'] - # self.classes[name] = name - # lex.opt_break() - - def process_class(self, lex, d): - name = d["id"] - if name in self.classes: - lex.error("multiple definition of class '{}'".format(name)) - self.cur_class = self.classes[name] = DocClass(name, d["descr"]) - self.cur_class.add_doc(lex) - - def process_classmethod(self, lex, d): - self.cur_class.process_classmethod(lex, d) - - def process_method(self, lex, d): - self.cur_class.process_method(lex, d) - - def process_constant(self, lex, d): - if self.cur_class is None: - # a module-level constant - name = d["id"] - if name in self.constants: - lex.error("multiple definition of constant '{}'".format(name)) - self.constants[name] = DocConstant(name, d["descr"]) - lex.opt_break() - else: - # a class-level constant - self.cur_class.process_constant(lex, d) - - def validate(self): - if self.descr is None: - raise DocValidateError("module {} referenced but never defined".format(self.name)) - - def dump(self, writer): - writer.module(self.name, self.descr, self.doc) - if self.functions: - writer.heading(2, "Functions") - for f in sorted(self.functions.values(), key=lambda x: x.name): - f.dump(self.name, writer) - if self.constants: - writer.heading(2, "Constants") - for c in sorted(self.constants.values(), key=lambda x: x.name): - c.dump(self.name, writer) - if self.classes: - writer.heading(2, "Classes") - for c in sorted(self.classes.values(), key=lambda x: x.name): - writer.para("[`{}.{}`]({}) - {}".format(self.name, c.name, c.name, c.descr)) - - def write_html(self, dir): - md_writer = MarkdownWriter() - md_writer.start() - self.dump(md_writer) - with open(os.path.join(dir, "index.html"), "wt") as f: - f.write(markdown.markdown(md_writer.end())) - for c in self.classes.values(): - class_dir = os.path.join(dir, c.name) - makedirs(class_dir) - md_writer.start() - md_writer.para("part of the [{} module](./)".format(self.name)) - c.dump(md_writer) - with open(os.path.join(class_dir, "index.html"), "wt") as f: - f.write(markdown.markdown(md_writer.end())) - - def write_rst(self, dir): - rst_writer = ReStructuredTextWriter() - rst_writer.start() - self.dump(rst_writer) - with open(dir + "/" + self.name + ".rst", "wt") as f: - f.write(rst_writer.end()) - for c in self.classes.values(): - rst_writer.start() - c.dump(rst_writer) - with open(dir + "/" + self.name + "." + c.name + ".rst", "wt") as f: - f.write(rst_writer.end()) - - -class Doc: - def __init__(self): - self.modules = {} - self.cur_module = None - - def new_file(self): - self.cur_module = None - for m in self.modules.values(): - m.new_file() - - def check_module(self, lex): - if self.cur_module is None: - lex.error("module not defined") - - def process_module(self, lex, d): - name = d["id"] - if name not in self.modules: - self.modules[name] = DocModule(name, None) - self.cur_module = self.modules[name] - if self.cur_module.descr is not None: - lex.error("multiple definition of module '{}'".format(name)) - self.cur_module.descr = d["descr"] - self.cur_module.add_doc(lex) - - def process_moduleref(self, lex, d): - name = d["id"] - if name not in self.modules: - self.modules[name] = DocModule(name, None) - self.cur_module = self.modules[name] - lex.opt_break() - - def process_class(self, lex, d): - self.check_module(lex) - self.cur_module.process_class(lex, d) - - def process_function(self, lex, d): - self.check_module(lex) - self.cur_module.process_function(lex, d) - - def process_classmethod(self, lex, d): - self.check_module(lex) - self.cur_module.process_classmethod(lex, d) - - def process_method(self, lex, d): - self.check_module(lex) - self.cur_module.process_method(lex, d) - - def process_constant(self, lex, d): - self.check_module(lex) - self.cur_module.process_constant(lex, d) - - def validate(self): - for m in self.modules.values(): - m.validate() - - def dump(self, writer): - writer.heading(1, "Modules") - writer.para("These are the Python modules that are implemented.") - for m in sorted(self.modules.values(), key=lambda x: x.name): - writer.para("[`{}`]({}/) - {}".format(m.name, m.name, m.descr)) - - def write_html(self, dir): - md_writer = MarkdownWriter() - with open(os.path.join(dir, "module", "index.html"), "wt") as f: - md_writer.start() - self.dump(md_writer) - f.write(markdown.markdown(md_writer.end())) - for m in self.modules.values(): - mod_dir = os.path.join(dir, "module", m.name) - makedirs(mod_dir) - m.write_html(mod_dir) - - def write_rst(self, dir): - # with open(os.path.join(dir, 'module', 'index.html'), 'wt') as f: - # f.write(markdown.markdown(self.dump())) - for m in self.modules.values(): - m.write_rst(dir) - - -regex_descr = r"(?P.*)" - -doc_regexs = ( - (Doc.process_module, re.compile(r"\\module (?P[a-z][a-z0-9]*) - " + regex_descr + r"$")), - (Doc.process_moduleref, re.compile(r"\\moduleref (?P[a-z]+)$")), - (Doc.process_function, re.compile(r"\\function (?P[a-z0-9_]+)(?P\(.*\))$")), - (Doc.process_classmethod, re.compile(r"\\classmethod (?P\\?[a-z0-9_]+)(?P\(.*\))$")), - (Doc.process_method, re.compile(r"\\method (?P\\?[a-z0-9_]+)(?P\(.*\))$")), - ( - Doc.process_constant, - re.compile(r"\\constant (?P[A-Za-z0-9_]+) - " + regex_descr + r"$"), - ), - # (Doc.process_classref, re.compile(r'\\classref (?P[A-Za-z0-9_]+)$')), - (Doc.process_class, re.compile(r"\\class (?P[A-Za-z0-9_]+) - " + regex_descr + r"$")), -) - - -def process_file(file, doc): - lex = Lexer(file) - doc.new_file() - try: - try: - while True: - line = lex.next() - fun, match = re_match_first(doc_regexs, line) - if fun == None: - lex.error("unknown line format: {}".format(line)) - fun(doc, lex, match.groupdict()) - - except Lexer.Break: - lex.error("unexpected break") - - except Lexer.EOF: - pass - - except Lexer.LexerError: - return False - - return True - - -def main(): - cmd_parser = argparse.ArgumentParser( - description="Generate documentation for pyboard API from C files." - ) - cmd_parser.add_argument( - "--outdir", metavar="", default="gendoc-out", help="output directory" - ) - cmd_parser.add_argument("--format", default="html", help="output format: html or rst") - cmd_parser.add_argument("files", nargs="+", help="input files") - args = cmd_parser.parse_args() - - doc = Doc() - for file in args.files: - print("processing", file) - if not process_file(file, doc): - return - try: - doc.validate() - except DocValidateError as e: - print(e) - - makedirs(args.outdir) - - if args.format == "html": - doc.write_html(args.outdir) - elif args.format == "rst": - doc.write_rst(args.outdir) - else: - print("unknown format:", args.format) - return - - print("written to", args.outdir) - - -if __name__ == "__main__": - main() diff --git a/tools/hci_trace_to_pcap.py b/tools/hci_trace_to_pcap.py new file mode 100755 index 0000000000..d55df3fb61 --- /dev/null +++ b/tools/hci_trace_to_pcap.py @@ -0,0 +1,134 @@ +#!/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. + +# Simple script to take the output printed when HCI_TRACE is enabled in +# unix/mpbthciport.c, extmod/btstack/btstack_hci_uart.c, +# extmod/nimble/hal/hal_uart.c and turn it into a .pcap file suitable for use +# with Wireshark. + +import re +import sys +import struct + +# pcap file header: +# typedef struct pcap_hdr_s { +# guint32 magic_number; /* magic number */ +# guint16 version_major; /* major version number */ +# guint16 version_minor; /* minor version number */ +# gint32 thiszone; /* GMT to local correction */ +# guint32 sigfigs; /* accuracy of timestamps */ +# guint32 snaplen; /* max length of captured packets, in octets */ +# guint32 network; /* data link type */ +# } pcap_hdr_t; + +# pcap record header +# typedef struct pcaprec_hdr_s { +# guint32 ts_sec; /* timestamp seconds */ +# guint32 ts_usec; /* timestamp microseconds */ +# guint32 incl_len; /* number of octets of packet saved in file */ +# guint32 orig_len; /* actual length of packet */ +# } pcaprec_hdr_t; + +_LINKTYPE_BLUETOOTH_HCI_H4_WITH_PHDR = 201 # "!I" direction, followed by data +sys.stdout.buffer.write( + struct.pack("!IHHiIII", 0xA1B2C3D4, 2, 4, 0, 0, 65535, _LINKTYPE_BLUETOOTH_HCI_H4_WITH_PHDR) +) + +_DIR_CONTROLLER_TO_HOST = 1 +_DIR_HOST_TO_CONTROLLER = 0 + +reassemble_timestamp = 0 +reassemble_packet = bytearray() + +with open(sys.argv[1], "r") as f: + for line in f: + line = line.strip() + m = re.match("([<>]) \\[ *([0-9]+)\\] ([A-Fa-f0-9:]+)", line) + if not m: + continue + + timestamp = int(m.group(2)) + data = bytes.fromhex(m.group(3).replace(":", "")) + + if m.group(1) == "<": + # Host to controller. + # These are always complete. + sys.stdout.buffer.write( + struct.pack( + "!IIIII", + timestamp // 1000, + timestamp % 1000 * 1000, + len(data) + 4, + len(data) + 4, + _DIR_HOST_TO_CONTROLLER, + ) + ) + sys.stdout.buffer.write(data) + if m.group(1) == ">": + # Controller to host. + # Several of the sources print byte-at-a-time so need to reconstruct packets. + + if not reassemble_packet: + # Use the timestamp of the first fragment. + reassemble_timestamp = timestamp + + reassemble_packet.extend(data) + + if len(reassemble_packet) > 4: + plen = 0 + if reassemble_packet[0] == 1: + # command + plen = 3 + reassemble_packet[3] + elif reassemble_packet[0] == 2: + # acl + plen = 5 + reassemble_packet[3] + (reassemble_packet[4] << 8) + elif reassemble_packet[0] == 4: + # event + plen = 3 + reassemble_packet[2] + + if len(reassemble_packet) >= plen: + # Got a complete packet. + data = reassemble_packet[0:plen] + reassemble_packet = reassemble_packet[plen:] + reassemble_timestamp = timestamp + sys.stdout.buffer.write( + struct.pack( + "!IIIII", + reassemble_timestamp // 1000, + reassemble_timestamp % 1000 * 1000, + len(data) + 4, + len(data) + 4, + _DIR_CONTROLLER_TO_HOST, + ) + ) + sys.stdout.buffer.write(data) + +if reassemble_packet: + print( + "Error: Unknown byte in HCI stream. Remainder:", + reassemble_packet.hex(":"), + file=sys.stderr, + ) diff --git a/tools/makemanifest.py b/tools/makemanifest.py index b2856889b9..a74a6934ae 100644 --- a/tools/makemanifest.py +++ b/tools/makemanifest.py @@ -29,127 +29,14 @@ import sys import os import subprocess +# Always use the mpy-cross from this repo. +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "../mpy-cross")) +import mpy_cross -########################################################################### -# Public functions to be used in the manifest - - -def include(manifest, **kwargs): - """Include another manifest. - - The manifest argument can be a string (filename) or an iterable of - strings. - - Relative paths are resolved with respect to the current manifest file. - - Optional kwargs can be provided which will be available to the - included script via the `options` variable. - - e.g. include("path.py", extra_features=True) - - in path.py: - options.defaults(standard_features=True) - - # freeze minimal modules. - if options.standard_features: - # freeze standard modules. - if options.extra_features: - # freeze extra modules. - """ - - if not isinstance(manifest, str): - for m in manifest: - include(m) - else: - manifest = convert_path(manifest) - with open(manifest) as f: - # Make paths relative to this manifest file while processing it. - # Applies to includes and input files. - prev_cwd = os.getcwd() - os.chdir(os.path.dirname(manifest)) - exec(f.read(), globals(), {"options": IncludeOptions(**kwargs)}) - os.chdir(prev_cwd) - - -def freeze(path, script=None, opt=0): - """Freeze the input, automatically determining its type. A .py script - will be compiled to a .mpy first then frozen, and a .mpy file will be - frozen directly. - - `path` must be a directory, which is the base directory to search for - files from. When importing the resulting frozen modules, the name of - the module will start after `path`, ie `path` is excluded from the - module name. - - If `path` is relative, it is resolved to the current manifest.py. - Use $(MPY_DIR), $(MPY_LIB_DIR), $(PORT_DIR), $(BOARD_DIR) if you need - to access specific paths. - - If `script` is None all files in `path` will be frozen. - - If `script` is an iterable then freeze() is called on all items of the - iterable (with the same `path` and `opt` passed through). - - If `script` is a string then it specifies the file or directory to - freeze, and can include extra directories before the file or last - directory. The file or directory will be searched for in `path`. If - `script` is a directory then all files in that directory will be frozen. - - `opt` is the optimisation level to pass to mpy-cross when compiling .py - to .mpy. - """ - - freeze_internal(KIND_AUTO, path, script, opt) - - -def freeze_as_str(path): - """Freeze the given `path` and all .py scripts within it as a string, - which will be compiled upon import. - """ - - freeze_internal(KIND_AS_STR, path, None, 0) - - -def freeze_as_mpy(path, script=None, opt=0): - """Freeze the input (see above) by first compiling the .py scripts to - .mpy files, then freezing the resulting .mpy files. - """ - - freeze_internal(KIND_AS_MPY, path, script, opt) - - -def freeze_mpy(path, script=None, opt=0): - """Freeze the input (see above), which must be .mpy files that are - frozen directly. - """ - - freeze_internal(KIND_MPY, path, script, opt) - - -########################################################################### -# Internal implementation - -KIND_AUTO = 0 -KIND_AS_STR = 1 -KIND_AS_MPY = 2 -KIND_MPY = 3 +import manifestfile VARS = {} -manifest_list = [] - - -class IncludeOptions: - def __init__(self, **kwargs): - self._kwargs = kwargs - self._defaults = {} - - def defaults(self, **kwargs): - self._defaults = kwargs - - def __getattr__(self, name): - return self._kwargs.get(name, self._defaults.get(name, None)) - class FreezeError(Exception): pass @@ -163,15 +50,6 @@ def system(cmd): return -1, er.output -def convert_path(path): - # Perform variable substitution. - for name, value in VARS.items(): - path = path.replace("$({})".format(name), value) - # Convert to absolute path (so that future operations don't rely on - # still being chdir'ed). - return os.path.abspath(path) - - def get_timestamp(path, default=None): try: stat = os.stat(path) @@ -182,119 +60,64 @@ def get_timestamp(path, default=None): return default -def get_timestamp_newest(path): - ts_newest = 0 - for dirpath, dirnames, filenames in os.walk(path, followlinks=True): - for f in filenames: - ts_newest = max(ts_newest, get_timestamp(os.path.join(dirpath, f))) - return ts_newest - - def mkdir(filename): path = os.path.dirname(filename) if not os.path.isdir(path): os.makedirs(path) -def freeze_internal(kind, path, script, opt): - path = convert_path(path) - if not os.path.isdir(path): - raise FreezeError("freeze path must be a directory: {}".format(path)) - if script is None and kind == KIND_AS_STR: - manifest_list.append((KIND_AS_STR, path, script, opt)) - elif script is None or isinstance(script, str) and script.find(".") == -1: - # Recursively search `path` for files to freeze, optionally restricted - # to a subdirectory specified by `script` - if script is None: - subdir = "" - else: - subdir = "/" + script - for dirpath, dirnames, filenames in os.walk(path + subdir, followlinks=True): - for f in filenames: - freeze_internal(kind, path, (dirpath + "/" + f)[len(path) + 1 :], opt) - elif not isinstance(script, str): - # `script` is an iterable of items to freeze - for s in script: - freeze_internal(kind, path, s, opt) - else: - # `script` should specify an individual file to be frozen - extension_kind = {KIND_AS_MPY: ".py", KIND_MPY: ".mpy"} - if kind == KIND_AUTO: - for k, ext in extension_kind.items(): - if script.endswith(ext): - kind = k - break - else: - print("warn: unsupported file type, skipped freeze: {}".format(script)) - return - wanted_extension = extension_kind[kind] - if not script.endswith(wanted_extension): - raise FreezeError("expecting a {} file, got {}".format(wanted_extension, script)) - manifest_list.append((kind, path, script, opt)) - - # Formerly make-frozen.py. # This generates: # - MP_FROZEN_STR_NAMES macro # - mp_frozen_str_sizes # - mp_frozen_str_content -def generate_frozen_str_content(paths): - def module_name(f): - return f +def generate_frozen_str_content(modules): + output = [ + b"#include \n", + b"#define MP_FROZEN_STR_NAMES \\\n", + ] - modules = [] - output = [b"#include \n"] - - for path in paths: - root = path.rstrip("/") - root_len = len(root) - - for dirpath, dirnames, filenames in os.walk(root): - for f in filenames: - fullpath = dirpath + "/" + f - st = os.stat(fullpath) - modules.append((path, fullpath[root_len + 1 :], st)) - - output.append(b"#define MP_FROZEN_STR_NAMES \\\n") - for _path, f, st in modules: - m = module_name(f) - output.append(b'"%s\\0" \\\n' % m.encode()) + for _, target_path in modules: + print("STR", target_path) + output.append(b'"%s\\0" \\\n' % target_path.encode()) output.append(b"\n") output.append(b"const uint32_t mp_frozen_str_sizes[] = { ") - for _path, f, st in modules: + for full_path, _ in modules: + st = os.stat(full_path) output.append(b"%d, " % st.st_size) output.append(b"0 };\n") output.append(b"const char mp_frozen_str_content[] = {\n") - for path, f, st in modules: - data = open(path + "/" + f, "rb").read() + for full_path, _ in modules: + with open(full_path, "rb") as f: + data = f.read() - # We need to properly escape the script data to create a C string. - # When C parses hex characters of the form \x00 it keeps parsing the hex - # data until it encounters a non-hex character. Thus one must create - # strings of the form "data\x01" "abc" to properly encode this kind of - # data. We could just encode all characters as hex digits but it's nice - # to be able to read the resulting C code as ASCII when possible. + # We need to properly escape the script data to create a C string. + # When C parses hex characters of the form \x00 it keeps parsing the hex + # data until it encounters a non-hex character. Thus one must create + # strings of the form "data\x01" "abc" to properly encode this kind of + # data. We could just encode all characters as hex digits but it's nice + # to be able to read the resulting C code as ASCII when possible. - data = bytearray(data) # so Python2 extracts each byte as an integer - esc_dict = {ord("\n"): b"\\n", ord("\r"): b"\\r", ord('"'): b'\\"', ord("\\"): b"\\\\"} - output.append(b'"') - break_str = False - for c in data: - try: - output.append(esc_dict[c]) - except KeyError: - if 32 <= c <= 126: - if break_str: - output.append(b'" "') - break_str = False - output.append(chr(c).encode()) - else: - output.append(b"\\x%02x" % c) - break_str = True - output.append(b'\\0"\n') + data = bytearray(data) # so Python2 extracts each byte as an integer + esc_dict = {ord("\n"): b"\\n", ord("\r"): b"\\r", ord('"'): b'\\"', ord("\\"): b"\\\\"} + output.append(b'"') + break_str = False + for c in data: + try: + output.append(esc_dict[c]) + except KeyError: + if 32 <= c <= 126: + if break_str: + output.append(b'" "') + break_str = False + output.append(chr(c).encode()) + else: + output.append(b"\\x%02x" % c) + break_str = True + output.append(b'\\0"\n') output.append(b'"\\0"\n};\n\n') return b"".join(output) @@ -329,7 +152,7 @@ def main(): sys.exit(1) # Get paths to tools - MPY_CROSS = VARS["MPY_DIR"] + "/mpy-cross/mpy-cross" + MPY_CROSS = VARS["MPY_DIR"] + "/mpy-cross/build/mpy-cross" if sys.platform == "win32": MPY_CROSS += ".exe" MPY_CROSS = os.getenv("MICROPY_MPYCROSS", MPY_CROSS) @@ -340,14 +163,13 @@ def main(): print("mpy-cross not found at {}, please build it first".format(MPY_CROSS)) sys.exit(1) + manifest = manifestfile.ManifestFile(manifestfile.MODE_FREEZE, VARS) + # Include top-level inputs, to generate the manifest for input_manifest in args.files: try: - if input_manifest.endswith(".py"): - include(input_manifest) - else: - exec(input_manifest) - except FreezeError as er: + manifest.execute(input_manifest) + except manifestfile.ManifestFileError as er: print('freeze error executing "{}": {}'.format(input_manifest, er.args[0])) sys.exit(1) @@ -355,34 +177,42 @@ def main(): str_paths = [] mpy_files = [] ts_newest = 0 - for kind, path, script, opt in manifest_list: - if kind == KIND_AS_STR: - str_paths.append(path) - ts_outfile = get_timestamp_newest(path) - elif kind == KIND_AS_MPY: - infile = "{}/{}".format(path, script) - outfile = "{}/frozen_mpy/{}.mpy".format(args.build_dir, script[:-3]) - ts_infile = get_timestamp(infile) - ts_outfile = get_timestamp(outfile, 0) - if ts_infile >= ts_outfile: - print("MPY", script) - mkdir(outfile) - res, out = system( - [MPY_CROSS] - + args.mpy_cross_flags.split() - + ["-o", outfile, "-s", script, "-O{}".format(opt), infile] + for result in manifest.files(): + if result.kind == manifestfile.KIND_FREEZE_AS_STR: + str_paths.append( + ( + result.full_path, + result.target_path, ) - if res != 0: - print("error compiling {}:".format(infile)) - sys.stdout.buffer.write(out) - raise SystemExit(1) + ) + ts_outfile = result.timestamp + elif result.kind == manifestfile.KIND_FREEZE_AS_MPY: + outfile = "{}/frozen_mpy/{}.mpy".format(args.build_dir, result.target_path[:-3]) + ts_outfile = get_timestamp(outfile, 0) + if result.timestamp >= ts_outfile: + print("MPY", result.target_path) + mkdir(outfile) + # Add __version__ to the end of the file before compiling. + with manifestfile.tagged_py_file(result.full_path, result.metadata) as tagged_path: + try: + mpy_cross.compile( + tagged_path, + dest=outfile, + src_path=result.target_path, + opt=result.opt, + mpy_cross=MPY_CROSS, + extra_args=args.mpy_cross_flags.split(), + ) + except mpy_cross.CrossCompileError as ex: + print("error compiling {}:".format(result.target_path)) + print(ex.args[0]) + raise SystemExit(1) ts_outfile = get_timestamp(outfile) mpy_files.append(outfile) else: - assert kind == KIND_MPY - infile = "{}/{}".format(path, script) - mpy_files.append(infile) - ts_outfile = get_timestamp(infile) + assert result.kind == manifestfile.KIND_FREEZE_MPY + mpy_files.append(result.full_path) + ts_outfile = result.timestamp ts_newest = max(ts_newest, ts_outfile) # Check if output file needs generating diff --git a/tools/manifestfile.py b/tools/manifestfile.py new file mode 100644 index 0000000000..f9b8a70357 --- /dev/null +++ b/tools/manifestfile.py @@ -0,0 +1,614 @@ +#!/usr/bin/env python3 +# +# This file is part of the MicroPython project, http://micropython.org/ +# +# The MIT License (MIT) +# +# Copyright (c) 2022 Jim Mussared +# Copyright (c) 2019 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. + +from __future__ import print_function +import contextlib +import os +import sys +import glob +import tempfile +from collections import namedtuple + +__all__ = ["ManifestFileError", "ManifestFile"] + +# Allow freeze*() etc. +MODE_FREEZE = 1 +# Only allow include/require/module/package. +MODE_COMPILE = 2 +# Same as compile, but handles require(..., pypi="name") as a requirements.txt entry. +MODE_PYPROJECT = 3 + +# In compile mode, .py -> KIND_COMPILE_AS_MPY +# In freeze mode, .py -> KIND_FREEZE_AS_MPY, .mpy->KIND_FREEZE_MPY +KIND_AUTO = 1 +# Freeze-mode only, .py -> KIND_FREEZE_AS_MPY, .mpy->KIND_FREEZE_MPY +KIND_FREEZE_AUTO = 2 + +# Freeze-mode only, The .py file will be frozen as text. +KIND_FREEZE_AS_STR = 3 +# Freeze-mode only, The .py file will be compiled and frozen as bytecode. +KIND_FREEZE_AS_MPY = 4 +# Freeze-mode only, The .mpy file will be frozen directly. +KIND_FREEZE_MPY = 5 +# Compile mode only, the .py file should be compiled to .mpy. +KIND_COMPILE_AS_MPY = 6 + +# File on the local filesystem. +FILE_TYPE_LOCAL = 1 +# URL to file. (TODO) +FILE_TYPE_HTTP = 2 + + +class ManifestFileError(Exception): + pass + + +class ManifestIgnoreException(Exception): + pass + + +class ManifestUsePyPIException(Exception): + def __init__(self, pypi_name): + self.pypi_name = pypi_name + + +# The set of files that this manifest references. +ManifestOutput = namedtuple( + "ManifestOutput", + [ + "file_type", # FILE_TYPE_*. + "full_path", # The input file full path. + "target_path", # The target path on the device. + "timestamp", # Last modified date of the input file. + "kind", # KIND_*. + "metadata", # Metadata for the containing package. + "opt", # Optimisation level (or None). + ], +) + + +# Represents the metadata for a package. +class ManifestPackageMetadata: + def __init__(self, is_require=False): + self._is_require = is_require + self._initialised = False + + self.version = None + self.description = None + self.license = None + self.author = None + + # Annotate a package as being from the python standard library. + self.stdlib = False + + # Allows a python-ecosys package to be annotated with the + # corresponding name in PyPI. e.g. micropython-lib/urequests is based + # on pypi/requests. + self.pypi = None + # For a micropython package, this is the name that we will publish it + # to PyPI as. e.g. micropython-lib/senml publishes as + # pypi/micropython-senml. + self.pypi_publish = None + + def update( + self, + mode, + description=None, + version=None, + license=None, + author=None, + stdlib=False, + pypi=None, + pypi_publish=None, + ): + if self._initialised: + raise ManifestFileError("Duplicate call to metadata().") + + # In MODE_PYPROJECT, if this manifest is being evaluated as a result + # of a require(), then figure out if it should be replaced by a PyPI + # dependency instead. + if mode == MODE_PYPROJECT and self._is_require: + if stdlib: + # No dependency required at all for CPython. + raise ManifestIgnoreException + if pypi_publish or pypi: + # In the case where a package is both based on a PyPI package and + # provides one, preference depending on the published one. + # (This should be pretty rare). + raise ManifestUsePyPIException(pypi_publish or pypi) + + self.description = description + self.version = version + self.license = version + self.author = author + self.pypi = pypi + self.pypi_publish = pypi_publish + self._initialised = True + + def check_initialised(self, mode): + # Ensure that metadata() is the first thing a manifest.py does. + # This is to ensure that we early-exit if it should be replaced by a pypi dependency. + if mode in (MODE_COMPILE, MODE_PYPROJECT): + if not self._initialised: + raise ManifestFileError("metadata() must be the first command in a manifest file.") + + def __str__(self): + return "version={} description={} license={} author={} pypi={} pypi_publish={}".format( + self.version, self.description, self.license, self.author, self.pypi, self.pypi_publish + ) + + +# Turns a dict of options into a object with attributes used to turn the +# kwargs passed to include() and require into the "options" global in the +# included manifest. +# options = IncludeOptions(foo="bar", blah="stuff") +# options.foo # "bar" +# options.blah # "stuff" +class IncludeOptions: + def __init__(self, **kwargs): + self._kwargs = kwargs + self._defaults = {} + + def defaults(self, **kwargs): + self._defaults = kwargs + + def __getattr__(self, name): + return self._kwargs.get(name, self._defaults.get(name, None)) + + +class ManifestFile: + def __init__(self, mode, path_vars=None): + # See MODE_* constants above. + self._mode = mode + # Path substitution variables. + self._path_vars = path_vars or {} + # List of files (as ManifestFileResult) references by this manifest. + self._manifest_files = [] + # List of PyPI dependencies (when mode=MODE_PYPROJECT). + self._pypi_dependencies = [] + # Don't allow including the same file twice. + self._visited = set() + # Stack of metadata for each level. + self._metadata = [ManifestPackageMetadata()] + + def _resolve_path(self, path): + # Convert path to an absolute path, applying variable substitutions. + for name, value in self._path_vars.items(): + if value is not None: + path = path.replace("$({})".format(name), value) + return os.path.abspath(path) + + def _manifest_globals(self, kwargs): + # This is the "API" available to a manifest file. + g = { + "metadata": self.metadata, + "include": self.include, + "require": self.require, + "package": self.package, + "module": self.module, + "options": IncludeOptions(**kwargs), + } + + # Extra legacy functions only for freeze mode. + if self._mode == MODE_FREEZE: + g.update( + { + "freeze": self.freeze, + "freeze_as_str": self.freeze_as_str, + "freeze_as_mpy": self.freeze_as_mpy, + "freeze_mpy": self.freeze_mpy, + } + ) + + return g + + def files(self): + return self._manifest_files + + def pypi_dependencies(self): + # In MODE_PYPROJECT, this will return a list suitable for requirements.txt. + return self._pypi_dependencies + + def execute(self, manifest_file): + if manifest_file.endswith(".py"): + # Execute file from filesystem. + self.include(manifest_file) + else: + # Execute manifest code snippet. + try: + exec(manifest_file, self._manifest_globals({})) + except Exception as er: + raise ManifestFileError("Error in manifest: {}".format(er)) + + def _add_file(self, full_path, target_path, kind=KIND_AUTO, opt=None): + # Check file exists and get timestamp. + try: + stat = os.stat(full_path) + timestamp = stat.st_mtime + except OSError: + raise ManifestFileError("Cannot stat {}".format(full_path)) + + # Map the AUTO kinds to their actual kind based on mode and extension. + _, ext = os.path.splitext(full_path) + if self._mode == MODE_FREEZE: + if kind in ( + KIND_AUTO, + KIND_FREEZE_AUTO, + ): + if ext.lower() == ".py": + kind = KIND_FREEZE_AS_MPY + elif ext.lower() == ".mpy": + kind = KIND_FREEZE_MPY + else: + if kind != KIND_AUTO: + raise ManifestFileError("Not in freeze mode") + if ext.lower() != ".py": + raise ManifestFileError("Expected .py file") + kind = KIND_COMPILE_AS_MPY + + self._manifest_files.append( + ManifestOutput( + FILE_TYPE_LOCAL, full_path, target_path, timestamp, kind, self._metadata[-1], opt + ) + ) + + def _search(self, base_path, package_path, files, exts, kind, opt=None, strict=False): + base_path = self._resolve_path(base_path) + + if files: + # Use explicit list of files (relative to package_path). + for file in files: + if package_path: + file = os.path.join(package_path, file) + self._add_file(os.path.join(base_path, file), file, kind=kind, opt=opt) + else: + if base_path: + prev_cwd = os.getcwd() + os.chdir(self._resolve_path(base_path)) + + # Find all candidate files. + for dirpath, _, filenames in os.walk(package_path or ".", followlinks=True): + for file in filenames: + file = os.path.relpath(os.path.join(dirpath, file), ".") + _, ext = os.path.splitext(file) + if ext.lower() in exts: + self._add_file( + os.path.join(base_path, file), + file, + kind=kind, + opt=opt, + ) + elif strict: + raise ManifestFileError("Unexpected file type") + + if base_path: + os.chdir(prev_cwd) + + def metadata(self, **kwargs): + """ + From within a manifest file, use this to set the metadata for the + package described by current manifest. + + After executing a manifest file (via execute()), call this + to obtain the metadata for the top-level manifest file. + + See ManifestPackageMetadata.update() for valid kwargs. + """ + if kwargs: + self._metadata[-1].update(self._mode, **kwargs) + return self._metadata[-1] + + def include(self, manifest_path, is_require=False, **kwargs): + """ + Include another manifest. + + The manifest argument can be a string (filename) or an iterable of + strings. + + Relative paths are resolved with respect to the current manifest file. + + If the path is to a directory, then it implicitly includes the + manifest.py file inside that directory. + + Optional kwargs can be provided which will be available to the + included script via the `options` variable. + + e.g. include("path.py", extra_features=True) + + in path.py: + options.defaults(standard_features=True) + + # freeze minimal modules. + if options.standard_features: + # freeze standard modules. + if options.extra_features: + # freeze extra modules. + """ + if is_require: + self._metadata[-1].check_initialised(self._mode) + + if not isinstance(manifest_path, str): + for m in manifest_path: + self.include(m, **kwargs) + else: + manifest_path = self._resolve_path(manifest_path) + # Including a directory grabs the manifest.py inside it. + if os.path.isdir(manifest_path): + manifest_path = os.path.join(manifest_path, "manifest.py") + if manifest_path in self._visited: + return + self._visited.add(manifest_path) + if is_require: + # This include is the result of require("name"), so push a new + # package metadata onto the stack. + self._metadata.append(ManifestPackageMetadata(is_require=True)) + try: + with open(manifest_path) as f: + # Make paths relative to this manifest file while processing it. + # Applies to includes and input files. + prev_cwd = os.getcwd() + os.chdir(os.path.dirname(manifest_path)) + try: + exec(f.read(), self._manifest_globals(kwargs)) + finally: + os.chdir(prev_cwd) + except ManifestIgnoreException: + # e.g. MODE_PYPROJECT and this was a stdlib dependency. No-op. + pass + except ManifestUsePyPIException as e: + # e.g. MODE_PYPROJECT and this was a package from + # python-ecosys. Add PyPI dependency instead. + self._pypi_dependencies.append(e.pypi_name) + except Exception as e: + raise ManifestFileError("Error in manifest file: {}: {}".format(manifest_path, e)) + if is_require: + self._metadata.pop() + + def require(self, name, version=None, unix_ffi=False, pypi=None, **kwargs): + """ + Require a module 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. + """ + self._metadata[-1].check_initialised(self._mode) + + if self._mode == MODE_PYPROJECT and pypi: + # In PYPROJECT mode, allow overriding the PyPI dependency name + # explicitly. Otherwise if the dependent package has metadata + # (pypi_publish) or metadata(pypi) we will use that. + self._pypi_dependencies.append(pypi) + return + + if self._path_vars["MPY_LIB_DIR"]: + lib_dirs = ["micropython", "python-stdlib", "python-ecosys"] + if unix_ffi: + # 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 os.path.basename(root) == name and "manifest.py" in filenames: + self.include(root, is_require=True, **kwargs) + return + + raise ValueError("Library 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 package(self, package_path, files=None, base_path=".", opt=None): + """ + Define a package, optionally restricting to a set of files. + + Simple case, a package in the current directory: + package("foo") + will include all .py files in foo, and will be stored as foo/bar/baz.py. + + If the package isn't in the current directory, use base_path: + package("foo", base_path="src") + + To restrict to certain files in the package use files (note: paths should be relative to the package): + package("foo", files=["bar/baz.py"]) + """ + self._metadata[-1].check_initialised(self._mode) + + # Include "base_path/package_path/**/*.py" --> "package_path/**/*.py" + self._search(base_path, package_path, files, exts=(".py",), kind=KIND_AUTO, opt=opt) + + def module(self, module_path, base_path=".", opt=None): + """ + Include a single Python file as a module. + + If the file is in the current directory: + module("foo.py") + + Otherwise use base_path to locate the file: + module("foo.py", "src/drivers") + """ + self._metadata[-1].check_initialised(self._mode) + + # Include "base_path/module_path" --> "module_path" + base_path = self._resolve_path(base_path) + _, ext = os.path.splitext(module_path) + if ext.lower() != ".py": + raise ManifestFileError("module must be .py file") + # TODO: version None + self._add_file(os.path.join(base_path, module_path), module_path, opt=opt) + + def _freeze_internal(self, path, script, exts, kind, opt): + if script is None: + self._search(path, None, None, exts=exts, kind=kind, opt=opt) + elif isinstance(script, str) and os.path.isdir(os.path.join(path, script)): + self._search(path, script, None, exts=exts, kind=kind, opt=opt) + elif not isinstance(script, str): + self._search(path, None, script, exts=exts, kind=kind, opt=opt) + else: + self._search(path, None, (script,), exts=exts, kind=kind, opt=opt) + + def freeze(self, path, script=None, opt=None): + """ + Freeze the input, automatically determining its type. A .py script + will be compiled to a .mpy first then frozen, and a .mpy file will be + frozen directly. + + `path` must be a directory, which is the base directory to _search for + files from. When importing the resulting frozen modules, the name of + the module will start after `path`, ie `path` is excluded from the + module name. + + If `path` is relative, it is resolved to the current manifest.py. + Use $(MPY_DIR), $(MPY_LIB_DIR), $(PORT_DIR), $(BOARD_DIR) if you need + to access specific paths. + + If `script` is None all files in `path` will be frozen. + + If `script` is an iterable then freeze() is called on all items of the + iterable (with the same `path` and `opt` passed through). + + If `script` is a string then it specifies the file or directory to + freeze, and can include extra directories before the file or last + directory. The file or directory will be _searched for in `path`. If + `script` is a directory then all files in that directory will be frozen. + + `opt` is the optimisation level to pass to mpy-cross when compiling .py + to .mpy. + """ + self._freeze_internal( + path, + script, + exts=( + ".py", + ".mpy", + ), + kind=KIND_FREEZE_AUTO, + opt=opt, + ) + + def freeze_as_str(self, path): + """ + Freeze the given `path` and all .py scripts within it as a string, + which will be compiled upon import. + """ + self._search(path, None, None, exts=(".py",), kind=KIND_FREEZE_AS_STR) + + def freeze_as_mpy(self, path, script=None, opt=None): + """ + Freeze the input (see above) by first compiling the .py scripts to + .mpy files, then freezing the resulting .mpy files. + """ + self._freeze_internal(path, script, exts=(".py",), kind=KIND_FREEZE_AS_MPY, opt=opt) + + def freeze_mpy(self, path, script=None, opt=None): + """ + Freeze the input (see above), which must be .mpy files that are + frozen directly. + """ + self._freeze_internal(path, script, exts=(".mpy",), kind=KIND_FREEZE_MPY, opt=opt) + + +# Generate a temporary file with a line appended to the end that adds __version__. +@contextlib.contextmanager +def tagged_py_file(path, metadata): + dest_fd, dest_path = tempfile.mkstemp(suffix=".py", text=True) + try: + with os.fdopen(dest_fd, "w") as dest: + with open(path, "r") as src: + contents = src.read() + dest.write(contents) + + # Don't overwrite a version definition if the file already has one in it. + if metadata.version and "__version__ =" not in contents: + dest.write("\n\n__version__ = {}\n".format(repr(metadata.version))) + yield dest_path + finally: + os.unlink(dest_path) + + +def main(): + import argparse + + cmd_parser = argparse.ArgumentParser(description="List the files referenced by a manifest.") + cmd_parser.add_argument("--freeze", action="store_true", help="freeze mode") + cmd_parser.add_argument("--compile", action="store_true", help="compile mode") + cmd_parser.add_argument("--pyproject", action="store_true", help="pyproject mode") + cmd_parser.add_argument( + "--lib", + default=os.path.join(os.path.dirname(__file__), "../lib/micropython-lib"), + help="path to micropython-lib repo", + ) + cmd_parser.add_argument("--port", default=None, help="path to port dir") + cmd_parser.add_argument("--board", default=None, help="path to board dir") + cmd_parser.add_argument( + "--top", + default=os.path.join(os.path.dirname(__file__), ".."), + help="path to micropython repo", + ) + cmd_parser.add_argument("files", nargs="+", help="input manifest.py") + args = cmd_parser.parse_args() + + path_vars = { + "MPY_DIR": os.path.abspath(args.top) if args.top else None, + "BOARD_DIR": os.path.abspath(args.board) if args.board else None, + "PORT_DIR": os.path.abspath(args.port) if args.port else None, + "MPY_LIB_DIR": os.path.abspath(args.lib) if args.lib else None, + } + + mode = None + if args.freeze: + mode = MODE_FREEZE + elif args.compile: + mode = MODE_COMPILE + elif args.pyproject: + mode = MODE_PYPROJECT + else: + print("Error: No mode specified.", file=sys.stderr) + exit(1) + + m = ManifestFile(mode, path_vars) + for manifest_file in args.files: + try: + m.execute(manifest_file) + except ManifestFileError as er: + print(er, file=sys.stderr) + exit(1) + print(m.metadata()) + for f in m.files(): + print(f) + if mode == MODE_PYPROJECT: + for r in m.pypi_dependencies(): + print("pypi-require:", r) + + +if __name__ == "__main__": + main() diff --git a/tools/merge_micropython.py b/tools/merge_micropython.py index a6d3efcc0d..b596cd59c5 100644 --- a/tools/merge_micropython.py +++ b/tools/merge_micropython.py @@ -2,11 +2,14 @@ This is a helper script for merging in new versions of MicroPython. You *must* evaluate its correctness and adapt it for each MP version. This is committed in the repo more for reference than "fire and forget" use. + +Updated for v1.20.0 merge - dhalbert """ +from io import StringIO + import sh from sh import git -from io import StringIO out_buf = StringIO() @@ -16,17 +19,18 @@ ports_to_delete = [ "embed", "esp32", "esp8266", - "javascript", "mimxrt", "minimal", "pic16bit", "powerpc", "qemu-arm", + "raspberrypi", "renesas-ra", "rp2", "samd", "stm32", "teensy", + "webassembly", "windows", "zephyr", ] @@ -90,25 +94,47 @@ while line: # Delete docs and tests for things we don't need anymore docs_to_delete = [ "conf.py", - "pyboard", - "library/pyb.*", - "library/esp*", - "library/lcd160cr.rst", - "esp8266", - "wipy", - "esp32", - "library/machine.*", - "library/network.*", - "library/ubluetooth.rst", - "library/ucryptolib.rst", - "library/uos.rst", - "library/usocket.rst", - "library/ussl.rst", - "library/ustruct.rst", "develop", - "reference", + "differences", + "esp32", + "esp8266", + "library/bluetooth.rst", + "library/btree.rst", + "library/cryptolib.rst", + "library/esp*.rst", + "library/framebuf.rst", + "library/hashlib.rst", + "library/lcd160cr.rst", + "library/machine*.rst", + "library/math.rst", + "library/network*.rst", + "library/os.rst", + "library/pyb*.rst", + "library/random.rst", + "library/rp2*.rst", + "library/uos.rst", + "library/socket.rst", + "library/ssl.rst", + "library/stm.rst", + "library/struct.rst", + "library/_thread.rst", + "library/time.rst", + "library/uasyncio.rst", + "library/uctypes.rst", + "library/wipy.rst", + "library/wm8960.rst", + "library/zephyr*.rst", + "library/zlib.rst", "make.bat", + "mimxrt", + "pyboard", + "reference", + "renesas-ra", + "rp2", + "samd", "templates/topindex.html", + "wipy", + "zephyr", ] for d in docs_to_delete: try: @@ -117,13 +143,13 @@ for d in docs_to_delete: pass tests_to_delete = [ - "wipy", - "pyb", - "multi_net", - "net_inet", - "multi_bluetooth", "esp32", + "multi_bluetooth", + "multi_net", "net_hosted", + "net_inet", + "pyb", + "wipy", ] for t in tests_to_delete: try: @@ -133,12 +159,19 @@ for t in tests_to_delete: libs_to_delete = [ - "mynewt-nimble", - "nxp_driver", - "mbedtls", - "mbedtls_errors", "asf4", "btstack", + "libhydrogen", + "lwip", + "mbedtls", + "mbedtls_errors", + "micropython-lib", + "mynewt-nimble", + "nrfx", + "nxp_driver", + "pico-sdk", + "stm32lib", + "wiznet5k", ] for l in libs_to_delete: try: @@ -147,30 +180,34 @@ for l in libs_to_delete: pass extmod_to_delete = [ - "machine_*", - "webrepl", - "uzlib", - "ussl*", - "modlwip.c", - "moducryptolib.c", - "modbluetooth*", - "network*", - "nimble", "btstack", - "mpbthci*", + "extmod.cmake", + "machine_*", + "mbedtls", + "modbluetooth.*", + "modbtree.*", + "modframebuf.*", + "modlwip.*", + "modnetwork.*", + "modonewire.*", + "moducryptolib.*", + "modusocket.*", + "modussl_*.*", + "modutimeq.*", + "moduwebsocket.*", + "modwebrepl.*", + "mpbthci.*", + "network_*.*", + "nimble", ] for e in extmod_to_delete: try: git.rm("-rf", "extmod/" + e) except sh.ErrorReturnCode_128 as error: print(error) - pass top_delete = [ "drivers", - ".travis.yml", - ".github/FUNDING.yml", - ".github/workflows/docs.yml", "README.md", "CODEOFCONDUCT.md", "CODECONVENTIONS.md", @@ -181,8 +218,31 @@ for t in top_delete: except sh.ErrorReturnCode_128: pass +dot_github_delete = [ + ".github/dependabot.yml", + ".github/FUNDING.yml", + ".github/ISSUE_TEMPLATE/documentation.md", + ".github/ISSUE_TEMPLATE/security.md", + ".github/workflows/code_formatting.yml", + ".github/workflows/code_size_comment.yml", + ".github/workflows/code_size.yml", + ".github/workflows/commit_formatting.yml", + ".github/workflows/docs.yml", + ".github/workflows/examples.yml", + ".github/workflows/mpremote.yml", + ".github/workflows/mpy_format.yml", + ".github/workflows/mpy_format.yml", + ".github/workflows/ports_*.yml", +] +for t in dot_github_delete: + try: + git.rm("-rf", t) + except sh.ErrorReturnCode_128: + pass + # Always ours: always_ours = [ + ".github", "devices", "supervisor", "shared-bindings", @@ -209,24 +269,24 @@ for ours in always_ours: print(state, path) line = out_buf.readline() -# Check to see if any files changed only in formatting -out_buf = StringIO() -git.status("--porcelain=1", ".", _out=out_buf) -out_buf.seek(0) -line = out_buf.readline() -while line: - state = line.split()[0] - if state in ("D", "R", "DD"): - line = out_buf.readline() - continue - state, path = line.split() - log_buf = StringIO() - git.log("--pretty=tformat:%H", "25ae98f..HEAD", path, _out=log_buf, _tty_out=False) - log_buf.seek(0) - commits = [] - for line in log_buf.readlines(): - commits.append(line.strip()) - if state in ["UU", "M"] and commits == ["a52eb88031620a81521b937f2a0651dbac2bb350"]: - git.checkout("--theirs", path) - git.add(path) - line = out_buf.readline() +# # Check to see if any files changed only in formatting +# out_buf = StringIO() +# git.status("--porcelain=1", ".", _out=out_buf) +# out_buf.seek(0) +# line = out_buf.readline() +# while line: +# state = line.split()[0] +# if state in ("D", "R", "DD"): +# line = out_buf.readline() +# continue +# state, path = line.split() +# log_buf = StringIO() +# git.log("--pretty=tformat:%H", "25ae98f..HEAD", path, _out=log_buf, _tty_out=False) +# log_buf.seek(0) +# commits = [] +# for line in log_buf.readlines(): +# commits.append(line.strip()) +# if state in ["UU", "M"] and commits == ["a52eb88031620a81521b937f2a0651dbac2bb350"]: +# git.checkout("--theirs", path) +# git.add(path) +# line = out_buf.readline() diff --git a/tools/metrics.py b/tools/metrics.py index c857d0733e..da4d188568 100755 --- a/tools/metrics.py +++ b/tools/metrics.py @@ -60,12 +60,14 @@ class PortData: port_data = { "b": PortData("bare-arm", "bare-arm", "build/firmware.elf"), "m": PortData("minimal x86", "minimal", "build/firmware.elf"), - "u": PortData("unix x64", "unix", "micropython"), - "n": PortData("unix nanbox", "unix", "micropython-nanbox", "VARIANT=nanbox"), + "u": PortData("unix x64", "unix", "build-standard/micropython"), + "n": PortData("unix nanbox", "unix", "build-nanbox/micropython", "VARIANT=nanbox"), "s": PortData("stm32", "stm32", "build-PYBV10/firmware.elf", "BOARD=PYBV10"), "c": PortData("cc3200", "cc3200", "build/WIPY/release/application.axf", "BTARGET=application"), "8": PortData("esp8266", "esp8266", "build-GENERIC/firmware.elf"), "3": PortData("esp32", "esp32", "build-GENERIC/micropython.elf"), + "x": PortData("mimxrt", "mimxrt", "build-TEENSY40/firmware.elf"), + "e": PortData("renesas-ra", "renesas-ra", "build-RA6M2_EK/firmware.elf"), "r": PortData("nrf", "nrf", "build-pca10040/firmware.elf"), "p": PortData("rp2", "rp2", "build-PICO/firmware.elf"), "d": PortData("samd", "samd", "build-ADAFRUIT_ITSYBITSY_M4_EXPRESS/firmware.elf"), diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index 3a59ac6590..a02104322c 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -88,6 +88,7 @@ class FreezeError(Exception): class Config: MPY_VERSION = 6 + MPY_SUB_VERSION = 1 MICROPY_LONGINT_IMPL_NONE = 0 MICROPY_LONGINT_IMPL_LONGLONG = 1 MICROPY_LONGINT_IMPL_MPZ = 2 @@ -934,6 +935,10 @@ class RawCode(object): " .line_info = fun_data_%s + %u," % (self.escaped_name, self.offset_line_info) ) + print( + " .line_info_top = fun_data_%s + %u," + % (self.escaped_name, self.offset_closure_info) + ) print( " .opcodes = fun_data_%s + %u," % (self.escaped_name, self.offset_opcodes) ) @@ -1334,6 +1339,9 @@ def read_mpy(filename): feature_byte = header[2] mpy_native_arch = feature_byte >> 2 if mpy_native_arch != MP_NATIVE_ARCH_NONE: + mpy_sub_version = feature_byte & 3 + if mpy_sub_version != config.MPY_SUB_VERSION: + raise MPYReadError(filename, "incompatible .mpy sub-version") if config.native_arch == MP_NATIVE_ARCH_NONE: config.native_arch = mpy_native_arch elif config.native_arch != mpy_native_arch: @@ -1657,7 +1665,9 @@ def merge_mpy(compiled_modules, output_file): else: main_cm_idx = None for idx, cm in enumerate(compiled_modules): - if cm.header[2]: + feature_byte = cm.header[2] + mpy_native_arch = feature_byte >> 2 + if mpy_native_arch: # Must use qstr_table and obj_table from this raw_code if main_cm_idx is not None: raise Exception("can't merge files when more than one contains native code") @@ -1669,7 +1679,7 @@ def merge_mpy(compiled_modules, output_file): header = bytearray(4) header[0] = ord("C") header[1] = config.MPY_VERSION - header[2] = config.native_arch << 2 + header[2] = config.native_arch << 2 | config.MPY_SUB_VERSION if config.native_arch else 0 header[3] = config.mp_small_int_bits merged_mpy.extend(header) diff --git a/tools/mpy_ld.py b/tools/mpy_ld.py index 4d01f52df8..1241b98ab8 100755 --- a/tools/mpy_ld.py +++ b/tools/mpy_ld.py @@ -36,6 +36,7 @@ import makeqstrdata as qstrutil # MicroPython constants MPY_VERSION = 6 +MPY_SUB_VERSION = 1 MP_CODE_BYTECODE = 2 MP_CODE_NATIVE_VIPER = 4 MP_NATIVE_ARCH_X86 = 1 @@ -919,7 +920,15 @@ def build_mpy(env, entry_offset, fmpy, native_qstr_vals, native_qstr_objs): out.open(fmpy) # MPY: header +<<<<<<< ours out.write_bytes(bytearray([ord("C"), MPY_VERSION, env.arch.mpy_feature, MP_SMALL_INT_BITS])) +======= + out.write_bytes( + bytearray( + [ord("M"), MPY_VERSION, env.arch.mpy_feature | MPY_SUB_VERSION, MP_SMALL_INT_BITS] + ) + ) +>>>>>>> theirs # MPY: n_qstr out.write_uint(1 + len(native_qstr_vals)) diff --git a/tools/pyboard.py b/tools/pyboard.py index a521a04a2e..bfec3e1972 100755 --- a/tools/pyboard.py +++ b/tools/pyboard.py @@ -48,10 +48,13 @@ Or: """ +import ast +import os +import struct import sys import time -import os -import ast + +from collections import namedtuple try: stdout = sys.stdout.buffer @@ -67,7 +70,15 @@ def stdout_write_bytes(b): class PyboardError(Exception): - pass + def convert(self, info): + if len(self.args) >= 3: + if b"OSError" in self.args[2] and b"ENOENT" in self.args[2]: + return OSError(errno.ENOENT, info) + + return self + + +listdir_result = namedtuple("dir_result", ["name", "st_mode", "st_ino", "st_size"]) class TelnetToSerial: @@ -256,7 +267,19 @@ class Pyboard: delayed = False for attempt in range(wait + 1): try: - self.serial = serial.Serial(device, **serial_kwargs) + if 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, IOError): # Py2 and Py3 have different errors if wait == 0: @@ -360,7 +383,7 @@ class Pyboard: def raw_paste_write(self, command_bytes): # Read initial header, with window size. data = self.serial.read(2) - window_size = data[0] | data[1] << 8 + window_size = struct.unpack(" 1: + print(str(er.args[2], "ascii")) + else: + print(er) pyb.exit_raw_repl() pyb.close() sys.exit(1) @@ -737,7 +854,7 @@ def main(): # do filesystem commands, if given if args.filesystem: - filesystem_command(pyb, args.files) + filesystem_command(pyb, args.files, verbose=True) del args.files[:] # run the command, if given diff --git a/tools/uncrustify.cfg b/tools/uncrustify.cfg index 3812cd3182..bf5e37bf3f 100644 --- a/tools/uncrustify.cfg +++ b/tools/uncrustify.cfg @@ -1,5 +1,8 @@ # Uncrustify-0.71.0_f +# IMPORTANT: Output is different if using Uncrustify 0.73 or newer, and config file format has changed in newer versions. +# Use version 0.71 or 0.72 to get matching code formatting. + # # General options # diff --git a/tools/upip.py b/tools/upip.py deleted file mode 100644 index a62d04f66b..0000000000 --- a/tools/upip.py +++ /dev/null @@ -1,351 +0,0 @@ -# upip - Package manager for MicroPython - -# SPDX-FileCopyrightText: Copyright (c) 2015-2018 Paul Sokolovsky -# SPDX-FileCopyrightText: 2014 MicroPython & CircuitPython contributors (https://github.com/adafruit/circuitpython/graphs/contributors) -# -# SPDX-License-Identifier: MIT - -import sys -import gc -import uos as os -import uerrno as errno -import ujson as json -import uzlib -import upip_utarfile as tarfile - -gc.collect() - - -debug = False -index_urls = ["https://micropython.org/pi", "https://pypi.org/pypi"] -install_path = None -cleanup_files = [] -gzdict_sz = 16 + 15 - -file_buf = bytearray(512) - - -class NotFoundError(Exception): - pass - - -def op_split(path): - if path == "": - return ("", "") - r = path.rsplit("/", 1) - if len(r) == 1: - return ("", path) - head = r[0] - if not head: - head = "/" - return (head, r[1]) - - -# Expects *file* name -def _makedirs(name, mode=0o777): - ret = False - s = "" - comps = name.rstrip("/").split("/")[:-1] - if comps[0] == "": - s = "/" - for c in comps: - if s and s[-1] != "/": - s += "/" - s += c - try: - os.mkdir(s) - ret = True - except OSError as e: - if e.errno != errno.EEXIST and e.errno != errno.EISDIR: - raise e - ret = False - return ret - - -def save_file(fname, subf): - global file_buf - with open(fname, "wb") as outf: - while True: - sz = subf.readinto(file_buf) - if not sz: - break - outf.write(file_buf, sz) - - -def install_tar(f, prefix): - meta = {} - for info in f: - # print(info) - fname = info.name - try: - fname = fname[fname.index("/") + 1 :] - except ValueError: - fname = "" - - save = True - for p in ("setup.", "PKG-INFO", "README"): - # print(fname, p) - if fname.startswith(p) or ".egg-info" in fname: - if fname.endswith("/requires.txt"): - meta["deps"] = f.extractfile(info).read() - save = False - if debug: - print("Skipping", fname) - break - - if save: - outfname = prefix + fname - if info.type != tarfile.DIRTYPE: - if debug: - print("Extracting " + outfname) - _makedirs(outfname) - subf = f.extractfile(info) - save_file(outfname, subf) - return meta - - -def expandhome(s): - if "~/" in s: - h = os.getenv("HOME") - s = s.replace("~/", h + "/") - return s - - -import ussl -import usocket - -warn_ussl = True - - -def url_open(url): - global warn_ussl - - if debug: - print(url) - - proto, _, host, urlpath = url.split("/", 3) - try: - port = 443 - if ":" in host: - host, port = host.split(":") - port = int(port) - ai = usocket.getaddrinfo(host, port, 0, usocket.SOCK_STREAM) - except OSError as e: - fatal("Unable to resolve %s (no Internet?)" % host, e) - # print("Address infos:", ai) - ai = ai[0] - - s = usocket.socket(ai[0], ai[1], ai[2]) - try: - # print("Connect address:", addr) - s.connect(ai[-1]) - - if proto == "https:": - s = ussl.wrap_socket(s, server_hostname=host) - if warn_ussl: - print("Warning: %s SSL certificate is not validated" % host) - warn_ussl = False - - # MicroPython rawsocket module supports file interface directly - s.write("GET /%s HTTP/1.0\r\nHost: %s:%s\r\n\r\n" % (urlpath, host, port)) - l = s.readline() - protover, status, msg = l.split(None, 2) - if status != b"200": - if status == b"404" or status == b"301": - raise NotFoundError("Package not found") - raise ValueError(status) - while 1: - l = s.readline() - if not l: - raise ValueError("Unexpected EOF in HTTP headers") - if l == b"\r\n": - break - except Exception as e: - s.close() - raise e - - return s - - -def get_pkg_metadata(name): - for url in index_urls: - try: - f = url_open("%s/%s/json" % (url, name)) - except NotFoundError: - continue - try: - return json.load(f) - finally: - f.close() - raise NotFoundError("Package not found") - - -def fatal(msg, exc=None): - print("Error:", msg) - if exc and debug: - raise exc - sys.exit(1) - - -def install_pkg(pkg_spec, install_path): - package = pkg_spec.split("==") - data = get_pkg_metadata(package[0]) - - if len(package) == 1: - latest_ver = data["info"]["version"] - else: - latest_ver = package[1] - packages = data["releases"][latest_ver] - del data - gc.collect() - assert len(packages) == 1 - package_url = packages[0]["url"] - print("Installing %s %s from %s" % (pkg_spec, latest_ver, package_url)) - f1 = url_open(package_url) - try: - f2 = uzlib.DecompIO(f1, gzdict_sz) - f3 = tarfile.TarFile(fileobj=f2) - meta = install_tar(f3, install_path) - finally: - f1.close() - del f3 - del f2 - gc.collect() - return meta - - -def install(to_install, install_path=None): - # Calculate gzip dictionary size to use - global gzdict_sz - sz = gc.mem_free() + gc.mem_alloc() - if sz <= 65536: - gzdict_sz = 16 + 12 - - if install_path is None: - install_path = get_install_path() - if install_path[-1] != "/": - install_path += "/" - if not isinstance(to_install, list): - to_install = [to_install] - print("Installing to: " + install_path) - # sets would be perfect here, but don't depend on them - installed = [] - try: - while to_install: - if debug: - print("Queue:", to_install) - pkg_spec = to_install.pop(0) - if pkg_spec in installed: - continue - meta = install_pkg(pkg_spec, install_path) - installed.append(pkg_spec) - if debug: - print(meta) - deps = meta.get("deps", "").rstrip() - if deps: - deps = deps.decode("utf-8").split("\n") - to_install.extend(deps) - except Exception as e: - print( - "Error installing '{}': {}, packages may be partially installed".format(pkg_spec, e), - file=sys.stderr, - ) - - -def get_install_path(): - global install_path - if install_path is None: - # sys.path[0] is current module's path - install_path = sys.path[1] - if install_path == ".frozen": - install_path = sys.path[2] - install_path = expandhome(install_path) - return install_path - - -def cleanup(): - for fname in cleanup_files: - try: - os.unlink(fname) - except OSError: - print("Warning: Cannot delete " + fname) - - -def help(): - print( - """\ -upip - Simple PyPI package manager for MicroPython -Usage: micropython -m upip install [-p ] ... | -r -import upip; upip.install(package_or_list, []) - -If isn't given, packages will be installed to sys.path[1], or -sys.path[2] if the former is .frozen (path can be set from MICROPYPATH -environment variable if supported).""" - ) - print("Default install path:", get_install_path()) - print( - """\ - -Note: only MicroPython packages (usually, named micropython-*) are supported -for installation, upip does not support arbitrary code in setup.py. -""" - ) - - -def main(): - global debug - global index_urls - global install_path - install_path = None - - if len(sys.argv) < 2 or sys.argv[1] == "-h" or sys.argv[1] == "--help": - help() - return - - if sys.argv[1] != "install": - fatal("Only 'install' command supported") - - to_install = [] - - i = 2 - while i < len(sys.argv) and sys.argv[i][0] == "-": - opt = sys.argv[i] - i += 1 - if opt == "-h" or opt == "--help": - help() - return - elif opt == "-p": - install_path = sys.argv[i] - i += 1 - elif opt == "-r": - list_file = sys.argv[i] - i += 1 - with open(list_file) as f: - while True: - l = f.readline() - if not l: - break - if l[0] == "#": - continue - to_install.append(l.rstrip()) - elif opt == "-i": - index_urls = [sys.argv[i]] - i += 1 - elif opt == "--debug": - debug = True - else: - fatal("Unknown/unsupported option: " + opt) - - to_install.extend(sys.argv[i:]) - if not to_install: - help() - return - - install(to_install) - - if not debug: - cleanup() - - -if __name__ == "__main__": - main() diff --git a/tools/upip_utarfile.py b/tools/upip_utarfile.py deleted file mode 100644 index 3e527fad8d..0000000000 --- a/tools/upip_utarfile.py +++ /dev/null @@ -1,99 +0,0 @@ -# SPDX-FileCopyrightText: 2014 MicroPython & CircuitPython contributors (https://github.com/adafruit/circuitpython/graphs/contributors) -# -# SPDX-License-Identifier: MIT - -import uctypes - -# http://www.gnu.org/software/tar/manual/html_node/Standard.html -TAR_HEADER = { - "name": (uctypes.ARRAY | 0, uctypes.UINT8 | 100), - "size": (uctypes.ARRAY | 124, uctypes.UINT8 | 11), -} - -DIRTYPE = "dir" -REGTYPE = "file" - - -def roundup(val, align): - return (val + align - 1) & ~(align - 1) - - -class FileSection: - def __init__(self, f, content_len, aligned_len): - self.f = f - self.content_len = content_len - self.align = aligned_len - content_len - - def read(self, sz=65536): - if self.content_len == 0: - return b"" - if sz > self.content_len: - sz = self.content_len - data = self.f.read(sz) - sz = len(data) - self.content_len -= sz - return data - - def readinto(self, buf): - if self.content_len == 0: - return 0 - if len(buf) > self.content_len: - buf = memoryview(buf)[: self.content_len] - sz = self.f.readinto(buf) - self.content_len -= sz - return sz - - def skip(self): - sz = self.content_len + self.align - if sz: - buf = bytearray(16) - while sz: - s = min(sz, 16) - self.f.readinto(buf, s) - sz -= s - - -class TarInfo: - def __str__(self): - return "TarInfo(%r, %s, %d)" % (self.name, self.type, self.size) - - -class TarFile: - def __init__(self, name=None, fileobj=None): - if fileobj: - self.f = fileobj - else: - self.f = open(name, "rb") - self.subf = None - - def next(self): - if self.subf: - self.subf.skip() - buf = self.f.read(512) - if not buf: - return None - - h = uctypes.struct(uctypes.addressof(buf), TAR_HEADER, uctypes.LITTLE_ENDIAN) - - # Empty block means end of archive - if h.name[0] == 0: - return None - - d = TarInfo() - d.name = str(h.name, "utf-8").rstrip("\0") - d.size = int(bytes(h.size), 8) - d.type = [REGTYPE, DIRTYPE][d.name[-1] == "/"] - self.subf = d.subf = FileSection(self.f, d.size, roundup(d.size, 512)) - return d - - def __iter__(self): - return self - - def __next__(self): - v = self.next() - if v is None: - raise StopIteration - return v - - def extractfile(self, tarinfo): - return tarinfo.subf diff --git a/tools/verifygitlog.py b/tools/verifygitlog.py index 09c10c80d6..c237cb46be 100755 --- a/tools/verifygitlog.py +++ b/tools/verifygitlog.py @@ -7,6 +7,8 @@ import sys verbosity = 0 # Show what's going on, 0 1 or 2. suggestions = 1 # Set to 0 to not include lengthy suggestions in error messages. +ignore_prefixes = [] + def verbose(*args): if verbosity: @@ -18,6 +20,22 @@ def very_verbose(*args): print(*args) +class ErrorCollection: + # Track errors and warnings as the program runs + def __init__(self): + self.has_errors = False + self.has_warnings = False + self.prefix = "" + + def error(self, text): + print("error: {}{}".format(self.prefix, text)) + self.has_errors = True + + def warning(self, text): + print("warning: {}{}".format(self.prefix, text)) + self.has_warnings = True + + def git_log(pretty_format, *args): # Delete pretty argument from user args so it doesn't interfere with what we do. args = ["git", "log"] + [arg for arg in args if "--pretty" not in args] @@ -28,83 +46,108 @@ def git_log(pretty_format, *args): yield line.decode().rstrip("\r\n") -def verify(sha): +def diagnose_subject_line(subject_line, subject_line_format, err): + err.error("Subject line: " + subject_line) + if not subject_line.endswith("."): + err.error('* should end with "."') + if not re.match(r"^[^!]+: ", subject_line): + err.error('* should start with "path: "') + if re.match(r"^[^!]+: *$", subject_line): + err.error("* should contain a subject after the path.") + m = re.match(r"^[^!]+: ([a-z][^ ]*)", subject_line) + if m: + err.error('* first word of subject ("{}") should be capitalised.'.format(m.group(1))) + if re.match(r"^[^!]+: [^ ]+$", subject_line): + err.error("* subject should contain more than one word.") + err.error("* should match: " + repr(subject_line_format)) + err.error('* Example: "py/runtime: Add support for foo to bar."') + + +def verify(sha, err): verbose("verify", sha) - errors = [] - warnings = [] - - def error_text(err): - return "commit " + sha + ": " + err - - def error(err): - errors.append(error_text(err)) - - def warning(err): - warnings.append(error_text(err)) + err.prefix = "commit " + sha + ": " # Author and committer email. for line in git_log("%ae%n%ce", sha, "-n1"): very_verbose("email", line) if "noreply" in line: - error("Unwanted email address: " + line) + err.error("Unwanted email address: " + line) # Message body. raw_body = list(git_log("%B", sha, "-n1")) + verify_message_body(raw_body, err) + + +def verify_message_body(raw_body, err): if not raw_body: - error("Message is empty") - return errors, warnings + err.error("Message is empty") + return # Subject line. subject_line = raw_body[0] + for prefix in ignore_prefixes: + if subject_line.startswith(prefix): + verbose("Skipping ignored commit message") + return very_verbose("subject_line", subject_line) subject_line_format = r"^[^!]+: [A-Z]+.+ .+\.$" if not re.match(subject_line_format, subject_line): - error("Subject line should match " + repr(subject_line_format) + ": " + subject_line) + diagnose_subject_line(subject_line, subject_line_format, err) if len(subject_line) >= 73: - error("Subject line should be 72 or less characters: " + subject_line) + err.error("Subject line should be 72 or fewer characters: " + subject_line) # Second one divides subject and body. if len(raw_body) > 1 and raw_body[1]: - error("Second message line should be empty: " + raw_body[1]) + err.error("Second message line should be empty: " + raw_body[1]) # Message body lines. for line in raw_body[2:]: # Long lines with URLs are exempt from the line length rule. if len(line) >= 76 and "://" not in line: - error("Message lines should be 75 or less characters: " + line) + err.error("Message lines should be 75 or less characters: " + line) if not raw_body[-1].startswith("Signed-off-by: ") or "@" not in raw_body[-1]: - warning("Message should be signed-off") - - return errors, warnings + err.warning('Message should be signed-off. Use "git commit -s".') def run(args): verbose("run", *args) - has_errors = False - has_warnings = False - for sha in git_log("%h", *args): - errors, warnings = verify(sha) - has_errors |= any(errors) - has_warnings |= any(warnings) - for err in errors: - print("error:", err) - for err in warnings: - print("warning:", err) - if has_errors or has_warnings: + + err = ErrorCollection() + + if "--check-file" in args: + filename = args[-1] + verbose("checking commit message from", filename) + with open(args[-1]) as f: + # Remove comment lines as well as any empty lines at the end. + lines = [line.rstrip("\r\n") for line in f if not line.startswith("#")] + while not lines[-1]: + lines.pop() + verify_message_body(lines, err) + else: # Normal operation, pass arguments to git log + for sha in git_log("%h", *args): + verify(sha, err) + + if err.has_errors or err.has_warnings: if suggestions: print("See https://github.com/micropython/micropython/blob/master/CODECONVENTIONS.md") else: print("ok") - if has_errors: + if err.has_errors: sys.exit(1) def show_help(): - print("usage: verifygitlog.py [-v -n -h] ...") - print("-v : increase verbosity, can be specified multiple times") + print("usage: verifygitlog.py [-v -n -h --check-file] ...") + print("-v : increase verbosity, can be speficied multiple times") print("-n : do not print multi-line suggestions") print("-h : print this help message and exit") + print( + "--check-file : Pass a single argument which is a file containing a candidate commit message" + ) + print( + "--ignore-rebase : Skip checking commits with git rebase autosquash prefixes or WIP as a prefix" + ) print("... : arguments passed to git log to retrieve commits to verify") print(" see https://www.git-scm.com/docs/git-log") print(" passing no arguments at all will verify all commits") @@ -117,6 +160,10 @@ if __name__ == "__main__": args = sys.argv[1:] verbosity = args.count("-v") suggestions = args.count("-n") == 0 + if "--ignore-rebase" in args: + args.remove("--ignore-rebase") + ignore_prefixes = ["squash!", "fixup!", "amend!", "WIP"] + if "-h" in args: show_help() else: