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/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/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. + 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 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 5a8b1709..510ae0f6 100644 --- a/include/pocketpy/interpreter/frame.h +++ b/include/pocketpy/interpreter/frame.h @@ -4,10 +4,9 @@ #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); +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 { @@ -18,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; @@ -31,28 +30,33 @@ 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; + bool is_locals_special; + 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_locals_special); 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/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..92f47cec 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 @@ -94,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`. @@ -128,11 +131,9 @@ 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(); /* 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/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/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/include/pocketpy/pocketpy.h b/include/pocketpy/pocketpy.h index d8a70302..30561501 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 *************/ @@ -176,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. @@ -204,6 +203,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`. @@ -535,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/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 deleted file mode 100644 index 8922a37b..00000000 --- a/src/common/strname.c +++ /dev/null @@ -1,64 +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 - -// TODO: use a more efficient data structure -static c11_smallmap_s2n _interned; -static c11_vector /*T=char* */ _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)); - -#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(char*, &_r_interned, i)); - } - 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) { - // TODO: PK_GLOBAL_SCOPE_LOCK() - 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'; - c11_vector__push(char*, &_r_interned, p); - 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(char*, &_r_interned, index - 1); -} - -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)}; -} diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index eeefad2d..064de234 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -102,17 +102,20 @@ 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 - } else { - // we cannot determine the scope when calling exec()/eval() - if(self->scope == NAME_GLOBAL_UNKNOWN) op = OP_LOAD_NAME; + 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); } @@ -124,8 +127,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 +1225,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 +1339,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 +1726,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); @@ -2185,9 +2191,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 1ff28e10..2b337614 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,8 +179,12 @@ 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) { + if(frame->is_locals_special) { + RuntimeError("cannot create closure from special locals"); + 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 @@ -191,6 +198,7 @@ FrameResult VM__run_top_frame(VM* self) { DISPATCH(); /*****************************************/ case OP_LOAD_FAST: { + 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); @@ -200,38 +208,45 @@ FrameResult VM__run_top_frame(VM* self) { DISPATCH(); } case OP_LOAD_NAME: { - assert(frame->is_dynamic); + assert(frame->is_locals_special); py_Name name = byte.arg; - py_TValue* tmp; - py_newstr(SP()++, py_name2str(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 { + 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(); } - } - // globals - if(py_getitem(&frame->p0[0], TOP())) { - py_assign(TOP(), py_retval()); - DISPATCH(); - } else { - if(py_matchexc(tp_KeyError)) { - py_clearexc(NULL); - } else { + 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 + 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) { - py_assign(TOP(), tmp); + PUSH(tmp); DISPATCH(); } NameError(name); @@ -239,16 +254,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 +276,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 +307,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); @@ -335,41 +354,41 @@ 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_NAME: { - assert(frame->is_dynamic); - py_Name name = byte.arg; - py_newstr(SP()++, py_name2str(name)); - // [value, name] - if(!py_isnone(&frame->p0[1])) { - // locals - if(py_setitem(&frame->p0[1], TOP(), SECOND())) { - STACK_SHRINK(2); - DISPATCH(); - } else { - if(py_matchexc(tp_KeyError)) { - py_clearexc(NULL); - NameError(name); - } - goto __ERROR; - } - } else { - // globals - if(py_setitem(&frame->p0[0], TOP(), SECOND())) { - STACK_SHRINK(2); - DISPATCH(); - } else { - if(py_matchexc(tp_KeyError)) { - py_clearexc(NULL); - NameError(name); - } - goto __ERROR; - } - } + case OP_STORE_FAST: { + assert(!frame->is_locals_special); + frame->locals[byte.arg] = POPX(); DISPATCH(); } + case OP_STORE_NAME: { + assert(frame->is_locals_special); + py_Name name = byte.arg; + 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(); + } + case tp_nil: { + // globals + if(!Frame__setglobal(frame, name, TOP())) goto __ERROR; + POP(); + DISPATCH(); + } + default: c11__unreachable(); + } + } case OP_STORE_GLOBAL: { - py_setdict(frame->module, byte.arg, TOP()); + if(!Frame__setglobal(frame, byte.arg, TOP())) goto __ERROR; POP(); DISPATCH(); } @@ -397,6 +416,7 @@ FrameResult VM__run_top_frame(VM* self) { goto __ERROR; } case OP_DELETE_FAST: { + 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); @@ -407,44 +427,42 @@ FrameResult VM__run_top_frame(VM* self) { DISPATCH(); } case OP_DELETE_NAME: { - assert(frame->is_dynamic); + assert(frame->is_locals_special); py_Name name = byte.arg; - py_newstr(SP()++, py_name2str(name)); - if(!py_isnone(&frame->p0[1])) { - // locals - if(py_delitem(&frame->p0[1], TOP())) { - POP(); - DISPATCH(); - } else { - if(py_matchexc(tp_KeyError)) { - py_clearexc(NULL); - NameError(name); + 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; } - } else { - // globals - if(py_delitem(&frame->p0[0], TOP())) { - POP(); - DISPATCH(); - } else { - if(py_matchexc(tp_KeyError)) { - py_clearexc(NULL); - NameError(name); - } + case tp_nil: { + // globals + int res = Frame__delglobal(frame, name); + if(res == 1) DISPATCH(); + if(res == 0) NameError(name); goto __ERROR; } + default: c11__unreachable(); } - DISPATCH(); } 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; + NameError(name); + goto __ERROR; } case OP_DELETE_ATTR: { @@ -493,11 +511,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(); @@ -860,7 +877,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 +886,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 +1015,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 +1182,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_locals_special ? frame->co->name->data : NULL); __ERROR_RE_RAISE: do { } while(0); @@ -1183,6 +1199,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..7ce44fab 100644 --- a/src/interpreter/frame.c +++ b/src/interpreter/frame.c @@ -11,19 +11,28 @@ 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) { 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 +48,25 @@ 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_locals_special) { + assert(module->type == tp_module); + assert(globals->type == tp_module || globals->type == tp_dict); + 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->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_locals_special = is_locals_special; + self->ip = -1; self->uw_list = NULL; return self; } @@ -75,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 = (self->locals + uw->offset); // unwind the stack + _s->sp = (self->p0 + uw->offset); // unwind the stack return c11__at(CodeBlock, &self->co->blocks, iblock)->end; } @@ -91,38 +106,71 @@ 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 - self->p0; } else { UnwindTarget* prev = self->uw_list; - self->uw_list = UnwindTarget__new(prev, iblock, sp - self->locals); + self->uw_list = UnwindTarget__new(prev, iblock, sp - self->p0); } } void Frame__gc_mark(Frame* self) { - pk__mark_value(self->module); + pk__mark_value(self->globals); + if(self->is_locals_special) 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); - return FastLocals__try_get_by_name(self->locals, self->co, name); +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_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_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); } \ No newline at end of file diff --git a/src/interpreter/generator.c b/src/interpreter/generator.c index a55b0a3f..bccfe13a 100644 --- a/src/interpreter/generator.c +++ b/src/interpreter/generator.c @@ -28,10 +28,11 @@ static bool generator__next__(int argc, py_Ref argv) { if(ud->state == 2) return StopIteration(); // reset frame->p0 + 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; - + // restore the context py_Ref backup = py_getslot(argv, 0); int length = py_list_len(backup); diff --git a/src/interpreter/name.c b/src/interpreter/name.c new file mode 100644 index 00000000..71966ee1 --- /dev/null +++ b/src/interpreter/name.c @@ -0,0 +1,75 @@ +#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 = 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); + 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 5ef35650..3c76f9fc 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); @@ -126,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()); @@ -257,7 +258,8 @@ 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); } 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); @@ -399,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 @@ -431,9 +437,8 @@ static bool co->name->data); } else { // add to **kwargs - bool ok = py_dict_setitem_by_str(&buffer[decl->starred_kwarg], - py_name2str(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,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, false)); return opcall ? RES_CALL : VM__run_top_frame(self); } else { // decl-based binding @@ -509,7 +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, &fn->module, p0, argv, true)); + 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 @@ -525,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, &fn->module, p0, argv, true); + 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; @@ -693,6 +698,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/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/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/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/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..791a6d31 100644 --- a/src/public/exec.c +++ b/src/public/exec.c @@ -3,16 +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" - -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,14 +54,43 @@ 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] - - Frame* frame = Frame__new(co, module, sp, sp, 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; - 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); + + // check globals + if(globals->type == tp_namedict) { + globals = py_getslot(globals, 0); + assert(globals->type == tp_module); + } else { + 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); + 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/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) { diff --git a/src/public/modules.c b/src/public/modules.c index 74d00564..9adcd670 100644 --- a/src/public/modules.c +++ b/src/public/modules.c @@ -498,23 +498,47 @@ 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) { + py_newdict(out); return; } - if(frame->has_function) { - pk_mappingproxy__locals(out, frame); + 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_newdict(out); return; + default: c11__unreachable(); + } + } + FastLocals__to_dict(frame->locals, frame->co); + py_assign(out, py_retval()); +} + +static void pk_push_special_locals() { + Frame* frame = pk_current_vm->top_frame; + if(!frame) { + py_pushnil(); + return; + } + if(frame->is_locals_special) { + py_push(frame->locals); } else { - py_newglobals(out); + 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; } } @@ -522,52 +546,64 @@ static bool _builtins_execdyn(const char* title, int argc, py_Ref argv, enum py_ switch(argc) { case 1: { py_newglobals(py_pushtmp()); - py_newlocals(py_pushtmp()); + pk_push_special_locals(); break; } case 2: { + // globals if(py_isnone(py_arg(1))) { py_newglobals(py_pushtmp()); } else { py_push(py_arg(1)); } - py_pushnone(); + // locals + py_pushnil(); break; } case 3: { + // globals if(py_isnone(py_arg(1))) { py_newglobals(py_pushtmp()); } else { py_push(py_arg(1)); } - py_push(py_arg(2)); + // locals + if(py_isnone(py_arg(2))) { + py_pushnil(); + } else { + py_push(py_arg(2)); + } break; } default: return TypeError("%s() takes at most 3 arguments", title); } - py_Ref code; if(py_isstr(argv)) { bool ok = py_compile(py_tostr(argv), "", mode, true); if(!ok) return false; - code = py_retval(); + py_push(py_retval()); } else if(py_istype(argv, tp_code)) { - code = argv; + py_push(argv); } else { return TypeError("%s() expected 'str' or 'code', got '%t'", title, argv->type); } - py_push(code); // keep it alive - - // [globals, locals, code] - 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"); - py_shrink(3); - } Frame* frame = pk_current_vm->top_frame; - return pk_exec(co, frame ? frame->module : NULL); + // [globals, locals, 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); + 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; + } } static bool builtins_exec(int argc, py_Ref argv) { @@ -736,6 +772,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,14 +816,14 @@ 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_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) self_arg = &frame->locals[0]; + if(frame->co->nlocals > 0) { self_arg = &frame->locals[0]; } } } } 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 b8da64e0..805958d7 100644 --- a/src/public/py_mappingproxy.c +++ b/src/public/py_mappingproxy.c @@ -1,9 +1,8 @@ #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) { py_newobject(out, tp_namedict, 1, 0); @@ -58,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_newstr(py_tuple_getitem(slot, 0), py_name2str(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_newstr(py_tuple_getitem(slot, 0), py_name2str(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_newstr(py_tuple_getitem(slot, 0), py_name2str(kv->key)); - py_assign(py_tuple_getitem(slot, 1), &kv->value); + p[0] = *py_name2ref(kv->key); + p[1] = kv->value; } return true; } @@ -103,67 +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->has_function && !frame->is_dynamic); - Frame** ud = py_newobject(out, tp_locals, 0, sizeof(Frame*)); - *ud = frame; -} - -static bool locals__getitem__(int argc, py_Ref argv) { - PY_CHECK_ARGC(2); - PY_CHECK_ARG_TYPE(1, tp_str); - Frame** ud = py_touserdata(argv); - py_Name name = py_namev(py_tosv(py_arg(1))); - py_Ref slot = Frame__f_locals_try_get(*ud, name); - if(!slot || py_isnil(slot)) return KeyError(py_arg(1)); - py_assign(py_retval(), slot); - return true; -} - -static bool locals__setitem__(int argc, py_Ref argv) { - PY_CHECK_ARGC(3); - PY_CHECK_ARG_TYPE(1, tp_str); - Frame** ud = py_touserdata(argv); - py_Name name = py_namev(py_tosv(py_arg(1))); - py_Ref slot = Frame__f_locals_try_get(*ud, name); - if(!slot) return KeyError(py_arg(1)); - py_assign(slot, py_arg(2)); - py_newnone(py_retval()); - return true; -} - -static bool locals__delitem__(int argc, py_Ref argv) { - PY_CHECK_ARGC(2); - PY_CHECK_ARG_TYPE(1, tp_str); - Frame** ud = py_touserdata(argv); - py_Name name = py_namev(py_tosv(py_arg(1))); - py_Ref res = Frame__f_locals_try_get(*ud, name); - if(!res || py_isnil(res)) return KeyError(py_arg(1)); - py_newnil(res); - py_newnone(py_retval()); - return true; -} - -static bool locals__contains__(int argc, py_Ref argv) { - PY_CHECK_ARGC(2); - PY_CHECK_ARG_TYPE(1, tp_str); - Frame** ud = py_touserdata(argv); - py_Name name = py_namev(py_tosv(py_arg(1))); - py_Ref slot = Frame__f_locals_try_get(*ud, name); - py_newbool(py_retval(), slot && !py_isnil(slot)); - return true; -} - -py_Type pk_locals__register() { - py_Type type = pk_newtype("locals", tp_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 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_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/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; 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..2a51c22a 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 @@ -67,3 +65,12 @@ try: exit(1) except NameError: pass + +# 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, 'res': res}) +assert res == [42, 42] +assert x == 33 + diff --git a/tests/67_locals_vs_globals.py b/tests/67_locals_vs_globals.py new file mode 100644 index 00000000..992f65a1 --- /dev/null +++ b/tests/67_locals_vs_globals.py @@ -0,0 +1,41 @@ +# https://gist.github.com/dean0x7d/df5ce97e4a1a05be4d56d1378726ff92 + +a = 1 +my_locals = {"b": 2} + +# With user-defined locals: +exec(""" +import sys +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 "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() == {} +assert "sys" in globals() +def main(): + 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