From c680da31546bc8adaf48f883a61c05e51e2264b0 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Sun, 26 May 2024 16:17:18 +0800 Subject: [PATCH] fix bugs --- docs/C-API/stack.md | 18 +++++++++++++++++- include/pocketpy/frame.h | 21 +++++++++++++++++--- include/pocketpy/opcodes.h | 1 + include/pocketpy/pocketpy_c.h | 1 + src/ceval.cpp | 4 ++++ src/compiler.cpp | 1 + src/frame.cpp | 36 ++++++++++++++++++++++++++++++++--- src/iter.cpp | 11 +++++++++++ src/pocketpy_c.cpp | 10 ++++++++++ src/vm.cpp | 4 +++- src2/main.cpp | 2 +- src2/pocketpy_c.c | 5 +++++ tests/70_collections.py | 9 ++++++--- tests/99_bugs.py | 8 ++++++++ 14 files changed, 119 insertions(+), 12 deletions(-) diff --git a/docs/C-API/stack.md b/docs/C-API/stack.md index f3c6636a..793304b3 100644 --- a/docs/C-API/stack.md +++ b/docs/C-API/stack.md @@ -150,4 +150,20 @@ PK_EXPORT bool pkpy_is_none(pkpy_vm*, int i); ``` [value] -> [repr(value)] - ``` \ No newline at end of file + ``` + ++ `bool pkpy_py_str(pkpy_vm*)` + + Get the str of the value at the top of the stack. + + ``` + [value] -> [str(value)] + ``` + ++ `bool pkpy_py_import(pkpy_vm*, const char* name)` + + Import a module and push it onto the stack. + + ``` + [] -> [module] + ``` diff --git a/include/pocketpy/frame.h b/include/pocketpy/frame.h index 44acd2e6..6696aa52 100644 --- a/include/pocketpy/frame.h +++ b/include/pocketpy/frame.h @@ -75,6 +75,14 @@ struct ValueStack { ValueStack& operator=(ValueStack&&) = delete; }; +struct UnwindTarget{ + UnwindTarget* next; + int iblock; + int offset; + + UnwindTarget(int iblock, int offset): next(nullptr), iblock(iblock), offset(offset) {} +}; + struct Frame { const Bytecode* _ip; // This is for unwinding only, use `actual_sp_base()` for value stack access @@ -85,21 +93,24 @@ struct Frame { PyVar _callable; // a function object or nullptr (global scope) FastLocals _locals; + // This list will be freed in __pop_frame + UnwindTarget* _uw_list; + NameDict& f_globals() { return _module->attr(); } PyVar f_closure_try_get(StrName name); int ip() const { return _ip - co->codes.data(); } // function scope Frame(PyVar* p0, const CodeObject* co, PyVar _module, PyVar _callable, PyVar* _locals_base) - : _ip(co->codes.data()-1), _sp_base(p0), co(co), _module(_module), _callable(_callable), _locals(co, _locals_base) { } + : _ip(co->codes.data()-1), _sp_base(p0), co(co), _module(_module), _callable(_callable), _locals(co, _locals_base), _uw_list(nullptr) { } // exec/eval Frame(PyVar* p0, const CodeObject* co, PyVar _module, PyVar _callable, FastLocals _locals) - : _ip(co->codes.data()-1), _sp_base(p0), co(co), _module(_module), _callable(_callable), _locals(_locals) { } + : _ip(co->codes.data()-1), _sp_base(p0), co(co), _module(_module), _callable(_callable), _locals(_locals), _uw_list(nullptr) { } // global scope Frame(PyVar* p0, const CodeObject_& co, PyVar _module) - : _ip(co->codes.data()-1), _sp_base(p0), co(co.get()), _module(_module), _callable(nullptr), _locals(co.get(), p0) {} + : _ip(co->codes.data()-1), _sp_base(p0), co(co.get()), _module(_module), _callable(nullptr), _locals(co.get(), p0), _uw_list(nullptr) { } PyVar* actual_sp_base() const { return _locals.a; } ArgsView stack_view(ValueStack* _s) const { return ArgsView(actual_sp_base(), _s->_sp); } @@ -116,6 +127,10 @@ struct Frame { int curr_lineno() const { return co->lines[ip()].lineno; } + void set_unwind_target(PyVar* _sp); + UnwindTarget* find_unwind_target(int iblock); + void free_unwind_target(); + void _gc_mark(VM* vm) const; }; diff --git a/include/pocketpy/opcodes.h b/include/pocketpy/opcodes.h index d820a164..29c606d1 100644 --- a/include/pocketpy/opcodes.h +++ b/include/pocketpy/opcodes.h @@ -140,6 +140,7 @@ OPCODE(ADD_CLASS_ANNOTATION) OPCODE(WITH_ENTER) OPCODE(WITH_EXIT) /**************************/ +OPCODE(TRY_ENTER) OPCODE(EXCEPTION_MATCH) OPCODE(RAISE) OPCODE(RAISE_ASSERT) diff --git a/include/pocketpy/pocketpy_c.h b/include/pocketpy/pocketpy_c.h index a089b525..22906899 100644 --- a/include/pocketpy/pocketpy_c.h +++ b/include/pocketpy/pocketpy_c.h @@ -77,6 +77,7 @@ PK_EXPORT bool pkpy_unpack_sequence(pkpy_vm*, int size); PK_EXPORT bool pkpy_get_unbound_method(pkpy_vm*, pkpy_CName name); PK_EXPORT bool pkpy_py_repr(pkpy_vm*); PK_EXPORT bool pkpy_py_str(pkpy_vm*); +PK_EXPORT bool pkpy_py_import(pkpy_vm*, pkpy_CString name); /* Error Handling */ PK_EXPORT bool pkpy_error(pkpy_vm*, const char* name, pkpy_CString msg); diff --git a/src/ceval.cpp b/src/ceval.cpp index 3467a542..a0d99cde 100644 --- a/src/ceval.cpp +++ b/src/ceval.cpp @@ -986,6 +986,10 @@ __NEXT_STEP: POP(); DISPATCH() /*****************************************/ + case OP_TRY_ENTER: { + frame->set_unwind_target(s_data._sp); + DISPATCH() + } case OP_EXCEPTION_MATCH: { PyVar assumed_type = POPX(); check_type(assumed_type, tp_type); diff --git a/src/compiler.cpp b/src/compiler.cpp index 19dcf4c0..1e1d265b 100644 --- a/src/compiler.cpp +++ b/src/compiler.cpp @@ -703,6 +703,7 @@ __EAT_DOTS_END: void Compiler::compile_try_except() { ctx()->enter_block(CodeBlockType::TRY_EXCEPT); + ctx()->emit_(OP_TRY_ENTER, BC_NOARG, prev().line); compile_block_body(); small_vector_2 patches; patches.push_back( diff --git a/src/frame.cpp b/src/frame.cpp index 70f2e894..7c257b02 100644 --- a/src/frame.cpp +++ b/src/frame.cpp @@ -24,15 +24,17 @@ namespace pkpy{ } int Frame::prepare_jump_exception_handler(ValueStack* _s){ - PyVar obj = _s->popx(); // try to find a parent try block int i = co->lines[ip()].iblock; while(i >= 0){ if(co->blocks[i].type == CodeBlockType::TRY_EXCEPT) break; - i = _exit_block(_s, i); + i = co->blocks[i].parent; } - _s->push(obj); if(i < 0) return -1; + PyVar obj = _s->popx(); // pop exception object + UnwindTarget* uw = find_unwind_target(i); + _s->reset(actual_sp_base() + uw->offset); // unwind the stack + _s->push(obj); // push it back return co->blocks[i].end; } @@ -69,4 +71,32 @@ namespace pkpy{ } } + void Frame::set_unwind_target(PyVar* _sp){ + int iblock = co->lines[ip()].iblock; + UnwindTarget* existing = find_unwind_target(iblock); + if(existing){ + existing->offset = _sp - actual_sp_base(); + }else{ + UnwindTarget* prev = _uw_list; + _uw_list = new UnwindTarget(iblock, _sp - actual_sp_base()); + _uw_list->next = prev; + } + } + + UnwindTarget* Frame::find_unwind_target(int iblock){ + UnwindTarget* p; + for(p=_uw_list; p!=nullptr; p=p->next){ + if(p->iblock == iblock) return p; + } + return nullptr; + } + + void Frame::free_unwind_target(){ + while(_uw_list != nullptr){ + UnwindTarget* p = _uw_list; + _uw_list = p->next; + delete p; + } + } + } // namespace pkpy \ No newline at end of file diff --git a/src/iter.cpp b/src/iter.cpp index 2a205152..0c90d707 100644 --- a/src/iter.cpp +++ b/src/iter.cpp @@ -55,6 +55,17 @@ namespace pkpy{ frame._locals.a = vm->s_data._sp; // restore the context for(PyVar obj: s_backup) vm->s_data.push(obj); + // relocate stack objects (their addresses become invalid) + for(PyVar* p=frame.actual_sp_base(); p!=vm->s_data.end(); p++){ + if(p->type == VM::tp_stack_memory){ + // TODO: refactor this + int count = p->as().count; + if(count < 0){ + void* new_p = p + count; + p[1]._1 = reinterpret_cast(new_p); + } + } + } s_backup.clear(); vm->callstack.emplace(std::move(frame)); diff --git a/src/pocketpy_c.cpp b/src/pocketpy_c.cpp index 11d70c5a..998dded2 100644 --- a/src/pocketpy_c.cpp +++ b/src/pocketpy_c.cpp @@ -489,6 +489,16 @@ bool pkpy_py_str(pkpy_vm* vm_handle) { return true; } +bool pkpy_py_import(pkpy_vm* vm_handle, pkpy_CString name) { + VM* vm = (VM*) vm_handle; + PK_ASSERT_NO_ERROR() + PK_PROTECTED( + PyVar module = vm->py_import(name); + vm->s_data.push(module); + ) + return true; +} + /* Error Handling */ bool pkpy_error(pkpy_vm* vm_handle, const char* name, pkpy_CString message) { VM* vm = (VM*) vm_handle; diff --git a/src/vm.cpp b/src/vm.cpp index 47086328..9208b7a7 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -1644,7 +1644,9 @@ void NextBreakpoint::_step(VM* vm){ #endif void VM::__pop_frame(){ - s_data.reset(callstack.top()._sp_base); + Frame& frame = callstack.top(); + s_data.reset(frame._sp_base); + frame.free_unwind_target(); callstack.pop(); #if PK_ENABLE_PROFILER diff --git a/src2/main.cpp b/src2/main.cpp index 4de58e4f..8525a7ca 100644 --- a/src2/main.cpp +++ b/src2/main.cpp @@ -65,7 +65,7 @@ int main(int argc, char** argv){ pkpy_vm* vm = pkpy_new_vm(true); pkpy_push_function(vm, "input(prompt=None) -> str", f_input); - pkpy_eval(vm, "__import__('builtins')"); + pkpy_py_import(vm, "builtins"); pkpy_setattr(vm, pkpy_name("input")); if(argc == 1){ diff --git a/src2/pocketpy_c.c b/src2/pocketpy_c.c index 74727738..7fdbb225 100644 --- a/src2/pocketpy_c.c +++ b/src2/pocketpy_c.c @@ -203,6 +203,11 @@ bool pkpy_py_str(pkpy_vm* vm) { return returnValue; } +bool pkpy_py_import(pkpy_vm* vm, pkpy_CString name) { + bool returnValue; + return returnValue; +} + bool pkpy_error(pkpy_vm* vm, const char* name, pkpy_CString msg) { bool returnValue; return returnValue; diff --git a/tests/70_collections.py b/tests/70_collections.py index d8c62ba3..b707553e 100644 --- a/tests/70_collections.py +++ b/tests/70_collections.py @@ -210,9 +210,12 @@ for e in [d, deque('abc'), deque('ab'), deque(), list(d)]: assertEqual(d == e, type(d) == type(e) and list(d) == list(e)) assertEqual(d != e, not (type(d) == type(e) and list(d) == list(e))) -args = map(deque, ('', 'a', 'b', 'ab', 'ba', 'abc', 'xba', 'xabc', 'cba')) -for x in args: - for y in args: +def get_args(): + args = map(deque, ('', 'a', 'b', 'ab', 'ba', 'abc', 'xba', 'xabc', 'cba')) + return args + +for x in get_args(): + for y in get_args(): assertEqual(x == y, list(x) == list(y)) assertEqual(x != y, list(x) != list(y)) # assertEqual(x < y, list(x) < list(y)) # not currently supported diff --git a/tests/99_bugs.py b/tests/99_bugs.py index 4c34213c..a3c59c5e 100644 --- a/tests/99_bugs.py +++ b/tests/99_bugs.py @@ -1,3 +1,11 @@ +# multi-loop generator +out = [] +args = map(lambda x: x+1, [1, 2, 3]) +for x in args: + for y in args: + out.append((x, y)) +assert out == [(2, 3), (2, 4)] + # multi loop bug out = [] a = [1, 2]