Merge branch 'pocketpy:main' into pkpy-v2

This commit is contained in:
PrimedErwin 2025-02-28 17:38:21 +08:00 committed by GitHub
commit 0aaff843a6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
40 changed files with 606 additions and 462 deletions

View File

@ -208,34 +208,32 @@ static bool libhv_HttpServer_ws_recv(int argc, py_Ref argv) {
py_newnone(py_retval()); py_newnone(py_retval());
return true; return true;
} }
py_newtuple(py_retval(), 2); py_Ref data = py_newtuple(py_retval(), 2);
switch(msg.type) { switch(msg.type) {
case WsMessageType::onopen: { case WsMessageType::onopen: {
// "onopen", (channel, request) // "onopen", (channel, request)
assert(msg.request != nullptr); assert(msg.request != nullptr);
py_newstr(py_tuple_getitem(py_retval(), 0), "onopen"); py_newstr(py_offset(data, 0), "onopen");
py_Ref args = py_tuple_getitem(py_retval(), 1); py_Ref p = py_newtuple(py_offset(data, 1), 2);
py_newtuple(args, 2); py_newint(py_offset(p, 0), (py_i64)msg.channel);
py_newint(py_tuple_getitem(args, 0), (py_i64)msg.channel); libhv_HttpRequest_create(py_offset(p, 1), msg.request);
libhv_HttpRequest_create(py_tuple_getitem(args, 1), msg.request);
break; break;
} }
case WsMessageType::onclose: { case WsMessageType::onclose: {
// "onclose", channel // "onclose", channel
py_newstr(py_tuple_getitem(py_retval(), 0), "onclose"); py_newstr(py_offset(data, 0), "onclose");
py_newint(py_tuple_getitem(py_retval(), 1), (py_i64)msg.channel); py_newint(py_offset(data, 1), (py_i64)msg.channel);
break; break;
} }
case WsMessageType::onmessage: { case WsMessageType::onmessage: {
// "onmessage", (channel, body) // "onmessage", (channel, body)
py_newstr(py_tuple_getitem(py_retval(), 0), "onmessage"); py_newstr(py_offset(data, 0), "onmessage");
py_Ref args = py_tuple_getitem(py_retval(), 1); py_Ref p = py_newtuple(py_offset(data, 1), 2);
py_newtuple(args, 2); py_newint(py_offset(p, 0), (py_i64)msg.channel);
py_newint(py_tuple_getitem(args, 0), (py_i64)msg.channel);
c11_sv sv; c11_sv sv;
sv.data = msg.body.data(); sv.data = msg.body.data();
sv.size = msg.body.size(); sv.size = msg.body.size();
py_newstrv(py_tuple_getitem(args, 1), sv); py_newstrv(py_offset(p, 1), sv);
break; break;
} }
} }

View File

@ -99,22 +99,21 @@ py_Type libhv_register_WebSocketClient(py_GlobalRef mod) {
py_newnone(py_retval()); py_newnone(py_retval());
return true; return true;
} else { } else {
py_newtuple(py_retval(), 2); py_Ref p = py_newtuple(py_retval(), 2);
switch(mq_msg.first) { switch(mq_msg.first) {
case WsMessageType::onopen: { case WsMessageType::onopen: {
py_newstr(py_tuple_getitem(py_retval(), 0), "onopen"); py_newstr(py_offset(p, 0), "onopen");
py_newnone(py_tuple_getitem(py_retval(), 1)); py_newnone(py_offset(p, 1));
break; break;
} }
case WsMessageType::onclose: { case WsMessageType::onclose: {
py_newstr(py_tuple_getitem(py_retval(), 0), "onclose"); py_newstr(py_offset(p, 0), "onclose");
py_newnone(py_tuple_getitem(py_retval(), 1)); py_newnone(py_offset(p, 1));
break; break;
} }
case WsMessageType::onmessage: { case WsMessageType::onmessage: {
py_newstr(py_tuple_getitem(py_retval(), 0), "onmessage"); py_newstr(py_offset(p, 0), "onmessage");
py_newstrv(py_tuple_getitem(py_retval(), 1), py_newstrv(py_offset(p, 1), {mq_msg.second.data(), (int)mq_msg.second.size()});
{mq_msg.second.data(), (int)mq_msg.second.size()});
break; break;
} }
} }

View File

@ -11,7 +11,7 @@ ROOT = 'include/pocketpy'
PUBLIC_HEADERS = ['config.h', 'export.h', 'linalg.h', 'pocketpy.h'] PUBLIC_HEADERS = ['config.h', 'export.h', 'linalg.h', 'pocketpy.h']
COPYRIGHT = '''/* COPYRIGHT = '''/*
* Copyright (c) 2024 blueloveTH * Copyright (c) 2025 blueloveTH
* Distributed Under The MIT License * Distributed Under The MIT License
* https://github.com/pocketpy/pocketpy * https://github.com/pocketpy/pocketpy
*/ */

View File

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

View File

@ -11,7 +11,7 @@ pkpy is licensed under the [MIT License](http://opensource.org/licenses/MIT).
``` ```
MIT License MIT License
Copyright (c) 2024 blueloveTH Copyright (c) 2025 blueloveTH
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -1,7 +0,0 @@
#pragma once
#include <stdint.h>
#include "pocketpy/common/str.h"
void py_Name__initialize();
void py_Name__finalize();

View File

@ -4,10 +4,9 @@
#include "pocketpy/objects/codeobject.h" #include "pocketpy/objects/codeobject.h"
#include "pocketpy/objects/namedict.h" #include "pocketpy/objects/namedict.h"
#include "pocketpy/objects/object.h" #include "pocketpy/objects/object.h"
#include "pocketpy/common/strname.h"
#include "pocketpy/pocketpy.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); NameDict* FastLocals__to_namedict(py_TValue* locals, const CodeObject* co);
typedef struct ValueStack { typedef struct ValueStack {
@ -18,7 +17,7 @@ typedef struct ValueStack {
} ValueStack; } ValueStack;
void ValueStack__ctor(ValueStack* self); void ValueStack__ctor(ValueStack* self);
void ValueStack__clear(ValueStack* self); void ValueStack__dtor(ValueStack* self);
typedef struct UnwindTarget { typedef struct UnwindTarget {
struct UnwindTarget* next; struct UnwindTarget* next;
@ -31,28 +30,33 @@ void UnwindTarget__delete(UnwindTarget* self);
typedef struct Frame { typedef struct Frame {
struct Frame* f_back; struct Frame* f_back;
const Bytecode* ip;
const CodeObject* co; const CodeObject* co;
py_GlobalRef module;
py_StackRef p0; // unwinding base py_StackRef p0; // unwinding base
py_StackRef locals; // locals base py_GlobalRef module;
bool has_function; // is p0 a function? py_Ref globals; // a module object or a dict object
bool is_dynamic; // is dynamic frame? py_Ref locals;
bool is_locals_special;
int ip;
UnwindTarget* uw_list; UnwindTarget* uw_list;
} Frame; } Frame;
Frame* Frame__new(const CodeObject* co, Frame* Frame__new(const CodeObject* co,
py_GlobalRef module,
py_StackRef p0, py_StackRef p0,
py_StackRef locals, py_GlobalRef module,
bool has_function); py_Ref globals,
py_Ref locals,
bool is_locals_special);
void Frame__delete(Frame* self); void Frame__delete(Frame* self);
int Frame__ip(const Frame* self);
int Frame__lineno(const Frame* self); int Frame__lineno(const Frame* self);
int Frame__iblock(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*); int Frame__prepare_jump_exception_handler(Frame* self, ValueStack*);

View File

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

View File

@ -7,6 +7,7 @@
#include "pocketpy/interpreter/frame.h" #include "pocketpy/interpreter/frame.h"
#include "pocketpy/interpreter/modules.h" #include "pocketpy/interpreter/modules.h"
#include "pocketpy/interpreter/typeinfo.h" #include "pocketpy/interpreter/typeinfo.h"
#include "pocketpy/interpreter/name.h"
// TODO: // TODO:
// 1. __eq__ and __ne__ fallbacks // 1. __eq__ and __ne__ fallbacks
@ -41,6 +42,7 @@ typedef struct VM {
py_StackRef __curr_function; py_StackRef __curr_function;
py_TValue __vectorcall_buffer[PK_MAX_CO_VARNAMES]; py_TValue __vectorcall_buffer[PK_MAX_CO_VARNAMES];
InternedNames names;
FixedMemoryPool pool_frame; FixedMemoryPool pool_frame;
ManagedHeap heap; ManagedHeap heap;
ValueStack stack; // put `stack` at the end for better cache locality 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_callmagic(py_Name name, int argc, py_Ref argv);
bool pk_exec(CodeObject* co, py_Ref module); 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. /// Assumes [a, b] are on the stack, performs a binary op.
/// The result is stored in `self->last_retval`. /// 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_classmethod__register();
py_Type pk_generator__register(); py_Type pk_generator__register();
py_Type pk_namedict__register(); py_Type pk_namedict__register();
py_Type pk_locals__register();
py_Type pk_code__register(); py_Type pk_code__register();
py_TValue pk_builtins__register(); py_TValue pk_builtins__register();
/* mappingproxy */ /* mappingproxy */
void pk_mappingproxy__namedict(py_Ref out, py_Ref object); void pk_mappingproxy__namedict(py_Ref out, py_Ref object);
void pk_mappingproxy__locals(py_Ref out, Frame* frame);

View File

@ -19,5 +19,6 @@ typedef struct py_TValue {
PyObject* _obj; PyObject* _obj;
c11_vec2 _vec2; c11_vec2 _vec2;
c11_vec2i _vec2i; c11_vec2i _vec2i;
void* _ptr;
}; };
} py_TValue; } py_TValue;

View File

@ -23,7 +23,6 @@ typedef enum FuncType {
typedef enum NameScope { typedef enum NameScope {
NAME_LOCAL, NAME_LOCAL,
NAME_GLOBAL, NAME_GLOBAL,
NAME_GLOBAL_UNKNOWN,
} NameScope; } NameScope;
typedef enum CodeBlockType { typedef enum CodeBlockType {
@ -128,11 +127,12 @@ void FuncDecl__gc_mark(const FuncDecl* self);
// runtime function // runtime function
typedef struct Function { typedef struct Function {
FuncDecl_ decl; FuncDecl_ decl;
py_TValue module; // weak ref py_GlobalRef module; // maybe NULL, weak ref
PyObject* clazz; // weak ref py_Ref globals; // maybe NULL, strong ref
NameDict* closure; // strong ref NameDict* closure; // maybe NULL, strong ref
py_CFunction cfunc; // wrapped C function PyObject* clazz; // weak ref; for super()
py_CFunction cfunc; // wrapped C function; for decl-based binding
} Function; } 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); void Function__dtor(Function* self);

View File

@ -1,10 +1,6 @@
#pragma once #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/sourcedata.h"
#include "pocketpy/objects/object.h"
#include "pocketpy/pocketpy.h" #include "pocketpy/pocketpy.h"
typedef struct{ typedef struct{

View File

@ -135,7 +135,6 @@ PK_API bool py_compile(const char* source,
/// Python equivalent to `globals()`. /// Python equivalent to `globals()`.
PK_API void py_newglobals(py_OutRef); PK_API void py_newglobals(py_OutRef);
/// Python equivalent to `locals()`. /// Python equivalent to `locals()`.
/// @return a temporary object, which expires on the associated function return.
PK_API void py_newlocals(py_OutRef); PK_API void py_newlocals(py_OutRef);
/************* Values Creation *************/ /************* Values Creation *************/
@ -176,7 +175,7 @@ PK_API void py_newellipsis(py_OutRef);
PK_API void py_newnil(py_OutRef); PK_API void py_newnil(py_OutRef);
/// Create a `tuple` with `n` UNINITIALIZED elements. /// Create a `tuple` with `n` UNINITIALIZED elements.
/// You should initialize all elements before using it. /// 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`. /// Create an empty `list`.
PK_API void py_newlist(py_OutRef); PK_API void py_newlist(py_OutRef);
/// Create a `list` with `n` UNINITIALIZED elements. /// 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*); PK_API py_Name py_name(const char*);
/// Convert a name to a null-terminated string. /// Convert a name to a null-terminated string.
PK_API const char* py_name2str(py_Name); 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. /// Convert a `c11_sv` to a name.
PK_API py_Name py_namev(c11_sv); PK_API py_Name py_namev(c11_sv);
/// Convert a name to a `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) \ #define AttributeError(self, n) \
py_exception(tp_AttributeError, "'%t' object has no attribute '%n'", (self)->type, (n)) py_exception(tp_AttributeError, "'%t' object has no attribute '%n'", (self)->type, (n))
#define UnboundLocalError(n) \ #define UnboundLocalError(n) \
py_exception(tp_UnboundLocalError, "local variable '%n' referenced before assignment", (n)) py_exception(tp_UnboundLocalError, "cannot access local variable '%n' where it is not associated with a value", (n))
PK_API bool StopIteration() PY_RAISE; PK_API bool StopIteration() PY_RAISE;
PK_API bool KeyError(py_Ref key) PY_RAISE; PK_API bool KeyError(py_Ref key) PY_RAISE;

View File

@ -3,5 +3,6 @@
"reportMissingModuleSource": "none", "reportMissingModuleSource": "none",
"reportArgumentType": "none", "reportArgumentType": "none",
"reportWildcardImportFromLibrary": "none", "reportWildcardImportFromLibrary": "none",
"reportRedeclaration": "none",
"pythonVersion": "3.12" "pythonVersion": "3.12"
} }

View File

@ -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 <stdio.h>
// 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)};
}

View File

@ -102,17 +102,20 @@ void NameExpr__emit_(Expr* self_, Ctx* ctx) {
NameExpr* self = (NameExpr*)self_; NameExpr* self = (NameExpr*)self_;
int index = c11_smallmap_n2i__get(&ctx->co->varnames_inv, self->name, -1); int index = c11_smallmap_n2i__get(&ctx->co->varnames_inv, self->name, -1);
if(self->scope == NAME_LOCAL && index >= 0) { if(self->scope == NAME_LOCAL && index >= 0) {
// we know this is a local variable
Ctx__emit_(ctx, OP_LOAD_FAST, index, self->line); Ctx__emit_(ctx, OP_LOAD_FAST, index, self->line);
} else { } else {
Opcode op = ctx->level <= 1 ? OP_LOAD_GLOBAL : OP_LOAD_NONLOCAL; Opcode op = ctx->level <= 1 ? OP_LOAD_GLOBAL : OP_LOAD_NONLOCAL;
if(ctx->is_compiling_class && self->scope == NAME_GLOBAL) { if(self->scope == NAME_GLOBAL) {
// if we are compiling a class, we should use OP_LOAD_ATTR_GLOBAL instead of if(ctx->co->src->is_dynamic) {
// OP_LOAD_GLOBAL this supports @property.setter op = OP_LOAD_NAME;
op = OP_LOAD_CLASS_GLOBAL;
// exec()/eval() won't work with OP_LOAD_ATTR_GLOBAL in class body
} else { } else {
// we cannot determine the scope when calling exec()/eval() if(ctx->is_compiling_class) {
if(self->scope == NAME_GLOBAL_UNKNOWN) op = OP_LOAD_NAME; // 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); Ctx__emit_(ctx, op, self->name, self->line);
} }
@ -124,8 +127,11 @@ bool NameExpr__emit_del(Expr* self_, Ctx* ctx) {
case NAME_LOCAL: case NAME_LOCAL:
Ctx__emit_(ctx, OP_DELETE_FAST, Ctx__add_varname(ctx, self->name), self->line); Ctx__emit_(ctx, OP_DELETE_FAST, Ctx__add_varname(ctx, self->name), self->line);
break; break;
case NAME_GLOBAL: Ctx__emit_(ctx, OP_DELETE_GLOBAL, self->name, self->line); break; case NAME_GLOBAL: {
case NAME_GLOBAL_UNKNOWN: Ctx__emit_(ctx, OP_DELETE_NAME, self->name, self->line); break; 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(); default: c11__unreachable();
} }
return true; 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) { static void Ctx__emit_store_name(Ctx* self, NameScope scope, py_Name name, int line) {
switch(scope) { switch(scope) {
case NAME_LOCAL: Ctx__emit_(self, OP_STORE_FAST, Ctx__add_varname(self, name), line); break; 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: {
case NAME_GLOBAL_UNKNOWN: Ctx__emit_(self, OP_STORE_NAME, name, line); break; Opcode op = self->co->src->is_dynamic ? OP_STORE_NAME : OP_STORE_GLOBAL;
Ctx__emit_(self, op, name, line);
} break;
default: c11__unreachable(); default: c11__unreachable();
} }
} }
@ -1331,9 +1339,7 @@ static void Compiler__dtor(Compiler* self) {
if((err = B)) return err if((err = B)) return err
static NameScope name_scope(Compiler* self) { static NameScope name_scope(Compiler* self) {
NameScope s = self->contexts.length > 1 ? NAME_LOCAL : NAME_GLOBAL; return self->contexts.length > 1 ? NAME_LOCAL : NAME_GLOBAL;
if(self->src->is_dynamic && s == NAME_GLOBAL) s = NAME_GLOBAL_UNKNOWN;
return s;
} }
Error* SyntaxError(Compiler* self, const char* fmt, ...) { Error* SyntaxError(Compiler* self, const char* fmt, ...) {
@ -1720,7 +1726,7 @@ static Error* exprName(Compiler* self) {
NameScope scope = name_scope(self); NameScope scope = name_scope(self);
// promote this name to global scope if needed // promote this name to global scope if needed
if(c11_smallmap_n2i__contains(&ctx()->global_names, name)) { 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; scope = NAME_GLOBAL;
} }
NameExpr* e = NameExpr__new(prev()->line, name, scope); 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; if(curr()->type == TK_RPAREN) break;
} }
consume(TK_RPAREN); consume(TK_RPAREN);
py_newtuple(out, count); py_Ref p = py_newtuple(out, count);
for(int i = 0; i < count; i++) { for(int i = 0; i < count; i++) {
py_tuple_setitem(out, i, &cpnts[i]); p[i] = cpnts[i];
} }
return NULL; return NULL;
} }

View File

@ -33,7 +33,7 @@ static bool stack_format_object(VM* self, c11_sv spec);
} while(0) } while(0)
#define DISPATCH_JUMP_ABSOLUTE(__target) \ #define DISPATCH_JUMP_ABSOLUTE(__target) \
do { \ do { \
frame->ip = c11__at(Bytecode, &frame->co->codes, __target); \ frame->ip = __target; \
goto __NEXT_STEP; \ goto __NEXT_STEP; \
} while(0) } 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) { FrameResult VM__run_top_frame(VM* self) {
Frame* frame = self->top_frame; Frame* frame = self->top_frame;
Bytecode* codes;
const Frame* base_frame = frame; const Frame* base_frame = frame;
while(true) { while(true) {
Bytecode byte; Bytecode byte;
__NEXT_FRAME: __NEXT_FRAME:
codes = frame->co->codes.data;
frame->ip++; frame->ip++;
__NEXT_STEP: __NEXT_STEP:
byte = *frame->ip; byte = codes[frame->ip];
#ifndef NDEBUG #ifndef NDEBUG
pk_print_stack(self, frame, byte); pk_print_stack(self, frame, byte);
@ -176,8 +179,12 @@ FrameResult VM__run_top_frame(VM* self) {
CHECK_STACK_OVERFLOW(); CHECK_STACK_OVERFLOW();
FuncDecl_ decl = c11__getitem(FuncDecl_, &frame->co->func_decls, byte.arg); FuncDecl_ decl = c11__getitem(FuncDecl_, &frame->co->func_decls, byte.arg);
Function* ud = py_newobject(SP(), tp_function, 0, sizeof(Function)); 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(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); ud->closure = FastLocals__to_namedict(frame->locals, frame->co);
py_Name name = py_name(decl->code.name->data); py_Name name = py_name(decl->code.name->data);
// capture itself to allow recursion // capture itself to allow recursion
@ -191,6 +198,7 @@ FrameResult VM__run_top_frame(VM* self) {
DISPATCH(); DISPATCH();
/*****************************************/ /*****************************************/
case OP_LOAD_FAST: { case OP_LOAD_FAST: {
assert(!frame->is_locals_special);
PUSH(&frame->locals[byte.arg]); PUSH(&frame->locals[byte.arg]);
if(py_isnil(TOP())) { if(py_isnil(TOP())) {
py_Name name = c11__getitem(uint16_t, &frame->co->varnames, byte.arg); py_Name name = c11__getitem(uint16_t, &frame->co->varnames, byte.arg);
@ -200,38 +208,45 @@ FrameResult VM__run_top_frame(VM* self) {
DISPATCH(); DISPATCH();
} }
case OP_LOAD_NAME: { case OP_LOAD_NAME: {
assert(frame->is_dynamic); assert(frame->is_locals_special);
py_Name name = byte.arg; py_Name name = byte.arg;
py_TValue* tmp;
py_newstr(SP()++, py_name2str(name));
// locals // locals
if(!py_isnone(&frame->p0[1])) { switch(frame->locals->type) {
if(py_getitem(&frame->p0[1], TOP())) { case tp_locals: {
py_assign(TOP(), py_retval()); Frame* noproxy = frame->locals->_ptr;
DISPATCH(); py_Ref slot = Frame__getlocal_noproxy(noproxy, name);
} else { if(slot == NULL) break;
if(py_matchexc(tp_KeyError)) { if(py_isnil(slot)) {
py_clearexc(NULL); UnboundLocalError(name);
} else {
goto __ERROR; 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 // globals
if(py_getitem(&frame->p0[0], TOP())) { int res = Frame__getglobal(frame, name);
py_assign(TOP(), py_retval()); if(res == 1) {
PUSH(&self->last_retval);
DISPATCH(); DISPATCH();
} else {
if(py_matchexc(tp_KeyError)) {
py_clearexc(NULL);
} else {
goto __ERROR;
}
} }
if(res == -1) goto __ERROR;
// builtins // builtins
tmp = py_getdict(&self->builtins, name); py_Ref tmp = py_getdict(&self->builtins, name);
if(tmp != NULL) { if(tmp != NULL) {
py_assign(TOP(), tmp); PUSH(tmp);
DISPATCH(); DISPATCH();
} }
NameError(name); NameError(name);
@ -239,16 +254,18 @@ FrameResult VM__run_top_frame(VM* self) {
} }
case OP_LOAD_NONLOCAL: { case OP_LOAD_NONLOCAL: {
py_Name name = byte.arg; 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) { if(tmp != NULL) {
PUSH(tmp); PUSH(tmp);
DISPATCH(); DISPATCH();
} }
tmp = py_getdict(frame->module, name); int res = Frame__getglobal(frame, name);
if(tmp != NULL) { if(res == 1) {
PUSH(tmp); PUSH(&self->last_retval);
DISPATCH(); DISPATCH();
} }
if(res == -1) goto __ERROR;
tmp = py_getdict(&self->builtins, name); tmp = py_getdict(&self->builtins, name);
if(tmp != NULL) { if(tmp != NULL) {
PUSH(tmp); PUSH(tmp);
@ -259,12 +276,13 @@ FrameResult VM__run_top_frame(VM* self) {
} }
case OP_LOAD_GLOBAL: { case OP_LOAD_GLOBAL: {
py_Name name = byte.arg; py_Name name = byte.arg;
py_Ref tmp = py_getdict(frame->module, name); int res = Frame__getglobal(frame, name);
if(tmp != NULL) { if(res == 1) {
PUSH(tmp); PUSH(&self->last_retval);
DISPATCH(); DISPATCH();
} }
tmp = py_getdict(&self->builtins, name); if(res == -1) goto __ERROR;
py_Ref tmp = py_getdict(&self->builtins, name);
if(tmp != NULL) { if(tmp != NULL) {
PUSH(tmp); PUSH(tmp);
DISPATCH(); DISPATCH();
@ -289,11 +307,12 @@ FrameResult VM__run_top_frame(VM* self) {
DISPATCH(); DISPATCH();
} }
// load global if attribute not found // load global if attribute not found
tmp = py_getdict(frame->module, name); int res = Frame__getglobal(frame, name);
if(tmp) { if(res == 1) {
PUSH(tmp); PUSH(&self->last_retval);
DISPATCH(); DISPATCH();
} }
if(res == -1) goto __ERROR;
tmp = py_getdict(&self->builtins, name); tmp = py_getdict(&self->builtins, name);
if(tmp) { if(tmp) {
PUSH(tmp); PUSH(tmp);
@ -335,41 +354,41 @@ FrameResult VM__run_top_frame(VM* self) {
TypeError("'%t' object is not subscriptable", SECOND()->type); TypeError("'%t' object is not subscriptable", SECOND()->type);
goto __ERROR; goto __ERROR;
} }
case OP_STORE_FAST: frame->locals[byte.arg] = POPX(); DISPATCH(); case OP_STORE_FAST: {
assert(!frame->is_locals_special);
frame->locals[byte.arg] = POPX();
DISPATCH();
}
case OP_STORE_NAME: { case OP_STORE_NAME: {
assert(frame->is_dynamic); assert(frame->is_locals_special);
py_Name name = byte.arg; py_Name name = byte.arg;
py_newstr(SP()++, py_name2str(name)); switch(frame->locals->type) {
// [value, name] case tp_locals: {
if(!py_isnone(&frame->p0[1])) { Frame* noproxy = frame->locals->_ptr;
// locals py_Ref slot = Frame__getlocal_noproxy(noproxy, name);
if(py_setitem(&frame->p0[1], TOP(), SECOND())) { if(slot == NULL) {
STACK_SHRINK(2); UnboundLocalError(name);
DISPATCH();
} else {
if(py_matchexc(tp_KeyError)) {
py_clearexc(NULL);
NameError(name);
}
goto __ERROR; goto __ERROR;
} }
} else { *slot = POPX();
DISPATCH();
}
case tp_dict: {
if(!py_dict_setitem(frame->locals, py_name2ref(name), TOP())) goto __ERROR;
POP();
DISPATCH();
}
case tp_nil: {
// globals // globals
if(py_setitem(&frame->p0[0], TOP(), SECOND())) { if(!Frame__setglobal(frame, name, TOP())) goto __ERROR;
STACK_SHRINK(2); POP();
DISPATCH(); DISPATCH();
} else {
if(py_matchexc(tp_KeyError)) {
py_clearexc(NULL);
NameError(name);
} }
goto __ERROR; default: c11__unreachable();
} }
} }
DISPATCH();
}
case OP_STORE_GLOBAL: { case OP_STORE_GLOBAL: {
py_setdict(frame->module, byte.arg, TOP()); if(!Frame__setglobal(frame, byte.arg, TOP())) goto __ERROR;
POP(); POP();
DISPATCH(); DISPATCH();
} }
@ -397,6 +416,7 @@ FrameResult VM__run_top_frame(VM* self) {
goto __ERROR; goto __ERROR;
} }
case OP_DELETE_FAST: { case OP_DELETE_FAST: {
assert(!frame->is_locals_special);
py_Ref tmp = &frame->locals[byte.arg]; py_Ref tmp = &frame->locals[byte.arg];
if(py_isnil(tmp)) { if(py_isnil(tmp)) {
py_Name name = c11__getitem(py_Name, &frame->co->varnames, byte.arg); py_Name name = c11__getitem(py_Name, &frame->co->varnames, byte.arg);
@ -407,45 +427,43 @@ FrameResult VM__run_top_frame(VM* self) {
DISPATCH(); DISPATCH();
} }
case OP_DELETE_NAME: { case OP_DELETE_NAME: {
assert(frame->is_dynamic); assert(frame->is_locals_special);
py_Name name = byte.arg; py_Name name = byte.arg;
py_newstr(SP()++, py_name2str(name)); switch(frame->locals->type) {
if(!py_isnone(&frame->p0[1])) { case tp_locals: {
// locals Frame* noproxy = frame->locals->_ptr;
if(py_delitem(&frame->p0[1], TOP())) { py_Ref slot = Frame__getlocal_noproxy(noproxy, name);
POP(); if(slot == NULL || py_isnil(slot)) {
DISPATCH(); UnboundLocalError(name);
} else {
if(py_matchexc(tp_KeyError)) {
py_clearexc(NULL);
NameError(name);
}
goto __ERROR; goto __ERROR;
} }
} else { 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 // globals
if(py_delitem(&frame->p0[0], TOP())) { int res = Frame__delglobal(frame, name);
POP(); if(res == 1) DISPATCH();
DISPATCH(); if(res == 0) NameError(name);
} else {
if(py_matchexc(tp_KeyError)) {
py_clearexc(NULL);
NameError(name);
}
goto __ERROR; goto __ERROR;
} }
default: c11__unreachable();
} }
DISPATCH();
} }
case OP_DELETE_GLOBAL: { case OP_DELETE_GLOBAL: {
py_Name name = byte.arg; py_Name name = byte.arg;
bool ok = py_deldict(frame->module, name); int res = Frame__delglobal(frame, name);
if(!ok) { if(res == 1) DISPATCH();
if(res == -1) goto __ERROR;
NameError(name); NameError(name);
goto __ERROR; goto __ERROR;
} }
DISPATCH();
}
case OP_DELETE_ATTR: { case OP_DELETE_ATTR: {
if(!py_delattr(TOP(), byte.arg)) goto __ERROR; if(!py_delattr(TOP(), byte.arg)) goto __ERROR;
@ -493,11 +511,10 @@ FrameResult VM__run_top_frame(VM* self) {
} }
case OP_BUILD_TUPLE: { case OP_BUILD_TUPLE: {
py_TValue tmp; py_TValue tmp;
py_newtuple(&tmp, byte.arg); py_Ref p = py_newtuple(&tmp, byte.arg);
py_TValue* begin = SP() - byte.arg; py_TValue* begin = SP() - byte.arg;
for(int i = 0; i < byte.arg; i++) { for(int i = 0; i < byte.arg; i++)
py_tuple_setitem(&tmp, i, begin + i); p[i] = begin[i];
}
SP() = begin; SP() = begin;
PUSH(&tmp); PUSH(&tmp);
DISPATCH(); DISPATCH();
@ -860,7 +877,7 @@ FrameResult VM__run_top_frame(VM* self) {
ImportError("cannot import name '%n'", name); ImportError("cannot import name '%n'", name);
goto __ERROR; goto __ERROR;
} else { } else {
py_setdict(frame->module, name, value); if(!Frame__setglobal(frame, name, value)) goto __ERROR;
} }
} }
} else { } else {
@ -869,7 +886,7 @@ FrameResult VM__run_top_frame(VM* self) {
if(!kv->key) continue; if(!kv->key) continue;
c11_sv name = py_name2sv(kv->key); c11_sv name = py_name2sv(kv->key);
if(name.size == 0 || name.data[0] == '_') continue; 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(); POP();
@ -998,8 +1015,7 @@ FrameResult VM__run_top_frame(VM* self) {
case OP_END_CLASS: { case OP_END_CLASS: {
// [cls or decorated] // [cls or decorated]
py_Name name = byte.arg; py_Name name = byte.arg;
// set into f_globals if(!Frame__setglobal(frame, name, TOP())) goto __ERROR;
py_setdict(frame->module, name, TOP());
if(py_istype(TOP(), tp_type)) { if(py_istype(TOP(), tp_type)) {
// call on_end_subclass // call on_end_subclass
@ -1166,7 +1182,7 @@ FrameResult VM__run_top_frame(VM* self) {
py_BaseException__stpush(&self->curr_exception, py_BaseException__stpush(&self->curr_exception,
frame->co->src, frame->co->src,
Frame__lineno(frame), Frame__lineno(frame),
frame->has_function ? frame->co->name->data : NULL); !frame->is_locals_special ? frame->co->name->data : NULL);
__ERROR_RE_RAISE: __ERROR_RE_RAISE:
do { do {
} while(0); } while(0);
@ -1183,6 +1199,7 @@ FrameResult VM__run_top_frame(VM* self) {
return RES_ERROR; return RES_ERROR;
} }
frame = self->top_frame; frame = self->top_frame;
codes = frame->co->codes.data;
goto __ERROR; goto __ERROR;
} }
} }

View File

@ -11,19 +11,28 @@ void ValueStack__ctor(ValueStack* self) {
self->end = self->begin + PK_VM_STACK_SIZE; 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) { void FastLocals__to_dict(py_TValue* locals, const CodeObject* co) {
int index = c11_smallmap_n2i__get(&co->varnames_inv, name, -1); py_StackRef dict = py_pushtmp();
if(index == -1) return NULL; py_newdict(dict);
return &locals[index]; 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* FastLocals__to_namedict(py_TValue* locals, const CodeObject* co) {
NameDict* dict = NameDict__new(); NameDict* dict = NameDict__new();
c11__foreach(c11_smallmap_n2i_KV, &co->varnames_inv, entry) { c11__foreach(c11_smallmap_n2i_KV, &co->varnames_inv, entry) {
py_TValue value = locals[entry->value]; 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; return dict;
} }
@ -39,19 +48,25 @@ UnwindTarget* UnwindTarget__new(UnwindTarget* next, int iblock, int offset) {
void UnwindTarget__delete(UnwindTarget* self) { PK_FREE(self); } void UnwindTarget__delete(UnwindTarget* self) { PK_FREE(self); }
Frame* Frame__new(const CodeObject* co, Frame* Frame__new(const CodeObject* co,
py_GlobalRef module,
py_StackRef p0, py_StackRef p0,
py_StackRef locals, py_GlobalRef module,
bool has_function) { 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); Frame* self = FixedMemoryPool__alloc(&pk_current_vm->pool_frame);
self->f_back = NULL; self->f_back = NULL;
self->ip = (Bytecode*)co->codes.data - 1;
self->co = co; self->co = co;
self->module = module;
self->p0 = p0; self->p0 = p0;
self->module = module;
self->globals = globals;
self->locals = locals; self->locals = locals;
self->has_function = has_function; self->is_locals_special = is_locals_special;
self->is_dynamic = co->src->is_dynamic; self->ip = -1;
self->uw_list = NULL; self->uw_list = NULL;
return self; return self;
} }
@ -75,7 +90,7 @@ int Frame__prepare_jump_exception_handler(Frame* self, ValueStack* _s) {
} }
if(iblock < 0) return -1; if(iblock < 0) return -1;
UnwindTarget* uw = Frame__find_unwind_target(self, iblock); 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; 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); int iblock = Frame__iblock(self);
UnwindTarget* existing = Frame__find_unwind_target(self, iblock); UnwindTarget* existing = Frame__find_unwind_target(self, iblock);
if(existing) { if(existing) {
existing->offset = sp - self->locals; existing->offset = sp - self->p0;
} else { } else {
UnwindTarget* prev = self->uw_list; 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) { 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); 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 Frame__lineno(const Frame* self) {
int ip = Frame__ip(self); int ip = self->ip;
return c11__getitem(BytecodeEx, &self->co->codes_ex, ip).lineno; return c11__getitem(BytecodeEx, &self->co->codes_ex, ip).lineno;
} }
int Frame__iblock(const Frame* self) { 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; return c11__getitem(BytecodeEx, &self->co->codes_ex, ip).iblock;
} }
py_TValue* Frame__f_locals_try_get(Frame* self, py_Name name) { int Frame__getglobal(Frame* self, py_Name name) {
assert(!self->is_dynamic); if(self->globals->type == tp_module) {
return FastLocals__try_get_by_name(self->locals, self->co, name); 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);
} }

View File

@ -28,6 +28,7 @@ static bool generator__next__(int argc, py_Ref argv) {
if(ud->state == 2) return StopIteration(); if(ud->state == 2) return StopIteration();
// reset frame->p0 // reset frame->p0
assert(!ud->frame->is_locals_special);
int locals_offset = ud->frame->locals - ud->frame->p0; int locals_offset = ud->frame->locals - ud->frame->p0;
ud->frame->p0 = py_peek(0); ud->frame->p0 = py_peek(0);
ud->frame->locals = ud->frame->p0 + locals_offset; ud->frame->locals = ud->frame->p0 + locals_offset;

75
src/interpreter/name.c Normal file
View File

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

View File

@ -57,6 +57,7 @@ static void py_TypeInfo__ctor(py_TypeInfo* self,
void VM__ctor(VM* self) { void VM__ctor(VM* self) {
self->top_frame = NULL; self->top_frame = NULL;
InternedNames__ctor(&self->names);
ModuleDict__ctor(&self->modules, NULL, *py_NIL()); ModuleDict__ctor(&self->modules, NULL, *py_NIL());
TypeList__ctor(&self->types); TypeList__ctor(&self->types);
@ -126,7 +127,7 @@ void VM__ctor(VM* self) {
validate(tp_Exception, pk_Exception__register()); validate(tp_Exception, pk_Exception__register());
validate(tp_bytes, pk_bytes__register()); validate(tp_bytes, pk_bytes__register());
validate(tp_namedict, pk_namedict__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_code, pk_code__register());
validate(tp_dict, pk_dict__register()); validate(tp_dict, pk_dict__register());
@ -257,7 +258,8 @@ void VM__dtor(VM* self) {
ModuleDict__dtor(&self->modules); ModuleDict__dtor(&self->modules);
TypeList__dtor(&self->types); TypeList__dtor(&self->types);
FixedMemoryPool__dtor(&self->pool_frame); 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) { 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; 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)) { if(py_isint(slice)) {
int index = py_toint(slice); int index = py_toint(slice);
bool ok = pk__normalize_index(&index, length); bool ok = pk__normalize_index(&index, length);
@ -399,9 +405,9 @@ static bool
if(decl->starred_arg != -1) { if(decl->starred_arg != -1) {
int exceed_argc = p1 - t; int exceed_argc = p1 - t;
py_Ref vargs = &buffer[decl->starred_arg]; 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++) { for(int j = 0; j < exceed_argc; j++) {
py_tuple_setitem(vargs, j, t++); data[j] = *t++;
} }
} else { } else {
// kwdefaults override // kwdefaults override
@ -431,9 +437,8 @@ static bool
co->name->data); co->name->data);
} else { } else {
// add to **kwargs // add to **kwargs
bool ok = py_dict_setitem_by_str(&buffer[decl->starred_kwarg], bool ok =
py_name2str(key), py_dict_setitem(&buffer[decl->starred_kwarg], py_name2ref(key), &p1[2 * j + 1]);
&p1[2 * j + 1]);
if(!ok) return false; if(!ok) return false;
} }
} }
@ -480,7 +485,7 @@ FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall
// submit the call // submit the call
if(!fn->cfunc) { if(!fn->cfunc) {
// python function // 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); return opcall ? RES_CALL : VM__run_top_frame(self);
} else { } else {
// decl-based binding // decl-based binding
@ -509,7 +514,7 @@ FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall
// submit the call // submit the call
if(!fn->cfunc) { if(!fn->cfunc) {
// python function // 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); return opcall ? RES_CALL : VM__run_top_frame(self);
} else { } else {
// decl-based binding // 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 // copy buffer back to stack
self->stack.sp = argv + co->nlocals; self->stack.sp = argv + co->nlocals;
memcpy(argv, self->__vectorcall_buffer, co->nlocals * sizeof(py_TValue)); 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); pk_newgenerator(py_retval(), frame, p0, self->stack.sp);
self->stack.sp = p0; // reset the stack self->stack.sp = p0; // reset the stack
return RES_RETURN; return RES_RETURN;
@ -693,6 +698,11 @@ void ManagedHeap__mark(ManagedHeap* self) {
for(int i = 0; i < c11__count_array(vm->reg); i++) { for(int i = 0; i < c11__count_array(vm->reg); i++) {
pk__mark_value(&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) { void pk_print_stack(VM* self, Frame* frame, Bytecode byte) {

View File

@ -606,8 +606,7 @@ static bool array2d_like_get_bounding_rect(int argc, py_Ref argv) {
if(width <= 0 || height <= 0) { if(width <= 0 || height <= 0) {
return ValueError("value not found"); return ValueError("value not found");
} else { } else {
py_newtuple(py_retval(), 4); py_TValue* data = py_newtuple(py_retval(), 4);
py_TValue* data = py_tuple_data(py_retval());
py_newint(&data[0], left); py_newint(&data[0], left);
py_newint(&data[1], top); py_newint(&data[1], top);
py_newint(&data[2], width); py_newint(&data[2], width);
@ -786,8 +785,7 @@ static bool array2d_like_iterator__next__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1); PY_CHECK_ARGC(1);
c11_array2d_like_iterator* self = py_touserdata(argv); c11_array2d_like_iterator* self = py_touserdata(argv);
if(self->j >= self->array->n_rows) return StopIteration(); if(self->j >= self->array->n_rows) return StopIteration();
py_newtuple(py_retval(), 2); py_TValue* data = py_newtuple(py_retval(), 2);
py_TValue* data = py_tuple_data(py_retval());
py_newvec2i(&data[0], py_newvec2i(&data[0],
(c11_vec2i){ (c11_vec2i){
{self->i, self->j} {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) { static bool chunked_array2d__iter__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1); PY_CHECK_ARGC(1);
c11_chunked_array2d* self = py_touserdata(argv); 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++) { 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_chunked_array2d_chunks_KV* kv =
c11__at(c11_chunked_array2d_chunks_KV, &self->chunks, i); c11__at(c11_chunked_array2d_chunks_KV, &self->chunks, i);
py_newtuple(slot, 2); py_Ref p = py_newtuple(&data[i], 2);
py_newvec2i(py_tuple_getitem(slot, 0), kv->key); py_newvec2i(&p[0], kv->key); // pos
py_tuple_setitem(slot, 1, &kv->value[0]); p[1] = kv->value[0]; // context
} }
bool ok = py_iter(py_peek(-1)); bool ok = py_iter(py_peek(-1));
if(!ok) return false; 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 pos = py_tovec2i(&argv[1]);
c11_vec2i chunk_pos, local_pos; c11_vec2i chunk_pos, local_pos;
c11_chunked_array2d__world_to_chunk(self, pos.x, pos.y, &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* p = py_newtuple(py_retval(), 2);
py_TValue* data = py_tuple_data(py_retval()); py_newvec2i(&p[0], chunk_pos);
py_newvec2i(&data[0], chunk_pos); py_newvec2i(&p[1], local_pos);
py_newvec2i(&data[1], local_pos);
return true; return true;
} }

View File

@ -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; if(name_sv.size == 0 || name_sv.data[0] == '_') return true;
py_push(ctx); py_push(ctx);
py_pushnil(); py_pushnil();
py_newstr(py_pushtmp(), py_name2str(name)); py_assign(py_pushtmp(), py_name2ref(name));
py_push(value); py_push(value);
bool ok = py_vectorcall(2, 0); bool ok = py_vectorcall(2, 0);
if(!ok) return false; if(!ok) return false;

View File

@ -432,12 +432,12 @@ static bool vec2_smoothdamp_STATIC(int argc, py_Ref argv) {
} }
py_Ref ret = py_retval(); py_Ref ret = py_retval();
py_newtuple(ret, 2); py_Ref p = py_newtuple(ret, 2);
py_newvec2(py_tuple_getitem(ret, 0), py_newvec2(&p[0],
(c11_vec2){ (c11_vec2){
{output_x, output_y} {output_x, output_y}
}); });
py_newvec2(py_tuple_getitem(ret, 1), currentVelocity); py_newvec2(&p[1], currentVelocity);
return true; return true;
} }

View File

@ -136,11 +136,9 @@ static bool math_modf(int argc, py_Ref argv) {
PY_CHECK_ARGC(1); PY_CHECK_ARGC(1);
double i; double i;
double f = modf(py_tofloat(py_arg(0)), &i); double f = modf(py_tofloat(py_arg(0)), &i);
py_newtuple(py_retval(), 2); py_Ref p = py_newtuple(py_retval(), 2);
py_Ref _0 = py_tuple_getitem(py_retval(), 0); py_newfloat(&p[0], f);
py_Ref _1 = py_tuple_getitem(py_retval(), 1); py_newfloat(&p[1], i);
py_newfloat(_0, f);
py_newfloat(_1, i);
return true; return true;
} }

View File

@ -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) { 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 p0 = py_peek(0);
py_StackRef memo = py_pushtmp(); py_Ref p_memo = py_newtuple(py_pushtmp(), memo_length);
py_newtuple(memo, memo_length);
while(true) { while(true) {
PickleOp op = (PickleOp)*p; PickleOp op = (PickleOp)*p;
p++; p++;
switch(op) { switch(op) {
case PKL_MEMO_GET: { case PKL_MEMO_GET: {
int index = pkl__read_int(&p); int index = pkl__read_int(&p);
py_Ref val = py_tuple_getitem(memo, index); py_Ref val = &p_memo[index];
assert(!py_isnil(val)); assert(!py_isnil(val));
py_push(val); py_push(val);
break; break;
} }
case PKL_MEMO_SET: { case PKL_MEMO_SET: {
int index = pkl__read_int(&p); int index = pkl__read_int(&p);
py_tuple_setitem(memo, index, py_peek(-1)); p_memo[index] = *py_peek(-1);
break; break;
} }
case PKL_NIL: { 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: { case PKL_BUILD_TUPLE: {
int length = pkl__read_int(&p); int length = pkl__read_int(&p);
py_OutRef val = py_retval(); py_OutRef val = py_retval();
py_newtuple(val, length); py_Ref p = py_newtuple(val, length);
for(int i = length - 1; i >= 0; i--) { for(int i = length - 1; i >= 0; i--) {
py_StackRef item = py_peek(-1); p[i] = *py_peek(-1);
py_tuple_setitem(val, i, item);
py_pop(); py_pop();
} }
py_push(val); py_push(val);

View File

@ -159,12 +159,13 @@ void CodeObject__dtor(CodeObject* self) {
c11_vector__dtor(&self->func_decls); 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); PK_INCREF(decl);
self->decl = decl; self->decl = decl;
self->module = module ? *module : *py_NIL(); self->module = module;
self->clazz = NULL; self->globals = globals;
self->closure = NULL; self->closure = NULL;
self->clazz = NULL;
self->cfunc = NULL; self->cfunc = NULL;
} }

View File

@ -3,16 +3,13 @@
#include "pocketpy/pocketpy.h" #include "pocketpy/pocketpy.h"
#include "pocketpy/common/utils.h" #include "pocketpy/common/utils.h"
#include "pocketpy/common/sstream.h"
#include "pocketpy/objects/object.h"
#include "pocketpy/interpreter/vm.h" #include "pocketpy/interpreter/vm.h"
#include "pocketpy/compiler/compiler.h" #include "pocketpy/compiler/compiler.h"
#include <assert.h>
static void code__gc_mark(void* ud) { CodeObject__gc_mark(ud); }
py_Type pk_code__register() { py_Type pk_code__register() {
py_Type type = pk_newtype("code", tp_object, NULL, (py_Dtor)CodeObject__dtor, false, true); 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; return type;
} }
@ -57,14 +54,43 @@ bool pk_exec(CodeObject* co, py_Ref module) {
assert(module->type == tp_module); assert(module->type == tp_module);
py_StackRef sp = vm->stack.sp; py_StackRef sp = vm->stack.sp;
if(co->src->is_dynamic) sp -= 3; // [globals, locals, code] Frame* frame = Frame__new(co, sp, module, module, py_NIL(), true);
Frame* frame = Frame__new(co, module, sp, sp, false);
VM__push_frame(vm, frame); VM__push_frame(vm, frame);
FrameResult res = VM__run_top_frame(vm); FrameResult res = VM__run_top_frame(vm);
if(res == RES_ERROR) return false; if(res == RES_ERROR) return false;
if(res == RES_RETURN) return true; assert(res == RES_RETURN);
c11__unreachable(); 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) { bool py_exec(const char* source, const char* filename, enum py_CompileMode mode, py_Ref module) {

View File

@ -27,8 +27,6 @@ void py_initialize() {
static_assert(sizeof(py_TValue) == 16, "sizeof(py_TValue) != 16"); static_assert(sizeof(py_TValue) == 16, "sizeof(py_TValue) != 16");
static_assert(offsetof(py_TValue, extra) == 4, "offsetof(py_TValue, extra) != 4"); 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; pk_current_vm = pk_all_vm[0] = &pk_default_vm;
// initialize some convenient references // initialize some convenient references
@ -61,7 +59,6 @@ void py_finalize() {
pk_current_vm = &pk_default_vm; pk_current_vm = &pk_default_vm;
VM__dtor(&pk_default_vm); VM__dtor(&pk_default_vm);
pk_current_vm = NULL; pk_current_vm = NULL;
py_Name__finalize();
} }
void py_switchvm(int index) { void py_switchvm(int index) {

View File

@ -498,23 +498,47 @@ void py_newglobals(py_Ref out) {
pk_mappingproxy__namedict(out, &pk_current_vm->main); pk_mappingproxy__namedict(out, &pk_current_vm->main);
return; return;
} }
if(frame->is_dynamic) { if(frame->globals->type == tp_module) {
py_assign(out, &frame->p0[0]); pk_mappingproxy__namedict(out, frame->globals);
} else { } else {
pk_mappingproxy__namedict(out, frame->module); *out = *frame->globals; // dict
} }
} }
void py_newlocals(py_Ref out) { void py_newlocals(py_Ref out) {
Frame* frame = pk_current_vm->top_frame; Frame* frame = pk_current_vm->top_frame;
if(frame->is_dynamic) { if(!frame) {
py_assign(out, &frame->p0[1]); py_newdict(out);
return; return;
} }
if(frame->has_function) { if(frame->is_locals_special) {
pk_mappingproxy__locals(out, frame); 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 { } 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) { switch(argc) {
case 1: { case 1: {
py_newglobals(py_pushtmp()); py_newglobals(py_pushtmp());
py_newlocals(py_pushtmp()); pk_push_special_locals();
break; break;
} }
case 2: { case 2: {
// globals
if(py_isnone(py_arg(1))) { if(py_isnone(py_arg(1))) {
py_newglobals(py_pushtmp()); py_newglobals(py_pushtmp());
} else { } else {
py_push(py_arg(1)); py_push(py_arg(1));
} }
py_pushnone(); // locals
py_pushnil();
break; break;
} }
case 3: { case 3: {
// globals
if(py_isnone(py_arg(1))) { if(py_isnone(py_arg(1))) {
py_newglobals(py_pushtmp()); py_newglobals(py_pushtmp());
} else { } else {
py_push(py_arg(1)); py_push(py_arg(1));
} }
// locals
if(py_isnone(py_arg(2))) {
py_pushnil();
} else {
py_push(py_arg(2)); py_push(py_arg(2));
}
break; break;
} }
default: return TypeError("%s() takes at most 3 arguments", title); default: return TypeError("%s() takes at most 3 arguments", title);
} }
py_Ref code;
if(py_isstr(argv)) { if(py_isstr(argv)) {
bool ok = py_compile(py_tostr(argv), "<string>", mode, true); bool ok = py_compile(py_tostr(argv), "<string>", mode, true);
if(!ok) return false; if(!ok) return false;
code = py_retval(); py_push(py_retval());
} else if(py_istype(argv, tp_code)) { } else if(py_istype(argv, tp_code)) {
code = argv; py_push(argv);
} else { } else {
return TypeError("%s() expected 'str' or 'code', got '%t'", title, argv->type); 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; 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) { 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) { static void function__gc_mark(void* ud) {
Function* func = ud; Function* func = ud;
if(func->globals) pk__mark_value(func->globals);
if(func->closure) pk__mark_namedict(func->closure); if(func->closure) pk__mark_namedict(func->closure);
FuncDecl__gc_mark(func->decl); FuncDecl__gc_mark(func->decl);
} }
@ -779,14 +816,14 @@ static bool super__new__(int argc, py_Ref argv) {
py_Ref self_arg = NULL; py_Ref self_arg = NULL;
if(argc == 1) { if(argc == 1) {
// super() // super()
if(frame->has_function) { if(!frame->is_locals_special) {
py_TValue* callable = frame->p0; py_TValue* callable = frame->p0;
if(callable->type == tp_boundmethod) callable = py_getslot(frame->p0, 1); if(callable->type == tp_boundmethod) callable = py_getslot(frame->p0, 1);
if(callable->type == tp_function) { if(callable->type == tp_function) {
Function* func = py_touserdata(callable); Function* func = py_touserdata(callable);
if(func->clazz != NULL) { if(func->clazz != NULL) {
class_arg = *(py_Type*)PyObject__userdata(func->clazz); 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]; }
} }
} }
} }

View File

@ -496,14 +496,14 @@ static bool dict_items(int argc, py_Ref argv) {
static bool dict_keys(int argc, py_Ref argv) { static bool dict_keys(int argc, py_Ref argv) {
PY_CHECK_ARGC(1); PY_CHECK_ARGC(1);
Dict* self = py_touserdata(argv); Dict* self = py_touserdata(argv);
py_newtuple(py_retval(), self->length); py_Ref p = py_newtuple(py_retval(), self->length);
DictIterator iter; DictIterator iter;
DictIterator__ctor(&iter, self); DictIterator__ctor(&iter, self);
int i = 0; int i = 0;
while(1) { while(1) {
DictEntry* entry = DictIterator__next(&iter); DictEntry* entry = DictIterator__next(&iter);
if(!entry) break; if(!entry) break;
py_tuple_setitem(py_retval(), i++, &entry->key); p[i++] = entry->key;
} }
assert(i == self->length); assert(i == self->length);
return true; return true;
@ -512,14 +512,14 @@ static bool dict_keys(int argc, py_Ref argv) {
static bool dict_values(int argc, py_Ref argv) { static bool dict_values(int argc, py_Ref argv) {
PY_CHECK_ARGC(1); PY_CHECK_ARGC(1);
Dict* self = py_touserdata(argv); Dict* self = py_touserdata(argv);
py_newtuple(py_retval(), self->length); py_Ref p = py_newtuple(py_retval(), self->length);
DictIterator iter; DictIterator iter;
DictIterator__ctor(&iter, self); DictIterator__ctor(&iter, self);
int i = 0; int i = 0;
while(1) { while(1) {
DictEntry* entry = DictIterator__next(&iter); DictEntry* entry = DictIterator__next(&iter);
if(!entry) break; if(!entry) break;
py_tuple_setitem(py_retval(), i++, &entry->val); p[i++] = entry->val;
} }
assert(i == self->length); assert(i == self->length);
return true; return true;
@ -570,9 +570,9 @@ static bool dict_items__next__(int argc, py_Ref argv) {
DictIterator* iter = py_touserdata(py_arg(0)); DictIterator* iter = py_touserdata(py_arg(0));
DictEntry* entry = (DictIterator__next(iter)); DictEntry* entry = (DictIterator__next(iter));
if(entry) { if(entry) {
py_newtuple(py_retval(), 2); py_Ref p = py_newtuple(py_retval(), 2);
py_tuple_setitem(py_retval(), 0, &entry->key); p[0] = entry->key;
py_tuple_setitem(py_retval(), 1, &entry->val); p[1] = entry->val;
return true; return true;
} }
return StopIteration(); return StopIteration();

View File

@ -88,8 +88,8 @@ static bool BaseException_args(int argc, py_Ref argv){
PY_CHECK_ARGC(1); PY_CHECK_ARGC(1);
py_Ref arg = py_getslot(argv, 0); py_Ref arg = py_getslot(argv, 0);
if(!py_isnil(arg)) { if(!py_isnil(arg)) {
py_newtuple(py_retval(), 1); py_Ref p = py_newtuple(py_retval(), 1);
py_setslot(py_retval(), 0, arg); p[0] = *arg;
}else{ }else{
py_newtuple(py_retval(), 0); py_newtuple(py_retval(), 0);
} }

View File

@ -1,9 +1,8 @@
#include "pocketpy/pocketpy.h" #include "pocketpy/pocketpy.h"
#include "pocketpy/common/utils.h"
#include "pocketpy/objects/object.h" #include "pocketpy/objects/object.h"
#include "pocketpy/interpreter/vm.h" #include "pocketpy/interpreter/vm.h"
#include "pocketpy/common/sstream.h" #include <stdbool.h>
void pk_mappingproxy__namedict(py_Ref out, py_Ref object) { void pk_mappingproxy__namedict(py_Ref out, py_Ref object) {
py_newobject(out, tp_namedict, 1, 0); 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++) { for(int j = 0; j < PK_MAGIC_SLOTS_COMMON_LENGTH; j++) {
if(py_isnil(ti->magic_0 + j)) continue; if(py_isnil(ti->magic_0 + j)) continue;
py_Ref slot = py_list_emplace(py_retval()); py_Ref slot = py_list_emplace(py_retval());
py_newtuple(slot, 2); py_Ref p = py_newtuple(slot, 2);
py_newstr(py_tuple_getitem(slot, 0), py_name2str(j + PK_MAGIC_SLOTS_UNCOMMON_LENGTH)); p[0] = *py_name2ref(j + PK_MAGIC_SLOTS_UNCOMMON_LENGTH);
py_assign(py_tuple_getitem(slot, 1), ti->magic_0 + j); p[1] = ti->magic_0[j];
} }
if(ti->magic_1) { if(ti->magic_1) {
for(int j = 0; j < PK_MAGIC_SLOTS_UNCOMMON_LENGTH; j++) { for(int j = 0; j < PK_MAGIC_SLOTS_UNCOMMON_LENGTH; j++) {
if(py_isnil(ti->magic_1 + j)) continue; if(py_isnil(ti->magic_1 + j)) continue;
py_Ref slot = py_list_emplace(py_retval()); py_Ref slot = py_list_emplace(py_retval());
py_newtuple(slot, 2); py_Ref p = py_newtuple(slot, 2);
py_newstr(py_tuple_getitem(slot, 0), py_name2str(j)); p[0] = *py_name2ref(j);
py_assign(py_tuple_getitem(slot, 1), ti->magic_1 + j); p[1] = ti->magic_1[j];
} }
} }
} }
for(int i = 0; i < dict->length; i++) { for(int i = 0; i < dict->length; i++) {
py_Ref slot = py_list_emplace(py_retval()); 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); NameDict_KV* kv = c11__at(NameDict_KV, dict, i);
py_newstr(py_tuple_getitem(slot, 0), py_name2str(kv->key)); p[0] = *py_name2ref(kv->key);
py_assign(py_tuple_getitem(slot, 1), &kv->value); p[1] = kv->value;
} }
return true; return true;
} }
@ -103,67 +102,3 @@ py_Type pk_namedict__register() {
py_bindmethod(type, "clear", namedict_clear); py_bindmethod(type, "clear", namedict_clear);
return type; 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;
}

View File

@ -181,9 +181,9 @@ static bool int__divmod__(int argc, py_Ref argv) {
py_i64 lhs = py_toint(&argv[0]); py_i64 lhs = py_toint(&argv[0]);
py_i64 rhs = py_toint(&argv[1]); py_i64 rhs = py_toint(&argv[1]);
if(rhs == 0) return ZeroDivisionError("integer division or modulo by zero"); if(rhs == 0) return ZeroDivisionError("integer division or modulo by zero");
py_newtuple(py_retval(), 2); py_Ref p = py_newtuple(py_retval(), 2);
py_newint(py_getslot(py_retval(), 0), cpy11__fast_floor_div(lhs, rhs)); py_newint(&p[0], cpy11__fast_floor_div(lhs, rhs));
py_newint(py_getslot(py_retval(), 1), cpy11__fast_mod(lhs, rhs)); py_newint(&p[1], cpy11__fast_mod(lhs, rhs));
return true; return true;
} }

View File

@ -84,7 +84,7 @@ static bool type__base__(int argc, py_Ref argv) {
static bool type__name__(int argc, py_Ref argv) { static bool type__name__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1); PY_CHECK_ARGC(1);
py_TypeInfo* ti = pk__type_info(py_totype(argv)); 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; return true;
} }

View File

@ -153,7 +153,7 @@ bool py_getattr(py_Ref self, py_Name name) {
if(fallback) { if(fallback) {
py_push(fallback); py_push(fallback);
py_push(self); py_push(self);
py_newstr(py_pushtmp(), py_name2str(name)); py_assign(py_pushtmp(), py_name2ref(name));
return py_vectorcall(1, 0); return py_vectorcall(1, 0);
} }

View File

@ -5,12 +5,13 @@
#include "pocketpy/objects/object.h" #include "pocketpy/objects/object.h"
#include "pocketpy/interpreter/vm.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; VM* vm = pk_current_vm;
PyObject* obj = ManagedHeap__gcnew(&vm->heap, tp_tuple, n, 0); PyObject* obj = ManagedHeap__gcnew(&vm->heap, tp_tuple, n, 0);
out->type = tp_tuple; out->type = tp_tuple;
out->is_ptr = true; out->is_ptr = true;
out->_obj = obj; out->_obj = obj;
return PyObject__slots(obj);
} }
py_Ref py_tuple_getitem(py_Ref self, int i) { return py_getslot(self, i); } 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(); py_Ref tmp = py_pushtmp();
*tmp = *py_retval(); // backup the list *tmp = *py_retval(); // backup the list
int length = py_list_len(tmp); 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++) { 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(); py_pop();
return true; return true;
@ -86,9 +87,9 @@ static bool tuple__getitem__(int argc, py_Ref argv) {
py_newlist(tmp); py_newlist(tmp);
PK_SLICE_LOOP(i, start, stop, step) py_list_append(tmp, py_getslot(argv, i)); PK_SLICE_LOOP(i, start, stop, step) py_list_append(tmp, py_getslot(argv, i));
// convert list to tuple // 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++) { 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(); py_pop();
return true; return true;

View File

@ -104,7 +104,7 @@ py_Name
decl->docstring = docstring; decl->docstring = docstring;
// construct the function // construct the function
Function* ud = py_newobject(out, tp_function, slots, sizeof(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; ud->cfunc = f;
CodeObject__dtor(&code); CodeObject__dtor(&code);
PK_DECREF(source); PK_DECREF(source);

View File

@ -31,12 +31,10 @@ def f():
) )
assert b == 8 assert b == 8
class G: pass
def abc(): def abc():
g = G() g = {}
exec('a=1', g.__dict__) exec('a=1', g)
return g.a return g['a']
res = abc() res = abc()
assert (res==1), res assert (res==1), res
@ -67,3 +65,12 @@ try:
exit(1) exit(1)
except NameError: except NameError:
pass 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

View File

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