diff --git a/python/Makefile b/python/Makefile index 07edec47d..e22e7d26f 100644 --- a/python/Makefile +++ b/python/Makefile @@ -67,7 +67,6 @@ py_objs = $(addprefix python/src/py/,\ runtime_utils.o \ scheduler.o \ nativeglue.o \ - stackctrl.o \ argcheck.o \ warning.o \ map.o \ @@ -179,8 +178,21 @@ port_objs += $(addprefix python/port/,\ modkandinsky.o \ modkandinsky_impl.o \ mphalport.o \ + stackctrl.o \ ) +# Reduce optimization for the emscripten platform. +# With optimization, register and stack variables might be held in a JavaScript +# local variable, which breaks garbage collection. Indeed, these JavaScript +# variables cannot be marked as root during garbage collection, which means that +# the heap objects they depend on will likely be destroyed. When the Python +# computing resumes, if necessary heap objects have been destroyed, the Python +# program crashes. + +ifeq ($(PLATFORM),emscripten) +$(py_objs): SFLAGS := $(subst -Os,-O0,$(SFLAGS)) +endif + # QSTR generation python/port/genhdr/qstrdefs.generated.h: python/port/genhdr/qstrdefs.in.h diff --git a/python/port/port.cpp b/python/port/port.cpp index 720d5a891..7fa914ba5 100644 --- a/python/port/port.cpp +++ b/python/port/port.cpp @@ -137,7 +137,9 @@ void MicroPython::registerScriptProvider(ScriptProvider * s) { } void gc_collect(void) { - assert(MP_STATE_THREAD(stack_top) != NULL); + void * python_stack_top = MP_STATE_THREAD(stack_top); + assert(python_stack_top != NULL); + gc_collect_start(); /* get the registers. @@ -146,18 +148,19 @@ void gc_collect(void) { jmp_buf regs; setjmp(regs); - void **regs_ptr = (void**)(void*)®s; + void **regs_ptr = (void**)®s; /* On the device, the stack is stored in reverse order, but it might not be * the case on a computer. We thus have to take the absolute value of the * addresses difference. */ size_t stackLength; - if ((uintptr_t)MP_STATE_THREAD(stack_top) > (uintptr_t)(®s)) { - stackLength = (((uintptr_t)(MP_STATE_THREAD(stack_top)) - (uintptr_t)(®s)) / sizeof(uintptr_t)); + if ((uintptr_t)python_stack_top > (uintptr_t)®s) { + stackLength = ((uintptr_t)python_stack_top - (uintptr_t)®s) / sizeof(uintptr_t); + gc_collect_root(regs_ptr, stackLength); } else { - stackLength = (((uintptr_t)(®s) - (uintptr_t)(MP_STATE_THREAD(stack_top))) / sizeof(uintptr_t)); + stackLength = ((uintptr_t)(®s) - (uintptr_t)python_stack_top) / sizeof(uintptr_t); + gc_collect_root((void **)python_stack_top, stackLength); } - gc_collect_root(regs_ptr, stackLength); gc_collect_end(); } diff --git a/python/port/stackctrl.c b/python/port/stackctrl.c new file mode 100644 index 000000000..49ea37cf9 --- /dev/null +++ b/python/port/stackctrl.c @@ -0,0 +1,70 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014 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 "py/mpstate.h" +#include "py/nlr.h" +#include "py/obj.h" +#include "py/runtime.h" +#include "py/stackctrl.h" + +void mp_stack_ctrl_init(void) { + volatile int stack_dummy; + MP_STATE_THREAD(stack_top) = (char*)&stack_dummy; +} + +void mp_stack_set_top(void *top) { + MP_STATE_THREAD(stack_top) = top; +} + +mp_uint_t mp_stack_usage(void) { + /* Micropython's stackctrl.c assumes that the stack is in descending order, + * which is not always the case for us. */ + volatile int stack_dummy; + if (MP_STATE_THREAD(stack_top) > (char*)&stack_dummy) { + return MP_STATE_THREAD(stack_top) - (char*)&stack_dummy; + } else { + return (char*)&stack_dummy - MP_STATE_THREAD(stack_top); + } +} + +#if MICROPY_STACK_CHECK + +void mp_stack_set_limit(mp_uint_t limit) { + MP_STATE_THREAD(stack_limit) = limit; +} + +void mp_exc_recursion_depth(void) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_RuntimeError, + MP_OBJ_NEW_QSTR(MP_QSTR_maximum_space_recursion_space_depth_space_exceeded))); +} + +void mp_stack_check(void) { + if (mp_stack_usage() >= MP_STATE_THREAD(stack_limit)) { + mp_exc_recursion_depth(); + } +} + +#endif // MICROPY_STACK_CHECK