This commit is contained in:
blueloveTH 2025-02-28 16:57:26 +08:00
parent bf7bfa938c
commit 4ead475cd1
6 changed files with 34 additions and 29 deletions

View File

@ -536,7 +536,7 @@ PK_API void py_clearexc(py_StackRef p0);
#define AttributeError(self, n) \ #define AttributeError(self, n) \
py_exception(tp_AttributeError, "'%t' object has no attribute '%n'", (self)->type, (n)) py_exception(tp_AttributeError, "'%t' object has no attribute '%n'", (self)->type, (n))
#define UnboundLocalError(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 StopIteration() PY_RAISE;
PK_API bool KeyError(py_Ref key) PY_RAISE; PK_API bool KeyError(py_Ref key) PY_RAISE;

View File

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

View File

@ -68,14 +68,23 @@ bool pk_execdyn(CodeObject* co, py_Ref module, py_Ref globals, py_Ref locals) {
assert(module->type == tp_module); assert(module->type == tp_module);
py_StackRef sp = vm->stack.sp; py_StackRef sp = vm->stack.sp;
assert(globals != NULL); assert(globals != NULL && locals != NULL);
// check globals
if(globals->type == tp_namedict) { if(globals->type == tp_namedict) {
globals = py_getslot(globals, 0); globals = py_getslot(globals, 0);
assert(globals->type == tp_module); assert(globals->type == tp_module);
} else { } 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); Frame* frame = Frame__new(co, sp, module, globals, locals, true);
VM__push_frame(vm, frame); VM__push_frame(vm, frame);
FrameResult res = VM__run_top_frame(vm); FrameResult res = VM__run_top_frame(vm);

View File

@ -554,11 +554,10 @@ static bool _builtins_execdyn(const char* title, int argc, py_Ref argv, enum py_
if(py_isnone(py_arg(1))) { if(py_isnone(py_arg(1))) {
py_newglobals(py_pushtmp()); py_newglobals(py_pushtmp());
} else { } else {
if(!py_checktype(py_arg(1), tp_dict)) return false;
py_push(py_arg(1)); py_push(py_arg(1));
} }
// locals // locals
pk_push_special_locals(); py_pushnil();
break; break;
} }
case 3: { 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))) { if(py_isnone(py_arg(1))) {
py_newglobals(py_pushtmp()); py_newglobals(py_pushtmp());
} else { } else {
if(!py_checktype(py_arg(1), tp_dict)) return false;
py_push(py_arg(1)); py_push(py_arg(1));
} }
// locals // locals
if(py_isnone(py_arg(2))) { if(py_isnone(py_arg(2))) {
pk_push_special_locals(); py_pushnil();
} else { } else {
if(!py_checktype(py_arg(2), tp_dict)) return false;
py_push(py_arg(2)); py_push(py_arg(2));
} }
break; break;

View File

@ -31,12 +31,10 @@ def f():
) )
assert b == 8 assert b == 8
class G: pass
def abc(): def abc():
g = G() g = {}
exec('a=1', g.__dict__) exec('a=1', g)
return g.a return g['a']
res = abc() res = abc()
assert (res==1), res assert (res==1), res
@ -69,6 +67,11 @@ except NameError:
pass pass
# https://github.com/pocketpy/pocketpy/issues/339 # 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 x = 33
exec(code, {'x': 42}) exec(code, {'x': 42})
assert res == [42, 42]
assert x == 33

View File

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