blueloveTH 1 年之前
父節點
當前提交
4ead475cd1
共有 6 個文件被更改,包括 34 次插入29 次删除
  1. 1 1
      include/pocketpy/pocketpy.h
  2. 10 12
      src/compiler/compiler.c
  3. 11 2
      src/public/exec.c
  4. 2 5
      src/public/modules.c
  5. 10 7
      tests/66_eval.py
  6. 0 2
      tests/67_locals_vs_globals.py

+ 1 - 1
include/pocketpy/pocketpy.h

@@ -536,7 +536,7 @@ PK_API void py_clearexc(py_StackRef p0);
 #define AttributeError(self, n)                                                                    \
     py_exception(tp_AttributeError, "'%t' object has no attribute '%n'", (self)->type, (n))
 #define UnboundLocalError(n)                                                                       \
-    py_exception(tp_UnboundLocalError, "local variable '%n' referenced before assignment", (n))
+    py_exception(tp_UnboundLocalError, "cannot access local variable '%n' where it is not associated with a value", (n))
 
 PK_API bool StopIteration() PY_RAISE;
 PK_API bool KeyError(py_Ref key) PY_RAISE;

+ 10 - 12
src/compiler/compiler.c

@@ -105,18 +105,16 @@ void NameExpr__emit_(Expr* self_, Ctx* ctx) {
         // we know this is a local variable
         Ctx__emit_(ctx, OP_LOAD_FAST, index, self->line);
     } else {
-        Opcode op;
-        // otherwise, if we are running dynamically, force `OP_LOAD_NAME`
-        if(ctx->co->src->is_dynamic) {
-            op = OP_LOAD_NAME;
-            // `OP_LOAD_NAME` won't handle `OP_LOAD_CLASS_GLOBAL`
-            // so `exec()` will raise an error for @property.setter
-        } else {
-            op = ctx->level <= 1 ? OP_LOAD_GLOBAL : OP_LOAD_NONLOCAL;
-            if(ctx->is_compiling_class && self->scope == NAME_GLOBAL) {
-                // if we are compiling a class, we should use `OP_LOAD_CLASS_GLOBAL`
-                // this is for @property.setter
-                op = OP_LOAD_CLASS_GLOBAL;
+        Opcode op = ctx->level <= 1 ? OP_LOAD_GLOBAL : OP_LOAD_NONLOCAL;
+        if(self->scope == NAME_GLOBAL) {
+            if(ctx->co->src->is_dynamic) {
+                op = OP_LOAD_NAME;
+            } else {
+                if(ctx->is_compiling_class) {
+                    // if we are compiling a class, we should use `OP_LOAD_CLASS_GLOBAL`
+                    // this is for @property.setter
+                    op = OP_LOAD_CLASS_GLOBAL;
+                }
             }
         }
         Ctx__emit_(ctx, op, self->name, self->line);

+ 11 - 2
src/public/exec.c

@@ -68,14 +68,23 @@ bool pk_execdyn(CodeObject* co, py_Ref module, py_Ref globals, py_Ref locals) {
     assert(module->type == tp_module);
 
     py_StackRef sp = vm->stack.sp;
-    assert(globals != NULL);
+    assert(globals != NULL && locals != NULL);
 
+    // check globals
     if(globals->type == tp_namedict) {
         globals = py_getslot(globals, 0);
         assert(globals->type == tp_module);
     } else {
-        assert(globals->type == tp_dict);
+        if(!py_istype(globals, tp_dict)) { return TypeError("globals must be a dict object"); }
     }
+    // check locals
+    switch(locals->type) {
+        case tp_locals: break;
+        case tp_dict: break;
+        case tp_nil: break;
+        default: return TypeError("locals must be a dict object");
+    }
+
     Frame* frame = Frame__new(co, sp, module, globals, locals, true);
     VM__push_frame(vm, frame);
     FrameResult res = VM__run_top_frame(vm);

+ 2 - 5
src/public/modules.c

@@ -554,11 +554,10 @@ 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));
             }
             // locals
-            pk_push_special_locals();
+            py_pushnil();
             break;
         }
         case 3: {
@@ -566,14 +565,12 @@ 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));
             }
             // locals
             if(py_isnone(py_arg(2))) {
-                pk_push_special_locals();
+                py_pushnil();
             } else {
-                if(!py_checktype(py_arg(2), tp_dict)) return false;
                 py_push(py_arg(2));
             }
             break;

+ 10 - 7
tests/66_eval.py

@@ -31,12 +31,10 @@ def f():
     )
     assert b == 8
 
-class G: pass
-
 def abc():
-    g = G()
-    exec('a=1', g.__dict__)
-    return g.a
+    g = {}
+    exec('a=1', g)
+    return g['a']
 
 res = abc()
 assert (res==1), res
@@ -69,6 +67,11 @@ except NameError:
     pass
 
 # https://github.com/pocketpy/pocketpy/issues/339
-code = '\nprint(x)\ndef f():\n  print(x)\nf()\n'
+res = []
+
+code = '\nres.append(x)\ndef f():\n  res.append(x)\nf()\n'
 x = 33
-exec(code, {'x': 42})
+exec(code, {'x': 42})
+assert res == [42, 42]
+assert x == 33
+

+ 0 - 2
tests/67_locals_vs_globals.py

@@ -32,11 +32,9 @@ assert "sys" not in globals()
 # With default locals:
 exec("""
 import sys
-assert locals() == globals()
 assert "sys" in locals()
 assert "sys" in globals()
 def main():
-    assert locals() != globals()
     assert "sys" not in locals()  # not the same locals as the outer scope
     assert "sys" in globals()     # but now be can access `sys` via `globals()`
 main()