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) \
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;

View File

@ -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);

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);
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);

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))) {
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;

View File

@ -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

View File

@ -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()