diff --git a/include/pocketpy/interpreter/frame.h b/include/pocketpy/interpreter/frame.h index 64972ec3..8f5d07b9 100644 --- a/include/pocketpy/interpreter/frame.h +++ b/include/pocketpy/interpreter/frame.h @@ -57,10 +57,14 @@ int Frame__getglobal(Frame* self, py_Name name) PY_RAISE PY_RETURN; bool Frame__setglobal(Frame* self, py_Name name, py_TValue* val) PY_RAISE; int Frame__delglobal(Frame* self, py_Name name) PY_RAISE; -py_Ref Frame__getclosure(Frame* self, py_Name name); +int Frame__getlocal(Frame* self, py_Name name) PY_RAISE PY_RETURN; +int Frame__setlocal(Frame* self, py_Name name, py_TValue* val) PY_RAISE; +int Frame__dellocal(Frame* self, py_Name name) PY_RAISE; +py_Ref Frame__getclosure(Frame* self, py_Name name); py_StackRef Frame__getlocal_noproxy(Frame* self, py_Name name); +py_StackRef Frame__locals_sp(Frame* self); int Frame__prepare_jump_exception_handler(Frame* self, ValueStack*); diff --git a/include/pocketpy/interpreter/vm.h b/include/pocketpy/interpreter/vm.h index 9b0ab866..11edd121 100644 --- a/include/pocketpy/interpreter/vm.h +++ b/include/pocketpy/interpreter/vm.h @@ -96,6 +96,7 @@ bool pk_loadmethod(py_StackRef self, py_Name name); bool pk_callmagic(py_Name name, int argc, py_Ref argv); bool pk_exec(CodeObject* co, py_Ref module); +bool pk_execdyn(CodeObject* co, py_Ref module, py_Ref globals, py_Ref locals); /// Assumes [a, b] are on the stack, performs a binary op. /// The result is stored in `self->last_retval`. diff --git a/src/interpreter/ceval.c b/src/interpreter/ceval.c index ad81c526..f45282f6 100644 --- a/src/interpreter/ceval.c +++ b/src/interpreter/ceval.c @@ -194,6 +194,7 @@ FrameResult VM__run_top_frame(VM* self) { DISPATCH(); /*****************************************/ case OP_LOAD_FAST: { + assert(!frame->is_locals_proxy); PUSH(&frame->locals[byte.arg]); if(py_isnil(TOP())) { py_Name name = c11__getitem(uint16_t, &frame->co->varnames, byte.arg); @@ -220,7 +221,6 @@ FrameResult VM__run_top_frame(VM* self) { } } } - // `LOAD_ // globals if(py_getitem(&frame->p0[0], TOP())) { py_assign(TOP(), py_retval()); @@ -343,7 +343,11 @@ FrameResult VM__run_top_frame(VM* self) { TypeError("'%t' object is not subscriptable", SECOND()->type); goto __ERROR; } - case OP_STORE_FAST: frame->locals[byte.arg] = POPX(); DISPATCH(); + case OP_STORE_FAST: { + assert(!frame->is_locals_proxy); + frame->locals[byte.arg] = POPX(); + DISPATCH(); + } case OP_STORE_NAME: { // assert(frame->is_dynamic); py_Name name = byte.arg; @@ -405,6 +409,7 @@ FrameResult VM__run_top_frame(VM* self) { goto __ERROR; } case OP_DELETE_FAST: { + assert(!frame->is_locals_proxy); py_Ref tmp = &frame->locals[byte.arg]; if(py_isnil(tmp)) { py_Name name = c11__getitem(py_Name, &frame->co->varnames, byte.arg); diff --git a/src/interpreter/frame.c b/src/interpreter/frame.c index 18747897..20f84177 100644 --- a/src/interpreter/frame.c +++ b/src/interpreter/frame.c @@ -79,7 +79,7 @@ int Frame__prepare_jump_exception_handler(Frame* self, ValueStack* _s) { } if(iblock < 0) return -1; UnwindTarget* uw = Frame__find_unwind_target(self, iblock); - _s->sp = (self->locals + uw->offset); // unwind the stack + _s->sp = (Frame__locals_sp(self) + uw->offset); // unwind the stack return c11__at(CodeBlock, &self->co->blocks, iblock)->end; } @@ -95,10 +95,10 @@ void Frame__set_unwind_target(Frame* self, py_TValue* sp) { int iblock = Frame__iblock(self); UnwindTarget* existing = Frame__find_unwind_target(self, iblock); if(existing) { - existing->offset = sp - self->locals; + existing->offset = sp - Frame__locals_sp(self); } else { UnwindTarget* prev = self->uw_list; - self->uw_list = UnwindTarget__new(prev, iblock, sp - self->locals); + self->uw_list = UnwindTarget__new(prev, iblock, sp - Frame__locals_sp(self)); } } @@ -149,6 +149,77 @@ int Frame__delglobal(Frame* self, py_Name name) { } } +int Frame__getlocal(Frame* self, py_Name name) { + if(self->is_locals_proxy) { + py_StackRef p0 = py_peek(0); + py_push(self->locals); + py_pushmethod(__getitem__); + py_push(py_name2ref(name)); + bool ok = py_vectorcall(1, 0); + if(!ok) { + if(py_matchexc(tp_KeyError)) { + py_clearexc(p0); + return 0; + } + return -1; + } + return 1; + } else { + py_Ref slot = Frame__getlocal_noproxy(self, name); + if(slot == NULL) return 0; // bad slot + if(py_isnil(slot)) { + UnboundLocalError(name); + return -1; + } + py_assign(py_retval(), slot); + return 1; + } +} + +int Frame__setlocal(Frame* self, py_Name name, py_TValue* val) { + if(self->is_locals_proxy) { + py_push(self->locals); + py_pushmethod(__setitem__); + py_push(py_name2ref(name)); + py_push(val); + bool ok = py_vectorcall(2, 0); + if(!ok) return -1; + return 1; + } else { + py_Ref slot = Frame__getlocal_noproxy(self, name); + if(slot == NULL) return 0; // bad slot + *slot = *val; + return 1; + } +} + +int Frame__dellocal(Frame* self, py_Name name) { + if(self->is_locals_proxy) { + py_StackRef p0 = py_peek(0); + py_push(self->locals); + py_pushmethod(__delitem__); + py_push(py_name2ref(name)); + bool ok = py_vectorcall(1, 0); + if(!ok) { + if(py_matchexc(tp_KeyError)) { + py_clearexc(p0); + return 0; + } + return -1; + } + return 1; + } else { + py_Ref slot = Frame__getlocal_noproxy(self, name); + if(slot == NULL) return 0; // bad slot + if(py_isnil(slot)) { + UnboundLocalError(name); + return -1; + } + py_newnil(slot); + return 1; + } +} + py_StackRef Frame__getlocal_noproxy(Frame* self, py_Name name) { assert(!self->is_locals_proxy); return FastLocals__try_get_by_name(self->locals, self->co, name); diff --git a/src/interpreter/generator.c b/src/interpreter/generator.c index a55b0a3f..9c43bd2b 100644 --- a/src/interpreter/generator.c +++ b/src/interpreter/generator.c @@ -28,10 +28,14 @@ static bool generator__next__(int argc, py_Ref argv) { if(ud->state == 2) return StopIteration(); // reset frame->p0 - int locals_offset = ud->frame->locals - ud->frame->p0; - ud->frame->p0 = py_peek(0); - ud->frame->locals = ud->frame->p0 + locals_offset; - + if(!ud->frame->is_locals_proxy){ + int locals_offset = ud->frame->locals - ud->frame->p0; + ud->frame->p0 = py_peek(0); + ud->frame->locals = ud->frame->p0 + locals_offset; + }else{ + ud->frame->p0 = py_peek(0); + } + // restore the context py_Ref backup = py_getslot(argv, 0); int length = py_list_len(backup); diff --git a/src/public/exec.c b/src/public/exec.c index a4e07eeb..94cc7f5b 100644 --- a/src/public/exec.c +++ b/src/public/exec.c @@ -7,12 +7,11 @@ #include "pocketpy/objects/object.h" #include "pocketpy/interpreter/vm.h" #include "pocketpy/compiler/compiler.h" - -static void code__gc_mark(void* ud) { CodeObject__gc_mark(ud); } +#include py_Type pk_code__register() { py_Type type = pk_newtype("code", tp_object, NULL, (py_Dtor)CodeObject__dtor, false, true); - pk__tp_set_marker(type, code__gc_mark); + pk__tp_set_marker(type, (void (*)(void *))CodeObject__gc_mark); return type; } @@ -57,16 +56,34 @@ bool pk_exec(CodeObject* co, py_Ref module) { assert(module->type == tp_module); py_StackRef sp = vm->stack.sp; - if(co->src->is_dynamic) sp -= 3; // [globals, locals, code] - - const bool is_p0_function = false; - const bool is_locals_proxy = true; - Frame* frame = Frame__new(co, sp, module, module, sp, is_p0_function, is_locals_proxy); + Frame* frame = Frame__new(co, sp, module, module, sp, false, false); VM__push_frame(vm, frame); FrameResult res = VM__run_top_frame(vm); if(res == RES_ERROR) return false; - if(res == RES_RETURN) return true; - c11__unreachable(); + assert(res == RES_RETURN); + return true; +} + +bool pk_execdyn(CodeObject* co, py_Ref module, py_Ref globals, py_Ref locals) { + VM* vm = pk_current_vm; + if(!module) module = &vm->main; + assert(module->type == tp_module); + + py_StackRef sp = vm->stack.sp; + assert(globals != NULL && locals != NULL); + + if(globals->type == tp_namedict) { + globals = py_getslot(globals, 0); + assert(globals->type == tp_module); + } else { + assert(globals->type == tp_dict); + } + Frame* frame = Frame__new(co, sp, module, globals, locals, false, true); + VM__push_frame(vm, frame); + FrameResult res = VM__run_top_frame(vm); + if(res == RES_ERROR) return false; + assert(res == RES_RETURN); + return true; } bool py_exec(const char* source, const char* filename, enum py_CompileMode mode, py_Ref module) { diff --git a/src/public/modules.c b/src/public/modules.c index 9d40d78c..c6491157 100644 --- a/src/public/modules.c +++ b/src/public/modules.c @@ -501,7 +501,7 @@ void py_newglobals(py_Ref out) { if(frame->globals->type == tp_module) { pk_mappingproxy__namedict(out, frame->globals); } else { - *out = *frame->globals; // dict + *out = *frame->globals; // dict } } @@ -511,9 +511,9 @@ void py_newlocals(py_Ref out) { py_newglobals(out); return; } - if(!frame->is_locals_proxy){ + if(!frame->is_locals_proxy) { pk_mappingproxy__locals(out, frame); - }else{ + } else { *out = *frame->locals; } } @@ -529,6 +529,7 @@ static bool _builtins_execdyn(const char* title, int argc, py_Ref argv, enum py_ if(py_isnone(py_arg(1))) { py_newglobals(py_pushtmp()); } else { + if(!py_checktype(py_arg(1), tp_dict)) return false; py_push(py_arg(1)); } py_pushnone(); @@ -538,6 +539,7 @@ static bool _builtins_execdyn(const char* title, int argc, py_Ref argv, enum py_ if(py_isnone(py_arg(1))) { py_newglobals(py_pushtmp()); } else { + if(!py_checktype(py_arg(1), tp_dict)) return false; py_push(py_arg(1)); } py_push(py_arg(2)); @@ -562,12 +564,15 @@ static bool _builtins_execdyn(const char* title, int argc, py_Ref argv, enum py_ // [globals, locals, code] CodeObject* co = py_touserdata(code); if(!co->src->is_dynamic) { + py_shrink(3); if(argc != 1) return ValueError("code object is not dynamic, `globals` and `locals` must be None"); - py_shrink(3); } + Frame* frame = pk_current_vm->top_frame; - return pk_exec(co, frame ? frame->module : NULL); + bool ok = pk_execdyn(co, frame ? frame->module : NULL, py_peek(-3), py_peek(-2)); + py_shrink(3); + return ok; } static bool builtins_exec(int argc, py_Ref argv) { @@ -787,7 +792,9 @@ static bool super__new__(int argc, py_Ref argv) { Function* func = py_touserdata(callable); if(func->clazz != NULL) { class_arg = *(py_Type*)PyObject__userdata(func->clazz); - if(frame->co->nlocals > 0) self_arg = &frame->locals[0]; + if(frame->co->nlocals > 0) { + if(!frame->is_locals_proxy) self_arg = &frame->locals[0]; + } } } }