Browse Source

support `execdyn`

blueloveTH 1 year ago
parent
commit
46fb524a01

+ 7 - 5
include/pocketpy/interpreter/frame.h

@@ -36,17 +36,19 @@ typedef struct Frame {
     const Bytecode* ip;
     const CodeObject* co;
     py_GlobalRef module;
-    bool has_function;     // is p0 a function?
-    py_StackRef p0;        // unwinding base
-    py_StackRef locals;    // locals base
+    py_StackRef p0;      // unwinding base
+    py_StackRef locals;  // locals base
+    bool has_function;   // is p0 a function?
+    bool is_dynamic;     // is dynamic frame?
     UnwindTarget* uw_list;
 } Frame;
 
 Frame* Frame__new(const CodeObject* co,
                   py_GlobalRef module,
-                  bool has_function,
                   py_StackRef p0,
-                  py_StackRef locals);
+                  py_StackRef locals,
+                  bool has_function,
+                  bool is_dynamic);
 void Frame__delete(Frame* self);
 
 int Frame__ip(const Frame* self);

+ 6 - 0
include/pocketpy/interpreter/vm.h

@@ -125,5 +125,11 @@ py_Type pk_property__register();
 py_Type pk_staticmethod__register();
 py_Type pk_classmethod__register();
 py_Type pk_generator__register();
+py_Type pk_namedict__register();
+py_Type pk_locals__register();
 
 py_TValue pk_builtins__register();
+
+/* mappingproxy */
+void pk_mappingproxy__namedict(py_Ref out, py_Ref object);
+void pk_mappingproxy__locals(py_Ref out, Frame* frame);

+ 3 - 2
include/pocketpy/pocketpy.h

@@ -79,7 +79,7 @@ bool py_exec(const char* source,
 /// Assume `globals()` and `locals()` are pushed to the stack.
 /// After the execution, the result will be set to `py_retval()`.
 /// The stack size will be reduced by 2.
-bool py_execdynamic(const char* source,
+bool py_execdyn(const char* source,
                     const char* filename,
                     enum py_CompileMode mode,
                     py_Ref module) PY_RAISE;
@@ -526,7 +526,8 @@ enum py_PredefinedTypes {
     tp_BaseException,  // 2 slots (arg + inner exc)
     tp_Exception,
     tp_bytes,
-    tp_mappingproxy,
+    tp_namedict,
+    tp_locals,
     tp_dict,
     tp_dict_items,    // 1 slot
     tp_property,      // 2 slots (getter + setter)

+ 106 - 39
src/interpreter/ceval.c

@@ -159,28 +159,65 @@ FrameResult VM__run_top_frame(VM* self) {
             }
             case OP_LOAD_NAME: {
                 py_Name name = byte.arg;
-                py_Ref tmp = Frame__f_locals_try_get(frame, name);
-                if(tmp != NULL) {
-                    if(py_isnil(tmp)) {
-                        UnboundLocalError(name);
-                        goto __ERROR;
+                py_TValue* tmp;
+                if(!frame->is_dynamic) {
+                    // locals
+                    tmp = Frame__f_locals_try_get(frame, name);
+                    if(tmp != NULL) {
+                        if(py_isnil(tmp)) {
+                            UnboundLocalError(name);
+                            goto __ERROR;
+                        }
+                        PUSH(tmp);
+                        DISPATCH();
+                    }
+                    // closure
+                    tmp = Frame__f_closure_try_get(frame, name);
+                    if(tmp != NULL) {
+                        PUSH(tmp);
+                        DISPATCH();
+                    }
+                    // globals
+                    tmp = py_getdict(frame->module, name);
+                    if(tmp != NULL) {
+                        PUSH(tmp);
+                        DISPATCH();
+                    }
+                } else {
+                    py_newstr(SP()++, py_name2str(name));
+                    // locals
+                    if(py_getitem(&frame->p0[1], TOP())) {
+                        py_assign(TOP(), py_retval());
+                        DISPATCH();
+                    } else {
+                        if(py_matchexc(tp_KeyError)) {
+                            py_clearexc(NULL);
+                        } else {
+                            goto __ERROR;
+                        }
+                    }
+                    // closure
+                    tmp = Frame__f_closure_try_get(frame, name);
+                    if(tmp != NULL) {
+                        py_assign(TOP(), tmp);
+                        DISPATCH();
+                    }
+                    // globals
+                    if(py_getitem(&frame->p0[0], TOP())) {
+                        py_assign(TOP(), py_retval());
+                        DISPATCH();
+                    } else {
+                        if(py_matchexc(tp_KeyError)) {
+                            py_clearexc(NULL);
+                        } else {
+                            goto __ERROR;
+                        }
                     }
-                    PUSH(tmp);
-                    DISPATCH();
-                }
-                tmp = Frame__f_closure_try_get(frame, name);
-                if(tmp != NULL) {
-                    PUSH(tmp);
-                    DISPATCH();
-                }
-                tmp = py_getdict(frame->module, name);
-                if(tmp != NULL) {
-                    PUSH(tmp);
-                    DISPATCH();
                 }
+                // builtins
                 tmp = py_getdict(&self->builtins, name);
                 if(tmp != NULL) {
-                    PUSH(tmp);
+                    py_assign(TOP(), tmp);
                     DISPATCH();
                 }
                 NameError(name);
@@ -284,19 +321,35 @@ FrameResult VM__run_top_frame(VM* self) {
             }
             case OP_STORE_FAST: frame->locals[byte.arg] = POPX(); DISPATCH();
             case OP_STORE_NAME: {
+                assert(frame->is_dynamic);
                 py_Name name = byte.arg;
-                if(frame->has_function) {
-                    py_Ref slot = Frame__f_locals_try_get(frame, name);
-                    if(slot != NULL) {
-                        *slot = *TOP();  // store in locals if possible
+                py_newstr(SP()++, py_name2str(name));
+                // [value, name]
+                if(!py_isnone(&frame->p0[1])){
+                    // locals
+                    if(py_setitem(&frame->p0[1], TOP(), SECOND())) {
+                        STACK_SHRINK(2);
+                        DISPATCH();
                     } else {
-                        NameError(name);
+                        if(py_matchexc(tp_KeyError)) {
+                            py_clearexc(NULL);
+                            NameError(name);
+                        }
+                        goto __ERROR;
+                    }
+                }else{
+                    // globals
+                    if(py_setitem(&frame->p0[0], TOP(), SECOND())) {
+                        STACK_SHRINK(2);
+                        DISPATCH();
+                    } else {
+                        if(py_matchexc(tp_KeyError)) {
+                            py_clearexc(NULL);
+                            NameError(name);
+                        }
                         goto __ERROR;
                     }
-                } else {
-                    py_setdict(frame->module, name, TOP());
                 }
-                POP();
                 DISPATCH();
             }
             case OP_STORE_GLOBAL: {
@@ -339,19 +392,31 @@ FrameResult VM__run_top_frame(VM* self) {
                 DISPATCH();
             }
             case OP_DELETE_NAME: {
+                assert(frame->is_dynamic);
                 py_Name name = byte.arg;
-                if(frame->has_function) {
-                    py_TValue* slot = Frame__f_locals_try_get(frame, name);
-                    if(slot) {
-                        py_newnil(slot);
+                py_newstr(SP()++, py_name2str(name));
+                if(!py_isnone(&frame->p0[1])){
+                    // locals
+                    if(py_delitem(&frame->p0[1], TOP())) {
+                        POP();
+                        DISPATCH();
                     } else {
-                        NameError(name);
+                        if(py_matchexc(tp_KeyError)) {
+                            py_clearexc(NULL);
+                            NameError(name);
+                        }
                         goto __ERROR;
                     }
-                } else {
-                    bool ok = py_deldict(frame->module, name);
-                    if(!ok) {
-                        NameError(name);
+                }else{
+                    // globals
+                    if(py_delitem(&frame->p0[0], TOP())) {
+                        POP();
+                        DISPATCH();
+                    } else {
+                        if(py_matchexc(tp_KeyError)) {
+                            py_clearexc(NULL);
+                            NameError(name);
+                        }
                         goto __ERROR;
                     }
                 }
@@ -889,8 +954,9 @@ FrameResult VM__run_top_frame(VM* self) {
             case OP_WITH_ENTER: {
                 // [expr]
                 py_push(TOP());
-                if(!py_pushmethod(__enter__)){
-                    TypeError("'%t' object does not support the context manager protocol", TOP()->type);
+                if(!py_pushmethod(__enter__)) {
+                    TypeError("'%t' object does not support the context manager protocol",
+                              TOP()->type);
                     goto __ERROR;
                 }
                 if(!py_vectorcall(0, 0)) goto __ERROR;
@@ -900,8 +966,9 @@ FrameResult VM__run_top_frame(VM* self) {
             case OP_WITH_EXIT: {
                 // [expr]
                 py_push(TOP());
-                if(!py_pushmethod(__exit__)){
-                    TypeError("'%t' object does not support the context manager protocol", TOP()->type);
+                if(!py_pushmethod(__exit__)) {
+                    TypeError("'%t' object does not support the context manager protocol",
+                              TOP()->type);
                     goto __ERROR;
                 }
                 if(!py_vectorcall(0, 0)) goto __ERROR;

+ 7 - 3
src/interpreter/frame.c

@@ -2,6 +2,7 @@
 #include "pocketpy/objects/codeobject.h"
 #include "pocketpy/objects/object.h"
 #include "pocketpy/pocketpy.h"
+#include <stdbool.h>
 
 void ValueStack__ctor(ValueStack* self) {
     self->sp = self->begin;
@@ -37,18 +38,20 @@ void UnwindTarget__delete(UnwindTarget* self) { free(self); }
 
 Frame* Frame__new(const CodeObject* co,
                   py_GlobalRef module,
-                  bool has_function,
                   py_StackRef p0,
-                  py_StackRef locals) {
+                  py_StackRef locals,
+                  bool has_function,
+                  bool is_dynamic) {
     static_assert(sizeof(Frame) <= kPoolFrameBlockSize, "!(sizeof(Frame) <= kPoolFrameBlockSize)");
     Frame* self = PoolFrame_alloc();
     self->f_back = NULL;
     self->ip = (Bytecode*)co->codes.data - 1;
     self->co = co;
     self->module = module;
-    self->has_function = has_function;
     self->p0 = p0;
     self->locals = locals;
+    self->has_function = has_function;
+    self->is_dynamic = is_dynamic;
     self->uw_list = NULL;
     return self;
 }
@@ -150,5 +153,6 @@ int Frame__iblock(const Frame* self) {
 }
 
 py_TValue* Frame__f_locals_try_get(Frame* self, py_Name name) {
+    assert(!self->is_dynamic);
     return FastLocals__try_get_by_name(self->locals, self->co, name);
 }

+ 7 - 11
src/interpreter/vm.c

@@ -7,6 +7,7 @@
 #include "pocketpy/objects/base.h"
 #include "pocketpy/common/_generated.h"
 #include "pocketpy/pocketpy.h"
+#include <stdbool.h>
 
 static char* pk_default_import_file(const char* path) {
 #if PK_ENABLE_OS
@@ -112,7 +113,8 @@ void VM__ctor(VM* self) {
     validate(tp_BaseException, pk_BaseException__register());
     validate(tp_Exception, pk_Exception__register());
     validate(tp_bytes, pk_bytes__register());
-    validate(tp_mappingproxy, pk_newtype("mappingproxy", tp_object, NULL, NULL, false, true));
+    validate(tp_namedict, pk_namedict__register());
+    validate(tp_locals, pk_locals__register());
 
     validate(tp_dict, pk_dict__register());
     validate(tp_dict_items, pk_dict_items__register());
@@ -427,10 +429,10 @@ FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall
                 memcpy(argv, self->__vectorcall_buffer, co->nlocals * sizeof(py_TValue));
                 // submit the call
                 if(!fn->cfunc) {
-                    VM__push_frame(self, Frame__new(co, &fn->module, true, p0, argv));
+                    VM__push_frame(self, Frame__new(co, &fn->module, p0, argv, true, false));
                     return opcall ? RES_CALL : VM__run_top_frame(self);
                 } else {
-                    bool ok = fn->cfunc(co->nlocals, argv);
+                    bool ok = py_callcfunc(fn->cfunc, co->nlocals, argv);
                     self->stack.sp = p0;
                     return ok ? RES_RETURN : RES_ERROR;
                 }
@@ -451,22 +453,16 @@ FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall
                 // initialize local variables to py_NIL
                 memset(p1, 0, (char*)self->stack.sp - (char*)p1);
                 // submit the call
-                VM__push_frame(self, Frame__new(co, &fn->module, true, p0, argv));
+                VM__push_frame(self, Frame__new(co, &fn->module, p0, argv, true, false));
                 return opcall ? RES_CALL : VM__run_top_frame(self);
             case FuncType_GENERATOR: {
                 bool ok = prepare_py_call(self->__vectorcall_buffer, argv, p1, kwargc, fn->decl);
                 if(!ok) return RES_ERROR;
-                Frame* frame = Frame__new(co, &fn->module, false, p0, argv);
+                Frame* frame = Frame__new(co, &fn->module, p0, argv, false, false);
                 pk_newgenerator(py_retval(), frame, self->__vectorcall_buffer, co->nlocals);
                 self->stack.sp = p0;
                 return RES_RETURN;
             }
-                // prepare_py_call(__vectorcall_buffer, args, kwargs, fn.decl);
-                // s_data.reset(p0);
-                // callstack.emplace(nullptr, co, fn._module, callable.get(), nullptr);
-                // return __py_generator(
-                //     callstack.popx(),
-                //     ArgsView(__vectorcall_buffer, __vectorcall_buffer + co->nlocals));
             default: c11__unreachedable();
         };
 

+ 8 - 8
src/public/internal.c

@@ -7,6 +7,7 @@
 #include "pocketpy/objects/object.h"
 #include "pocketpy/interpreter/vm.h"
 #include "pocketpy/compiler/compiler.h"
+#include <stdbool.h>
 #include <stdint.h>
 
 VM* pk_current_vm;
@@ -103,7 +104,7 @@ static bool _py_exec(const char* source,
         sp -= 2;
     }
 
-    Frame* frame = Frame__new(&co, module, false, sp, sp);
+    Frame* frame = Frame__new(&co, module, sp, sp, false, is_dynamic);
     VM__push_frame(vm, frame);
     FrameResult res = VM__run_top_frame(vm);
     CodeObject__dtor(&co);
@@ -117,10 +118,7 @@ bool py_exec(const char* source, const char* filename, enum py_CompileMode mode,
     return _py_exec(source, filename, mode, module, false);
 }
 
-bool py_execdynamic(const char* source,
-                    const char* filename,
-                    enum py_CompileMode mode,
-                    py_Ref module) {
+bool py_execdyn(const char* source, const char* filename, enum py_CompileMode mode, py_Ref module) {
     return _py_exec(source, filename, mode, module, true);
 }
 
@@ -128,11 +126,14 @@ bool py_call(py_Ref f, int argc, py_Ref argv) {
     if(f->type == tp_nativefunc) {
         return py_callcfunc(f->_cfunc, argc, argv);
     } else {
+        py_StackRef p0 = py_peek(0);
         py_push(f);
         py_pushnil();
         for(int i = 0; i < argc; i++)
             py_push(py_offset(argv, i));
-        return py_vectorcall(argc, 0);
+        bool ok = py_vectorcall(argc, 0);
+        pk_current_vm->stack.sp = p0;
+        return ok;
     }
 }
 
@@ -150,8 +151,7 @@ bool py_callcfunc(py_CFunction f, int argc, py_Ref argv) {
 }
 
 bool py_vectorcall(uint16_t argc, uint16_t kwargc) {
-    VM* vm = pk_current_vm;
-    return VM__vectorcall(vm, argc, kwargc, false) != RES_ERROR;
+    return VM__vectorcall(pk_current_vm, argc, kwargc, false) != RES_ERROR;
 }
 
 py_Ref py_retval() { return &pk_current_vm->last_retval; }

+ 65 - 21
src/public/modules.c

@@ -252,20 +252,6 @@ static bool builtins_print(int argc, py_Ref argv) {
     return true;
 }
 
-static bool builtins_exec(int argc, py_Ref argv) {
-    PY_CHECK_ARGC(1);
-    PY_CHECK_ARG_TYPE(0, tp_str);
-    Frame* frame = pk_current_vm->top_frame;
-    return py_exec(py_tostr(argv), "<exec>", EXEC_MODE, frame->module);
-}
-
-static bool builtins_eval(int argc, py_Ref argv) {
-    PY_CHECK_ARGC(1);
-    PY_CHECK_ARG_TYPE(0, tp_str);
-    Frame* frame = pk_current_vm->top_frame;
-    return py_exec(py_tostr(argv), "<eval>", EVAL_MODE, frame->module);
-}
-
 static bool builtins_isinstance(int argc, py_Ref argv) {
     PY_CHECK_ARGC(2);
     if(py_istuple(py_arg(1))) {
@@ -301,10 +287,9 @@ static bool builtins_getattr(int argc, py_Ref argv) {
     if(argc == 2) {
         return py_getattr(py_arg(0), name);
     } else if(argc == 3) {
-        py_StackRef p0 = py_peek(0);
         bool ok = py_getattr(py_arg(0), name);
         if(!ok && py_matchexc(tp_AttributeError)) {
-            py_clearexc(p0);
+            py_clearexc(NULL);
             py_assign(py_retval(), py_arg(2));
             return true;  // default value
         }
@@ -327,14 +312,13 @@ static bool builtins_hasattr(int argc, py_Ref argv) {
     PY_CHECK_ARGC(2);
     PY_CHECK_ARG_TYPE(1, tp_str);
     py_Name name = py_namev(py_tosv(py_arg(1)));
-    py_StackRef p0 = py_peek(0);
     bool ok = py_getattr(py_arg(0), name);
     if(ok) {
         py_newbool(py_retval(), true);
         return true;
     }
     if(py_matchexc(tp_AttributeError)) {
-        py_clearexc(p0);
+        py_clearexc(NULL);
         py_newbool(py_retval(), false);
         return true;
     }
@@ -369,6 +353,64 @@ static bool builtins_ord(int argc, py_Ref argv) {
     return true;
 }
 
+static bool builtins_globals(int argc, py_Ref argv) {
+    Frame* frame = pk_current_vm->top_frame;
+    if(frame->is_dynamic){
+        py_assign(py_retval(), &frame->p0[0]);
+        return true;
+    }
+    pk_mappingproxy__namedict(py_retval(), frame->module);
+    return true;
+}
+
+static bool builtins_locals(int argc, py_Ref argv){
+    Frame* frame = pk_current_vm->top_frame;
+    if(frame->is_dynamic){
+        py_assign(py_retval(), &frame->p0[1]);
+        return true;
+    }
+    if(!frame->has_function) return builtins_globals(argc, argv);
+    pk_mappingproxy__locals(py_retval(), frame);
+    return true;
+}
+
+static bool _builtins_execdyn(const char* title, int argc, py_Ref argv, enum py_CompileMode mode) {
+    PY_CHECK_ARG_TYPE(0, tp_str);
+    Frame* frame = pk_current_vm->top_frame;
+    switch(argc){
+        case 1: {
+            // system globals + system locals
+            if(!builtins_globals(0, NULL)) return false;
+            py_push(py_retval());
+            if(!builtins_locals(0, NULL)) return false;
+            py_push(py_retval());
+            break;
+        }
+        case 2: {
+            // user globals + user globals
+            py_push(py_arg(1));
+            py_push(py_arg(1));
+            break;
+        }
+        case 3: {
+            // user globals + user locals
+            py_push(py_arg(1));
+            py_push(py_arg(2));
+            break;
+        }
+        default: return TypeError("%s() takes at most 3 arguments", title);
+    }
+    return py_execdyn(py_tostr(argv), "<string>", mode, frame->module);
+}
+
+static bool builtins_exec(int argc, py_Ref argv){
+    return _builtins_execdyn("exec", argc, argv, EXEC_MODE);
+}
+
+static bool builtins_eval(int argc, py_Ref argv) {
+    return _builtins_execdyn("eval", argc, argv, EVAL_MODE);
+}
+
 static bool NoneType__repr__(int argc, py_Ref argv) {
     py_newstr(py_retval(), "None");
     return true;
@@ -396,9 +438,6 @@ py_TValue pk_builtins__register() {
     py_bindfunc(builtins, "abs", builtins_abs);
     py_bindfunc(builtins, "divmod", builtins_divmod);
 
-    py_bindfunc(builtins, "exec", builtins_exec);
-    py_bindfunc(builtins, "eval", builtins_eval);
-
     py_bind(builtins, "print(*args, sep=' ', end='\\n')", builtins_print);
 
     py_bindfunc(builtins, "isinstance", builtins_isinstance);
@@ -412,6 +451,11 @@ py_TValue pk_builtins__register() {
     py_bindfunc(builtins, "chr", builtins_chr);
     py_bindfunc(builtins, "ord", builtins_ord);
 
+    py_bindfunc(builtins, "globals", builtins_globals);
+    py_bindfunc(builtins, "locals", builtins_locals);
+    py_bindfunc(builtins, "exec", builtins_exec);
+    py_bindfunc(builtins, "eval", builtins_eval);
+
     // some patches
     py_bindmagic(tp_NoneType, __repr__, NoneType__repr__);
     py_bindmagic(tp_ellipsis, __repr__, ellipsis__repr__);

+ 123 - 0
src/public/py_mappingproxy.c

@@ -0,0 +1,123 @@
+#include "pocketpy/pocketpy.h"
+
+#include "pocketpy/common/utils.h"
+#include "pocketpy/objects/object.h"
+#include "pocketpy/interpreter/vm.h"
+#include "pocketpy/common/sstream.h"
+
+
+void pk_mappingproxy__namedict(py_Ref out, py_Ref object){
+    py_newobject(py_retval(), tp_namedict, 1, 0);
+    assert(object->is_ptr && object->_obj->slots == -1);
+    py_setslot(py_retval(), 0, object);
+}
+
+static bool namedict__getitem__(int argc, py_Ref argv){
+    PY_CHECK_ARGC(2);
+    PY_CHECK_ARG_TYPE(1, tp_str);
+    py_Name name = py_namev(py_tosv(py_arg(1)));
+    py_Ref res = py_getdict(py_getslot(argv, 0), name);
+    if(!res) return KeyError(py_arg(1));
+    py_assign(py_retval(), res);
+    return true;
+}
+
+static bool namedict__setitem__(int argc, py_Ref argv){
+    PY_CHECK_ARGC(3);
+    PY_CHECK_ARG_TYPE(1, tp_str);
+    py_Name name = py_namev(py_tosv(py_arg(1)));
+    py_setdict(py_getslot(argv, 0), name, py_arg(2));
+    py_newnone(py_retval());
+    return true;
+}
+
+static bool namedict__delitem__(int argc, py_Ref argv){
+    PY_CHECK_ARGC(2);
+    PY_CHECK_ARG_TYPE(1, tp_str);
+    py_Name name = py_namev(py_tosv(py_arg(1)));
+    if(!py_deldict(py_getslot(argv, 0), name)) return KeyError(py_arg(1));
+    py_newnone(py_retval());
+    return true;
+}
+
+static bool namedict__contains__(int argc, py_Ref argv){
+    PY_CHECK_ARGC(2);
+    PY_CHECK_ARG_TYPE(1, tp_str);
+    py_Name name = py_namev(py_tosv(py_arg(1)));
+    py_Ref res = py_getdict(py_getslot(argv, 0), name);
+    py_newbool(py_retval(), res != NULL);
+    return true;
+}
+
+py_Type pk_namedict__register() {
+    py_Type type = pk_newtype("namedict", tp_object, NULL, NULL, false, true);
+
+    py_bindmagic(type, __getitem__, namedict__getitem__);
+    py_bindmagic(type, __setitem__, namedict__setitem__);
+    py_bindmagic(type, __delitem__, namedict__delitem__);
+    py_bindmagic(type, __contains__, namedict__contains__);
+    return type;
+}
+
+//////////////////////
+
+void pk_mappingproxy__locals(py_Ref out, Frame* frame){
+    assert(frame->has_function && !frame->is_dynamic);
+    Frame** ud = py_newobject(py_retval(), tp_locals, 0, sizeof(Frame*));
+    *ud = frame;
+}
+
+static bool locals__getitem__(int argc, py_Ref argv){
+    PY_CHECK_ARGC(2);
+    PY_CHECK_ARG_TYPE(1, tp_str);
+    Frame** ud = py_touserdata(argv);
+    py_Name name = py_namev(py_tosv(py_arg(1)));
+    py_Ref slot = Frame__f_locals_try_get(*ud, name);
+    if(!slot || py_isnil(slot)) return KeyError(py_arg(1));
+    py_assign(py_retval(), slot);
+    return true;
+}
+
+static bool locals__setitem__(int argc, py_Ref argv){
+    PY_CHECK_ARGC(3);
+    PY_CHECK_ARG_TYPE(1, tp_str);
+    Frame** ud = py_touserdata(argv);
+    py_Name name = py_namev(py_tosv(py_arg(1)));
+    py_Ref slot = Frame__f_locals_try_get(*ud, name);
+    if(!slot) return KeyError(py_arg(1));
+    py_assign(slot, py_arg(2));
+    py_newnone(py_retval());
+    return true;
+}
+
+static bool locals__delitem__(int argc, py_Ref argv){
+    PY_CHECK_ARGC(2);
+    PY_CHECK_ARG_TYPE(1, tp_str);
+    Frame** ud = py_touserdata(argv);
+    py_Name name = py_namev(py_tosv(py_arg(1)));
+    py_Ref res = Frame__f_locals_try_get(*ud, name);
+    if(!res || py_isnil(res)) return KeyError(py_arg(1));
+    py_newnil(res);
+    py_newnone(py_retval());
+    return true;
+}
+
+static bool locals__contains__(int argc, py_Ref argv){
+    PY_CHECK_ARGC(2);
+    PY_CHECK_ARG_TYPE(1, tp_str);
+    Frame** ud = py_touserdata(argv);
+    py_Name name = py_namev(py_tosv(py_arg(1)));
+    py_Ref slot = Frame__f_locals_try_get(*ud, name);
+    py_newbool(py_retval(), slot && !py_isnil(slot));
+    return true;
+}
+
+py_Type pk_locals__register() {
+    py_Type type = pk_newtype("locals", tp_locals, NULL, NULL, false, true);
+
+    py_bindmagic(type, __getitem__, locals__getitem__);
+    py_bindmagic(type, __setitem__, locals__setitem__);
+    py_bindmagic(type, __delitem__, locals__delitem__);
+    py_bindmagic(type, __contains__, locals__contains__);
+    return type;
+}

+ 11 - 0
src/public/py_object.c

@@ -44,6 +44,16 @@ static bool object__repr__(int argc, py_Ref argv) {
     return true;
 }
 
+static bool object__dict__getter(int argc, py_Ref argv) {
+    PY_CHECK_ARGC(1);
+    if(argv->is_ptr && argv->_obj->slots == -1){
+        pk_mappingproxy__namedict(py_retval(), argv);
+    }else{
+        py_newnone(py_retval());
+    }
+    return true;
+}
+
 static bool type__repr__(int argc, py_Ref argv) {
     PY_CHECK_ARGC(1);
     c11_sbuf buf;
@@ -88,6 +98,7 @@ void pk_object__register() {
     py_bindmagic(tp_object, __eq__, object__eq__);
     py_bindmagic(tp_object, __ne__, object__ne__);
     py_bindmagic(tp_object, __repr__, object__repr__);
+    py_bindproperty(tp_object, "__dict__", object__dict__getter, NULL);
 
     py_bindmagic(tp_type, __repr__, type__repr__);
     py_bindmagic(tp_type, __new__, type__new__);

+ 1 - 2
src/public/py_ops.c

@@ -75,10 +75,9 @@ int py_next(py_Ref val) {
         TypeError("'%t' object is not an iterator", val->type);
         return -1;
     }
-    py_StackRef p0 = py_peek(0);
     if(py_call(tmp, 1, val)) return true;
     if(vm->curr_exception.type == tp_StopIteration) {
-        py_clearexc(p0);
+        py_clearexc(NULL);
         vm->is_stopiteration = true;
     }
     int retval = vm->is_stopiteration ? 0 : -1;

+ 15 - 15
tests/99_builtin_func.py

@@ -327,8 +327,8 @@ assert s.step == 3
 # test slice.__repr__
 assert type(repr(slice(1,1,1))) is str
 
-# /************ mappingproxy ************/
-# test mappingproxy.keys:
+# /************ namedict ************/
+# test namedict.keys:
 class A():
     def __init__(self):
         self.a = 10
@@ -336,12 +336,12 @@ class A():
         pass
 
 
-my_mappingproxy = A().__dict__
-assert type(my_mappingproxy.keys()) is list
+my_namedict = A().__dict__
+assert type(my_namedict.keys()) is list
 
 
 # 未完全测试准确性-----------------------------------------------
-# test mappingproxy.values:
+# test namedict.values:
 class A():
     def __init__(self):
         self.a = 10
@@ -349,8 +349,8 @@ class A():
         pass
 
 
-my_mappingproxy = A().__dict__
-assert type(my_mappingproxy.values()) is list
+my_namedict = A().__dict__
+assert type(my_namedict.values()) is list
 
 
 class A():
@@ -360,8 +360,8 @@ class A():
         pass
 
 
-my_mappingproxy = A().__dict__
-assert type(len(my_mappingproxy)) is int
+my_namedict = A().__dict__
+assert type(len(my_namedict)) is int
 
 
 class A():
@@ -371,11 +371,11 @@ class A():
         pass
 
 
-my_mappingproxy = A().__dict__
+my_namedict = A().__dict__
 
 try:
-    hash(my_mappingproxy)
-    print('未能拦截错误, 在测试 mappingproxy.__hash__')
+    hash(my_namedict)
+    print('未能拦截错误, 在测试 namedict.__hash__')
     exit(1)
 except TypeError:
     pass
@@ -393,7 +393,7 @@ except TypeError:
     pass
 
 # 未完全测试准确性-----------------------------------------------
-# test mappingproxy.__repr__:
+# test namedict.__repr__:
 class A():
     def __init__(self):
         self.a = 10
@@ -401,8 +401,8 @@ class A():
         pass
 
 
-my_mappingproxy = A().__dict__
-assert type(repr(my_mappingproxy)) is str
+my_namedict = A().__dict__
+assert type(repr(my_namedict)) is str
 
 
 # /************ dict ************/