diff options
-rw-r--r-- | py/emitbc.c | 26 | ||||
-rw-r--r-- | py/obj.h | 3 | ||||
-rw-r--r-- | py/objgetitemiter.c | 53 | ||||
-rw-r--r-- | py/objstr.c | 4 | ||||
-rw-r--r-- | py/objtype.c | 17 | ||||
-rw-r--r-- | py/py.mk | 1 | ||||
-rw-r--r-- | py/qstrdefs.h | 4 | ||||
-rw-r--r-- | py/runtime.c | 33 | ||||
-rw-r--r-- | py/vm.c | 9 | ||||
-rw-r--r-- | stm/Makefile | 1 | ||||
-rw-r--r-- | stm/gccollect.c | 39 | ||||
-rw-r--r-- | stm/gccollect.h | 9 | ||||
-rw-r--r-- | stm/main.c | 38 | ||||
-rw-r--r-- | stm/stm32f405.ld | 1 | ||||
-rw-r--r-- | tests/basics/getitem.py | 22 |
15 files changed, 185 insertions, 75 deletions
diff --git a/py/emitbc.c b/py/emitbc.c index 10a95fbcfa..9fa2880ecb 100644 --- a/py/emitbc.c +++ b/py/emitbc.c @@ -71,10 +71,14 @@ static void emit_write_code_info_qstr(emit_t* emit, qstr qstr) { c[3] = (qstr >> 24) & 0xff; } -static void emit_write_code_info_byte_byte(emit_t* emit, byte b1, uint b2) { - byte* c = emit_get_cur_to_write_code_info(emit, 2); - c[0] = b1; - c[1] = b2; +static void emit_write_code_info_bytes_lines(emit_t* emit, uint bytes_to_skip, uint lines_to_skip) { + for (; bytes_to_skip > 31; bytes_to_skip -= 31) { + *emit_get_cur_to_write_code_info(emit, 1) = 31; + } + for (; lines_to_skip > 7; lines_to_skip -= 7) { + *emit_get_cur_to_write_code_info(emit, 1) = 7 << 5; + } + *emit_get_cur_to_write_code_info(emit, 1) = bytes_to_skip | (lines_to_skip << 5); } // all functions must go through this one to emit byte code @@ -218,7 +222,7 @@ static void emit_bc_end_pass(emit_t *emit) { printf("ERROR: stack size not back to zero; got %d\n", emit->stack_size); } - emit_write_code_info_byte_byte(emit, 0, 0); // end of line number info + emit_write_code_info_bytes_lines(emit, 0, 0); // end of line number info if (emit->pass == PASS_2) { // calculate size of code in bytes @@ -246,15 +250,9 @@ static void emit_bc_set_stack_size(emit_t *emit, int size) { static void emit_bc_set_source_line(emit_t *emit, int source_line) { //printf("source: line %d -> %d offset %d -> %d\n", emit->last_source_line, source_line, emit->last_source_line_offset, emit->byte_code_offset); if (source_line > emit->last_source_line) { - int bytes_to_skip = emit->byte_code_offset - emit->last_source_line_offset; - for (; bytes_to_skip > 255; bytes_to_skip -= 255) { - emit_write_code_info_byte_byte(emit, 255, 0); - } - int lines_to_skip = source_line - emit->last_source_line; - for (; lines_to_skip > 255; lines_to_skip -= 255) { - emit_write_code_info_byte_byte(emit, 0, 255); - } - emit_write_code_info_byte_byte(emit, bytes_to_skip, lines_to_skip); + uint bytes_to_skip = emit->byte_code_offset - emit->last_source_line_offset; + uint lines_to_skip = source_line - emit->last_source_line; + emit_write_code_info_bytes_lines(emit, bytes_to_skip, lines_to_skip); //printf(" %d %d\n", bytes_to_skip, lines_to_skip); emit->last_source_line_offset = emit->byte_code_offset; emit->last_source_line = source_line; @@ -39,7 +39,7 @@ typedef struct _mp_obj_base_t mp_obj_base_t; #define MP_OBJ_IS_SMALL_INT(o) ((((mp_small_int_t)(o)) & 1) != 0) #define MP_OBJ_IS_QSTR(o) ((((mp_small_int_t)(o)) & 3) == 2) #define MP_OBJ_IS_OBJ(o) ((((mp_small_int_t)(o)) & 3) == 0) -#define MP_OBJ_IS_TYPE(o, t) (MP_OBJ_IS_OBJ(o) && (((mp_obj_base_t*)(o))->type == (t))) +#define MP_OBJ_IS_TYPE(o, t) (MP_OBJ_IS_OBJ(o) && (((mp_obj_base_t*)(o))->type == (t))) // this does not work for checking a string, use below macro for that #define MP_OBJ_IS_STR(o) (MP_OBJ_IS_QSTR(o) || MP_OBJ_IS_TYPE(o, &str_type)) #define MP_OBJ_SMALL_INT_VALUE(o) (((mp_small_int_t)(o)) >> 1) @@ -231,6 +231,7 @@ mp_obj_t mp_obj_new_dict(int n_args); mp_obj_t mp_obj_new_set(int 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); mp_obj_t mp_obj_new_bound_meth(mp_obj_t meth, mp_obj_t self); +mp_obj_t mp_obj_new_getitem_iter(mp_obj_t *args); mp_obj_t mp_obj_new_module(qstr module_name); mp_obj_type_t *mp_obj_get_type(mp_obj_t o_in); diff --git a/py/objgetitemiter.c b/py/objgetitemiter.c new file mode 100644 index 0000000000..40ed1a1520 --- /dev/null +++ b/py/objgetitemiter.c @@ -0,0 +1,53 @@ +#include <stdlib.h> +#include <stdint.h> + +#include "nlr.h" +#include "misc.h" +#include "mpconfig.h" +#include "qstr.h" +#include "obj.h" +#include "runtime.h" + +// this is a wrapper object that is turns something that has a __getitem__ method into an iterator + +typedef struct _mp_obj_getitem_iter_t { + mp_obj_base_t base; + mp_obj_t args[3]; +} mp_obj_getitem_iter_t; + +static mp_obj_t it_iternext(mp_obj_t self_in) { + mp_obj_getitem_iter_t *self = self_in; + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + // try to get next item + mp_obj_t value = rt_call_method_n_kw(1, 0, self->args); + self->args[2] = MP_OBJ_NEW_SMALL_INT(MP_OBJ_SMALL_INT_VALUE(self->args[2]) + 1); + nlr_pop(); + return value; + } else { + // an exception was raised + if (MP_OBJ_IS_TYPE(nlr.ret_val, &exception_type) && mp_obj_exception_get_type(nlr.ret_val) == MP_QSTR_StopIteration) { + // return mp_const_stop_iteration instead of raising StopIteration + return mp_const_stop_iteration; + } else { + // re-raise exception + nlr_jump(nlr.ret_val); + } + } +} + +static const mp_obj_type_t it_type = { + { &mp_const_type }, + "iterator", + .iternext = it_iternext +}; + +// args are those returned from rt_load_method_maybe (ie either an attribute or a method) +mp_obj_t mp_obj_new_getitem_iter(mp_obj_t *args) { + mp_obj_getitem_iter_t *o = m_new_obj(mp_obj_getitem_iter_t); + o->base.type = &it_type; + o->args[0] = args[0]; + o->args[1] = args[1]; + o->args[2] = MP_OBJ_NEW_SMALL_INT(0); + return o; +} diff --git a/py/objstr.c b/py/objstr.c index 723eebd614..3a4d69cfcc 100644 --- a/py/objstr.c +++ b/py/objstr.c @@ -77,7 +77,7 @@ mp_obj_t str_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) { if (MP_OBJ_IS_SMALL_INT(rhs_in)) { uint index = mp_get_index(mp_obj_get_type(lhs_in), lhs_len, rhs_in); if (MP_OBJ_IS_TYPE(lhs_in, &bytes_type)) { - return MP_OBJ_NEW_SMALL_INT(lhs_data[index]); + return MP_OBJ_NEW_SMALL_INT((mp_small_int_t)lhs_data[index]); } else { return mp_obj_new_str(lhs_data + index, 1, true); } @@ -549,7 +549,7 @@ mp_obj_t bytes_it_iternext(mp_obj_t self_in) { mp_obj_str_it_t *self = self_in; GET_STR_DATA_LEN(self->str, str, len); if (self->cur < len) { - mp_obj_t o_out = MP_OBJ_NEW_SMALL_INT(str[self->cur]); + mp_obj_t o_out = MP_OBJ_NEW_SMALL_INT((mp_small_int_t)str[self->cur]); self->cur += 1; return o_out; } else { diff --git a/py/objtype.c b/py/objtype.c index 75755f4fb9..9cb9b8654d 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -116,9 +116,8 @@ static mp_obj_t class_make_new(mp_obj_t self_in, uint n_args, uint n_kw, const m return o; } -// TODO somehow replace const char * with a qstr -static const char *binary_op_method_name[] = { - [RT_BINARY_OP_SUBSCR] = "__getitem__", +static const qstr binary_op_method_name[] = { + [RT_BINARY_OP_SUBSCR] = MP_QSTR___getitem__, /* RT_BINARY_OP_OR, RT_BINARY_OP_XOR, @@ -126,8 +125,8 @@ static const char *binary_op_method_name[] = { RT_BINARY_OP_LSHIFT, RT_BINARY_OP_RSHIFT, */ - [RT_BINARY_OP_ADD] = "__add__", - [RT_BINARY_OP_SUBTRACT] = "__sub__", + [RT_BINARY_OP_ADD] = MP_QSTR___add__, + [RT_BINARY_OP_SUBTRACT] = MP_QSTR___sub__, /* RT_BINARY_OP_MULTIPLY, RT_BINARY_OP_FLOOR_DIVIDE, @@ -157,16 +156,16 @@ static const char *binary_op_method_name[] = { RT_COMPARE_OP_IS, RT_COMPARE_OP_IS_NOT, */ - [RT_COMPARE_OP_EXCEPTION_MATCH] = "__not_implemented__", + [RT_COMPARE_OP_EXCEPTION_MATCH] = MP_QSTR_, // not implemented, used to make sure array has full size }; static mp_obj_t class_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) { mp_obj_class_t *lhs = lhs_in; - const char *op_name = binary_op_method_name[op]; - if (op_name == NULL) { + qstr op_name = binary_op_method_name[op]; + if (op_name == 0) { return MP_OBJ_NULL; } - mp_obj_t member = mp_obj_class_lookup(lhs->base.type, QSTR_FROM_STR_STATIC(op_name)); + mp_obj_t member = mp_obj_class_lookup(lhs->base.type, op_name); if (member != MP_OBJ_NULL) { return rt_call_function_2(member, lhs_in, rhs_in); } else { @@ -47,6 +47,7 @@ PY_O_BASENAME = \ objfloat.o \ objfun.o \ objgenerator.o \ + objgetitemiter.o \ objint.o \ objint_longlong.o \ objlist.o \ diff --git a/py/qstrdefs.h b/py/qstrdefs.h index f2c4dfd97f..e76efaf0e0 100644 --- a/py/qstrdefs.h +++ b/py/qstrdefs.h @@ -13,6 +13,10 @@ Q(__next__) Q(__qualname__) Q(__repl_print__) +Q(__getitem__) +Q(__add__) +Q(__sub__) + Q(micropython) Q(byte_code) Q(native) diff --git a/py/runtime.c b/py/runtime.c index 25992a52c0..8ad98d5f12 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -87,7 +87,7 @@ void rt_init(void) { // init loaded modules table mp_map_init(&map_loaded_modules, 3); - // built-in exceptions (TODO, make these proper classes) + // built-in exceptions (TODO, make these proper classes, and const if possible) mp_map_add_qstr(&map_builtins, MP_QSTR_AttributeError, mp_obj_new_exception(MP_QSTR_AttributeError)); mp_map_add_qstr(&map_builtins, MP_QSTR_IndexError, mp_obj_new_exception(MP_QSTR_IndexError)); mp_map_add_qstr(&map_builtins, MP_QSTR_KeyError, mp_obj_new_exception(MP_QSTR_KeyError)); @@ -100,6 +100,7 @@ void rt_init(void) { mp_map_add_qstr(&map_builtins, MP_QSTR_OverflowError, mp_obj_new_exception(MP_QSTR_OverflowError)); mp_map_add_qstr(&map_builtins, MP_QSTR_OSError, mp_obj_new_exception(MP_QSTR_OSError)); mp_map_add_qstr(&map_builtins, MP_QSTR_AssertionError, mp_obj_new_exception(MP_QSTR_AssertionError)); + mp_map_add_qstr(&map_builtins, MP_QSTR_StopIteration, mp_obj_new_exception(MP_QSTR_StopIteration)); // built-in objects mp_map_add_qstr(&map_builtins, MP_QSTR_Ellipsis, mp_const_ellipsis); @@ -809,7 +810,7 @@ mp_obj_t rt_load_attr(mp_obj_t base, qstr attr) { // use load_method mp_obj_t dest[2]; rt_load_method(base, attr, dest); - if (dest[1] == NULL) { + if (dest[1] == MP_OBJ_NULL) { // load_method returned just a normal attribute return dest[0]; } else { @@ -818,9 +819,10 @@ mp_obj_t rt_load_attr(mp_obj_t base, qstr attr) { } } -void rt_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest) { - DEBUG_OP_printf("load method %p.%s\n", base, qstr_str(attr)); - +// no attribute found, returns: dest[0] == MP_OBJ_NULL, dest[1] == MP_OBJ_NULL +// normal attribute found, returns: dest[0] == <attribute>, dest[1] == MP_OBJ_NULL +// method attribute found, returns: dest[0] == <method>, dest[1] == <self> +static void rt_load_method_maybe(mp_obj_t base, qstr attr, mp_obj_t *dest) { // clear output to indicate no attribute/method found yet dest[0] = MP_OBJ_NULL; dest[1] = MP_OBJ_NULL; @@ -834,7 +836,7 @@ void rt_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest) { } // if nothing found yet, look for built-in and generic names - if (dest[0] == NULL) { + if (dest[0] == MP_OBJ_NULL) { if (attr == MP_QSTR___next__ && type->iternext != NULL) { dest[0] = (mp_obj_t)&mp_builtin_next_obj; dest[1] = base; @@ -865,8 +867,14 @@ void rt_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest) { } } } +} + +void rt_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest) { + DEBUG_OP_printf("load method %p.%s\n", base, qstr_str(attr)); + + rt_load_method_maybe(base, attr, dest); - if (dest[0] == NULL) { + if (dest[0] == MP_OBJ_NULL) { // no attribute/method called attr // following CPython, we give a more detailed error message for type objects if (MP_OBJ_IS_TYPE(base, &mp_const_type)) { @@ -914,7 +922,16 @@ mp_obj_t rt_getiter(mp_obj_t o_in) { if (type->getiter != NULL) { return type->getiter(o_in); } else { - nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_TypeError, "'%s' object is not iterable", type->name)); + // check for __getitem__ method + mp_obj_t dest[2]; + rt_load_method_maybe(o_in, qstr_from_str("__getitem__"), dest); + if (dest[0] != MP_OBJ_NULL) { + // __getitem__ exists, create an iterator + return mp_obj_new_getitem_iter(dest); + } else { + // object not iterable + nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_TypeError, "'%s' object is not iterable", type->name)); + } } } @@ -550,12 +550,9 @@ bool mp_execute_byte_code_2(const byte *code_info, const byte **ip_in_out, mp_ob machine_uint_t source_line = 1; machine_uint_t bc = save_ip - code_info - code_info_size; //printf("find %lu %d %d\n", bc, code_info[12], code_info[13]); - for (const byte* ci = code_info + 12; bc >= ci[0]; ci += 2) { - bc -= ci[0]; - source_line += ci[1]; - if (ci[0] == 0 && ci[1] == 0) { - break; - } + for (const byte* ci = code_info + 12; *ci && bc >= ((*ci) & 31); ci++) { + bc -= *ci & 31; + source_line += *ci >> 5; } mp_obj_exception_add_traceback(nlr.ret_val, source_file, source_line, block_name); } diff --git a/stm/Makefile b/stm/Makefile index b3483e089d..c0ae6a89c0 100644 --- a/stm/Makefile +++ b/stm/Makefile @@ -40,6 +40,7 @@ SRC_C = \ string0.c \ malloc0.c \ systick.c \ + gccollect.c \ lexerfatfs.c \ led.c \ lcd.c \ diff --git a/stm/gccollect.c b/stm/gccollect.c new file mode 100644 index 0000000000..c0f67ac0d5 --- /dev/null +++ b/stm/gccollect.c @@ -0,0 +1,39 @@ +#include <stdio.h> + +#include "misc.h" +#include "mpconfig.h" +#include "qstr.h" +#include "obj.h" +#include "gc.h" +#include "gccollect.h" +#include "systick.h" + +void gc_helper_get_regs_and_clean_stack(machine_uint_t *regs, machine_uint_t heap_end); + +void gc_collect(void) { + uint32_t start = sys_tick_counter; + gc_collect_start(); + gc_collect_root((void**)&_ram_start, ((uint32_t)&_heap_start - (uint32_t)&_ram_start) / 4); + machine_uint_t regs[10]; + gc_helper_get_regs_and_clean_stack(regs, HEAP_END); + gc_collect_root((void**)HEAP_END, (RAM_END - HEAP_END) / 4); // will trace regs since they now live in this function on the stack + gc_collect_end(); + uint32_t ticks = sys_tick_counter - start; // TODO implement a function that does this properly + + if (0) { + // print GC info + gc_info_t info; + gc_info(&info); + printf("GC@%lu %lums\n", start, ticks); + printf(" %lu total\n", info.total); + printf(" %lu : %lu\n", info.used, info.free); + printf(" 1=%lu 2=%lu m=%lu\n", info.num_1block, info.num_2block, info.max_block); + } +} + +static mp_obj_t pyb_gc(void) { + gc_collect(); + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_0(pyb_gc_obj, pyb_gc); diff --git a/stm/gccollect.h b/stm/gccollect.h new file mode 100644 index 0000000000..6467ec7d21 --- /dev/null +++ b/stm/gccollect.h @@ -0,0 +1,9 @@ +#define HEAP_END (0x2001c000) // tunable +#define RAM_END (0x20020000) // fixed for chip + +extern uint32_t _ram_start; +extern uint32_t _heap_start; + +void gc_collect(void); + +MP_DECLARE_CONST_FUN_OBJ(pyb_gc_obj); diff --git a/stm/main.c b/stm/main.c index 2ce2b6dc61..4114ec1979 100644 --- a/stm/main.c +++ b/stm/main.c @@ -28,6 +28,7 @@ #include "runtime.h" #include "repl.h" #include "gc.h" +#include "gccollect.h" #include "systick.h" #include "led.h" #include "servo.h" @@ -47,8 +48,6 @@ int errno; -extern uint32_t _heap_start; - static FATFS fatfs0; void flash_error(int n) { @@ -180,6 +179,7 @@ static mp_obj_t pyb_info(void) { printf("_ebss=%p\n", &_ebss); printf("_estack=%p\n", &_estack); printf("_etext=%p\n", &_etext); + printf("_ram_start=%p\n", &_ram_start); printf("_heap_start=%p\n", &_heap_start); } @@ -455,38 +455,6 @@ bool do_file(const char *filename) { } } -#define RAM_START (0x20000000) // fixed for chip -#define HEAP_END (0x2001c000) // tunable -#define RAM_END (0x20020000) // fixed for chip - -void gc_helper_get_regs_and_clean_stack(machine_uint_t *regs, machine_uint_t heap_end); - -void gc_collect(void) { - uint32_t start = sys_tick_counter; - gc_collect_start(); - gc_collect_root((void**)RAM_START, (((uint32_t)&_heap_start) - RAM_START) / 4); - machine_uint_t regs[10]; - gc_helper_get_regs_and_clean_stack(regs, HEAP_END); - gc_collect_root((void**)HEAP_END, (RAM_END - HEAP_END) / 4); // will trace regs since they now live in this function on the stack - gc_collect_end(); - uint32_t ticks = sys_tick_counter - start; // TODO implement a function that does this properly - - if (0) { - // print GC info - gc_info_t info; - gc_info(&info); - printf("GC@%lu %lums\n", start, ticks); - printf(" %lu total\n", info.total); - printf(" %lu : %lu\n", info.used, info.free); - printf(" 1=%lu 2=%lu m=%lu\n", info.num_1block, info.num_2block, info.max_block); - } -} - -mp_obj_t pyb_gc(void) { - gc_collect(); - return mp_const_none; -} - mp_obj_t pyb_gpio(uint n_args, mp_obj_t *args) { //assert(1 <= n_args && n_args <= 2); @@ -655,7 +623,7 @@ soft_reset: rt_store_attr(m, MP_QSTR_source_dir, rt_make_function_n(1, pyb_source_dir)); rt_store_attr(m, MP_QSTR_main, rt_make_function_n(1, pyb_main)); rt_store_attr(m, MP_QSTR_sync, rt_make_function_n(0, pyb_sync)); - rt_store_attr(m, MP_QSTR_gc, rt_make_function_n(0, pyb_gc)); + rt_store_attr(m, MP_QSTR_gc, (mp_obj_t)&pyb_gc_obj); rt_store_attr(m, MP_QSTR_delay, rt_make_function_n(1, pyb_delay)); #if MICROPY_HW_HAS_SWITCH rt_store_attr(m, MP_QSTR_switch, (mp_obj_t)&pyb_switch_obj); diff --git a/stm/stm32f405.ld b/stm/stm32f405.ld index c19e6a1c19..fbfc584f9d 100644 --- a/stm/stm32f405.ld +++ b/stm/stm32f405.ld @@ -69,6 +69,7 @@ SECTIONS { . = ALIGN(4); _sdata = .; /* create a global symbol at data start; used by startup code in order to initialise the .data section in RAM */ + _ram_start = .; /* create a global symbol at ram start for garbage collector */ *(.data) /* .data sections */ *(.data*) /* .data* sections */ diff --git a/tests/basics/getitem.py b/tests/basics/getitem.py new file mode 100644 index 0000000000..f39296aca3 --- /dev/null +++ b/tests/basics/getitem.py @@ -0,0 +1,22 @@ +# create a class that has a __getitem__ method +class A: + def __getitem__(self, index): + print('getitem', index) + if index > 10: + raise StopIteration + +# test __getitem__ +A()[0] +A()[1] + +# iterate using a for loop +for i in A(): + pass + +# iterate manually +it = iter(A()) +try: + while True: + next(it) +except StopIteration: + pass |