From 5a72730853aa958a792418725bf81fd04c1247da Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Mon, 24 Feb 2025 20:56:49 +0800 Subject: [PATCH 01/18] backup --- .github/workflows.zip | Bin 0 -> 4423 bytes .github/workflows/main.yml | 207 -------------------------- .github/workflows/pybind11.yml | 76 ---------- .github/workflows/website.yml | 42 ------ include/pocketpy/interpreter/frame.h | 31 ++-- include/pocketpy/objects/base.h | 1 + include/pocketpy/objects/codeobject.h | 12 +- include/pocketpy/pocketpy.h | 2 + pyrightconfig.json | 1 + src/common/strname.c | 41 +++-- src/compiler/compiler.c | 40 +++-- src/interpreter/ceval.c | 72 +++++---- src/interpreter/frame.c | 78 +++++++--- src/interpreter/vm.c | 10 +- src/modules/enum.c | 2 +- src/objects/codeobject.c | 7 +- src/public/exec.c | 4 +- src/public/modules.c | 21 +-- src/public/py_mappingproxy.c | 33 ++-- src/public/py_object.c | 2 +- src/public/py_ops.c | 2 +- src/public/values.c | 2 +- tests/66_eval.py | 5 + tests/67_locals_vs_globals.py | 45 ++++++ 24 files changed, 276 insertions(+), 460 deletions(-) create mode 100644 .github/workflows.zip delete mode 100644 .github/workflows/main.yml delete mode 100644 .github/workflows/pybind11.yml delete mode 100644 .github/workflows/website.yml create mode 100644 tests/67_locals_vs_globals.py diff --git a/.github/workflows.zip b/.github/workflows.zip new file mode 100644 index 0000000000000000000000000000000000000000..9daa7ca1b017d212e1e75d5029784cde9ab1e854 GIT binary patch literal 4423 zcmb7|2{@Gd+sDT?Ci^zXHbz-bS+f<#p5|}OmL>>Ab%N43uXX)Y)+MX9QmM}1JH~BD%v$F0O0%gO~2;>0YJax87JrB zBIj{%Fw)X7F}IeHcKDg{my7{k&Hv2k;s|zecXW0n85vrUSgjIFBdF7PX~fz1sg|XL zfy+y>Y;*!T+EImu>8X;MA0ta+*r9z`47M`I+B7|@A}9Ykh9(n%IdR`RD=v5UN=uhf zbFN@++vCwB7J#V%J;=lOTB;qnJxYsv&C~500xd59!1>D%q;<_5%uzmm@bA4RlBbYF zJ}BpZOk?r-BjXNPuGoGf1#7A3uGBDUTuWp*p|xiKtbI70S}&@?`B{kYva1@M%)nn= zWo(j8n%bUp8%ekOGyMg`qr<5+-9EhIA^`+yMJ<8IW9kgS+3g5O8M;kSbLAj7*hb_0xpof;dAOhsk& z!VJ)xY=2$~N3)yIpZqp=m3d}TmayWnqp|yK+lRLka+>hv-Q~D`8m+XtVc_v|TY&n! zV*DYAmHzfaEn_r-^I}9L>NsDCK+4GB>jf|l3NYls9z1e1`Nt62&&y9*$mAS6{of+z zEfG#S4ER$T zQxHXFA(ZnN0at?0Zt$x#DkezkY>T#v8r$d}y|?(t_Nn!;-Z0F9Yl6btv@x40+bM1u zB+!U9nRxyWoi9qWjbZ$&1osPVS4q;TLH7%^Xib`mu^(c9R<@ae-R%k|#GL(4=SaXbpkB`8r zA79Y5lk0UdCJH=7yFA7M=HKuSVe73XSGZaVb=_?1U9aM02+;)SV(t9#;Nc8VcbT(o%_ZPXd9?V#?UP(eMedLJ;1xK7v*w zmmJ&B{aTRcWh*k*3`@F745YjQ4J6?90*+4=m}l#y0^rl%+`fI2$MsoQc=jEASyFwt(=ZccYzJm))pq-MHgdohlt;fA44Csypny&P=WF5+D{6@?`$ znW*(SGVBy$_E-wwRQk!%4h;YR{)MF=xU;_p3jPx%A-1J|L#fK#K52-I(o#)*p{xnw z@LKsD+1m+;g@qroo~DZhjn((YH^90YKZPqp;DOo8T5>WsRm(QU{r!q_+gVayWvyCU zo(=*>FrO$DEDSRI%w8UCQh0rcq}nl97qqD;{6*u1RFW}F#N&i0hw@nQdWIj$W+7=_ zL3qH}O)}v8fJK*(@T%69sY#E13R8x@L9C@tA?``N$xGs`9v>r8(-)gg$j(lqbdm<#U+vyR2sJ=44EzhB|mYX}RBFE$~vO4)S%ZmP)d(>xrqFKh4+BOG1K7E;ltc zyRBl9uV8{&-rSmvHahg0;8-0FGbiKsb4L|3pI?lH&d^ zyUfdT2p^&n+qVJ-EgT&+?xuyZ2OmgcV5oao7@@uj-;!kl8of}u2(y3@t~?LGhl*zm z=a-M^**iXqmw={q#rLZR>6adzX3u*o;zmy|pTfdM@Kcv*A@Ruhr#v}!HIc|3BYV<0 zbAhzEc}u8aCTqQQ**E=8@e3P)H1ICQAF=*+H48K6>~tvjviOTZahJ?=G!W0^K3qmH zPnlK&z0YkPREbkX5L6N9(yGI!8{VJPj^Trv3%*b-W6aIxd8Y7i>4xlQMMLX66OQhc zdTSSpK09Mw!%SDOSTIa(#=wws-B}eW<^fwZ70XR%_*^!tmU<++`&2anKcNzlHZb|J zbd=BoPh3y{j#lL1Zp*bTOsh<72*i%vNnim=^z?E$6e3Y+?fHNQI7aVUOU<*|x(rxZ z?s@81|LxtG0PWTB{tLB^+E5HMBKw_KGhPLP#ReQIJ7h!hg=r-%_xM?n zJ~*&)gWHa!s$1EItsJ`+<5ZM4oSS9 z=>p@~hisAsF!%|14vZYj%<a{fqdI6%Z#+#7THsQXnd7pvoBNE$) z$z~@)RzYCr+Ds|VrZevR4CgN#W=K4;QAe`_*Mgtx=T8KFj8$!iaXwZKXwJ{Xd%R3+ zV2nZByQX+c1>_=HqU9hIUWIB)hzbP1Y_F|71G-w()X_hpXVO3#*qS-NE;<}kYBgwS zvm^5%zW|&x;_?8a>k$3YFqz zDdpB?94`L>xW$wn9d4F`@phbcwmX!cWq-99%Sm?>&&{bfRN?RkoQPmQf0X)c_m{(e z{grNiOf?tOeEGq|@craC`-cr**4JL413!9XF*DA&iWUt(E0h^`1pDYaXI)cDCsXK` z;*K!Zcj=c&J{&u)o#RvUI>o!X)2GI}tLC&%^@L^V)5yY1XGi<_!t}pxV!vA5^G{lz ztLM6LN~f}{&i4M*sPwORi!koNNjXda*VN>GSD>kX4CZ9+!(o8(YMZ^^^&B@?xkBxWSd~>HN+S*}^m6-8o9Zq9=?R`gO znoL)C^6QC~-9$ z<&k&oHw^_B@|2QDa|I-LRfK*Nv6=OqTP%Zd^dJJ&DaS3z1!++jivIgiFO5KsBj^AC z%9efbW==5~g5=Horw`h%YY*Jle@oW>zYP(Ws|zS^EpRdWWO#ho^D7%>A-&w3?lLDS zeba+R#_6uulRN#}5>Zie(fqqJ{96tK0E=3btG(O#E`k3~pZIs)A5L+<4Wl^4AF&vD zhJ5*RUcccRO%3)cQ0&}(*Fmv!KLQs?N43B6gYCru=I);b_PYj(dHE5zNHq1nTiBb? zZ&;cAwJ1JhzpAA8kRMSOd6=vu_DcMYE7@OzqEGj06N*0l5p|JpGUR(TenYM9FF_GG z`&SG_> ~/.zshrc - source ~/.zshrc - - - name: Set up CMake - uses: jwlawson/actions-setup-cmake@v1.10 - - - name: Test - run: | - cd include/pybind11/tests - cmake -B build -DENABLE_TEST=ON - cmake --build build --config Release --parallel - ./build/PKBIND_TEST diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml deleted file mode 100644 index a2add796..00000000 --- a/.github/workflows/website.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: website - -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] - -permissions: - contents: write - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - ################################################### - - uses: actions/setup-node@v3.1.1 - - name: Retype build - run: | - python scripts/gen_docs.py - cd docs - npm install retypeapp -g - retype build - ################################################### - - name: Setup emsdk - uses: mymindstorm/setup-emsdk@v12 - with: - version: latest - actions-cache-folder: 'emsdk-cache' - - name: Compile - run: | - bash build_web.sh - mv web docs/.retype/static - ################################################### - - uses: crazy-max/ghaction-github-pages@v3 - with: - target_branch: gh-pages - build_dir: docs/.retype - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - if: github.ref == 'refs/heads/main' diff --git a/include/pocketpy/interpreter/frame.h b/include/pocketpy/interpreter/frame.h index 5a8b1709..04e5a6ab 100644 --- a/include/pocketpy/interpreter/frame.h +++ b/include/pocketpy/interpreter/frame.h @@ -31,28 +31,37 @@ void UnwindTarget__delete(UnwindTarget* self); typedef struct Frame { struct Frame* f_back; - const Bytecode* ip; const CodeObject* co; + py_StackRef p0; // unwinding base py_GlobalRef module; - py_StackRef p0; // unwinding base - py_StackRef locals; // locals base - bool has_function; // is p0 a function? - bool is_dynamic; // is dynamic frame? + py_Ref globals; // a module object or a dict object + py_Ref locals; // locals base or a proxy object (such as dict) + bool is_p0_function; + bool is_locals_proxy; + int ip; UnwindTarget* uw_list; } Frame; Frame* Frame__new(const CodeObject* co, - py_GlobalRef module, py_StackRef p0, - py_StackRef locals, - bool has_function); + py_GlobalRef module, + py_Ref globals, + py_Ref locals, + bool is_p0_function, + bool is_locals_proxy); void Frame__delete(Frame* self); -int Frame__ip(const Frame* self); int Frame__lineno(const Frame* self); int Frame__iblock(const Frame* self); -py_TValue* Frame__f_locals_try_get(Frame* self, py_Name name); -py_TValue* Frame__f_closure_try_get(Frame* self, py_Name name); + +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); + +py_StackRef Frame__getlocal_noproxy(Frame* self, py_Name name); + int Frame__prepare_jump_exception_handler(Frame* self, ValueStack*); diff --git a/include/pocketpy/objects/base.h b/include/pocketpy/objects/base.h index 3df4fc0e..18c522ba 100644 --- a/include/pocketpy/objects/base.h +++ b/include/pocketpy/objects/base.h @@ -19,5 +19,6 @@ typedef struct py_TValue { PyObject* _obj; c11_vec2 _vec2; c11_vec2i _vec2i; + void* _ptr; }; } py_TValue; diff --git a/include/pocketpy/objects/codeobject.h b/include/pocketpy/objects/codeobject.h index 9df8cc0f..1583ba3b 100644 --- a/include/pocketpy/objects/codeobject.h +++ b/include/pocketpy/objects/codeobject.h @@ -23,7 +23,6 @@ typedef enum FuncType { typedef enum NameScope { NAME_LOCAL, NAME_GLOBAL, - NAME_GLOBAL_UNKNOWN, } NameScope; typedef enum CodeBlockType { @@ -128,11 +127,12 @@ void FuncDecl__gc_mark(const FuncDecl* self); // runtime function typedef struct Function { FuncDecl_ decl; - py_TValue module; // weak ref - PyObject* clazz; // weak ref - NameDict* closure; // strong ref - py_CFunction cfunc; // wrapped C function + py_GlobalRef module; // maybe NULL, weak ref + py_Ref globals; // maybe NULL, strong ref + NameDict* closure; // maybe NULL, strong ref + PyObject* clazz; // weak ref; for super() + py_CFunction cfunc; // wrapped C function; for decl-based binding } Function; -void Function__ctor(Function* self, FuncDecl_ decl, py_TValue* module); +void Function__ctor(Function* self, FuncDecl_ decl, py_GlobalRef module, py_Ref globals); void Function__dtor(Function* self); diff --git a/include/pocketpy/pocketpy.h b/include/pocketpy/pocketpy.h index d8a70302..4997b6da 100644 --- a/include/pocketpy/pocketpy.h +++ b/include/pocketpy/pocketpy.h @@ -204,6 +204,8 @@ PK_API void py_newboundmethod(py_OutRef out, py_Ref self, py_Ref func); PK_API py_Name py_name(const char*); /// Convert a name to a null-terminated string. PK_API const char* py_name2str(py_Name); +/// Convert a name to a python `str` object with cache. +PK_API py_GlobalRef py_name2ref(py_Name); /// Convert a `c11_sv` to a name. PK_API py_Name py_namev(c11_sv); /// Convert a name to a `c11_sv`. diff --git a/pyrightconfig.json b/pyrightconfig.json index d08b4f98..b820ef59 100644 --- a/pyrightconfig.json +++ b/pyrightconfig.json @@ -3,5 +3,6 @@ "reportMissingModuleSource": "none", "reportArgumentType": "none", "reportWildcardImportFromLibrary": "none", + "reportRedeclaration": "none", "pythonVersion": "3.12" } diff --git a/src/common/strname.c b/src/common/strname.c index 8922a37b..6ba3805e 100644 --- a/src/common/strname.c +++ b/src/common/strname.c @@ -6,16 +6,19 @@ #include +typedef struct { + char* data; // null-terminated data + int size; // size of the data excluding the null-terminator + py_TValue* ref; // cached `str` object (lazy initialized) +} RInternedEntry; + // TODO: use a more efficient data structure static c11_smallmap_s2n _interned; -static c11_vector /*T=char* */ _r_interned; +static c11_vector /* T=RInternedEntry */ _r_interned; void py_Name__initialize() { c11_smallmap_s2n__ctor(&_interned); - for(int i = 0; i < _r_interned.length; i++) { - PK_FREE(c11__at(char*, &_r_interned, i)); - } - c11_vector__ctor(&_r_interned, sizeof(c11_sv)); + c11_vector__ctor(&_r_interned, sizeof(RInternedEntry)); #define MAGIC_METHOD(x) \ if(x != py_name(#x)) abort(); @@ -26,7 +29,7 @@ void py_Name__initialize() { void py_Name__finalize() { // free all char* for(int i = 0; i < _r_interned.length; i++) { - PK_FREE(c11__getitem(char*, &_r_interned, i)); + PK_FREE(c11__getitem(RInternedEntry, &_r_interned, i).data); } c11_smallmap_s2n__dtor(&_interned); c11_vector__dtor(&_r_interned); @@ -35,7 +38,6 @@ void py_Name__finalize() { py_Name py_name(const char* name) { return py_namev((c11_sv){name, strlen(name)}); } py_Name py_namev(c11_sv name) { - // TODO: PK_GLOBAL_SCOPE_LOCK() uint16_t index = c11_smallmap_s2n__get(&_interned, name, 0); if(index != 0) return index; // generate new index @@ -44,7 +46,11 @@ py_Name py_namev(c11_sv name) { char* p = PK_MALLOC(name.size + 1); memcpy(p, name.data, name.size); p[name.size] = '\0'; - c11_vector__push(char*, &_r_interned, p); + RInternedEntry entry; + entry.data = p; + entry.size = name.size; + entry.ref = NULL; + c11_vector__push(RInternedEntry, &_r_interned, entry); index = _r_interned.length; // 1-based // save to _interned c11_smallmap_s2n__set(&_interned, (c11_sv){p, name.size}, index); @@ -54,11 +60,24 @@ py_Name py_namev(c11_sv name) { const char* py_name2str(py_Name index) { assert(index > 0 && index <= _interned.length); - return c11__getitem(char*, &_r_interned, index - 1); + return c11__getitem(RInternedEntry, &_r_interned, index - 1).data; } c11_sv py_name2sv(py_Name index) { assert(index > 0 && index <= _interned.length); - const char* p = py_name2str(index); - return (c11_sv){p, strlen(p)}; + RInternedEntry entry = c11__getitem(RInternedEntry, &_r_interned, index - 1); + return (c11_sv){entry.data, entry.size}; +} + +py_GlobalRef py_name2ref(py_Name index) { + assert(index > 0 && index <= _interned.length); + RInternedEntry entry = c11__getitem(RInternedEntry, &_r_interned, index - 1); + if(entry.ref == NULL){ + entry.ref = PK_MALLOC(16); // ... + c11_sv sv; + sv.data = entry.data; + sv.size = entry.size; + py_newstrv(entry.ref, sv); + } + return entry.ref; } diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index eeefad2d..6adc55ce 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -102,17 +102,22 @@ void NameExpr__emit_(Expr* self_, Ctx* ctx) { NameExpr* self = (NameExpr*)self_; int index = c11_smallmap_n2i__get(&ctx->co->varnames_inv, self->name, -1); if(self->scope == NAME_LOCAL && index >= 0) { + // we know this is a local variable Ctx__emit_(ctx, OP_LOAD_FAST, index, self->line); } else { - Opcode 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_ATTR_GLOBAL instead of - // OP_LOAD_GLOBAL this supports @property.setter - op = OP_LOAD_CLASS_GLOBAL; - // exec()/eval() won't work with OP_LOAD_ATTR_GLOBAL in class body + 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 { - // we cannot determine the scope when calling exec()/eval() - if(self->scope == NAME_GLOBAL_UNKNOWN) op = OP_LOAD_NAME; + 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; + } } Ctx__emit_(ctx, op, self->name, self->line); } @@ -124,8 +129,11 @@ bool NameExpr__emit_del(Expr* self_, Ctx* ctx) { case NAME_LOCAL: Ctx__emit_(ctx, OP_DELETE_FAST, Ctx__add_varname(ctx, self->name), self->line); break; - case NAME_GLOBAL: Ctx__emit_(ctx, OP_DELETE_GLOBAL, self->name, self->line); break; - case NAME_GLOBAL_UNKNOWN: Ctx__emit_(ctx, OP_DELETE_NAME, self->name, self->line); break; + case NAME_GLOBAL: { + Opcode op = ctx->co->src->is_dynamic ? OP_DELETE_NAME : OP_DELETE_GLOBAL; + Ctx__emit_(ctx, op, self->name, self->line); + break; + } default: c11__unreachable(); } return true; @@ -1219,8 +1227,10 @@ static int Ctx__add_const(Ctx* self, py_Ref v) { static void Ctx__emit_store_name(Ctx* self, NameScope scope, py_Name name, int line) { switch(scope) { case NAME_LOCAL: Ctx__emit_(self, OP_STORE_FAST, Ctx__add_varname(self, name), line); break; - case NAME_GLOBAL: Ctx__emit_(self, OP_STORE_GLOBAL, name, line); break; - case NAME_GLOBAL_UNKNOWN: Ctx__emit_(self, OP_STORE_NAME, name, line); break; + case NAME_GLOBAL: { + Opcode op = self->co->src->is_dynamic ? OP_STORE_NAME : OP_STORE_GLOBAL; + Ctx__emit_(self, op, name, line); + } break; default: c11__unreachable(); } } @@ -1331,9 +1341,7 @@ static void Compiler__dtor(Compiler* self) { if((err = B)) return err static NameScope name_scope(Compiler* self) { - NameScope s = self->contexts.length > 1 ? NAME_LOCAL : NAME_GLOBAL; - if(self->src->is_dynamic && s == NAME_GLOBAL) s = NAME_GLOBAL_UNKNOWN; - return s; + return self->contexts.length > 1 ? NAME_LOCAL : NAME_GLOBAL; } Error* SyntaxError(Compiler* self, const char* fmt, ...) { @@ -1720,7 +1728,7 @@ static Error* exprName(Compiler* self) { NameScope scope = name_scope(self); // promote this name to global scope if needed if(c11_smallmap_n2i__contains(&ctx()->global_names, name)) { - if(scope == NAME_GLOBAL_UNKNOWN) return SyntaxError(self, "cannot use global keyword here"); + if(self->src->is_dynamic) return SyntaxError(self, "cannot use global keyword here"); scope = NAME_GLOBAL; } NameExpr* e = NameExpr__new(prev()->line, name, scope); diff --git a/src/interpreter/ceval.c b/src/interpreter/ceval.c index 1ff28e10..ad81c526 100644 --- a/src/interpreter/ceval.c +++ b/src/interpreter/ceval.c @@ -33,7 +33,7 @@ static bool stack_format_object(VM* self, c11_sv spec); } while(0) #define DISPATCH_JUMP_ABSOLUTE(__target) \ do { \ - frame->ip = c11__at(Bytecode, &frame->co->codes, __target); \ + frame->ip = __target; \ goto __NEXT_STEP; \ } while(0) @@ -86,15 +86,18 @@ static bool unpack_dict_to_buffer(py_Ref key, py_Ref val, void* ctx) { FrameResult VM__run_top_frame(VM* self) { Frame* frame = self->top_frame; + Bytecode* codes; + const Frame* base_frame = frame; while(true) { Bytecode byte; __NEXT_FRAME: + codes = frame->co->codes.data; frame->ip++; __NEXT_STEP: - byte = *frame->ip; + byte = codes[frame->ip]; #ifndef NDEBUG pk_print_stack(self, frame, byte); @@ -176,7 +179,7 @@ FrameResult VM__run_top_frame(VM* self) { CHECK_STACK_OVERFLOW(); FuncDecl_ decl = c11__getitem(FuncDecl_, &frame->co->func_decls, byte.arg); Function* ud = py_newobject(SP(), tp_function, 0, sizeof(Function)); - Function__ctor(ud, decl, frame->module); + Function__ctor(ud, decl, frame->module, frame->globals); if(decl->nested) { ud->closure = FastLocals__to_namedict(frame->locals, frame->co); py_Name name = py_name(decl->code.name->data); @@ -200,10 +203,10 @@ FrameResult VM__run_top_frame(VM* self) { DISPATCH(); } case OP_LOAD_NAME: { - assert(frame->is_dynamic); + // assert(frame->is_dynamic); py_Name name = byte.arg; py_TValue* tmp; - py_newstr(SP()++, py_name2str(name)); + py_assign(SP()++, py_name2ref(name)); // locals if(!py_isnone(&frame->p0[1])) { if(py_getitem(&frame->p0[1], TOP())) { @@ -217,6 +220,7 @@ FrameResult VM__run_top_frame(VM* self) { } } } + // `LOAD_ // globals if(py_getitem(&frame->p0[0], TOP())) { py_assign(TOP(), py_retval()); @@ -239,16 +243,18 @@ FrameResult VM__run_top_frame(VM* self) { } case OP_LOAD_NONLOCAL: { py_Name name = byte.arg; - py_Ref tmp = Frame__f_closure_try_get(frame, name); + py_Ref tmp = Frame__getclosure(frame, name); if(tmp != NULL) { PUSH(tmp); DISPATCH(); } - tmp = py_getdict(frame->module, name); - if(tmp != NULL) { - PUSH(tmp); + int res = Frame__getglobal(frame, name); + if(res == 1) { + PUSH(&self->last_retval); DISPATCH(); } + if(res == -1) goto __ERROR; + tmp = py_getdict(&self->builtins, name); if(tmp != NULL) { PUSH(tmp); @@ -259,12 +265,13 @@ FrameResult VM__run_top_frame(VM* self) { } case OP_LOAD_GLOBAL: { py_Name name = byte.arg; - py_Ref tmp = py_getdict(frame->module, name); - if(tmp != NULL) { - PUSH(tmp); + int res = Frame__getglobal(frame, name); + if(res == 1) { + PUSH(&self->last_retval); DISPATCH(); } - tmp = py_getdict(&self->builtins, name); + if(res == -1) goto __ERROR; + py_Ref tmp = py_getdict(&self->builtins, name); if(tmp != NULL) { PUSH(tmp); DISPATCH(); @@ -289,11 +296,12 @@ FrameResult VM__run_top_frame(VM* self) { DISPATCH(); } // load global if attribute not found - tmp = py_getdict(frame->module, name); - if(tmp) { - PUSH(tmp); + int res = Frame__getglobal(frame, name); + if(res == 1) { + PUSH(&self->last_retval); DISPATCH(); } + if(res == -1) goto __ERROR; tmp = py_getdict(&self->builtins, name); if(tmp) { PUSH(tmp); @@ -337,9 +345,9 @@ 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); + // assert(frame->is_dynamic); py_Name name = byte.arg; - py_newstr(SP()++, py_name2str(name)); + py_assign(SP()++, py_name2ref(name)); // [value, name] if(!py_isnone(&frame->p0[1])) { // locals @@ -369,7 +377,7 @@ FrameResult VM__run_top_frame(VM* self) { DISPATCH(); } case OP_STORE_GLOBAL: { - py_setdict(frame->module, byte.arg, TOP()); + if(!Frame__setglobal(frame, byte.arg, TOP())) goto __ERROR; POP(); DISPATCH(); } @@ -407,9 +415,9 @@ FrameResult VM__run_top_frame(VM* self) { DISPATCH(); } case OP_DELETE_NAME: { - assert(frame->is_dynamic); + // assert(frame->is_dynamic); py_Name name = byte.arg; - py_newstr(SP()++, py_name2str(name)); + py_assign(SP()++, py_name2ref(name)); if(!py_isnone(&frame->p0[1])) { // locals if(py_delitem(&frame->p0[1], TOP())) { @@ -439,12 +447,12 @@ FrameResult VM__run_top_frame(VM* self) { } case OP_DELETE_GLOBAL: { py_Name name = byte.arg; - bool ok = py_deldict(frame->module, name); - if(!ok) { - NameError(name); - goto __ERROR; - } - DISPATCH(); + int res = Frame__delglobal(frame, name); + if(res == 1) DISPATCH(); + if(res == -1) goto __ERROR; + // res == 0 + NameError(name); + goto __ERROR; } case OP_DELETE_ATTR: { @@ -860,7 +868,7 @@ FrameResult VM__run_top_frame(VM* self) { ImportError("cannot import name '%n'", name); goto __ERROR; } else { - py_setdict(frame->module, name, value); + if(!Frame__setglobal(frame, name, value)) goto __ERROR; } } } else { @@ -869,7 +877,7 @@ FrameResult VM__run_top_frame(VM* self) { if(!kv->key) continue; c11_sv name = py_name2sv(kv->key); if(name.size == 0 || name.data[0] == '_') continue; - py_setdict(frame->module, kv->key, &kv->value); + if(!Frame__setglobal(frame, kv->key, &kv->value)) goto __ERROR; } } POP(); @@ -998,8 +1006,7 @@ FrameResult VM__run_top_frame(VM* self) { case OP_END_CLASS: { // [cls or decorated] py_Name name = byte.arg; - // set into f_globals - py_setdict(frame->module, name, TOP()); + if(!Frame__setglobal(frame, name, TOP())) goto __ERROR; if(py_istype(TOP(), tp_type)) { // call on_end_subclass @@ -1166,7 +1173,7 @@ FrameResult VM__run_top_frame(VM* self) { py_BaseException__stpush(&self->curr_exception, frame->co->src, Frame__lineno(frame), - frame->has_function ? frame->co->name->data : NULL); + frame->is_p0_function ? frame->co->name->data : NULL); __ERROR_RE_RAISE: do { } while(0); @@ -1183,6 +1190,7 @@ FrameResult VM__run_top_frame(VM* self) { return RES_ERROR; } frame = self->top_frame; + codes = frame->co->codes.data; goto __ERROR; } } diff --git a/src/interpreter/frame.c b/src/interpreter/frame.c index 64c7e5ee..18747897 100644 --- a/src/interpreter/frame.c +++ b/src/interpreter/frame.c @@ -23,7 +23,7 @@ NameDict* FastLocals__to_namedict(py_TValue* locals, const CodeObject* co) { NameDict* dict = NameDict__new(); c11__foreach(c11_smallmap_n2i_KV, &co->varnames_inv, entry) { py_TValue value = locals[entry->value]; - if(!py_isnil(&value)) { NameDict__set(dict, entry->key, value); } + if(!py_isnil(&value)) NameDict__set(dict, entry->key, value); } return dict; } @@ -39,19 +39,23 @@ UnwindTarget* UnwindTarget__new(UnwindTarget* next, int iblock, int offset) { void UnwindTarget__delete(UnwindTarget* self) { PK_FREE(self); } Frame* Frame__new(const CodeObject* co, - py_GlobalRef module, py_StackRef p0, - py_StackRef locals, - bool has_function) { + py_GlobalRef module, + py_Ref globals, + py_Ref locals, + bool is_p0_function, + bool is_locals_proxy) { + assert(module->type == tp_module || module->type == tp_dict); Frame* self = FixedMemoryPool__alloc(&pk_current_vm->pool_frame); self->f_back = NULL; - self->ip = (Bytecode*)co->codes.data - 1; self->co = co; - self->module = module; self->p0 = p0; + self->module = module; + self->globals = globals; self->locals = locals; - self->has_function = has_function; - self->is_dynamic = co->src->is_dynamic; + self->is_p0_function = is_p0_function; + self->is_locals_proxy = is_locals_proxy; + self->ip = -1; self->uw_list = NULL; return self; } @@ -99,30 +103,60 @@ void Frame__set_unwind_target(Frame* self, py_TValue* sp) { } void Frame__gc_mark(Frame* self) { - pk__mark_value(self->module); + pk__mark_value(self->globals); + if(self->is_locals_proxy) pk__mark_value(self->locals); CodeObject__gc_mark(self->co); } -py_TValue* Frame__f_closure_try_get(Frame* self, py_Name name) { - if(!self->has_function) return NULL; - Function* ud = py_touserdata(self->p0); - if(ud->closure == NULL) return NULL; - return NameDict__try_get(ud->closure, name); -} - -int Frame__ip(const Frame* self) { return self->ip - (Bytecode*)self->co->codes.data; } - int Frame__lineno(const Frame* self) { - int ip = Frame__ip(self); + int ip = self->ip; return c11__getitem(BytecodeEx, &self->co->codes_ex, ip).lineno; } int Frame__iblock(const Frame* self) { - int ip = Frame__ip(self); + int ip = self->ip; return c11__getitem(BytecodeEx, &self->co->codes_ex, ip).iblock; } -py_TValue* Frame__f_locals_try_get(Frame* self, py_Name name) { - assert(!self->is_dynamic); +int Frame__getglobal(Frame* self, py_Name name) { + if(self->globals->type == tp_module) { + py_ItemRef item = py_getdict(self->globals, name); + if(item != NULL) { + py_assign(py_retval(), item); + return 1; + } + return 0; + } else { + return py_dict_getitem(self->globals, py_name2ref(name)); + } +} + +bool Frame__setglobal(Frame* self, py_Name name, py_TValue* val) { + if(self->globals->type == tp_module) { + py_setdict(self->globals, name, val); + return true; + } else { + return py_dict_setitem(self->globals, py_name2ref(name), val); + } +} + +int Frame__delglobal(Frame* self, py_Name name) { + if(self->globals->type == tp_module) { + bool found = py_deldict(self->globals, name); + return found ? 1 : 0; + } else { + return py_dict_delitem(self->globals, py_name2ref(name)); + } +} + +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); +} + +py_Ref Frame__getclosure(Frame* self, py_Name name) { + if(!self->is_p0_function) return NULL; + Function* ud = py_touserdata(self->p0); + if(ud->closure == NULL) return NULL; + return NameDict__try_get(ud->closure, name); } \ No newline at end of file diff --git a/src/interpreter/vm.c b/src/interpreter/vm.c index 5ef35650..44f41737 100644 --- a/src/interpreter/vm.c +++ b/src/interpreter/vm.c @@ -431,8 +431,8 @@ static bool co->name->data); } else { // add to **kwargs - bool ok = py_dict_setitem_by_str(&buffer[decl->starred_kwarg], - py_name2str(key), + bool ok = py_dict_setitem(&buffer[decl->starred_kwarg], + py_name2ref(key), &p1[2 * j + 1]); if(!ok) return false; } @@ -480,7 +480,7 @@ FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall // submit the call if(!fn->cfunc) { // python function - VM__push_frame(self, Frame__new(co, &fn->module, p0, argv, true)); + VM__push_frame(self, Frame__new(co, p0, fn->module, fn->globals, argv, true, false)); return opcall ? RES_CALL : VM__run_top_frame(self); } else { // decl-based binding @@ -509,7 +509,7 @@ FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall // submit the call if(!fn->cfunc) { // python function - VM__push_frame(self, Frame__new(co, &fn->module, p0, argv, true)); + VM__push_frame(self, Frame__new(co, p0, fn->module, fn->globals, argv, true, false)); return opcall ? RES_CALL : VM__run_top_frame(self); } else { // decl-based binding @@ -525,7 +525,7 @@ FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall // copy buffer back to stack self->stack.sp = argv + co->nlocals; memcpy(argv, self->__vectorcall_buffer, co->nlocals * sizeof(py_TValue)); - Frame* frame = Frame__new(co, &fn->module, p0, argv, true); + Frame* frame = Frame__new(co, p0, fn->module, fn->globals, argv, true, false); pk_newgenerator(py_retval(), frame, p0, self->stack.sp); self->stack.sp = p0; // reset the stack return RES_RETURN; diff --git a/src/modules/enum.c b/src/modules/enum.c index 11bb57f9..f63fa3a1 100644 --- a/src/modules/enum.c +++ b/src/modules/enum.c @@ -7,7 +7,7 @@ static bool Enum__wrapper_field(py_Name name, py_Ref value, void* ctx) { if(name_sv.size == 0 || name_sv.data[0] == '_') return true; py_push(ctx); py_pushnil(); - py_newstr(py_pushtmp(), py_name2str(name)); + py_assign(py_pushtmp(), py_name2ref(name)); py_push(value); bool ok = py_vectorcall(2, 0); if(!ok) return false; diff --git a/src/objects/codeobject.c b/src/objects/codeobject.c index ba90874b..6483bfe5 100644 --- a/src/objects/codeobject.c +++ b/src/objects/codeobject.c @@ -159,12 +159,13 @@ void CodeObject__dtor(CodeObject* self) { c11_vector__dtor(&self->func_decls); } -void Function__ctor(Function* self, FuncDecl_ decl, py_TValue* module) { +void Function__ctor(Function* self, FuncDecl_ decl, py_GlobalRef module, py_Ref globals) { PK_INCREF(decl); self->decl = decl; - self->module = module ? *module : *py_NIL(); - self->clazz = NULL; + self->module = module; + self->globals = globals; self->closure = NULL; + self->clazz = NULL; self->cfunc = NULL; } diff --git a/src/public/exec.c b/src/public/exec.c index de5a09fc..a4e07eeb 100644 --- a/src/public/exec.c +++ b/src/public/exec.c @@ -59,7 +59,9 @@ bool pk_exec(CodeObject* co, py_Ref module) { py_StackRef sp = vm->stack.sp; if(co->src->is_dynamic) sp -= 3; // [globals, locals, code] - Frame* frame = Frame__new(co, module, sp, sp, false); + 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); VM__push_frame(vm, frame); FrameResult res = VM__run_top_frame(vm); if(res == RES_ERROR) return false; diff --git a/src/public/modules.c b/src/public/modules.c index 74d00564..9d40d78c 100644 --- a/src/public/modules.c +++ b/src/public/modules.c @@ -498,23 +498,23 @@ void py_newglobals(py_Ref out) { pk_mappingproxy__namedict(out, &pk_current_vm->main); return; } - if(frame->is_dynamic) { - py_assign(out, &frame->p0[0]); + if(frame->globals->type == tp_module) { + pk_mappingproxy__namedict(out, frame->globals); } else { - pk_mappingproxy__namedict(out, frame->module); + *out = *frame->globals; // dict } } void py_newlocals(py_Ref out) { Frame* frame = pk_current_vm->top_frame; - if(frame->is_dynamic) { - py_assign(out, &frame->p0[1]); + if(!frame || !frame->is_p0_function) { + py_newglobals(out); return; } - if(frame->has_function) { + if(!frame->is_locals_proxy){ pk_mappingproxy__locals(out, frame); - } else { - py_newglobals(out); + }else{ + *out = *frame->locals; } } @@ -563,7 +563,7 @@ static bool _builtins_execdyn(const char* title, int argc, py_Ref argv, enum py_ CodeObject* co = py_touserdata(code); if(!co->src->is_dynamic) { if(argc != 1) - return ValueError("code object is not dynamic, so globals and locals must be None"); + return ValueError("code object is not dynamic, `globals` and `locals` must be None"); py_shrink(3); } Frame* frame = pk_current_vm->top_frame; @@ -736,6 +736,7 @@ py_TValue pk_builtins__register() { static void function__gc_mark(void* ud) { Function* func = ud; + if(func->globals) pk__mark_value(func->globals); if(func->closure) pk__mark_namedict(func->closure); FuncDecl__gc_mark(func->decl); } @@ -779,7 +780,7 @@ static bool super__new__(int argc, py_Ref argv) { py_Ref self_arg = NULL; if(argc == 1) { // super() - if(frame->has_function) { + if(frame->is_p0_function && !frame->is_locals_proxy) { py_TValue* callable = frame->p0; if(callable->type == tp_boundmethod) callable = py_getslot(frame->p0, 1); if(callable->type == tp_function) { diff --git a/src/public/py_mappingproxy.c b/src/public/py_mappingproxy.c index b8da64e0..343d8c4a 100644 --- a/src/public/py_mappingproxy.c +++ b/src/public/py_mappingproxy.c @@ -4,6 +4,7 @@ #include "pocketpy/objects/object.h" #include "pocketpy/interpreter/vm.h" #include "pocketpy/common/sstream.h" +#include void pk_mappingproxy__namedict(py_Ref out, py_Ref object) { py_newobject(out, tp_namedict, 1, 0); @@ -59,7 +60,7 @@ static bool namedict_items(int argc, py_Ref argv) { if(py_isnil(ti->magic_0 + j)) continue; py_Ref slot = py_list_emplace(py_retval()); py_newtuple(slot, 2); - py_newstr(py_tuple_getitem(slot, 0), py_name2str(j + PK_MAGIC_SLOTS_UNCOMMON_LENGTH)); + py_assign(py_tuple_getitem(slot, 0), py_name2ref(j + PK_MAGIC_SLOTS_UNCOMMON_LENGTH)); py_assign(py_tuple_getitem(slot, 1), ti->magic_0 + j); } if(ti->magic_1) { @@ -67,7 +68,7 @@ static bool namedict_items(int argc, py_Ref argv) { if(py_isnil(ti->magic_1 + j)) continue; py_Ref slot = py_list_emplace(py_retval()); py_newtuple(slot, 2); - py_newstr(py_tuple_getitem(slot, 0), py_name2str(j)); + py_assign(py_tuple_getitem(slot, 0), py_name2ref(j)); py_assign(py_tuple_getitem(slot, 1), ti->magic_1 + j); } } @@ -76,7 +77,7 @@ static bool namedict_items(int argc, py_Ref argv) { py_Ref slot = py_list_emplace(py_retval()); py_newtuple(slot, 2); NameDict_KV* kv = c11__at(NameDict_KV, dict, i); - py_newstr(py_tuple_getitem(slot, 0), py_name2str(kv->key)); + py_assign(py_tuple_getitem(slot, 0), py_name2ref(kv->key)); py_assign(py_tuple_getitem(slot, 1), &kv->value); } return true; @@ -107,17 +108,21 @@ py_Type pk_namedict__register() { ////////////////////// void pk_mappingproxy__locals(py_Ref out, Frame* frame) { - assert(frame->has_function && !frame->is_dynamic); - Frame** ud = py_newobject(out, tp_locals, 0, sizeof(Frame*)); - *ud = frame; + assert(frame->is_p0_function && !frame->is_locals_proxy); + out->type = tp_locals; + out->is_ptr = false; + out->extra = 0; + // this is a weak reference + // locals() will expire when the frame is destroyed + out->_ptr = 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); + Frame* frame = argv->_ptr; py_Name name = py_namev(py_tosv(py_arg(1))); - py_Ref slot = Frame__f_locals_try_get(*ud, name); + py_Ref slot = Frame__getlocal_noproxy(frame, name); if(!slot || py_isnil(slot)) return KeyError(py_arg(1)); py_assign(py_retval(), slot); return true; @@ -126,9 +131,9 @@ static bool locals__getitem__(int argc, py_Ref argv) { 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); + Frame* frame = argv->_ptr; py_Name name = py_namev(py_tosv(py_arg(1))); - py_Ref slot = Frame__f_locals_try_get(*ud, name); + py_Ref slot = Frame__getlocal_noproxy(frame, name); if(!slot) return KeyError(py_arg(1)); py_assign(slot, py_arg(2)); py_newnone(py_retval()); @@ -138,9 +143,9 @@ static bool locals__setitem__(int argc, py_Ref argv) { 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); + Frame* frame = argv->_ptr; py_Name name = py_namev(py_tosv(py_arg(1))); - py_Ref res = Frame__f_locals_try_get(*ud, name); + py_Ref res = Frame__getlocal_noproxy(frame, name); if(!res || py_isnil(res)) return KeyError(py_arg(1)); py_newnil(res); py_newnone(py_retval()); @@ -150,9 +155,9 @@ static bool locals__delitem__(int argc, py_Ref argv) { 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); + Frame* frame = argv->_ptr; py_Name name = py_namev(py_tosv(py_arg(1))); - py_Ref slot = Frame__f_locals_try_get(*ud, name); + py_Ref slot = Frame__getlocal_noproxy(frame, name); py_newbool(py_retval(), slot && !py_isnil(slot)); return true; } diff --git a/src/public/py_object.c b/src/public/py_object.c index 7e9a3e04..827c0065 100644 --- a/src/public/py_object.c +++ b/src/public/py_object.c @@ -84,7 +84,7 @@ static bool type__base__(int argc, py_Ref argv) { static bool type__name__(int argc, py_Ref argv) { PY_CHECK_ARGC(1); py_TypeInfo* ti = pk__type_info(py_totype(argv)); - py_newstr(py_retval(), py_name2str(ti->name)); + py_assign(py_retval(), py_name2ref(ti->name)); return true; } diff --git a/src/public/py_ops.c b/src/public/py_ops.c index 53c1fa43..9495ff4a 100644 --- a/src/public/py_ops.c +++ b/src/public/py_ops.c @@ -153,7 +153,7 @@ bool py_getattr(py_Ref self, py_Name name) { if(fallback) { py_push(fallback); py_push(self); - py_newstr(py_pushtmp(), py_name2str(name)); + py_assign(py_pushtmp(), py_name2ref(name)); return py_vectorcall(1, 0); } diff --git a/src/public/values.c b/src/public/values.c index 27d59e06..5db42560 100644 --- a/src/public/values.c +++ b/src/public/values.c @@ -104,7 +104,7 @@ py_Name decl->docstring = docstring; // construct the function Function* ud = py_newobject(out, tp_function, slots, sizeof(Function)); - Function__ctor(ud, decl, NULL); + Function__ctor(ud, decl, NULL, NULL); ud->cfunc = f; CodeObject__dtor(&code); PK_DECREF(source); diff --git a/tests/66_eval.py b/tests/66_eval.py index 3d1137b6..54692830 100644 --- a/tests/66_eval.py +++ b/tests/66_eval.py @@ -67,3 +67,8 @@ try: exit(1) except NameError: pass + +# https://github.com/pocketpy/pocketpy/issues/339 +code = '\nprint(x)\ndef f():\n print(x)\nf()\n' +x = 33 +exec(code, {'x': 42}) \ No newline at end of file diff --git a/tests/67_locals_vs_globals.py b/tests/67_locals_vs_globals.py new file mode 100644 index 00000000..010a2a12 --- /dev/null +++ b/tests/67_locals_vs_globals.py @@ -0,0 +1,45 @@ +# https://gist.github.com/dean0x7d/df5ce97e4a1a05be4d56d1378726ff92 + +a = 1 +my_locals = {"b": 2} + +# With user-defined locals: +exec(""" +import sys +assert locals() != globals() +assert "sys" in locals() +assert "sys" not in globals() +assert "a" not in locals() +assert "a" in globals() +# print(a) # checks `locals()` first, fails, but finds it in `globals()` +assert (a == 1), a +assert "b" in locals() +assert "b" not in globals() +# print(b) +assert (b == 2), b +def main(): + assert locals() != globals() + assert "sys" not in locals() # not the same `locals()` as the outer scope + assert "sys" not in globals() # and `sys` isn't in `globals()`, same as before + assert "b" not in locals() # again, not the same `locals()` as the outer scope +main() +""", globals(), my_locals) + +assert "sys" in my_locals # side effect +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() +""", globals()) + +assert "sys" in globals() \ No newline at end of file From 2d5561441d5f05b16e95d260f91cb6462b5a7426 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Tue, 25 Feb 2025 16:45:32 +0800 Subject: [PATCH 02/18] backup --- include/pocketpy/common/strname.h | 7 --- include/pocketpy/interpreter/frame.h | 1 - include/pocketpy/interpreter/name.h | 18 ++++++ include/pocketpy/interpreter/vm.h | 2 + include/pocketpy/objects/error.h | 4 -- src/common/strname.c | 83 ---------------------------- src/interpreter/name.c | 77 ++++++++++++++++++++++++++ src/interpreter/vm.c | 24 ++++++-- src/public/internal.c | 3 - 9 files changed, 115 insertions(+), 104 deletions(-) delete mode 100644 include/pocketpy/common/strname.h create mode 100644 include/pocketpy/interpreter/name.h delete mode 100644 src/common/strname.c create mode 100644 src/interpreter/name.c diff --git a/include/pocketpy/common/strname.h b/include/pocketpy/common/strname.h deleted file mode 100644 index a7407b8a..00000000 --- a/include/pocketpy/common/strname.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include -#include "pocketpy/common/str.h" - -void py_Name__initialize(); -void py_Name__finalize(); diff --git a/include/pocketpy/interpreter/frame.h b/include/pocketpy/interpreter/frame.h index 04e5a6ab..64972ec3 100644 --- a/include/pocketpy/interpreter/frame.h +++ b/include/pocketpy/interpreter/frame.h @@ -4,7 +4,6 @@ #include "pocketpy/objects/codeobject.h" #include "pocketpy/objects/namedict.h" #include "pocketpy/objects/object.h" -#include "pocketpy/common/strname.h" #include "pocketpy/pocketpy.h" py_TValue* FastLocals__try_get_by_name(py_TValue* locals, const CodeObject* co, py_Name name); diff --git a/include/pocketpy/interpreter/name.h b/include/pocketpy/interpreter/name.h new file mode 100644 index 00000000..b1f08520 --- /dev/null +++ b/include/pocketpy/interpreter/name.h @@ -0,0 +1,18 @@ +#pragma once + +#include "pocketpy/objects/base.h" +#include "pocketpy/common/smallmap.h" + +typedef struct { + char* data; // null-terminated data + int size; // size of the data excluding the null-terminator + py_TValue obj; // cached `str` object (lazy initialized) +} RInternedEntry; + +typedef struct { + c11_smallmap_s2n interned; + c11_vector /* T=RInternedEntry */ r_interned; +} InternedNames; + +void InternedNames__ctor(InternedNames* self); +void InternedNames__dtor(InternedNames* self); diff --git a/include/pocketpy/interpreter/vm.h b/include/pocketpy/interpreter/vm.h index 0080b8fe..9b0ab866 100644 --- a/include/pocketpy/interpreter/vm.h +++ b/include/pocketpy/interpreter/vm.h @@ -7,6 +7,7 @@ #include "pocketpy/interpreter/frame.h" #include "pocketpy/interpreter/modules.h" #include "pocketpy/interpreter/typeinfo.h" +#include "pocketpy/interpreter/name.h" // TODO: // 1. __eq__ and __ne__ fallbacks @@ -41,6 +42,7 @@ typedef struct VM { py_StackRef __curr_function; py_TValue __vectorcall_buffer[PK_MAX_CO_VARNAMES]; + InternedNames names; FixedMemoryPool pool_frame; ManagedHeap heap; ValueStack stack; // put `stack` at the end for better cache locality diff --git a/include/pocketpy/objects/error.h b/include/pocketpy/objects/error.h index 4ca63d35..d483767c 100644 --- a/include/pocketpy/objects/error.h +++ b/include/pocketpy/objects/error.h @@ -1,10 +1,6 @@ #pragma once -#include "pocketpy/common/str.h" -#include "pocketpy/common/strname.h" -#include "pocketpy/objects/codeobject.h" #include "pocketpy/objects/sourcedata.h" -#include "pocketpy/objects/object.h" #include "pocketpy/pocketpy.h" typedef struct{ diff --git a/src/common/strname.c b/src/common/strname.c deleted file mode 100644 index 6ba3805e..00000000 --- a/src/common/strname.c +++ /dev/null @@ -1,83 +0,0 @@ -#include "pocketpy/common/strname.h" -#include "pocketpy/common/smallmap.h" -#include "pocketpy/common/utils.h" -#include "pocketpy/common/vector.h" -#include "pocketpy/pocketpy.h" - -#include - -typedef struct { - char* data; // null-terminated data - int size; // size of the data excluding the null-terminator - py_TValue* ref; // cached `str` object (lazy initialized) -} RInternedEntry; - -// TODO: use a more efficient data structure -static c11_smallmap_s2n _interned; -static c11_vector /* T=RInternedEntry */ _r_interned; - -void py_Name__initialize() { - c11_smallmap_s2n__ctor(&_interned); - c11_vector__ctor(&_r_interned, sizeof(RInternedEntry)); - -#define MAGIC_METHOD(x) \ - if(x != py_name(#x)) abort(); -#include "pocketpy/xmacros/magics.h" -#undef MAGIC_METHOD -} - -void py_Name__finalize() { - // free all char* - for(int i = 0; i < _r_interned.length; i++) { - PK_FREE(c11__getitem(RInternedEntry, &_r_interned, i).data); - } - c11_smallmap_s2n__dtor(&_interned); - c11_vector__dtor(&_r_interned); -} - -py_Name py_name(const char* name) { return py_namev((c11_sv){name, strlen(name)}); } - -py_Name py_namev(c11_sv name) { - uint16_t index = c11_smallmap_s2n__get(&_interned, name, 0); - if(index != 0) return index; - // generate new index - if(_interned.length > 65530) c11__abort("py_Name index overflow"); - // NOTE: we must allocate the string in the heap so iterators are not invalidated - char* p = PK_MALLOC(name.size + 1); - memcpy(p, name.data, name.size); - p[name.size] = '\0'; - RInternedEntry entry; - entry.data = p; - entry.size = name.size; - entry.ref = NULL; - c11_vector__push(RInternedEntry, &_r_interned, entry); - index = _r_interned.length; // 1-based - // save to _interned - c11_smallmap_s2n__set(&_interned, (c11_sv){p, name.size}, index); - assert(_interned.length == _r_interned.length); - return index; -} - -const char* py_name2str(py_Name index) { - assert(index > 0 && index <= _interned.length); - return c11__getitem(RInternedEntry, &_r_interned, index - 1).data; -} - -c11_sv py_name2sv(py_Name index) { - assert(index > 0 && index <= _interned.length); - RInternedEntry entry = c11__getitem(RInternedEntry, &_r_interned, index - 1); - return (c11_sv){entry.data, entry.size}; -} - -py_GlobalRef py_name2ref(py_Name index) { - assert(index > 0 && index <= _interned.length); - RInternedEntry entry = c11__getitem(RInternedEntry, &_r_interned, index - 1); - if(entry.ref == NULL){ - entry.ref = PK_MALLOC(16); // ... - c11_sv sv; - sv.data = entry.data; - sv.size = entry.size; - py_newstrv(entry.ref, sv); - } - return entry.ref; -} diff --git a/src/interpreter/name.c b/src/interpreter/name.c new file mode 100644 index 00000000..1a34c2ad --- /dev/null +++ b/src/interpreter/name.c @@ -0,0 +1,77 @@ +#include "pocketpy/interpreter/name.h" +#include "pocketpy/interpreter/vm.h" + +void InternedNames__ctor(InternedNames* self) { + c11_smallmap_s2n__ctor(&self->interned); + c11_vector__ctor(&self->r_interned, sizeof(RInternedEntry)); + + // initialize all magic names +#define MAGIC_METHOD(x) \ + if(x != py_name(#x)) abort(); +#include "pocketpy/xmacros/magics.h" +#undef MAGIC_METHOD +} + +void InternedNames__dtor(InternedNames* self) { + for(int i = 0; i < self->r_interned.length; i++) { + PK_FREE(c11__getitem(RInternedEntry, &self->r_interned, i).data); + } + c11_smallmap_s2n__dtor(&self->interned); + c11_vector__dtor(&self->r_interned); +} + +py_Name py_name(const char* name) { + c11_sv sv; + sv.data = name; + sv.size = strlen(name); + return py_namev(sv); +} + +py_Name py_namev(c11_sv name) { + InternedNames* self = &pk_current_vm->names; + uint16_t index = c11_smallmap_s2n__get(&self->interned, name, 0); + if(index != 0) return index; + // generate new index + if(self->interned.length > 65530) c11__abort("py_Name index overflow"); + // NOTE: we must allocate the string in the heap so iterators are not invalidated + char* p = PK_MALLOC(name.size + 1); + memcpy(p, name.data, name.size); + p[name.size] = '\0'; + RInternedEntry entry; + entry.data = p; + entry.size = name.size; + entry.obj.type = tp_nil; + c11_vector__push(RInternedEntry, &self->r_interned, entry); + index = self->r_interned.length; // 1-based + // save to _interned + c11_smallmap_s2n__set(&self->interned, (c11_sv){p, name.size}, index); + assert(self->interned.length == self->r_interned.length); + return index; +} + +const char* py_name2str(py_Name index) { + InternedNames* self = &pk_current_vm->names; + assert(index > 0 && index <= self->interned.length); + return c11__getitem(RInternedEntry, &self->r_interned, index - 1).data; +} + +c11_sv py_name2sv(py_Name index) { + InternedNames* self = &pk_current_vm->names; + assert(index > 0 && index <= self->interned.length); + RInternedEntry entry = c11__getitem(RInternedEntry, &self->r_interned, index - 1); + return (c11_sv){entry.data, entry.size}; +} + +py_GlobalRef py_name2ref(py_Name index) { + InternedNames* self = &pk_current_vm->names; + assert(index > 0 && index <= self->interned.length); + RInternedEntry* entry = c11__at(RInternedEntry, &self->r_interned, index - 1); + if(entry->obj.type == tp_nil){ + c11_sv sv; + sv.data = entry->data; + sv.size = entry->size; + py_newstrv(&entry->obj, sv); + } + return &entry->obj; +} + diff --git a/src/interpreter/vm.c b/src/interpreter/vm.c index 44f41737..933b78bc 100644 --- a/src/interpreter/vm.c +++ b/src/interpreter/vm.c @@ -57,6 +57,7 @@ static void py_TypeInfo__ctor(py_TypeInfo* self, void VM__ctor(VM* self) { self->top_frame = NULL; + InternedNames__ctor(&self->names); ModuleDict__ctor(&self->modules, NULL, *py_NIL()); TypeList__ctor(&self->types); @@ -258,6 +259,7 @@ void VM__dtor(VM* self) { TypeList__dtor(&self->types); FixedMemoryPool__dtor(&self->pool_frame); ValueStack__clear(&self->stack); + InternedNames__dtor(&self->names); } void VM__push_frame(VM* self, Frame* frame) { @@ -281,7 +283,11 @@ static void _clip_int(int* value, int min, int max) { if(*value > max) *value = max; } -bool pk__parse_int_slice(py_Ref slice, int length, int* restrict start, int* restrict stop, int* restrict step) { +bool pk__parse_int_slice(py_Ref slice, + int length, + int* restrict start, + int* restrict stop, + int* restrict step) { if(py_isint(slice)) { int index = py_toint(slice); bool ok = pk__normalize_index(&index, length); @@ -431,9 +437,8 @@ static bool co->name->data); } else { // add to **kwargs - bool ok = py_dict_setitem(&buffer[decl->starred_kwarg], - py_name2ref(key), - &p1[2 * j + 1]); + bool ok = + py_dict_setitem(&buffer[decl->starred_kwarg], py_name2ref(key), &p1[2 * j + 1]); if(!ok) return false; } } @@ -480,7 +485,8 @@ FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall // submit the call if(!fn->cfunc) { // python function - VM__push_frame(self, Frame__new(co, p0, fn->module, fn->globals, argv, true, false)); + VM__push_frame(self, + Frame__new(co, p0, fn->module, fn->globals, argv, true, false)); return opcall ? RES_CALL : VM__run_top_frame(self); } else { // decl-based binding @@ -509,7 +515,8 @@ FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall // submit the call if(!fn->cfunc) { // python function - VM__push_frame(self, Frame__new(co, p0, fn->module, fn->globals, argv, true, false)); + VM__push_frame(self, + Frame__new(co, p0, fn->module, fn->globals, argv, true, false)); return opcall ? RES_CALL : VM__run_top_frame(self); } else { // decl-based binding @@ -693,6 +700,11 @@ void ManagedHeap__mark(ManagedHeap* self) { for(int i = 0; i < c11__count_array(vm->reg); i++) { pk__mark_value(&vm->reg[i]); } + // mark interned names + for(int i = 0; i < vm->names.r_interned.length; i++) { + RInternedEntry* entry = c11__at(RInternedEntry, &vm->names.r_interned, i); + pk__mark_value(&entry->obj); + } } void pk_print_stack(VM* self, Frame* frame, Bytecode byte) { diff --git a/src/public/internal.c b/src/public/internal.c index 277789ee..6516b436 100644 --- a/src/public/internal.c +++ b/src/public/internal.c @@ -27,8 +27,6 @@ void py_initialize() { static_assert(sizeof(py_TValue) == 16, "sizeof(py_TValue) != 16"); static_assert(offsetof(py_TValue, extra) == 4, "offsetof(py_TValue, extra) != 4"); - py_Name__initialize(); - pk_current_vm = pk_all_vm[0] = &pk_default_vm; // initialize some convenient references @@ -61,7 +59,6 @@ void py_finalize() { pk_current_vm = &pk_default_vm; VM__dtor(&pk_default_vm); pk_current_vm = NULL; - py_Name__finalize(); } void py_switchvm(int index) { From 9af9d228dd388a51f4fcc9839fc92e5f8cd1b175 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Tue, 25 Feb 2025 19:30:46 +0800 Subject: [PATCH 03/18] backup --- include/pocketpy/interpreter/frame.h | 6 ++- include/pocketpy/interpreter/vm.h | 1 + src/interpreter/ceval.c | 9 +++- src/interpreter/frame.c | 77 ++++++++++++++++++++++++++-- src/interpreter/generator.c | 12 +++-- src/public/exec.c | 37 +++++++++---- src/public/modules.c | 19 ++++--- 7 files changed, 135 insertions(+), 26 deletions(-) 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]; + } } } } From 2ba131a2528fe024eeadf2abad3c50f7b4d3332c Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Thu, 27 Feb 2025 13:21:55 +0800 Subject: [PATCH 04/18] backup --- include/pocketpy/interpreter/frame.h | 6 +- include/pocketpy/interpreter/vm.h | 1 - include/pocketpy/pocketpy.h | 1 - src/interpreter/frame.c | 115 +++++++++++++-------------- src/interpreter/vm.c | 4 +- src/public/modules.c | 14 +++- src/public/py_mappingproxy.c | 56 ------------- 7 files changed, 69 insertions(+), 128 deletions(-) diff --git a/include/pocketpy/interpreter/frame.h b/include/pocketpy/interpreter/frame.h index 8f5d07b9..91a1f698 100644 --- a/include/pocketpy/interpreter/frame.h +++ b/include/pocketpy/interpreter/frame.h @@ -6,7 +6,7 @@ #include "pocketpy/objects/object.h" #include "pocketpy/pocketpy.h" -py_TValue* FastLocals__try_get_by_name(py_TValue* locals, const CodeObject* co, py_Name name); +void FastLocals__to_dict(py_TValue* locals, const CodeObject* co) PY_RETURN; NameDict* FastLocals__to_namedict(py_TValue* locals, const CodeObject* co); typedef struct ValueStack { @@ -17,7 +17,7 @@ typedef struct ValueStack { } ValueStack; void ValueStack__ctor(ValueStack* self); -void ValueStack__clear(ValueStack* self); +void ValueStack__dtor(ValueStack* self); typedef struct UnwindTarget { struct UnwindTarget* next; @@ -58,7 +58,7 @@ bool Frame__setglobal(Frame* self, py_Name name, py_TValue* val) PY_RAISE; int Frame__delglobal(Frame* self, py_Name name) PY_RAISE; 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; +bool 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); diff --git a/include/pocketpy/interpreter/vm.h b/include/pocketpy/interpreter/vm.h index 11edd121..167aa9d6 100644 --- a/include/pocketpy/interpreter/vm.h +++ b/include/pocketpy/interpreter/vm.h @@ -131,7 +131,6 @@ 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_Type pk_code__register(); py_TValue pk_builtins__register(); diff --git a/include/pocketpy/pocketpy.h b/include/pocketpy/pocketpy.h index 4997b6da..f9bba425 100644 --- a/include/pocketpy/pocketpy.h +++ b/include/pocketpy/pocketpy.h @@ -135,7 +135,6 @@ PK_API bool py_compile(const char* source, /// Python equivalent to `globals()`. PK_API void py_newglobals(py_OutRef); /// Python equivalent to `locals()`. -/// @return a temporary object, which expires on the associated function return. PK_API void py_newlocals(py_OutRef); /************* Values Creation *************/ diff --git a/src/interpreter/frame.c b/src/interpreter/frame.c index 20f84177..73c4f675 100644 --- a/src/interpreter/frame.c +++ b/src/interpreter/frame.c @@ -11,12 +11,21 @@ void ValueStack__ctor(ValueStack* self) { self->end = self->begin + PK_VM_STACK_SIZE; } -void ValueStack__clear(ValueStack* self) { self->sp = self->begin; } +void ValueStack__dtor(ValueStack* self) { self->sp = self->begin; } -py_TValue* FastLocals__try_get_by_name(py_TValue* locals, const CodeObject* co, py_Name name) { - int index = c11_smallmap_n2i__get(&co->varnames_inv, name, -1); - if(index == -1) return NULL; - return &locals[index]; +void FastLocals__to_dict(py_TValue* locals, const CodeObject* co) { + py_StackRef dict = py_pushtmp(); + py_newdict(dict); + c11__foreach(c11_smallmap_n2i_KV, &co->varnames_inv, entry) { + py_TValue* value = &locals[entry->value]; + if(!py_isnil(value)) { + bool ok = py_dict_setitem(dict, py_name2ref(entry->key), value); + assert(ok); + (void)ok; + } + } + py_assign(py_retval(), dict); + py_pop(); } NameDict* FastLocals__to_namedict(py_TValue* locals, const CodeObject* co) { @@ -151,78 +160,62 @@ 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; + if(self->locals->type == tp_locals) { + self = self->locals->_ptr; + } else { + assert(self->locals->type == tp_dict); + return py_dict_getitem(self->locals, py_name2ref(name)); } - 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; } + 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) { +bool 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; + if(self->locals->type == tp_locals) { + self = self->locals->_ptr; + } else { + assert(self->locals->type == tp_dict); + return py_dict_setitem(self->locals, py_name2ref(name), val); + } } + py_Ref slot = Frame__getlocal_noproxy(self, name); + if(slot == NULL) return false; // bad slot + *slot = *val; + return true; } 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; + if(self->locals->type == tp_locals) { + self = self->locals->_ptr; + } else { + assert(self->locals->type == tp_dict); + return py_dict_delitem(self->locals, py_name2ref(name)); } - 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_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); + int index = c11_smallmap_n2i__get(&self->co->varnames_inv, name, -1); + if(index == -1) return NULL; + return &self->locals[index]; } py_Ref Frame__getclosure(Frame* self, py_Name name) { diff --git a/src/interpreter/vm.c b/src/interpreter/vm.c index 933b78bc..2f7ecf0e 100644 --- a/src/interpreter/vm.c +++ b/src/interpreter/vm.c @@ -127,7 +127,7 @@ void VM__ctor(VM* self) { validate(tp_Exception, pk_Exception__register()); validate(tp_bytes, pk_bytes__register()); validate(tp_namedict, pk_namedict__register()); - validate(tp_locals, pk_locals__register()); + validate(tp_locals, pk_newtype("locals", tp_object, NULL, NULL, false, true)); validate(tp_code, pk_code__register()); validate(tp_dict, pk_dict__register()); @@ -258,7 +258,7 @@ void VM__dtor(VM* self) { ModuleDict__dtor(&self->modules); TypeList__dtor(&self->types); FixedMemoryPool__dtor(&self->pool_frame); - ValueStack__clear(&self->stack); + ValueStack__dtor(&self->stack); InternedNames__dtor(&self->names); } diff --git a/src/public/modules.c b/src/public/modules.c index c6491157..450ec2e2 100644 --- a/src/public/modules.c +++ b/src/public/modules.c @@ -511,11 +511,17 @@ void py_newlocals(py_Ref out) { py_newglobals(out); return; } - if(!frame->is_locals_proxy) { - pk_mappingproxy__locals(out, frame); - } else { - *out = *frame->locals; + if(frame->is_locals_proxy) { + if(frame->locals->type == tp_locals) { + frame = frame->locals->_ptr; + } else { + assert(frame->locals->type == tp_dict); + *out = *frame->locals; + return; + } } + FastLocals__to_dict(frame->locals, frame->co); + py_assign(out, py_retval()); } static bool _builtins_execdyn(const char* title, int argc, py_Ref argv, enum py_CompileMode mode) { diff --git a/src/public/py_mappingproxy.c b/src/public/py_mappingproxy.c index 343d8c4a..174c6d81 100644 --- a/src/public/py_mappingproxy.c +++ b/src/public/py_mappingproxy.c @@ -116,59 +116,3 @@ void pk_mappingproxy__locals(py_Ref out, Frame* frame) { // locals() will expire when the frame is destroyed out->_ptr = frame; } - -static bool locals__getitem__(int argc, py_Ref argv) { - PY_CHECK_ARGC(2); - PY_CHECK_ARG_TYPE(1, tp_str); - Frame* frame = argv->_ptr; - py_Name name = py_namev(py_tosv(py_arg(1))); - py_Ref slot = Frame__getlocal_noproxy(frame, 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* frame = argv->_ptr; - py_Name name = py_namev(py_tosv(py_arg(1))); - py_Ref slot = Frame__getlocal_noproxy(frame, 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* frame = argv->_ptr; - py_Name name = py_namev(py_tosv(py_arg(1))); - py_Ref res = Frame__getlocal_noproxy(frame, 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* frame = argv->_ptr; - py_Name name = py_namev(py_tosv(py_arg(1))); - py_Ref slot = Frame__getlocal_noproxy(frame, name); - py_newbool(py_retval(), slot && !py_isnil(slot)); - return true; -} - -py_Type pk_locals__register() { - py_Type type = pk_newtype("locals", tp_object, 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__); - py_newnone(py_tpgetmagic(type, __hash__)); - return type; -} \ No newline at end of file From f941d26845ab40f33048c5370852a6ebd03de0c1 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Thu, 27 Feb 2025 14:13:16 +0800 Subject: [PATCH 05/18] backup --- src/interpreter/ceval.c | 4 ++++ src/interpreter/frame.c | 7 +++++++ src/interpreter/generator.c | 11 +++++------ 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/interpreter/ceval.c b/src/interpreter/ceval.c index f45282f6..e9ad94ad 100644 --- a/src/interpreter/ceval.c +++ b/src/interpreter/ceval.c @@ -181,6 +181,10 @@ FrameResult VM__run_top_frame(VM* self) { Function* ud = py_newobject(SP(), tp_function, 0, sizeof(Function)); Function__ctor(ud, decl, frame->module, frame->globals); if(decl->nested) { + if(frame->is_locals_proxy) { + RuntimeError("cannot create closure from locals proxy"); + goto __ERROR; + } ud->closure = FastLocals__to_namedict(frame->locals, frame->co); py_Name name = py_name(decl->code.name->data); // capture itself to allow recursion diff --git a/src/interpreter/frame.c b/src/interpreter/frame.c index 73c4f675..dddc8236 100644 --- a/src/interpreter/frame.c +++ b/src/interpreter/frame.c @@ -78,6 +78,13 @@ void Frame__delete(Frame* self) { FixedMemoryPool__dealloc(&pk_current_vm->pool_frame, self); } +py_StackRef Frame__locals_sp(Frame *self){ + if(!self->is_locals_proxy){ + return self->locals; + } + return self->p0; +} + int Frame__prepare_jump_exception_handler(Frame* self, ValueStack* _s) { // try to find a parent try block int iblock = Frame__iblock(self); diff --git a/src/interpreter/generator.c b/src/interpreter/generator.c index 9c43bd2b..06d73da0 100644 --- a/src/interpreter/generator.c +++ b/src/interpreter/generator.c @@ -28,13 +28,12 @@ static bool generator__next__(int argc, py_Ref argv) { if(ud->state == 2) return StopIteration(); // reset frame->p0 - 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); + if(ud->frame->is_locals_proxy){ + return RuntimeError("cannot resume generator with 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; // restore the context py_Ref backup = py_getslot(argv, 0); From 96ff66ef69d475af70386efa3763dfc01a470e73 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Thu, 27 Feb 2025 19:51:13 +0800 Subject: [PATCH 06/18] backup --- include/pocketpy/interpreter/vm.h | 3 +- src/interpreter/ceval.c | 106 ++++++++++-------------------- src/interpreter/frame.c | 10 +-- src/public/exec.c | 8 +-- src/public/modules.c | 70 +++++++++++++++----- src/public/py_mappingproxy.c | 14 ---- 6 files changed, 97 insertions(+), 114 deletions(-) diff --git a/include/pocketpy/interpreter/vm.h b/include/pocketpy/interpreter/vm.h index 167aa9d6..92f47cec 100644 --- a/include/pocketpy/interpreter/vm.h +++ b/include/pocketpy/interpreter/vm.h @@ -136,5 +136,4 @@ py_Type pk_code__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); \ No newline at end of file +void pk_mappingproxy__namedict(py_Ref out, py_Ref object); \ No newline at end of file diff --git a/src/interpreter/ceval.c b/src/interpreter/ceval.c index e9ad94ad..eef52054 100644 --- a/src/interpreter/ceval.c +++ b/src/interpreter/ceval.c @@ -208,38 +208,31 @@ FrameResult VM__run_top_frame(VM* self) { DISPATCH(); } case OP_LOAD_NAME: { - // assert(frame->is_dynamic); py_Name name = byte.arg; - py_TValue* tmp; - py_assign(SP()++, py_name2ref(name)); // locals - if(!py_isnone(&frame->p0[1])) { - 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; - } - } + int res = Frame__getlocal(frame, name); + if(res == 1) { + PUSH(&self->last_retval); + DISPATCH(); + } + if(res == -1) goto __ERROR; + // closure + py_Ref tmp = Frame__getclosure(frame, name); + if(tmp != NULL) { + PUSH(tmp); + DISPATCH(); } // globals - if(py_getitem(&frame->p0[0], TOP())) { - py_assign(TOP(), py_retval()); + res = Frame__getglobal(frame, name); + if(res == 1) { + PUSH(&self->last_retval); DISPATCH(); - } else { - if(py_matchexc(tp_KeyError)) { - py_clearexc(NULL); - } else { - goto __ERROR; - } } + if(res == -1) goto __ERROR; // builtins tmp = py_getdict(&self->builtins, name); if(tmp != NULL) { - py_assign(TOP(), tmp); + PUSH(tmp); DISPATCH(); } NameError(name); @@ -353,36 +346,22 @@ FrameResult VM__run_top_frame(VM* self) { DISPATCH(); } case OP_STORE_NAME: { - // assert(frame->is_dynamic); py_Name name = byte.arg; - py_assign(SP()++, py_name2ref(name)); - // [value, name] - if(!py_isnone(&frame->p0[1])) { + if(frame->locals != NULL) { // locals - if(py_setitem(&frame->p0[1], TOP(), SECOND())) { - STACK_SHRINK(2); + int res = Frame__setlocal(frame, name, TOP()); + if(res == 1) { + POP(); DISPATCH(); - } else { - if(py_matchexc(tp_KeyError)) { - py_clearexc(NULL); - NameError(name); - } - goto __ERROR; } + if(res == 0) 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; - } + if(!Frame__setglobal(frame, name, TOP())) { goto __ERROR; } + POP(); + DISPATCH(); } - DISPATCH(); } case OP_STORE_GLOBAL: { if(!Frame__setglobal(frame, byte.arg, TOP())) goto __ERROR; @@ -424,42 +403,27 @@ FrameResult VM__run_top_frame(VM* self) { DISPATCH(); } case OP_DELETE_NAME: { - // assert(frame->is_dynamic); py_Name name = byte.arg; - py_assign(SP()++, py_name2ref(name)); - if(!py_isnone(&frame->p0[1])) { + if(frame->locals != NULL) { // locals - if(py_delitem(&frame->p0[1], TOP())) { - POP(); - DISPATCH(); - } else { - if(py_matchexc(tp_KeyError)) { - py_clearexc(NULL); - NameError(name); - } - goto __ERROR; - } + int res = Frame__dellocal(frame, name); + if(res == 1) DISPATCH(); + if(res == 0) NameError(name); + goto __ERROR; } else { // globals - if(py_delitem(&frame->p0[0], TOP())) { - POP(); - DISPATCH(); - } else { - if(py_matchexc(tp_KeyError)) { - py_clearexc(NULL); - NameError(name); - } - goto __ERROR; - } + int res = Frame__delglobal(frame, name); + if(res == 1) DISPATCH(); + if(res == 0) NameError(name); + goto __ERROR; + DISPATCH(); } - DISPATCH(); } case OP_DELETE_GLOBAL: { py_Name name = byte.arg; int res = Frame__delglobal(frame, name); if(res == 1) DISPATCH(); if(res == -1) goto __ERROR; - // res == 0 NameError(name); goto __ERROR; } diff --git a/src/interpreter/frame.c b/src/interpreter/frame.c index dddc8236..77e9456d 100644 --- a/src/interpreter/frame.c +++ b/src/interpreter/frame.c @@ -54,7 +54,9 @@ Frame* Frame__new(const CodeObject* co, py_Ref locals, bool is_p0_function, bool is_locals_proxy) { - assert(module->type == tp_module || module->type == tp_dict); + assert(module->type == tp_module); + assert(globals->type == tp_module || globals->type == tp_dict); + assert(locals->type == tp_locals || locals->type == tp_dict || locals->type == tp_nil); Frame* self = FixedMemoryPool__alloc(&pk_current_vm->pool_frame); self->f_back = NULL; self->co = co; @@ -78,10 +80,8 @@ void Frame__delete(Frame* self) { FixedMemoryPool__dealloc(&pk_current_vm->pool_frame, self); } -py_StackRef Frame__locals_sp(Frame *self){ - if(!self->is_locals_proxy){ - return self->locals; - } +py_StackRef Frame__locals_sp(Frame* self) { + if(!self->is_locals_proxy) { return self->locals; } return self->p0; } diff --git a/src/public/exec.c b/src/public/exec.c index 94cc7f5b..86bd3867 100644 --- a/src/public/exec.c +++ b/src/public/exec.c @@ -3,15 +3,13 @@ #include "pocketpy/pocketpy.h" #include "pocketpy/common/utils.h" -#include "pocketpy/common/sstream.h" -#include "pocketpy/objects/object.h" #include "pocketpy/interpreter/vm.h" #include "pocketpy/compiler/compiler.h" #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, (void (*)(void *))CodeObject__gc_mark); + pk__tp_set_marker(type, (void (*)(void*))CodeObject__gc_mark); return type; } @@ -56,7 +54,7 @@ bool pk_exec(CodeObject* co, py_Ref module) { assert(module->type == tp_module); py_StackRef sp = vm->stack.sp; - Frame* frame = Frame__new(co, sp, module, module, sp, false, false); + Frame* frame = Frame__new(co, sp, module, module, py_NIL(), false, false); VM__push_frame(vm, frame); FrameResult res = VM__run_top_frame(vm); if(res == RES_ERROR) return false; @@ -70,7 +68,7 @@ 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 && locals != NULL); + assert(globals != NULL); if(globals->type == tp_namedict) { globals = py_getslot(globals, 0); diff --git a/src/public/modules.c b/src/public/modules.c index 450ec2e2..762292a7 100644 --- a/src/public/modules.c +++ b/src/public/modules.c @@ -507,7 +507,7 @@ void py_newglobals(py_Ref out) { void py_newlocals(py_Ref out) { Frame* frame = pk_current_vm->top_frame; - if(!frame || !frame->is_p0_function) { + if(!frame) { py_newglobals(out); return; } @@ -524,61 +524,97 @@ void py_newlocals(py_Ref out) { py_assign(out, py_retval()); } +static void pk_push_locals_proxy() { + Frame* frame = pk_current_vm->top_frame; + if(!frame) { + py_pushnil(); + return; + } + if(frame->is_locals_proxy) { + py_push(frame->locals); + } else { + if(py_isnil(frame->locals)) { + py_pushnil(); + } else { + py_StackRef out = py_pushtmp(); + out->type = tp_locals; + out->is_ptr = false; + out->extra = 0; + // this is a weak reference + // which will expire when the frame is destroyed + out->_ptr = frame; + } + } +} + static bool _builtins_execdyn(const char* title, int argc, py_Ref argv, enum py_CompileMode mode) { switch(argc) { case 1: { py_newglobals(py_pushtmp()); - py_newlocals(py_pushtmp()); + pk_push_locals_proxy(); break; } case 2: { + // globals 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(); + // locals + pk_push_locals_proxy(); break; } case 3: { + // globals 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)); + // locals + if(py_isnone(py_arg(2))) { + pk_push_locals_proxy(); + } else { + if(!py_checktype(py_arg(2), tp_dict)) return false; + py_push(py_arg(2)); + } break; } default: return TypeError("%s() takes at most 3 arguments", title); } - py_Ref code; + py_Ref tmp_code; if(py_isstr(argv)) { bool ok = py_compile(py_tostr(argv), "", mode, true); if(!ok) return false; - code = py_retval(); + tmp_code = py_retval(); } else if(py_istype(argv, tp_code)) { - code = argv; + tmp_code = argv; } else { return TypeError("%s() expected 'str' or 'code', got '%t'", title, argv->type); } - py_push(code); // keep it alive + py_push(tmp_code); // keep it alive + Frame* frame = pk_current_vm->top_frame; // [globals, locals, code] - CodeObject* co = py_touserdata(code); - if(!co->src->is_dynamic) { + CodeObject* code = py_touserdata(tmp_code); + if(code->src->is_dynamic) { + bool ok = pk_execdyn(code, frame ? frame->module : NULL, py_peek(-3), py_peek(-2)); py_shrink(3); - if(argc != 1) - return ValueError("code object is not dynamic, `globals` and `locals` must be None"); + return ok; + } else { + if(argc != 1) { + return ValueError( + "code object is not dynamic, `globals` and `locals` must not be specified"); + } + bool ok = pk_exec(code, frame ? frame->module : NULL); + py_shrink(3); + return ok; } - - Frame* frame = pk_current_vm->top_frame; - 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) { diff --git a/src/public/py_mappingproxy.c b/src/public/py_mappingproxy.c index 174c6d81..525016c0 100644 --- a/src/public/py_mappingproxy.c +++ b/src/public/py_mappingproxy.c @@ -1,9 +1,7 @@ #include "pocketpy/pocketpy.h" -#include "pocketpy/common/utils.h" #include "pocketpy/objects/object.h" #include "pocketpy/interpreter/vm.h" -#include "pocketpy/common/sstream.h" #include void pk_mappingproxy__namedict(py_Ref out, py_Ref object) { @@ -104,15 +102,3 @@ py_Type pk_namedict__register() { py_bindmethod(type, "clear", namedict_clear); return type; } - -////////////////////// - -void pk_mappingproxy__locals(py_Ref out, Frame* frame) { - assert(frame->is_p0_function && !frame->is_locals_proxy); - out->type = tp_locals; - out->is_ptr = false; - out->extra = 0; - // this is a weak reference - // locals() will expire when the frame is destroyed - out->_ptr = frame; -} From 2800f5eedba65bf74229569ab7959ba1921a443e Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Fri, 28 Feb 2025 11:51:23 +0800 Subject: [PATCH 07/18] Update ideas.md --- docs/gsoc2025/ideas.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/gsoc2025/ideas.md b/docs/gsoc2025/ideas.md index dd073931..94a5f497 100644 --- a/docs/gsoc2025/ideas.md +++ b/docs/gsoc2025/ideas.md @@ -24,3 +24,5 @@ pocketpy is planning to provide a tensor library `cTensor` for users who want to In this project, students will help develop and test the `cTensor` library, which is written in C11. We expect students to have a good understanding of further mathematics and C programming. +> This year we also accept custom project ideas. If you are not interested in the above, you can propose your own idea and contact me via `blueloveth@foxmail.com`. We will discuss the feasibility and mentorship for your idea. + From 2a253cb014738a357588b0a8489e5bf074a70420 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Fri, 28 Feb 2025 11:51:23 +0800 Subject: [PATCH 08/18] Update ideas.md --- docs/gsoc2025/ideas.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/gsoc2025/ideas.md b/docs/gsoc2025/ideas.md index dd073931..94a5f497 100644 --- a/docs/gsoc2025/ideas.md +++ b/docs/gsoc2025/ideas.md @@ -24,3 +24,5 @@ pocketpy is planning to provide a tensor library `cTensor` for users who want to In this project, students will help develop and test the `cTensor` library, which is written in C11. We expect students to have a good understanding of further mathematics and C programming. +> This year we also accept custom project ideas. If you are not interested in the above, you can propose your own idea and contact me via `blueloveth@foxmail.com`. We will discuss the feasibility and mentorship for your idea. + From 330e005881ab53a07e662f0e2d69c34518d34e4e Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Fri, 28 Feb 2025 15:09:19 +0800 Subject: [PATCH 09/18] backup --- include/pocketpy/interpreter/frame.h | 8 ++-- src/interpreter/ceval.c | 12 +++--- src/interpreter/frame.c | 56 ++++++++++++++-------------- src/interpreter/generator.c | 4 +- src/interpreter/vm.c | 8 ++-- src/public/exec.c | 4 +- src/public/modules.c | 39 ++++++++----------- 7 files changed, 59 insertions(+), 72 deletions(-) diff --git a/include/pocketpy/interpreter/frame.h b/include/pocketpy/interpreter/frame.h index 91a1f698..be5b160d 100644 --- a/include/pocketpy/interpreter/frame.h +++ b/include/pocketpy/interpreter/frame.h @@ -34,9 +34,8 @@ typedef struct Frame { py_StackRef p0; // unwinding base py_GlobalRef module; py_Ref globals; // a module object or a dict object - py_Ref locals; // locals base or a proxy object (such as dict) - bool is_p0_function; - bool is_locals_proxy; + py_Ref locals; + bool is_locals_special; int ip; UnwindTarget* uw_list; } Frame; @@ -46,8 +45,7 @@ Frame* Frame__new(const CodeObject* co, py_GlobalRef module, py_Ref globals, py_Ref locals, - bool is_p0_function, - bool is_locals_proxy); + bool is_locals_special); void Frame__delete(Frame* self); int Frame__lineno(const Frame* self); diff --git a/src/interpreter/ceval.c b/src/interpreter/ceval.c index eef52054..4c1303f2 100644 --- a/src/interpreter/ceval.c +++ b/src/interpreter/ceval.c @@ -181,8 +181,8 @@ FrameResult VM__run_top_frame(VM* self) { Function* ud = py_newobject(SP(), tp_function, 0, sizeof(Function)); Function__ctor(ud, decl, frame->module, frame->globals); if(decl->nested) { - if(frame->is_locals_proxy) { - RuntimeError("cannot create closure from locals proxy"); + if(frame->is_locals_special) { + RuntimeError("cannot create closure from special locals"); goto __ERROR; } ud->closure = FastLocals__to_namedict(frame->locals, frame->co); @@ -198,7 +198,7 @@ FrameResult VM__run_top_frame(VM* self) { DISPATCH(); /*****************************************/ case OP_LOAD_FAST: { - assert(!frame->is_locals_proxy); + assert(!frame->is_locals_special); PUSH(&frame->locals[byte.arg]); if(py_isnil(TOP())) { py_Name name = c11__getitem(uint16_t, &frame->co->varnames, byte.arg); @@ -341,7 +341,7 @@ FrameResult VM__run_top_frame(VM* self) { goto __ERROR; } case OP_STORE_FAST: { - assert(!frame->is_locals_proxy); + assert(!frame->is_locals_special); frame->locals[byte.arg] = POPX(); DISPATCH(); } @@ -392,7 +392,7 @@ FrameResult VM__run_top_frame(VM* self) { goto __ERROR; } case OP_DELETE_FAST: { - assert(!frame->is_locals_proxy); + assert(!frame->is_locals_special); py_Ref tmp = &frame->locals[byte.arg]; if(py_isnil(tmp)) { py_Name name = c11__getitem(py_Name, &frame->co->varnames, byte.arg); @@ -1146,7 +1146,7 @@ FrameResult VM__run_top_frame(VM* self) { py_BaseException__stpush(&self->curr_exception, frame->co->src, Frame__lineno(frame), - frame->is_p0_function ? frame->co->name->data : NULL); + !frame->is_locals_special ? frame->co->name->data : NULL); __ERROR_RE_RAISE: do { } while(0); diff --git a/src/interpreter/frame.c b/src/interpreter/frame.c index 77e9456d..8920273a 100644 --- a/src/interpreter/frame.c +++ b/src/interpreter/frame.c @@ -52,11 +52,12 @@ Frame* Frame__new(const CodeObject* co, py_GlobalRef module, py_Ref globals, py_Ref locals, - bool is_p0_function, - bool is_locals_proxy) { + bool is_locals_special) { assert(module->type == tp_module); assert(globals->type == tp_module || globals->type == tp_dict); - assert(locals->type == tp_locals || locals->type == tp_dict || locals->type == tp_nil); + if(is_locals_special) { + assert(locals->type == tp_nil || locals->type == tp_locals || locals->type == tp_dict); + } Frame* self = FixedMemoryPool__alloc(&pk_current_vm->pool_frame); self->f_back = NULL; self->co = co; @@ -64,8 +65,7 @@ Frame* Frame__new(const CodeObject* co, self->module = module; self->globals = globals; self->locals = locals; - self->is_p0_function = is_p0_function; - self->is_locals_proxy = is_locals_proxy; + self->is_locals_special = is_locals_special; self->ip = -1; self->uw_list = NULL; return self; @@ -81,8 +81,7 @@ void Frame__delete(Frame* self) { } py_StackRef Frame__locals_sp(Frame* self) { - if(!self->is_locals_proxy) { return self->locals; } - return self->p0; + return !self->is_locals_special ? self->locals : self->p0; } int Frame__prepare_jump_exception_handler(Frame* self, ValueStack* _s) { @@ -120,7 +119,7 @@ void Frame__set_unwind_target(Frame* self, py_TValue* sp) { void Frame__gc_mark(Frame* self) { pk__mark_value(self->globals); - if(self->is_locals_proxy) pk__mark_value(self->locals); + if(self->is_locals_special) pk__mark_value(self->locals); CodeObject__gc_mark(self->co); } @@ -166,12 +165,12 @@ int Frame__delglobal(Frame* self, py_Name name) { } int Frame__getlocal(Frame* self, py_Name name) { - if(self->is_locals_proxy) { - if(self->locals->type == tp_locals) { - self = self->locals->_ptr; - } else { - assert(self->locals->type == tp_dict); - return py_dict_getitem(self->locals, py_name2ref(name)); + if(self->is_locals_special) { + switch(self->locals->type) { + case tp_locals: self = self->locals->_ptr; break; + case tp_dict: return py_dict_getitem(self->locals, py_name2ref(name)); + case tp_nil: return 0; + default: c11__unreachable(); } } py_Ref slot = Frame__getlocal_noproxy(self, name); @@ -185,12 +184,12 @@ int Frame__getlocal(Frame* self, py_Name name) { } bool Frame__setlocal(Frame* self, py_Name name, py_TValue* val) { - if(self->is_locals_proxy) { - if(self->locals->type == tp_locals) { - self = self->locals->_ptr; - } else { - assert(self->locals->type == tp_dict); - return py_dict_setitem(self->locals, py_name2ref(name), val); + if(self->is_locals_special) { + switch(self->locals->type) { + case tp_locals: self = self->locals->_ptr; break; + case tp_dict: return py_dict_setitem(self->locals, py_name2ref(name), val); + case tp_nil: return false; + default: c11__unreachable(); } } py_Ref slot = Frame__getlocal_noproxy(self, name); @@ -200,12 +199,12 @@ bool Frame__setlocal(Frame* self, py_Name name, py_TValue* val) { } int Frame__dellocal(Frame* self, py_Name name) { - if(self->is_locals_proxy) { - if(self->locals->type == tp_locals) { - self = self->locals->_ptr; - } else { - assert(self->locals->type == tp_dict); - return py_dict_delitem(self->locals, py_name2ref(name)); + if(self->is_locals_special) { + switch(self->locals->type) { + case tp_locals: self = self->locals->_ptr; break; + case tp_dict: return py_dict_delitem(self->locals, py_name2ref(name)); + case tp_nil: return 0; + default: c11__unreachable(); } } py_Ref slot = Frame__getlocal_noproxy(self, name); @@ -219,14 +218,15 @@ int Frame__dellocal(Frame* self, py_Name name) { } py_StackRef Frame__getlocal_noproxy(Frame* self, py_Name name) { - assert(!self->is_locals_proxy); + assert(!self->is_locals_special); int index = c11_smallmap_n2i__get(&self->co->varnames_inv, name, -1); if(index == -1) return NULL; return &self->locals[index]; } py_Ref Frame__getclosure(Frame* self, py_Name name) { - if(!self->is_p0_function) return NULL; + if(self->is_locals_special) return NULL; + assert(self->p0->type == tp_function); Function* ud = py_touserdata(self->p0); if(ud->closure == NULL) return NULL; return NameDict__try_get(ud->closure, name); diff --git a/src/interpreter/generator.c b/src/interpreter/generator.c index 06d73da0..bccfe13a 100644 --- a/src/interpreter/generator.c +++ b/src/interpreter/generator.c @@ -28,9 +28,7 @@ static bool generator__next__(int argc, py_Ref argv) { if(ud->state == 2) return StopIteration(); // reset frame->p0 - if(ud->frame->is_locals_proxy){ - return RuntimeError("cannot resume generator with locals proxy"); - } + assert(!ud->frame->is_locals_special); int locals_offset = ud->frame->locals - ud->frame->p0; ud->frame->p0 = py_peek(0); ud->frame->locals = ud->frame->p0 + locals_offset; diff --git a/src/interpreter/vm.c b/src/interpreter/vm.c index 2f7ecf0e..91705554 100644 --- a/src/interpreter/vm.c +++ b/src/interpreter/vm.c @@ -485,8 +485,7 @@ FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall // submit the call if(!fn->cfunc) { // python function - VM__push_frame(self, - Frame__new(co, p0, fn->module, fn->globals, argv, true, false)); + VM__push_frame(self, Frame__new(co, p0, fn->module, fn->globals, argv, false)); return opcall ? RES_CALL : VM__run_top_frame(self); } else { // decl-based binding @@ -515,8 +514,7 @@ FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall // submit the call if(!fn->cfunc) { // python function - VM__push_frame(self, - Frame__new(co, p0, fn->module, fn->globals, argv, true, false)); + VM__push_frame(self, Frame__new(co, p0, fn->module, fn->globals, argv, false)); return opcall ? RES_CALL : VM__run_top_frame(self); } else { // decl-based binding @@ -532,7 +530,7 @@ FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall // copy buffer back to stack self->stack.sp = argv + co->nlocals; memcpy(argv, self->__vectorcall_buffer, co->nlocals * sizeof(py_TValue)); - Frame* frame = Frame__new(co, p0, fn->module, fn->globals, argv, true, false); + Frame* frame = Frame__new(co, p0, fn->module, fn->globals, argv, false); pk_newgenerator(py_retval(), frame, p0, self->stack.sp); self->stack.sp = p0; // reset the stack return RES_RETURN; diff --git a/src/public/exec.c b/src/public/exec.c index 86bd3867..782dfe40 100644 --- a/src/public/exec.c +++ b/src/public/exec.c @@ -54,7 +54,7 @@ bool pk_exec(CodeObject* co, py_Ref module) { assert(module->type == tp_module); py_StackRef sp = vm->stack.sp; - Frame* frame = Frame__new(co, sp, module, module, py_NIL(), false, false); + Frame* frame = Frame__new(co, sp, module, module, py_NIL(), true); VM__push_frame(vm, frame); FrameResult res = VM__run_top_frame(vm); if(res == RES_ERROR) return false; @@ -76,7 +76,7 @@ bool pk_execdyn(CodeObject* co, py_Ref module, py_Ref globals, py_Ref locals) { } else { assert(globals->type == tp_dict); } - Frame* frame = Frame__new(co, sp, module, globals, locals, false, true); + Frame* frame = Frame__new(co, sp, module, globals, locals, true); VM__push_frame(vm, frame); FrameResult res = VM__run_top_frame(vm); if(res == RES_ERROR) return false; diff --git a/src/public/modules.c b/src/public/modules.c index 762292a7..37782c32 100644 --- a/src/public/modules.c +++ b/src/public/modules.c @@ -511,13 +511,12 @@ void py_newlocals(py_Ref out) { py_newglobals(out); return; } - if(frame->is_locals_proxy) { - if(frame->locals->type == tp_locals) { - frame = frame->locals->_ptr; - } else { - assert(frame->locals->type == tp_dict); - *out = *frame->locals; - return; + if(frame->is_locals_special) { + switch(frame->locals->type) { + case tp_locals: frame = frame->locals->_ptr; break; + case tp_dict: *out = *frame->locals; return; + case tp_nil: py_newglobals(out); return; + default: c11__unreachable(); } } FastLocals__to_dict(frame->locals, frame->co); @@ -530,20 +529,16 @@ static void pk_push_locals_proxy() { py_pushnil(); return; } - if(frame->is_locals_proxy) { + if(frame->is_locals_special) { py_push(frame->locals); } else { - if(py_isnil(frame->locals)) { - py_pushnil(); - } else { - py_StackRef out = py_pushtmp(); - out->type = tp_locals; - out->is_ptr = false; - out->extra = 0; - // this is a weak reference - // which will expire when the frame is destroyed - out->_ptr = frame; - } + py_StackRef out = py_pushtmp(); + out->type = tp_locals; + out->is_ptr = false; + out->extra = 0; + // this is a weak reference + // which will expire when the frame is destroyed + out->_ptr = frame; } } @@ -827,16 +822,14 @@ static bool super__new__(int argc, py_Ref argv) { py_Ref self_arg = NULL; if(argc == 1) { // super() - if(frame->is_p0_function && !frame->is_locals_proxy) { + if(!frame->is_locals_special) { py_TValue* callable = frame->p0; if(callable->type == tp_boundmethod) callable = py_getslot(frame->p0, 1); if(callable->type == tp_function) { Function* func = py_touserdata(callable); if(func->clazz != NULL) { class_arg = *(py_Type*)PyObject__userdata(func->clazz); - if(frame->co->nlocals > 0) { - if(!frame->is_locals_proxy) self_arg = &frame->locals[0]; - } + if(frame->co->nlocals > 0) { self_arg = &frame->locals[0]; } } } } From 45842638b3733caa0685bd92f43f9143e44868aa Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Fri, 28 Feb 2025 15:11:24 +0800 Subject: [PATCH 10/18] backup --- include/pocketpy/interpreter/frame.h | 2 -- src/interpreter/frame.c | 10 +++------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/include/pocketpy/interpreter/frame.h b/include/pocketpy/interpreter/frame.h index be5b160d..03426333 100644 --- a/include/pocketpy/interpreter/frame.h +++ b/include/pocketpy/interpreter/frame.h @@ -62,8 +62,6 @@ 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*); UnwindTarget* Frame__find_unwind_target(Frame* self, int iblock); diff --git a/src/interpreter/frame.c b/src/interpreter/frame.c index 8920273a..dfeb35a6 100644 --- a/src/interpreter/frame.c +++ b/src/interpreter/frame.c @@ -80,10 +80,6 @@ void Frame__delete(Frame* self) { FixedMemoryPool__dealloc(&pk_current_vm->pool_frame, self); } -py_StackRef Frame__locals_sp(Frame* self) { - return !self->is_locals_special ? self->locals : self->p0; -} - int Frame__prepare_jump_exception_handler(Frame* self, ValueStack* _s) { // try to find a parent try block int iblock = Frame__iblock(self); @@ -94,7 +90,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 = (Frame__locals_sp(self) + uw->offset); // unwind the stack + _s->sp = (self->p0 + uw->offset); // unwind the stack return c11__at(CodeBlock, &self->co->blocks, iblock)->end; } @@ -110,10 +106,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 - Frame__locals_sp(self); + existing->offset = sp - self->p0; } else { UnwindTarget* prev = self->uw_list; - self->uw_list = UnwindTarget__new(prev, iblock, sp - Frame__locals_sp(self)); + self->uw_list = UnwindTarget__new(prev, iblock, sp - self->p0); } } From a51e7e6fd4aae40f1611d3000a63a22a6819a6ea Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Fri, 28 Feb 2025 15:16:34 +0800 Subject: [PATCH 11/18] backup --- amalgamate.py | 2 +- docs/license.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/amalgamate.py b/amalgamate.py index 04371316..797b7788 100644 --- a/amalgamate.py +++ b/amalgamate.py @@ -11,7 +11,7 @@ ROOT = 'include/pocketpy' PUBLIC_HEADERS = ['config.h', 'export.h', 'linalg.h', 'pocketpy.h'] COPYRIGHT = '''/* - * Copyright (c) 2024 blueloveTH + * Copyright (c) 2025 blueloveTH * Distributed Under The MIT License * https://github.com/pocketpy/pocketpy */ diff --git a/docs/license.md b/docs/license.md index b6e3a26c..509011c0 100644 --- a/docs/license.md +++ b/docs/license.md @@ -11,7 +11,7 @@ pkpy is licensed under the [MIT License](http://opensource.org/licenses/MIT). ``` MIT License -Copyright (c) 2024 blueloveTH +Copyright (c) 2025 blueloveTH Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 2e31412d3adc1dd96024df42adeb7d12ba3efcff Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Fri, 28 Feb 2025 15:40:17 +0800 Subject: [PATCH 12/18] backup --- 3rd/libhv/src/HttpServer.cpp | 24 +++++++++++------------- 3rd/libhv/src/WebSocketClient.cpp | 17 ++++++++--------- include/pocketpy/pocketpy.h | 2 +- src/compiler/compiler.c | 4 ++-- src/interpreter/ceval.c | 7 +++---- src/interpreter/vm.c | 4 ++-- src/modules/array2d.c | 22 +++++++++------------- src/modules/linalg.c | 6 +++--- src/modules/math.c | 8 +++----- src/modules/pickle.c | 12 +++++------- src/public/py_dict.c | 14 +++++++------- src/public/py_exception.c | 4 ++-- src/public/py_mappingproxy.c | 18 +++++++++--------- src/public/py_number.c | 6 +++--- src/public/py_tuple.c | 11 ++++++----- 15 files changed, 74 insertions(+), 85 deletions(-) diff --git a/3rd/libhv/src/HttpServer.cpp b/3rd/libhv/src/HttpServer.cpp index 7a59e2e3..b481c908 100644 --- a/3rd/libhv/src/HttpServer.cpp +++ b/3rd/libhv/src/HttpServer.cpp @@ -208,34 +208,32 @@ static bool libhv_HttpServer_ws_recv(int argc, py_Ref argv) { py_newnone(py_retval()); return true; } - py_newtuple(py_retval(), 2); + py_Ref data = py_newtuple(py_retval(), 2); switch(msg.type) { case WsMessageType::onopen: { // "onopen", (channel, request) assert(msg.request != nullptr); - py_newstr(py_tuple_getitem(py_retval(), 0), "onopen"); - py_Ref args = py_tuple_getitem(py_retval(), 1); - py_newtuple(args, 2); - py_newint(py_tuple_getitem(args, 0), (py_i64)msg.channel); - libhv_HttpRequest_create(py_tuple_getitem(args, 1), msg.request); + py_newstr(py_offset(data, 0), "onopen"); + py_Ref p = py_newtuple(py_offset(data, 1), 2); + py_newint(py_offset(p, 0), (py_i64)msg.channel); + libhv_HttpRequest_create(py_offset(p, 1), msg.request); break; } case WsMessageType::onclose: { // "onclose", channel - py_newstr(py_tuple_getitem(py_retval(), 0), "onclose"); - py_newint(py_tuple_getitem(py_retval(), 1), (py_i64)msg.channel); + py_newstr(py_offset(data, 0), "onclose"); + py_newint(py_offset(data, 1), (py_i64)msg.channel); break; } case WsMessageType::onmessage: { // "onmessage", (channel, body) - py_newstr(py_tuple_getitem(py_retval(), 0), "onmessage"); - py_Ref args = py_tuple_getitem(py_retval(), 1); - py_newtuple(args, 2); - py_newint(py_tuple_getitem(args, 0), (py_i64)msg.channel); + py_newstr(py_offset(data, 0), "onmessage"); + py_Ref p = py_newtuple(py_offset(data, 1), 2); + py_newint(py_offset(p, 0), (py_i64)msg.channel); c11_sv sv; sv.data = msg.body.data(); sv.size = msg.body.size(); - py_newstrv(py_tuple_getitem(args, 1), sv); + py_newstrv(py_offset(p, 1), sv); break; } } diff --git a/3rd/libhv/src/WebSocketClient.cpp b/3rd/libhv/src/WebSocketClient.cpp index 21763d55..303bd025 100644 --- a/3rd/libhv/src/WebSocketClient.cpp +++ b/3rd/libhv/src/WebSocketClient.cpp @@ -59,7 +59,7 @@ py_Type libhv_register_WebSocketClient(py_GlobalRef mod) { http_headers* p_headers = (http_headers*)ctx; if(!py_checkstr(key)) return false; if(!py_checkstr(value)) return false; - p_headers->operator[](py_tostr(key)) = py_tostr(value); + p_headers->operator[] (py_tostr(key)) = py_tostr(value); return true; }, &headers); @@ -99,22 +99,21 @@ py_Type libhv_register_WebSocketClient(py_GlobalRef mod) { py_newnone(py_retval()); return true; } else { - py_newtuple(py_retval(), 2); + py_Ref p = py_newtuple(py_retval(), 2); switch(mq_msg.first) { case WsMessageType::onopen: { - py_newstr(py_tuple_getitem(py_retval(), 0), "onopen"); - py_newnone(py_tuple_getitem(py_retval(), 1)); + py_newstr(py_offset(p, 0), "onopen"); + py_newnone(py_offset(p, 1)); break; } case WsMessageType::onclose: { - py_newstr(py_tuple_getitem(py_retval(), 0), "onclose"); - py_newnone(py_tuple_getitem(py_retval(), 1)); + py_newstr(py_offset(p, 0), "onclose"); + py_newnone(py_offset(p, 1)); break; } case WsMessageType::onmessage: { - py_newstr(py_tuple_getitem(py_retval(), 0), "onmessage"); - py_newstrv(py_tuple_getitem(py_retval(), 1), - {mq_msg.second.data(), (int)mq_msg.second.size()}); + py_newstr(py_offset(p, 0), "onmessage"); + py_newstrv(py_offset(p, 1), {mq_msg.second.data(), (int)mq_msg.second.size()}); break; } } diff --git a/include/pocketpy/pocketpy.h b/include/pocketpy/pocketpy.h index f9bba425..355d1405 100644 --- a/include/pocketpy/pocketpy.h +++ b/include/pocketpy/pocketpy.h @@ -175,7 +175,7 @@ PK_API void py_newellipsis(py_OutRef); PK_API void py_newnil(py_OutRef); /// Create a `tuple` with `n` UNINITIALIZED elements. /// You should initialize all elements before using it. -PK_API void py_newtuple(py_OutRef, int n); +PK_API py_ObjectRef py_newtuple(py_OutRef, int n); /// Create an empty `list`. PK_API void py_newlist(py_OutRef); /// Create a `list` with `n` UNINITIALIZED elements. diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index 6adc55ce..9512a308 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -2193,9 +2193,9 @@ static Error* read_literal(Compiler* self, py_Ref out) { if(curr()->type == TK_RPAREN) break; } consume(TK_RPAREN); - py_newtuple(out, count); + py_Ref p = py_newtuple(out, count); for(int i = 0; i < count; i++) { - py_tuple_setitem(out, i, &cpnts[i]); + p[i] = cpnts[i]; } return NULL; } diff --git a/src/interpreter/ceval.c b/src/interpreter/ceval.c index 4c1303f2..4db89b2b 100644 --- a/src/interpreter/ceval.c +++ b/src/interpreter/ceval.c @@ -474,11 +474,10 @@ FrameResult VM__run_top_frame(VM* self) { } case OP_BUILD_TUPLE: { py_TValue tmp; - py_newtuple(&tmp, byte.arg); + py_Ref p = py_newtuple(&tmp, byte.arg); py_TValue* begin = SP() - byte.arg; - for(int i = 0; i < byte.arg; i++) { - py_tuple_setitem(&tmp, i, begin + i); - } + for(int i = 0; i < byte.arg; i++) + p[i] = begin[i]; SP() = begin; PUSH(&tmp); DISPATCH(); diff --git a/src/interpreter/vm.c b/src/interpreter/vm.c index 91705554..3c76f9fc 100644 --- a/src/interpreter/vm.c +++ b/src/interpreter/vm.c @@ -405,9 +405,9 @@ static bool if(decl->starred_arg != -1) { int exceed_argc = p1 - t; py_Ref vargs = &buffer[decl->starred_arg]; - py_newtuple(vargs, exceed_argc); + py_Ref data = py_newtuple(vargs, exceed_argc); for(int j = 0; j < exceed_argc; j++) { - py_tuple_setitem(vargs, j, t++); + data[j] = *t++; } } else { // kwdefaults override diff --git a/src/modules/array2d.c b/src/modules/array2d.c index c86f367e..f4db1b41 100644 --- a/src/modules/array2d.c +++ b/src/modules/array2d.c @@ -606,8 +606,7 @@ static bool array2d_like_get_bounding_rect(int argc, py_Ref argv) { if(width <= 0 || height <= 0) { return ValueError("value not found"); } else { - py_newtuple(py_retval(), 4); - py_TValue* data = py_tuple_data(py_retval()); + py_TValue* data = py_newtuple(py_retval(), 4); py_newint(&data[0], left); py_newint(&data[1], top); py_newint(&data[2], width); @@ -786,8 +785,7 @@ static bool array2d_like_iterator__next__(int argc, py_Ref argv) { PY_CHECK_ARGC(1); c11_array2d_like_iterator* self = py_touserdata(argv); if(self->j >= self->array->n_rows) return StopIteration(); - py_newtuple(py_retval(), 2); - py_TValue* data = py_tuple_data(py_retval()); + py_TValue* data = py_newtuple(py_retval(), 2); py_newvec2i(&data[0], (c11_vec2i){ {self->i, self->j} @@ -1081,14 +1079,13 @@ static bool chunked_array2d__delitem__(int argc, py_Ref argv) { static bool chunked_array2d__iter__(int argc, py_Ref argv) { PY_CHECK_ARGC(1); c11_chunked_array2d* self = py_touserdata(argv); - py_newtuple(py_pushtmp(), self->chunks.length); + py_Ref data = py_newtuple(py_pushtmp(), self->chunks.length); for(int i = 0; i < self->chunks.length; i++) { - py_Ref slot = py_tuple_getitem(py_peek(-1), i); c11_chunked_array2d_chunks_KV* kv = c11__at(c11_chunked_array2d_chunks_KV, &self->chunks, i); - py_newtuple(slot, 2); - py_newvec2i(py_tuple_getitem(slot, 0), kv->key); - py_tuple_setitem(slot, 1, &kv->value[0]); + py_Ref p = py_newtuple(&data[i], 2); + py_newvec2i(&p[0], kv->key); // pos + p[1] = kv->value[0]; // context } bool ok = py_iter(py_peek(-1)); if(!ok) return false; @@ -1147,10 +1144,9 @@ static bool chunked_array2d_world_to_chunk(int argc, py_Ref argv) { c11_vec2i pos = py_tovec2i(&argv[1]); c11_vec2i chunk_pos, local_pos; c11_chunked_array2d__world_to_chunk(self, pos.x, pos.y, &chunk_pos, &local_pos); - py_newtuple(py_retval(), 2); - py_TValue* data = py_tuple_data(py_retval()); - py_newvec2i(&data[0], chunk_pos); - py_newvec2i(&data[1], local_pos); + py_TValue* p = py_newtuple(py_retval(), 2); + py_newvec2i(&p[0], chunk_pos); + py_newvec2i(&p[1], local_pos); return true; } diff --git a/src/modules/linalg.c b/src/modules/linalg.c index 3c313d6d..188bc779 100644 --- a/src/modules/linalg.c +++ b/src/modules/linalg.c @@ -432,12 +432,12 @@ static bool vec2_smoothdamp_STATIC(int argc, py_Ref argv) { } py_Ref ret = py_retval(); - py_newtuple(ret, 2); - py_newvec2(py_tuple_getitem(ret, 0), + py_Ref p = py_newtuple(ret, 2); + py_newvec2(&p[0], (c11_vec2){ {output_x, output_y} }); - py_newvec2(py_tuple_getitem(ret, 1), currentVelocity); + py_newvec2(&p[1], currentVelocity); return true; } diff --git a/src/modules/math.c b/src/modules/math.c index e5d0b7c2..09b5d590 100644 --- a/src/modules/math.c +++ b/src/modules/math.c @@ -136,11 +136,9 @@ static bool math_modf(int argc, py_Ref argv) { PY_CHECK_ARGC(1); double i; double f = modf(py_tofloat(py_arg(0)), &i); - py_newtuple(py_retval(), 2); - py_Ref _0 = py_tuple_getitem(py_retval(), 0); - py_Ref _1 = py_tuple_getitem(py_retval(), 1); - py_newfloat(_0, f); - py_newfloat(_1, i); + py_Ref p = py_newtuple(py_retval(), 2); + py_newfloat(&p[0], f); + py_newfloat(&p[1], i); return true; } diff --git a/src/modules/pickle.c b/src/modules/pickle.c index b5afae1e..4024e14a 100644 --- a/src/modules/pickle.c +++ b/src/modules/pickle.c @@ -477,22 +477,21 @@ static py_Type pkl__fix_type(py_Type type, c11_smallmap_n2i* type_mapping) { bool py_pickle_loads_body(const unsigned char* p, int memo_length, c11_smallmap_n2i* type_mapping) { py_StackRef p0 = py_peek(0); - py_StackRef memo = py_pushtmp(); - py_newtuple(memo, memo_length); + py_Ref p_memo = py_newtuple(py_pushtmp(), memo_length); while(true) { PickleOp op = (PickleOp)*p; p++; switch(op) { case PKL_MEMO_GET: { int index = pkl__read_int(&p); - py_Ref val = py_tuple_getitem(memo, index); + py_Ref val = &p_memo[index]; assert(!py_isnil(val)); py_push(val); break; } case PKL_MEMO_SET: { int index = pkl__read_int(&p); - py_tuple_setitem(memo, index, py_peek(-1)); + p_memo[index] = *py_peek(-1); break; } case PKL_NIL: { @@ -589,10 +588,9 @@ bool py_pickle_loads_body(const unsigned char* p, int memo_length, c11_smallmap_ case PKL_BUILD_TUPLE: { int length = pkl__read_int(&p); py_OutRef val = py_retval(); - py_newtuple(val, length); + py_Ref p = py_newtuple(val, length); for(int i = length - 1; i >= 0; i--) { - py_StackRef item = py_peek(-1); - py_tuple_setitem(val, i, item); + p[i] = *py_peek(-1); py_pop(); } py_push(val); diff --git a/src/public/py_dict.c b/src/public/py_dict.c index 9a5361a1..8f12f476 100644 --- a/src/public/py_dict.c +++ b/src/public/py_dict.c @@ -496,14 +496,14 @@ static bool dict_items(int argc, py_Ref argv) { static bool dict_keys(int argc, py_Ref argv) { PY_CHECK_ARGC(1); Dict* self = py_touserdata(argv); - py_newtuple(py_retval(), self->length); + py_Ref p = py_newtuple(py_retval(), self->length); DictIterator iter; DictIterator__ctor(&iter, self); int i = 0; while(1) { DictEntry* entry = DictIterator__next(&iter); if(!entry) break; - py_tuple_setitem(py_retval(), i++, &entry->key); + p[i++] = entry->key; } assert(i == self->length); return true; @@ -512,14 +512,14 @@ static bool dict_keys(int argc, py_Ref argv) { static bool dict_values(int argc, py_Ref argv) { PY_CHECK_ARGC(1); Dict* self = py_touserdata(argv); - py_newtuple(py_retval(), self->length); + py_Ref p = py_newtuple(py_retval(), self->length); DictIterator iter; DictIterator__ctor(&iter, self); int i = 0; while(1) { DictEntry* entry = DictIterator__next(&iter); if(!entry) break; - py_tuple_setitem(py_retval(), i++, &entry->val); + p[i++] = entry->val; } assert(i == self->length); return true; @@ -570,9 +570,9 @@ static bool dict_items__next__(int argc, py_Ref argv) { DictIterator* iter = py_touserdata(py_arg(0)); DictEntry* entry = (DictIterator__next(iter)); if(entry) { - py_newtuple(py_retval(), 2); - py_tuple_setitem(py_retval(), 0, &entry->key); - py_tuple_setitem(py_retval(), 1, &entry->val); + py_Ref p = py_newtuple(py_retval(), 2); + p[0] = entry->key; + p[1] = entry->val; return true; } return StopIteration(); diff --git a/src/public/py_exception.c b/src/public/py_exception.c index 22ab7da8..088e06ad 100644 --- a/src/public/py_exception.c +++ b/src/public/py_exception.c @@ -88,8 +88,8 @@ static bool BaseException_args(int argc, py_Ref argv){ PY_CHECK_ARGC(1); py_Ref arg = py_getslot(argv, 0); if(!py_isnil(arg)) { - py_newtuple(py_retval(), 1); - py_setslot(py_retval(), 0, arg); + py_Ref p = py_newtuple(py_retval(), 1); + p[0] = *arg; }else{ py_newtuple(py_retval(), 0); } diff --git a/src/public/py_mappingproxy.c b/src/public/py_mappingproxy.c index 525016c0..805958d7 100644 --- a/src/public/py_mappingproxy.c +++ b/src/public/py_mappingproxy.c @@ -57,26 +57,26 @@ static bool namedict_items(int argc, py_Ref argv) { for(int j = 0; j < PK_MAGIC_SLOTS_COMMON_LENGTH; j++) { if(py_isnil(ti->magic_0 + j)) continue; py_Ref slot = py_list_emplace(py_retval()); - py_newtuple(slot, 2); - py_assign(py_tuple_getitem(slot, 0), py_name2ref(j + PK_MAGIC_SLOTS_UNCOMMON_LENGTH)); - py_assign(py_tuple_getitem(slot, 1), ti->magic_0 + j); + py_Ref p = py_newtuple(slot, 2); + p[0] = *py_name2ref(j + PK_MAGIC_SLOTS_UNCOMMON_LENGTH); + p[1] = ti->magic_0[j]; } if(ti->magic_1) { for(int j = 0; j < PK_MAGIC_SLOTS_UNCOMMON_LENGTH; j++) { if(py_isnil(ti->magic_1 + j)) continue; py_Ref slot = py_list_emplace(py_retval()); - py_newtuple(slot, 2); - py_assign(py_tuple_getitem(slot, 0), py_name2ref(j)); - py_assign(py_tuple_getitem(slot, 1), ti->magic_1 + j); + py_Ref p = py_newtuple(slot, 2); + p[0] = *py_name2ref(j); + p[1] = ti->magic_1[j]; } } } for(int i = 0; i < dict->length; i++) { py_Ref slot = py_list_emplace(py_retval()); - py_newtuple(slot, 2); + py_Ref p = py_newtuple(slot, 2); NameDict_KV* kv = c11__at(NameDict_KV, dict, i); - py_assign(py_tuple_getitem(slot, 0), py_name2ref(kv->key)); - py_assign(py_tuple_getitem(slot, 1), &kv->value); + p[0] = *py_name2ref(kv->key); + p[1] = kv->value; } return true; } diff --git a/src/public/py_number.c b/src/public/py_number.c index c7769aa8..6b2ee092 100644 --- a/src/public/py_number.c +++ b/src/public/py_number.c @@ -181,9 +181,9 @@ static bool int__divmod__(int argc, py_Ref argv) { py_i64 lhs = py_toint(&argv[0]); py_i64 rhs = py_toint(&argv[1]); if(rhs == 0) return ZeroDivisionError("integer division or modulo by zero"); - py_newtuple(py_retval(), 2); - py_newint(py_getslot(py_retval(), 0), cpy11__fast_floor_div(lhs, rhs)); - py_newint(py_getslot(py_retval(), 1), cpy11__fast_mod(lhs, rhs)); + py_Ref p = py_newtuple(py_retval(), 2); + py_newint(&p[0], cpy11__fast_floor_div(lhs, rhs)); + py_newint(&p[1], cpy11__fast_mod(lhs, rhs)); return true; } diff --git a/src/public/py_tuple.c b/src/public/py_tuple.c index 8a9a3d55..1c0f97b2 100644 --- a/src/public/py_tuple.c +++ b/src/public/py_tuple.c @@ -5,12 +5,13 @@ #include "pocketpy/objects/object.h" #include "pocketpy/interpreter/vm.h" -void py_newtuple(py_Ref out, int n) { +py_ObjectRef py_newtuple(py_Ref out, int n) { VM* vm = pk_current_vm; PyObject* obj = ManagedHeap__gcnew(&vm->heap, tp_tuple, n, 0); out->type = tp_tuple; out->is_ptr = true; out->_obj = obj; + return PyObject__slots(obj); } py_Ref py_tuple_getitem(py_Ref self, int i) { return py_getslot(self, i); } @@ -59,9 +60,9 @@ static bool tuple__new__(int argc, py_Ref argv) { py_Ref tmp = py_pushtmp(); *tmp = *py_retval(); // backup the list int length = py_list_len(tmp); - py_newtuple(py_retval(), length); + py_Ref p = py_newtuple(py_retval(), length); for(int i = 0; i < py_tuple_len(py_retval()); i++) { - py_tuple_setitem(py_retval(), i, py_list_getitem(tmp, i)); + p[i] = *py_list_getitem(tmp, i); } py_pop(); return true; @@ -86,9 +87,9 @@ static bool tuple__getitem__(int argc, py_Ref argv) { py_newlist(tmp); PK_SLICE_LOOP(i, start, stop, step) py_list_append(tmp, py_getslot(argv, i)); // convert list to tuple - py_newtuple(py_retval(), py_list_len(tmp)); + py_Ref p = py_newtuple(py_retval(), py_list_len(tmp)); for(int i = 0; i < py_tuple_len(py_retval()); i++) { - py_tuple_setitem(py_retval(), i, py_list_getitem(tmp, i)); + p[i] = *py_list_getitem(tmp, i); } py_pop(); return true; From d160d79d3fcbd185b9a72db49f3547fc78437101 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Fri, 28 Feb 2025 15:52:30 +0800 Subject: [PATCH 13/18] fix a bug --- src/interpreter/name.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/interpreter/name.c b/src/interpreter/name.c index 1a34c2ad..71966ee1 100644 --- a/src/interpreter/name.c +++ b/src/interpreter/name.c @@ -37,11 +37,10 @@ py_Name py_namev(c11_sv name) { char* p = PK_MALLOC(name.size + 1); memcpy(p, name.data, name.size); p[name.size] = '\0'; - RInternedEntry entry; - entry.data = p; - entry.size = name.size; - entry.obj.type = tp_nil; - c11_vector__push(RInternedEntry, &self->r_interned, entry); + RInternedEntry* entry = c11_vector__emplace(&self->r_interned); + entry->data = p; + entry->size = name.size; + memset(&entry->obj, 0, sizeof(py_TValue)); index = self->r_interned.length; // 1-based // save to _interned c11_smallmap_s2n__set(&self->interned, (c11_sv){p, name.size}, index); @@ -66,7 +65,7 @@ py_GlobalRef py_name2ref(py_Name index) { InternedNames* self = &pk_current_vm->names; assert(index > 0 && index <= self->interned.length); RInternedEntry* entry = c11__at(RInternedEntry, &self->r_interned, index - 1); - if(entry->obj.type == tp_nil){ + if(entry->obj.type == tp_nil) { c11_sv sv; sv.data = entry->data; sv.size = entry->size; @@ -74,4 +73,3 @@ py_GlobalRef py_name2ref(py_Name index) { } return &entry->obj; } - From bf7bfa938c1484b44cec6698c10a50d32e1bd263 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Fri, 28 Feb 2025 16:25:22 +0800 Subject: [PATCH 14/18] some fix --- include/pocketpy/interpreter/frame.h | 4 - src/interpreter/ceval.c | 111 ++++++++++++++++++--------- src/interpreter/frame.c | 53 ------------- src/public/modules.c | 17 ++-- 4 files changed, 81 insertions(+), 104 deletions(-) diff --git a/include/pocketpy/interpreter/frame.h b/include/pocketpy/interpreter/frame.h index 03426333..510ae0f6 100644 --- a/include/pocketpy/interpreter/frame.h +++ b/include/pocketpy/interpreter/frame.h @@ -55,10 +55,6 @@ 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; -int Frame__getlocal(Frame* self, py_Name name) PY_RAISE PY_RETURN; -bool 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); diff --git a/src/interpreter/ceval.c b/src/interpreter/ceval.c index 4db89b2b..2b337614 100644 --- a/src/interpreter/ceval.c +++ b/src/interpreter/ceval.c @@ -208,29 +208,43 @@ FrameResult VM__run_top_frame(VM* self) { DISPATCH(); } case OP_LOAD_NAME: { + assert(frame->is_locals_special); py_Name name = byte.arg; // locals - int res = Frame__getlocal(frame, name); - if(res == 1) { - PUSH(&self->last_retval); - DISPATCH(); - } - if(res == -1) goto __ERROR; - // closure - py_Ref tmp = Frame__getclosure(frame, name); - if(tmp != NULL) { - PUSH(tmp); - DISPATCH(); + switch(frame->locals->type) { + case tp_locals: { + Frame* noproxy = frame->locals->_ptr; + py_Ref slot = Frame__getlocal_noproxy(noproxy, name); + if(slot == NULL) break; + if(py_isnil(slot)) { + UnboundLocalError(name); + goto __ERROR; + } + PUSH(slot); + DISPATCH(); + } + case tp_dict: { + int res = py_dict_getitem(frame->locals, py_name2ref(name)); + if(res == 1) { + PUSH(&self->last_retval); + DISPATCH(); + } + if(res == 0) break; + assert(res == -1); + goto __ERROR; + } + case tp_nil: break; + default: c11__unreachable(); } // globals - res = Frame__getglobal(frame, name); + int res = Frame__getglobal(frame, name); if(res == 1) { PUSH(&self->last_retval); DISPATCH(); } if(res == -1) goto __ERROR; // builtins - tmp = py_getdict(&self->builtins, name); + py_Ref tmp = py_getdict(&self->builtins, name); if(tmp != NULL) { PUSH(tmp); DISPATCH(); @@ -346,21 +360,31 @@ FrameResult VM__run_top_frame(VM* self) { DISPATCH(); } case OP_STORE_NAME: { + assert(frame->is_locals_special); py_Name name = byte.arg; - if(frame->locals != NULL) { - // locals - int res = Frame__setlocal(frame, name, TOP()); - if(res == 1) { + switch(frame->locals->type) { + case tp_locals: { + Frame* noproxy = frame->locals->_ptr; + py_Ref slot = Frame__getlocal_noproxy(noproxy, name); + if(slot == NULL) { + UnboundLocalError(name); + goto __ERROR; + } + *slot = POPX(); + DISPATCH(); + } + case tp_dict: { + if(!py_dict_setitem(frame->locals, py_name2ref(name), TOP())) goto __ERROR; POP(); DISPATCH(); } - if(res == 0) NameError(name); - goto __ERROR; - } else { - // globals - if(!Frame__setglobal(frame, name, TOP())) { goto __ERROR; } - POP(); - DISPATCH(); + case tp_nil: { + // globals + if(!Frame__setglobal(frame, name, TOP())) goto __ERROR; + POP(); + DISPATCH(); + } + default: c11__unreachable(); } } case OP_STORE_GLOBAL: { @@ -403,20 +427,33 @@ FrameResult VM__run_top_frame(VM* self) { DISPATCH(); } case OP_DELETE_NAME: { + assert(frame->is_locals_special); py_Name name = byte.arg; - if(frame->locals != NULL) { - // locals - int res = Frame__dellocal(frame, name); - if(res == 1) DISPATCH(); - if(res == 0) NameError(name); - goto __ERROR; - } else { - // globals - int res = Frame__delglobal(frame, name); - if(res == 1) DISPATCH(); - if(res == 0) NameError(name); - goto __ERROR; - DISPATCH(); + switch(frame->locals->type) { + case tp_locals: { + Frame* noproxy = frame->locals->_ptr; + py_Ref slot = Frame__getlocal_noproxy(noproxy, name); + if(slot == NULL || py_isnil(slot)) { + UnboundLocalError(name); + goto __ERROR; + } + py_newnil(slot); + DISPATCH(); + } + case tp_dict: { + int res = py_dict_delitem(frame->locals, py_name2ref(name)); + if(res == 1) DISPATCH(); + if(res == 0) UnboundLocalError(name); + goto __ERROR; + } + case tp_nil: { + // globals + int res = Frame__delglobal(frame, name); + if(res == 1) DISPATCH(); + if(res == 0) NameError(name); + goto __ERROR; + } + default: c11__unreachable(); } } case OP_DELETE_GLOBAL: { diff --git a/src/interpreter/frame.c b/src/interpreter/frame.c index dfeb35a6..7ce44fab 100644 --- a/src/interpreter/frame.c +++ b/src/interpreter/frame.c @@ -160,59 +160,6 @@ int Frame__delglobal(Frame* self, py_Name name) { } } -int Frame__getlocal(Frame* self, py_Name name) { - if(self->is_locals_special) { - switch(self->locals->type) { - case tp_locals: self = self->locals->_ptr; break; - case tp_dict: return py_dict_getitem(self->locals, py_name2ref(name)); - case tp_nil: return 0; - default: c11__unreachable(); - } - } - 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; -} - -bool Frame__setlocal(Frame* self, py_Name name, py_TValue* val) { - if(self->is_locals_special) { - switch(self->locals->type) { - case tp_locals: self = self->locals->_ptr; break; - case tp_dict: return py_dict_setitem(self->locals, py_name2ref(name), val); - case tp_nil: return false; - default: c11__unreachable(); - } - } - py_Ref slot = Frame__getlocal_noproxy(self, name); - if(slot == NULL) return false; // bad slot - *slot = *val; - return true; -} - -int Frame__dellocal(Frame* self, py_Name name) { - if(self->is_locals_special) { - switch(self->locals->type) { - case tp_locals: self = self->locals->_ptr; break; - case tp_dict: return py_dict_delitem(self->locals, py_name2ref(name)); - case tp_nil: return 0; - default: c11__unreachable(); - } - } - 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_special); int index = c11_smallmap_n2i__get(&self->co->varnames_inv, name, -1); diff --git a/src/public/modules.c b/src/public/modules.c index 37782c32..091b4fbc 100644 --- a/src/public/modules.c +++ b/src/public/modules.c @@ -523,7 +523,7 @@ void py_newlocals(py_Ref out) { py_assign(out, py_retval()); } -static void pk_push_locals_proxy() { +static void pk_push_special_locals() { Frame* frame = pk_current_vm->top_frame; if(!frame) { py_pushnil(); @@ -546,7 +546,7 @@ static bool _builtins_execdyn(const char* title, int argc, py_Ref argv, enum py_ switch(argc) { case 1: { py_newglobals(py_pushtmp()); - pk_push_locals_proxy(); + pk_push_special_locals(); break; } case 2: { @@ -558,7 +558,7 @@ static bool _builtins_execdyn(const char* title, int argc, py_Ref argv, enum py_ py_push(py_arg(1)); } // locals - pk_push_locals_proxy(); + pk_push_special_locals(); break; } case 3: { @@ -571,7 +571,7 @@ static bool _builtins_execdyn(const char* title, int argc, py_Ref argv, enum py_ } // locals if(py_isnone(py_arg(2))) { - pk_push_locals_proxy(); + pk_push_special_locals(); } else { if(!py_checktype(py_arg(2), tp_dict)) return false; py_push(py_arg(2)); @@ -581,22 +581,19 @@ static bool _builtins_execdyn(const char* title, int argc, py_Ref argv, enum py_ default: return TypeError("%s() takes at most 3 arguments", title); } - py_Ref tmp_code; if(py_isstr(argv)) { bool ok = py_compile(py_tostr(argv), "", mode, true); if(!ok) return false; - tmp_code = py_retval(); + py_push(py_retval()); } else if(py_istype(argv, tp_code)) { - tmp_code = argv; + py_push(argv); } else { return TypeError("%s() expected 'str' or 'code', got '%t'", title, argv->type); } - py_push(tmp_code); // keep it alive Frame* frame = pk_current_vm->top_frame; - // [globals, locals, code] - CodeObject* code = py_touserdata(tmp_code); + CodeObject* code = py_touserdata(py_peek(-1)); if(code->src->is_dynamic) { bool ok = pk_execdyn(code, frame ? frame->module : NULL, py_peek(-3), py_peek(-2)); py_shrink(3); From 4ead475cd1e50398a18a46155858930c04f6e67e Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Fri, 28 Feb 2025 16:57:26 +0800 Subject: [PATCH 15/18] fix bugs --- include/pocketpy/pocketpy.h | 2 +- src/compiler/compiler.c | 22 ++++++++++------------ src/public/exec.c | 13 +++++++++++-- src/public/modules.c | 7 ++----- tests/66_eval.py | 17 ++++++++++------- tests/67_locals_vs_globals.py | 2 -- 6 files changed, 34 insertions(+), 29 deletions(-) diff --git a/include/pocketpy/pocketpy.h b/include/pocketpy/pocketpy.h index 355d1405..30561501 100644 --- a/include/pocketpy/pocketpy.h +++ b/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; diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index 9512a308..064de234 100644 --- a/src/compiler/compiler.c +++ b/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); diff --git a/src/public/exec.c b/src/public/exec.c index 782dfe40..791a6d31 100644 --- a/src/public/exec.c +++ b/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); diff --git a/src/public/modules.c b/src/public/modules.c index 091b4fbc..fd90b3ad 100644 --- a/src/public/modules.c +++ b/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; diff --git a/tests/66_eval.py b/tests/66_eval.py index 54692830..4eaad666 100644 --- a/tests/66_eval.py +++ b/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}) \ No newline at end of file +exec(code, {'x': 42}) +assert res == [42, 42] +assert x == 33 + diff --git a/tests/67_locals_vs_globals.py b/tests/67_locals_vs_globals.py index 010a2a12..749837ea 100644 --- a/tests/67_locals_vs_globals.py +++ b/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() From bbf4d8efc642c70ef53ac0e84a2153ca7ffe6962 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Fri, 28 Feb 2025 16:58:03 +0800 Subject: [PATCH 16/18] Update 66_eval.py --- tests/66_eval.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/66_eval.py b/tests/66_eval.py index 4eaad666..2a51c22a 100644 --- a/tests/66_eval.py +++ b/tests/66_eval.py @@ -68,10 +68,9 @@ except NameError: # https://github.com/pocketpy/pocketpy/issues/339 res = [] - code = '\nres.append(x)\ndef f():\n res.append(x)\nf()\n' x = 33 -exec(code, {'x': 42}) +exec(code, {'x': 42, 'res': res}) assert res == [42, 42] assert x == 33 From f0b3cb102e9cb7b9ff1583ef094c0aa943ada5b1 Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Fri, 28 Feb 2025 16:58:38 +0800 Subject: [PATCH 17/18] restore workflows --- .github/workflows.zip | Bin 4423 -> 0 bytes .github/workflows/main.yml | 207 +++++++++++++++++++++++++++++++++ .github/workflows/pybind11.yml | 76 ++++++++++++ .github/workflows/website.yml | 42 +++++++ 4 files changed, 325 insertions(+) delete mode 100644 .github/workflows.zip create mode 100644 .github/workflows/main.yml create mode 100644 .github/workflows/pybind11.yml create mode 100644 .github/workflows/website.yml diff --git a/.github/workflows.zip b/.github/workflows.zip deleted file mode 100644 index 9daa7ca1b017d212e1e75d5029784cde9ab1e854..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4423 zcmb7|2{@Gd+sDT?Ci^zXHbz-bS+f<#p5|}OmL>>Ab%N43uXX)Y)+MX9QmM}1JH~BD%v$F0O0%gO~2;>0YJax87JrB zBIj{%Fw)X7F}IeHcKDg{my7{k&Hv2k;s|zecXW0n85vrUSgjIFBdF7PX~fz1sg|XL zfy+y>Y;*!T+EImu>8X;MA0ta+*r9z`47M`I+B7|@A}9Ykh9(n%IdR`RD=v5UN=uhf zbFN@++vCwB7J#V%J;=lOTB;qnJxYsv&C~500xd59!1>D%q;<_5%uzmm@bA4RlBbYF zJ}BpZOk?r-BjXNPuGoGf1#7A3uGBDUTuWp*p|xiKtbI70S}&@?`B{kYva1@M%)nn= zWo(j8n%bUp8%ekOGyMg`qr<5+-9EhIA^`+yMJ<8IW9kgS+3g5O8M;kSbLAj7*hb_0xpof;dAOhsk& z!VJ)xY=2$~N3)yIpZqp=m3d}TmayWnqp|yK+lRLka+>hv-Q~D`8m+XtVc_v|TY&n! zV*DYAmHzfaEn_r-^I}9L>NsDCK+4GB>jf|l3NYls9z1e1`Nt62&&y9*$mAS6{of+z zEfG#S4ER$T zQxHXFA(ZnN0at?0Zt$x#DkezkY>T#v8r$d}y|?(t_Nn!;-Z0F9Yl6btv@x40+bM1u zB+!U9nRxyWoi9qWjbZ$&1osPVS4q;TLH7%^Xib`mu^(c9R<@ae-R%k|#GL(4=SaXbpkB`8r zA79Y5lk0UdCJH=7yFA7M=HKuSVe73XSGZaVb=_?1U9aM02+;)SV(t9#;Nc8VcbT(o%_ZPXd9?V#?UP(eMedLJ;1xK7v*w zmmJ&B{aTRcWh*k*3`@F745YjQ4J6?90*+4=m}l#y0^rl%+`fI2$MsoQc=jEASyFwt(=ZccYzJm))pq-MHgdohlt;fA44Csypny&P=WF5+D{6@?`$ znW*(SGVBy$_E-wwRQk!%4h;YR{)MF=xU;_p3jPx%A-1J|L#fK#K52-I(o#)*p{xnw z@LKsD+1m+;g@qroo~DZhjn((YH^90YKZPqp;DOo8T5>WsRm(QU{r!q_+gVayWvyCU zo(=*>FrO$DEDSRI%w8UCQh0rcq}nl97qqD;{6*u1RFW}F#N&i0hw@nQdWIj$W+7=_ zL3qH}O)}v8fJK*(@T%69sY#E13R8x@L9C@tA?``N$xGs`9v>r8(-)gg$j(lqbdm<#U+vyR2sJ=44EzhB|mYX}RBFE$~vO4)S%ZmP)d(>xrqFKh4+BOG1K7E;ltc zyRBl9uV8{&-rSmvHahg0;8-0FGbiKsb4L|3pI?lH&d^ zyUfdT2p^&n+qVJ-EgT&+?xuyZ2OmgcV5oao7@@uj-;!kl8of}u2(y3@t~?LGhl*zm z=a-M^**iXqmw={q#rLZR>6adzX3u*o;zmy|pTfdM@Kcv*A@Ruhr#v}!HIc|3BYV<0 zbAhzEc}u8aCTqQQ**E=8@e3P)H1ICQAF=*+H48K6>~tvjviOTZahJ?=G!W0^K3qmH zPnlK&z0YkPREbkX5L6N9(yGI!8{VJPj^Trv3%*b-W6aIxd8Y7i>4xlQMMLX66OQhc zdTSSpK09Mw!%SDOSTIa(#=wws-B}eW<^fwZ70XR%_*^!tmU<++`&2anKcNzlHZb|J zbd=BoPh3y{j#lL1Zp*bTOsh<72*i%vNnim=^z?E$6e3Y+?fHNQI7aVUOU<*|x(rxZ z?s@81|LxtG0PWTB{tLB^+E5HMBKw_KGhPLP#ReQIJ7h!hg=r-%_xM?n zJ~*&)gWHa!s$1EItsJ`+<5ZM4oSS9 z=>p@~hisAsF!%|14vZYj%<a{fqdI6%Z#+#7THsQXnd7pvoBNE$) z$z~@)RzYCr+Ds|VrZevR4CgN#W=K4;QAe`_*Mgtx=T8KFj8$!iaXwZKXwJ{Xd%R3+ zV2nZByQX+c1>_=HqU9hIUWIB)hzbP1Y_F|71G-w()X_hpXVO3#*qS-NE;<}kYBgwS zvm^5%zW|&x;_?8a>k$3YFqz zDdpB?94`L>xW$wn9d4F`@phbcwmX!cWq-99%Sm?>&&{bfRN?RkoQPmQf0X)c_m{(e z{grNiOf?tOeEGq|@craC`-cr**4JL413!9XF*DA&iWUt(E0h^`1pDYaXI)cDCsXK` z;*K!Zcj=c&J{&u)o#RvUI>o!X)2GI}tLC&%^@L^V)5yY1XGi<_!t}pxV!vA5^G{lz ztLM6LN~f}{&i4M*sPwORi!koNNjXda*VN>GSD>kX4CZ9+!(o8(YMZ^^^&B@?xkBxWSd~>HN+S*}^m6-8o9Zq9=?R`gO znoL)C^6QC~-9$ z<&k&oHw^_B@|2QDa|I-LRfK*Nv6=OqTP%Zd^dJJ&DaS3z1!++jivIgiFO5KsBj^AC z%9efbW==5~g5=Horw`h%YY*Jle@oW>zYP(Ws|zS^EpRdWWO#ho^D7%>A-&w3?lLDS zeba+R#_6uulRN#}5>Zie(fqqJ{96tK0E=3btG(O#E`k3~pZIs)A5L+<4Wl^4AF&vD zhJ5*RUcccRO%3)cQ0&}(*Fmv!KLQs?N43B6gYCru=I);b_PYj(dHE5zNHq1nTiBb? zZ&;cAwJ1JhzpAA8kRMSOd6=vu_DcMYE7@OzqEGj06N*0l5p|JpGUR(TenYM9FF_GG z`&SG_> ~/.zshrc + source ~/.zshrc + + - name: Set up CMake + uses: jwlawson/actions-setup-cmake@v1.10 + + - name: Test + run: | + cd include/pybind11/tests + cmake -B build -DENABLE_TEST=ON + cmake --build build --config Release --parallel + ./build/PKBIND_TEST diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml new file mode 100644 index 00000000..a2add796 --- /dev/null +++ b/.github/workflows/website.yml @@ -0,0 +1,42 @@ +name: website + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +permissions: + contents: write + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + ################################################### + - uses: actions/setup-node@v3.1.1 + - name: Retype build + run: | + python scripts/gen_docs.py + cd docs + npm install retypeapp -g + retype build + ################################################### + - name: Setup emsdk + uses: mymindstorm/setup-emsdk@v12 + with: + version: latest + actions-cache-folder: 'emsdk-cache' + - name: Compile + run: | + bash build_web.sh + mv web docs/.retype/static + ################################################### + - uses: crazy-max/ghaction-github-pages@v3 + with: + target_branch: gh-pages + build_dir: docs/.retype + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + if: github.ref == 'refs/heads/main' From 7c1174f816045040ed0d293d2bebe4b76e1a1b4f Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Fri, 28 Feb 2025 17:04:30 +0800 Subject: [PATCH 18/18] some fix --- src/public/modules.c | 4 ++-- tests/67_locals_vs_globals.py | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/public/modules.c b/src/public/modules.c index fd90b3ad..9adcd670 100644 --- a/src/public/modules.c +++ b/src/public/modules.c @@ -508,14 +508,14 @@ void py_newglobals(py_Ref out) { void py_newlocals(py_Ref out) { Frame* frame = pk_current_vm->top_frame; if(!frame) { - py_newglobals(out); + py_newdict(out); return; } if(frame->is_locals_special) { switch(frame->locals->type) { case tp_locals: frame = frame->locals->_ptr; break; case tp_dict: *out = *frame->locals; return; - case tp_nil: py_newglobals(out); return; + case tp_nil: py_newdict(out); return; default: c11__unreachable(); } } diff --git a/tests/67_locals_vs_globals.py b/tests/67_locals_vs_globals.py index 749837ea..992f65a1 100644 --- a/tests/67_locals_vs_globals.py +++ b/tests/67_locals_vs_globals.py @@ -6,7 +6,6 @@ my_locals = {"b": 2} # With user-defined locals: exec(""" import sys -assert locals() != globals() assert "sys" in locals() assert "sys" not in globals() assert "a" not in locals() @@ -18,7 +17,6 @@ assert "b" not in globals() # print(b) assert (b == 2), b def main(): - assert locals() != globals() assert "sys" not in locals() # not the same `locals()` as the outer scope assert "sys" not in globals() # and `sys` isn't in `globals()`, same as before assert "b" not in locals() # again, not the same `locals()` as the outer scope @@ -32,7 +30,7 @@ assert "sys" not in globals() # With default locals: exec(""" import sys -assert "sys" in locals() +assert locals() == {} assert "sys" in globals() def main(): assert "sys" not in locals() # not the same locals as the outer scope