diff options
| author | Benjamin Shockley <benjaminshockley@hotmail.com> | 2020-08-20 13:48:15 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-08-20 13:48:15 -0500 |
| commit | 0084f0af24e4e219bc040c52ee723959423de6e2 (patch) | |
| tree | 1aebdb362f08bf6417caa87836e3e4e739afc964 /py | |
| parent | 9aebe2f1efc99871fcf283c304e3a8700050d736 (diff) | |
| parent | 4d7b9cde33934a44c4c905a49d2f831339fc8bd2 (diff) | |
Merge pull request #2 from adafruit/master
Master
Diffstat (limited to 'py')
84 files changed, 3357 insertions, 467 deletions
diff --git a/py/argcheck.c b/py/argcheck.c index 5cc18d6a2..a8df206e2 100644 --- a/py/argcheck.c +++ b/py/argcheck.c @@ -31,13 +31,22 @@ #include "supervisor/shared/translate.h" -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) { + +void mp_arg_check_num(size_t n_args, mp_map_t *kw_args, size_t n_args_min, size_t n_args_max, bool takes_kw) { + size_t n_kw = 0; + if (kw_args != NULL) { + n_kw = kw_args->used; + } + mp_arg_check_num_kw_array(n_args, n_kw, n_args_min, n_args_max, takes_kw); +} + +void mp_arg_check_num_kw_array(size_t n_args, size_t n_kw, size_t n_args_min, size_t n_args_max, bool takes_kw) { // NOTE(tannewt): This prevents this function from being optimized away. // Without it, functions can crash when reading invalid args. __asm volatile (""); // TODO maybe take the function name as an argument so we can print nicer error messages - if (n_kw && !takes_kw) { + if (n_kw > 0 && !takes_kw) { #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE mp_arg_error_terse_mismatch(); #else diff --git a/py/binary.c b/py/binary.c index ca851c936..2ec12fa93 100644 --- a/py/binary.c +++ b/py/binary.c @@ -49,7 +49,7 @@ size_t mp_binary_get_size(char struct_type, char val_type, mp_uint_t *palign) { switch (struct_type) { case '<': case '>': switch (val_type) { - case 'b': case 'B': + case 'b': case 'B': case 'x': size = 1; break; case 'h': case 'H': size = 2; break; @@ -79,7 +79,7 @@ size_t mp_binary_get_size(char struct_type, char val_type, mp_uint_t *palign) { // particular (or any) ABI. switch (val_type) { case BYTEARRAY_TYPECODE: - case 'b': case 'B': + case 'b': case 'B': case 'x': align = size = 1; break; case 'h': case 'H': align = alignof(short); @@ -126,6 +126,7 @@ mp_obj_t mp_binary_get_val_array(char typecode, void *p, mp_uint_t index) { break; case BYTEARRAY_TYPECODE: case 'B': + case 'x': // value will be discarded val = ((unsigned char*)p)[index]; break; case 'h': @@ -183,7 +184,7 @@ long long mp_binary_get_int(mp_uint_t size, bool is_signed, bool big_endian, con val = -1; } for (uint i = 0; i < size; i++) { - val <<= 8; + val *= 256; val |= *src; src += delta; } @@ -303,15 +304,20 @@ void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte ** break; } #endif - default: + 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)) { + // 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); return; } else #endif { val = mp_obj_get_int(val_in); + // Small int checking is separate, to be fast. + mp_small_int_buffer_overflow_check(val, size, signed_type); // zero/sign extend if needed if (BYTES_PER_WORD < 8 && size > sizeof(val)) { int c = (is_signed(val_type) && (mp_int_t)val < 0) ? 0xff : 0x00; @@ -321,6 +327,7 @@ void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte ** } } } + } } mp_binary_set_int(MIN((size_t)size, sizeof(val)), struct_type == '>', p, val); @@ -342,16 +349,24 @@ void mp_binary_set_val_array(char typecode, void *p, mp_uint_t index, mp_obj_t v ((mp_obj_t*)p)[index] = val_in; break; #endif - default: + default: { + size_t size = mp_binary_get_size('@', typecode, NULL); + bool signed_type = is_signed(typecode); + #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE if (MP_OBJ_IS_TYPE(val_in, &mp_type_int)) { - size_t size = mp_binary_get_size('@', typecode, NULL); + // 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, size, (uint8_t*)p + index * size); return; } #endif - mp_binary_set_val_array_from_int(typecode, p, index, mp_obj_get_int(val_in)); + mp_int_t val = mp_obj_get_int(val_in); + // Small int checking is separate, to be fast. + mp_small_int_buffer_overflow_check(val, size, signed_type); + mp_binary_set_val_array_from_int(typecode, p, index, val); + } } } @@ -364,6 +379,8 @@ void mp_binary_set_val_array_from_int(char typecode, void *p, mp_uint_t index, m case 'B': ((unsigned char*)p)[index] = val; break; + case 'x': + ((unsigned char*)p)[index] = 0; case 'h': ((short*)p)[index] = val; break; diff --git a/py/builtin.h b/py/builtin.h index 84b99a8a4..6e0d5d9be 100644 --- a/py/builtin.h +++ b/py/builtin.h @@ -117,6 +117,13 @@ extern const mp_obj_module_t mp_module_websocket; extern const mp_obj_module_t mp_module_webrepl; extern const mp_obj_module_t mp_module_framebuf; extern const mp_obj_module_t mp_module_btree; +extern const mp_obj_module_t ulab_user_cmodule; +extern mp_obj_module_t ulab_fft_module; +extern mp_obj_module_t ulab_filter_module; +extern mp_obj_module_t ulab_linalg_module; +extern mp_obj_module_t ulab_numerical_module; +extern mp_obj_module_t ulab_poly_module; + extern const char MICROPY_PY_BUILTINS_HELP_TEXT[]; diff --git a/py/builtinhelp.c b/py/builtinhelp.c index 9a3407a16..01c0bc84e 100644 --- a/py/builtinhelp.c +++ b/py/builtinhelp.c @@ -135,7 +135,7 @@ STATIC void mp_help_print_modules(void) { // let the user know there may be other modules available from the filesystem const compressed_string_t* compressed = translate("Plus any modules on the filesystem\n"); - char decompressed[compressed->length]; + char decompressed[decompress_length(compressed)]; decompress(compressed, decompressed); mp_print_str(MP_PYTHON_PRINTER, decompressed); } @@ -181,7 +181,7 @@ STATIC mp_obj_t mp_builtin_help(size_t n_args, const mp_obj_t *args) { // print a general help message. Translate only works on single strings on one line. const compressed_string_t* compressed = translate("Welcome to Adafruit CircuitPython %s!\n\nPlease visit learn.adafruit.com/category/circuitpython for project guides.\n\nTo list built-in modules please do `help(\"modules\")`.\n"); - char decompressed[compressed->length]; + char decompressed[decompress_length(compressed)]; decompress(compressed, decompressed); mp_printf(MP_PYTHON_PRINTER, decompressed, MICROPY_GIT_TAG); } else { diff --git a/py/builtinimport.c b/py/builtinimport.c index 6ed0a7594..2be779c6c 100644 --- a/py/builtinimport.c +++ b/py/builtinimport.c @@ -400,21 +400,31 @@ mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) { DEBUG_printf("Current path: %.*s\n", vstr_len(&path), vstr_str(&path)); if (stat == MP_IMPORT_STAT_NO_EXIST) { - #if MICROPY_MODULE_WEAK_LINKS - // check if there is a weak link to this module - if (i == mod_len) { - mp_map_elem_t *el = mp_map_lookup((mp_map_t*)&mp_builtin_module_weak_links_map, MP_OBJ_NEW_QSTR(mod_name), MP_MAP_LOOKUP); + // This is just the module name after the previous . + qstr current_module_name = qstr_from_strn(mod_str + last, i - last); + mp_map_elem_t *el = NULL; + if (outer_module_obj == MP_OBJ_NULL) { + el = mp_map_lookup((mp_map_t*)&mp_builtin_module_map, + MP_OBJ_NEW_QSTR(current_module_name), + MP_MAP_LOOKUP); + #if MICROPY_MODULE_WEAK_LINKS + // check if there is a weak link to this module if (el == NULL) { - goto no_exist; + el = mp_map_lookup((mp_map_t*)&mp_builtin_module_weak_links_map, + MP_OBJ_NEW_QSTR(current_module_name), + MP_MAP_LOOKUP); } - // found weak linked module + #endif + } else { + el = mp_map_lookup(&((mp_obj_module_t*) outer_module_obj)->globals->map, + MP_OBJ_NEW_QSTR(current_module_name), + MP_MAP_LOOKUP); + } + + if (el != NULL && MP_OBJ_IS_TYPE(el->value, &mp_type_module)) { module_obj = el->value; mp_module_call_init(mod_name, module_obj); } else { - no_exist: - #else - { - #endif // couldn't find the file, so fail if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) { mp_raise_ImportError(translate("module not found")); diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk new file mode 100644 index 000000000..67fbde6e4 --- /dev/null +++ b/py/circuitpy_defns.mk @@ -0,0 +1,504 @@ +# This file is part of the MicroPython project, http://micropython.org/ +# +# The MIT License (MIT) +# +# Copyright (c) 2019 Dan Halbert for Adafruit Industries +# +# 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. + +# Common Makefile definitions that can be shared across CircuitPython ports. + +### +# Common compile warnings. + +BASE_CFLAGS = \ + -fsingle-precision-constant \ + -fno-strict-aliasing \ + -Wdouble-promotion \ + -Wno-endif-labels \ + -Wstrict-prototypes \ + -Werror-implicit-function-declaration \ + -Wfloat-equal \ + -Wundef \ + -Wshadow \ + -Wwrite-strings \ + -Wsign-compare \ + -Wmissing-format-attribute \ + -Wno-deprecated-declarations \ + -Wnested-externs \ + -Wunreachable-code \ + -Wcast-align \ + -Wno-error=lto-type-mismatch \ + -D__$(CHIP_VARIANT)__ \ + -ffunction-sections \ + -fdata-sections \ + -DCIRCUITPY_SOFTWARE_SAFE_MODE=0x0ADABEEF \ + -DCIRCUITPY_CANARY_WORD=0xADAF00 \ + -DCIRCUITPY_SAFE_RESTART_WORD=0xDEADBEEF \ + --param max-inline-insns-single=500 + +# Use these flags to debug build times and header includes. +# -ftime-report +# -H + + +### +# Handle frozen modules. + +ifneq ($(FROZEN_DIR),) +# To use frozen source modules, put your .py files in a subdirectory (eg scripts/) +# and then invoke make with FROZEN_DIR=scripts (be sure to build from scratch). +CFLAGS += -DMICROPY_MODULE_FROZEN_STR +CFLAGS += -Wno-error=lto-type-mismatch +endif + +# To use frozen bytecode, put your .py files in a subdirectory (eg frozen/) and +# then invoke make with FROZEN_MPY_DIR=frozen or FROZEN_MPY_DIRS="dir1 dir2" +# (be sure to build from scratch). + +ifneq ($(FROZEN_MPY_DIRS),) +CFLAGS += -DMICROPY_QSTR_EXTRA_POOL=mp_qstr_frozen_const_pool +CFLAGS += -DMICROPY_MODULE_FROZEN_MPY +CFLAGS += -Wno-error=lto-type-mismatch +endif + + +### +# Propagate longint choice from .mk to C. There's no easy string comparison +# in cpp conditionals, so we #define separate names for each. +ifeq ($(LONGINT_IMPL),NONE) +CFLAGS += -DLONGINT_IMPL_NONE +endif + +ifeq ($(LONGINT_IMPL),MPZ) +CFLAGS += -DLONGINT_IMPL_MPZ +endif + +ifeq ($(LONGINT_IMPL),LONGLONG) +CFLAGS += -DLONGINT_IMPL_LONGLONG +endif + + +### +# Select which builtin modules to compile and include. + +ifeq ($(CIRCUITPY_AESIO),1) +SRC_PATTERNS += aesio/% +endif +ifeq ($(CIRCUITPY_ANALOGIO),1) +SRC_PATTERNS += analogio/% +endif +ifeq ($(CIRCUITPY_AUDIOBUSIO),1) +SRC_PATTERNS += audiobusio/% +endif +ifeq ($(CIRCUITPY_AUDIOIO),1) +SRC_PATTERNS += audioio/% +endif +ifeq ($(CIRCUITPY_AUDIOPWMIO),1) +SRC_PATTERNS += audiopwmio/% +endif +ifeq ($(CIRCUITPY_AUDIOCORE),1) +SRC_PATTERNS += audiocore/% +endif +ifeq ($(CIRCUITPY_AUDIOMIXER),1) +SRC_PATTERNS += audiomixer/% +endif +ifeq ($(CIRCUITPY_AUDIOMP3),1) +SRC_PATTERNS += audiomp3/% +endif +ifeq ($(CIRCUITPY_BITBANGIO),1) +SRC_PATTERNS += bitbangio/% +endif +# Some builds need bitbang SPI for the dotstar but don't make bitbangio available so include it separately. +ifeq ($(CIRCUITPY_BITBANG_APA102),1) +SRC_PATTERNS += bitbangio/SPI% +endif +ifeq ($(CIRCUITPY_BLEIO),1) +SRC_PATTERNS += _bleio/% +endif +ifeq ($(CIRCUITPY_BOARD),1) +SRC_PATTERNS += board/% +endif +ifeq ($(CIRCUITPY_BUSIO),1) +SRC_PATTERNS += busio/% bitbangio/OneWire.% +endif +ifeq ($(CIRCUITPY_COUNTIO),1) +SRC_PATTERNS += countio/% +endif +ifeq ($(CIRCUITPY_DIGITALIO),1) +SRC_PATTERNS += digitalio/% +endif +ifeq ($(CIRCUITPY_DISPLAYIO),1) +SRC_PATTERNS += displayio/% terminalio/% fontio/% +endif +ifeq ($(CIRCUITPY_VECTORIO),1) +SRC_PATTERNS += vectorio/% +endif +ifeq ($(CIRCUITPY_FRAMEBUFFERIO),1) +SRC_PATTERNS += framebufferio/% +endif +ifeq ($(CIRCUITPY_FREQUENCYIO),1) +SRC_PATTERNS += frequencyio/% +endif +ifeq ($(CIRCUITPY_GAMEPAD),1) +SRC_PATTERNS += gamepad/% +endif +ifeq ($(CIRCUITPY_GAMEPADSHIFT),1) +SRC_PATTERNS += gamepadshift/% +endif +ifeq ($(CIRCUITPY_I2CSLAVE),1) +SRC_PATTERNS += i2cslave/% +endif +ifeq ($(CIRCUITPY_MATH),1) +SRC_PATTERNS += math/% +endif +ifeq ($(CIRCUITPY__EVE),1) +SRC_PATTERNS += _eve/% +endif +ifeq ($(CIRCUITPY_MICROCONTROLLER),1) +SRC_PATTERNS += microcontroller/% +endif +ifeq ($(CIRCUITPY_NEOPIXEL_WRITE),1) +SRC_PATTERNS += neopixel_write/% +endif +ifeq ($(CIRCUITPY_NETWORK),1) +SRC_PATTERNS += network/% socket/% +endif +ifeq ($(CIRCUITPY_NVM),1) +SRC_PATTERNS += nvm/% +endif +ifeq ($(CIRCUITPY_OS),1) +SRC_PATTERNS += os/% +endif +ifeq ($(CIRCUITPY_PIXELBUF),1) +SRC_PATTERNS += _pixelbuf/% +endif +ifeq ($(CIRCUITPY_RGBMATRIX),1) +SRC_PATTERNS += rgbmatrix/% +endif +ifeq ($(CIRCUITPY_PULSEIO),1) +SRC_PATTERNS += pulseio/% +endif +ifeq ($(CIRCUITPY_PS2IO),1) +SRC_PATTERNS += ps2io/% +endif +ifeq ($(CIRCUITPY_RANDOM),1) +SRC_PATTERNS += random/% +endif +ifeq ($(CIRCUITPY_ROTARYIO),1) +SRC_PATTERNS += rotaryio/% +endif +ifeq ($(CIRCUITPY_RTC),1) +SRC_PATTERNS += rtc/% +endif +ifeq ($(CIRCUITPY_SAMD),1) +SRC_PATTERNS += samd/% +endif +ifeq ($(CIRCUITPY_STAGE),1) +SRC_PATTERNS += _stage/% +endif +ifeq ($(CIRCUITPY_STORAGE),1) +SRC_PATTERNS += storage/% +endif +ifeq ($(CIRCUITPY_STRUCT),1) +SRC_PATTERNS += struct/% +endif +ifeq ($(CIRCUITPY_SUPERVISOR),1) +SRC_PATTERNS += supervisor/% +endif +ifeq ($(CIRCUITPY_TIME),1) +SRC_PATTERNS += time/% +endif +ifeq ($(CIRCUITPY_TOUCHIO),1) +SRC_PATTERNS += touchio/% +endif +ifeq ($(CIRCUITPY_UHEAP),1) +SRC_PATTERNS += uheap/% +endif +ifeq ($(CIRCUITPY_USB_HID),1) +SRC_PATTERNS += usb_hid/% +endif +ifeq ($(CIRCUITPY_USB_MIDI),1) +SRC_PATTERNS += usb_midi/% +endif +ifeq ($(CIRCUITPY_USTACK),1) +SRC_PATTERNS += ustack/% +endif +ifeq ($(CIRCUITPY_WATCHDOG),1) +SRC_PATTERNS += watchdog/% +endif +ifeq ($(CIRCUITPY_PEW),1) +SRC_PATTERNS += _pew/% +endif + +# All possible sources are listed here, and are filtered by SRC_PATTERNS in SRC_COMMON_HAL +SRC_COMMON_HAL_ALL = \ + _bleio/__init__.c \ + _bleio/Adapter.c \ + _bleio/Attribute.c \ + _bleio/Characteristic.c \ + _bleio/CharacteristicBuffer.c \ + _bleio/Connection.c \ + _bleio/Descriptor.c \ + _bleio/PacketBuffer.c \ + _bleio/Service.c \ + _bleio/UUID.c \ + analogio/AnalogIn.c \ + analogio/AnalogOut.c \ + analogio/__init__.c \ + audiobusio/__init__.c \ + audiobusio/I2SOut.c \ + audiobusio/PDMIn.c \ + audiopwmio/__init__.c \ + audiopwmio/PWMAudioOut.c \ + audioio/__init__.c \ + audioio/AudioOut.c \ + board/__init__.c \ + busio/I2C.c \ + busio/SPI.c \ + busio/UART.c \ + busio/__init__.c \ + countio/Counter.c \ + countio/__init__.c \ + digitalio/DigitalInOut.c \ + digitalio/__init__.c \ + displayio/ParallelBus.c \ + frequencyio/__init__.c \ + frequencyio/FrequencyIn.c \ + i2cslave/I2CSlave.c \ + i2cslave/__init__.c \ + microcontroller/Pin.c \ + microcontroller/Processor.c \ + microcontroller/__init__.c \ + neopixel_write/__init__.c \ + nvm/ByteArray.c \ + nvm/__init__.c \ + os/__init__.c \ + rgbmatrix/RGBMatrix.c \ + rgbmatrix/__init__.c \ + pulseio/PWMOut.c \ + pulseio/PulseIn.c \ + pulseio/PulseOut.c \ + pulseio/__init__.c \ + ps2io/Ps2.c \ + ps2io/__init__.c \ + rotaryio/IncrementalEncoder.c \ + rotaryio/__init__.c \ + rtc/RTC.c \ + rtc/__init__.c \ + supervisor/Runtime.c \ + supervisor/__init__.c \ + watchdog/__init__.c \ + watchdog/WatchDogMode.c \ + watchdog/WatchDogTimer.c \ + +SRC_COMMON_HAL = $(filter $(SRC_PATTERNS), $(SRC_COMMON_HAL_ALL)) + +# These don't have corresponding files in each port but are still located in +# shared-bindings to make it clear what the contents of the modules are. +# All possible sources are listed here, and are filtered by SRC_PATTERNS. +SRC_BINDINGS_ENUMS = \ +$(filter $(SRC_PATTERNS), \ + _bleio/Address.c \ + _bleio/Attribute.c \ + _bleio/ScanEntry.c \ + digitalio/Direction.c \ + digitalio/DriveMode.c \ + digitalio/Pull.c \ + fontio/Glyph.c \ + microcontroller/RunMode.c \ + math/__init__.c \ + _eve/__init__.c \ +) + +SRC_BINDINGS_ENUMS += \ + util.c + +SRC_SHARED_MODULE_ALL = \ + _bleio/Address.c \ + _bleio/Attribute.c \ + _bleio/ScanEntry.c \ + _bleio/ScanResults.c \ + _pixelbuf/PixelBuf.c \ + _pixelbuf/__init__.c \ + _stage/Layer.c \ + _stage/Text.c \ + _stage/__init__.c \ + audiopwmio/__init__.c \ + audioio/__init__.c \ + audiocore/__init__.c \ + audiocore/RawSample.c \ + audiocore/WaveFile.c \ + audiomixer/__init__.c \ + audiomixer/Mixer.c \ + audiomixer/MixerVoice.c \ + audiomp3/__init__.c \ + audiomp3/MP3Decoder.c \ + bitbangio/I2C.c \ + bitbangio/OneWire.c \ + bitbangio/SPI.c \ + bitbangio/__init__.c \ + board/__init__.c \ + busio/OneWire.c \ + aesio/__init__.c \ + aesio/aes.c \ + displayio/Bitmap.c \ + displayio/ColorConverter.c \ + displayio/Display.c \ + displayio/EPaperDisplay.c \ + displayio/FourWire.c \ + displayio/Group.c \ + displayio/I2CDisplay.c \ + displayio/OnDiskBitmap.c \ + displayio/Palette.c \ + displayio/Shape.c \ + displayio/TileGrid.c \ + displayio/__init__.c \ + vectorio/Circle.c \ + vectorio/Rectangle.c \ + vectorio/Polygon.c \ + vectorio/VectorShape.c \ + vectorio/__init__.c \ + fontio/BuiltinFont.c \ + fontio/__init__.c \ + framebufferio/FramebufferDisplay.c \ + framebufferio/__init__.c \ + gamepad/GamePad.c \ + gamepad/__init__.c \ + gamepadshift/GamePadShift.c \ + gamepadshift/__init__.c \ + os/__init__.c \ + random/__init__.c \ + socket/__init__.c \ + network/__init__.c \ + rgbmatrix/RGBMatrix.c \ + rgbmatrix/__init__.c \ + storage/__init__.c \ + struct/__init__.c \ + time/__init__.c \ + terminalio/Terminal.c \ + terminalio/__init__.c \ + uheap/__init__.c \ + ustack/__init__.c \ + _pew/__init__.c \ + _pew/PewPew.c \ + _eve/__init__.c + +# All possible sources are listed here, and are filtered by SRC_PATTERNS. +SRC_SHARED_MODULE = $(filter $(SRC_PATTERNS), $(SRC_SHARED_MODULE_ALL)) + +# Use the native touchio if requested. This flag is set conditionally in, say, mpconfigport.h. +# The presence of common-hal/touchio/* # does not imply it's available for all chips in a port, +# so there is an explicit flag. For example, SAMD21 touchio is native, but SAMD51 is not. +ifeq ($(CIRCUITPY_TOUCHIO_USE_NATIVE),1) +SRC_COMMON_HAL_ALL += \ + touchio/TouchIn.c \ + touchio/__init__.c +else +SRC_SHARED_MODULE_ALL += \ + touchio/TouchIn.c \ + touchio/__init__.c +endif +ifeq ($(CIRCUITPY_AUDIOMP3),1) +SRC_MOD += $(addprefix lib/mp3/src/, \ + bitstream.c \ + buffers.c \ + dct32.c \ + dequant.c \ + dqchan.c \ + huffman.c \ + hufftabs.c \ + imdct.c \ + mp3dec.c \ + mp3tabs.c \ + polyphase.c \ + scalfact.c \ + stproc.c \ + subband.c \ + trigtabs.c \ +) +$(BUILD)/lib/mp3/src/buffers.o: CFLAGS += -include "py/misc.h" -D'MPDEC_ALLOCATOR(x)=m_malloc(x,0)' -D'MPDEC_FREE(x)=m_free(x)' +endif +ifeq ($(CIRCUITPY_RGBMATRIX),1) +SRC_MOD += $(addprefix lib/protomatter/, \ + core.c \ +) +$(BUILD)/lib/protomatter/core.o: CFLAGS += -include "shared-module/rgbmatrix/allocator.h" -DCIRCUITPY -Wno-missing-braces +endif + +# All possible sources are listed here, and are filtered by SRC_PATTERNS. +SRC_SHARED_MODULE_INTERNAL = \ +$(filter $(SRC_PATTERNS), \ + displayio/display_core.c \ +) + +ifeq ($(INTERNAL_LIBM),1) +SRC_LIBM = \ +$(addprefix lib/,\ + libm/math.c \ + libm/roundf.c \ + libm/fmodf.c \ + libm/nearbyintf.c \ + libm/ef_sqrt.c \ + libm/kf_rem_pio2.c \ + libm/kf_sin.c \ + libm/kf_cos.c \ + libm/kf_tan.c \ + libm/ef_rem_pio2.c \ + libm/sf_sin.c \ + libm/sf_cos.c \ + libm/sf_tan.c \ + libm/sf_frexp.c \ + libm/sf_modf.c \ + libm/sf_ldexp.c \ + libm/asinfacosf.c \ + libm/atanf.c \ + libm/atan2f.c \ + ) +ifeq ($(CIRCUITPY_ULAB),1) +SRC_LIBM += \ +$(addprefix lib/,\ + libm/acoshf.c \ + libm/asinhf.c \ + libm/atanhf.c \ + libm/erf_lgamma.c \ + libm/log1pf.c \ + libm/sf_erf.c \ + libm/wf_lgamma.c \ + libm/wf_tgamma.c \ + ) +endif +endif + +ifdef LD_TEMPLATE_FILE +# Generate a linker script (.ld file) from a template, for those builds that use it. +GENERATED_LD_FILE = $(BUILD)/$(notdir $(patsubst %.template.ld,%.ld,$(LD_TEMPLATE_FILE))) +# +# ld_defines.pp is generated from ld_defines.c. See py/mkrules.mk. +# Run gen_ld_files.py over ALL *.template.ld files, not just LD_TEMPLATE_FILE, +# because it may include other template files. +$(GENERATED_LD_FILE): $(BUILD)/ld_defines.pp boards/*.template.ld + $(STEPECHO) "GEN $@" + $(Q)$(PYTHON3) $(TOP)/tools/gen_ld_files.py --defines $< --out_dir $(BUILD) boards/*.template.ld +endif + +.PHONY: check-release-needs-clean-build + +check-release-needs-clean-build: + @echo "RELEASE_NEEDS_CLEAN_BUILD = $(RELEASE_NEEDS_CLEAN_BUILD)" diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h new file mode 100644 index 000000000..3fbb46900 --- /dev/null +++ b/py/circuitpy_mpconfig.h @@ -0,0 +1,768 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Dan Halbert for Adafruit Industries + * + * 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 contains settings that are common across CircuitPython ports, to make +// sure that the same feature set and settings are used, such as in atmel-samd +// and nrf. + +#ifndef __INCLUDED_MPCONFIG_CIRCUITPY_H +#define __INCLUDED_MPCONFIG_CIRCUITPY_H + +#include <stdint.h> +#include <stdatomic.h> + +// This is CircuitPython. +#define CIRCUITPY 1 + +// REPR_C encodes qstrs, 31-bit ints, and 30-bit floats in a single 32-bit word. +#define MICROPY_OBJ_REPR (MICROPY_OBJ_REPR_C) + +// options to control how MicroPython is built +// TODO(tannewt): Reduce this number if we want the REPL to function under 512 +// free bytes. +// #define MICROPY_ALLOC_PARSE_RULE_INIT (64) + +// Sorted alphabetically for easy finding. +// +// default is 128; consider raising to reduce fragmentation. +#define MICROPY_ALLOC_PARSE_CHUNK_INIT (16) +// default is 512. +#define MICROPY_ALLOC_PATH_MAX (256) +#define MICROPY_CAN_OVERRIDE_BUILTINS (1) +#define MICROPY_COMP_CONST (1) +#define MICROPY_COMP_DOUBLE_TUPLE_ASSIGN (1) +#define MICROPY_COMP_MODULE_CONST (1) +#define MICROPY_COMP_TRIPLE_TUPLE_ASSIGN (0) +#define MICROPY_DEBUG_PRINTERS (0) +#define MICROPY_EMIT_INLINE_THUMB (CIRCUITPY_ENABLE_MPY_NATIVE) +#define MICROPY_EMIT_THUMB (CIRCUITPY_ENABLE_MPY_NATIVE) +#define MICROPY_EMIT_X64 (0) +#define MICROPY_ENABLE_DOC_STRING (0) +#define MICROPY_ENABLE_FINALISER (1) +#define MICROPY_ENABLE_GC (1) +#define MICROPY_ENABLE_SOURCE_LINE (1) +#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_NORMAL) +#define MICROPY_FLOAT_HIGH_QUALITY_HASH (0) +#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) +#define MICROPY_GC_ALLOC_THRESHOLD (0) +#define MICROPY_HELPER_LEXER_UNIX (0) +#define MICROPY_HELPER_REPL (1) +#define MICROPY_KBD_EXCEPTION (1) +#define MICROPY_MEM_STATS (0) +#define MICROPY_MODULE_BUILTIN_INIT (1) +#define MICROPY_NONSTANDARD_TYPECODES (0) +#define MICROPY_OPT_COMPUTED_GOTO (1) +#define MICROPY_PERSISTENT_CODE_LOAD (1) + +#define MICROPY_PY_ARRAY (1) +#define MICROPY_PY_ARRAY_SLICE_ASSIGN (1) +#define MICROPY_PY_ASYNC_AWAIT (0) +#define MICROPY_PY_ATTRTUPLE (1) + +#define MICROPY_PY_BUILTINS_BYTEARRAY (1) +#define MICROPY_PY_BUILTINS_ENUMERATE (1) +#define MICROPY_PY_BUILTINS_FILTER (1) +#define MICROPY_PY_BUILTINS_HELP (1) +#define MICROPY_PY_BUILTINS_HELP_MODULES (1) +#define MICROPY_PY_BUILTINS_INPUT (1) +#define MICROPY_PY_BUILTINS_MEMORYVIEW (1) +#define MICROPY_PY_BUILTINS_MIN_MAX (1) +#define MICROPY_PY_BUILTINS_PROPERTY (1) +#define MICROPY_PY_BUILTINS_REVERSED (1) +#define MICROPY_PY_BUILTINS_ROUND_INT (1) +#define MICROPY_PY_BUILTINS_SET (1) +#define MICROPY_PY_BUILTINS_SLICE (1) +#define MICROPY_PY_BUILTINS_SLICE_ATTRS (1) +#define MICROPY_PY_BUILTINS_STR_UNICODE (1) + +#define MICROPY_PY_CMATH (0) +#define MICROPY_PY_COLLECTIONS (1) +#define MICROPY_PY_DESCRIPTORS (1) +#define MICROPY_PY_IO_FILEIO (1) +#define MICROPY_PY_GC (1) +// Supplanted by shared-bindings/math +#define MICROPY_PY_MATH (0) +#define MICROPY_PY_MICROPYTHON_MEM_INFO (0) +// Supplanted by shared-bindings/struct +#define MICROPY_PY_STRUCT (0) +#define MICROPY_PY_SYS (1) +#define MICROPY_PY_SYS_MAXSIZE (1) +#define MICROPY_PY_SYS_STDFILES (1) +// Supplanted by shared-bindings/random +#define MICROPY_PY_URANDOM (0) +#define MICROPY_PY_URANDOM_EXTRA_FUNCS (0) +#define MICROPY_PY___FILE__ (1) + +#define MICROPY_QSTR_BYTES_IN_HASH (1) +#define MICROPY_REPL_AUTO_INDENT (1) +#define MICROPY_REPL_EVENT_DRIVEN (0) +#define MICROPY_STACK_CHECK (1) +#define MICROPY_STREAMS_NON_BLOCK (1) +#ifndef MICROPY_USE_INTERNAL_PRINTF +#define MICROPY_USE_INTERNAL_PRINTF (1) +#endif + +// fatfs configuration used in ffconf.h +// +// 1 = SFN/ANSI 437=LFN/U.S.(OEM) +#define MICROPY_FATFS_ENABLE_LFN (1) +#define MICROPY_FATFS_LFN_CODE_PAGE (437) +#define MICROPY_FATFS_USE_LABEL (1) +#define MICROPY_FATFS_RPATH (2) +#define MICROPY_FATFS_MULTI_PARTITION (1) + +// Only enable this if you really need it. It allocates a byte cache of this size. +// #define MICROPY_FATFS_MAX_SS (4096) + +#define FILESYSTEM_BLOCK_SIZE (512) + +#define MICROPY_VFS (1) +#define MICROPY_VFS_FAT (MICROPY_VFS) +#define MICROPY_READER_VFS (MICROPY_VFS) + +// type definitions for the specific machine + +#define BYTES_PER_WORD (4) + +#define MICROPY_MAKE_POINTER_CALLABLE(p) ((void*)((mp_uint_t)(p) | 1)) + +// Track stack usage. Expose results via ustack module. +#define MICROPY_MAX_STACK_USAGE (0) + +// This port is intended to be 32-bit, but unfortunately, int32_t for +// different targets may be defined in different ways - either as int +// or as long. This requires different printf formatting specifiers +// to print such value. So, we avoid int32_t and use int directly. +#define UINT_FMT "%u" +#define INT_FMT "%d" +typedef int mp_int_t; // must be pointer size +typedef unsigned mp_uint_t; // must be pointer size +typedef long mp_off_t; + +#define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(str, len) + +#define mp_type_fileio mp_type_vfs_fat_fileio +#define mp_type_textio mp_type_vfs_fat_textio + +#define mp_import_stat mp_vfs_import_stat +#define mp_builtin_open_obj mp_vfs_open_obj + + +// extra built in names to add to the global namespace +#define MICROPY_PORT_BUILTINS \ + { MP_OBJ_NEW_QSTR(MP_QSTR_help), (mp_obj_t)&mp_builtin_help_obj }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_input), (mp_obj_t)&mp_builtin_input_obj }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_open), (mp_obj_t)&mp_builtin_open_obj }, + +////////////////////////////////////////////////////////////////////////////////////////////////// +// board-specific definitions, which control and may override definitions below. +#include "mpconfigboard.h" + +// Turning off FULL_BUILD removes some functionality to reduce flash size on tiny SAMD21s +#define MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG (CIRCUITPY_FULL_BUILD) +#define MICROPY_CPYTHON_COMPAT (CIRCUITPY_FULL_BUILD) +#define MICROPY_COMP_FSTRING_LITERAL (MICROPY_CPYTHON_COMPAT) +#define MICROPY_MODULE_WEAK_LINKS (CIRCUITPY_FULL_BUILD) +#define MICROPY_PY_ALL_SPECIAL_METHODS (CIRCUITPY_FULL_BUILD) +#define MICROPY_PY_BUILTINS_COMPLEX (CIRCUITPY_FULL_BUILD) +#define MICROPY_PY_BUILTINS_FROZENSET (CIRCUITPY_FULL_BUILD) +#define MICROPY_PY_BUILTINS_STR_CENTER (CIRCUITPY_FULL_BUILD) +#define MICROPY_PY_BUILTINS_STR_PARTITION (CIRCUITPY_FULL_BUILD) +#define MICROPY_PY_BUILTINS_STR_SPLITLINES (CIRCUITPY_FULL_BUILD) +#define MICROPY_PY_UERRNO (CIRCUITPY_FULL_BUILD) +// Opposite setting is deliberate. +#define MICROPY_PY_UERRNO_ERRORCODE (!CIRCUITPY_FULL_BUILD) +#ifndef MICROPY_PY_URE +#define MICROPY_PY_URE (CIRCUITPY_FULL_BUILD) +#endif +#define MICROPY_PY_URE_MATCH_GROUPS (CIRCUITPY_FULL_BUILD) +#define MICROPY_PY_URE_MATCH_SPAN_START_END (CIRCUITPY_FULL_BUILD) +#define MICROPY_PY_URE_SUB (CIRCUITPY_FULL_BUILD) + +// LONGINT_IMPL_xxx are defined in the Makefile. +// +#ifdef LONGINT_IMPL_NONE +#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_NONE) +#endif + +#ifdef LONGINT_IMPL_MPZ +#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) +#define MP_SSIZE_MAX (0x7fffffff) +#endif + +#ifdef LONGINT_IMPL_LONGLONG +#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_LONGLONG) +#define MP_SSIZE_MAX (0x7fffffff) +#endif + +#ifndef MICROPY_PY_REVERSE_SPECIAL_METHODS +#define MICROPY_PY_REVERSE_SPECIAL_METHODS (CIRCUITPY_FULL_BUILD) +#endif + +#if INTERNAL_FLASH_FILESYSTEM == 0 && QSPI_FLASH_FILESYSTEM == 0 && SPI_FLASH_FILESYSTEM == 0 && !DISABLE_FILESYSTEM +#error No *_FLASH_FILESYSTEM set! +#endif + +// These CIRCUITPY_xxx values should all be defined in the *.mk files as being on or off. +// So if any are not defined in *.mk, they'll throw an error here. + +#if CIRCUITPY_AESIO +extern const struct _mp_obj_module_t aesio_module; +#define AESIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_aesio), (mp_obj_t)&aesio_module }, +#else +#define AESIO_MODULE +#endif + +#if CIRCUITPY_ANALOGIO +#define ANALOGIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_analogio), (mp_obj_t)&analogio_module }, +extern const struct _mp_obj_module_t analogio_module; +#else +#define ANALOGIO_MODULE +#endif + +#if CIRCUITPY_AUDIOBUSIO +#define AUDIOBUSIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_audiobusio), (mp_obj_t)&audiobusio_module }, +extern const struct _mp_obj_module_t audiobusio_module; +#else +#define AUDIOBUSIO_MODULE +#endif + +#if CIRCUITPY_AUDIOCORE +#define AUDIOCORE_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_audiocore), (mp_obj_t)&audiocore_module }, +extern const struct _mp_obj_module_t audiocore_module; +#else +#define AUDIOCORE_MODULE +#endif + +#if CIRCUITPY_AUDIOIO +#define AUDIOIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_audioio), (mp_obj_t)&audioio_module }, +extern const struct _mp_obj_module_t audioio_module; +#else +#define AUDIOIO_MODULE +#endif + +#if CIRCUITPY_AUDIOMIXER +#define AUDIOMIXER_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_audiomixer), (mp_obj_t)&audiomixer_module }, +extern const struct _mp_obj_module_t audiomixer_module; +#else +#define AUDIOMIXER_MODULE +#endif + +#if CIRCUITPY_AUDIOMP3 +#define AUDIOMP3_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_audiomp3), (mp_obj_t)&audiomp3_module }, +extern const struct _mp_obj_module_t audiomp3_module; +#else +#define AUDIOMP3_MODULE +#endif + +#if CIRCUITPY_AUDIOPWMIO +#define AUDIOPWMIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_audiopwmio), (mp_obj_t)&audiopwmio_module }, +extern const struct _mp_obj_module_t audiopwmio_module; +#else +#define AUDIOPWMIO_MODULE +#endif + +#if CIRCUITPY_BITBANGIO +#define BITBANGIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_bitbangio), (mp_obj_t)&bitbangio_module }, +extern const struct _mp_obj_module_t bitbangio_module; +#else +#define BITBANGIO_MODULE +#endif + +#if CIRCUITPY_BLEIO +#define BLEIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR__bleio), (mp_obj_t)&bleio_module }, +extern const struct _mp_obj_module_t bleio_module; +#else +#define BLEIO_MODULE +#endif + +#if CIRCUITPY_BOARD +#define BOARD_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_board), (mp_obj_t)&board_module }, +extern const struct _mp_obj_module_t board_module; + +#define BOARD_I2C (defined(DEFAULT_I2C_BUS_SDA) && defined(DEFAULT_I2C_BUS_SCL)) +#define BOARD_SPI (defined(DEFAULT_SPI_BUS_SCK) && defined(DEFAULT_SPI_BUS_MISO) && defined(DEFAULT_SPI_BUS_MOSI)) +#define BOARD_UART (defined(DEFAULT_UART_BUS_RX) && defined(DEFAULT_UART_BUS_TX)) + +// I2C and SPI are always allocated off the heap. + +#if BOARD_UART +#define BOARD_UART_ROOT_POINTER mp_obj_t shared_uart_bus; +#else +#define BOARD_UART_ROOT_POINTER +#endif + +#else +#define BOARD_MODULE +#define BOARD_UART_ROOT_POINTER +#endif + +#if CIRCUITPY_BUSIO +extern const struct _mp_obj_module_t busio_module; +#define BUSIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_busio), (mp_obj_t)&busio_module }, +#else +#define BUSIO_MODULE +#endif + +#if CIRCUITPY_COUNTIO +extern const struct _mp_obj_module_t countio_module; +#define COUNTIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_countio), (mp_obj_t)&countio_module }, +#else +#define COUNTIO_MODULE +#endif + +#if CIRCUITPY_DIGITALIO +extern const struct _mp_obj_module_t digitalio_module; +#define DIGITALIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_digitalio), (mp_obj_t)&digitalio_module }, +#else +#define DIGITALIO_MODULE +#endif + +#if CIRCUITPY_DISPLAYIO +extern const struct _mp_obj_module_t displayio_module; +extern const struct _mp_obj_module_t fontio_module; +extern const struct _mp_obj_module_t terminalio_module; +#define DISPLAYIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_displayio), (mp_obj_t)&displayio_module }, +#define FONTIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_fontio), (mp_obj_t)&fontio_module }, +#define TERMINALIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_terminalio), (mp_obj_t)&terminalio_module }, +#ifndef CIRCUITPY_DISPLAY_LIMIT +#define CIRCUITPY_DISPLAY_LIMIT (1) +#endif +#else +#define DISPLAYIO_MODULE +#define FONTIO_MODULE +#define TERMINALIO_MODULE +#define CIRCUITPY_DISPLAY_LIMIT (0) +#endif + +#if CIRCUITPY_FRAMEBUFFERIO +extern const struct _mp_obj_module_t framebufferio_module; +#define FRAMEBUFFERIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_framebufferio), (mp_obj_t)&framebufferio_module }, +#else +#define FRAMEBUFFERIO_MODULE +#endif + +#if CIRCUITPY_VECTORIO +extern const struct _mp_obj_module_t vectorio_module; +#define VECTORIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_vectorio), (mp_obj_t)&vectorio_module }, +#else +#define VECTORIO_MODULE +#endif + +#if CIRCUITPY_FREQUENCYIO +extern const struct _mp_obj_module_t frequencyio_module; +#define FREQUENCYIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_frequencyio), (mp_obj_t)&frequencyio_module }, +#else +#define FREQUENCYIO_MODULE +#endif + +#if CIRCUITPY_GAMEPAD +extern const struct _mp_obj_module_t gamepad_module; +#define GAMEPAD_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_gamepad),(mp_obj_t)&gamepad_module }, +#else +#define GAMEPAD_MODULE +#endif + +#if CIRCUITPY_GAMEPADSHIFT +extern const struct _mp_obj_module_t gamepadshift_module; +#define GAMEPADSHIFT_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_gamepadshift),(mp_obj_t)&gamepadshift_module }, +#else +#define GAMEPADSHIFT_MODULE +#endif + +#if CIRCUITPY_GAMEPAD || CIRCUITPY_GAMEPADSHIFT +// Scan gamepad every 32ms +#define CIRCUITPY_GAMEPAD_TICKS 0x1f +#define GAMEPAD_ROOT_POINTERS mp_obj_t gamepad_singleton; +#else +#define GAMEPAD_ROOT_POINTERS +#endif + +#if CIRCUITPY_I2CSLAVE +extern const struct _mp_obj_module_t i2cslave_module; +#define I2CSLAVE_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_i2cslave), (mp_obj_t)&i2cslave_module }, +#else +#define I2CSLAVE_MODULE +#endif + +#if CIRCUITPY_MATH +extern const struct _mp_obj_module_t math_module; +#define MATH_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_math), (mp_obj_t)&math_module }, +#else +#define MATH_MODULE +#endif + +#if CIRCUITPY__EVE +extern const struct _mp_obj_module_t _eve_module; +#define _EVE_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR__eve), (mp_obj_t)&_eve_module }, +#else +#define _EVE_MODULE +#endif + +#if CIRCUITPY_MICROCONTROLLER +extern const struct _mp_obj_module_t microcontroller_module; +#define MICROCONTROLLER_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_microcontroller), (mp_obj_t)µcontroller_module }, +#else +#define MICROCONTROLLER_MODULE +#endif + +#if CIRCUITPY_NEOPIXEL_WRITE +extern const struct _mp_obj_module_t neopixel_write_module; +#define NEOPIXEL_WRITE_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_neopixel_write),(mp_obj_t)&neopixel_write_module }, +#else +#define NEOPIXEL_WRITE_MODULE +#endif + +#if CIRCUITPY_NETWORK +extern const struct _mp_obj_module_t network_module; +extern const struct _mp_obj_module_t socket_module; +#define NETWORK_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_network), (mp_obj_t)&network_module }, +#define SOCKET_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_socket), (mp_obj_t)&socket_module }, +#define NETWORK_ROOT_POINTERS mp_obj_list_t mod_network_nic_list; +#if MICROPY_PY_WIZNET5K + extern const struct _mp_obj_module_t wiznet_module; + #define WIZNET_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_wiznet), (mp_obj_t)&wiznet_module }, +#endif +#else +#define NETWORK_MODULE +#define SOCKET_MODULE +#define WIZNET_MODULE +#define NETWORK_ROOT_POINTERS +#endif + +// This is not a top-level module; it's microcontroller.nvm. +#if CIRCUITPY_NVM +extern const struct _mp_obj_module_t nvm_module; +#endif + +#if CIRCUITPY_OS +extern const struct _mp_obj_module_t os_module; +#define OS_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_os), (mp_obj_t)&os_module }, +#define OS_MODULE_ALT_NAME { MP_OBJ_NEW_QSTR(MP_QSTR__os), (mp_obj_t)&os_module }, +#else +#define OS_MODULE +#define OS_MODULE_ALT_NAME +#endif + +#if CIRCUITPY_PEW +extern const struct _mp_obj_module_t pew_module; +#define PEW_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR__pew),(mp_obj_t)&pew_module }, +#else +#define PEW_MODULE +#endif + +#if CIRCUITPY_PIXELBUF +extern const struct _mp_obj_module_t pixelbuf_module; +#define PIXELBUF_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR__pixelbuf),(mp_obj_t)&pixelbuf_module }, +#else +#define PIXELBUF_MODULE +#endif + +#if CIRCUITPY_RGBMATRIX +extern const struct _mp_obj_module_t rgbmatrix_module; +#define RGBMATRIX_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_rgbmatrix),(mp_obj_t)&rgbmatrix_module }, +#else +#define RGBMATRIX_MODULE +#endif + +#if CIRCUITPY_PULSEIO +extern const struct _mp_obj_module_t pulseio_module; +#define PULSEIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_pulseio), (mp_obj_t)&pulseio_module }, +#else +#define PULSEIO_MODULE +#endif + +#if CIRCUITPY_PS2IO +extern const struct _mp_obj_module_t ps2io_module; +#define PS2IO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_ps2io), (mp_obj_t)&ps2io_module }, +#else +#define PS2IO_MODULE +#endif + +#if CIRCUITPY_RANDOM +extern const struct _mp_obj_module_t random_module; +#define RANDOM_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_random), (mp_obj_t)&random_module }, +#else +#define RANDOM_MODULE +#endif + +#if CIRCUITPY_ROTARYIO +extern const struct _mp_obj_module_t rotaryio_module; +#define ROTARYIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_rotaryio), (mp_obj_t)&rotaryio_module }, +#else +#define ROTARYIO_MODULE +#endif + +#if CIRCUITPY_RTC +extern const struct _mp_obj_module_t rtc_module; +#define RTC_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_rtc), (mp_obj_t)&rtc_module }, +#else +#define RTC_MODULE +#endif + +#if CIRCUITPY_SAMD +extern const struct _mp_obj_module_t samd_module; +#define SAMD_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_samd),(mp_obj_t)&samd_module }, +#else +#define SAMD_MODULE +#endif + +#if CIRCUITPY_STAGE +extern const struct _mp_obj_module_t stage_module; +#define STAGE_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR__stage), (mp_obj_t)&stage_module }, +#else +#define STAGE_MODULE +#endif + +#if CIRCUITPY_STORAGE +extern const struct _mp_obj_module_t storage_module; +#define STORAGE_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_storage), (mp_obj_t)&storage_module }, +#else +#define STORAGE_MODULE +#endif + +#if CIRCUITPY_STRUCT +extern const struct _mp_obj_module_t struct_module; +#define STRUCT_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_struct), (mp_obj_t)&struct_module }, +#else +#define STRUCT_MODULE +#endif + +#if CIRCUITPY_SUPERVISOR +extern const struct _mp_obj_module_t supervisor_module; +#define SUPERVISOR_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_supervisor), (mp_obj_t)&supervisor_module }, +#else +#define SUPERVISOR_MODULE +#endif + +#if CIRCUITPY_TIME +extern const struct _mp_obj_module_t time_module; +#define TIME_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_time), (mp_obj_t)&time_module }, +#define TIME_MODULE_ALT_NAME { MP_OBJ_NEW_QSTR(MP_QSTR__time), (mp_obj_t)&time_module }, +#else +#define TIME_MODULE +#define TIME_MODULE_ALT_NAME +#endif + +#if CIRCUITPY_TOUCHIO +extern const struct _mp_obj_module_t touchio_module; +#define TOUCHIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_touchio), (mp_obj_t)&touchio_module }, +#else +#define TOUCHIO_MODULE +#endif + +#if CIRCUITPY_UHEAP +extern const struct _mp_obj_module_t uheap_module; +#define UHEAP_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_uheap),(mp_obj_t)&uheap_module }, +#else +#define UHEAP_MODULE +#endif + +#if CIRCUITPY_USB_HID +extern const struct _mp_obj_module_t usb_hid_module; +#define USB_HID_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_usb_hid),(mp_obj_t)&usb_hid_module }, +#else +#define USB_HID_MODULE +#endif + +#if CIRCUITPY_USB_MIDI +extern const struct _mp_obj_module_t usb_midi_module; +#define USB_MIDI_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_usb_midi),(mp_obj_t)&usb_midi_module }, +#else +#define USB_MIDI_MODULE +#endif + +#if CIRCUITPY_USTACK +extern const struct _mp_obj_module_t ustack_module; +#define USTACK_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_ustack),(mp_obj_t)&ustack_module }, +#else +#define USTACK_MODULE +#endif + +// These modules are not yet in shared-bindings, but we prefer the non-uxxx names. +#if MICROPY_PY_UERRNO +#define ERRNO_MODULE { MP_ROM_QSTR(MP_QSTR_errno), MP_ROM_PTR(&mp_module_uerrno) }, +#else +#define ERRNO_MODULE +#endif + +#if MICROPY_PY_UJSON +#define JSON_MODULE { MP_ROM_QSTR(MP_QSTR_json), MP_ROM_PTR(&mp_module_ujson) }, +#else +#define JSON_MODULE +#endif + +#if defined(CIRCUITPY_ULAB) && CIRCUITPY_ULAB +// ulab requires reverse special methods +#if defined(MICROPY_PY_REVERSE_SPECIAL_METHODS) && !MICROPY_PY_REVERSE_SPECIAL_METHODS +#error "ulab requires MICROPY_PY_REVERSE_SPECIAL_METHODS" +#endif +#define ULAB_MODULE \ + { MP_ROM_QSTR(MP_QSTR_ulab), MP_ROM_PTR(&ulab_user_cmodule) }, +#else +#define ULAB_MODULE +#endif + +#if MICROPY_PY_URE +#define RE_MODULE { MP_ROM_QSTR(MP_QSTR_re), MP_ROM_PTR(&mp_module_ure) }, +#else +#define RE_MODULE +#endif + +// This is not a top-level module; it's microcontroller.watchdog. +#if CIRCUITPY_WATCHDOG +extern const struct _mp_obj_module_t watchdog_module; +#define WATCHDOG_MODULE { MP_ROM_QSTR(MP_QSTR_watchdog), MP_ROM_PTR(&watchdog_module) }, +#else +#define WATCHDOG_MODULE +#endif + +// Define certain native modules with weak links so they can be replaced with Python +// implementations. This list may grow over time. +#define MICROPY_PORT_BUILTIN_MODULE_WEAK_LINKS \ + OS_MODULE \ + TIME_MODULE \ + +// Native modules that are weak links can be accessed directly +// by prepending their name with an underscore. This list should correspond to +// MICROPY_PORT_BUILTIN_MODULE_WEAK_LINKS, assuming you want the native modules +// to be accessible when overriden. +#define MICROPY_PORT_BUILTIN_MODULE_ALT_NAMES \ + OS_MODULE_ALT_NAME \ + TIME_MODULE_ALT_NAME \ + +// This is an inclusive list that should correspond to the CIRCUITPY_XXX list above, +// including dependencies such as TERMINALIO depending on DISPLAYIO (shown by indentation). +// Some of these definitions will be blank depending on what is turned on and off. +// Some are omitted because they're in MICROPY_PORT_BUILTIN_MODULE_WEAK_LINKS above. +#define MICROPY_PORT_BUILTIN_MODULES_STRONG_LINKS \ + AESIO_MODULE \ + ANALOGIO_MODULE \ + AUDIOBUSIO_MODULE \ + AUDIOCORE_MODULE \ + AUDIOIO_MODULE \ + AUDIOMIXER_MODULE \ + AUDIOMP3_MODULE \ + AUDIOPWMIO_MODULE \ + BITBANGIO_MODULE \ + BLEIO_MODULE \ + BOARD_MODULE \ + BUSIO_MODULE \ + COUNTIO_MODULE \ + DIGITALIO_MODULE \ + DISPLAYIO_MODULE \ + FONTIO_MODULE \ + TERMINALIO_MODULE \ + VECTORIO_MODULE \ + ERRNO_MODULE \ + FRAMEBUFFERIO_MODULE \ + FREQUENCYIO_MODULE \ + GAMEPAD_MODULE \ + GAMEPADSHIFT_MODULE \ + I2CSLAVE_MODULE \ + JSON_MODULE \ + MATH_MODULE \ + _EVE_MODULE \ + MICROCONTROLLER_MODULE \ + NEOPIXEL_WRITE_MODULE \ + NETWORK_MODULE \ + SOCKET_MODULE \ + WIZNET_MODULE \ + PEW_MODULE \ + PIXELBUF_MODULE \ + PS2IO_MODULE \ + PULSEIO_MODULE \ + RANDOM_MODULE \ + RE_MODULE \ + RGBMATRIX_MODULE \ + ROTARYIO_MODULE \ + RTC_MODULE \ + SAMD_MODULE \ + STAGE_MODULE \ + STORAGE_MODULE \ + STRUCT_MODULE \ + SUPERVISOR_MODULE \ + TOUCHIO_MODULE \ + UHEAP_MODULE \ + USB_HID_MODULE \ + USB_MIDI_MODULE \ + USTACK_MODULE \ + WATCHDOG_MODULE \ + +// If weak links are enabled, just include strong links in the main list of modules, +// and also include the underscore alternate names. +#if MICROPY_MODULE_WEAK_LINKS +#define MICROPY_PORT_BUILTIN_MODULES \ + MICROPY_PORT_BUILTIN_MODULES_STRONG_LINKS \ + MICROPY_PORT_BUILTIN_MODULE_ALT_NAMES +#else +// If weak links are disabled, included both strong and potentially weak lines +#define MICROPY_PORT_BUILTIN_MODULES \ + MICROPY_PORT_BUILTIN_MODULES_STRONG_LINKS \ + MICROPY_PORT_BUILTIN_MODULE_WEAK_LINKS +#endif + +// We need to provide a declaration/definition of alloca() +#include <alloca.h> + +#define MP_STATE_PORT MP_STATE_VM + +#include "supervisor/flash_root_pointers.h" + +#define CIRCUITPY_COMMON_ROOT_POINTERS \ + const char *readline_hist[8]; \ + vstr_t *repl_line; \ + mp_obj_t rtc_time_source; \ + GAMEPAD_ROOT_POINTERS \ + mp_obj_t pew_singleton; \ + mp_obj_t terminal_tilegrid_tiles; \ + BOARD_UART_ROOT_POINTER \ + FLASH_ROOT_POINTERS \ + NETWORK_ROOT_POINTERS \ + +void supervisor_run_background_tasks_if_tick(void); +#define RUN_BACKGROUND_TASKS (supervisor_run_background_tasks_if_tick()) + +// TODO: Used in wiznet5k driver, but may not be needed in the long run. +#define MICROPY_THREAD_YIELD() + +#define MICROPY_VM_HOOK_LOOP RUN_BACKGROUND_TASKS; +#define MICROPY_VM_HOOK_RETURN RUN_BACKGROUND_TASKS; + +// CIRCUITPY_AUTORELOAD_DELAY_MS = 0 will completely disable autoreload. +#ifndef CIRCUITPY_AUTORELOAD_DELAY_MS +#define CIRCUITPY_AUTORELOAD_DELAY_MS 500 +#endif + +#ifndef CIRCUITPY_FILESYSTEM_FLUSH_INTERVAL_MS +#define CIRCUITPY_FILESYSTEM_FLUSH_INTERVAL_MS 1000 +#endif + +#define CIRCUITPY_BOOT_OUTPUT_FILE "/boot_out.txt" + +#define CIRCUITPY_VERBOSE_BLE 0 + +#endif // __INCLUDED_MPCONFIG_CIRCUITPY_H diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk new file mode 100644 index 000000000..865654a0d --- /dev/null +++ b/py/circuitpy_mpconfig.mk @@ -0,0 +1,237 @@ +# +# This file is part of the MicroPython project, http://micropython.org/ +# +# The MIT License (MIT) +# +# Copyright (c) 2019 Dan Halbert for Adafruit Industries +# +# 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. + +# Boards default to all modules enabled (with exceptions) +# Manually disable by overriding in #mpconfigboard.mk + +# Smaller builds can be forced for resource constrained chips (typically SAMD21s +# without external flash) by setting CIRCUITPY_FULL_BUILD=0. Avoid using this +# for merely incomplete ports, as it changes settings in other files. +CIRCUITPY_FULL_BUILD ?= 1 +CFLAGS += -DCIRCUITPY_FULL_BUILD=$(CIRCUITPY_FULL_BUILD) + + +CIRCUITPY_AESIO ?= 0 +CFLAGS += -DCIRCUITPY_AESIO=$(CIRCUITPY_AESIO) + +CIRCUITPY_ANALOGIO ?= 1 +CFLAGS += -DCIRCUITPY_ANALOGIO=$(CIRCUITPY_ANALOGIO) + +CIRCUITPY_AUDIOBUSIO ?= $(CIRCUITPY_FULL_BUILD) +CFLAGS += -DCIRCUITPY_AUDIOBUSIO=$(CIRCUITPY_AUDIOBUSIO) + +CIRCUITPY_AUDIOIO ?= $(CIRCUITPY_FULL_BUILD) +CFLAGS += -DCIRCUITPY_AUDIOIO=$(CIRCUITPY_AUDIOIO) + +CIRCUITPY_AUDIOIO_COMPAT ?= $(CIRCUITPY_AUDIOIO) +CFLAGS += -DCIRCUITPY_AUDIOIO_COMPAT=$(CIRCUITPY_AUDIOIO_COMPAT) + +CIRCUITPY_AUDIOPWMIO ?= 0 +CFLAGS += -DCIRCUITPY_AUDIOPWMIO=$(CIRCUITPY_AUDIOPWMIO) + +ifndef CIRCUITPY_AUDIOCORE +ifeq ($(CIRCUITPY_AUDIOPWMIO),1) +CIRCUITPY_AUDIOCORE = $(CIRCUITPY_AUDIOPWMIO) +else +CIRCUITPY_AUDIOCORE = $(CIRCUITPY_AUDIOIO) +endif +endif +CFLAGS += -DCIRCUITPY_AUDIOCORE=$(CIRCUITPY_AUDIOCORE) + +CIRCUITPY_AUDIOMIXER ?= $(CIRCUITPY_AUDIOIO) +CFLAGS += -DCIRCUITPY_AUDIOMIXER=$(CIRCUITPY_AUDIOMIXER) + +ifndef CIRCUITPY_AUDIOMP3 +ifeq ($(CIRCUITPY_FULL_BUILD),1) +CIRCUITPY_AUDIOMP3 = $(CIRCUITPY_AUDIOCORE) +else +CIRCUITPY_AUDIOMP3 = 0 +endif +endif +CFLAGS += -DCIRCUITPY_AUDIOMP3=$(CIRCUITPY_AUDIOMP3) + +CIRCUITPY_BITBANGIO ?= $(CIRCUITPY_FULL_BUILD) +CFLAGS += -DCIRCUITPY_BITBANGIO=$(CIRCUITPY_BITBANGIO) + +# Explicitly enabled for boards that support _bleio. +CIRCUITPY_BLEIO ?= 0 +CFLAGS += -DCIRCUITPY_BLEIO=$(CIRCUITPY_BLEIO) + +CIRCUITPY_BOARD ?= 1 +CFLAGS += -DCIRCUITPY_BOARD=$(CIRCUITPY_BOARD) + +CIRCUITPY_BUSIO ?= 1 +CFLAGS += -DCIRCUITPY_BUSIO=$(CIRCUITPY_BUSIO) + +CIRCUITPY_DIGITALIO ?= 1 +CFLAGS += -DCIRCUITPY_DIGITALIO=$(CIRCUITPY_DIGITALIO) + +CIRCUITPY_COUNTIO ?= $(CIRCUITPY_FULL_BUILD) +CFLAGS += -DCIRCUITPY_COUNTIO=$(CIRCUITPY_COUNTIO) + +CIRCUITPY_DISPLAYIO ?= $(CIRCUITPY_FULL_BUILD) +CFLAGS += -DCIRCUITPY_DISPLAYIO=$(CIRCUITPY_DISPLAYIO) + +CIRCUITPY_FRAMEBUFFERIO ?= 0 +CFLAGS += -DCIRCUITPY_FRAMEBUFFERIO=$(CIRCUITPY_FRAMEBUFFERIO) + +CIRCUITPY_VECTORIO ?= $(CIRCUITPY_DISPLAYIO) +CFLAGS += -DCIRCUITPY_VECTORIO=$(CIRCUITPY_VECTORIO) + +CIRCUITPY_FREQUENCYIO ?= $(CIRCUITPY_FULL_BUILD) +CFLAGS += -DCIRCUITPY_FREQUENCYIO=$(CIRCUITPY_FREQUENCYIO) + +CIRCUITPY_GAMEPAD ?= $(CIRCUITPY_FULL_BUILD) +CFLAGS += -DCIRCUITPY_GAMEPAD=$(CIRCUITPY_GAMEPAD) + +CIRCUITPY_GAMEPADSHIFT ?= 0 +CFLAGS += -DCIRCUITPY_GAMEPADSHIFT=$(CIRCUITPY_GAMEPADSHIFT) + +CIRCUITPY_I2CSLAVE ?= $(CIRCUITPY_FULL_BUILD) +CFLAGS += -DCIRCUITPY_I2CSLAVE=$(CIRCUITPY_I2CSLAVE) + +CIRCUITPY_MATH ?= 1 +CFLAGS += -DCIRCUITPY_MATH=$(CIRCUITPY_MATH) + +CIRCUITPY__EVE ?= 0 +CFLAGS += -DCIRCUITPY__EVE=$(CIRCUITPY__EVE) + +CIRCUITPY_MICROCONTROLLER ?= 1 +CFLAGS += -DCIRCUITPY_MICROCONTROLLER=$(CIRCUITPY_MICROCONTROLLER) + +CIRCUITPY_NEOPIXEL_WRITE ?= 1 +CFLAGS += -DCIRCUITPY_NEOPIXEL_WRITE=$(CIRCUITPY_NEOPIXEL_WRITE) + +# Enabled on SAMD51. Won't fit on SAMD21 builds. Not tested on nRF or STM32F4 builds. +CIRCUITPY_NETWORK ?= 0 +CFLAGS += -DCIRCUITPY_NETWORK=$(CIRCUITPY_NETWORK) + +CIRCUITPY_NVM ?= 1 +CFLAGS += -DCIRCUITPY_NVM=$(CIRCUITPY_NVM) + +CIRCUITPY_OS ?= 1 +CFLAGS += -DCIRCUITPY_OS=$(CIRCUITPY_OS) + +CIRCUITPY_PIXELBUF ?= $(CIRCUITPY_FULL_BUILD) +CFLAGS += -DCIRCUITPY_PIXELBUF=$(CIRCUITPY_PIXELBUF) + +# Only for SAMD boards for the moment +CIRCUITPY_RGBMATRIX ?= 0 +CFLAGS += -DCIRCUITPY_RGBMATRIX=$(CIRCUITPY_RGBMATRIX) + +CIRCUITPY_PULSEIO ?= 1 +CFLAGS += -DCIRCUITPY_PULSEIO=$(CIRCUITPY_PULSEIO) + +# Only for SAMD boards for the moment +CIRCUITPY_PS2IO ?= 0 +CFLAGS += -DCIRCUITPY_PS2IO=$(CIRCUITPY_PS2IO) + +CIRCUITPY_RANDOM ?= 1 +CFLAGS += -DCIRCUITPY_RANDOM=$(CIRCUITPY_RANDOM) + +CIRCUITPY_ROTARYIO ?= 1 +CFLAGS += -DCIRCUITPY_ROTARYIO=$(CIRCUITPY_ROTARYIO) + +CIRCUITPY_RTC ?= 1 +CFLAGS += -DCIRCUITPY_RTC=$(CIRCUITPY_RTC) + +# CIRCUITPY_SAMD is handled in the atmel-samd tree. +# Only for SAMD chips. +# Assume not a SAMD build. +CIRCUITPY_SAMD ?= 0 +CFLAGS += -DCIRCUITPY_SAMD=$(CIRCUITPY_SAMD) + +# Currently always off. +CIRCUITPY_STAGE ?= 0 +CFLAGS += -DCIRCUITPY_STAGE=$(CIRCUITPY_STAGE) + +CIRCUITPY_STORAGE ?= 1 +CFLAGS += -DCIRCUITPY_STORAGE=$(CIRCUITPY_STORAGE) + +CIRCUITPY_STRUCT ?= 1 +CFLAGS += -DCIRCUITPY_STRUCT=$(CIRCUITPY_STRUCT) + +CIRCUITPY_SUPERVISOR ?= 1 +CFLAGS += -DCIRCUITPY_SUPERVISOR=$(CIRCUITPY_SUPERVISOR) + +CIRCUITPY_TIME ?= 1 +CFLAGS += -DCIRCUITPY_TIME=$(CIRCUITPY_TIME) + +# touchio might be native or generic. See circuitpy_defns.mk. +CIRCUITPY_TOUCHIO_USE_NATIVE ?= 0 +CFLAGS += -DCIRCUITPY_TOUCHIO_USE_NATIVE=$(CIRCUITPY_TOUCHIO_USE_NATIVE) + +CIRCUITPY_TOUCHIO ?= 1 +CFLAGS += -DCIRCUITPY_TOUCHIO=$(CIRCUITPY_TOUCHIO) + +# For debugging. +CIRCUITPY_UHEAP ?= 0 +CFLAGS += -DCIRCUITPY_UHEAP=$(CIRCUITPY_UHEAP) + +CIRCUITPY_USB_HID ?= 1 +CFLAGS += -DCIRCUITPY_USB_HID=$(CIRCUITPY_USB_HID) + +CIRCUITPY_USB_MIDI ?= 1 +CFLAGS += -DCIRCUITPY_USB_MIDI=$(CIRCUITPY_USB_MIDI) + +CIRCUITPY_PEW ?= 0 +CFLAGS += -DCIRCUITPY_PEW=$(CIRCUITPY_PEW) + +# For debugging. +CIRCUITPY_USTACK ?= 0 +CFLAGS += -DCIRCUITPY_USTACK=$(CIRCUITPY_USTACK) + +# Non-module conditionals + +CIRCUITPY_BITBANG_APA102 ?= 0 +CFLAGS += -DCIRCUITPY_BITBANG_APA102=$(CIRCUITPY_BITBANG_APA102) + +# Should busio.I2C() check for pullups? +# Some boards in combination with certain peripherals may not want this. +CIRCUITPY_REQUIRE_I2C_PULLUPS ?= 1 +CFLAGS += -DCIRCUITPY_REQUIRE_I2C_PULLUPS=$(CIRCUITPY_REQUIRE_I2C_PULLUPS) + +# REPL over BLE +CIRCUITPY_SERIAL_BLE ?= 0 +CFLAGS += -DCIRCUITPY_SERIAL_BLE=$(CIRCUITPY_SERIAL_BLE) + +CIRCUITPY_BLE_FILE_SERVICE ?= 0 +CFLAGS += -DCIRCUITPY_BLE_FILE_SERVICE=$(CIRCUITPY_BLE_FILE_SERVICE) + +# REPL over UART +CIRCUITPY_SERIAL_UART ?= 0 +CFLAGS += -DCIRCUITPY_SERIAL_UART=$(CIRCUITPY_SERIAL_UART) + +# ulab numerics library +CIRCUITPY_ULAB ?= $(CIRCUITPY_FULL_BUILD) +CFLAGS += -DCIRCUITPY_ULAB=$(CIRCUITPY_ULAB) + +# watchdog hardware support +CIRCUITPY_WATCHDOG ?= 0 +CFLAGS += -DCIRCUITPY_WATCHDOG=$(CIRCUITPY_WATCHDOG) + +# Enabled micropython.native decorator (experimental) +CIRCUITPY_ENABLE_MPY_NATIVE ?= 0 +CFLAGS += -DCIRCUITPY_ENABLE_MPY_NATIVE=$(CIRCUITPY_ENABLE_MPY_NATIVE) diff --git a/py/compile.c b/py/compile.c index 1218aa07e..d5fae0299 100644 --- a/py/compile.c +++ b/py/compile.c @@ -775,13 +775,22 @@ STATIC bool compile_built_in_decorator(compiler_t *comp, int name_len, mp_parse_ qstr attr = MP_PARSE_NODE_LEAF_ARG(name_nodes[1]); if (attr == MP_QSTR_bytecode) { *emit_options = MP_EMIT_OPT_BYTECODE; -#if MICROPY_EMIT_NATIVE + // @micropython.native decorator. } else if (attr == MP_QSTR_native) { + // Different from MicroPython: native doesn't raise SyntaxError if native support isn't + // compiled, it just passes through the function unmodified. + #if MICROPY_EMIT_NATIVE *emit_options = MP_EMIT_OPT_NATIVE_PYTHON; + #else + return true; + #endif + #if MICROPY_EMIT_NATIVE + // @micropython.viper decorator. } else if (attr == MP_QSTR_viper) { *emit_options = MP_EMIT_OPT_VIPER; -#endif + #endif #if MICROPY_EMIT_INLINE_ASM + // @micropython.asm_thumb decorator. } else if (attr == ASM_DECORATOR_QSTR) { *emit_options = MP_EMIT_OPT_ASM; #endif @@ -1702,6 +1711,16 @@ STATIC void compile_yield_from(compiler_t *comp) { } #if MICROPY_PY_ASYNC_AWAIT +STATIC bool compile_require_async_context(compiler_t *comp, mp_parse_node_struct_t *pns) { + int scope_flags = comp->scope_cur->scope_flags; + if(scope_flags & MP_SCOPE_FLAG_GENERATOR) { + return true; + } + compile_syntax_error(comp, (mp_parse_node_t)pns, + translate("'async for' or 'async with' outside async function")); + return false; +} + STATIC void compile_await_object_method(compiler_t *comp, qstr method) { EMIT_ARG(load_method, method, false); EMIT_ARG(call_method, 0, 0, 0); @@ -1711,6 +1730,10 @@ STATIC void compile_await_object_method(compiler_t *comp, qstr method) { STATIC void compile_async_for_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { // comp->break_label |= MP_EMIT_BREAK_FROM_FOR; + if(!compile_require_async_context(comp, pns)) { + return; + } + qstr context = MP_PARSE_NODE_LEAF_ARG(pns->nodes[1]); uint while_else_label = comp_next_label(comp); uint try_exception_label = comp_next_label(comp); @@ -1848,6 +1871,9 @@ STATIC void compile_async_with_stmt_helper(compiler_t *comp, int n, mp_parse_nod } STATIC void compile_async_with_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { + if(!compile_require_async_context(comp, pns)) { + return; + } // get the nodes for the pre-bit of the with (the a as b, c as d, ... bit) mp_parse_node_t *nodes; int n = mp_parse_node_extract_list(&pns->nodes[0], PN_with_stmt_list, &nodes); @@ -3207,7 +3233,7 @@ 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++) { + for (int j = 1; j < n_args; j++) { if (!MP_PARSE_NODE_IS_SMALL_INT(pn_arg[j])) { compile_syntax_error(comp, nodes[i], translate("'data' requires integer arguments")); return; diff --git a/py/emitglue.c b/py/emitglue.c index 74bf8ddca..7708689dd 100644 --- a/py/emitglue.c +++ b/py/emitglue.c @@ -142,11 +142,12 @@ mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, mp_obj_t def_ar fun = mp_obj_new_fun_asm(rc->n_pos_args, rc->data.u_native.fun_data, rc->data.u_native.type_sig); break; #endif - default: - // rc->kind should always be set and BYTECODE is the only remaining case - assert(rc->kind == MP_CODE_BYTECODE); + case MP_CODE_BYTECODE: fun = mp_obj_new_fun_bc(def_args, def_kw_args, rc->data.u_byte.bytecode, rc->data.u_byte.const_table); break; + default: + // All other kinds are invalid. + mp_raise_RuntimeError(translate("Corrupt raw code")); } // check for generator functions and if so wrap in generator object diff --git a/py/emitnative.c b/py/emitnative.c index 67b0025c6..60f31d15f 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -58,6 +58,22 @@ #define DEBUG_printf(...) (void)0 #endif +#ifndef N_X64 + #define N_X64 (0) +#endif +#ifndef N_X86 + #define N_X86 (0) +#endif +#ifndef N_THUMB + #define N_THUMB (0) +#endif +#ifndef N_ARM + #define N_ARM (0) +#endif +#ifndef N_XTENSA + #define N_XTENSA (0) +#endif + // wrapper around everything in this file #if N_X64 || N_X86 || N_THUMB || N_ARM || N_XTENSA @@ -443,10 +459,13 @@ STATIC void emit_native_end_pass(emit_t *emit) { type_sig |= (emit->local_vtype[i] & 0xf) << (i * 4 + 4); } + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wcast-align" mp_emit_glue_assign_native(emit->scope->raw_code, emit->do_viper_types ? MP_CODE_NATIVE_VIPER : MP_CODE_NATIVE_PY, f, f_len, (mp_uint_t*)((byte*)f + emit->const_table_offset), emit->scope->num_pos_args, emit->scope->scope_flags, type_sig); + #pragma GCC diagnostic pop } } @@ -31,6 +31,8 @@ #include "py/gc.h" #include "py/runtime.h" +#include "supervisor/shared/safe_mode.h" + #if MICROPY_ENABLE_GC #if MICROPY_DEBUG_VERBOSE // print debugging info @@ -51,9 +53,6 @@ // detect untraced object still in use #define CLEAR_ON_SWEEP (0) -#define WORDS_PER_BLOCK ((MICROPY_BYTES_PER_GC_BLOCK) / BYTES_PER_WORD) -#define BYTES_PER_BLOCK (MICROPY_BYTES_PER_GC_BLOCK) - // ATB = allocation table byte // 0b00 = FREE -- free block // 0b01 = HEAD -- head of a chain of blocks @@ -151,9 +150,13 @@ void gc_init(void *start, void *end) { #endif // Set first free ATB index to the start of the heap. - MP_STATE_MEM(gc_first_free_atb_index) = 0; + for (size_t i = 0; i < MICROPY_ATB_INDICES; i++) { + MP_STATE_MEM(gc_first_free_atb_index)[i] = 0; + } + // Set last free ATB index to the end of the heap. MP_STATE_MEM(gc_last_free_atb_index) = MP_STATE_MEM(gc_alloc_table_byte_len) - 1; + // Set the lowest long lived ptr to the end of the heap to start. This will be lowered as long // lived objects are allocated. MP_STATE_MEM(gc_lowest_long_lived_ptr) = (void*) PTR_FROM_BLOCK(MP_STATE_MEM(gc_alloc_table_byte_len * BLOCKS_PER_ATB)); @@ -174,6 +177,8 @@ void gc_init(void *start, void *end) { 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 @@ -182,6 +187,13 @@ void gc_init(void *start, void *end) { 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); } +void gc_deinit(void) { + // Run any finalizers before we stop using the heap. + gc_sweep_all(); + + MP_STATE_MEM(gc_pool_start) = 0; +} + void gc_lock(void) { GC_ENTER(); MP_STATE_MEM(gc_lock_depth)++; @@ -198,13 +210,6 @@ bool gc_is_locked(void) { return MP_STATE_MEM(gc_lock_depth) != 0; } -// ptr should be of type void* -#define VERIFY_PTR(ptr) ( \ - ((uintptr_t)(ptr) & (BYTES_PER_BLOCK - 1)) == 0 /* must be aligned on a block */ \ - && ptr >= (void*)MP_STATE_MEM(gc_pool_start) /* must be above start of pool */ \ - && ptr < (void*)MP_STATE_MEM(gc_pool_end) /* must be below end of pool */ \ - ) - #ifndef TRACE_MARK #if DEBUG_PRINT #define TRACE_MARK(block, ptr) DEBUG_printf("gc_mark(%p)\n", ptr) @@ -334,6 +339,19 @@ STATIC void gc_sweep(void) { } } +// Mark can handle NULL pointers because it verifies the pointer is within the heap bounds. +STATIC void 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); + } + } +} + void gc_collect_start(void) { GC_ENTER(); MP_STATE_MEM(gc_lock_depth)++; @@ -350,6 +368,8 @@ 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); @@ -357,25 +377,23 @@ void gc_collect_start(void) { #endif } +void gc_collect_ptr(void *ptr) { + gc_mark(ptr); +} + void gc_collect_root(void **ptrs, size_t len) { for (size_t i = 0; i < len; i++) { void *ptr = ptrs[i]; - 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); - } - } + gc_mark(ptr); } } void gc_collect_end(void) { gc_deal_with_stack_overflow(); gc_sweep(); - MP_STATE_MEM(gc_first_free_atb_index) = 0; + for (size_t i = 0; i < MICROPY_ATB_INDICES; i++) { + MP_STATE_MEM(gc_first_free_atb_index)[i] = 0; + } MP_STATE_MEM(gc_last_free_atb_index) = MP_STATE_MEM(gc_alloc_table_byte_len) - 1; MP_STATE_MEM(gc_lock_depth)--; GC_EXIT(); @@ -452,6 +470,10 @@ void gc_info(gc_info_t *info) { GC_EXIT(); } +bool gc_alloc_possible(void) { + return MP_STATE_MEM(gc_pool_start) != 0; +} + // We place long lived objects at the end of the heap rather than the start. This reduces // fragmentation by localizing the heap churn to one portion of memory (the start of the heap.) void *gc_alloc(size_t n_bytes, bool has_finaliser, bool long_lived) { @@ -463,6 +485,10 @@ void *gc_alloc(size_t n_bytes, bool has_finaliser, bool long_lived) { return NULL; } + if (MP_STATE_MEM(gc_pool_start) == 0) { + reset_into_safe_mode(GC_ALLOC_OUTSIDE_VM); + } + GC_ENTER(); // check if GC is locked @@ -483,7 +509,6 @@ void *gc_alloc(size_t n_bytes, bool has_finaliser, bool long_lived) { gc_collect(); collected = 1; GC_ENTER(); - collected = true; } #endif @@ -494,14 +519,16 @@ void *gc_alloc(size_t n_bytes, bool has_finaliser, bool long_lived) { size_t crossover_block = BLOCK_FROM_PTR(MP_STATE_MEM(gc_lowest_long_lived_ptr)); while (keep_looking) { int8_t direction = 1; - size_t start = MP_STATE_MEM(gc_first_free_atb_index); + size_t bucket = MIN(n_blocks, MICROPY_ATB_INDICES) - 1; + size_t first_free = MP_STATE_MEM(gc_first_free_atb_index)[bucket]; + size_t start = first_free; if (long_lived) { direction = -1; start = MP_STATE_MEM(gc_last_free_atb_index); } n_free = 0; // look for a run of n_blocks available blocks - for (size_t i = start; keep_looking && MP_STATE_MEM(gc_first_free_atb_index) <= i && i <= MP_STATE_MEM(gc_last_free_atb_index); i += direction) { + for (size_t i = start; keep_looking && first_free <= i && i <= MP_STATE_MEM(gc_last_free_atb_index); i += direction) { byte a = MP_STATE_MEM(gc_alloc_table_start)[i]; // Four ATB states are packed into a single byte. int j = 0; @@ -546,22 +573,24 @@ void *gc_alloc(size_t n_bytes, bool has_finaliser, bool long_lived) { // Found free space ending at found_block inclusive. // Also, set last free ATB index to block after last block we found, for start of - // next scan. To reduce fragmentation, we only do this if we were looking - // for a single free block, which guarantees that there are no free blocks - // before this one. Also, whenever we free or shrink a block we must check - // if this index needs adjusting (see gc_realloc and gc_free). + // next scan. Also, whenever we free or shrink a block we must check if this index needs + // adjusting (see gc_realloc and gc_free). if (!long_lived) { end_block = found_block; start_block = found_block - n_free + 1; - if (n_blocks == 1) { - MP_STATE_MEM(gc_first_free_atb_index) = (found_block + 1) / BLOCKS_PER_ATB; + if (n_blocks < MICROPY_ATB_INDICES) { + size_t next_free_atb = (found_block + n_blocks) / BLOCKS_PER_ATB; + // Update all atb indices for larger blocks too. + for (size_t i = n_blocks - 1; i < MICROPY_ATB_INDICES; i++) { + MP_STATE_MEM(gc_first_free_atb_index)[i] = next_free_atb; + } } } else { start_block = found_block; end_block = found_block + n_free - 1; - if (n_blocks == 1) { - MP_STATE_MEM(gc_last_free_atb_index) = (found_block - 1) / BLOCKS_PER_ATB; - } + // Always update the bounds of the long lived area because we assume it is contiguous. (It + // can still be reset by a sweep.) + MP_STATE_MEM(gc_last_free_atb_index) = (found_block - 1) / BLOCKS_PER_ATB; } #ifdef LOG_HEAP_ACTIVITY @@ -652,32 +681,42 @@ void gc_free(void *ptr) { if (ptr == NULL) { GC_EXIT(); } else { + if (MP_STATE_MEM(gc_pool_start) == 0) { + reset_into_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); + size_t start_block = BLOCK_FROM_PTR(ptr); + assert(ATB_GET_KIND(start_block) == AT_HEAD); #if MICROPY_ENABLE_FINALISER - FTB_CLEAR(block); + FTB_CLEAR(start_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_first_free_atb_index)) { - MP_STATE_MEM(gc_first_free_atb_index) = block / BLOCKS_PER_ATB; - } - 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; - } - // free head and all of its tail blocks - #ifdef LOG_HEAP_ACTIVITY - gc_log_change(block, 0); - #endif + #ifdef LOG_HEAP_ACTIVITY + gc_log_change(start_block, 0); + #endif + size_t block = start_block; do { ATB_ANY_TO_FREE(block); block += 1; } while (ATB_GET_KIND(block) == AT_TAIL); + // Update the first free pointer for our size only. Not much calls gc_free directly so there + // is decent chance we'll want to allocate this size again. By only updating the specific + // size we don't risk something smaller fitting in. + size_t n_blocks = block - start_block; + size_t bucket = MIN(n_blocks, MICROPY_ATB_INDICES) - 1; + size_t new_free_atb = start_block / BLOCKS_PER_ATB; + if (new_free_atb < MP_STATE_MEM(gc_first_free_atb_index)[bucket]) { + MP_STATE_MEM(gc_first_free_atb_index)[bucket] = new_free_atb; + } + // set the last_free pointer to this block if it's earlier in the heap + if (new_free_atb > MP_STATE_MEM(gc_last_free_atb_index)) { + MP_STATE_MEM(gc_last_free_atb_index) = new_free_atb; + } + GC_EXIT(); #if EXTENSIVE_HEAP_PROFILING @@ -848,11 +887,13 @@ void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) { } // 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_first_free_atb_index)) { - MP_STATE_MEM(gc_first_free_atb_index) = (block + new_blocks) / BLOCKS_PER_ATB; + size_t new_free_atb = (block + new_blocks) / BLOCKS_PER_ATB; + size_t bucket = MIN(n_blocks - new_blocks, MICROPY_ATB_INDICES) - 1; + if (new_free_atb < MP_STATE_MEM(gc_first_free_atb_index)[bucket]) { + MP_STATE_MEM(gc_first_free_atb_index)[bucket] = new_free_atb; } - 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 (new_free_atb > MP_STATE_MEM(gc_last_free_atb_index)) { + MP_STATE_MEM(gc_last_free_atb_index) = new_free_atb; } GC_EXIT(); @@ -925,6 +966,36 @@ 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); + 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; + } + } + current_reference_block = current_reference_block[0]; + } + void** next_block = gc_alloc(BYTES_PER_BLOCK, false, true); + if (next_block == NULL) { + return false; + } + if (MP_STATE_MEM(permanent_pointers) == NULL) { + MP_STATE_MEM(permanent_pointers) = next_block; + } else { + current_reference_block[0] = next_block; + } + next_block[1] = ptr; + return true; +} + void gc_dump_info(void) { gc_info_t info; gc_info(&info); @@ -29,9 +29,21 @@ #include <stdint.h> #include "py/mpconfig.h" +#include "py/mpstate.h" #include "py/misc.h" +#define WORDS_PER_BLOCK ((MICROPY_BYTES_PER_GC_BLOCK) / BYTES_PER_WORD) +#define BYTES_PER_BLOCK (MICROPY_BYTES_PER_GC_BLOCK) + +// ptr should be of type void* +#define VERIFY_PTR(ptr) ( \ + ((uintptr_t)(ptr) & (BYTES_PER_BLOCK - 1)) == 0 /* must be aligned on a block */ \ + && ptr >= (void*)MP_STATE_MEM(gc_pool_start) /* must be above start of pool */ \ + && ptr < (void*)MP_STATE_MEM(gc_pool_end) /* must be below end of pool */ \ + ) + void gc_init(void *start, void *end); +void gc_deinit(void); // These lock/unlock functions can be nested. // They can be used to prevent the GC from allocating/freeing. @@ -42,9 +54,12 @@ bool gc_is_locked(void); // A given port must implement gc_collect by using the other collect functions. void gc_collect(void); void gc_collect_start(void); +void gc_collect_ptr(void *ptr); void gc_collect_root(void **ptrs, size_t len); void gc_collect_end(void); +// Is the gc heap available? +bool gc_alloc_possible(void); void *gc_alloc(size_t n_bytes, bool has_finaliser, bool long_lived); // Use this function to sweep the whole heap and run all finalisers @@ -56,6 +71,10 @@ bool gc_has_finaliser(const void *ptr); void *gc_make_long_lived(void *old_ptr); void *gc_realloc(void *ptr, size_t n_bytes, bool allow_move); +// Prevents a pointer from ever being freed because it establishes a permanent reference to it. Use +// very sparingly because it can leak memory. +bool gc_never_free(void *ptr); + typedef struct _gc_info_t { size_t total; size_t used; diff --git a/py/gc_long_lived.c b/py/gc_long_lived.c index 3c54de7ed..0e94390e9 100755 --- a/py/gc_long_lived.c +++ b/py/gc_long_lived.c @@ -27,6 +27,7 @@ #include "py/emitglue.h" #include "py/gc_long_lived.h" #include "py/gc.h" +#include "py/mpstate.h" mp_obj_fun_bc_t *make_fun_bc_long_lived(mp_obj_fun_bc_t *fun_bc, uint8_t max_depth) { #ifndef MICROPY_ENABLE_GC @@ -88,7 +89,7 @@ mp_obj_dict_t *make_dict_long_lived(mp_obj_dict_t *dict, uint8_t max_depth) { #ifndef MICROPY_ENABLE_GC return dict; #endif - if (dict == NULL || max_depth == 0) { + if (dict == NULL || max_depth == 0 || dict == &MP_STATE_VM(dict_main) || dict->map.is_fixed) { return dict; } // Don't recurse unnecessarily. Return immediately if we've already seen this dict. @@ -125,6 +126,11 @@ mp_obj_t make_obj_long_lived(mp_obj_t obj, uint8_t max_depth){ if (obj == NULL) { return obj; } + // If not in the GC pool, do nothing. This can happen (at least) when + // there are frozen mp_type_bytes objects in ROM. + if (!VERIFY_PTR((void *)obj)) { + return obj; + } if (MP_OBJ_IS_TYPE(obj, &mp_type_fun_bc)) { mp_obj_fun_bc_t *fun_bc = MP_OBJ_TO_PTR(obj); return MP_OBJ_FROM_PTR(make_fun_bc_long_lived(fun_bc, max_depth)); diff --git a/py/lexer.c b/py/lexer.c index 755fa625b..00cd59bca 100644 --- a/py/lexer.c +++ b/py/lexer.c @@ -64,6 +64,12 @@ STATIC bool is_char_or3(mp_lexer_t *lex, byte c1, byte c2, byte c3) { return lex->chr0 == c1 || lex->chr0 == c2 || lex->chr0 == c3; } +#if MICROPY_COMP_FSTRING_LITERAL +STATIC bool is_char_or4(mp_lexer_t *lex, byte c1, byte c2, byte c3, byte c4) { + return lex->chr0 == c1 || lex->chr0 == c2 || lex->chr0 == c3 || lex->chr0 == c4; +} +#endif + STATIC bool is_char_following(mp_lexer_t *lex, byte c) { return lex->chr1 == c; } @@ -107,7 +113,13 @@ STATIC bool is_following_odigit(mp_lexer_t *lex) { STATIC bool is_string_or_bytes(mp_lexer_t *lex) { return is_char_or(lex, '\'', '\"') +#if MICROPY_COMP_FSTRING_LITERAL + || (is_char_or4(lex, 'r', 'u', 'b', 'f') && is_char_following_or(lex, '\'', '\"')) + || ((is_char_and(lex, 'r', 'f') || is_char_and(lex, 'f', 'r')) + && is_char_following_following_or(lex, '\'', '\"')) +#else || (is_char_or3(lex, 'r', 'u', 'b') && is_char_following_or(lex, '\'', '\"')) +#endif || ((is_char_and(lex, 'r', 'b') || is_char_and(lex, 'b', 'r')) && is_char_following_following_or(lex, '\'', '\"')); } @@ -121,6 +133,31 @@ STATIC bool is_tail_of_identifier(mp_lexer_t *lex) { return is_head_of_identifier(lex) || is_digit(lex); } +#if MICROPY_COMP_FSTRING_LITERAL +STATIC void swap_char_banks(mp_lexer_t *lex) { + if (lex->vstr_postfix_processing) { + lex->chr3 = lex->chr0; + lex->chr4 = lex->chr1; + lex->chr5 = lex->chr2; + lex->chr0 = lex->vstr_postfix.buf[0]; + lex->chr1 = lex->vstr_postfix.buf[1]; + lex->chr2 = lex->vstr_postfix.buf[2]; + + lex->vstr_postfix_idx = 3; + } else { + // blindly reset to the "backup" bank when done postfix processing + // this restores control to the mp_reader + lex->chr0 = lex->chr3; + lex->chr1 = lex->chr4; + lex->chr2 = lex->chr5; + // willfully ignoring setting chr3-5 here - WARNING consider those garbage data now + + vstr_reset(&lex->vstr_postfix); + lex->vstr_postfix_idx = 0; + } +} +#endif + STATIC void next_char(mp_lexer_t *lex) { if (lex->chr0 == '\n') { // a new line @@ -136,7 +173,19 @@ STATIC void next_char(mp_lexer_t *lex) { lex->chr0 = lex->chr1; lex->chr1 = lex->chr2; - lex->chr2 = lex->reader.readbyte(lex->reader.data); + +#if MICROPY_COMP_FSTRING_LITERAL + if (lex->vstr_postfix_processing) { + if (lex->vstr_postfix_idx == lex->vstr_postfix.len) { + lex->chr2 = '\0'; + } else { + lex->chr2 = lex->vstr_postfix.buf[lex->vstr_postfix_idx++]; + } + } else +#endif + { + lex->chr2 = lex->reader.readbyte(lex->reader.data); + } if (lex->chr1 == '\r') { // CR is a new line, converted to LF @@ -151,6 +200,13 @@ STATIC void next_char(mp_lexer_t *lex) { if (lex->chr2 == MP_LEXER_EOF && lex->chr1 != MP_LEXER_EOF && lex->chr1 != '\n') { lex->chr2 = '\n'; } + +#if MICROPY_COMP_FSTRING_LITERAL + if (lex->vstr_postfix_processing && lex->chr0 == '\0') { + lex->vstr_postfix_processing = false; + swap_char_banks(lex); + } +#endif } STATIC void indent_push(mp_lexer_t *lex, size_t indent) { @@ -270,7 +326,7 @@ STATIC bool get_hex(mp_lexer_t *lex, size_t num_digits, mp_uint_t *result) { return true; } -STATIC void parse_string_literal(mp_lexer_t *lex, bool is_raw) { +STATIC void parse_string_literal(mp_lexer_t *lex, bool is_raw, bool is_fstring) { // get first quoting character char quote_char = '\''; if (is_char(lex, '\"')) { @@ -291,15 +347,71 @@ STATIC void parse_string_literal(mp_lexer_t *lex, bool is_raw) { } size_t n_closing = 0; +#if MICROPY_COMP_FSTRING_LITERAL + bool in_expression = false; + bool expression_eat = true; +#endif + while (!is_end(lex) && (num_quotes > 1 || !is_char(lex, '\n')) && n_closing < num_quotes) { if (is_char(lex, quote_char)) { n_closing += 1; vstr_add_char(&lex->vstr, CUR_CHAR(lex)); } else { n_closing = 0; +#if MICROPY_COMP_FSTRING_LITERAL + if (is_fstring && is_char(lex, '{')) { + vstr_add_char(&lex->vstr, CUR_CHAR(lex)); + in_expression = !in_expression; + expression_eat = in_expression; + + if (lex->vstr_postfix.len == 0) { + vstr_add_str(&lex->vstr_postfix, ".format("); + } + + next_char(lex); + continue; + } + + if (is_fstring && is_char(lex, '}')) { + vstr_add_char(&lex->vstr, CUR_CHAR(lex)); + + if (in_expression) { + in_expression = false; + vstr_add_char(&lex->vstr_postfix, ','); + } + + next_char(lex); + continue; + } + + if (in_expression) { + // throw errors for illegal chars inside f-string expressions + if (is_char(lex, '#')) { + lex->tok_kind = MP_TOKEN_FSTRING_COMMENT; + return; + } else if (is_char(lex, '\\')) { + lex->tok_kind = MP_TOKEN_FSTRING_BACKSLASH; + return; + } else if (is_char(lex, ':')) { + expression_eat = false; + } + + unichar c = CUR_CHAR(lex); + if (expression_eat) { + vstr_add_char(&lex->vstr_postfix, c); + } else { + vstr_add_char(&lex->vstr, c); + } + + next_char(lex); + continue; + } +#endif + if (is_char(lex, '\\')) { next_char(lex); unichar c = CUR_CHAR(lex); + if (is_raw) { // raw strings allow escaping of quotes, but the backslash is also emitted vstr_add_char(&lex->vstr, '\\'); @@ -430,6 +542,15 @@ STATIC bool skip_whitespace(mp_lexer_t *lex, bool stop_at_newline) { } void mp_lexer_to_next(mp_lexer_t *lex) { +#if MICROPY_COMP_FSTRING_LITERAL + if (lex->vstr_postfix.len && !lex->vstr_postfix_processing) { + // end format call injection + vstr_add_char(&lex->vstr_postfix, ')'); + lex->vstr_postfix_processing = true; + swap_char_banks(lex); + } +#endif + // start new token text vstr_reset(&lex->vstr); @@ -481,10 +602,19 @@ void mp_lexer_to_next(mp_lexer_t *lex) { // MP_TOKEN_END is used to indicate that this is the first string token lex->tok_kind = MP_TOKEN_END; +#if MICROPY_COMP_FSTRING_LITERAL + bool saw_normal = false, saw_fstring = false; +#endif + // Loop to accumulate string/bytes literals do { // parse type codes bool is_raw = false; +#if MICROPY_COMP_FSTRING_LITERAL + bool is_fstring = false; +#else + const bool is_fstring = false; +#endif mp_token_kind_t kind = MP_TOKEN_STRING; int n_char = 0; if (is_char(lex, 'u')) { @@ -503,7 +633,33 @@ void mp_lexer_to_next(mp_lexer_t *lex) { kind = MP_TOKEN_BYTES; n_char = 2; } +#if MICROPY_COMP_FSTRING_LITERAL + if (is_char_following(lex, 'f')) { + lex->tok_kind = MP_TOKEN_FSTRING_RAW; + break; + } + } else if (is_char(lex, 'f')) { + if (is_char_following(lex, 'r')) { + lex->tok_kind = MP_TOKEN_FSTRING_RAW; + break; + } + n_char = 1; + is_fstring = true; +#endif + } + +#if MICROPY_COMP_FSTRING_LITERAL + if (is_fstring) { + saw_fstring = true; + } else { + saw_normal = true; + } + + if (saw_fstring && saw_normal) { + // Can't concatenate f-string with normal string + break; } +#endif // Set or check token kind if (lex->tok_kind == MP_TOKEN_END) { @@ -522,13 +678,12 @@ void mp_lexer_to_next(mp_lexer_t *lex) { } // Parse the literal - parse_string_literal(lex, is_raw); + parse_string_literal(lex, is_raw, is_fstring); // Skip whitespace so we can check if there's another string following skip_whitespace(lex, true); } while (is_string_or_bytes(lex)); - } else if (is_head_of_identifier(lex)) { lex->tok_kind = MP_TOKEN_NAME; @@ -682,6 +837,9 @@ mp_lexer_t *mp_lexer_new(qstr src_name, mp_reader_t reader) { lex->num_indent_level = 1; lex->indent_level = m_new(uint16_t, lex->alloc_indent_level); vstr_init(&lex->vstr, 32); +#if MICROPY_COMP_FSTRING_LITERAL + vstr_init(&lex->vstr_postfix, 0); +#endif // store sentinel for first indentation level lex->indent_level[0] = 0; diff --git a/py/lexer.h b/py/lexer.h index a29709107..a3eaa2a7e 100644 --- a/py/lexer.h +++ b/py/lexer.h @@ -44,6 +44,14 @@ typedef enum _mp_token_kind_t { MP_TOKEN_INVALID, MP_TOKEN_DEDENT_MISMATCH, MP_TOKEN_LONELY_STRING_OPEN, +#if MICROPY_COMP_FSTRING_LITERAL + MP_TOKEN_FSTRING_BACKSLASH, + MP_TOKEN_FSTRING_COMMENT, + MP_TOKEN_FSTRING_UNCLOSED, + MP_TOKEN_FSTRING_UNOPENED, + MP_TOKEN_FSTRING_EMPTY_EXP, + MP_TOKEN_FSTRING_RAW, +#endif MP_TOKEN_NEWLINE, MP_TOKEN_INDENT, @@ -150,6 +158,9 @@ typedef struct _mp_lexer_t { mp_reader_t reader; // stream source unichar chr0, chr1, chr2; // current cached characters from source +#if MICROPY_COMP_FSTRING_LITERAL + unichar chr3, chr4, chr5; // current cached characters from alt source +#endif size_t line; // current source line size_t column; // current source column @@ -165,6 +176,11 @@ typedef struct _mp_lexer_t { size_t tok_column; // token source column mp_token_kind_t tok_kind; // token kind vstr_t vstr; // token data +#if MICROPY_COMP_FSTRING_LITERAL + vstr_t vstr_postfix; // postfix to apply to string + bool vstr_postfix_processing; + uint16_t vstr_postfix_idx; +#endif } mp_lexer_t; mp_lexer_t *mp_lexer_new(qstr src_name, mp_reader_t reader); diff --git a/py/makemoduledefs.py b/py/makemoduledefs.py new file mode 100644 index 000000000..18d327f00 --- /dev/null +++ b/py/makemoduledefs.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python + +# This pre-processor parses provided objects' c files for +# MP_REGISTER_MODULE(module_name, obj_module, enabled_define) +# These are used to generate a header with the required entries for +# "mp_rom_map_elem_t mp_builtin_module_table[]" in py/objmodule.c + +from __future__ import print_function + +import re +import os +import argparse + + +pattern = re.compile( + r"[\n;]\s*MP_REGISTER_MODULE\((.*?),\s*(.*?),\s*(.*?)\);", + flags=re.DOTALL +) + + +def find_c_file(obj_file, vpath): + """ Search vpaths for the c file that matches the provided object_file. + + :param str obj_file: object file to find the matching c file for + :param List[str] vpath: List of base paths, similar to gcc vpath + :return: str path to c file or None + """ + c_file = None + relative_c_file = os.path.splitext(obj_file)[0] + ".c" + relative_c_file = relative_c_file.lstrip('/\\') + for p in vpath: + possible_c_file = os.path.join(p, relative_c_file) + if os.path.exists(possible_c_file): + c_file = possible_c_file + break + + return c_file + + +def find_module_registrations(c_file): + """ Find any MP_REGISTER_MODULE definitions in the provided c file. + + :param str c_file: path to c file to check + :return: List[(module_name, obj_module, enabled_define)] + """ + global pattern + + if c_file is None: + # No c file to match the object file, skip + return set() + + with open(c_file) as c_file_obj: + return set(re.findall(pattern, c_file_obj.read())) + + +def generate_module_table_header(modules): + """ Generate header with module table entries for builtin modules. + + :param List[(module_name, obj_module, enabled_define)] modules: module defs + :return: None + """ + + # Print header file for all external modules. + mod_defs = [] + print("// Automatically generated by makemoduledefs.py.\n") + for module_name, obj_module, enabled_define in modules: + mod_def = "MODULE_DEF_{}".format(module_name.upper()) + mod_defs.append(mod_def) + print(( + "#if ({enabled_define})\n" + " extern const struct _mp_obj_module_t {obj_module};\n" + " #define {mod_def} {{ MP_ROM_QSTR({module_name}), MP_ROM_PTR(&{obj_module}) }},\n" + "#else\n" + " #define {mod_def}\n" + "#endif\n" + ).format(module_name=module_name, obj_module=obj_module, + enabled_define=enabled_define, mod_def=mod_def) + ) + + print("\n#define MICROPY_REGISTERED_MODULES \\") + + for mod_def in mod_defs: + print(" {mod_def} \\".format(mod_def=mod_def)) + + print("// MICROPY_REGISTERED_MODULES") + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--vpath", default=".", + help="comma separated list of folders to search for c files in") + parser.add_argument("files", nargs="*", + help="list of c files to search") + args = parser.parse_args() + + vpath = [p.strip() for p in args.vpath.split(',')] + + modules = set() + for obj_file in args.files: + c_file = find_c_file(obj_file, vpath) + modules |= find_module_registrations(c_file) + + generate_module_table_header(sorted(modules)) + + +if __name__ == '__main__': + main() diff --git a/py/makeqstrdata.py b/py/makeqstrdata.py index 5b5ec1c37..df2c687e5 100644 --- a/py/makeqstrdata.py +++ b/py/makeqstrdata.py @@ -1,7 +1,10 @@ """ Process raw qstr file and output qstr data with length, hash and data bytes. -This script works with Python 2.6, 2.7, 3.3 and 3.4. +This script works with Python 2.7, 3.3 and 3.4. + +For documentation about the format of compressed translated strings, see +supervisor/shared/translate.h """ from __future__ import print_function @@ -103,14 +106,10 @@ def compute_huffman_coding(translations, qstrs, compression_filename): # go through each qstr and print it out for _, _, qstr in qstrs.values(): all_strings.append(qstr) - all_strings_concat = "".join(all_strings).encode("utf-8") + all_strings_concat = "".join(all_strings) counts = collections.Counter(all_strings_concat) - # add other values - for i in range(256): - if i not in counts: - counts[i] = 0 cb = huffman.codebook(counts.items()) - values = bytearray() + values = [] length_count = {} renumbered = 0 last_l = None @@ -124,30 +123,49 @@ def compute_huffman_coding(translations, qstrs, compression_filename): if last_l: renumbered <<= (l - last_l) canonical[ch] = '{0:0{width}b}'.format(renumbered, width=l) - if chr(ch) in C_ESCAPES: - s = C_ESCAPES[chr(ch)] - else: - s = chr(ch) - print("//", ch, s, counts[ch], canonical[ch], renumbered) + s = C_ESCAPES.get(ch, ch) + print("//", ord(ch), s, counts[ch], canonical[ch], renumbered) renumbered += 1 last_l = l lengths = bytearray() - for i in range(1, max(length_count) + 1): + print("// length count", length_count) + for i in range(1, max(length_count) + 2): lengths.append(length_count.get(i, 0)) + print("// values", values, "lengths", len(lengths), lengths) + print("// estimated total memory size", len(lengths) + 2*len(values) + sum(len(cb[u]) for u in all_strings_concat)) print("//", values, lengths) + values_type = "uint16_t" if max(ord(u) for u in values) > 255 else "uint8_t" + max_translation_encoded_length = max(len(translation.encode("utf-8")) for original,translation in translations) with open(compression_filename, "w") as f: f.write("const uint8_t lengths[] = {{ {} }};\n".format(", ".join(map(str, lengths)))) - f.write("const uint8_t values[256] = {{ {} }};\n".format(", ".join(map(str, values)))) + f.write("const {} values[] = {{ {} }};\n".format(values_type, ", ".join(str(ord(u)) for u in values))) + f.write("#define compress_max_length_bits ({})\n".format(max_translation_encoded_length.bit_length())) return values, lengths -def decompress(encoding_table, length, encoded): +def decompress(encoding_table, encoded, encoded_length_bits): values, lengths = encoding_table - #print(l, encoded) - dec = bytearray(length) + dec = [] this_byte = 0 this_bit = 7 b = encoded[this_byte] - for i in range(length): + bits = 0 + for i in range(encoded_length_bits): + bits <<= 1 + if 0x80 & b: + bits |= 1 + + b <<= 1 + if this_bit == 0: + this_bit = 7 + this_byte += 1 + if this_byte < len(encoded): + b = encoded[this_byte] + else: + this_bit -= 1 + length = bits + + i = 0 + while i < length: bits = 0 bit_length = 0 max_code = lengths[0] @@ -173,18 +191,32 @@ def decompress(encoding_table, length, encoded): searched_length += lengths[bit_length] v = values[searched_length + bits - max_code] - dec[i] = v - return dec + i += len(v.encode('utf-8')) + dec.append(v) + return ''.join(dec) -def compress(encoding_table, decompressed): - if not isinstance(decompressed, bytes): +def compress(encoding_table, decompressed, encoded_length_bits, len_translation_encoded): + if not isinstance(decompressed, str): raise TypeError() values, lengths = encoding_table - enc = bytearray(len(decompressed)) + enc = bytearray(len(decompressed) * 3) #print(decompressed) #print(lengths) current_bit = 7 current_byte = 0 + + code = len_translation_encoded + bits = encoded_length_bits+1 + for i in range(bits - 1, 0, -1): + if len_translation_encoded & (1 << (i - 1)): + enc[current_byte] |= 1 << current_bit + if current_bit == 0: + current_bit = 7 + #print("packed {0:0{width}b}".format(enc[current_byte], width=8)) + current_byte += 1 + else: + current_bit -= 1 + for c in decompressed: #print() #print("char", c, values.index(c)) @@ -227,6 +259,8 @@ def compress(encoding_table, decompressed): current_bit -= 1 if current_bit != 7: current_byte += 1 + if current_byte > len(decompressed): + print("Note: compression increased length", repr(decompressed), len(decompressed), current_byte, file=sys.stderr) return enc[:current_byte] def qstr_escape(qst): @@ -343,14 +377,17 @@ def print_qstr_data(encoding_table, qcfgs, qstrs, i18ns): total_text_size = 0 total_text_compressed_size = 0 + max_translation_encoded_length = max(len(translation.encode("utf-8")) for original, translation in i18ns) + encoded_length_bits = max_translation_encoded_length.bit_length() for original, translation in i18ns: translation_encoded = translation.encode("utf-8") - compressed = compress(encoding_table, translation_encoded) + compressed = compress(encoding_table, translation, encoded_length_bits, len(translation_encoded)) total_text_compressed_size += len(compressed) - decompressed = decompress(encoding_table, len(translation_encoded), compressed).decode("utf-8") + decompressed = decompress(encoding_table, compressed, encoded_length_bits) + assert decompressed == translation for c in C_ESCAPES: decompressed = decompressed.replace(c, C_ESCAPES[c]) - print("TRANSLATION(\"{}\", {}, {{ {} }}) // {}".format(original, len(translation_encoded)+1, ", ".join(["0x{:02x}".format(x) for x in compressed]), decompressed)) + print("TRANSLATION(\"{}\", {}) // {}".format(original, ", ".join(["{:d}".format(x) for x in compressed]), decompressed)) total_text_size += len(translation.encode("utf-8")) print() @@ -386,6 +423,7 @@ if __name__ == "__main__": qcfgs, qstrs, i18ns = parse_input_headers(args.infiles) if args.translation: + i18ns = sorted(i18ns) translations = translate(args.translation, i18ns) encoding_table = compute_huffman_coding(translations, qstrs, args.compression_filename) print_qstr_data(encoding_table, qcfgs, qstrs, translations) @@ -33,6 +33,8 @@ #include "py/misc.h" #include "py/runtime.h" +#include "supervisor/linker.h" + #if MICROPY_DEBUG_VERBOSE // print debugging info #define DEBUG_PRINT (1) #else // don't print debugging info @@ -143,7 +145,7 @@ STATIC void mp_map_rehash(mp_map_t *map) { // - returns slot, with key non-null and value=MP_OBJ_NULL if it was added // MP_MAP_LOOKUP_REMOVE_IF_FOUND behaviour: // - returns NULL if not found, else the slot if was found in with key null and value non-null -mp_map_elem_t *mp_map_lookup(mp_map_t *map, mp_obj_t index, mp_map_lookup_kind_t lookup_kind) { +mp_map_elem_t *PLACE_IN_ITCM(mp_map_lookup)(mp_map_t *map, mp_obj_t index, mp_map_lookup_kind_t lookup_kind) { // If the map is a fixed array then we must only be called for a lookup assert(!map->is_fixed || lookup_kind == MP_MAP_LOOKUP); diff --git a/py/mkrules.mk b/py/mkrules.mk index aa94ba412..13a73b90e 100644 --- a/py/mkrules.mk +++ b/py/mkrules.mk @@ -20,12 +20,12 @@ endif # can be located. By following this scheme, it allows a single build rule # to be used to compile all .c files. -vpath %.S . $(TOP) +vpath %.S . $(TOP) $(USER_C_MODULES) $(BUILD)/%.o: %.S $(STEPECHO) "CC $<" $(Q)$(CC) $(CFLAGS) -c -o $@ $< -vpath %.s . $(TOP) +vpath %.s . $(TOP) $(USER_C_MODULES) $(BUILD)/%.o: %.s $(STEPECHO) "AS $<" $(Q)$(AS) -o $@ $< @@ -42,7 +42,7 @@ $(Q)$(CC) $(CFLAGS) -c -MD -o $@ $< $(RM) -f $(@:.o=.d) endef -vpath %.c . $(TOP) +vpath %.c . $(TOP) $(USER_C_MODULES) $(BUILD)/%.o: %.c $(call compile_c) @@ -56,7 +56,7 @@ $(BUILD)/%.o: %.c QSTR_GEN_EXTRA_CFLAGS += -I$(BUILD)/tmp -vpath %.c . $(TOP) +vpath %.c . $(TOP) $(USER_C_MODULES) $(BUILD)/%.pp: %.c $(STEPECHO) "PreProcess $<" @@ -145,7 +145,7 @@ $(PROG): $(OBJ) # 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) -ifndef DEBUG +ifdef STRIP_CIRCUITPYTHON $(Q)$(STRIP) $(STRIPFLAGS_EXTRA) $(PROG) endif $(Q)$(SIZE) $$(find $(BUILD) -path "$(BUILD)/build/frozen*.o") $(PROG) diff --git a/py/modbuiltins.c b/py/modbuiltins.c index fc7ec24c7..e764f1987 100644 --- a/py/modbuiltins.c +++ b/py/modbuiltins.c @@ -371,10 +371,11 @@ STATIC mp_obj_t mp_builtin_pow(size_t n_args, const mp_obj_t *args) { MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_pow_obj, 2, 3, mp_builtin_pow); STATIC mp_obj_t mp_builtin_print(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_sep, ARG_end, ARG_file }; + enum { ARG_sep, ARG_end, ARG_flush, ARG_file }; static const mp_arg_t allowed_args[] = { { MP_QSTR_sep, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_QSTR(MP_QSTR__space_)} }, { MP_QSTR_end, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_QSTR(MP_QSTR__0x0a_)} }, + { MP_QSTR_flush, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES { MP_QSTR_file, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_sys_stdout_obj)} }, #endif @@ -414,6 +415,9 @@ STATIC mp_obj_t mp_builtin_print(size_t n_args, const mp_obj_t *pos_args, mp_map } #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES mp_stream_write_adaptor(print.data, end_data, u.len[1]); + if (u.args[ARG_flush].u_bool) { + mp_stream_flush(MP_OBJ_FROM_PTR(print.data)); + } #else mp_print_strn(&mp_plat_print, end_data, u.len[1], 0, 0, 0); #endif @@ -451,13 +455,13 @@ STATIC mp_obj_t mp_builtin_round(size_t n_args, const mp_obj_t *args) { return o_in; } - #if !MICROPY_PY_BUILTINS_ROUND_INT - mp_raise_NotImplementedError(NULL); - #else mp_int_t num_dig = mp_obj_get_int(args[1]); if (num_dig >= 0) { return o_in; } + #if !MICROPY_PY_BUILTINS_ROUND_INT + mp_raise_NotImplementedError(NULL); + #else mp_obj_t mult = mp_binary_op(MP_BINARY_OP_POWER, MP_OBJ_NEW_SMALL_INT(10), MP_OBJ_NEW_SMALL_INT(-num_dig)); mp_obj_t half_mult = mp_binary_op(MP_BINARY_OP_FLOOR_DIVIDE, mult, MP_OBJ_NEW_SMALL_INT(2)); @@ -516,7 +520,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(translate("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_type_list.make_new(&mp_type_list, 1, args, NULL); mp_obj_list_sort(1, &self, kwargs); return self; @@ -718,6 +722,7 @@ STATIC const mp_rom_map_elem_t mp_module_builtins_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_KeyError), MP_ROM_PTR(&mp_type_KeyError) }, { MP_ROM_QSTR(MP_QSTR_LookupError), MP_ROM_PTR(&mp_type_LookupError) }, { MP_ROM_QSTR(MP_QSTR_MemoryError), MP_ROM_PTR(&mp_type_MemoryError) }, + { MP_ROM_QSTR(MP_QSTR_MpyError), MP_ROM_PTR(&mp_type_MpyError) }, { MP_ROM_QSTR(MP_QSTR_NameError), MP_ROM_PTR(&mp_type_NameError) }, { MP_ROM_QSTR(MP_QSTR_NotImplementedError), MP_ROM_PTR(&mp_type_NotImplementedError) }, { MP_ROM_QSTR(MP_QSTR_OSError), MP_ROM_PTR(&mp_type_OSError) }, diff --git a/py/modio.c b/py/modio.c index 9b09de96e..17840a6d8 100644 --- a/py/modio.c +++ b/py/modio.c @@ -46,11 +46,11 @@ STATIC const mp_obj_type_t mp_type_iobase; STATIC mp_obj_base_t iobase_singleton = {&mp_type_iobase}; -STATIC mp_obj_t iobase_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 iobase_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { (void)type; (void)n_args; - (void)n_kw; (void)args; + (void)kw_args; return MP_OBJ_FROM_PTR(&iobase_singleton); } @@ -90,6 +90,7 @@ 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, @@ -113,8 +114,8 @@ typedef struct _mp_obj_bufwriter_t { byte buf[0]; } mp_obj_bufwriter_t; -STATIC mp_obj_t bufwriter_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { - mp_arg_check_num(n_args, n_kw, 2, 2, false); +STATIC mp_obj_t bufwriter_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + mp_arg_check_num(n_args, kw_args, 2, 2, false); size_t alloc = mp_obj_get_int(args[1]); mp_obj_bufwriter_t *o = m_new_obj_var(mp_obj_bufwriter_t, byte, alloc); o->base.type = type; @@ -185,6 +186,7 @@ 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, }; diff --git a/py/modstruct.c b/py/modstruct.c index 3f1b2f8b8..a238d3935 100644 --- a/py/modstruct.c +++ b/py/modstruct.c @@ -97,7 +97,10 @@ STATIC size_t calc_size_items(const char *fmt, size_t *total_sz) { total_cnt += 1; size += cnt; } else { - total_cnt += cnt; + // Pad bytes are skipped and don't get included in the item count. + if (*fmt != 'x') { + total_cnt += cnt; + } mp_uint_t align; size_t sz = mp_binary_get_size(fmt_type, *fmt, &align); while (cnt--) { @@ -166,7 +169,10 @@ STATIC mp_obj_t struct_unpack_from(size_t n_args, const mp_obj_t *args) { } else { while (cnt--) { item = mp_binary_get_val(fmt_type, *fmt, &p); - res->items[i++] = item; + // Pad bytes ('x') are just skipped. + if (*fmt != 'x') { + res->items[i++] = item; + } } } fmt++; @@ -204,7 +210,11 @@ STATIC void struct_pack_into_internal(mp_obj_t fmt_in, byte *p, size_t n_args, c } else { // If we run out of args then we just finish; CPython would raise struct.error while (cnt-- && i < n_args) { - mp_binary_set_val(fmt_type, *fmt, args[i++], &p); + mp_binary_set_val(fmt_type, *fmt, args[i], &p); + // Pad bytes don't have a corresponding argument. + if (*fmt != 'x') { + i++; + } } } fmt++; diff --git a/py/moduerrno.c b/py/moduerrno.c index c1feafaa1..3be5adba1 100644 --- a/py/moduerrno.c +++ b/py/moduerrno.c @@ -84,7 +84,11 @@ STATIC const mp_obj_dict_t errorcode_dict = { #endif STATIC const mp_rom_map_elem_t mp_module_uerrno_globals_table[] = { +#if CIRCUITPY + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_errno) }, +#else { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uerrno) }, +#endif #if MICROPY_PY_UERRNO_ERRORCODE { MP_ROM_QSTR(MP_QSTR_errorcode), MP_ROM_PTR(&errorcode_dict) }, #endif @@ -151,9 +155,10 @@ const char *mp_common_errno_to_str(mp_obj_t errno_val, char *buf, size_t len) { case EEXIST: desc = translate("File exists"); break; case ENODEV: desc = translate("Unsupported operation"); break; case EINVAL: desc = translate("Invalid argument"); break; + case ENOSPC: desc = translate("No space left on device"); break; case EROFS: desc = translate("Read-only filesystem"); break; } - if (desc != NULL && desc->length <= len) { + if (desc != NULL && decompress_length(desc) <= len) { decompress(desc, buf); return buf; } diff --git a/py/mpconfig.h b/py/mpconfig.h index a846f6a25..513f04f6e 100755 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -45,6 +45,11 @@ #include <mpconfigport.h> #endif +// Is this a CircuitPython build? +#ifndef CIRCUITPY +#define CIRCUITPY 0 +#endif + // Any options not explicitly set in mpconfigport.h will get default // values below. @@ -239,6 +244,14 @@ #define alloca(x) m_malloc(x) #endif +// Number of atb indices to cache. Allocations of fewer blocks will be faster +// because the search will be accelerated by the index cache. This only applies +// to short lived allocations because we assume the long lived allocations are +// contiguous. +#ifndef MICROPY_ATB_INDICES +#define MICROPY_ATB_INDICES (8) +#endif + /*****************************************************************************/ /* MicroPython emitters */ @@ -364,6 +377,11 @@ #define MICROPY_COMP_RETURN_IF_EXPR (0) #endif +// Whether to include parsing of f-string literals +#ifndef MICROPY_COMP_FSTRING_LITERAL +#define MICROPY_COMP_FSTRING_LITERAL (1) +#endif + /*****************************************************************************/ /* Internal debugging stuff */ @@ -1160,10 +1178,26 @@ typedef double mp_float_t; #define MICROPY_PY_UJSON (0) #endif +#ifndef CIRCUITPY_ULAB +#define CIRCUITPY_ULAB (0) +#endif + #ifndef MICROPY_PY_URE #define MICROPY_PY_URE (0) #endif +#ifndef MICROPY_PY_URE_MATCH_GROUPS +#define MICROPY_PY_URE_MATCH_GROUPS (0) +#endif + +#ifndef MICROPY_PY_URE_MATCH_SPAN_START_END +#define MICROPY_PY_URE_MATCH_SPAN_START_END (0) +#endif + +#ifndef MICROPY_PY_URE_SUB +#define MICROPY_PY_URE_SUB (0) +#endif + #ifndef MICROPY_PY_UHEAPQ #define MICROPY_PY_UHEAPQ (0) #endif @@ -1423,6 +1457,15 @@ typedef double mp_float_t; #define MP_UNLIKELY(x) __builtin_expect((x), 0) #endif +// To annotate that code is unreachable +#ifndef MP_UNREACHABLE +#if defined(__GNUC__) +#define MP_UNREACHABLE __builtin_unreachable(); +#else +#define MP_UNREACHABLE for (;;); +#endif +#endif + #ifndef MP_HTOBE16 #if MP_ENDIANNESS_LITTLE # define MP_HTOBE16(x) ((uint16_t)( (((x) & 0xff) << 8) | (((x) >> 8) & 0xff) )) diff --git a/py/mpstate.c b/py/mpstate.c index 6ce64adfd..32f1d60a5 100644 --- a/py/mpstate.c +++ b/py/mpstate.c @@ -25,9 +25,10 @@ */ #include "py/mpstate.h" +#include "supervisor/linker.h" #if MICROPY_DYNAMIC_COMPILER mp_dynamic_compiler_t mp_dynamic_compiler = {0}; #endif -mp_state_ctx_t mp_state_ctx; +mp_state_ctx_t PLACE_IN_DTCM_BSS(mp_state_ctx); diff --git a/py/mpstate.h b/py/mpstate.h index eef8696d3..a5815776a 100644 --- a/py/mpstate.h +++ b/py/mpstate.h @@ -92,7 +92,7 @@ typedef struct _mp_state_mem_t { size_t gc_alloc_threshold; #endif - size_t gc_first_free_atb_index; + size_t gc_first_free_atb_index[MICROPY_ATB_INDICES]; size_t gc_last_free_atb_index; #if MICROPY_PY_GC_COLLECT_RETVAL @@ -103,6 +103,8 @@ typedef struct _mp_state_mem_t { // This is a global mutex used to make the GC thread-safe. mp_thread_mutex_t gc_mutex; #endif + + void** permanent_pointers; } mp_state_mem_t; // This structure hold runtime and VM information. It includes a section @@ -36,33 +36,45 @@ // If MICROPY_NLR_SETJMP is not enabled then auto-detect the machine arch #if !defined(MICROPY_NLR_SETJMP) || !MICROPY_NLR_SETJMP -#define MICROPY_NLR_SETJMP (0) // A lot of nlr-related things need different treatment on Windows -#if defined(_WIN32) || defined(__CYGWIN__) -#define MICROPY_NLR_OS_WINDOWS 1 -#else -#define MICROPY_NLR_OS_WINDOWS 0 -#endif -#if defined(__i386__) - #define MICROPY_NLR_X86 (1) - #define MICROPY_NLR_NUM_REGS (6) -#elif defined(__x86_64__) - #define MICROPY_NLR_X64 (1) - #if MICROPY_NLR_OS_WINDOWS - #define MICROPY_NLR_NUM_REGS (10) + #if defined(_WIN32) || defined(__CYGWIN__) + #define MICROPY_NLR_OS_WINDOWS 1 #else - #define MICROPY_NLR_NUM_REGS (8) + #define MICROPY_NLR_OS_WINDOWS 0 #endif -#elif defined(__thumb2__) || defined(__thumb__) || defined(__arm__) - #define MICROPY_NLR_THUMB (1) - #define MICROPY_NLR_NUM_REGS (10) -#elif defined(__xtensa__) - #define MICROPY_NLR_XTENSA (1) - #define MICROPY_NLR_NUM_REGS (10) -#else - #define MICROPY_NLR_SETJMP (1) + #if defined(__i386__) + #define MICROPY_NLR_X86 (1) + #define MICROPY_NLR_NUM_REGS (6) + #elif defined(__x86_64__) + #define MICROPY_NLR_X64 (1) + #if MICROPY_NLR_OS_WINDOWS + #define MICROPY_NLR_NUM_REGS (10) + #else + #define MICROPY_NLR_NUM_REGS (8) + #endif + #elif defined(__thumb2__) || defined(__thumb__) || defined(__arm__) + #define MICROPY_NLR_THUMB (1) + #if defined(__SOFTFP__) + #define MICROPY_NLR_NUM_REGS (10) + #else + // With hardware FP registers s16-s31 are callee save so in principle + // should be saved and restored by the NLR code. gcc only uses s16-s21 + // so only save/restore those as an optimisation. + #define MICROPY_NLR_NUM_REGS (10 + 6) + #endif + #elif defined(__xtensa__) + #define MICROPY_NLR_XTENSA (1) + #define MICROPY_NLR_NUM_REGS (10) + #else + #define MICROPY_NLR_SETJMP (1) //#warning "No native NLR support for this arch, using setjmp implementation" + #endif #endif + +// If MICROPY_NLR_SETJMP is not defined above - define/disable it here + +#if !defined(MICROPY_NLR_SETJMP) + #define MICROPY_NLR_SETJMP (0) #endif #if MICROPY_NLR_SETJMP diff --git a/py/nlrthumb.c b/py/nlrthumb.c index 69d3f868a..056aa358e 100644 --- a/py/nlrthumb.c +++ b/py/nlrthumb.c @@ -63,6 +63,11 @@ __attribute__((naked)) unsigned int nlr_push(nlr_buf_t *nlr) { "str r10, [r0, #36] \n" // store r10 into nlr_buf "str r11, [r0, #40] \n" // store r11 into nlr_buf "str r13, [r0, #44] \n" // store r13=sp into nlr_buf + #if MICROPY_NLR_NUM_REGS == 16 + "vstr d8, [r0, #48] \n" // store s16-s17 into nlr_buf + "vstr d9, [r0, #56] \n" // store s18-s19 into nlr_buf + "vstr d10, [r0, #64] \n" // store s20-s21 into nlr_buf + #endif "str lr, [r0, #8] \n" // store lr into nlr_buf #endif @@ -118,6 +123,11 @@ NORETURN void nlr_jump(void *val) { "ldr r10, [r0, #36] \n" // load r10 from nlr_buf "ldr r11, [r0, #40] \n" // load r11 from nlr_buf "ldr r13, [r0, #44] \n" // load r13=sp from nlr_buf + #if MICROPY_NLR_NUM_REGS == 16 + "vldr d8, [r0, #48] \n" // load s16-s17 from nlr_buf + "vldr d9, [r0, #56] \n" // load s18-s19 from nlr_buf + "vldr d10, [r0, #64] \n" // load s20-s21 from nlr_buf + #endif "ldr lr, [r0, #8] \n" // load lr from nlr_buf #endif "movs r0, #1 \n" // return 1, non-local return @@ -127,11 +137,7 @@ NORETURN void nlr_jump(void *val) { : // clobbered registers ); - #if defined(__GNUC__) - __builtin_unreachable(); - #else - for (;;); // needed to silence compiler warning - #endif + MP_UNREACHABLE } #endif // MICROPY_NLR_THUMB diff --git a/py/nlrx64.c b/py/nlrx64.c index 9b2b22c22..569ad84fb 100644 --- a/py/nlrx64.c +++ b/py/nlrx64.c @@ -108,7 +108,7 @@ NORETURN void nlr_jump(void *val) { : // clobbered registers ); - for (;;); // needed to silence compiler warning + MP_UNREACHABLE } #endif // MICROPY_NLR_X64 diff --git a/py/nlrx86.c b/py/nlrx86.c index 4900a4c0d..6fbbe4432 100644 --- a/py/nlrx86.c +++ b/py/nlrx86.c @@ -100,7 +100,7 @@ NORETURN void nlr_jump(void *val) { : // clobbered registers ); - for (;;); // needed to silence compiler warning + MP_UNREACHABLE } #endif // MICROPY_NLR_X86 diff --git a/py/nlrxtensa.c b/py/nlrxtensa.c index d66c7a9a7..564035004 100644 --- a/py/nlrxtensa.c +++ b/py/nlrxtensa.c @@ -77,7 +77,7 @@ NORETURN void nlr_jump(void *val) { : // clobbered registers ); - for (;;); // needed to silence compiler warning + MP_UNREACHABLE } #endif // MICROPY_NLR_XTENSA @@ -33,10 +33,13 @@ #include "py/objtype.h" #include "py/objint.h" #include "py/objstr.h" +#include "py/qstr.h" #include "py/runtime.h" #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.h" mp_obj_type_t *mp_obj_get_type(mp_const_obj_t o_in) { @@ -61,6 +64,9 @@ const char *mp_obj_get_type_str(mp_const_obj_t o_in) { void mp_obj_print_helper(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { // There can be data structures nested too deep, or just recursive MP_STACK_CHECK(); + #ifdef RUN_BACKGROUND_TASKS + RUN_BACKGROUND_TASKS; + #endif #ifndef NDEBUG if (o_in == MP_OBJ_NULL) { mp_print_str(print, "(nil)"); @@ -81,24 +87,24 @@ void mp_obj_print(mp_obj_t o_in, mp_print_kind_t kind) { // helper function to print an exception with traceback void mp_obj_print_exception(const mp_print_t *print, mp_obj_t exc) { - if (mp_obj_is_exception_instance(exc)) { + if (mp_obj_is_exception_instance(exc) && stack_ok()) { size_t n, *values; mp_obj_exception_get_traceback(exc, &n, &values); if (n > 0) { assert(n % 3 == 0); // Decompress the format strings const compressed_string_t* traceback = translate("Traceback (most recent call last):\n"); - char decompressed[traceback->length]; + char decompressed[decompress_length(traceback)]; decompress(traceback, decompressed); #if MICROPY_ENABLE_SOURCE_LINE const compressed_string_t* frame = translate(" File \"%q\", line %d"); #else const compressed_string_t* frame = translate(" File \"%q\""); #endif - char decompressed_frame[frame->length]; + char decompressed_frame[decompress_length(frame)]; decompress(frame, decompressed_frame); const compressed_string_t* block_fmt = translate(", in %q\n"); - char decompressed_block[block_fmt->length]; + char decompressed_block[decompress_length(block_fmt)]; decompress(block_fmt, decompressed_block); // Print the traceback @@ -123,7 +129,7 @@ void mp_obj_print_exception(const mp_print_t *print, mp_obj_t exc) { mp_print_str(print, "\n"); } -bool mp_obj_is_true(mp_obj_t arg) { +bool PLACE_IN_ITCM(mp_obj_is_true)(mp_obj_t arg) { if (arg == mp_const_false) { return 0; } else if (arg == mp_const_true) { @@ -484,12 +490,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) { mp_obj_type_t *type = mp_obj_get_type(base); + if (type->subscr != NULL) { mp_obj_t ret = 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) { @@ -527,6 +535,36 @@ mp_obj_t mp_identity_getiter(mp_obj_t self, mp_obj_iter_buf_t *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); + mp_obj_type_t *type = mp_obj_get_type(self->obj); + mp_obj_t current_length = 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->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); +} + bool mp_get_buffer(mp_obj_t obj, mp_buffer_info_t *bufinfo, mp_uint_t flags) { mp_obj_type_t *type = mp_obj_get_type(obj); if (type->buffer_p.get_buffer == NULL) { @@ -338,6 +338,13 @@ 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} +// Declare a module as a builtin, processed by makemoduledefs.py +// param module_name: MP_QSTR_<module name> +// param obj_module: mp_obj_module_t instance +// prarm enabled_define: used as `#if (enabled_define) around entry` + +#define MP_REGISTER_MODULE(module_name, obj_module, enabled_define) + // Underlying map/hash table implementation (not dict object or map function) typedef struct _mp_map_elem_t { @@ -432,7 +439,7 @@ typedef struct _mp_obj_iter_buf_t { #define MP_OBJ_ITER_BUF_NSLOTS ((sizeof(mp_obj_iter_buf_t) + sizeof(mp_obj_t) - 1) / sizeof(mp_obj_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_make_new_fun_t)(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *args, mp_map_t *kw_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); typedef mp_obj_t (*mp_unary_op_fun_t)(mp_unary_op_t op, mp_obj_t); typedef mp_obj_t (*mp_binary_op_fun_t)(mp_binary_op_t op, mp_obj_t, mp_obj_t); @@ -592,6 +599,7 @@ 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_MpyError; 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; @@ -639,7 +647,9 @@ 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_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_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); @@ -659,6 +669,7 @@ 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); @@ -670,7 +681,7 @@ mp_obj_t mp_obj_new_memoryview(byte typecode, size_t nitems, void *items); mp_obj_type_t *mp_obj_get_type(mp_const_obj_t o_in); const char *mp_obj_get_type_str(mp_const_obj_t o_in); 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_instance_cast_to_native_base(mp_const_obj_t self_in, mp_const_obj_t native_type); +mp_obj_t mp_instance_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); @@ -719,7 +730,7 @@ void mp_obj_exception_add_traceback(mp_obj_t self_in, qstr file, size_t line, qs void mp_obj_exception_get_traceback(mp_obj_t self_in, size_t *n, size_t **values); mp_obj_t mp_obj_exception_get_traceback_obj(mp_obj_t self_in); 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_obj_exception_make_new(const mp_obj_type_t *type_in, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args); mp_obj_t mp_alloc_emergency_exception_buf(mp_obj_t size_in); void mp_init_emergency_exception_buf(void); @@ -754,6 +765,7 @@ void mp_obj_tuple_del(mp_obj_t self_in); mp_int_t mp_obj_tuple_hash(mp_obj_t self_in); // list +mp_obj_t mp_obj_list_clear(mp_obj_t self_in); mp_obj_t mp_obj_list_append(mp_obj_t self_in, mp_obj_t arg); mp_obj_t mp_obj_list_remove(mp_obj_t self_in, mp_obj_t value); void mp_obj_list_get(mp_obj_t self_in, size_t *len, mp_obj_t **items); @@ -810,6 +822,10 @@ mp_obj_t mp_identity(mp_obj_t self); MP_DECLARE_CONST_FUN_OBJ_1(mp_identity_obj); mp_obj_t mp_identity_getiter(mp_obj_t self, mp_obj_iter_buf_t *iter_buf); +// Generic iterator that uses unary op and subscr to iterate over a native type. It will be slower +// than a custom iterator but applies broadly. +mp_obj_t mp_obj_new_generic_iterator(mp_obj_t self, mp_obj_iter_buf_t *iter_buf); + // module typedef struct _mp_obj_module_t { mp_obj_base_t base; @@ -842,7 +858,10 @@ typedef struct { mp_uint_t stop; mp_int_t step; } mp_bound_slice_t; +void mp_obj_slice_indices(mp_obj_t self_in, mp_int_t length, mp_bound_slice_t *result); +// Compute the new length of a sequence and ensure an exception is thrown on overflow. +size_t mp_seq_multiply_len(size_t item_sz, size_t len); void mp_seq_multiply(const void *items, size_t item_sz, size_t len, size_t times, void *dest); #if MICROPY_PY_BUILTINS_SLICE bool mp_seq_get_fast_slice_indexes(mp_uint_t len, mp_obj_t slice, mp_bound_slice_t *indexes); diff --git a/py/objarray.c b/py/objarray.c index 69ff6f328..fccb966a2 100644 --- a/py/objarray.c +++ b/py/objarray.c @@ -63,6 +63,10 @@ STATIC mp_obj_t array_iterator_new(mp_obj_t array_in, mp_obj_iter_buf_t *iter_bu STATIC mp_obj_t array_append(mp_obj_t self_in, mp_obj_t arg); STATIC mp_obj_t array_extend(mp_obj_t self_in, mp_obj_t arg_in); STATIC mp_int_t array_get_buffer(mp_obj_t o_in, mp_buffer_info_t *bufinfo, mp_uint_t flags); +#if MICROPY_CPYTHON_COMPAT +STATIC mp_obj_t array_decode(size_t n_args, const mp_obj_t *args); +#endif + /******************************************************************************/ // array @@ -157,9 +161,9 @@ STATIC mp_obj_t array_construct(char typecode, mp_obj_t initializer) { #endif #if MICROPY_PY_ARRAY -STATIC mp_obj_t array_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { +STATIC mp_obj_t array_make_new(const mp_obj_type_t *type_in, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { (void)type_in; - mp_arg_check_num(n_args, n_kw, 1, 2, false); + mp_arg_check_num(n_args, kw_args, 1, 2, false); // get typecode const char *typecode = mp_obj_str_get_str(args[0]); @@ -175,9 +179,9 @@ STATIC mp_obj_t array_make_new(const mp_obj_type_t *type_in, size_t n_args, size #endif #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) { +STATIC mp_obj_t bytearray_make_new(const mp_obj_type_t *type_in, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { (void)type_in; - mp_arg_check_num(n_args, n_kw, 0, 1, false); + mp_arg_check_num(n_args, kw_args, 0, 1, false); if (n_args == 0) { // no args: construct an empty bytearray @@ -207,13 +211,13 @@ mp_obj_t mp_obj_new_memoryview(byte typecode, size_t nitems, void *items) { return MP_OBJ_FROM_PTR(self); } -STATIC mp_obj_t memoryview_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { +STATIC mp_obj_t memoryview_make_new(const mp_obj_type_t *type_in, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { (void)type_in; // TODO possibly allow memoryview constructor to take start/stop so that one // can do memoryview(b, 4, 8) instead of memoryview(b)[4:8] (uses less RAM) - mp_arg_check_num(n_args, n_kw, 1, 1, false); + mp_arg_check_num(n_args, kw_args, 1, 1, false); mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ); @@ -546,7 +550,24 @@ STATIC mp_int_t array_get_buffer(mp_obj_t o_in, mp_buffer_info_t *bufinfo, mp_ui return 0; } -#if MICROPY_PY_BUILTINS_BYTEARRAY || MICROPY_PY_ARRAY + +#if MICROPY_CPYTHON_COMPAT && MICROPY_PY_BUILTINS_BYTEARRAY +// Directly lifted from objstr.c +STATIC mp_obj_t array_decode(size_t n_args, const mp_obj_t *args) { + mp_obj_t new_args[2]; + if (n_args == 1) { + new_args[0] = args[0]; + new_args[1] = MP_OBJ_NEW_QSTR(MP_QSTR_utf_hyphen_8); + args = new_args; + n_args++; + } + return mp_obj_str_make_new(&mp_type_str, n_args, args, NULL); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(array_decode_obj, 1, 3, array_decode); +#endif + + +#if MICROPY_PY_ARRAY STATIC const mp_rom_map_elem_t array_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_append), MP_ROM_PTR(&array_append_obj) }, { MP_ROM_QSTR(MP_QSTR_extend), MP_ROM_PTR(&array_extend_obj) }, @@ -555,6 +576,19 @@ STATIC const mp_rom_map_elem_t array_locals_dict_table[] = { STATIC MP_DEFINE_CONST_DICT(array_locals_dict, array_locals_dict_table); #endif +#if MICROPY_PY_BUILTINS_BYTEARRAY +STATIC const mp_rom_map_elem_t bytearray_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_append), MP_ROM_PTR(&array_append_obj) }, + { MP_ROM_QSTR(MP_QSTR_extend), MP_ROM_PTR(&array_extend_obj) }, +#if MICROPY_CPYTHON_COMPAT + { MP_ROM_QSTR(MP_QSTR_decode), MP_ROM_PTR(&array_decode_obj) }, +#endif +}; + +STATIC MP_DEFINE_CONST_DICT(bytearray_locals_dict, bytearray_locals_dict_table); +#endif + + #if MICROPY_PY_ARRAY const mp_obj_type_t mp_type_array = { { &mp_type_type }, @@ -581,7 +615,7 @@ const mp_obj_type_t mp_type_bytearray = { .binary_op = array_binary_op, .subscr = array_subscr, .buffer_p = { .get_buffer = array_get_buffer }, - .locals_dict = (mp_obj_dict_t*)&array_locals_dict, + .locals_dict = (mp_obj_dict_t*)&bytearray_locals_dict, }; #endif @@ -611,6 +645,12 @@ mp_obj_t mp_obj_new_bytearray(size_t n, void *items) { return MP_OBJ_FROM_PTR(o); } +mp_obj_t mp_obj_new_bytearray_of_zeros(size_t n) { + mp_obj_array_t *o = array_new(BYTEARRAY_TYPECODE, n); + memset(o->items, 0, n); + return MP_OBJ_FROM_PTR(o); +} + // Create bytearray which references specified memory area mp_obj_t mp_obj_new_bytearray_by_ref(size_t n, void *items) { mp_obj_array_t *o = m_new_obj(mp_obj_array_t); diff --git a/py/objbool.c b/py/objbool.c index 5755b188e..cd7d7100c 100644 --- a/py/objbool.c +++ b/py/objbool.c @@ -50,9 +50,9 @@ STATIC void bool_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_ } } -STATIC mp_obj_t bool_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { +STATIC mp_obj_t bool_make_new(const mp_obj_type_t *type_in, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { (void)type_in; - mp_arg_check_num(n_args, n_kw, 0, 1, false); + mp_arg_check_num(n_args, kw_args, 0, 1, false); if (n_args == 0) { return mp_const_false; diff --git a/py/objcomplex.c b/py/objcomplex.c index 3336d7b05..b38e2c5fa 100644 --- a/py/objcomplex.c +++ b/py/objcomplex.c @@ -75,9 +75,9 @@ STATIC void complex_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_ } } -STATIC mp_obj_t complex_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { +STATIC mp_obj_t complex_make_new(const mp_obj_type_t *type_in, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { (void)type_in; - mp_arg_check_num(n_args, n_kw, 0, 2, false); + mp_arg_check_num(n_args, kw_args, 0, 2, false); switch (n_args) { case 0: diff --git a/py/objdeque.c b/py/objdeque.c index dd0141831..b2785b5b6 100644 --- a/py/objdeque.c +++ b/py/objdeque.c @@ -44,8 +44,8 @@ typedef struct _mp_obj_deque_t { #define FLAG_CHECK_OVERFLOW 1 } mp_obj_deque_t; -STATIC mp_obj_t deque_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { - mp_arg_check_num(n_args, n_kw, 2, 3, false); +STATIC mp_obj_t deque_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + mp_arg_check_num(n_args, kw_args, 2, 3, false); /* Initialization from existing sequence is not supported, so an empty tuple must be passed as such. */ diff --git a/py/objdict.c b/py/objdict.c index cd110f1c1..3ec3cbe80 100644 --- a/py/objdict.c +++ b/py/objdict.c @@ -31,6 +31,7 @@ #include "py/builtin.h" #include "py/objtype.h" +#include "supervisor/linker.h" #include "supervisor/shared/translate.h" #define MP_OBJ_IS_DICT_TYPE(o) (MP_OBJ_IS_OBJ(o) && ((mp_obj_base_t*)MP_OBJ_TO_PTR(o))->type->make_new == dict_make_new) @@ -81,7 +82,7 @@ STATIC void dict_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_ } } -STATIC mp_obj_t dict_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 dict_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { 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; @@ -90,11 +91,12 @@ STATIC mp_obj_t dict_make_new(const mp_obj_type_t *type, size_t n_args, size_t n 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; - mp_map_init_fixed_table(&kwargs, n_kw, args + n_args); - dict_update(n_args + 1, args2, &kwargs); // dict_update will check that n_args + 1 == 1 or 2 + if (n_args > 0 || kw_args != NULL) { + mp_obj_t args2[2] = {dict_out, NULL}; // args[0] is always valid, even if it's not a positional arg + if (n_args > 0) { + args2[1] = args[0]; + } + dict_update(n_args + 1, args2, kw_args); // dict_update will check that n_args + 1 == 1 or 2 } return dict_out; } @@ -323,12 +325,12 @@ STATIC mp_obj_t dict_popitem(mp_obj_t self_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(dict_popitem_obj, dict_popitem); -STATIC mp_obj_t dict_update(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { +STATIC mp_obj_t PLACE_IN_ITCM(dict_update)(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { mp_check_self(MP_OBJ_IS_DICT_TYPE(args[0])); mp_obj_dict_t *self = MP_OBJ_TO_PTR(args[0]); mp_ensure_not_fixed(self); - mp_arg_check_num(n_args, kwargs->used, 1, 2, true); + mp_arg_check_num(n_args, kwargs, 1, 2, true); if (n_args == 2) { // given a positional argument @@ -589,7 +591,7 @@ size_t mp_obj_dict_len(mp_obj_t self_in) { return self->map.used; } -mp_obj_t mp_obj_dict_store(mp_obj_t self_in, mp_obj_t key, mp_obj_t value) { +mp_obj_t PLACE_IN_ITCM(mp_obj_dict_store)(mp_obj_t self_in, mp_obj_t key, mp_obj_t value) { mp_check_self(MP_OBJ_IS_DICT_TYPE(self_in)); mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_in); mp_ensure_not_fixed(self); diff --git a/py/objenumerate.c b/py/objenumerate.c index 1a9d30f83..818725d85 100644 --- a/py/objenumerate.c +++ b/py/objenumerate.c @@ -39,7 +39,7 @@ typedef struct _mp_obj_enumerate_t { STATIC mp_obj_t enumerate_iternext(mp_obj_t self_in); -STATIC mp_obj_t enumerate_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 enumerate_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { #if MICROPY_CPYTHON_COMPAT static const mp_arg_t allowed_args[] = { { MP_QSTR_iterable, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, @@ -50,7 +50,7 @@ STATIC mp_obj_t enumerate_make_new(const mp_obj_type_t *type, size_t n_args, siz struct { mp_arg_val_t iterable, start; } arg_vals; - mp_arg_parse_all_kw_array(n_args, n_kw, args, + mp_arg_parse_all(n_args, args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, (mp_arg_val_t*)&arg_vals); // create enumerate object @@ -59,7 +59,7 @@ STATIC mp_obj_t enumerate_make_new(const mp_obj_type_t *type, size_t n_args, siz o->iter = mp_getiter(arg_vals.iterable.u_obj, NULL); o->cur = arg_vals.start.u_int; #else - (void)n_kw; + (void)kw_args; mp_obj_enumerate_t *o = m_new_obj(mp_obj_enumerate_t); o->base.type = type; o->iter = mp_getiter(args[0], NULL); diff --git a/py/objexcept.c b/py/objexcept.c index c54e5fd4a..796be122f 100644 --- a/py/objexcept.c +++ b/py/objexcept.c @@ -112,17 +112,23 @@ void mp_obj_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kin if (o->args == NULL || o->args->len == 0) { mp_print_str(print, ""); return; - } else if (o->args->len == 1) { + } + if (MP_OBJ_IS_SMALL_INT(o->args->items[0]) && + mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(o->base.type), MP_OBJ_FROM_PTR(&mp_type_OSError)) && + o->args->len <= 2) { // try to provide a nice OSError error message - if (MP_OBJ_IS_SMALL_INT(o->args->items[0]) && - mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(o->base.type), MP_OBJ_FROM_PTR(&mp_type_OSError))) { - char decompressed[50]; - const char *msg = mp_common_errno_to_str(o->args->items[0], decompressed, sizeof(decompressed)); - if (msg != NULL) { - mp_printf(print, "[Errno " INT_FMT "] %s", MP_OBJ_SMALL_INT_VALUE(o->args->items[0]), msg); - return; + char decompressed[50]; + const char *msg = mp_common_errno_to_str(o->args->items[0], decompressed, sizeof(decompressed)); + if (msg != NULL) { + mp_printf(print, "[Errno " INT_FMT "] %s", MP_OBJ_SMALL_INT_VALUE(o->args->items[0]), msg); + // if second arg exists, it is filename. + if (o->args->len == 2) { + mp_printf(print, ": '%s'", mp_obj_str_get_str(o->args->items[1])); } + return; } + } + if (o->args->len == 1) { mp_obj_print_helper(print, o->args->items[0], PRINT_STR); return; } @@ -130,8 +136,8 @@ 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); } -mp_obj_t mp_obj_exception_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { - mp_arg_check_num(n_args, n_kw, 0, MP_OBJ_FUN_ARGS_MAX, false); +mp_obj_t mp_obj_exception_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + mp_arg_check_num(n_args, kw_args, 0, MP_OBJ_FUN_ARGS_MAX, false); // Try to allocate memory for the exception, with fallback to emergency exception object mp_obj_exception_t *o_exc = m_new_obj_maybe(mp_obj_exception_t); @@ -311,6 +317,7 @@ MP_DEFINE_EXCEPTION(Exception, BaseException) MP_DEFINE_EXCEPTION(UnicodeError, ValueError) //TODO: Implement more UnicodeError subclasses which take arguments #endif + MP_DEFINE_EXCEPTION(MpyError, ValueError) /* MP_DEFINE_EXCEPTION(Warning, Exception) MP_DEFINE_EXCEPTION(DeprecationWarning, Warning) @@ -336,7 +343,7 @@ mp_obj_t mp_obj_new_exception_arg1(const mp_obj_type_t *exc_type, mp_obj_t arg) 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); - return exc_type->make_new(exc_type, n_args, 0, args); + return exc_type->make_new(exc_type, n_args, args, NULL); } mp_obj_t mp_obj_new_exception_msg(const mp_obj_type_t *exc_type, const compressed_string_t *msg) { @@ -393,7 +400,7 @@ mp_obj_t mp_obj_new_exception_msg_vlist(const mp_obj_type_t *exc_type, const com // Try to allocate memory for the message mp_obj_str_t *o_str = m_new_obj_maybe(mp_obj_str_t); - size_t o_str_alloc = fmt->length + 1; + size_t o_str_alloc = decompress_length(fmt); byte *o_str_buf = m_new_maybe(byte, o_str_alloc); bool used_emg_buf = false; @@ -426,7 +433,7 @@ mp_obj_t mp_obj_new_exception_msg_vlist(const mp_obj_type_t *exc_type, const com // We have some memory to format the string struct _exc_printer_t exc_pr = {!used_emg_buf, o_str_alloc, 0, o_str_buf}; mp_print_t print = {&exc_pr, exc_add_strn}; - char fmt_decompressed[fmt->length]; + char fmt_decompressed[decompress_length(fmt)]; decompress(fmt, fmt_decompressed); mp_vprintf(&print, fmt_decompressed, ap); exc_pr.buf[exc_pr.len] = '\0'; @@ -438,7 +445,7 @@ mp_obj_t mp_obj_new_exception_msg_vlist(const mp_obj_type_t *exc_type, const com o_str->base.type = &mp_type_str; o_str->hash = qstr_compute_hash(o_str->data, o_str->len); mp_obj_t arg = MP_OBJ_FROM_PTR(o_str); - return mp_obj_exception_make_new(exc_type, 1, 0, &arg); + return mp_obj_exception_make_new(exc_type, 1, &arg, NULL); } // return true if the given object is an exception type @@ -607,7 +614,7 @@ STATIC mp_obj_t code_make_new(qstr file, qstr block) { mp_obj_new_bytearray(0, NULL), // co_lnotab }; - return namedtuple_make_new((const mp_obj_type_t*)&code_type_obj, 15, 0, elems); + return namedtuple_make_new((const mp_obj_type_t*)&code_type_obj, 15, elems, NULL); } STATIC const mp_obj_namedtuple_type_t frame_type_obj = { @@ -650,7 +657,7 @@ STATIC mp_obj_t frame_make_new(mp_obj_t f_code, int f_lineno) { mp_const_none, // f_trace }; - return namedtuple_make_new((const mp_obj_type_t*)&frame_type_obj, 8, 0, elems); + return namedtuple_make_new((const mp_obj_type_t*)&frame_type_obj, 8, elems, NULL); } STATIC const mp_obj_namedtuple_type_t traceback_type_obj = { @@ -687,7 +694,7 @@ STATIC mp_obj_t traceback_from_values(size_t *values, mp_obj_t tb_next) { tb_next, }; - return namedtuple_make_new((const mp_obj_type_t*)&traceback_type_obj, 4, 0, elems); + return namedtuple_make_new((const mp_obj_type_t*)&traceback_type_obj, 4, elems, NULL); }; mp_obj_t mp_obj_exception_get_traceback_obj(mp_obj_t self_in) { diff --git a/py/objfilter.c b/py/objfilter.c index cb965d8c3..af95326e6 100644 --- a/py/objfilter.c +++ b/py/objfilter.c @@ -34,8 +34,8 @@ typedef struct _mp_obj_filter_t { mp_obj_t iter; } mp_obj_filter_t; -STATIC mp_obj_t filter_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { - mp_arg_check_num(n_args, n_kw, 2, 2, false); +STATIC mp_obj_t filter_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + mp_arg_check_num(n_args, kw_args, 2, 2, false); mp_obj_filter_t *o = m_new_obj(mp_obj_filter_t); o->base.type = type; o->fun = args[0]; diff --git a/py/objfloat.c b/py/objfloat.c index c3f47018c..f544ade05 100644 --- a/py/objfloat.c +++ b/py/objfloat.c @@ -133,9 +133,9 @@ STATIC void float_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t } } -STATIC mp_obj_t float_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { +STATIC mp_obj_t float_make_new(const mp_obj_type_t *type_in, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { (void)type_in; - mp_arg_check_num(n_args, n_kw, 0, 1, false); + mp_arg_check_num(n_args, kw_args, 0, 1, false); switch (n_args) { case 0: diff --git a/py/objfun.c b/py/objfun.c index 8c51d92e0..c586a290a 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -34,6 +34,8 @@ #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 @@ -52,7 +54,7 @@ STATIC mp_obj_t fun_builtin_0_call(mp_obj_t self_in, size_t n_args, size_t n_kw, (void)args; assert(MP_OBJ_IS_TYPE(self_in, &mp_type_fun_builtin_0)); mp_obj_fun_builtin_fixed_t *self = MP_OBJ_TO_PTR(self_in); - mp_arg_check_num(n_args, n_kw, 0, 0, false); + mp_arg_check_num_kw_array(n_args, n_kw, 0, 0, false); return self->fun._0(); } @@ -66,7 +68,7 @@ const mp_obj_type_t mp_type_fun_builtin_0 = { STATIC mp_obj_t fun_builtin_1_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { assert(MP_OBJ_IS_TYPE(self_in, &mp_type_fun_builtin_1)); mp_obj_fun_builtin_fixed_t *self = MP_OBJ_TO_PTR(self_in); - mp_arg_check_num(n_args, n_kw, 1, 1, false); + mp_arg_check_num_kw_array(n_args, n_kw, 1, 1, false); return self->fun._1(args[0]); } @@ -80,7 +82,7 @@ const mp_obj_type_t mp_type_fun_builtin_1 = { 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); + mp_arg_check_num_kw_array(n_args, n_kw, 2, 2, false); return self->fun._2(args[0], args[1]); } @@ -94,7 +96,7 @@ const mp_obj_type_t mp_type_fun_builtin_2 = { STATIC mp_obj_t fun_builtin_3_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { assert(MP_OBJ_IS_TYPE(self_in, &mp_type_fun_builtin_3)); mp_obj_fun_builtin_fixed_t *self = MP_OBJ_TO_PTR(self_in); - mp_arg_check_num(n_args, n_kw, 3, 3, false); + mp_arg_check_num_kw_array(n_args, n_kw, 3, 3, false); return self->fun._3(args[0], args[1], args[2]); } @@ -110,7 +112,7 @@ STATIC mp_obj_t fun_builtin_var_call(mp_obj_t self_in, size_t n_args, size_t n_k mp_obj_fun_builtin_var_t *self = MP_OBJ_TO_PTR(self_in); // check number of arguments - mp_arg_check_num(n_args, n_kw, self->n_args_min, self->n_args_max, self->is_kw); + mp_arg_check_num_kw_array(n_args, n_kw, self->n_args_min, self->n_args_max, self->is_kw); if (self->is_kw) { // function allows keywords @@ -249,7 +251,7 @@ mp_code_state_t *mp_obj_fun_bc_prepare_codestate(mp_obj_t self_in, size_t n_args } #endif -STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { +STATIC mp_obj_t PLACE_IN_ITCM(fun_bc_call)(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { MP_STACK_CHECK(); DEBUG_printf("Input n_args: " UINT_FMT ", n_kw: " UINT_FMT "\n", n_args, n_kw); @@ -436,7 +438,7 @@ typedef mp_uint_t (*viper_fun_4_t)(mp_uint_t, mp_uint_t, mp_uint_t, mp_uint_t); STATIC mp_obj_t fun_viper_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_obj_fun_viper_t *self = self_in; - mp_arg_check_num(n_args, n_kw, self->n_args, self->n_args, false); + mp_arg_check_num_kw_array(n_args, n_kw, self->n_args, self->n_args, false); void *fun = MICROPY_MAKE_POINTER_CALLABLE(self->fun_data); @@ -546,7 +548,7 @@ STATIC mp_uint_t convert_obj_for_inline_asm(mp_obj_t obj) { STATIC mp_obj_t fun_asm_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_obj_fun_asm_t *self = self_in; - mp_arg_check_num(n_args, n_kw, self->n_args, self->n_args, false); + mp_arg_check_num_kw_array(n_args, n_kw, self->n_args, self->n_args, false); void *fun = MICROPY_MAKE_POINTER_CALLABLE(self->fun_data); diff --git a/py/objint.c b/py/objint.c index 2ac370d17..b78c9f25b 100644 --- a/py/objint.c +++ b/py/objint.c @@ -42,9 +42,9 @@ #endif // This dispatcher function is expected to be independent of the implementation of long int -STATIC mp_obj_t mp_obj_int_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { +STATIC mp_obj_t mp_obj_int_make_new(const mp_obj_type_t *type_in, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { (void)type_in; - mp_arg_check_num(n_args, n_kw, 0, 2, false); + mp_arg_check_num(n_args, kw_args, 0, 2, false); switch (n_args) { case 0: @@ -300,6 +300,76 @@ char *mp_obj_int_formatted(char **buf, size_t *buf_size, size_t *fmt_size, mp_co return b; } +#if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE + +void mp_obj_int_buffer_overflow_check(mp_obj_t self_in, size_t nbytes, bool is_signed) +{ + if (is_signed) { + // self must be < 2**(bits - 1) + mp_obj_t edge = mp_binary_op(MP_BINARY_OP_INPLACE_LSHIFT, + mp_obj_new_int(1), + mp_obj_new_int(nbytes * 8 - 1)); + + if (mp_binary_op(MP_BINARY_OP_LESS, self_in, edge) == mp_const_true) { + // and >= -2**(bits - 1) + edge = mp_unary_op(MP_UNARY_OP_NEGATIVE, edge); + if (mp_binary_op(MP_BINARY_OP_MORE_EQUAL, self_in, edge) == mp_const_true) { + return; + } + } + } else { + // self must be >= 0 + if (mp_obj_int_sign(self_in) >= 0) { + // and < 2**(bits) + mp_obj_t edge = mp_binary_op(MP_BINARY_OP_INPLACE_LSHIFT, + mp_obj_new_int(1), + mp_obj_new_int(nbytes * 8)); + + if (mp_binary_op(MP_BINARY_OP_LESS, self_in, edge) == mp_const_true) { + return; + } + } + } + + mp_raise_OverflowError_varg(translate("value must fit in %d byte(s)"), nbytes); +} + +#endif // MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE + +void mp_small_int_buffer_overflow_check(mp_int_t val, size_t nbytes, bool is_signed) { + // Fast path for zero. + if (val == 0) { + return; + } + + // Trying to store negative values in unsigned bytes falls through to failure. + if (is_signed || val >= 0) { + + if (nbytes >= sizeof(val)) { + // All non-negative N bit signed integers fit in an unsigned N bit integer. + // This case prevents shifting too far below. + return; + } + + if (is_signed) { + mp_int_t edge = ((mp_int_t)1 << (nbytes * 8 - 1)); + if (-edge <= val && val < edge) { + return; + } + // Out of range, fall through to failure. + } else { + // Unsigned. We already know val >= 0. + mp_int_t edge = ((mp_int_t)1 << (nbytes * 8)); + if (val < edge) { + return; + } + } + // Fall through to failure. + } + + mp_raise_OverflowError_varg(translate("value must fit in %d byte(s)"), nbytes); +} + #if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_NONE int mp_obj_int_sign(mp_obj_t self_in) { @@ -420,15 +490,24 @@ STATIC mp_obj_t int_from_bytes(size_t n_args, const mp_obj_t *args) { STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(int_from_bytes_fun_obj, 3, 4, int_from_bytes); STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(int_from_bytes_obj, MP_ROM_PTR(&int_from_bytes_fun_obj)); -STATIC mp_obj_t int_to_bytes(size_t n_args, const mp_obj_t *args) { - // TODO: Support signed param (assumes signed=False) - (void)n_args; - - mp_int_t len = mp_obj_get_int(args[1]); +STATIC mp_obj_t int_to_bytes(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_length, ARG_byteorder, ARG_signed }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_length, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_byteorder, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_signed, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + }; + 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_int_t len = args[ARG_length].u_int; if (len < 0) { mp_raise_ValueError(NULL); } - bool big_endian = args[2] != MP_OBJ_NEW_QSTR(MP_QSTR_little); + + mp_obj_t self = pos_args[0]; + bool big_endian = args[ARG_byteorder].u_obj != MP_OBJ_NEW_QSTR(MP_QSTR_little); + bool signed_ = args[ARG_signed].u_bool; vstr_t vstr; vstr_init_len(&vstr, len); @@ -436,19 +515,26 @@ STATIC mp_obj_t int_to_bytes(size_t n_args, const mp_obj_t *args) { memset(data, 0, len); #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE - if (!MP_OBJ_IS_SMALL_INT(args[0])) { - mp_obj_int_to_bytes_impl(args[0], big_endian, len, data); + if (!MP_OBJ_IS_SMALL_INT(self)) { + mp_obj_int_buffer_overflow_check(self, len, signed_); + mp_obj_int_to_bytes_impl(self, big_endian, len, data); } else #endif { - mp_int_t val = MP_OBJ_SMALL_INT_VALUE(args[0]); + mp_int_t val = MP_OBJ_SMALL_INT_VALUE(self); + // Small int checking is separate, to be fast. + mp_small_int_buffer_overflow_check(val, len, signed_); size_t l = MIN((size_t)len, sizeof(val)); + if (val < 0) { + // Sign extend negative numbers. + memset(data, -1, len); + } mp_binary_set_int(l, big_endian, data + (big_endian ? (len - l) : 0), val); } return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(int_to_bytes_obj, 3, 4, int_to_bytes); +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(int_to_bytes_obj, 3, int_to_bytes); STATIC const mp_rom_map_elem_t int_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_from_bytes), MP_ROM_PTR(&int_from_bytes_obj) }, diff --git a/py/objint.h b/py/objint.h index 4b95acde9..e8c9bc3e0 100644 --- a/py/objint.h +++ b/py/objint.h @@ -53,6 +53,12 @@ char *mp_obj_int_formatted(char **buf, size_t *buf_size, size_t *fmt_size, mp_co int base, const char *prefix, char base_char, char comma); 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); +#if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE +void mp_obj_int_buffer_overflow_check(mp_obj_t self_in, size_t nbytes, bool is_signed); +#endif + +void mp_small_int_buffer_overflow_check(mp_int_t val, size_t nbytes, bool is_signed); + mp_int_t mp_obj_int_hash(mp_obj_t self_in); 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); diff --git a/py/objlist.c b/py/objlist.c index d4f9b0222..608ea9f6c 100644 --- a/py/objlist.c +++ b/py/objlist.c @@ -68,9 +68,14 @@ STATIC mp_obj_t list_extend_from_iter(mp_obj_t list, mp_obj_t iterable) { return list; } -STATIC mp_obj_t 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_new_list_from_iter(mp_obj_t iterable) { + mp_obj_t list = mp_obj_new_list(0, NULL); + return list_extend_from_iter(list, iterable); +} + +STATIC mp_obj_t list_make_new(const mp_obj_type_t *type_in, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { (void)type_in; - mp_arg_check_num(n_args, n_kw, 0, 1, false); + mp_arg_check_num(n_args, kw_args, 0, 1, false); switch (n_args) { case 0: @@ -88,7 +93,7 @@ STATIC mp_obj_t list_make_new(const mp_obj_type_t *type_in, size_t n_args, size_ } STATIC mp_obj_t list_unary_op(mp_unary_op_t op, mp_obj_t self_in) { - mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_list_t *self = mp_instance_cast_to_native_base(self_in, &mp_type_list); 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); @@ -103,7 +108,7 @@ STATIC mp_obj_t list_unary_op(mp_unary_op_t op, mp_obj_t self_in) { } STATIC mp_obj_t list_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) { - mp_obj_list_t *o = MP_OBJ_TO_PTR(lhs); + mp_obj_list_t *o = mp_instance_cast_to_native_base(lhs, &mp_type_list); switch (op) { case MP_BINARY_OP_ADD: { if (!MP_OBJ_IS_TYPE(rhs, &mp_type_list)) { @@ -126,7 +131,8 @@ STATIC mp_obj_t list_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) { if (n < 0) { n = 0; } - mp_obj_list_t *s = list_new(o->len * n); + size_t new_len = mp_seq_multiply_len(o->len, n); + mp_obj_list_t *s = list_new(new_len); mp_seq_multiply(o->items, sizeof(*o->items), o->len, n, s->items); return MP_OBJ_FROM_PTR(s); } @@ -153,11 +159,11 @@ STATIC mp_obj_t list_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) { } STATIC mp_obj_t list_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { + mp_obj_list_t *self = mp_instance_cast_to_native_base(self_in, &mp_type_list); if (value == MP_OBJ_NULL) { // delete #if MICROPY_PY_BUILTINS_SLICE if (MP_OBJ_IS_TYPE(index, &mp_type_slice)) { - mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in); mp_bound_slice_t slice; if (!mp_seq_get_fast_slice_indexes(self->len, index, &slice)) { mp_raise_NotImplementedError(NULL); @@ -173,12 +179,11 @@ STATIC mp_obj_t list_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { return mp_const_none; } #endif - mp_obj_t args[2] = {self_in, index}; + mp_obj_t args[2] = {self, index}; list_pop(2, args); return mp_const_none; } else if (value == MP_OBJ_SENTINEL) { // load - mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in); #if MICROPY_PY_BUILTINS_SLICE if (MP_OBJ_IS_TYPE(index, &mp_type_slice)) { mp_bound_slice_t slice; @@ -195,7 +200,6 @@ STATIC mp_obj_t list_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { } else { #if MICROPY_PY_BUILTINS_SLICE if (MP_OBJ_IS_TYPE(index, &mp_type_slice)) { - mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in); size_t value_len; mp_obj_t *value_items; mp_obj_get_array(value, &value_len, &value_items); mp_bound_slice_t slice_out; @@ -224,7 +228,7 @@ STATIC mp_obj_t list_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { return mp_const_none; } #endif - mp_obj_list_store(self_in, index, value); + mp_obj_list_store(self, index, value); return mp_const_none; } } @@ -235,7 +239,7 @@ STATIC mp_obj_t list_getiter(mp_obj_t o_in, mp_obj_iter_buf_t *iter_buf) { mp_obj_t mp_obj_list_append(mp_obj_t self_in, mp_obj_t arg) { mp_check_self(MP_OBJ_IS_TYPE(self_in, &mp_type_list)); - mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_list_t *self = mp_instance_cast_to_native_base(self_in, &mp_type_list); if (self->len >= self->alloc) { self->items = m_renew(mp_obj_t, self->items, self->alloc, self->alloc * 2); self->alloc *= 2; @@ -248,8 +252,8 @@ mp_obj_t mp_obj_list_append(mp_obj_t self_in, mp_obj_t arg) { STATIC mp_obj_t list_extend(mp_obj_t self_in, mp_obj_t arg_in) { mp_check_self(MP_OBJ_IS_TYPE(self_in, &mp_type_list)); if (MP_OBJ_IS_TYPE(arg_in, &mp_type_list)) { - mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in); - mp_obj_list_t *arg = MP_OBJ_TO_PTR(arg_in); + mp_obj_list_t *self = mp_instance_cast_to_native_base(self_in, &mp_type_list); + mp_obj_list_t *arg = mp_instance_cast_to_native_base(arg_in, &mp_type_list); if (self->len + arg->len > self->alloc) { // TODO: use alloc policy for "4" @@ -268,7 +272,7 @@ STATIC mp_obj_t list_extend(mp_obj_t self_in, mp_obj_t arg_in) { STATIC mp_obj_t list_pop(size_t n_args, const mp_obj_t *args) { mp_check_self(MP_OBJ_IS_TYPE(args[0], &mp_type_list)); - mp_obj_list_t *self = MP_OBJ_TO_PTR(args[0]); + mp_obj_list_t *self = mp_instance_cast_to_native_base(args[0], &mp_type_list); if (self->len == 0) { mp_raise_IndexError(translate("pop from empty list")); } @@ -328,7 +332,7 @@ mp_obj_t mp_obj_list_sort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ MP_ARRAY_SIZE(allowed_args), allowed_args, (mp_arg_val_t*)&args); mp_check_self(MP_OBJ_IS_TYPE(pos_args[0], &mp_type_list)); - mp_obj_list_t *self = MP_OBJ_TO_PTR(pos_args[0]); + mp_obj_list_t *self = mp_instance_cast_to_native_base(pos_args[0], &mp_type_list); if (self->len > 1) { mp_quicksort(self->items, self->items + self->len - 1, @@ -339,9 +343,9 @@ mp_obj_t mp_obj_list_sort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ return mp_const_none; } -STATIC mp_obj_t list_clear(mp_obj_t self_in) { +mp_obj_t mp_obj_list_clear(mp_obj_t self_in) { mp_check_self(MP_OBJ_IS_TYPE(self_in, &mp_type_list)); - mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_list_t *self = mp_instance_cast_to_native_base(self_in, &mp_type_list); self->len = 0; self->items = m_renew(mp_obj_t, self->items, self->alloc, LIST_MIN_ALLOC); self->alloc = LIST_MIN_ALLOC; @@ -351,25 +355,25 @@ STATIC mp_obj_t list_clear(mp_obj_t self_in) { STATIC mp_obj_t list_copy(mp_obj_t self_in) { mp_check_self(MP_OBJ_IS_TYPE(self_in, &mp_type_list)); - mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_list_t *self = mp_instance_cast_to_native_base(self_in, &mp_type_list); return mp_obj_new_list(self->len, self->items); } STATIC mp_obj_t list_count(mp_obj_t self_in, mp_obj_t value) { mp_check_self(MP_OBJ_IS_TYPE(self_in, &mp_type_list)); - mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_list_t *self = mp_instance_cast_to_native_base(self_in, &mp_type_list); return mp_seq_count_obj(self->items, self->len, value); } STATIC mp_obj_t list_index(size_t n_args, const mp_obj_t *args) { mp_check_self(MP_OBJ_IS_TYPE(args[0], &mp_type_list)); - mp_obj_list_t *self = MP_OBJ_TO_PTR(args[0]); + mp_obj_list_t *self = mp_instance_cast_to_native_base(args[0], &mp_type_list); return mp_seq_index_obj(self->items, self->len, n_args, args); } STATIC mp_obj_t list_insert(mp_obj_t self_in, mp_obj_t idx, mp_obj_t obj) { mp_check_self(MP_OBJ_IS_TYPE(self_in, &mp_type_list)); - mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_list_t *self = mp_instance_cast_to_native_base(self_in, &mp_type_list); // insert has its own strange index logic mp_int_t index = MP_OBJ_SMALL_INT_VALUE(idx); if (index < 0) { @@ -403,7 +407,7 @@ mp_obj_t mp_obj_list_remove(mp_obj_t self_in, mp_obj_t value) { STATIC mp_obj_t list_reverse(mp_obj_t self_in) { mp_check_self(MP_OBJ_IS_TYPE(self_in, &mp_type_list)); - mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_list_t *self = mp_instance_cast_to_native_base(self_in, &mp_type_list); mp_int_t len = self->len; for (mp_int_t i = 0; i < len/2; i++) { @@ -417,7 +421,7 @@ STATIC mp_obj_t list_reverse(mp_obj_t self_in) { STATIC MP_DEFINE_CONST_FUN_OBJ_2(list_append_obj, mp_obj_list_append); STATIC MP_DEFINE_CONST_FUN_OBJ_2(list_extend_obj, list_extend); -STATIC MP_DEFINE_CONST_FUN_OBJ_1(list_clear_obj, list_clear); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(list_clear_obj, mp_obj_list_clear); STATIC MP_DEFINE_CONST_FUN_OBJ_1(list_copy_obj, list_copy); STATIC MP_DEFINE_CONST_FUN_OBJ_2(list_count_obj, list_count); STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(list_index_obj, 2, 4, list_index); @@ -480,7 +484,7 @@ mp_obj_t mp_obj_new_list(size_t n, mp_obj_t *items) { } void mp_obj_list_get(mp_obj_t self_in, size_t *len, mp_obj_t **items) { - mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_list_t *self = mp_instance_cast_to_native_base(self_in, &mp_type_list); *len = self->len; *items = self->items; } @@ -493,7 +497,7 @@ void mp_obj_list_set_len(mp_obj_t self_in, size_t len) { } void mp_obj_list_store(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { - mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_list_t *self = mp_instance_cast_to_native_base(self_in, &mp_type_list); size_t i = mp_get_index(self->base.type, self->len, index, false); self->items[i] = value; } diff --git a/py/objmap.c b/py/objmap.c index 908c61507..cf71f99ee 100644 --- a/py/objmap.c +++ b/py/objmap.c @@ -36,8 +36,8 @@ typedef struct _mp_obj_map_t { mp_obj_t iters[]; } mp_obj_map_t; -STATIC mp_obj_t map_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { - mp_arg_check_num(n_args, n_kw, 2, MP_OBJ_FUN_ARGS_MAX, false); +STATIC mp_obj_t map_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + mp_arg_check_num(n_args, kw_args, 2, MP_OBJ_FUN_ARGS_MAX, false); mp_obj_map_t *o = m_new_obj_var(mp_obj_map_t, mp_obj_t, n_args - 1); o->base.type = type; o->n_iters = n_args - 1; diff --git a/py/objmodule.c b/py/objmodule.c index 828f556f3..b6a8a084e 100644 --- a/py/objmodule.c +++ b/py/objmodule.c @@ -32,6 +32,8 @@ #include "py/runtime.h" #include "py/builtin.h" +#include "genhdr/moduledefs.h" + STATIC void module_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { (void)kind; mp_obj_module_t *self = MP_OBJ_TO_PTR(self_in); @@ -67,6 +69,13 @@ STATIC void module_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { // delete/store attribute mp_obj_dict_t *dict = self->globals; if (dict->map.is_fixed) { + mp_map_elem_t *elem = mp_map_lookup(&dict->map, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP); + // Return success if the given value is already in the dictionary. This is the case for + // native packages with native submodules. + if (elem != NULL && elem->value == dest[1]) { + dest[0] = MP_OBJ_NULL; // indicate success + return; + } else #if MICROPY_CAN_OVERRIDE_BUILTINS if (dict == &mp_module_builtins_globals) { if (MP_STATE_VM(mp_module_builtins_override_dict) == NULL) { @@ -148,11 +157,16 @@ STATIC const mp_rom_map_elem_t mp_builtin_module_table[] = { { MP_ROM_QSTR(MP_QSTR_array), MP_ROM_PTR(&mp_module_array) }, #endif #if MICROPY_PY_IO +#if CIRCUITPY + { MP_ROM_QSTR(MP_QSTR_io), MP_ROM_PTR(&mp_module_io) }, +#else { MP_ROM_QSTR(MP_QSTR_uio), MP_ROM_PTR(&mp_module_io) }, #endif +#endif #if MICROPY_PY_COLLECTIONS { MP_ROM_QSTR(MP_QSTR_collections), MP_ROM_PTR(&mp_module_collections) }, #endif +// CircuitPython: Now in shared-bindings/, so not defined here. #if MICROPY_PY_STRUCT { MP_ROM_QSTR(MP_QSTR_ustruct), MP_ROM_PTR(&mp_module_ustruct) }, #endif @@ -178,8 +192,13 @@ STATIC const mp_rom_map_elem_t mp_builtin_module_table[] = { // extmod modules #if MICROPY_PY_UERRNO +#if CIRCUITPY +// CircuitPython: Defined in MICROPY_PORT_BUILTIN_MODULES, so not defined here. +// TODO: move to shared-bindings/ +#else { MP_ROM_QSTR(MP_QSTR_uerrno), MP_ROM_PTR(&mp_module_uerrno) }, #endif +#endif #if MICROPY_PY_UCTYPES { MP_ROM_QSTR(MP_QSTR_uctypes), MP_ROM_PTR(&mp_module_uctypes) }, #endif @@ -187,11 +206,29 @@ STATIC const mp_rom_map_elem_t mp_builtin_module_table[] = { { MP_ROM_QSTR(MP_QSTR_uzlib), MP_ROM_PTR(&mp_module_uzlib) }, #endif #if MICROPY_PY_UJSON +#if CIRCUITPY +// CircuitPython: Defined in MICROPY_PORT_BUILTIN_MODULES, so not defined here. +// TODO: move to shared-bindings/ +#else { MP_ROM_QSTR(MP_QSTR_ujson), MP_ROM_PTR(&mp_module_ujson) }, #endif +#endif +#if CIRCUITPY_ULAB +#if CIRCUITPY +// CircuitPython: Defined in MICROPY_PORT_BUILTIN_MODULES, so not defined here. +// TODO: move to shared-bindings/ +#else + { MP_ROM_QSTR(MP_QSTR_ulab), MP_ROM_PTR(&ulab_user_cmodule) }, +#endif +#endif #if MICROPY_PY_URE +#if CIRCUITPY +// CircuitPython: Defined in MICROPY_PORT_BUILTIN_MODULES, so not defined here. +// TODO: move to shared-bindings/ +#else { MP_ROM_QSTR(MP_QSTR_ure), MP_ROM_PTR(&mp_module_ure) }, #endif +#endif #if MICROPY_PY_UHEAPQ { MP_ROM_QSTR(MP_QSTR_uheapq), MP_ROM_PTR(&mp_module_uheapq) }, #endif @@ -232,6 +269,11 @@ STATIC const mp_rom_map_elem_t mp_builtin_module_table[] = { // extra builtin modules as defined by a port MICROPY_PORT_BUILTIN_MODULES + #ifdef MICROPY_REGISTERED_MODULES + // builtin modules declared with MP_REGISTER_MODULE() + MICROPY_REGISTERED_MODULES + #endif + #if defined(MICROPY_DEBUG_MODULES) && defined(MICROPY_PORT_BUILTIN_DEBUG_MODULES) , MICROPY_PORT_BUILTIN_DEBUG_MODULES #endif diff --git a/py/objnamedtuple.c b/py/objnamedtuple.c index 800991c43..84dcf7909 100644 --- a/py/objnamedtuple.c +++ b/py/objnamedtuple.c @@ -31,6 +31,7 @@ #include "py/runtime.h" #include "py/objstr.h" #include "py/objnamedtuple.h" +#include "py/objtype.h" #include "supervisor/shared/translate.h" @@ -93,9 +94,13 @@ void namedtuple_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { } } -mp_obj_t namedtuple_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { +mp_obj_t namedtuple_make_new(const mp_obj_type_t *type_in, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { const mp_obj_namedtuple_type_t *type = (const mp_obj_namedtuple_type_t*)type_in; size_t num_fields = type->n_fields; + size_t n_kw = 0; + if (kw_args != NULL) { + n_kw = kw_args->used; + } if (n_args + n_kw != num_fields) { if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) { mp_arg_error_terse_mismatch(); @@ -119,8 +124,8 @@ mp_obj_t namedtuple_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t // Fill in the remaining slots with the keyword args memset(&tuple->items[n_args], 0, sizeof(mp_obj_t) * n_kw); - for (size_t i = n_args; i < n_args + 2 * n_kw; i += 2) { - qstr kw = mp_obj_str_get_qstr(args[i]); + for (size_t i = 0; i < n_kw; i++) { + qstr kw = mp_obj_str_get_qstr(kw_args->table[i].key); size_t id = mp_obj_namedtuple_find_field(type, kw); if (id == (size_t)-1) { if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) { @@ -138,7 +143,7 @@ mp_obj_t namedtuple_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t translate("function got multiple values for argument '%q'"), kw); } } - tuple->items[id] = args[i + 1]; + tuple->items[id] = kw_args->table[i].value; } return MP_OBJ_FROM_PTR(tuple); diff --git a/py/objnamedtuple.h b/py/objnamedtuple.h index deac7107b..0ea0d2862 100644 --- a/py/objnamedtuple.h +++ b/py/objnamedtuple.h @@ -51,7 +51,7 @@ void namedtuple_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t ki size_t mp_obj_namedtuple_find_field(const mp_obj_namedtuple_type_t *type, qstr name); void namedtuple_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest); mp_obj_namedtuple_type_t *mp_obj_new_namedtuple_base(size_t n_fields, mp_obj_t *fields); -mp_obj_t namedtuple_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args); +mp_obj_t namedtuple_make_new(const mp_obj_type_t *type_in, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args); #endif // MICROPY_PY_COLLECTIONS diff --git a/py/objobject.c b/py/objobject.c index fa8c29cff..a42edde3c 100644 --- a/py/objobject.c +++ b/py/objobject.c @@ -35,9 +35,9 @@ typedef struct _mp_obj_object_t { mp_obj_base_t base; } mp_obj_object_t; -STATIC mp_obj_t object_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 object_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { (void)args; - mp_arg_check_num(n_args, n_kw, 0, 0, false); + mp_arg_check_num(n_args, kw_args, 0, 0, false); mp_obj_object_t *o = m_new_obj(mp_obj_object_t); o->base.type = type; return MP_OBJ_FROM_PTR(o); diff --git a/py/objproperty.c b/py/objproperty.c index 4aba6c7a1..ddf484af2 100644 --- a/py/objproperty.c +++ b/py/objproperty.c @@ -33,7 +33,7 @@ #if MICROPY_PY_BUILTINS_PROPERTY -STATIC mp_obj_t property_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 property_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { enum { ARG_fget, ARG_fset, ARG_fdel, ARG_doc }; static const mp_arg_t allowed_args[] = { { MP_QSTR_, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} }, @@ -42,7 +42,7 @@ STATIC mp_obj_t property_make_new(const mp_obj_type_t *type, size_t n_args, size { MP_QSTR_doc, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} }, }; mp_arg_val_t vals[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all_kw_array(n_args, n_kw, args, MP_ARRAY_SIZE(allowed_args), allowed_args, vals); + mp_arg_parse_all(n_args, args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, vals); mp_obj_property_t *o = m_new_obj(mp_obj_property_t); o->base.type = type; diff --git a/py/objrange.c b/py/objrange.c index 33c0d9b11..30d55c56c 100644 --- a/py/objrange.c +++ b/py/objrange.c @@ -91,8 +91,8 @@ STATIC void range_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind } } -STATIC mp_obj_t range_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, 3, false); +STATIC mp_obj_t range_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + mp_arg_check_num(n_args, kw_args, 1, 3, false); mp_obj_range_t *o = m_new_obj(mp_obj_range_t); o->base.type = type; diff --git a/py/objreversed.c b/py/objreversed.c index e498b553d..4937d0818 100644 --- a/py/objreversed.c +++ b/py/objreversed.c @@ -37,8 +37,8 @@ typedef struct _mp_obj_reversed_t { mp_uint_t cur_index; // current index, plus 1; 0=no more, 1=last one (index 0) } mp_obj_reversed_t; -STATIC mp_obj_t reversed_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); +STATIC mp_obj_t reversed_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + mp_arg_check_num(n_args, kw_args, 1, 1, false); // check if __reversed__ exists, and if so delegate to it mp_obj_t dest[2]; diff --git a/py/objset.c b/py/objset.c index 00d6eae0e..5d1608c7e 100644 --- a/py/objset.c +++ b/py/objset.c @@ -103,8 +103,8 @@ STATIC void set_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t #endif } -STATIC mp_obj_t set_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { - mp_arg_check_num(n_args, n_kw, 0, 1, false); +STATIC mp_obj_t set_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + mp_arg_check_num(n_args, kw_args, 0, 1, false); switch (n_args) { case 0: { @@ -299,7 +299,7 @@ STATIC mp_obj_t set_issubset_internal(mp_obj_t self_in, mp_obj_t other_in, bool if (is_set_or_frozenset(self_in)) { self = MP_OBJ_TO_PTR(self_in); } else { - self = MP_OBJ_TO_PTR(set_make_new(&mp_type_set, 1, 0, &self_in)); + self = MP_OBJ_TO_PTR(set_make_new(&mp_type_set, 1, &self_in, NULL)); cleanup_self = true; } @@ -308,7 +308,7 @@ STATIC mp_obj_t set_issubset_internal(mp_obj_t self_in, mp_obj_t other_in, bool if (is_set_or_frozenset(other_in)) { other = MP_OBJ_TO_PTR(other_in); } else { - other = MP_OBJ_TO_PTR(set_make_new(&mp_type_set, 1, 0, &other_in)); + other = MP_OBJ_TO_PTR(set_make_new(&mp_type_set, 1, &other_in, NULL)); cleanup_other = true; } mp_obj_t out = mp_const_true; diff --git a/py/objslice.c b/py/objslice.c index 08f18935b..cbbee326e 100644 --- a/py/objslice.c +++ b/py/objslice.c @@ -130,7 +130,7 @@ STATIC void slice_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { } STATIC mp_obj_t slice_make_new(const mp_obj_type_t *type, - size_t n_args, size_t n_kw, const mp_obj_t *args); + size_t n_args, const mp_obj_t *args, mp_map_t *kw_args); #endif const mp_obj_type_t mp_type_slice = { @@ -152,14 +152,79 @@ mp_obj_t mp_obj_new_slice(mp_obj_t ostart, mp_obj_t ostop, mp_obj_t ostep) { return MP_OBJ_FROM_PTR(o); } +// Return the real index and step values for a slice when applied to a sequence of +// the given length, resolving missing components, negative values and values off +// the end of the sequence. +void mp_obj_slice_indices(mp_obj_t self_in, mp_int_t length, mp_bound_slice_t *result) { + mp_obj_slice_t *self = MP_OBJ_TO_PTR(self_in); + mp_int_t start, stop, step; + + if (self->step == mp_const_none) { + step = 1; + } else { + step = mp_obj_get_int(self->step); + if (step == 0) { + mp_raise_ValueError(translate("slice step cannot be zero")); + } + } + + if (step > 0) { + // Positive step + if (self->start == mp_const_none) { + start = 0; + } else { + start = mp_obj_get_int(self->start); + if (start < 0) { + start += length; + } + start = MIN(length, MAX(start, 0)); + } + + if (self->stop == mp_const_none) { + stop = length; + } else { + stop = mp_obj_get_int(self->stop); + if (stop < 0) { + stop += length; + } + stop = MIN(length, MAX(stop, 0)); + } + } else { + // Negative step + if (self->start == mp_const_none) { + start = length - 1; + } else { + start = mp_obj_get_int(self->start); + if (start < 0) { + start += length; + } + start = MIN(length - 1, MAX(start, -1)); + } + + if (self->stop == mp_const_none) { + stop = -1; + } else { + stop = mp_obj_get_int(self->stop); + if (stop < 0) { + stop += length; + } + stop = MIN(length - 1, MAX(stop, -1)); + } + } + + result->start = start; + result->stop = stop; + result->step = step; +} + #if MICROPY_PY_BUILTINS_SLICE_ATTRS STATIC mp_obj_t slice_make_new(const mp_obj_type_t *type, - size_t n_args, size_t n_kw, const mp_obj_t *args) { + size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { if (type != &mp_type_slice) { mp_raise_NotImplementedError(translate("Cannot subclass slice")); } // check number of arguments - mp_arg_check_num(n_args, n_kw, 1, 3, false); + mp_arg_check_num(n_args, kw_args, 1, 3, false); // 1st argument is the pin mp_obj_t start = mp_const_none; diff --git a/py/objstr.c b/py/objstr.c index 621f1b837..a60f507e9 100644 --- a/py/objstr.c +++ b/py/objstr.c @@ -31,6 +31,7 @@ #include "py/unicode.h" #include "py/objstr.h" #include "py/objlist.h" +#include "py/objtype.h" #include "py/runtime.h" #include "py/stackctrl.h" @@ -41,6 +42,12 @@ 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'}; + +const char nibble_to_hex_lower[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'a', 'b', 'c', 'd', 'e', 'f'}; + /******************************************************************************/ /* str */ @@ -132,14 +139,14 @@ 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) { +mp_obj_t mp_obj_str_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { #if MICROPY_CPYTHON_COMPAT - if (n_kw != 0) { + if (kw_args != NULL && kw_args->used != 0) { mp_arg_error_unimpl_kw(); } #endif - mp_arg_check_num(n_args, n_kw, 0, 3, false); + mp_arg_check_num(n_args, kw_args, 0, 3, false); switch (n_args) { case 0: @@ -190,15 +197,15 @@ mp_obj_t mp_obj_str_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_ } } -STATIC mp_obj_t bytes_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { +STATIC mp_obj_t bytes_make_new(const mp_obj_type_t *type_in, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { (void)type_in; #if MICROPY_CPYTHON_COMPAT - if (n_kw != 0) { + if (kw_args != NULL && kw_args->used != 0) { mp_arg_error_unimpl_kw(); } #else - (void)n_kw; + (void)kw_args; #endif if (n_args == 0) { @@ -220,10 +227,6 @@ STATIC mp_obj_t bytes_make_new(const mp_obj_type_t *type_in, size_t n_args, size return MP_OBJ_FROM_PTR(o); } - if (n_args > 1) { - goto wrong_args; - } - if (MP_OBJ_IS_SMALL_INT(args[0])) { mp_int_t len = MP_OBJ_SMALL_INT_VALUE(args[0]); if (len < 0) { @@ -235,6 +238,17 @@ STATIC mp_obj_t bytes_make_new(const mp_obj_type_t *type_in, size_t n_args, size return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } + if (n_args > 1) { + goto wrong_args; + } + + // 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); + if (dest[0] != MP_OBJ_NULL) { + return mp_call_method_n_kw(0, 0, dest); + } + // check if argument has the buffer protocol mp_buffer_info_t bufinfo; if (mp_get_buffer(args[0], &bufinfo, MP_BUFFER_READ)) { @@ -332,8 +346,9 @@ 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; } } + size_t new_len = mp_seq_multiply_len(lhs_len, n); vstr_t vstr; - vstr_init_len(&vstr, lhs_len * n); + vstr_init_len(&vstr, new_len); mp_seq_multiply(lhs_data, sizeof(*lhs_data), lhs_len, n, vstr.buf); return mp_obj_new_str_from_vstr(lhs_type, &vstr); } @@ -407,6 +422,15 @@ 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(translate("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); @@ -455,7 +479,7 @@ 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_type_list.make_new(&mp_type_list, 1, &arg, NULL); } mp_obj_get_array(arg, &seq_len, &seq_items); @@ -1328,7 +1352,7 @@ STATIC vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *ar terse_str_format_value_error(); } else { mp_raise_ValueError_varg( - translate("unknown format code '%c' for object of type 'float'"), + translate("unknown format code '%c' for object of type '%s'"), type, mp_obj_get_type_str(arg)); } } @@ -1364,7 +1388,7 @@ STATIC vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *ar terse_str_format_value_error(); } else { mp_raise_ValueError_varg( - translate("unknown format code '%c' for object of type 'str'"), + translate("unknown format code '%c' for object of type '%s'"), type, mp_obj_get_type_str(arg)); } } @@ -1875,7 +1899,7 @@ STATIC mp_obj_t bytes_decode(size_t n_args, const mp_obj_t *args) { args = new_args; n_args++; } - return mp_obj_str_make_new(&mp_type_str, n_args, 0, args); + return mp_obj_str_make_new(&mp_type_str, n_args, args, NULL); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bytes_decode_obj, 1, 3, bytes_decode); @@ -1888,7 +1912,7 @@ STATIC mp_obj_t str_encode(size_t n_args, const mp_obj_t *args) { args = new_args; n_args++; } - return bytes_make_new(NULL, n_args, 0, args); + return bytes_make_new(NULL, n_args, args, NULL); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(str_encode_obj, 1, 3, str_encode); #endif @@ -2078,6 +2102,14 @@ 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; diff --git a/py/objstr.h b/py/objstr.h index 4e55cad09..61a11d0bd 100644 --- a/py/objstr.h +++ b/py/objstr.h @@ -61,7 +61,7 @@ const byte *mp_obj_str_get_data_no_check(mp_obj_t self_in, size_t *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; } #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); +mp_obj_t mp_obj_str_make_new(const mp_obj_type_t *type_in, size_t n_args, const mp_obj_t *args, mp_map_t *kw_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); @@ -71,10 +71,15 @@ mp_obj_t mp_obj_new_str_of_type(const mp_obj_type_t *type, const byte* data, siz 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); 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); +const char nibble_to_hex_upper[16]; +const char nibble_to_hex_lower[16]; + 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); diff --git a/py/objstringio.c b/py/objstringio.c index d21248ad7..178e6446c 100644 --- a/py/objstringio.c +++ b/py/objstringio.c @@ -186,8 +186,8 @@ STATIC mp_obj_stringio_t *stringio_new(const mp_obj_type_t *type) { return o; } -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) { - (void)n_kw; // TODO check n_kw==0 +STATIC mp_obj_t stringio_make_new(const mp_obj_type_t *type_in, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + (void)kw_args; // TODO check kw_args->used == 0 mp_uint_t sz = 16; bool initdata = false; @@ -240,6 +240,7 @@ 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, @@ -247,6 +248,7 @@ STATIC const mp_stream_p_t stringio_stream_p = { }; 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, diff --git a/py/objstrunicode.c b/py/objstrunicode.c index 03106f987..30000a51e 100644 --- a/py/objstrunicode.c +++ b/py/objstrunicode.c @@ -112,6 +112,26 @@ 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(translate("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, diff --git a/py/objtuple.c b/py/objtuple.c index b4f5b1bdf..0a2ed6f4c 100644 --- a/py/objtuple.c +++ b/py/objtuple.c @@ -29,6 +29,7 @@ #include "py/objtuple.h" #include "py/runtime.h" +#include "py/objtype.h" #include "supervisor/shared/translate.h" @@ -59,10 +60,10 @@ void mp_obj_tuple_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t } } -STATIC mp_obj_t mp_obj_tuple_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { +STATIC mp_obj_t mp_obj_tuple_make_new(const mp_obj_type_t *type_in, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { (void)type_in; - mp_arg_check_num(n_args, n_kw, 0, 1, false); + mp_arg_check_num(n_args, kw_args, 0, 1, false); switch (n_args) { case 0: @@ -160,7 +161,8 @@ 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; } - mp_obj_tuple_t *s = MP_OBJ_TO_PTR(mp_obj_new_tuple(o->len * n, NULL)); + 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); return MP_OBJ_FROM_PTR(s); } @@ -180,6 +182,11 @@ mp_obj_t mp_obj_tuple_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { if (value == MP_OBJ_SENTINEL) { // load mp_obj_tuple_t *self = MP_OBJ_TO_PTR(self_in); + // when called with a native type (eg namedtuple) using mp_obj_tuple_subscr, get the native self + if (self->base.type->subscr != &mp_obj_tuple_subscr) { + self = mp_instance_cast_to_native_base(self_in, &mp_type_tuple); + } + #if MICROPY_PY_BUILTINS_SLICE if (MP_OBJ_IS_TYPE(index, &mp_type_slice)) { mp_bound_slice_t slice; @@ -250,7 +257,8 @@ mp_obj_t mp_obj_new_tuple(size_t n, const mp_obj_t *items) { } void mp_obj_tuple_get(mp_obj_t self_in, size_t *len, mp_obj_t **items) { - assert(MP_OBJ_IS_TYPE(self_in, &mp_type_tuple)); + // type check is done on getiter method to allow tuple, namedtuple, attrtuple + mp_check_self(mp_obj_get_type(self_in)->getiter == mp_obj_tuple_getiter); mp_obj_tuple_t *self = MP_OBJ_TO_PTR(self_in); *len = self->len; *items = &self->items[0]; diff --git a/py/objtype.c b/py/objtype.c index 95dfc2a15..ad68b85d2 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -34,6 +34,7 @@ #include "py/objtype.h" #include "py/runtime.h" +#include "supervisor/shared/stack.h" #include "supervisor/shared/translate.h" #if MICROPY_DEBUG_VERBOSE // print debugging info @@ -50,7 +51,7 @@ #define TYPE_FLAG_IS_SUBCLASSED (0x0001) #define TYPE_FLAG_HAS_SPECIAL_ACCESSORS (0x0002) -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); +STATIC mp_obj_t static_class_method_make_new(const mp_obj_type_t *self_in, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args); /******************************************************************************/ // instance object @@ -90,14 +91,14 @@ STATIC int instance_count_native_bases(const mp_obj_type_t *type, const mp_obj_t // This wrapper function is allows a subclass of a native type to call the // __init__() method (corresponding to type->make_new) of the native type. -STATIC mp_obj_t native_base_init_wrapper(size_t n_args, const mp_obj_t *args) { - mp_obj_instance_t *self = MP_OBJ_TO_PTR(args[0]); +STATIC mp_obj_t native_base_init_wrapper(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + mp_obj_instance_t *self = MP_OBJ_TO_PTR(pos_args[0]); const mp_obj_type_t *native_base = NULL; instance_count_native_bases(self->base.type, &native_base); - self->subobj[0] = native_base->make_new(native_base, n_args - 1, 0, args + 1); + self->subobj[0] = native_base->make_new(native_base, n_args - 1, pos_args + 1, kw_args); return mp_const_none; } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(native_base_init_wrapper_obj, 1, MP_OBJ_FUN_ARGS_MAX, native_base_init_wrapper); +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(native_base_init_wrapper_obj, 1, native_base_init_wrapper); #if !MICROPY_CPYTHON_COMPAT STATIC @@ -117,6 +118,16 @@ mp_obj_instance_t *mp_obj_new_instance(const mp_obj_type_t *class, const mp_obj_ return o; } +// When instances are first created they have the base_init wrapper as their native parent's +// instance because make_new combines __new__ and __init__. This object is invalid for the native +// code so it must call this method to ensure that the given object has been __init__'d and is +// valid. +void mp_obj_assert_native_inited(mp_obj_t native_object) { + if (native_object == MP_OBJ_FROM_PTR(&native_base_init_wrapper_obj)) { + mp_raise_NotImplementedError(translate("Call super().__init__() before accessing native object.")); + } +} + // TODO // This implements depth-first left-to-right MRO, which is not compliant with Python3 MRO // http://python-history.blogspot.com/2010/06/method-resolution-order.html @@ -171,16 +182,12 @@ STATIC void mp_obj_class_lookup(struct class_lookup_data *lookup, const mp_obj_ // 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_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); + mp_convert_member_lookup(MP_OBJ_FROM_PTR(obj), type, elem->value, lookup->dest); } #if DEBUG_PRINT printf("mp_obj_class_lookup: Returning: "); @@ -281,7 +288,7 @@ STATIC void instance_print(const mp_print_t *print, mp_obj_t self_in, mp_print_k mp_printf(print, "<%s object at %p>", mp_obj_get_type_str(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) { +mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { assert(mp_obj_is_instance_type(self)); // look for __new__ function @@ -297,6 +304,10 @@ mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self, size_t n_args, size const mp_obj_type_t *native_base = NULL; mp_obj_instance_t *o; + size_t n_kw = 0; + if (kw_args != 0) { + n_kw = kw_args->used; + } if (init_fn[0] == MP_OBJ_NULL || init_fn[0] == MP_OBJ_SENTINEL) { // Either there is no __new__() method defined or there is a native // constructor. In both cases create a blank instance. @@ -315,9 +326,14 @@ 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)); + memcpy(args2 + 1, args, n_args * sizeof(mp_obj_t)); + if (kw_args) { + // copy in kwargs + memcpy(args2 + 1 + n_args, kw_args->table, 2 * n_kw * sizeof(mp_obj_t)); + } new_ret = mp_call_function_n_kw(init_fn[0], n_args + 1, n_kw, args2); m_del(mp_obj_t, args2, 1 + n_args + 2 * n_kw); } @@ -343,13 +359,16 @@ mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self, size_t n_args, size 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) { + if (n_args == 0 && kw_args == NULL) { 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]; - memcpy(args2 + 2, args, (n_args + 2 * n_kw) * sizeof(mp_obj_t)); + // copy in kwargs + memcpy(args2 + 2, args, n_args * sizeof(mp_obj_t)); + memcpy(args2 + 2 + n_args, kw_args->table, 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); } @@ -367,7 +386,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] = native_base->make_new(native_base, n_args, args, kw_args); } return MP_OBJ_FROM_PTR(o); @@ -826,8 +845,13 @@ 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); meth_args = 3; } - if (member[0] == MP_OBJ_SENTINEL) { - return mp_obj_subscr(self->subobj[0], index, value); + if (member[0] == MP_OBJ_SENTINEL) { // native base subscr exists + mp_obj_type_t *subobj_type = mp_obj_get_type(self->subobj[0]); + // return mp_obj_subscr(self->subobj[0], index, value, instance); + mp_obj_t ret = subobj_type->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; } else if (member[0] != MP_OBJ_NULL) { mp_obj_t args[3] = {self_in, index, value}; // TODO probably need to call mp_convert_member_lookup, and use mp_call_method_n_kw @@ -951,6 +975,21 @@ STATIC bool check_for_special_accessors(mp_obj_t key, mp_obj_t value) { #endif return false; } + +STATIC bool map_has_special_accessors(const mp_map_t *map) { + if (map == NULL) { + return false; + } + for (size_t i = 0; i < map->alloc; i++) { + if (MP_MAP_SLOT_IS_FILLED(map, i)) { + const mp_map_elem_t *elem = &map->table[i]; + if (check_for_special_accessors(elem->key, elem->value)) { + return true; + } + } + } + return false; +} #endif STATIC void type_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { @@ -959,10 +998,10 @@ STATIC void type_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_ mp_printf(print, "<class '%q'>", self->name); } -STATIC mp_obj_t type_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { +STATIC mp_obj_t type_make_new(const mp_obj_type_t *type_in, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { (void)type_in; - mp_arg_check_num(n_args, n_kw, 1, 3, false); + mp_arg_check_num(n_args, kw_args, 1, 3, false); switch (n_args) { case 1: @@ -992,8 +1031,10 @@ STATIC mp_obj_t type_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp } } - // make new instance - mp_obj_t o = self->make_new(self, n_args, n_kw, args); + // create a map directly from the given args array and make a new instance + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + mp_obj_t o = self->make_new(self, n_args, args, &kw_args); // return new instance return o; @@ -1053,7 +1094,7 @@ STATIC void type_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { // store attribute mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); - elem->value = make_obj_long_lived(dest[1], 10); + elem->value = dest[1]; dest[0] = MP_OBJ_NULL; // indicate success } } @@ -1143,20 +1184,6 @@ mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict) o->locals_dict = make_dict_long_lived(locals_dict, 10); - #if ENABLE_SPECIAL_ACCESSORS - // Check if the class has any special accessor methods - if (!(o->flags & 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]; - if (check_for_special_accessors(elem->key, elem->value)) { - o->flags |= TYPE_FLAG_HAS_SPECIAL_ACCESSORS; - break; - } - } - } - } - #endif const mp_obj_type_t *native_base; size_t num_native_bases = instance_count_native_bases(o, &native_base); @@ -1165,12 +1192,23 @@ mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict) } 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 & 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 |= TYPE_FLAG_HAS_SPECIAL_ACCESSORS; + } + #endif + 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 if (MP_OBJ_IS_FUN(elem->value)) { // __new__ is a function, wrap it in a staticmethod decorator - elem->value = static_class_method_make_new(&mp_type_staticmethod, 1, 0, &elem->value); + elem->value = static_class_method_make_new(&mp_type_staticmethod, 1, &elem->value, NULL); } } @@ -1196,11 +1234,11 @@ STATIC void super_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind mp_print_str(print, ">"); } -STATIC mp_obj_t super_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { +STATIC mp_obj_t super_make_new(const mp_obj_type_t *type_in, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { (void)type_in; // 0 arguments are turned into 2 in the compiler // 1 argument is not yet implemented - mp_arg_check_num(n_args, n_kw, 2, 2, false); + mp_arg_check_num(n_args, kw_args, 2, 2, false); if(!MP_OBJ_IS_TYPE(args[0], &mp_type_type)) { mp_raise_TypeError(translate("first argument to super() must be type")); } @@ -1394,11 +1432,14 @@ STATIC mp_obj_t mp_builtin_isinstance(mp_obj_t object, mp_obj_t classinfo) { MP_DEFINE_CONST_FUN_OBJ_2(mp_builtin_isinstance_obj, mp_builtin_isinstance); -mp_obj_t mp_instance_cast_to_native_base(mp_const_obj_t self_in, mp_const_obj_t native_type) { +mp_obj_t mp_instance_cast_to_native_base(mp_obj_t self_in, mp_const_obj_t native_type) { mp_obj_type_t *self_type = mp_obj_get_type(self_in); if (!mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(self_type), native_type)) { return MP_OBJ_NULL; } + if (MP_OBJ_FROM_PTR(self_type) == native_type) { + return self_in; + } mp_obj_instance_t *self = (mp_obj_instance_t*)MP_OBJ_TO_PTR(self_in); return self->subobj[0]; } @@ -1406,10 +1447,10 @@ mp_obj_t mp_instance_cast_to_native_base(mp_const_obj_t self_in, mp_const_obj_t /******************************************************************************/ // staticmethod and classmethod types (probably should go in a different file) -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, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { assert(self == &mp_type_staticmethod || self == &mp_type_classmethod); - mp_arg_check_num(n_args, n_kw, 1, 1, false); + mp_arg_check_num(n_args, kw_args, 1, 1, false); mp_obj_static_class_method_t *o = m_new_obj(mp_obj_static_class_method_t); *o = (mp_obj_static_class_method_t){{self}, args[0]}; diff --git a/py/objtype.h b/py/objtype.h index 3fc8c6e1b..a32c87496 100644 --- a/py/objtype.h +++ b/py/objtype.h @@ -37,6 +37,8 @@ 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); @@ -49,6 +51,6 @@ mp_obj_t mp_obj_instance_call(mp_obj_t self_in, size_t n_args, size_t n_kw, cons #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 *self_in, size_t n_args, size_t n_kw, const mp_obj_t *args); +mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self_in, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args); #endif // MICROPY_INCLUDED_PY_OBJTYPE_H diff --git a/py/objzip.c b/py/objzip.c index 0183925e3..ce9afd55d 100644 --- a/py/objzip.c +++ b/py/objzip.c @@ -36,8 +36,8 @@ typedef struct _mp_obj_zip_t { mp_obj_t iters[]; } mp_obj_zip_t; -STATIC mp_obj_t zip_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { - mp_arg_check_num(n_args, n_kw, 0, MP_OBJ_FUN_ARGS_MAX, false); +STATIC mp_obj_t zip_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + mp_arg_check_num(n_args, kw_args, 0, MP_OBJ_FUN_ARGS_MAX, false); mp_obj_zip_t *o = m_new_obj_var(mp_obj_zip_t, mp_obj_t, n_args); o->base.type = type; diff --git a/py/parse.c b/py/parse.c index 911b891e0..b8cfda2cb 100644 --- a/py/parse.c +++ b/py/parse.c @@ -477,6 +477,9 @@ STATIC void push_result_token(parser_t *parser, uint8_t rule_id) { mp_parse_node_t pn; mp_lexer_t *lex = parser->lexer; if (lex->tok_kind == MP_TOKEN_NAME) { + if(lex->vstr.len >= (1 << (8 * MICROPY_QSTR_BYTES_IN_LEN))) { + mp_raise_msg(&mp_type_SyntaxError, translate("Name too long")); + } qstr id = qstr_from_strn(lex->vstr.buf, lex->vstr.len); #if MICROPY_COMP_CONST // if name is a standalone identifier, look it up in the table of dynamic constants @@ -921,6 +924,7 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { backtrack = false; } for (; i < n; ++i) { + //printf("--> inside for @L924\n"); uint16_t kind = rule_arg[i] & RULE_ARG_KIND_MASK; if (kind == RULE_ARG_TOK) { if (lex->tok_kind == (rule_arg[i] & RULE_ARG_ARG_MASK)) { @@ -1165,15 +1169,57 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { ) { syntax_error:; mp_obj_t exc; - if (lex->tok_kind == MP_TOKEN_INDENT) { - exc = mp_obj_new_exception_msg(&mp_type_IndentationError, - translate("unexpected indent")); - } else if (lex->tok_kind == MP_TOKEN_DEDENT_MISMATCH) { - exc = mp_obj_new_exception_msg(&mp_type_IndentationError, - translate("unindent does not match any outer indentation level")); - } else { - exc = mp_obj_new_exception_msg(&mp_type_SyntaxError, - translate("invalid syntax")); + switch(lex->tok_kind) { + case MP_TOKEN_INDENT: + exc = mp_obj_new_exception_msg(&mp_type_IndentationError, + translate("unexpected indent")); + break; + case MP_TOKEN_DEDENT_MISMATCH: + exc = mp_obj_new_exception_msg(&mp_type_IndentationError, + translate("unindent does not match any outer indentation level")); + break; +#if MICROPY_COMP_FSTRING_LITERAL +#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED + case MP_TOKEN_FSTRING_BACKSLASH: + exc = mp_obj_new_exception_msg(&mp_type_SyntaxError, + translate("f-string expression part cannot include a backslash")); + break; + case MP_TOKEN_FSTRING_COMMENT: + exc = mp_obj_new_exception_msg(&mp_type_SyntaxError, + translate("f-string expression part cannot include a '#'")); + break; + case MP_TOKEN_FSTRING_UNCLOSED: + exc = mp_obj_new_exception_msg(&mp_type_SyntaxError, + translate("f-string: expecting '}'")); + break; + case MP_TOKEN_FSTRING_UNOPENED: + exc = mp_obj_new_exception_msg(&mp_type_SyntaxError, + translate("f-string: single '}' is not allowed")); + break; + case MP_TOKEN_FSTRING_EMPTY_EXP: + exc = mp_obj_new_exception_msg(&mp_type_SyntaxError, + translate("f-string: empty expression not allowed")); + break; + case MP_TOKEN_FSTRING_RAW: + exc = mp_obj_new_exception_msg(&mp_type_NotImplementedError, + translate("raw f-strings are not implemented")); + break; +#else + case MP_TOKEN_FSTRING_BACKSLASH: + case MP_TOKEN_FSTRING_COMMENT: + case MP_TOKEN_FSTRING_UNCLOSED: + case MP_TOKEN_FSTRING_UNOPENED: + case MP_TOKEN_FSTRING_EMPTY_EXP: + case MP_TOKEN_FSTRING_RAW: + exc = mp_obj_new_exception_msg(&mp_type_SyntaxError, + translate("malformed f-string")); + break; +#endif +#endif + default: + exc = mp_obj_new_exception_msg(&mp_type_SyntaxError, + translate("invalid syntax")); + break; } // add traceback to give info about file name and location // we don't have a 'block' name, so just pass the NULL qstr to indicate this diff --git a/py/persistentcode.c b/py/persistentcode.c index b44b5e38d..eb69bd407 100644 --- a/py/persistentcode.c +++ b/py/persistentcode.c @@ -102,20 +102,35 @@ STATIC void extract_prelude(const byte **ip, const byte **ip2, bytecode_prelude_ #include "py/parsenum.h" +STATIC void raise_corrupt_mpy(void) { + mp_raise_RuntimeError(translate("Corrupt .mpy file")); +} + STATIC int read_byte(mp_reader_t *reader) { - return reader->readbyte(reader->data); + mp_uint_t b = reader->readbyte(reader->data); + if (b == MP_READER_EOF) { + raise_corrupt_mpy(); + } + return b; } STATIC void read_bytes(mp_reader_t *reader, byte *buf, size_t len) { while (len-- > 0) { - *buf++ = reader->readbyte(reader->data); + mp_uint_t b =reader->readbyte(reader->data); + if (b == MP_READER_EOF) { + raise_corrupt_mpy(); + } + *buf++ = b; } } STATIC size_t read_uint(mp_reader_t *reader) { size_t unum = 0; for (;;) { - byte b = reader->readbyte(reader->data); + mp_uint_t b = reader->readbyte(reader->data); + if (b == MP_READER_EOF) { + raise_corrupt_mpy(); + } unum = (unum << 7) | (b & 0x7f); if ((b & 0x80) == 0) { break; @@ -145,11 +160,12 @@ STATIC mp_obj_t load_obj(mp_reader_t *reader) { return mp_obj_new_str_from_vstr(obj_type == 's' ? &mp_type_str : &mp_type_bytes, &vstr); } else if (obj_type == 'i') { return mp_parse_num_integer(vstr.buf, vstr.len, 10, NULL); - } else { - assert(obj_type == 'f' || obj_type == 'c'); + } else if (obj_type == 'f' || obj_type == 'c') { return mp_parse_num_decimal(vstr.buf, vstr.len, obj_type == 'c', false, NULL); } } + raise_corrupt_mpy(); + return MP_OBJ_FROM_PTR(&mp_const_none_obj); } STATIC void load_bytecode_qstrs(mp_reader_t *reader, byte *ip, byte *ip_top) { @@ -220,7 +236,7 @@ mp_raw_code_t *mp_raw_code_load(mp_reader_t *reader) { || header[1] != MPY_VERSION || header[2] != MPY_FEATURE_FLAGS || header[3] > mp_small_int_bits()) { - mp_raise_ValueError(translate("Incompatible .mpy file. Please update all .mpy files. See http://adafru.it/mpy-update for more info.")); + mp_raise_MpyError(translate("Incompatible .mpy file. Please update all .mpy files. See http://adafru.it/mpy-update for more info.")); } mp_raw_code_t *rc = load_raw_code(reader); reader->close(reader->data); diff --git a/py/proto.c b/py/proto.c new file mode 100644 index 000000000..e5053130b --- /dev/null +++ b/py/proto.c @@ -0,0 +1,50 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Jeff Epler for Adafruit Industries + * + * 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/obj.h" +#include "py/proto.h" +#include "py/runtime.h" + +#ifndef MICROPY_UNSAFE_PROTO +const void *mp_proto_get(uint16_t name, mp_const_obj_t obj) { + mp_obj_type_t *type = mp_obj_get_type(obj); + if (!type->protocol) return NULL; + uint16_t proto_name = *(const uint16_t*) type->protocol; + if (proto_name == name) { + return type->protocol; + } + return NULL; +} +#endif + +const void *mp_proto_get_or_throw(uint16_t name, mp_const_obj_t obj) { + const void *proto = mp_proto_get(name, obj); + if (proto) { + return proto; + } + mp_raise_TypeError_varg(translate("'%s' object does not support '%q'"), + mp_obj_get_type_str(obj), name); +} diff --git a/py/proto.h b/py/proto.h new file mode 100644 index 000000000..fadf1f882 --- /dev/null +++ b/py/proto.h @@ -0,0 +1,42 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Jeff Epler for Adafruit Industries + * + * 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_PY_PROTO_H +#define MICROPY_INCLUDED_PY_PROTO_H + +#ifdef MICROPY_UNSAFE_PROTO +#define MP_PROTOCOL_HEAD /* NOTHING */ +#define MP_PROTO_IMPLEMENT(name) /* NOTHING */ +static inline void *mp_proto_get(uint16_t name, mp_const_obj_type_t obj) { return mp_obj_get_type(obj)->protocol; } +#else +#define MP_PROTOCOL_HEAD \ + uint16_t name; // The name of this protocol, a qstr +#define MP_PROTO_IMPLEMENT(n) .name = n, +const void *mp_proto_get(uint16_t name, mp_const_obj_t obj); +const void *mp_proto_get_or_throw(uint16_t name, mp_const_obj_t obj); +#endif + +#endif @@ -105,6 +105,30 @@ $(BUILD)/$(BTREE_DIR)/%.o: CFLAGS += -Wno-old-style-definition -Wno-sign-compare $(BUILD)/extmod/modbtree.o: CFLAGS += $(BTREE_DEFS) endif +ifeq ($(CIRCUITPY_ULAB),1) +SRC_MOD += $(patsubst $(TOP)/%,%,$(wildcard $(TOP)/extmod/ulab/code/*.c)) +CFLAGS_MOD += -DCIRCUITPY_ULAB=1 -DMODULE_ULAB_ENABLED=1 +$(BUILD)/extmod/ulab/code/%.o: CFLAGS += -Wno-float-equal -Wno-sign-compare -DCIRCUITPY +endif + +# External modules written in C. +ifneq ($(USER_C_MODULES),) +# pre-define USERMOD variables as expanded so that variables are immediate +# expanded as they're added to them +SRC_USERMOD := +CFLAGS_USERMOD := +LDFLAGS_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)) +CFLAGS_MOD += $(CFLAGS_USERMOD) +LDFLAGS_MOD += $(LDFLAGS_USERMOD) +endif + # py object files PY_CORE_O_BASENAME = $(addprefix py/,\ mpstate.o \ @@ -196,6 +220,7 @@ PY_CORE_O_BASENAME = $(addprefix py/,\ objtype.o \ objzip.o \ opmethods.o \ + proto.o \ reload.o \ sequence.o \ stream.o \ @@ -221,6 +246,7 @@ PY_CORE_O_BASENAME = $(addprefix py/,\ repl.o \ smallint.o \ frozenmod.o \ + ringbuf.o \ ) PY_EXTMOD_O_BASENAME = \ @@ -233,12 +259,6 @@ PY_EXTMOD_O_BASENAME = \ extmod/moduhashlib.o \ extmod/modubinascii.o \ extmod/virtpin.o \ - extmod/machine_mem.o \ - extmod/machine_pinbase.o \ - extmod/machine_signal.o \ - extmod/machine_pulse.o \ - extmod/machine_i2c.o \ - extmod/machine_spi.o \ extmod/modussl_axtls.o \ extmod/modussl_mbedtls.o \ extmod/modurandom.o \ @@ -291,13 +311,20 @@ FORCE: $(HEADER_BUILD)/mpversion.h: FORCE | $(HEADER_BUILD) $(STEPECHO) "GEN $@" - $(Q)$(PYTHON) $(PY_SRC)/makeversionhdr.py $@ + $(Q)$(PYTHON3) $(PY_SRC)/makeversionhdr.py $@ + +# build a list of registered modules for py/objmodule.c. +$(HEADER_BUILD)/moduledefs.h: $(SRC_QSTR) $(QSTR_GLOBAL_DEPENDENCIES) | $(HEADER_BUILD)/mpversion.h + @$(STEPECHO) "GEN $@" + $(Q)$(PYTHON3) $(PY_SRC)/makemoduledefs.py --vpath="., $(TOP), $(USER_C_MODULES)" $(SRC_QSTR) > $@ + +SRC_QSTR += $(HEADER_BUILD)/moduledefs.h # mpconfigport.mk is optional, but changes to it may drastically change # overall config, so they need to be caught MPCONFIGPORT_MK = $(wildcard mpconfigport.mk) -$(HEADER_BUILD)/$(TRANSLATION).mo: $(TOP)/locale/$(TRANSLATION).po +$(HEADER_BUILD)/$(TRANSLATION).mo: $(TOP)/locale/$(TRANSLATION).po | $(HEADER_BUILD) $(Q)msgfmt -o $@ $^ $(HEADER_BUILD)/qstrdefs.preprocessed.h: $(PY_QSTR_DEFS) $(QSTR_DEFS) $(QSTR_DEFS_COLLECTED) mpconfigport.h $(MPCONFIGPORT_MK) $(PY_SRC)/mpconfig.h | $(HEADER_BUILD) @@ -324,10 +351,23 @@ $(PY_BUILD)/qstr.o: $(HEADER_BUILD)/qstrdefs.generated.h $(PY_BUILD)/nlr%.o: CFLAGS += -Os # 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 @@ -33,6 +33,8 @@ #include "py/qstr.h" #include "py/gc.h" +#include "supervisor/linker.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 // also probably need to include the length in the string data, to allow null bytes in the string @@ -248,7 +250,7 @@ qstr qstr_from_strn(const char *str, size_t len) { return q; } -mp_uint_t qstr_hash(qstr q) { +mp_uint_t PLACE_IN_ITCM(qstr_hash)(qstr q) { return Q_GET_HASH(find_qstr(q)); } @@ -187,6 +187,11 @@ size_t mp_repl_autocomplete(const char *str, size_t len, const mp_print_t *print if (s_len <= d_len && strncmp(s_start, d_str, s_len) == 0) { mp_load_method_protected(obj, q, dest, true); if (dest[0] != MP_OBJ_NULL) { + // special case; filter out words that begin with underscore + // unless there's already a partial match + if (s_len == 0 && d_str[0] == '_') { + continue; + } if (match_str == NULL) { match_str = d_str; match_len = d_len; @@ -210,6 +215,10 @@ size_t mp_repl_autocomplete(const char *str, size_t len, const mp_print_t *print // nothing found if (q_first == 0) { + if (s_len == 0) { + *compl_str = " "; + return 4; + } // If there're no better alternatives, and if it's first word // in the line, try to complete "import". if (s_start == org_str) { diff --git a/py/ringbuf.c b/py/ringbuf.c new file mode 100644 index 000000000..c19f1d44b --- /dev/null +++ b/py/ringbuf.c @@ -0,0 +1,115 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Paul Sokolovsky + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "ringbuf.h" + +// Dynamic initialization. This should be accessible from a root pointer. +// capacity is the number of bytes the ring buffer can hold. The actual +// size of the buffer is one greater than that, due to how the buffer +// handles empty and full statuses. +bool ringbuf_alloc(ringbuf_t *r, size_t capacity, bool long_lived) { + r->buf = gc_alloc(capacity + 1, false, long_lived); + r->size = capacity + 1; + r->iget = r->iput = 0; + return r->buf != NULL; +} + +void ringbuf_free(ringbuf_t *r) { + gc_free(r->buf); + r->size = 0; + ringbuf_clear(r); +} + +size_t ringbuf_capacity(ringbuf_t *r) { + return r->size - 1; +} + +// Returns -1 if buffer is empty, else returns byte fetched. +int ringbuf_get(ringbuf_t *r) { + if (r->iget == r->iput) { + return -1; + } + uint8_t v = r->buf[r->iget++]; + if (r->iget >= r->size) { + r->iget = 0; + } + return v; +} + +// Returns -1 if no room in buffer, else returns 0. +int ringbuf_put(ringbuf_t *r, uint8_t v) { + uint32_t iput_new = r->iput + 1; + if (iput_new >= r->size) { + iput_new = 0; + } + if (iput_new == r->iget) { + return -1; + } + r->buf[r->iput] = v; + r->iput = iput_new; + return 0; +} + +void ringbuf_clear(ringbuf_t *r) { + r->iput = r->iget = 0; +} + +// Number of free slots that can be written. +size_t ringbuf_num_empty(ringbuf_t *r) { + return (r->size + r->iget - r->iput - 1) % r->size; +} + +// Number of bytes available to read. +size_t ringbuf_num_filled(ringbuf_t *r) { + return (r->size + r->iput - r->iget) % r->size; +} + +// If the ring buffer fills up, not all bytes will be written. +// Returns how many bytes were successfully written. +size_t ringbuf_put_n(ringbuf_t* r, uint8_t* buf, size_t bufsize) +{ + for(size_t i=0; i < bufsize; i++) { + if ( ringbuf_put(r, buf[i]) < 0 ) { + // If ringbuf is full, give up and return how many bytes + // we wrote so far. + return i; + } + } + return bufsize; +} + +// Returns how many bytes were fetched. +size_t ringbuf_get_n(ringbuf_t* r, uint8_t* buf, size_t bufsize) +{ + for(size_t i=0; i < bufsize; i++) { + int b = ringbuf_get(r); + if (b < 0) { + return i; + } + buf[i] = b; + } + return bufsize; +} diff --git a/py/ringbuf.h b/py/ringbuf.h index c62e20e1d..476bd428f 100644 --- a/py/ringbuf.h +++ b/py/ringbuf.h @@ -26,49 +26,33 @@ #ifndef MICROPY_INCLUDED_PY_RINGBUF_H #define MICROPY_INCLUDED_PY_RINGBUF_H +#include "py/gc.h" + #include <stdint.h> typedef struct _ringbuf_t { uint8_t *buf; - uint16_t size; - uint16_t iget; - uint16_t iput; + // Allocated size; capacity is one less. Don't reference this directly. + uint32_t size; + uint32_t iget; + uint32_t iput; } ringbuf_t; +// Note that the capacity of the buffer is N-1! + // Static initialization: // byte buf_array[N]; // ringbuf_t buf = {buf_array, sizeof(buf_array)}; -// Dynamic initialization. This creates root pointer! -#define ringbuf_alloc(r, sz) \ -{ \ - (r)->buf = m_new(uint8_t, sz); \ - (r)->size = sz; \ - (r)->iget = (r)->iput = 0; \ -} - -static inline int ringbuf_get(ringbuf_t *r) { - if (r->iget == r->iput) { - return -1; - } - uint8_t v = r->buf[r->iget++]; - if (r->iget >= r->size) { - r->iget = 0; - } - return v; -} - -static inline int ringbuf_put(ringbuf_t *r, uint8_t v) { - uint32_t iput_new = r->iput + 1; - if (iput_new >= r->size) { - iput_new = 0; - } - if (iput_new == r->iget) { - return -1; - } - r->buf[r->iput] = v; - r->iput = iput_new; - return 0; -} +bool ringbuf_alloc(ringbuf_t *r, size_t capacity, bool long_lived); +void ringbuf_free(ringbuf_t *r); +size_t ringbuf_capacity(ringbuf_t *r); +int ringbuf_get(ringbuf_t *r); +int ringbuf_put(ringbuf_t *r, uint8_t v); +void ringbuf_clear(ringbuf_t *r); +size_t ringbuf_num_empty(ringbuf_t *r); +size_t ringbuf_num_filled(ringbuf_t *r); +size_t ringbuf_put_n(ringbuf_t* r, uint8_t* buf, size_t bufsize); +size_t ringbuf_get_n(ringbuf_t* r, uint8_t* buf, size_t bufsize); #endif // MICROPY_INCLUDED_PY_RINGBUF_H diff --git a/py/runtime.c b/py/runtime.c index 3d7a97f58..59dcbc7a1 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -29,12 +29,12 @@ #include <string.h> #include <assert.h> + #include "extmod/vfs.h" -// #include "py/mpstate.h" -// #include "py/nlr.h" #include "py/parsenum.h" #include "py/compile.h" +#include "py/mperrno.h" #include "py/objstr.h" #include "py/objtuple.h" #include "py/objtype.h" @@ -131,31 +131,6 @@ void mp_init(void) { sizeof(MP_STATE_VM(fs_user_mount)) - MICROPY_FATFS_NUM_PERSISTENT); #endif - #if MICROPY_VFS - #if MICROPY_FATFS_NUM_PERSISTENT > 0 - // We preserve the last MICROPY_FATFS_NUM_PERSISTENT mounts because newer - // mounts are put at the front of the list. - mp_vfs_mount_t *vfs = MP_STATE_VM(vfs_mount_table); - // Count how many mounts we have. - uint8_t count = 0; - while (vfs != NULL) { - vfs = vfs->next; - count++; - } - // Find the vfs MICROPY_FATFS_NUM_PERSISTENT mounts from the end. - vfs = MP_STATE_VM(vfs_mount_table); - for (uint8_t j = 0; j < count - MICROPY_FATFS_NUM_PERSISTENT; j++) { - vfs = vfs->next; - } - MP_STATE_VM(vfs_mount_table) = vfs; - MP_STATE_VM(vfs_cur) = vfs; - #else - // initialise the VFS sub-system - MP_STATE_VM(vfs_cur) = NULL; - MP_STATE_VM(vfs_mount_table) = NULL; - #endif - #endif - #if MICROPY_PY_THREAD_GIL mp_thread_mutex_init(&MP_STATE_VM(gil_mutex)); #endif @@ -227,7 +202,7 @@ mp_obj_t mp_load_build_class(void) { return MP_OBJ_FROM_PTR(&mp_builtin___build_class___obj); } -void mp_store_name(qstr qst, mp_obj_t obj) { +void PLACE_IN_ITCM(mp_store_name)(qstr qst, mp_obj_t obj) { DEBUG_OP_printf("store name %s <- %p\n", qstr_str(qst), obj); mp_obj_dict_store(MP_OBJ_FROM_PTR(mp_locals_get()), MP_OBJ_NEW_QSTR(qst), obj); } @@ -238,7 +213,7 @@ void mp_delete_name(qstr qst) { mp_obj_dict_delete(MP_OBJ_FROM_PTR(mp_locals_get()), MP_OBJ_NEW_QSTR(qst)); } -void mp_store_global(qstr qst, mp_obj_t obj) { +void PLACE_IN_ITCM(mp_store_global)(qstr qst, mp_obj_t obj) { DEBUG_OP_printf("store global %s <- %p\n", qstr_str(qst), obj); mp_obj_dict_store(MP_OBJ_FROM_PTR(mp_globals_get()), MP_OBJ_NEW_QSTR(qst), obj); } @@ -310,7 +285,7 @@ mp_obj_t mp_unary_op(mp_unary_op_t op, mp_obj_t arg) { } } -mp_obj_t mp_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) { +mp_obj_t PLACE_IN_ITCM(mp_binary_op)(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) { DEBUG_OP_printf("binary " UINT_FMT " %q %p %p\n", op, mp_binary_op_method_name[op], lhs, rhs); // TODO correctly distinguish inplace operators for mutable objects @@ -668,7 +643,7 @@ mp_obj_t mp_call_method_n_kw(size_t n_args, size_t n_kw, const mp_obj_t *args) { #if !MICROPY_STACKLESS STATIC #endif -void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_obj_t *args, mp_call_args_t *out_args) { +void PLACE_IN_ITCM(mp_call_prepare_args_n_kw_var)(bool have_self, size_t n_args_n_kw, const mp_obj_t *args, mp_call_args_t *out_args) { mp_obj_t fun = *args++; mp_obj_t self = MP_OBJ_NULL; if (have_self) { @@ -1197,7 +1172,7 @@ void mp_store_attr(mp_obj_t base, qstr attr, mp_obj_t value) { mp_raise_AttributeError(translate("no such attribute")); } else { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_AttributeError, - translate("'%s' object has no attribute '%q'"), + translate("'%s' object cannot assign attribute '%q'"), mp_obj_get_type_str(base), attr)); } } @@ -1599,11 +1574,50 @@ NORETURN void mp_raise_OSError(int errno_) { nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(errno_))); } +NORETURN void mp_raise_OSError_msg(const compressed_string_t *msg) { + mp_raise_msg(&mp_type_OSError, msg); +} + +NORETURN void mp_raise_OSError_errno_str(int errno_, mp_obj_t str) { + mp_obj_t args[2] = { + MP_OBJ_NEW_SMALL_INT(errno_), + str, + }; + nlr_raise(mp_obj_new_exception_args(&mp_type_OSError, 2, args)); +} + +NORETURN void mp_raise_OSError_msg_varg(const compressed_string_t *fmt, ...) { + va_list argptr; + va_start(argptr,fmt); + mp_obj_t exception = mp_obj_new_exception_msg_vlist(&mp_type_OSError, fmt, argptr); + va_end(argptr); + nlr_raise(exception); +} NORETURN void mp_raise_NotImplementedError(const compressed_string_t *msg) { mp_raise_msg(&mp_type_NotImplementedError, msg); } +NORETURN void mp_raise_NotImplementedError_varg(const compressed_string_t *fmt, ...) { + va_list argptr; + va_start(argptr,fmt); + mp_obj_t exception = mp_obj_new_exception_msg_vlist(&mp_type_NotImplementedError, fmt, argptr); + va_end(argptr); + nlr_raise(exception); +} + +NORETURN void mp_raise_OverflowError_varg(const compressed_string_t *fmt, ...) { + va_list argptr; + va_start(argptr,fmt); + mp_obj_t exception = mp_obj_new_exception_msg_vlist(&mp_type_OverflowError, fmt, argptr); + va_end(argptr); + nlr_raise(exception); +} + +NORETURN void mp_raise_MpyError(const compressed_string_t *msg) { + mp_raise_msg(&mp_type_MpyError, msg); +} + #if MICROPY_STACK_CHECK || MICROPY_ENABLE_PYSTACK NORETURN void mp_raise_recursion_depth(void) { mp_raise_RuntimeError(translate("maximum recursion depth exceeded")); diff --git a/py/runtime.h b/py/runtime.h index 9c7ffc02f..f81103557 100644 --- a/py/runtime.h +++ b/py/runtime.h @@ -29,6 +29,8 @@ #include "py/mpstate.h" #include "py/pystack.h" +#include "supervisor/linker.h" + typedef enum { MP_VM_RETURN_NORMAL, MP_VM_RETURN_YIELD, @@ -77,16 +79,17 @@ bool mp_sched_schedule(mp_obj_t function, mp_obj_t arg); // extra printing method specifically for mp_obj_t's which are integral type int mp_print_mp_int(const mp_print_t *print, mp_obj_t x, int base, int base_char, int flags, char fill, int width, int prec); -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); +void mp_arg_check_num(size_t n_args, mp_map_t *kw_args, size_t n_args_min, size_t n_args_max, bool 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_check_num_kw_array(size_t n_args, size_t n_kw, size_t n_args_min, size_t n_args_max, bool takes_kw); 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); NORETURN void mp_arg_error_terse_mismatch(void); NORETURN void mp_arg_error_unimpl_kw(void); -static inline mp_obj_dict_t *mp_locals_get(void) { return MP_STATE_THREAD(dict_locals); } -static inline void mp_locals_set(mp_obj_dict_t *d) { MP_STATE_THREAD(dict_locals) = d; } -static inline mp_obj_dict_t *mp_globals_get(void) { return MP_STATE_THREAD(dict_globals); } -static inline void mp_globals_set(mp_obj_dict_t *d) { MP_STATE_THREAD(dict_globals) = d; } +static inline mp_obj_dict_t *PLACE_IN_ITCM(mp_locals_get)(void) { return MP_STATE_THREAD(dict_locals); } +static inline void PLACE_IN_ITCM(mp_locals_set)(mp_obj_dict_t *d) { MP_STATE_THREAD(dict_locals) = d; } +static inline mp_obj_dict_t *PLACE_IN_ITCM(mp_globals_get)(void) { return MP_STATE_THREAD(dict_globals); } +static inline void PLACE_IN_ITCM(mp_globals_set)(mp_obj_dict_t *d) { MP_STATE_THREAD(dict_globals) = d; } mp_obj_t mp_load_name(qstr qst); mp_obj_t mp_load_global(qstr qst); @@ -158,7 +161,13 @@ NORETURN void mp_raise_RuntimeError(const compressed_string_t *msg); NORETURN void mp_raise_ImportError(const compressed_string_t *msg); NORETURN void mp_raise_IndexError(const compressed_string_t *msg); 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_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_MpyError(const compressed_string_t *msg); NORETURN void mp_raise_recursion_depth(void); #if MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG diff --git a/py/sequence.c b/py/sequence.c index cc31be983..0e668ea2a 100644 --- a/py/sequence.c +++ b/py/sequence.c @@ -34,6 +34,20 @@ #define SWAP(type, var1, var2) { type t = var2; var2 = var1; var1 = t; } +#if __GNUC__ < 5 +// n.b. does not actually detect overflow! +#define __builtin_mul_overflow(a, b, x) (*(x) = (a) * (b), false) +#endif + +// Detect when a multiply causes an overflow. +size_t mp_seq_multiply_len(size_t item_sz, size_t len) { + size_t new_len; + if (__builtin_mul_overflow(item_sz, len, &new_len)) { + mp_raise_msg(&mp_type_OverflowError, translate("small int overflow")); + } + return new_len; +} + // Implements backend of sequence * integer operation. Assumes elements are // memory-adjacent in sequence. void mp_seq_multiply(const void *items, size_t item_sz, size_t len, size_t times, void *dest) { diff --git a/py/showbc.c b/py/showbc.c index 3deb18cd3..e71d8a253 100644 --- a/py/showbc.c +++ b/py/showbc.c @@ -185,7 +185,7 @@ const byte *mp_bytecode_print_str(const byte *ip) { num--; } do { - num = (num << 7) | (*ip & 0x7f); + num = (num * 128) | (*ip & 0x7f); } while ((*ip++ & 0x80) != 0); printf("LOAD_CONST_SMALL_INT " INT_FMT, num); break; diff --git a/py/stackctrl.c b/py/stackctrl.c index 46cbefc8c..26fc065b7 100644 --- a/py/stackctrl.c +++ b/py/stackctrl.c @@ -77,7 +77,7 @@ void mp_stack_set_bottom(void* stack_bottom) { // // The stack_dummy approach used elsewhere in this file is not safe in // all cases. That value may be below the actual top of the stack. -static void* approx_stack_pointer(void){ +static void* approx_stack_pointer(void){ __asm volatile (""); return __builtin_frame_address(0); } @@ -90,7 +90,7 @@ void mp_stack_fill_with_sentinel(void) { // Continue until we've hit the bottom of the stack (lowest address, // logical "ceiling" of stack). char* p = (char *) approx_stack_pointer() - 1; - + while(p >= MP_STATE_THREAD(stack_bottom)) { *p-- = MP_MAX_STACK_USAGE_SENTINEL_BYTE; } diff --git a/py/stream.c b/py/stream.c index 8cf375b16..2ff2b76a6 100644 --- a/py/stream.c +++ b/py/stream.c @@ -86,8 +86,7 @@ mp_uint_t mp_stream_rw(mp_obj_t stream, void *buf_, mp_uint_t size, int *errcode } const mp_stream_p_t *mp_get_stream_raise(mp_obj_t self_in, int flags) { - mp_obj_type_t *type = mp_obj_get_type(self_in); - const mp_stream_p_t *stream_p = type->protocol; + 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) @@ -103,7 +102,7 @@ STATIC mp_obj_t stream_read_generic(size_t n_args, const mp_obj_t *args, byte fl // CPython does a readall, but here we silently let negatives through, // and they will cause a MemoryError. mp_int_t sz; - if (n_args == 1 || ((sz = mp_obj_get_int(args[1])) == -1)) { + if (n_args == 1 || args[1] == mp_const_none || ((sz = mp_obj_get_int(args[1])) == -1)) { return stream_readall(args[0]); } @@ -250,6 +249,9 @@ 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(translate("string not supported; use bytes or bytearray")); + } size_t max_len = (size_t)-1; size_t off = 0; if (n_args == 3) { @@ -282,6 +284,9 @@ 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_compatibility) { + mp_raise_ValueError(translate("length argument not allowed for this type")); + } len = mp_obj_get_int(args[2]); if (len > bufinfo.len) { len = bufinfo.len; @@ -335,6 +340,9 @@ 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; @@ -462,16 +470,19 @@ STATIC mp_obj_t stream_tell(mp_obj_t self) { } MP_DEFINE_CONST_FUN_OBJ_1(mp_stream_tell_obj, stream_tell); -STATIC mp_obj_t stream_flush(mp_obj_t self) { +mp_obj_t mp_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, stream_flush); +MP_DEFINE_CONST_FUN_OBJ_1(mp_stream_flush_obj, mp_stream_flush); STATIC mp_obj_t stream_ioctl(size_t n_args, const mp_obj_t *args) { mp_buffer_info_t bufinfo; @@ -510,7 +521,7 @@ int mp_stream_errno; 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 = o->type->protocol; + const mp_stream_p_t *stream_p = mp_get_stream(o); mp_uint_t out_sz = stream_p->write(stream, buf, len, &mp_stream_errno); if (out_sz == MP_STREAM_ERROR) { return -1; @@ -521,7 +532,7 @@ 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 = o->type->protocol; + const mp_stream_p_t *stream_p = mp_get_stream(o); mp_uint_t out_sz = stream_p->read(stream, buf, len, &mp_stream_errno); if (out_sz == MP_STREAM_ERROR) { return -1; @@ -532,7 +543,7 @@ 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 = o->type->protocol; + const mp_stream_p_t *stream_p = mp_get_stream(o); struct mp_stream_seek_t seek_s; seek_s.offset = offset; seek_s.whence = whence; @@ -545,7 +556,7 @@ off_t mp_stream_posix_lseek(mp_obj_t stream, off_t offset, int whence) { 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 = o->type->protocol; + const mp_stream_p_t *stream_p = mp_get_stream(o); mp_uint_t res = stream_p->ioctl(stream, MP_STREAM_FLUSH, 0, &mp_stream_errno); if (res == MP_STREAM_ERROR) { return -1; diff --git a/py/stream.h b/py/stream.h index 7b953138c..dc9fc84c9 100644 --- a/py/stream.h +++ b/py/stream.h @@ -27,6 +27,7 @@ #define MICROPY_INCLUDED_PY_STREAM_H #include "py/obj.h" +#include "py/proto.h" #include "py/mperrno.h" #define MP_STREAM_ERROR ((mp_uint_t)-1) @@ -64,12 +65,14 @@ struct mp_stream_seek_t { // Stream protocol typedef struct _mp_stream_p_t { + MP_PROTOCOL_HEAD // On error, functions should return MP_STREAM_ERROR and fill in *errcode (values // are implementation-dependent, but will be exposed to user, e.g. via exception). mp_uint_t (*read)(mp_obj_t obj, void *buf, mp_uint_t size, int *errcode); mp_uint_t (*write)(mp_obj_t obj, const void *buf, mp_uint_t size, int *errcode); mp_uint_t (*ioctl)(mp_obj_t obj, mp_uint_t request, uintptr_t arg, int *errcode); mp_uint_t is_text : 1; // default is bytes, set this for text stream + bool pyserial_compatibility: 1; // adjust API to match pyserial more closely } mp_stream_p_t; MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_read_obj); @@ -92,7 +95,7 @@ MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_ioctl_obj); // Object is assumed to have a non-NULL stream protocol with valid r/w/ioctl methods static inline const mp_stream_p_t *mp_get_stream(mp_const_obj_t self) { - return (const mp_stream_p_t*)((const mp_obj_base_t*)MP_OBJ_TO_PTR(self))->type->protocol; + 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); @@ -112,6 +115,7 @@ mp_uint_t mp_stream_rw(mp_obj_t stream, void *buf, mp_uint_t size, int *errcode, #define mp_stream_read_exactly(stream, buf, size, err) mp_stream_rw(stream, buf, size, err, MP_STREAM_RW_READ) void mp_stream_write_adaptor(void *self, const char *buf, size_t len); +mp_obj_t mp_stream_flush(mp_obj_t self); #if MICROPY_STREAMS_POSIX_API // Functions with POSIX-compatible signatures @@ -35,6 +35,8 @@ #include "py/bc0.h" #include "py/bc.h" +#include "supervisor/linker.h" + #if 0 #define TRACE(ip) printf("sp=%d ", (int)(sp - &code_state->state[0] + 1)); mp_bytecode_print2(ip, 1, code_state->fun_bc->const_table); #else @@ -116,7 +118,7 @@ // MP_VM_RETURN_NORMAL, sp valid, return value in *sp // MP_VM_RETURN_YIELD, ip, sp valid, yielded value in *sp // MP_VM_RETURN_EXCEPTION, exception in fastn[0] -mp_vm_return_kind_t mp_execute_bytecode(mp_code_state_t *code_state, volatile mp_obj_t inject_exc) { +mp_vm_return_kind_t PLACE_IN_ITCM(mp_execute_bytecode)(mp_code_state_t *code_state, volatile mp_obj_t inject_exc) { #define SELECTIVE_EXC_IP (0) #if SELECTIVE_EXC_IP #define MARK_EXC_IP_SELECTIVE() { code_state->ip = ip; } /* stores ip 1 byte past last opcode */ @@ -758,7 +760,7 @@ unwind_jump:; } else { PUSH(value); // push the next iteration value } - DISPATCH(); + DISPATCH_WITH_PEND_EXC_CHECK(); } // matched against: SETUP_EXCEPT, SETUP_FINALLY, SETUP_WITH diff --git a/py/vmentrytable.h b/py/vmentrytable.h index 615f4e2ce..31a96dbec 100644 --- a/py/vmentrytable.h +++ b/py/vmentrytable.h @@ -24,12 +24,14 @@ * THE SOFTWARE. */ -#if __clang__ +#ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Winitializer-overrides" #endif // __clang__ -static const void *const entry_table[256] = { +#include "supervisor/linker.h" + +static const void *const PLACE_IN_DTCM_DATA(entry_table[256]) = { [0 ... 255] = &&entry_default, [MP_BC_LOAD_CONST_FALSE] = &&entry_MP_BC_LOAD_CONST_FALSE, [MP_BC_LOAD_CONST_NONE] = &&entry_MP_BC_LOAD_CONST_NONE, @@ -113,6 +115,6 @@ static const void *const entry_table[256] = { [MP_BC_BINARY_OP_MULTI ... MP_BC_BINARY_OP_MULTI + MP_BINARY_OP_NUM_BYTECODE - 1] = &&entry_MP_BC_BINARY_OP_MULTI, }; -#if __clang__ +#ifdef __clang__ #pragma clang diagnostic pop #endif // __clang__ @@ -50,6 +50,9 @@ void vstr_init(vstr_t *vstr, size_t alloc) { // Init the vstr so it allocs exactly enough ram to hold a null-terminated // string of the given length, and set the length. void vstr_init_len(vstr_t *vstr, size_t len) { + if(len == SIZE_MAX) { + m_malloc_fail(len); + } vstr_init(vstr, len + 1); vstr->len = len; } |
