Compare commits

...

5 Commits

Author SHA1 Message Date
blueloveTH
f64885f4ca Update main.yml 2025-06-22 01:32:27 +08:00
blueloveTH
9cad7b3d58 Update main.yml 2025-06-22 01:29:02 +08:00
blueloveTH
790710b1a4 backup 2025-06-22 01:12:39 +08:00
blueloveTH
0f8c7d6d11 some refactor 2025-06-22 00:31:19 +08:00
blueloveTH
02b27b66c5 some builtin changes 2025-06-21 20:33:47 +08:00
22 changed files with 333 additions and 367 deletions

View File

@ -56,8 +56,14 @@ jobs:
- name: Setup Clang
uses: egor-tensin/setup-clang@v1
with:
version: 15
version: 17
platform: x64
- name: Run Sanitizers
run: |
sudo apt-get install -y libclang-rt-17-dev
bash build_g.sh
bash run_tests.sh
rm -rf ./main
- name: Unit Test with Coverage
run: bash run_tests.sh
- name: Upload coverage reports to Codecov

View File

@ -20,6 +20,11 @@ if(MSVC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Ox")
add_definitions(-DNDEBUG)
endif()
# if(CMAKE_BUILD_TYPE STREQUAL "Debug")
# message(">> Enable Address Sanitizer")
# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /Zi")
# endif()
else()
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2")
@ -99,6 +104,9 @@ if(PK_ENABLE_DETERMINISM)
endif()
endif()
find_package(Threads REQUIRED)
target_link_libraries(${PROJECT_NAME} Threads::Threads)
if(UNIX AND NOT APPLE)
if(NOT PK_ENABLE_DETERMINISM)
# use platform libm

View File

@ -4,10 +4,9 @@
#include "pocketpy/common/vector.h"
#include "pocketpy/objects/object.h"
#define PK_MAX_CHUNK_LENGTH 256
typedef struct py_TypeInfo {
py_Name name;
py_Type index;
py_Type base;
struct py_TypeInfo* base_ti;
@ -17,20 +16,13 @@ typedef struct py_TypeInfo {
bool is_python; // is it a python class? (not derived from c object)
bool is_sealed; // can it be subclassed?
void (*dtor)(void*);
py_Dtor dtor; // destructor for this type, NULL if no dtor
py_TValue annotations;
void (*on_end_subclass)(struct py_TypeInfo*); // backdoor for enum module
} py_TypeInfo;
typedef struct TypeList {
int length;
py_TypeInfo* chunks[PK_MAX_CHUNK_LENGTH];
} TypeList;
void TypeList__ctor(TypeList* self);
void TypeList__dtor(TypeList* self);
py_TypeInfo* TypeList__get(TypeList* self, py_Type index);
py_TypeInfo* TypeList__emplace(TypeList* self);
void TypeList__apply(TypeList* self, void (*f)(py_TypeInfo*, void*), void* ctx);
py_TypeInfo* pk_typeinfo(py_Type type);
py_ItemRef pk_tpfindname(py_TypeInfo* ti, py_Name name);
#define pk_tpfindmagic pk_tpfindname

View File

@ -29,14 +29,19 @@ typedef struct WatchdogInfo {
clock_t max_reset_time;
} WatchdogInfo;
typedef struct TypePointer {
py_TypeInfo* ti;
py_Dtor dtor;
} TypePointer;
typedef struct VM {
py_Frame* top_frame;
BinTree modules;
TypeList types;
c11_vector /*TypePointer*/ types;
py_TValue builtins; // builtins module
py_TValue main; // __main__ module
py_GlobalRef builtins; // builtins module
py_GlobalRef main; // __main__ module
py_Callbacks callbacks;
@ -80,7 +85,7 @@ bool pk__parse_int_slice(py_Ref slice,
bool pk__normalize_index(int* index, int length);
bool pk__object_new(int argc, py_Ref argv);
py_TypeInfo* pk__type_info(py_Type type);
py_TypeInfo* pk_typeinfo(py_Type type);
bool pk_wrapper__self(int argc, py_Ref argv);
@ -152,7 +157,7 @@ py_Type pk_generator__register();
py_Type pk_namedict__register();
py_Type pk_code__register();
py_TValue pk_builtins__register();
py_GlobalRef pk_builtins__register();
/* mappingproxy */
void pk_mappingproxy__namedict(py_Ref out, py_Ref object);

View File

@ -118,6 +118,8 @@ PK_API void py_setvmctx(void* ctx);
PK_API void py_sys_setargv(int argc, char** argv);
/// Set the trace function for the current VM.
PK_API void py_sys_settrace(py_TraceFunc func, bool reset);
/// Invoke the garbage collector.
PK_API int py_gc_collect();
/// Setup the callbacks for the current VM.
PK_API py_Callbacks* py_callbacks();

View File

@ -255,7 +255,7 @@ FrameResult VM__run_top_frame(VM* self) {
}
if(res == -1) goto __ERROR;
// builtins
py_Ref tmp = py_getdict(&self->builtins, name);
py_Ref tmp = py_getdict(self->builtins, name);
if(tmp != NULL) {
PUSH(tmp);
DISPATCH();
@ -277,7 +277,7 @@ FrameResult VM__run_top_frame(VM* self) {
}
if(res == -1) goto __ERROR;
tmp = py_getdict(&self->builtins, name);
tmp = py_getdict(self->builtins, name);
if(tmp != NULL) {
PUSH(tmp);
DISPATCH();
@ -293,7 +293,7 @@ FrameResult VM__run_top_frame(VM* self) {
DISPATCH();
}
if(res == -1) goto __ERROR;
py_Ref tmp = py_getdict(&self->builtins, name);
py_Ref tmp = py_getdict(self->builtins, name);
if(tmp != NULL) {
PUSH(tmp);
DISPATCH();
@ -325,7 +325,7 @@ FrameResult VM__run_top_frame(VM* self) {
DISPATCH();
}
if(res == -1) goto __ERROR;
tmp = py_getdict(&self->builtins, name);
tmp = py_getdict(self->builtins, name);
if(tmp) {
PUSH(tmp);
DISPATCH();
@ -505,7 +505,7 @@ FrameResult VM__run_top_frame(VM* self) {
/*****************************************/
case OP_BUILD_IMAG: {
// [x]
py_Ref f = py_getdict(&self->builtins, py_name("complex"));
py_Ref f = py_getdict(self->builtins, py_name("complex"));
assert(f != NULL);
py_TValue tmp = *TOP();
*TOP() = *f; // [complex]
@ -558,7 +558,7 @@ FrameResult VM__run_top_frame(VM* self) {
}
case OP_BUILD_SET: {
py_TValue* begin = SP() - byte.arg;
py_Ref typeobject_set = py_getdict(&self->builtins, py_name("set"));
py_Ref typeobject_set = py_getdict(self->builtins, py_name("set"));
assert(typeobject_set != NULL);
py_push(typeobject_set);
py_pushnil();
@ -1036,7 +1036,7 @@ FrameResult VM__run_top_frame(VM* self) {
}
POP();
py_TypeInfo* base_ti = TypeList__get(&self->types, base);
py_TypeInfo* base_ti = pk_typeinfo(base);
if(base_ti->is_sealed) {
TypeError("type '%t' is not an acceptable base type", base);
goto __ERROR;
@ -1059,7 +1059,7 @@ FrameResult VM__run_top_frame(VM* self) {
if(py_istype(TOP(), tp_type)) {
// call on_end_subclass
py_TypeInfo* ti = TypeList__get(&self->types, py_totype(TOP()));
py_TypeInfo* ti = py_touserdata(TOP());
if(ti->base != tp_object) {
py_TypeInfo* base_ti = ti->base_ti;
if(base_ti->on_end_subclass) base_ti->on_end_subclass(ti);
@ -1079,8 +1079,6 @@ FrameResult VM__run_top_frame(VM* self) {
}
case OP_STORE_CLASS_ATTR: {
assert(self->curr_class);
py_Type type = py_totype(self->curr_class);
py_TypeInfo* ti = TypeList__get(&self->types, type);
py_Name name = co_names[byte.arg];
// TOP() can be a function, classmethod or custom decorator
py_Ref actual_func = TOP();
@ -1098,8 +1096,7 @@ FrameResult VM__run_top_frame(VM* self) {
case OP_ADD_CLASS_ANNOTATION: {
assert(self->curr_class);
// [type_hint string]
py_Type type = py_totype(self->curr_class);
py_TypeInfo* ti = TypeList__get(&self->types, type);
py_TypeInfo* ti = py_touserdata(self->curr_class);
if(py_isnil(&ti->annotations)) py_newdict(&ti->annotations);
py_Name name = co_names[byte.arg];
bool ok = py_dict_setitem_by_str(&ti->annotations, py_name2str(name), TOP());

View File

@ -194,7 +194,7 @@ const char* py_Frame_sourceloc(py_Frame* self, int* lineno) {
void py_Frame_newglobals(py_Frame* frame, py_OutRef out) {
if(!frame) {
pk_mappingproxy__namedict(out, &pk_current_vm->main);
pk_mappingproxy__namedict(out, pk_current_vm->main);
return;
}
if(frame->globals->type == tp_module) {

View File

@ -1,51 +1,49 @@
#include "pocketpy/interpreter/typeinfo.h"
#include "pocketpy/common/utils.h"
#include "pocketpy/interpreter/vm.h"
#define CHUNK_SIZE 128
#define LOG2_CHUNK_SIZE 7
void TypeList__ctor(TypeList* self) {
self->length = 0;
memset(self->chunks, 0, sizeof(self->chunks));
py_ItemRef pk_tpfindname(py_TypeInfo* ti, py_Name name) {
assert(ti != NULL);
do {
py_Ref res = py_getdict(&ti->self, name);
if(res) return res;
ti = ti->base_ti;
} while(ti);
return NULL;
}
void TypeList__dtor(TypeList* self) {
for(py_Type t = 0; t < self->length; t++) {
py_TypeInfo* info = TypeList__get(self, t);
(void)info;
}
for(int i = 0; i < PK_MAX_CHUNK_LENGTH; i++) {
if(self->chunks[i]) PK_FREE(self->chunks[i]);
}
py_ItemRef py_tpfindname(py_Type type, py_Name name) {
py_TypeInfo* ti = pk_typeinfo(type);
return pk_tpfindname(ti, name);
}
py_TypeInfo* TypeList__get(TypeList* self, py_Type index) {
assert(index < self->length);
int chunk = index >> LOG2_CHUNK_SIZE;
int offset = index & (CHUNK_SIZE - 1);
return self->chunks[chunk] + offset;
py_Ref py_tpfindmagic(py_Type t, py_Name name) {
// assert(py_ismagicname(name));
return py_tpfindname(t, name);
}
py_TypeInfo* TypeList__emplace(TypeList* self) {
int chunk = self->length >> LOG2_CHUNK_SIZE;
int offset = self->length & (CHUNK_SIZE - 1);
if(self->chunks[chunk] == NULL) {
if(chunk >= PK_MAX_CHUNK_LENGTH) {
c11__abort("TypeList__emplace(): max chunk length exceeded");
}
self->chunks[chunk] = PK_MALLOC(sizeof(py_TypeInfo) * CHUNK_SIZE);
memset(self->chunks[chunk], 0, sizeof(py_TypeInfo) * CHUNK_SIZE);
}
self->length++;
return self->chunks[chunk] + offset;
py_Type py_tpbase(py_Type t) {
assert(t);
py_TypeInfo* ti = pk_typeinfo(t);
return ti->base;
}
void TypeList__apply(TypeList* self, void (*f)(py_TypeInfo*, void*), void* ctx) {
for(int i = 0; i < self->length; i++) {
py_TypeInfo* info = TypeList__get(self, i);
f(info, ctx);
}
PK_DEPRECATED py_Ref py_tpgetmagic(py_Type type, py_Name name) {
// assert(py_ismagicname(name));
py_TypeInfo* ti = pk_typeinfo(type);
py_Ref retval = py_getdict(&ti->self, name);
return retval != NULL ? retval : py_NIL();
}
#undef CHUNK_SIZE
#undef LOG2_CHUNK_SIZE
py_Ref py_tpobject(py_Type type) {
assert(type);
return &pk_typeinfo(type)->self;
}
const char* py_tpname(py_Type type) {
if(!type) return "nil";
py_Name name = pk_typeinfo(type)->name;
return py_name2str(name);
}
py_TypeInfo* pk_typeinfo(py_Type type) {
return c11__getitem(TypePointer, &pk_current_vm->types, type).ti;
}

View File

@ -39,40 +39,12 @@ void LineProfiler__tracefunc(py_Frame* frame, enum py_TraceEvent event) {
if(self->enabled && event == TRACE_EVENT_LINE) { LineProfiler__tracefunc_line(self, frame); }
}
static void py_TypeInfo__ctor(py_TypeInfo* self,
py_Name name,
py_Type index,
py_Type base,
py_TypeInfo* base_ti,
py_GlobalRef module) {
memset(self, 0, sizeof(py_TypeInfo));
self->name = name;
self->base = base;
self->base_ti = base_ti;
// create type object with __dict__
ManagedHeap* heap = &pk_current_vm->heap;
PyObject* typeobj = ManagedHeap__new(heap, tp_type, -1, sizeof(py_Type));
*(py_Type*)PyObject__userdata(typeobj) = index;
self->self = (py_TValue){
.type = typeobj->type,
.is_ptr = true,
._obj = typeobj,
};
self->module = module;
self->annotations = *py_NIL();
}
static int BinTree__cmp_cstr(void* lhs, void* rhs) {
const char* l = (const char*)lhs;
const char* r = (const char*)rhs;
return strcmp(l, r);
}
static int BinTree__cmp_voidp(void* lhs, void* rhs) { return lhs < rhs ? -1 : (lhs > rhs ? 1 : 0); }
void VM__ctor(VM* self) {
self->top_frame = NULL;
@ -81,10 +53,10 @@ void VM__ctor(VM* self) {
.need_free_key = true,
};
BinTree__ctor(&self->modules, c11_strdup(""), py_NIL(), &modules_config);
TypeList__ctor(&self->types);
c11_vector__ctor(&self->types, sizeof(TypePointer));
self->builtins = *py_NIL();
self->main = *py_NIL();
self->builtins = NULL;
self->main = NULL;
self->callbacks.importfile = pk_default_importfile;
self->callbacks.print = pk_default_print;
@ -116,14 +88,15 @@ void VM__ctor(VM* self) {
/* Init Builtin Types */
// 0: unused
void* placeholder = TypeList__emplace(&self->types);
memset(placeholder, 0, sizeof(py_TypeInfo));
TypePointer* placeholder = c11_vector__emplace(&self->types);
placeholder->ti = NULL;
placeholder->dtor = NULL;
#define validate(t, expr) \
if(t != (expr)) abort()
validate(tp_object, pk_newtype("object", 0, NULL, NULL, true, false));
validate(tp_type, pk_newtype("type", 1, NULL, NULL, false, true));
validate(tp_object, pk_newtype("object", tp_nil, NULL, NULL, true, false));
validate(tp_type, pk_newtype("type", tp_object, NULL, NULL, false, true));
pk_object__register();
validate(tp_int, pk_newtype("int", tp_object, NULL, NULL, false, true));
@ -175,8 +148,8 @@ void VM__ctor(VM* self) {
// inject some builtin exceptions
#define INJECT_BUILTIN_EXC(name, TBase) \
do { \
py_Type type = pk_newtype(#name, TBase, &self->builtins, NULL, false, true); \
py_setdict(&self->builtins, py_name(#name), py_tpobject(type)); \
py_Type type = pk_newtype(#name, TBase, self->builtins, NULL, false, true); \
py_setdict(self->builtins, py_name(#name), py_tpobject(type)); \
validate(tp_##name, type); \
} while(0)
@ -184,7 +157,7 @@ void VM__ctor(VM* self) {
INJECT_BUILTIN_EXC(KeyboardInterrupt, tp_BaseException);
validate(tp_StopIteration, pk_StopIteration__register());
py_setdict(&self->builtins, py_name("StopIteration"), py_tpobject(tp_StopIteration));
py_setdict(self->builtins, py_name("StopIteration"), py_tpobject(tp_StopIteration));
INJECT_BUILTIN_EXC(SyntaxError, tp_Exception);
INJECT_BUILTIN_EXC(RecursionError, tp_Exception);
@ -229,11 +202,11 @@ void VM__ctor(VM* self) {
};
for(int i = 0; i < c11__count_array(public_types); i++) {
py_TypeInfo* ti = pk__type_info(public_types[i]);
py_setdict(&self->builtins, ti->name, &ti->self);
py_TypeInfo* ti = pk_typeinfo(public_types[i]);
py_setdict(self->builtins, ti->name, &ti->self);
}
py_newnotimplemented(py_emplacedict(&self->builtins, py_name("NotImplemented")));
py_newnotimplemented(py_emplacedict(self->builtins, py_name("NotImplemented")));
pk__add_module_vmath();
pk__add_module_array2d();
@ -266,7 +239,7 @@ void VM__ctor(VM* self) {
// add python builtins
do {
bool ok;
ok = py_exec(kPythonLibs_builtins, "<builtins>", EXEC_MODE, &self->builtins);
ok = py_exec(kPythonLibs_builtins, "<builtins>", EXEC_MODE, self->builtins);
if(!ok) goto __ABORT;
break;
__ABORT:
@ -274,7 +247,7 @@ void VM__ctor(VM* self) {
c11__abort("failed to load python builtins!");
} while(0);
self->main = *py_newmodule("__main__");
self->main = py_newmodule("__main__");
}
void VM__dtor(VM* self) {
@ -287,11 +260,11 @@ void VM__dtor(VM* self) {
while(self->top_frame)
VM__pop_frame(self);
BinTree__dtor(&self->modules);
TypeList__dtor(&self->types);
FixedMemoryPool__dtor(&self->pool_frame);
ValueStack__dtor(&self->stack);
CachedNames__dtor(&self->cached_names);
NameDict__dtor(&self->compile_time_funcs);
c11_vector__dtor(&self->types);
}
void VM__push_frame(VM* self, py_Frame* frame) {
@ -398,16 +371,30 @@ py_Type pk_newtype(const char* name,
bool is_python,
bool is_sealed) {
py_Type index = pk_current_vm->types.length;
py_TypeInfo* ti = TypeList__emplace(&pk_current_vm->types);
py_TypeInfo* base_ti = base ? pk__type_info(base) : NULL;
py_TypeInfo* self = py_newobject(py_retval(), tp_type, -1, sizeof(py_TypeInfo));
py_TypeInfo* base_ti = base ? pk_typeinfo(base) : NULL;
if(base_ti && base_ti->is_sealed) {
c11__abort("type '%s' is not an acceptable base type", py_name2str(base_ti->name));
}
py_TypeInfo__ctor(ti, py_name(name), index, base, base_ti, module ? module : py_NIL());
memset(self, 0, sizeof(py_TypeInfo));
self->name = py_name(name);
self->index = index;
self->base = base;
self->base_ti = base_ti;
self->self = *py_retval();
self->module = module ? module : py_NIL();
self->annotations = *py_NIL();
if(!dtor && base) dtor = base_ti->dtor;
ti->dtor = dtor;
ti->is_python = is_python;
ti->is_sealed = is_sealed;
self->is_python = is_python;
self->is_sealed = is_sealed;
self->dtor = dtor;
TypePointer* pointer = c11_vector__emplace(&pk_current_vm->types);
pointer->ti = self;
pointer->dtor = dtor;
return index;
}
@ -628,12 +615,6 @@ FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall
}
/****************************************/
void PyObject__dtor(PyObject* self) {
py_TypeInfo* ti = pk__type_info(self->type);
if(ti->dtor) ti->dtor(PyObject__userdata(self));
if(self->slots == -1) NameDict__dtor(PyObject__dict(self));
}
void FuncDecl__gc_mark(const FuncDecl* self, c11_vector* p_stack) {
CodeObject__gc_mark(&self->code, p_stack);
for(int j = 0; j < self->kwargs.length; j++) {
@ -684,10 +665,8 @@ void ManagedHeap__mark(ManagedHeap* self) {
int types_length = vm->types.length;
// 0-th type is placeholder
for(py_Type i = 1; i < types_length; i++) {
py_TypeInfo* ti = TypeList__get(&vm->types, i);
// mark type object
py_TypeInfo* ti = c11__getitem(TypePointer, &vm->types, i).ti;
pk__mark_value(&ti->self);
// mark type annotations
pk__mark_value(&ti->annotations);
}
// mark frame
@ -763,125 +742,3 @@ void ManagedHeap__mark(ManagedHeap* self) {
}
}
}
void pk_print_stack(VM* self, py_Frame* frame, Bytecode byte) {
return;
if(frame == NULL || py_isnil(&self->main)) return;
py_TValue* sp = self->stack.sp;
c11_sbuf buf;
c11_sbuf__ctor(&buf);
for(py_Ref p = self->stack.begin; p != sp; p++) {
switch(p->type) {
case tp_nil: c11_sbuf__write_cstr(&buf, "nil"); break;
case tp_int: c11_sbuf__write_i64(&buf, p->_i64); break;
case tp_float: c11_sbuf__write_f64(&buf, p->_f64, -1); break;
case tp_bool: c11_sbuf__write_cstr(&buf, p->_bool ? "True" : "False"); break;
case tp_NoneType: c11_sbuf__write_cstr(&buf, "None"); break;
case tp_list: {
pk_sprintf(&buf, "list(%d)", py_list_len(p));
break;
}
case tp_tuple: {
pk_sprintf(&buf, "tuple(%d)", py_tuple_len(p));
break;
}
case tp_function: {
Function* ud = py_touserdata(p);
c11_sbuf__write_cstr(&buf, ud->decl->code.name->data);
c11_sbuf__write_cstr(&buf, "()");
break;
}
case tp_type: {
pk_sprintf(&buf, "<class '%t'>", py_totype(p));
break;
}
case tp_str: {
pk_sprintf(&buf, "%q", py_tosv(p));
break;
}
case tp_module: {
py_Ref path = py_getdict(p, __path__);
pk_sprintf(&buf, "<module '%v'>", py_tosv(path));
break;
}
default: {
pk_sprintf(&buf, "(%t)", p->type);
break;
}
}
if(p != &sp[-1]) c11_sbuf__write_cstr(&buf, ", ");
}
c11_string* stack_str = c11_sbuf__submit(&buf);
printf("%s:%-3d: %-25s %-6d [%s]\n",
frame->co->src->filename->data,
Frame__lineno(frame),
pk_opname(byte.op),
byte.arg,
stack_str->data);
c11_string__delete(stack_str);
}
bool pk_wrapper__self(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
py_assign(py_retval(), argv);
return true;
}
py_TypeInfo* pk__type_info(py_Type type) { return TypeList__get(&pk_current_vm->types, type); }
int py_replinput(char* buf, int max_size) {
buf[0] = '\0'; // reset first char because we check '@' at the beginning
int size = 0;
bool multiline = false;
printf(">>> ");
while(true) {
int c = pk_current_vm->callbacks.getchr();
if(c == EOF) return -1;
if(c == '\n') {
char last = '\0';
if(size > 0) last = buf[size - 1];
if(multiline) {
if(last == '\n') {
break; // 2 consecutive newlines to end multiline input
} else {
printf("... ");
}
} else {
if(last == ':' || last == '(' || last == '[' || last == '{' || buf[0] == '@') {
printf("... ");
multiline = true;
} else {
break;
}
}
}
if(size == max_size - 1) {
buf[size] = '\0';
return size;
}
buf[size++] = c;
}
buf[size] = '\0';
return size;
}
py_Ref py_name2ref(py_Name name) {
assert(name != NULL);
CachedNames* d = &pk_current_vm->cached_names;
py_Ref res = CachedNames__try_get(d, name);
if(res != NULL) return res;
// not found, create a new one
py_StackRef tmp = py_pushtmp();
py_newstrv(tmp, py_name2sv(name));
CachedNames__set(d, name, tmp);
py_pop();
return CachedNames__try_get(d, name);
}

127
src/interpreter/vmx.c Normal file
View File

@ -0,0 +1,127 @@
#include "pocketpy/interpreter/vm.h"
void pk_print_stack(VM* self, py_Frame* frame, Bytecode byte) {
return;
if(frame == NULL || py_isnil(self->main)) return;
py_TValue* sp = self->stack.sp;
c11_sbuf buf;
c11_sbuf__ctor(&buf);
for(py_Ref p = self->stack.begin; p != sp; p++) {
switch(p->type) {
case tp_nil: c11_sbuf__write_cstr(&buf, "nil"); break;
case tp_int: c11_sbuf__write_i64(&buf, p->_i64); break;
case tp_float: c11_sbuf__write_f64(&buf, p->_f64, -1); break;
case tp_bool: c11_sbuf__write_cstr(&buf, p->_bool ? "True" : "False"); break;
case tp_NoneType: c11_sbuf__write_cstr(&buf, "None"); break;
case tp_list: {
pk_sprintf(&buf, "list(%d)", py_list_len(p));
break;
}
case tp_tuple: {
pk_sprintf(&buf, "tuple(%d)", py_tuple_len(p));
break;
}
case tp_function: {
Function* ud = py_touserdata(p);
c11_sbuf__write_cstr(&buf, ud->decl->code.name->data);
c11_sbuf__write_cstr(&buf, "()");
break;
}
case tp_type: {
pk_sprintf(&buf, "<class '%t'>", py_totype(p));
break;
}
case tp_str: {
pk_sprintf(&buf, "%q", py_tosv(p));
break;
}
case tp_module: {
py_Ref path = py_getdict(p, __path__);
pk_sprintf(&buf, "<module '%v'>", py_tosv(path));
break;
}
default: {
pk_sprintf(&buf, "(%t)", p->type);
break;
}
}
if(p != &sp[-1]) c11_sbuf__write_cstr(&buf, ", ");
}
c11_string* stack_str = c11_sbuf__submit(&buf);
printf("%s:%-3d: %-25s %-6d [%s]\n",
frame->co->src->filename->data,
Frame__lineno(frame),
pk_opname(byte.op),
byte.arg,
stack_str->data);
c11_string__delete(stack_str);
}
bool pk_wrapper__self(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
py_assign(py_retval(), argv);
return true;
}
int py_replinput(char* buf, int max_size) {
buf[0] = '\0'; // reset first char because we check '@' at the beginning
int size = 0;
bool multiline = false;
printf(">>> ");
while(true) {
int c = pk_current_vm->callbacks.getchr();
if(c == EOF) return -1;
if(c == '\n') {
char last = '\0';
if(size > 0) last = buf[size - 1];
if(multiline) {
if(last == '\n') {
break; // 2 consecutive newlines to end multiline input
} else {
printf("... ");
}
} else {
if(last == ':' || last == '(' || last == '[' || last == '{' || buf[0] == '@') {
printf("... ");
multiline = true;
} else {
break;
}
}
}
if(size == max_size - 1) {
buf[size] = '\0';
return size;
}
buf[size++] = c;
}
buf[size] = '\0';
return size;
}
py_Ref py_name2ref(py_Name name) {
assert(name != NULL);
CachedNames* d = &pk_current_vm->cached_names;
py_Ref res = CachedNames__try_get(d, name);
if(res != NULL) return res;
// not found, create a new one
py_StackRef tmp = py_pushtmp();
py_newstrv(tmp, py_name2sv(name));
CachedNames__set(d, name, tmp);
py_pop();
return CachedNames__try_get(d, name);
}
void PyObject__dtor(PyObject* self) {
py_Dtor dtor = c11__getitem(TypePointer, &pk_current_vm->types, self->type).dtor;
if(dtor) dtor(PyObject__userdata(self));
if(self->slots == -1) NameDict__dtor(PyObject__dict(self));
}

View File

@ -86,5 +86,5 @@ void pk__add_module_enum() {
py_bindproperty(type, "name", Enum__name, NULL);
py_bindproperty(type, "value", Enum__value, NULL);
pk__type_info(type)->on_end_subclass = Enum__on_end_subclass;
pk_typeinfo(type)->on_end_subclass = Enum__on_end_subclass;
}

View File

@ -1,7 +1,7 @@
#include "pocketpy/pocketpy.h"
#include "pocketpy/interpreter/vm.h"
static bool gc_collect(int argc, py_Ref argv){
static bool gc_collect(int argc, py_Ref argv) {
PY_CHECK_ARGC(0);
ManagedHeap* heap = &pk_current_vm->heap;
int res = ManagedHeap__collect(heap);
@ -9,7 +9,7 @@ static bool gc_collect(int argc, py_Ref argv){
return true;
}
static bool gc_enable(int argc, py_Ref argv){
static bool gc_enable(int argc, py_Ref argv) {
PY_CHECK_ARGC(0);
ManagedHeap* heap = &pk_current_vm->heap;
heap->gc_enabled = true;
@ -17,7 +17,7 @@ static bool gc_enable(int argc, py_Ref argv){
return true;
}
static bool gc_disable(int argc, py_Ref argv){
static bool gc_disable(int argc, py_Ref argv) {
PY_CHECK_ARGC(0);
ManagedHeap* heap = &pk_current_vm->heap;
heap->gc_enabled = false;
@ -25,7 +25,7 @@ static bool gc_disable(int argc, py_Ref argv){
return true;
}
static bool gc_isenabled(int argc, py_Ref argv){
static bool gc_isenabled(int argc, py_Ref argv) {
PY_CHECK_ARGC(0);
ManagedHeap* heap = &pk_current_vm->heap;
py_newbool(py_retval(), heap->gc_enabled);
@ -39,4 +39,9 @@ void pk__add_module_gc() {
py_bindfunc(mod, "enable", gc_enable);
py_bindfunc(mod, "disable", gc_disable);
py_bindfunc(mod, "isenabled", gc_isenabled);
}
int py_gc_collect() {
ManagedHeap* heap = &pk_current_vm->heap;
return ManagedHeap__collect(heap);
}

View File

@ -229,7 +229,7 @@ void pk__add_module_io() {
py_newint(py_emplacedict(mod, py_name("SEEK_CUR")), SEEK_CUR);
py_newint(py_emplacedict(mod, py_name("SEEK_END")), SEEK_END);
py_setdict(&pk_current_vm->builtins, py_name("open"), py_tpobject(FileIO));
py_setdict(pk_current_vm->builtins, py_name("open"), py_tpobject(FileIO));
}
#else

View File

@ -61,7 +61,7 @@ static void PickleObject__write_bytes(PickleObject* buf, const void* data, int s
}
static void c11_sbuf__write_type_path(c11_sbuf* path_buf, py_Type type) {
py_TypeInfo* ti = pk__type_info(type);
py_TypeInfo* ti = pk_typeinfo(type);
if(py_isnil(ti->module)) {
c11_sbuf__write_cstr(path_buf, py_name2str(ti->name));
return;
@ -349,7 +349,7 @@ static bool pkl__write_object(PickleObject* buf, py_TValue* obj) {
// try memo for `is_ptr=true` objects
if(pkl__try_memo(buf, obj->_obj)) return true;
py_TypeInfo* ti = pk__type_info(obj->type);
py_TypeInfo* ti = pk_typeinfo(obj->type);
py_Ref f_reduce = py_tpfindmagic(obj->type, __reduce__);
if(f_reduce != NULL) {
if(!py_call(f_reduce, 1, obj)) return false;

View File

@ -60,7 +60,7 @@ static bool pkpy_memory_usage(int argc, py_Ref argv) {
static bool pkpy_is_user_defined_type(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
PY_CHECK_ARG_TYPE(0, tp_type);
py_TypeInfo* ti = pk__type_info(py_totype(argv));
py_TypeInfo* ti = py_touserdata(argv);
py_newbool(py_retval(), ti->is_python);
return true;
}

View File

@ -45,49 +45,11 @@ bool py_tobool(py_Ref self) {
py_Type py_totype(py_Ref self) {
assert(self->type == tp_type);
py_Type* ud = py_touserdata(self);
return *ud;
py_TypeInfo* ud = py_touserdata(self);
return ud->index;
}
void* py_touserdata(py_Ref self) {
assert(self && self->is_ptr);
return PyObject__userdata(self->_obj);
}
bool py_istype(py_Ref self, py_Type type) { return self->type == type; }
bool py_checktype(py_Ref self, py_Type type) {
if(self->type == type) return true;
return TypeError("expected '%t', got '%t'", type, self->type);
}
bool py_checkinstance(py_Ref self, py_Type type) {
if(py_isinstance(self, type)) return true;
return TypeError("expected '%t' or its subclass, got '%t'", type, self->type);
}
bool py_isinstance(py_Ref obj, py_Type type) { return py_issubclass(obj->type, type); }
bool py_issubclass(py_Type derived, py_Type base) {
TypeList* types = &pk_current_vm->types;
do {
if(derived == base) return true;
derived = TypeList__get(types, derived)->base;
} while(derived);
return false;
}
py_Type py_typeof(py_Ref self) { return self->type; }
py_Type py_gettype(const char* module, py_Name name) {
py_Ref mod;
if(module != NULL) {
mod = py_getmodule(module);
if(!mod) return 0;
} else {
mod = &pk_current_vm->builtins;
}
py_Ref object = py_getdict(mod, name);
if(object && py_istype(object, tp_type)) return py_totype(object);
return 0;
}

View File

@ -49,7 +49,7 @@ bool py_compile(const char* source,
bool pk_exec(CodeObject* co, py_Ref module) {
VM* vm = pk_current_vm;
if(!module) module = &vm->main;
if(!module) module = vm->main;
assert(module->type == tp_module);
py_StackRef sp = vm->stack.sp;
@ -63,7 +63,7 @@ bool pk_exec(CodeObject* co, py_Ref module) {
bool pk_execdyn(CodeObject* co, py_Ref module, py_Ref globals, py_Ref locals) {
VM* vm = pk_current_vm;
if(!module) module = &vm->main;
if(!module) module = vm->main;
assert(module->type == tp_module);
py_StackRef sp = vm->stack.sp;

View File

@ -238,7 +238,7 @@ bool pk_loadmethod(py_StackRef self, py_Name name) {
break;
case tp_classmethod:
self[0] = *py_getslot(cls_var, 0);
self[1] = pk__type_info(type)->self;
self[1] = pk_typeinfo(type)->self;
break;
default: c11__unreachable();
}
@ -247,45 +247,6 @@ bool pk_loadmethod(py_StackRef self, py_Name name) {
return false;
}
py_Ref py_tpfindmagic(py_Type t, py_Name name) {
// assert(py_ismagicname(name));
return py_tpfindname(t, name);
}
py_Ref py_tpfindname(py_Type t, py_Name name) {
py_TypeInfo* ti = pk__type_info(t);
do {
py_Ref res = py_getdict(&ti->self, name);
if(res) return res;
ti = ti->base_ti;
} while(ti);
return NULL;
}
py_Type py_tpbase(py_Type t) {
assert(t);
py_TypeInfo* ti = pk__type_info(t);
return ti->base;
}
PK_DEPRECATED py_Ref py_tpgetmagic(py_Type type, py_Name name) {
// assert(py_ismagicname(name));
py_TypeInfo* ti = pk__type_info(type);
py_Ref retval = py_getdict(&ti->self, name);
return retval != NULL ? retval : py_NIL();
}
py_Ref py_tpobject(py_Type type) {
assert(type);
return &pk__type_info(type)->self;
}
const char* py_tpname(py_Type type) {
if(!type) return "nil";
py_Name name = pk__type_info(type)->name;
return py_name2str(name);
}
bool py_tpcall(py_Type type, int argc, py_Ref argv) {
return py_call(py_tpobject(type), argc, argv);
}

View File

@ -16,11 +16,11 @@ py_Ref py_getmodule(const char* path) {
return BinTree__try_get(&vm->modules, (void*)path);
}
py_Ref py_getbuiltin(py_Name name) { return py_getdict(&pk_current_vm->builtins, name); }
py_Ref py_getbuiltin(py_Name name) { return py_getdict(pk_current_vm->builtins, name); }
py_Ref py_getglobal(py_Name name) { return py_getdict(&pk_current_vm->main, name); }
py_Ref py_getglobal(py_Name name) { return py_getdict(pk_current_vm->main, name); }
void py_setglobal(py_Name name, py_Ref val) { py_setdict(&pk_current_vm->main, name, val); }
void py_setglobal(py_Name name, py_Ref val) { py_setdict(pk_current_vm->main, name, val); }
py_Ref py_newmodule(const char* path) {
ManagedHeap* heap = &pk_current_vm->heap;
@ -608,7 +608,7 @@ static bool builtins_eval(int argc, py_Ref argv) {
static bool
pk_smartexec(const char* source, py_Ref module, enum py_CompileMode mode, va_list args) {
if(module == NULL) module = &pk_current_vm->main;
if(module == NULL) module = pk_current_vm->main;
pk_mappingproxy__namedict(py_pushtmp(), module); // globals
py_newdict(py_pushtmp()); // locals
bool ok = py_compile(source, "<string>", mode, true);
@ -716,8 +716,8 @@ static bool NotImplementedType__repr__(int argc, py_Ref argv) {
return true;
}
py_TValue pk_builtins__register() {
py_Ref builtins = py_newmodule("builtins");
py_GlobalRef pk_builtins__register() {
py_GlobalRef builtins = py_newmodule("builtins");
py_bindfunc(builtins, "exit", builtins_exit);
py_bindfunc(builtins, "input", builtins_input);
py_bindfunc(builtins, "repr", builtins_repr);
@ -760,7 +760,7 @@ py_TValue pk_builtins__register() {
py_setdict(py_tpobject(tp_ellipsis), __hash__, py_None());
py_bindmagic(tp_NotImplementedType, __repr__, NotImplementedType__repr__);
py_setdict(py_tpobject(tp_NotImplementedType), __hash__, py_None());
return *builtins;
return builtins;
}
void function__gc_mark(void* ud, c11_vector* p_stack) {
@ -843,7 +843,7 @@ static bool super__new__(int argc, py_Ref argv) {
if(callable->type == tp_function) {
Function* func = py_touserdata(callable);
if(func->clazz != NULL) {
class_arg = *(py_Type*)PyObject__userdata(func->clazz);
class_arg = ((py_TypeInfo*)PyObject__userdata(func->clazz))->index;
if(frame->co->nlocals > 0) { self_arg = &frame->locals[0]; }
}
}
@ -851,7 +851,7 @@ static bool super__new__(int argc, py_Ref argv) {
if(class_arg == 0 || self_arg == NULL) return RuntimeError("super(): no arguments");
if(self_arg->type == tp_type) {
// f(cls, ...)
class_arg = pk__type_info(class_arg)->base;
class_arg = pk_typeinfo(class_arg)->base;
if(class_arg == 0) return RuntimeError("super(): base class is invalid");
py_assign(py_retval(), py_tpobject(class_arg));
return true;
@ -868,7 +868,7 @@ static bool super__new__(int argc, py_Ref argv) {
return TypeError("super() takes 0 or 2 arguments");
}
class_arg = pk__type_info(class_arg)->base;
class_arg = pk_typeinfo(class_arg)->base;
if(class_arg == 0) return RuntimeError("super(): base class is invalid");
py_Type* p_class_arg = py_newobject(py_retval(), tp_super, 1, sizeof(py_Type));

View File

@ -4,8 +4,8 @@
bool pk__object_new(int argc, py_Ref argv) {
if(argc == 0) return TypeError("object.__new__(): not enough arguments");
py_Type cls = py_totype(py_arg(0));
py_TypeInfo* ti = pk__type_info(cls);
py_TypeInfo* ti = py_touserdata(argv);
py_Type cls = ti->index;
if(!ti->is_python) {
return TypeError("object.__new__(%t) is not safe, use %t.__new__() instead", cls, cls);
}
@ -72,7 +72,7 @@ static bool type__new__(int argc, py_Ref argv) {
static bool type__base__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
py_TypeInfo* ti = pk__type_info(py_totype(argv));
py_TypeInfo* ti = py_touserdata(argv);
if(ti->base) {
py_assign(py_retval(), &ti->base_ti->self);
} else {
@ -83,7 +83,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_TypeInfo* ti = py_touserdata(argv);
py_assign(py_retval(), py_name2ref(ti->name));
return true;
}
@ -100,7 +100,7 @@ static bool type__or__(int argc, py_Ref argv) {
static bool type__module__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
py_TypeInfo* ti = pk__type_info(py_totype(argv));
py_TypeInfo* ti = py_touserdata(argv);
if(py_isnil(ti->module)) {
py_newnone(py_retval());
} else {
@ -112,7 +112,7 @@ static bool type__module__(int argc, py_Ref argv) {
static bool type__annotations__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
py_TypeInfo* ti = pk__type_info(py_totype(argv));
py_TypeInfo* ti = py_touserdata(argv);
if(py_isnil(&ti->annotations)) {
py_newdict(py_retval());
} else {

View File

@ -52,7 +52,7 @@ int py_bool(py_Ref val) {
}
bool py_hash(py_Ref val, int64_t* out) {
py_TypeInfo* ti = pk__type_info(val->type);
py_TypeInfo* ti = pk_typeinfo(val->type);
do {
py_Ref slot_hash = py_getdict(&ti->self, __hash__);
if(slot_hash && py_isnone(slot_hash)) break;
@ -93,8 +93,8 @@ int py_next(py_Ref val) {
bool py_getattr(py_Ref self, py_Name name) {
// https://docs.python.org/3/howto/descriptor.html#invocation-from-an-instance
py_Type type = self->type;
py_Ref cls_var = py_tpfindname(type, name);
py_TypeInfo* ti = pk_typeinfo(self->type);
py_Ref cls_var = pk_tpfindname(ti, name);
if(cls_var) {
// handle descriptor
if(py_istype(cls_var, tp_property)) {
@ -111,8 +111,9 @@ bool py_getattr(py_Ref self, py_Name name) {
return true;
}
} else {
py_Type* inner_type = py_touserdata(self);
py_Ref res = py_tpfindname(*inner_type, name);
// self is a type object
py_TypeInfo* inner_type = py_touserdata(self);
py_Ref res = pk_tpfindname(inner_type, name);
if(res) {
if(py_istype(res, tp_staticmethod)) {
res = py_getslot(res, 0);
@ -145,7 +146,7 @@ bool py_getattr(py_Ref self, py_Name name) {
return true;
}
case tp_classmethod: {
py_newboundmethod(py_retval(), py_tpobject(type), py_getslot(cls_var, 0));
py_newboundmethod(py_retval(), &ti->self, py_getslot(cls_var, 0));
return true;
}
default: {
@ -156,7 +157,7 @@ bool py_getattr(py_Ref self, py_Name name) {
}
}
py_Ref fallback = py_tpfindmagic(type, __getattr__);
py_Ref fallback = pk_tpfindmagic(ti, __getattr__);
if(fallback) {
py_push(fallback);
py_push(self);

45
src/public/typecast.c Normal file
View File

@ -0,0 +1,45 @@
#include "pocketpy/objects/base.h"
#include "pocketpy/pocketpy.h"
#include "pocketpy/objects/object.h"
#include "pocketpy/interpreter/vm.h"
bool py_istype(py_Ref self, py_Type type) { return self->type == type; }
bool py_checktype(py_Ref self, py_Type type) {
if(self->type == type) return true;
return TypeError("expected '%t', got '%t'", type, self->type);
}
bool py_checkinstance(py_Ref self, py_Type type) {
if(py_isinstance(self, type)) return true;
return TypeError("expected '%t' or its subclass, got '%t'", type, self->type);
}
bool py_isinstance(py_Ref obj, py_Type type) { return py_issubclass(obj->type, type); }
bool py_issubclass(py_Type derived, py_Type base) {
assert(derived != 0 && base != 0);
py_TypeInfo* derived_ti = pk_typeinfo(derived);
py_TypeInfo* base_ti = pk_typeinfo(base);
do {
if(derived_ti == base_ti) return true;
derived_ti = derived_ti->base_ti;
} while(derived_ti);
return false;
}
py_Type py_typeof(py_Ref self) { return self->type; }
py_Type py_gettype(const char* module, py_Name name) {
py_Ref mod;
if(module != NULL) {
mod = py_getmodule(module);
if(!mod) return tp_nil;
} else {
mod = pk_current_vm->builtins;
}
py_Ref object = py_getdict(mod, name);
if(object && py_istype(object, tp_type)) return py_totype(object);
return tp_nil;
}