diff options
| author | TG-Techie <39284876+TG-Techie@users.noreply.github.com> | 2020-03-06 13:57:31 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-03-06 13:57:31 -0500 |
| commit | 7198056bc31ebae22337af119c07c7dca03e85d5 (patch) | |
| tree | 421b1c044250320ae15500f8578f6fec529937fa /py | |
| parent | 6d4ffc69fdfe19366b15861694f97cbf77452937 (diff) | |
| parent | ba0c14f9ddf01f6cf98d278cbc4769a476342db0 (diff) | |
Merge pull request #2 from adafruit/5.0.x
5.0.x
Diffstat (limited to 'py')
42 files changed, 521 insertions, 171 deletions
diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index 211e85368..68fa25d8d 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -117,6 +117,9 @@ 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 @@ -154,6 +157,9 @@ 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 @@ -233,6 +239,7 @@ SRC_COMMON_HAL_ALL = \ _bleio/CharacteristicBuffer.c \ _bleio/Connection.c \ _bleio/Descriptor.c \ + _bleio/PacketBuffer.c \ _bleio/Service.c \ _bleio/UUID.c \ analogio/AnalogIn.c \ @@ -294,7 +301,7 @@ $(filter $(SRC_PATTERNS), \ fontio/Glyph.c \ microcontroller/RunMode.c \ math/__init__.c \ - supervisor/__init__.c \ + _eve/__init__.c \ ) SRC_BINDINGS_ENUMS += \ @@ -319,6 +326,8 @@ SRC_SHARED_MODULE_ALL = \ audiomixer/__init__.c \ audiomixer/Mixer.c \ audiomixer/MixerVoice.c \ + audiomp3/__init__.c \ + audiomp3/MP3Decoder.c \ bitbangio/I2C.c \ bitbangio/OneWire.c \ bitbangio/SPI.c \ @@ -354,7 +363,8 @@ SRC_SHARED_MODULE_ALL = \ uheap/__init__.c \ ustack/__init__.c \ _pew/__init__.c \ - _pew/PewPew.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)) @@ -371,6 +381,26 @@ 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 # All possible sources are listed here, and are filtered by SRC_PATTERNS. SRC_SHARED_MODULE_INTERNAL = \ @@ -403,6 +433,18 @@ $(addprefix lib/,\ ) 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: diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index b1e7bc05a..53cd7b0bb 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -28,11 +28,12 @@ // sure that the same feature set and settings are used, such as in atmel-samd // and nrf. -#include <stdint.h> - #ifndef __INCLUDED_MPCONFIG_CIRCUITPY_H #define __INCLUDED_MPCONFIG_CIRCUITPY_H +#include <stdint.h> +#include <stdatomic.h> + // This is CircuitPython. #define CIRCUITPY 1 @@ -71,6 +72,7 @@ #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) @@ -139,7 +141,6 @@ #define MICROPY_VFS_FAT (MICROPY_VFS) #define MICROPY_READER_VFS (MICROPY_VFS) - // type definitions for the specific machine #define BYTES_PER_WORD (4) @@ -167,20 +168,25 @@ typedef long mp_off_t; #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 +////////////////////////////////////////////////////////////////////////////////////////////////// +// board-specific definitions, which control and may override definitions below. #include "mpconfigboard.h" // CIRCUITPY_FULL_BUILD is defined in a *.mk file. // Remove some lesser-used functionality to make small builds fit. #define MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG (CIRCUITPY_FULL_BUILD) -#define MICROPY_CPYTHON_COMPAT (CIRCUITPY_FULL_BUILD) +//TODO: replace this with a rework of the FULL_BUILD system +#if !defined(MICROPY_CPYTHON_COMPAT) + #define MICROPY_CPYTHON_COMPAT (CIRCUITPY_FULL_BUILD) +#endif #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) @@ -191,7 +197,9 @@ typedef long mp_off_t; #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) @@ -212,6 +220,9 @@ typedef long mp_off_t; #define MP_SSIZE_MAX (0x7fffffff) #endif +#if INTERNAL_FLASH_FILESYSTEM == 0 && QSPI_FLASH_FILESYSTEM == 0 && SPI_FLASH_FILESYSTEM == 0 && !CIRCUITPY_MINIMAL_BUILD +#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. @@ -251,6 +262,13 @@ extern const struct _mp_obj_module_t audiomixer_module; #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; @@ -314,7 +332,9 @@ 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 @@ -365,6 +385,13 @@ extern const struct _mp_obj_module_t math_module; #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 }, @@ -581,6 +608,7 @@ extern const struct _mp_obj_module_t ustack_module; AUDIOCORE_MODULE \ AUDIOIO_MODULE \ AUDIOMIXER_MODULE \ + AUDIOMP3_MODULE \ AUDIOPWMIO_MODULE \ BITBANGIO_MODULE \ BLEIO_MODULE \ @@ -597,6 +625,7 @@ extern const struct _mp_obj_module_t ustack_module; I2CSLAVE_MODULE \ JSON_MODULE \ MATH_MODULE \ + _EVE_MODULE \ MICROCONTROLLER_MODULE \ NEOPIXEL_WRITE_MODULE \ NETWORK_MODULE \ @@ -652,17 +681,26 @@ extern const struct _mp_obj_module_t ustack_module; FLASH_ROOT_POINTERS \ NETWORK_ROOT_POINTERS \ -void run_background_tasks(void); -#define RUN_BACKGROUND_TASKS (run_background_tasks()) +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(); +#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 index c1199571b..d6e033daf 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -23,7 +23,6 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. - # mpconfigboard.mk files can specify: # CIRCUITPY_FULL_BUILD = 1 (which is the default) # or @@ -34,15 +33,19 @@ ifndef CIRCUITPY_FULL_BUILD ifeq ($(CIRCUITPY_SMALL_BUILD),1) CIRCUITPY_FULL_BUILD = 0 - CFLAGS += -DCIRCUITPY_FULL_BUILD=0 else CIRCUITPY_FULL_BUILD = 1 - CFLAGS += -DCIRCUITPY_FULL_BUILD=1 endif endif +CFLAGS += -DCIRCUITPY_FULL_BUILD=$(CIRCUITPY_FULL_BUILD) # Setting CIRCUITPY_MINIMAL_BUILD = 1 will disable all features # Use for for early stage or highly restricted ports +ifndef CIRCUITPY_MINIMAL_BUILD +CIRCUITPY_MINIMAL_BUILD = 0 +endif +CFLAGS += -DCIRCUITPY_MINIMAL_BUILD=$(CIRCUITPY_MINIMAL_BUILD) + ifndef CIRCUITPY_DEFAULT_BUILD ifeq ($(CIRCUITPY_MINIMAL_BUILD),1) CIRCUITPY_FULL_BUILD = 0 @@ -51,12 +54,14 @@ ifndef CIRCUITPY_DEFAULT_BUILD CIRCUITPY_DEFAULT_BUILD = 1 endif endif +CFLAGS += -DCIRCUITPY_DEFAULT_BUILD=$(CIRCUITPY_DEFAULT_BUILD) # Some features have no unique HAL component, and thus there's never -# a reason to not include them. +# a reason to not include them. ifndef CIRCUITPY_ALWAYS_BUILD CIRCUITPY_ALWAYS_BUILD = 1 endif +CFLAGS += -DCIRCUITPY_ALWAYS_BUILD=$(CIRCUITPY_ALWAYS_BUILD) # All builtin modules are listed below, with default values (0 for off, 1 for on) @@ -104,6 +109,15 @@ CIRCUITPY_AUDIOMIXER = $(CIRCUITPY_AUDIOIO) endif 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) + ifndef CIRCUITPY_BITBANGIO CIRCUITPY_BITBANGIO = $(CIRCUITPY_FULL_BUILD) endif @@ -160,6 +174,11 @@ CIRCUITPY_MATH = $(CIRCUITPY_ALWAYS_BUILD) endif CFLAGS += -DCIRCUITPY_MATH=$(CIRCUITPY_MATH) +ifndef CIRCUITPY__EVE +CIRCUITPY__EVE = 0 +endif +CFLAGS += -DCIRCUITPY__EVE=$(CIRCUITPY__EVE) + ifndef CIRCUITPY_MICROCONTROLLER CIRCUITPY_MICROCONTROLLER = $(CIRCUITPY_DEFAULT_BUILD) endif @@ -170,7 +189,7 @@ CIRCUITPY_NEOPIXEL_WRITE = $(CIRCUITPY_DEFAULT_BUILD) endif CFLAGS += -DCIRCUITPY_NEOPIXEL_WRITE=$(CIRCUITPY_NEOPIXEL_WRITE) -# Only certain boards support NETWORK (Ethernet) +# Enabled on SAMD51. Won't fit on SAMD21 builds. Not tested on nRF or STM32F4 builds. ifndef CIRCUITPY_NETWORK CIRCUITPY_NETWORK = 0 endif @@ -289,12 +308,36 @@ CIRCUITPY_USTACK = 0 endif CFLAGS += -DCIRCUITPY_USTACK=$(CIRCUITPY_USTACK) +# Non-module conditionals ifndef CIRCUITPY_BITBANG_APA102 CIRCUITPY_BITBANG_APA102 = 0 endif CFLAGS += -DCIRCUITPY_BITBANG_APA102=$(CIRCUITPY_BITBANG_APA102) +# Should busio.I2C() check for pullups? +# Some boards in combination with certain peripherals may not want this. +ifndef CIRCUITPY_REQUIRE_I2C_PULLUPS +CIRCUITPY_REQUIRE_I2C_PULLUPS = 1 +endif +CFLAGS += -DCIRCUITPY_REQUIRE_I2C_PULLUPS=$(CIRCUITPY_REQUIRE_I2C_PULLUPS) + +# REPL over BLE +ifndef CIRCUITPY_SERIAL_BLE +CIRCUITPY_SERIAL_BLE = 0 +endif +CFLAGS += -DCIRCUITPY_SERIAL_BLE=$(CIRCUITPY_SERIAL_BLE) + +ifndef CIRCUITPY_BLE_FILE_SERVICE +CIRCUITPY_BLE_FILE_SERVICE = 0 +endif +CFLAGS += -DCIRCUITPY_BLE_FILE_SERVICE=$(CIRCUITPY_BLE_FILE_SERVICE) + +# REPL over UART +ifndef CIRCUITPY_SERIAL_UART +CIRCUITPY_SERIAL_UART = 0 +endif +CFLAGS += -DCIRCUITPY_SERIAL_UART=$(CIRCUITPY_SERIAL_UART) # Enabled micropython.native decorator (experimental) ifndef CIRCUITPY_ENABLE_MPY_NATIVE diff --git a/py/compile.c b/py/compile.c index 8b17935bd..77715d3fe 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 diff --git a/py/emitnative.c b/py/emitnative.c index 6d23bf097..60f31d15f 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -459,6 +459,7 @@ 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, @@ -53,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 @@ -153,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)); @@ -209,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) @@ -397,7 +391,9 @@ void gc_collect_root(void **ptrs, size_t len) { 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(); @@ -474,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) { @@ -519,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; @@ -571,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 @@ -677,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 @@ -873,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(); @@ -29,8 +29,19 @@ #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); @@ -47,6 +58,8 @@ 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 diff --git a/py/gc_long_lived.c b/py/gc_long_lived.c index 49bf1fcd7..01c22a7af 100755 --- a/py/gc_long_lived.c +++ b/py/gc_long_lived.c @@ -126,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/makeqstrdata.py b/py/makeqstrdata.py index 8e3835d86..0d667959d 100644 --- a/py/makeqstrdata.py +++ b/py/makeqstrdata.py @@ -103,14 +103,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,26 +120,27 @@ 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" 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))) return values, lengths def decompress(encoding_table, length, encoded): values, lengths = encoding_table #print(l, encoded) - dec = bytearray(length) + dec = [] this_byte = 0 this_bit = 7 b = encoded[this_byte] @@ -173,14 +170,14 @@ def decompress(encoding_table, length, encoded): searched_length += lengths[bit_length] v = values[searched_length + bits - max_code] - dec[i] = v - return dec + dec.append(v) + return ''.join(dec) def compress(encoding_table, decompressed): - if not isinstance(decompressed, bytes): + if not isinstance(decompressed, str): raise TypeError() values, lengths = encoding_table - enc = bytearray(len(decompressed) * 2) + enc = bytearray(len(decompressed) * 3) #print(decompressed) #print(lengths) current_bit = 7 @@ -228,7 +225,7 @@ def compress(encoding_table, decompressed): if current_bit != 7: current_byte += 1 if current_byte > len(decompressed): - print("Note: compression increased length", repr(decompressed.decode('utf-8')), len(decompressed), current_byte, file=sys.stderr) + print("Note: compression increased length", repr(decompressed), len(decompressed), current_byte, file=sys.stderr) return enc[:current_byte] def qstr_escape(qst): @@ -347,9 +344,9 @@ def print_qstr_data(encoding_table, qcfgs, qstrs, i18ns): total_text_compressed_size = 0 for original, translation in i18ns: translation_encoded = translation.encode("utf-8") - compressed = compress(encoding_table, translation_encoded) + compressed = compress(encoding_table, translation) total_text_compressed_size += len(compressed) - decompressed = decompress(encoding_table, len(translation_encoded), compressed).decode("utf-8") + decompressed = decompress(encoding_table, len(translation_encoded), compressed) 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)) @@ -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/modio.c b/py/modio.c index d7c1a58a8..17840a6d8 100644 --- a/py/modio.c +++ b/py/modio.c @@ -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, @@ -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/mpconfig.h b/py/mpconfig.h index 3ec383817..1512c7d3a 100755 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -244,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 */ @@ -1440,6 +1448,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 a3d7e5dcc..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 @@ -54,7 +54,14 @@ #endif #elif defined(__thumb2__) || defined(__thumb__) || defined(__arm__) #define MICROPY_NLR_THUMB (1) - #define MICROPY_NLR_NUM_REGS (10) + #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) 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 @@ -38,6 +38,7 @@ #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" @@ -128,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) { @@ -489,6 +490,7 @@ 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. @@ -496,7 +498,6 @@ mp_obj_t mp_obj_subscr(mp_obj_t base, mp_obj_t index, mp_obj_t value) { 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) { diff --git a/py/objdict.c b/py/objdict.c index 683fcb748..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) @@ -324,7 +325,7 @@ 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); @@ -590,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/objexcept.c b/py/objexcept.c index c33ada0d6..b7a536c5e 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; } diff --git a/py/objfun.c b/py/objfun.c index 7e5899456..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 @@ -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); diff --git a/py/objint.c b/py/objint.c index 9e11871f1..b78c9f25b 100644 --- a/py/objint.c +++ b/py/objint.c @@ -490,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); @@ -506,22 +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_buffer_overflow_check(args[0], len, false); - 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, false); + 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/objlist.c b/py/objlist.c index ea38e64e5..608ea9f6c 100644 --- a/py/objlist.c +++ b/py/objlist.c @@ -93,7 +93,7 @@ STATIC mp_obj_t list_make_new(const mp_obj_type_t *type_in, size_t n_args, const } 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); @@ -108,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)) { @@ -159,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); @@ -179,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; @@ -201,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; @@ -230,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; } } @@ -241,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; @@ -254,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" @@ -274,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")); } @@ -334,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, @@ -347,7 +345,7 @@ mp_obj_t mp_obj_list_sort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ 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; @@ -357,25 +355,25 @@ mp_obj_t mp_obj_list_clear(mp_obj_t self_in) { STATIC mp_obj_t list_copy(mp_obj_t self_in) { mp_check_self(MP_OBJ_IS_TYPE(self_in, &mp_type_list)); - 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) { @@ -409,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++) { @@ -486,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; } @@ -499,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/objnamedtuple.c b/py/objnamedtuple.c index a044fe3ff..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" diff --git a/py/objstr.c b/py/objstr.c index fde264681..a60f507e9 100644 --- a/py/objstr.c +++ b/py/objstr.c @@ -1352,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)); } } @@ -1388,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)); } } diff --git a/py/objstringio.c b/py/objstringio.c index d2ca6decd..178e6446c 100644 --- a/py/objstringio.c +++ b/py/objstringio.c @@ -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/objtuple.c b/py/objtuple.c index ed13cdcef..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" @@ -181,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; diff --git a/py/objtype.c b/py/objtype.c index 0212a78da..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 @@ -186,14 +187,7 @@ STATIC void mp_obj_class_lookup(struct class_lookup_data *lookup, const mp_obj_ 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: "); @@ -851,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 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..2d4f80565 --- /dev/null +++ b/py/proto.h @@ -0,0 +1,43 @@ +/* + * 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 + @@ -214,6 +214,7 @@ PY_CORE_O_BASENAME = $(addprefix py/,\ objtype.o \ objzip.o \ opmethods.o \ + proto.o \ reload.o \ sequence.o \ stream.o \ @@ -303,12 +304,12 @@ 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 - @$(ECHO) "GEN $@" - $(Q)$(PYTHON) $(PY_SRC)/makemoduledefs.py --vpath="., $(TOP), $(USER_C_MODULES)" $(SRC_QSTR) > $@ + @$(STEPECHO) "GEN $@" + $(Q)$(PYTHON3) $(PY_SRC)/makemoduledefs.py --vpath="., $(TOP), $(USER_C_MODULES)" $(SRC_QSTR) > $@ SRC_QSTR += $(HEADER_BUILD)/moduledefs.h @@ -316,7 +317,7 @@ SRC_QSTR += $(HEADER_BUILD)/moduledefs.h # 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) @@ -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)); } diff --git a/py/runtime.c b/py/runtime.c index a786619bf..c1c311ae4 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -29,10 +29,12 @@ #include <string.h> #include <assert.h> + #include "extmod/vfs.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" @@ -200,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); } @@ -211,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); } @@ -283,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 @@ -641,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) { @@ -1576,6 +1578,14 @@ 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); diff --git a/py/runtime.h b/py/runtime.h index e8398cf0e..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, @@ -84,10 +86,10 @@ void mp_arg_parse_all_kw_array(size_t n_pos, size_t n_kw, const mp_obj_t *args, 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); @@ -159,6 +161,7 @@ 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); diff --git a/py/sequence.c b/py/sequence.c index a750e5bac..0e668ea2a 100644 --- a/py/sequence.c +++ b/py/sequence.c @@ -34,6 +34,11 @@ #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; diff --git a/py/stream.c b/py/stream.c index 1d25f6470..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) @@ -522,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; @@ -533,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; @@ -544,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; @@ -557,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 03e24c746..543fe8c82 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,6 +65,7 @@ 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); @@ -93,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); @@ -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 */ diff --git a/py/vmentrytable.h b/py/vmentrytable.h index a0e2d4065..31a96dbec 100644 --- a/py/vmentrytable.h +++ b/py/vmentrytable.h @@ -29,7 +29,9 @@ #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, @@ -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; } |
