From d3d61dde0c5a6353f9d67abb8ae27c594365fa1b Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Mon, 8 Sep 2025 15:41:44 +0800 Subject: [PATCH] refactor code --- include/pocketpy/pocketpy.h | 734 +++++++++---------- src/{public => bindings}/py_array.c | 0 src/{public => bindings}/py_mappingproxy.c | 0 src/{public => bindings}/py_method.c | 0 src/{public => bindings}/py_number.c | 0 src/{public => bindings}/py_object.c | 0 src/{public => bindings}/py_property.c | 0 src/{public => bindings}/py_range.c | 0 src/{public => bindings}/py_str.c | 90 --- src/interpreter/ceval.c | 70 +- src/interpreter/frame.c | 41 -- src/interpreter/typeinfo.c | 63 +- src/{public/modules.c => modules/builtins.c} | 276 ------- src/modules/gc.c | 5 - src/modules/json.c | 6 - src/public/Bindings.c | 61 ++ src/public/{exec.c => CodeExecution.c} | 97 ++- src/public/{stack_ops.c => DictSlots.c} | 73 +- src/public/FrameOps.c | 43 ++ src/public/GlobalSetup.c | 161 ++++ src/public/Inspection.c | 28 + src/public/ModuleSystem.c | 191 +++++ src/public/{py_dict.c => PyDict.c} | 0 src/public/{py_exception.c => PyException.c} | 53 +- src/public/{py_list.c => PyList.c} | 0 src/public/{py_slice.c => PySlice.c} | 10 +- src/public/{py_tuple.c => PyTuple.c} | 0 src/public/{py_ops.c => PythonOps.c} | 90 ++- src/public/{internal.c => StackOps.c} | 189 ++--- src/public/TypeSystem.c | 104 +++ src/public/{cast.c => ValueCast.c} | 29 +- src/public/{values.c => ValueCreation.c} | 108 ++- src/public/typecast.c | 45 -- 33 files changed, 1296 insertions(+), 1271 deletions(-) rename src/{public => bindings}/py_array.c (100%) rename src/{public => bindings}/py_mappingproxy.c (100%) rename src/{public => bindings}/py_method.c (100%) rename src/{public => bindings}/py_number.c (100%) rename src/{public => bindings}/py_object.c (100%) rename src/{public => bindings}/py_property.c (100%) rename src/{public => bindings}/py_range.c (100%) rename src/{public => bindings}/py_str.c (88%) rename src/{public/modules.c => modules/builtins.c} (67%) create mode 100644 src/public/Bindings.c rename src/public/{exec.c => CodeExecution.c} (60%) rename src/public/{stack_ops.c => DictSlots.c} (56%) create mode 100644 src/public/FrameOps.c create mode 100644 src/public/GlobalSetup.c create mode 100644 src/public/Inspection.c create mode 100644 src/public/ModuleSystem.c rename src/public/{py_dict.c => PyDict.c} (100%) rename src/public/{py_exception.c => PyException.c} (98%) rename src/public/{py_list.c => PyList.c} (100%) rename src/public/{py_slice.c => PySlice.c} (91%) rename src/public/{py_tuple.c => PyTuple.c} (100%) rename src/public/{py_ops.c => PythonOps.c} (78%) rename src/public/{internal.c => StackOps.c} (54%) create mode 100644 src/public/TypeSystem.c rename src/public/{cast.c => ValueCast.c} (65%) rename src/public/{values.c => ValueCreation.c} (62%) delete mode 100644 src/public/typecast.c diff --git a/include/pocketpy/pocketpy.h b/include/pocketpy/pocketpy.h index ef57ef0d..02f618bf 100644 --- a/include/pocketpy/pocketpy.h +++ b/include/pocketpy/pocketpy.h @@ -123,14 +123,15 @@ PK_API void py_resetallvm(); PK_API void* py_getvmctx(); /// Set the current VM context. This is used for user-defined data. PK_API void py_setvmctx(void* ctx); +/// Setup the callbacks for the current VM. +PK_API py_Callbacks* py_callbacks(); + /// Set `sys.argv`. Used for storing command-line arguments. 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(); /// Wrapper for `PK_MALLOC(size)`. PK_API void* py_malloc(size_t size); @@ -139,18 +140,16 @@ PK_API void* py_realloc(void* ptr, size_t size); /// Wrapper for `PK_FREE(ptr)`. PK_API void py_free(void* ptr); -/// Begin the watchdog with `timeout` in milliseconds. -/// `PK_ENABLE_WATCHDOG` must be defined to `1` to use this feature. -/// You need to call `py_watchdog_end()` later. -/// If `timeout` is reached, `TimeoutError` will be raised. -PK_API void py_watchdog_begin(py_i64 timeout); -/// Reset the watchdog. -PK_API void py_watchdog_end(); +/// A shorthand for `True`. +PK_API py_GlobalRef py_True(); +/// A shorthand for `False`. +PK_API py_GlobalRef py_False(); +/// A shorthand for `None`. +PK_API py_GlobalRef py_None(); +/// A shorthand for `nil`. `nil` is not a valid python object. +PK_API py_GlobalRef py_NIL(); -/// Bind a compile-time function via "decl-based" style. -PK_API void py_macrobind(const char* sig, py_CFunction f); -/// Get a compile-time function by name. -PK_API py_ItemRef py_macroget(py_Name name); +/************* Frame Ops *************/ /// Get the current source location of the frame. PK_API const char* py_Frame_sourceloc(py_Frame* frame, int* lineno); @@ -162,6 +161,14 @@ PK_API void py_Frame_newlocals(py_Frame* frame, py_OutRef out); /// Returns `NULL` if not available. PK_API py_StackRef py_Frame_function(py_Frame* frame); +/************* Code Execution *************/ + +/// Compile a source string into a code object. +/// Use python's `exec()` or `eval()` to execute it. +PK_API bool py_compile(const char* source, + const char* filename, + enum py_CompileMode mode, + bool is_dynamic) PY_RAISE PY_RETURN; /// Run a source string. /// @param source source string. /// @param filename filename (for error messages). @@ -172,10 +179,8 @@ PK_API bool py_exec(const char* source, const char* filename, enum py_CompileMode mode, py_Ref module) PY_RAISE PY_RETURN; - /// Evaluate a source string. Equivalent to `py_exec(source, "", EVAL_MODE, module)`. PK_API bool py_eval(const char* source, py_Ref module) PY_RAISE PY_RETURN; - /// Run a source string with smart interpretation. /// Example: /// `py_newstr(py_r0(), "abc");` @@ -191,28 +196,7 @@ PK_API bool py_smartexec(const char* source, py_Ref module, ...) PY_RAISE PY_RET /// `// res will be 3`. PK_API bool py_smarteval(const char* source, py_Ref module, ...) PY_RAISE PY_RETURN; -/// Compile a source string into a code object. -/// Use python's `exec()` or `eval()` to execute it. -PK_API bool py_compile(const char* source, - const char* filename, - enum py_CompileMode mode, - bool is_dynamic) PY_RAISE PY_RETURN; - -/// Python equivalent to `globals()`. -PK_API void py_newglobals(py_OutRef); -/// Python equivalent to `locals()`. -PK_API void py_newlocals(py_OutRef); - -/************* Values Creation *************/ - -/// A shorthand for `True`. -PK_API py_GlobalRef py_True(); -/// A shorthand for `False`. -PK_API py_GlobalRef py_False(); -/// A shorthand for `None`. -PK_API py_GlobalRef py_None(); -/// A shorthand for `nil`. `nil` is not a valid python object. -PK_API py_GlobalRef py_NIL(); +/************* Value Creation *************/ /// Create an `int` object. PK_API void py_newint(py_OutRef, py_i64); @@ -241,19 +225,6 @@ PK_API void py_newellipsis(py_OutRef); /// Create a `nil` object. `nil` is an invalid representation of an object. /// Don't use it unless you know what you are doing. PK_API void py_newnil(py_OutRef); -/// Create a `tuple` with `n` UNINITIALIZED elements. -/// You should initialize all elements before using it. -PK_API py_ObjectRef py_newtuple(py_OutRef, int n); -/// Create an empty `list`. -PK_API void py_newlist(py_OutRef); -/// Create a `list` with `n` UNINITIALIZED elements. -/// You should initialize all elements before using it. -PK_API void py_newlistn(py_OutRef, int n); -/// Create an empty `dict`. -PK_API void py_newdict(py_OutRef); -/// Create an UNINITIALIZED `slice` object. -/// You should use `py_setslot()` to set `start`, `stop`, and `step`. -PK_API void py_newslice(py_OutRef); /// Create a `nativefunc` object. PK_API void py_newnativefunc(py_OutRef, py_CFunction); /// Create a `function` object. @@ -264,8 +235,15 @@ PK_API py_Name py_newfunction(py_OutRef out, int slots); /// Create a `boundmethod` object. PK_API void py_newboundmethod(py_OutRef out, py_Ref self, py_Ref func); +/// Create a new object. +/// @param out output reference. +/// @param type type of the object. +/// @param slots number of slots. Use `-1` to create a `__dict__`. +/// @param udsize size of your userdata. +/// @return pointer to the userdata. +PK_API void* py_newobject(py_OutRef out, py_Type type, int slots, int udsize); -/************* Name Conversions *************/ +/************* Name Conversion *************/ /// Convert a null-terminated string to a name. PK_API py_Name py_name(const char*); @@ -278,184 +256,6 @@ PK_API py_Name py_namev(c11_sv); /// Convert a name to a `c11_sv`. PK_API c11_sv py_name2sv(py_Name); -/************* Meta Operations *************/ - -/// Create a new type. -/// @param name name of the type. -/// @param base base type. -/// @param module module where the type is defined. Use `NULL` for built-in types. -/// @param dtor destructor function. Use `NULL` if not needed. -PK_API py_Type py_newtype(const char* name, py_Type base, const py_GlobalRef module, py_Dtor dtor); - -/// Create a new object. -/// @param out output reference. -/// @param type type of the object. -/// @param slots number of slots. Use `-1` to create a `__dict__`. -/// @param udsize size of your userdata. -/// @return pointer to the userdata. -PK_API void* py_newobject(py_OutRef out, py_Type type, int slots, int udsize); - -/************* Type Cast *************/ - -/// Convert an `int` object in python to `int64_t`. -PK_API py_i64 py_toint(py_Ref); -/// Get the address of the trivial value object. -PK_API void* py_totrivial(py_Ref); -/// Convert a `float` object in python to `double`. -PK_API py_f64 py_tofloat(py_Ref); -/// Cast a `int` or `float` object in python to `double`. -/// If successful, return true and set the value to `out`. -/// Otherwise, return false and raise `TypeError`. -PK_API bool py_castfloat(py_Ref, py_f64* out) PY_RAISE; -/// 32-bit version of `py_castfloat`. -PK_API bool py_castfloat32(py_Ref, float* out) PY_RAISE; -/// Cast a `int` object in python to `int64_t`. -PK_API bool py_castint(py_Ref, py_i64* out) PY_RAISE; -/// Convert a `bool` object in python to `bool`. -PK_API bool py_tobool(py_Ref); -/// Convert a `type` object in python to `py_Type`. -PK_API py_Type py_totype(py_Ref); -/// Convert a `str` object in python to null-terminated string. -PK_API const char* py_tostr(py_Ref); -/// Convert a `str` object in python to char array. -PK_API const char* py_tostrn(py_Ref, int* size); -/// Convert a `str` object in python to `c11_sv`. -PK_API c11_sv py_tosv(py_Ref); -/// Convert a `bytes` object in python to char array. -PK_API unsigned char* py_tobytes(py_Ref, int* size); -/// Resize a `bytes` object. It can only be resized down. -PK_API void py_bytes_resize(py_Ref, int size); -/// Convert a user-defined object to its userdata. -PK_API void* py_touserdata(py_Ref); - -#define py_isint(self) py_istype(self, tp_int) -#define py_isfloat(self) py_istype(self, tp_float) -#define py_isbool(self) py_istype(self, tp_bool) -#define py_isstr(self) py_istype(self, tp_str) -#define py_islist(self) py_istype(self, tp_list) -#define py_istuple(self) py_istype(self, tp_tuple) -#define py_isdict(self) py_istype(self, tp_dict) - -#define py_isnil(self) py_istype(self, 0) -#define py_isnone(self) py_istype(self, tp_NoneType) - -/// Get the type of the object. -PK_API py_Type py_typeof(py_Ref self); -/// Get type by module and name. e.g. `py_gettype("time", py_name("struct_time"))`. -/// Return `0` if not found. -PK_API py_Type py_gettype(const char* module, py_Name name); -/// Check if the object is exactly the given type. -PK_API bool py_istype(py_Ref, py_Type); -/// Check if the object is an instance of the given type. -PK_API bool py_isinstance(py_Ref obj, py_Type type); -/// Check if the derived type is a subclass of the base type. -PK_API bool py_issubclass(py_Type derived, py_Type base); - -/// Get the magic method from the given type only. -/// Return `nil` if not found. -PK_API PK_DEPRECATED py_GlobalRef py_tpgetmagic(py_Type type, py_Name name); -/// Search the magic method from the given type to the base type. -/// Return `NULL` if not found. -PK_API py_GlobalRef py_tpfindmagic(py_Type, py_Name name); -/// Search the name from the given type to the base type. -/// Return `NULL` if not found. -PK_API py_ItemRef py_tpfindname(py_Type, py_Name name); -/// Get the base type of the given type. -PK_API py_Type py_tpbase(py_Type type); - -/// Get the type object of the given type. -PK_API py_GlobalRef py_tpobject(py_Type type); -/// Get the type name. -PK_API const char* py_tpname(py_Type type); -/// Call a type to create a new instance. -PK_API bool py_tpcall(py_Type type, int argc, py_Ref argv) PY_RAISE PY_RETURN; -/// Disable the type for subclassing. -PK_API void py_tpsetfinal(py_Type type); -/// Set attribute hooks for the given type. -PK_API void py_tphookattributes(py_Type type, - bool (*getattribute)(py_Ref self, py_Name name) PY_RAISE PY_RETURN, - bool (*setattribute)(py_Ref self, py_Name name, py_Ref val) - PY_RAISE PY_RETURN, - bool (*delattribute)(py_Ref self, py_Name name) PY_RAISE, - bool (*getunboundmethod)(py_Ref self, py_Name name) PY_RETURN); - -/// Check if the object is an instance of the given type exactly. -/// Raise `TypeError` if the check fails. -PK_API bool py_checktype(py_Ref self, py_Type type) PY_RAISE; - -/// Check if the object is an instance of the given type or its subclass. -/// Raise `TypeError` if the check fails. -PK_API bool py_checkinstance(py_Ref self, py_Type type) PY_RAISE; - -#define py_checkint(self) py_checktype(self, tp_int) -#define py_checkfloat(self) py_checktype(self, tp_float) -#define py_checkbool(self) py_checktype(self, tp_bool) -#define py_checkstr(self) py_checktype(self, tp_str) - -/************* References *************/ - -/// Get the i-th register. -/// All registers are located in a contiguous memory. -PK_API py_GlobalRef py_getreg(int i); -/// Set the i-th register. -PK_API void py_setreg(int i, py_Ref val); - -#define py_r0() py_getreg(0) -#define py_r1() py_getreg(1) -#define py_r2() py_getreg(2) -#define py_r3() py_getreg(3) -#define py_r4() py_getreg(4) -#define py_r5() py_getreg(5) -#define py_r6() py_getreg(6) -#define py_r7() py_getreg(7) - -/// Get variable in the `__main__` module. -PK_API py_ItemRef py_getglobal(py_Name name); -/// Set variable in the `__main__` module. -PK_API void py_setglobal(py_Name name, py_Ref val); -/// Get variable in the `builtins` module. -PK_API py_ItemRef py_getbuiltin(py_Name name); - -/// Get the last return value. -/// Please note that `py_retval()` cannot be used as input argument. -PK_API py_GlobalRef py_retval(); - -/// Get an item from the object's `__dict__`. -/// Return `NULL` if not found. -PK_API py_ItemRef py_getdict(py_Ref self, py_Name name); -/// Set an item to the object's `__dict__`. -PK_API void py_setdict(py_Ref self, py_Name name, py_Ref val); -/// Delete an item from the object's `__dict__`. -/// Return `true` if the deletion is successful. -PK_API bool py_deldict(py_Ref self, py_Name name); -/// Prepare an insertion to the object's `__dict__`. -PK_API py_ItemRef py_emplacedict(py_Ref self, py_Name name); -/// Apply a function to all items in the object's `__dict__`. -/// Return `true` if the function is successful for all items. -/// NOTE: Be careful if `f` modifies the object's `__dict__`. -PK_API bool - py_applydict(py_Ref self, bool (*f)(py_Name name, py_Ref val, void* ctx), void* ctx) PY_RAISE; -/// Clear the object's `__dict__`. This function is dangerous. -PK_API void py_cleardict(py_Ref self); - -/// Get the i-th slot of the object. -/// The object must have slots and `i` must be in valid range. -PK_API py_ObjectRef py_getslot(py_Ref self, int i); -/// Set the i-th slot of the object. -PK_API void py_setslot(py_Ref self, int i, py_Ref val); - -/************* Inspection *************/ - -/// Get the current `function` object on the stack. -/// Return `NULL` if not available. -/// NOTE: This function should be placed at the beginning of your decl-based bindings. -PK_API py_StackRef py_inspect_currentfunction(); -/// Get the current `module` object where the code is executed. -/// Return `NULL` if not available. -PK_API py_GlobalRef py_inspect_currentmodule(); -/// Get the current frame object. -/// Return `NULL` if not available. -PK_API py_Frame* py_inspect_currentframe(); /************* Bindings *************/ /// Bind a function to the object via "decl-based" style. @@ -487,6 +287,225 @@ PK_API void py_bindproperty(py_Type type, const char* name, py_CFunction getter, py_CFunction setter); /// Bind a magic method to type. PK_API void py_bindmagic(py_Type type, py_Name name, py_CFunction f); +/// Bind a compile-time function via "decl-based" style. +PK_API void py_macrobind(const char* sig, py_CFunction f); +/// Get a compile-time function by name. +PK_API py_ItemRef py_macroget(py_Name name); + +/************* Value Cast *************/ + +/// Convert an `int` object in python to `int64_t`. +PK_API py_i64 py_toint(py_Ref); +/// Get the address of the trivial value object (16 bytes). +PK_API void* py_totrivial(py_Ref); +/// Convert a `float` object in python to `double`. +PK_API py_f64 py_tofloat(py_Ref); +/// Cast a `int` or `float` object in python to `double`. +/// If successful, return true and set the value to `out`. +/// Otherwise, return false and raise `TypeError`. +PK_API bool py_castfloat(py_Ref, py_f64* out) PY_RAISE; +/// 32-bit version of `py_castfloat`. +PK_API bool py_castfloat32(py_Ref, float* out) PY_RAISE; +/// Cast a `int` object in python to `int64_t`. +PK_API bool py_castint(py_Ref, py_i64* out) PY_RAISE; +/// Convert a `bool` object in python to `bool`. +PK_API bool py_tobool(py_Ref); +/// Convert a `type` object in python to `py_Type`. +PK_API py_Type py_totype(py_Ref); +/// Convert a user-defined object to its userdata. +PK_API void* py_touserdata(py_Ref); +/// Convert a `str` object in python to null-terminated string. +PK_API const char* py_tostr(py_Ref); +/// Convert a `str` object in python to char array. +PK_API const char* py_tostrn(py_Ref, int* size); +/// Convert a `str` object in python to `c11_sv`. +PK_API c11_sv py_tosv(py_Ref); +/// Convert a `bytes` object in python to char array. +PK_API unsigned char* py_tobytes(py_Ref, int* size); +/// Resize a `bytes` object. It can only be resized down. +PK_API void py_bytes_resize(py_Ref, int size); + +/************* Type System *************/ + +/// Create a new type. +/// @param name name of the type. +/// @param base base type. +/// @param module module where the type is defined. Use `NULL` for built-in types. +/// @param dtor destructor function. Use `NULL` if not needed. +PK_API py_Type py_newtype(const char* name, py_Type base, const py_GlobalRef module, py_Dtor dtor); +/// Check if the object is exactly the given type. +PK_API bool py_istype(py_Ref, py_Type); +/// Get the type of the object. +PK_API py_Type py_typeof(py_Ref self); +/// Check if the object is an instance of the given type. +PK_API bool py_isinstance(py_Ref obj, py_Type type); +/// Check if the derived type is a subclass of the base type. +PK_API bool py_issubclass(py_Type derived, py_Type base); +/// Get type by module and name. e.g. `py_gettype("time", py_name("struct_time"))`. +/// Return `0` if not found. +PK_API py_Type py_gettype(const char* module, py_Name name); +/// Check if the object is an instance of the given type exactly. +/// Raise `TypeError` if the check fails. +PK_API bool py_checktype(py_Ref self, py_Type type) PY_RAISE; +/// Check if the object is an instance of the given type or its subclass. +/// Raise `TypeError` if the check fails. +PK_API bool py_checkinstance(py_Ref self, py_Type type) PY_RAISE; +/// Get the magic method from the given type only. +/// Return `nil` if not found. +PK_API PK_DEPRECATED py_GlobalRef py_tpgetmagic(py_Type type, py_Name name); +/// Search the magic method from the given type to the base type. +/// Return `NULL` if not found. +PK_API py_GlobalRef py_tpfindmagic(py_Type, py_Name name); +/// Search the name from the given type to the base type. +/// Return `NULL` if not found. +PK_API py_ItemRef py_tpfindname(py_Type, py_Name name); +/// Get the base type of the given type. +PK_API py_Type py_tpbase(py_Type type); +/// Get the type object of the given type. +PK_API py_GlobalRef py_tpobject(py_Type type); +/// Get the type name. +PK_API const char* py_tpname(py_Type type); +/// Disable the type for subclassing. +PK_API void py_tpsetfinal(py_Type type); +/// Set attribute hooks for the given type. +PK_API void py_tphookattributes(py_Type type, + bool (*getattribute)(py_Ref self, py_Name name) PY_RAISE PY_RETURN, + bool (*setattribute)(py_Ref self, py_Name name, py_Ref val) + PY_RAISE PY_RETURN, + bool (*delattribute)(py_Ref self, py_Name name) PY_RAISE, + bool (*getunboundmethod)(py_Ref self, py_Name name) PY_RETURN); + +#define py_isint(self) py_istype(self, tp_int) +#define py_isfloat(self) py_istype(self, tp_float) +#define py_isbool(self) py_istype(self, tp_bool) +#define py_isstr(self) py_istype(self, tp_str) +#define py_islist(self) py_istype(self, tp_list) +#define py_istuple(self) py_istype(self, tp_tuple) +#define py_isdict(self) py_istype(self, tp_dict) +#define py_isnil(self) py_istype(self, 0) +#define py_isnone(self) py_istype(self, tp_NoneType) + +#define py_checkint(self) py_checktype(self, tp_int) +#define py_checkfloat(self) py_checktype(self, tp_float) +#define py_checkbool(self) py_checktype(self, tp_bool) +#define py_checkstr(self) py_checktype(self, tp_str) + +/************* Inspection *************/ + +/// Get the current `function` object on the stack. +/// Return `NULL` if not available. +/// NOTE: This function should be placed at the beginning of your decl-based bindings. +PK_API py_StackRef py_inspect_currentfunction(); +/// Get the current `module` object where the code is executed. +/// Return `NULL` if not available. +PK_API py_GlobalRef py_inspect_currentmodule(); +/// Get the current frame object. +/// Return `NULL` if not available. +PK_API py_Frame* py_inspect_currentframe(); +/// Python equivalent to `globals()`. +PK_API void py_newglobals(py_OutRef); +/// Python equivalent to `locals()`. +PK_API void py_newlocals(py_OutRef); + +/************* Dict & Slots *************/ + +/// Get the i-th register. +/// All registers are located in a contiguous memory. +PK_API py_GlobalRef py_getreg(int i); +/// Set the i-th register. +PK_API void py_setreg(int i, py_Ref val); +/// Get the last return value. +/// Please note that `py_retval()` cannot be used as input argument. +PK_API py_GlobalRef py_retval(); + +#define py_r0() py_getreg(0) +#define py_r1() py_getreg(1) +#define py_r2() py_getreg(2) +#define py_r3() py_getreg(3) +#define py_r4() py_getreg(4) +#define py_r5() py_getreg(5) +#define py_r6() py_getreg(6) +#define py_r7() py_getreg(7) + +/// Get an item from the object's `__dict__`. +/// Return `NULL` if not found. +PK_API py_ItemRef py_getdict(py_Ref self, py_Name name); +/// Set an item to the object's `__dict__`. +PK_API void py_setdict(py_Ref self, py_Name name, py_Ref val); +/// Delete an item from the object's `__dict__`. +/// Return `true` if the deletion is successful. +PK_API bool py_deldict(py_Ref self, py_Name name); +/// Prepare an insertion to the object's `__dict__`. +PK_API py_ItemRef py_emplacedict(py_Ref self, py_Name name); +/// Apply a function to all items in the object's `__dict__`. +/// Return `true` if the function is successful for all items. +/// NOTE: Be careful if `f` modifies the object's `__dict__`. +PK_API bool + py_applydict(py_Ref self, bool (*f)(py_Name name, py_Ref val, void* ctx), void* ctx) PY_RAISE; +/// Clear the object's `__dict__`. This function is dangerous. +PK_API void py_cleardict(py_Ref self); +/// Get the i-th slot of the object. +/// The object must have slots and `i` must be in valid range. +PK_API py_ObjectRef py_getslot(py_Ref self, int i); +/// Set the i-th slot of the object. +PK_API void py_setslot(py_Ref self, int i, py_Ref val); +/// Get variable in the `builtins` module. +PK_API py_ItemRef py_getbuiltin(py_Name name); +/// Get variable in the `__main__` module. +PK_API py_ItemRef py_getglobal(py_Name name); +/// Set variable in the `__main__` module. +PK_API void py_setglobal(py_Name name, py_Ref val); + +/************* Stack Ops *************/ + +/// Get the i-th object from the top of the stack. +/// `i` should be negative, e.g. (-1) means TOS. +PK_API py_StackRef py_peek(int i); +/// Push the object to the stack. +PK_API void py_push(py_Ref src); +/// Push a `nil` object to the stack. +PK_API void py_pushnil(); +/// Push a `None` object to the stack. +PK_API void py_pushnone(); +/// Push a `py_Name` to the stack. This is used for keyword arguments. +PK_API void py_pushname(py_Name name); +/// Pop an object from the stack. +PK_API void py_pop(); +/// Shrink the stack by n. +PK_API void py_shrink(int n); +/// Get a temporary variable from the stack. +PK_API py_StackRef py_pushtmp(); +/// Get the unbound method of the object. +/// Assume the object is located at the top of the stack. +/// If return true: `[self] -> [unbound, self]`. +/// If return false: `[self] -> [self]` (no change). +PK_API bool py_pushmethod(py_Name name); +/// Evaluate an expression and push the result to the stack. +/// This function is used for testing. +PK_API bool py_pusheval(const char* expr, py_GlobalRef module) PY_RAISE; +/// Call a callable object via pocketpy's calling convention. +/// You need to prepare the stack using the following format: +/// `callable, self/nil, arg1, arg2, ..., k1, v1, k2, v2, ...`. +/// `argc` is the number of positional arguments excluding `self`. +/// `kwargc` is the number of keyword arguments. +/// The result will be set to `py_retval()`. +/// The stack size will be reduced by `2 + argc + kwargc * 2`. +PK_API bool py_vectorcall(uint16_t argc, uint16_t kwargc) PY_RAISE PY_RETURN; +/// Call a function. +/// It prepares the stack and then performs a `vectorcall(argc, 0, false)`. +/// The result will be set to `py_retval()`. +/// The stack remains unchanged if successful. +PK_API bool py_call(py_Ref f, int argc, py_Ref argv) PY_RAISE PY_RETURN; +/// Call a type to create a new instance. +PK_API bool py_tpcall(py_Type type, int argc, py_Ref argv) PY_RAISE PY_RETURN; + +#ifndef NDEBUG +/// Call a `py_CFunction` in a safe way. +/// This function does extra checks to help you debug `py_CFunction`. +PK_API bool py_callcfunc(py_CFunction f, int argc, py_Ref argv) PY_RAISE PY_RETURN; +#else +#define py_callcfunc(f, argc, argv) (f((argc), (argv))) +#endif #define PY_CHECK_ARGC(n) \ if(argc != n) return TypeError("expected %d arguments, got %d", n, argc) @@ -498,25 +517,13 @@ PK_API void py_bindmagic(py_Type type, py_Name name, py_CFunction f); #define py_arg(i) (&argv[i]) #define py_assign(dst, src) *(dst) = *(src) -/************* Python Equivalents *************/ - -/// Python equivalent to `getattr(self, name)`. -PK_API bool py_getattr(py_Ref self, py_Name name) PY_RAISE PY_RETURN; -/// Python equivalent to `setattr(self, name, val)`. -PK_API bool py_setattr(py_Ref self, py_Name name, py_Ref val) PY_RAISE; -/// Python equivalent to `delattr(self, name)`. -PK_API bool py_delattr(py_Ref self, py_Name name) PY_RAISE; -/// Python equivalent to `self[key]`. -PK_API bool py_getitem(py_Ref self, py_Ref key) PY_RAISE PY_RETURN; -/// Python equivalent to `self[key] = val`. -PK_API bool py_setitem(py_Ref self, py_Ref key, py_Ref val) PY_RAISE; -/// Python equivalent to `del self[key]`. -PK_API bool py_delitem(py_Ref self, py_Ref key) PY_RAISE; - /// Perform a binary operation. /// The result will be set to `py_retval()`. /// The stack remains unchanged after the operation. PK_API bool py_binaryop(py_Ref lhs, py_Ref rhs, py_Name op, py_Name rop) PY_RAISE PY_RETURN; + +/************* Python Ops *************/ + /// lhs + rhs PK_API bool py_binaryadd(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN; /// lhs - rhs @@ -543,68 +550,74 @@ PK_API bool py_binaryor(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN; PK_API bool py_binaryxor(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN; /// lhs @ rhs PK_API bool py_binarymatmul(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN; +/// lhs == rhs +PK_API bool py_eq(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN; +/// lhs != rhs +PK_API bool py_ne(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN; +/// lhs < rhs +PK_API bool py_lt(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN; +/// lhs <= rhs +PK_API bool py_le(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN; +/// lhs > rhs +PK_API bool py_gt(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN; +/// lhs >= rhs +PK_API bool py_ge(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN; -/************* Stack Operations *************/ +/// Python equivalent to `lhs is rhs`. +PK_API bool py_isidentical(py_Ref, py_Ref); +/// Python equivalent to `bool(val)`. +/// 1: true, 0: false, -1: error +PK_API int py_bool(py_Ref val) PY_RAISE; +/// Compare two objects. +/// 1: lhs == rhs, 0: lhs != rhs, -1: error +PK_API int py_equal(py_Ref lhs, py_Ref rhs) PY_RAISE; +/// Compare two objects. +/// 1: lhs < rhs, 0: lhs >= rhs, -1: error +PK_API int py_less(py_Ref lhs, py_Ref rhs) PY_RAISE; +/// Python equivalent to `callable(val)`. +PK_API bool py_callable(py_Ref val); +/// Get the hash value of the object. +PK_API bool py_hash(py_Ref, py_i64* out) PY_RAISE; +/// Get the iterator of the object. +PK_API bool py_iter(py_Ref) PY_RAISE PY_RETURN; +/// Get the next element from the iterator. +/// 1: success, 0: StopIteration, -1: error +PK_API int py_next(py_Ref) PY_RAISE PY_RETURN; +/// Python equivalent to `str(val)`. +PK_API bool py_str(py_Ref val) PY_RAISE PY_RETURN; +/// Python equivalent to `repr(val)`. +PK_API bool py_repr(py_Ref val) PY_RAISE PY_RETURN; +/// Python equivalent to `len(val)`. +PK_API bool py_len(py_Ref val) PY_RAISE PY_RETURN; -/// Get the i-th object from the top of the stack. -/// `i` should be negative, e.g. (-1) means TOS. -PK_API py_StackRef py_peek(int i); -/// Push the object to the stack. -PK_API void py_push(py_Ref src); -/// Push a `nil` object to the stack. -PK_API void py_pushnil(); -/// Push a `None` object to the stack. -PK_API void py_pushnone(); -/// Push a `py_Name` to the stack. This is used for keyword arguments. -PK_API void py_pushname(py_Name name); -/// Pop an object from the stack. -PK_API void py_pop(); -/// Shrink the stack by n. -PK_API void py_shrink(int n); -/// Get a temporary variable from the stack. -PK_API py_StackRef py_pushtmp(); -/// Get the unbound method of the object. -/// Assume the object is located at the top of the stack. -/// If return true: `[self] -> [unbound, self]`. -/// If return false: `[self] -> [self]` (no change). -PK_API bool py_pushmethod(py_Name name); -/// Call a callable object via pocketpy's calling convention. -/// You need to prepare the stack using the following format: -/// `callable, self/nil, arg1, arg2, ..., k1, v1, k2, v2, ...`. -/// `argc` is the number of positional arguments excluding `self`. -/// `kwargc` is the number of keyword arguments. -/// The result will be set to `py_retval()`. -/// The stack size will be reduced by `2 + argc + kwargc * 2`. -PK_API bool py_vectorcall(uint16_t argc, uint16_t kwargc) PY_RAISE PY_RETURN; -/// Evaluate an expression and push the result to the stack. -/// This function is used for testing. -PK_API bool py_pusheval(const char* expr, py_GlobalRef module) PY_RAISE; +/// Python equivalent to `getattr(self, name)`. +PK_API bool py_getattr(py_Ref self, py_Name name) PY_RAISE PY_RETURN; +/// Python equivalent to `setattr(self, name, val)`. +PK_API bool py_setattr(py_Ref self, py_Name name, py_Ref val) PY_RAISE; +/// Python equivalent to `delattr(self, name)`. +PK_API bool py_delattr(py_Ref self, py_Name name) PY_RAISE; +/// Python equivalent to `self[key]`. +PK_API bool py_getitem(py_Ref self, py_Ref key) PY_RAISE PY_RETURN; +/// Python equivalent to `self[key] = val`. +PK_API bool py_setitem(py_Ref self, py_Ref key, py_Ref val) PY_RAISE; +/// Python equivalent to `del self[key]`. +PK_API bool py_delitem(py_Ref self, py_Ref key) PY_RAISE; -/************* Modules *************/ +/************* Module System *************/ -/// Create a new module. -PK_API py_GlobalRef py_newmodule(const char* path); /// Get a module by path. PK_API py_GlobalRef py_getmodule(const char* path); +/// Create a new module. +PK_API py_GlobalRef py_newmodule(const char* path); /// Reload an existing module. PK_API bool py_importlib_reload(py_Ref module) PY_RAISE PY_RETURN; - /// Import a module. /// The result will be set to `py_retval()`. /// -1: error, 0: not found, 1: success PK_API int py_import(const char* path) PY_RAISE PY_RETURN; -/************* Errors *************/ +/************* PyException *************/ -/// Raise an exception by type and message. Always return false. -PK_API bool py_exception(py_Type type, const char* fmt, ...) PY_RAISE; -/// Raise an exception object. Always return false. -PK_API bool py_raise(py_Ref) PY_RAISE; -/// Print the unhandled exception. -PK_API void py_printexc(); -/// Format the unhandled exception and return a null-terminated string. -/// The returned string should be freed by the caller. -PK_API char* py_formatexc(); /// Check if there is an unhandled exception. PK_API bool py_checkexc(); /// Check if the unhandled exception is an instance of the given type. @@ -613,6 +626,15 @@ PK_API bool py_matchexc(py_Type type) PY_RETURN; /// Clear the unhandled exception. /// @param p0 the unwinding point. Use `NULL` if not needed. PK_API void py_clearexc(py_StackRef p0); +/// Print the unhandled exception. +PK_API void py_printexc(); +/// Format the unhandled exception and return a null-terminated string. +/// The returned string should be freed by the caller. +PK_API char* py_formatexc(); +/// Raise an exception by type and message. Always return false. +PK_API bool py_exception(py_Type type, const char* fmt, ...) PY_RAISE; +/// Raise an exception object. Always return false. +PK_API bool py_raise(py_Ref) PY_RAISE; #define NameError(n) py_exception(tp_NameError, "name '%n' is not defined", (n)) #define TypeError(...) py_exception(tp_TypeError, __VA_ARGS__) @@ -630,100 +652,40 @@ PK_API void py_clearexc(py_StackRef p0); "cannot access local variable '%n' where it is not associated with a value", \ (n)) -PK_API bool StopIteration() PY_RAISE; PK_API bool KeyError(py_Ref key) PY_RAISE; +PK_API bool StopIteration() PY_RAISE; -/************* Operators *************/ +/************* Debugger *************/ -/// Python equivalent to `bool(val)`. -/// 1: true, 0: false, -1: error -PK_API int py_bool(py_Ref val) PY_RAISE; -/// Compare two objects. -/// 1: lhs == rhs, 0: lhs != rhs, -1: error -PK_API int py_equal(py_Ref lhs, py_Ref rhs) PY_RAISE; -/// Compare two objects. -/// 1: lhs < rhs, 0: lhs >= rhs, -1: error -PK_API int py_less(py_Ref lhs, py_Ref rhs) PY_RAISE; - -/// lhs == rhs -PK_API bool py_eq(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN; -/// lhs != rhs -PK_API bool py_ne(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN; -/// lhs < rhs -PK_API bool py_lt(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN; -/// lhs <= rhs -PK_API bool py_le(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN; -/// lhs > rhs -PK_API bool py_gt(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN; -/// lhs >= rhs -PK_API bool py_ge(py_Ref lhs, py_Ref rhs) PY_RAISE PY_RETURN; - -/// Python equivalent to `callable(val)`. -PK_API bool py_callable(py_Ref val); -/// Get the hash value of the object. -PK_API bool py_hash(py_Ref, py_i64* out) PY_RAISE; -/// Get the iterator of the object. -PK_API bool py_iter(py_Ref) PY_RAISE PY_RETURN; -/// Get the next element from the iterator. -/// 1: success, 0: StopIteration, -1: error -PK_API int py_next(py_Ref) PY_RAISE PY_RETURN; -/// Python equivalent to `lhs is rhs`. -PK_API bool py_isidentical(py_Ref, py_Ref); -/// Call a function. -/// It prepares the stack and then performs a `vectorcall(argc, 0, false)`. -/// The result will be set to `py_retval()`. -/// The stack remains unchanged if successful. -PK_API bool py_call(py_Ref f, int argc, py_Ref argv) PY_RAISE PY_RETURN; - -#ifndef NDEBUG -/// Call a `py_CFunction` in a safe way. -/// This function does extra checks to help you debug `py_CFunction`. -PK_API bool py_callcfunc(py_CFunction f, int argc, py_Ref argv) PY_RAISE PY_RETURN; -#else -#define py_callcfunc(f, argc, argv) (f((argc), (argv))) -#endif - -/// Python equivalent to `str(val)`. -PK_API bool py_str(py_Ref val) PY_RAISE PY_RETURN; -/// Python equivalent to `repr(val)`. -PK_API bool py_repr(py_Ref val) PY_RAISE PY_RETURN; -/// Python equivalent to `len(val)`. -PK_API bool py_len(py_Ref val) PY_RAISE PY_RETURN; -/// Python equivalent to `json.dumps(val)`. -PK_API bool py_json_dumps(py_Ref val, int indent) PY_RAISE PY_RETURN; -/// Python equivalent to `json.loads(val)`. -PK_API bool py_json_loads(const char* source) PY_RAISE PY_RETURN; -/// Python equivalent to `pickle.dumps(val)`. -PK_API bool py_pickle_dumps(py_Ref val) PY_RAISE PY_RETURN; -/// Python equivalent to `pickle.loads(val)`. -PK_API bool py_pickle_loads(const unsigned char* data, int size) PY_RAISE PY_RETURN; - -/************* Profiler *************/ -PK_API void py_profiler_begin(); -PK_API void py_profiler_end(); -PK_API void py_profiler_reset(); -PK_API char* py_profiler_report(); - -/************* DAP *************/ #if PK_ENABLE_OS PK_API void py_debugger_waitforattach(const char* hostname, unsigned short port); PK_API bool py_debugger_isattached(); PK_API void py_debugger_exceptionbreakpoint(py_Ref exc); -PK_API void py_debugger_exit(int exitCode); +PK_API void py_debugger_exit(int code); #else #define py_debugger_waitforattach(hostname, port) #define py_debugger_isattached() (false) #define py_debugger_exceptionbreakpoint(exc) -#define py_debugger_exit(exitCode) +#define py_debugger_exit(code) #endif -/************* Unchecked Functions *************/ +/************* PyTuple *************/ +/// Create a `tuple` with `n` UNINITIALIZED elements. +/// You should initialize all elements before using it. +PK_API py_ObjectRef py_newtuple(py_OutRef, int n); PK_API py_ObjectRef py_tuple_data(py_Ref self); PK_API py_ObjectRef py_tuple_getitem(py_Ref self, int i); PK_API void py_tuple_setitem(py_Ref self, int i, py_Ref val); PK_API int py_tuple_len(py_Ref self); +/************* PyList *************/ + +/// Create an empty `list`. +PK_API void py_newlist(py_OutRef); +/// Create a `list` with `n` UNINITIALIZED elements. +/// You should initialize all elements before using it. +PK_API void py_newlistn(py_OutRef, int n); PK_API py_ItemRef py_list_data(py_Ref self); PK_API py_ItemRef py_list_getitem(py_Ref self, int i); PK_API void py_list_setitem(py_Ref self, int i, py_Ref val); @@ -735,6 +697,10 @@ PK_API py_ItemRef py_list_emplace(py_Ref self); PK_API void py_list_clear(py_Ref self); PK_API void py_list_insert(py_Ref self, int i, py_Ref val); +/************* PyDict *************/ + +/// Create an empty `dict`. +PK_API void py_newdict(py_OutRef); /// -1: error, 0: not found, 1: found PK_API int py_dict_getitem(py_Ref self, py_Ref key) PY_RAISE PY_RETURN; /// true: success, false: error @@ -761,6 +727,14 @@ PK_API bool /// noexcept PK_API int py_dict_len(py_Ref self); +/************* PySlice *************/ + +/// Create an UNINITIALIZED `slice` object. +/// You should use `py_setslot()` to set `start`, `stop`, and `step`. +PK_API py_ObjectRef py_newslice(py_OutRef); +/// Create a `slice` object from 3 integers. +PK_API void py_newsliceint(py_OutRef out, py_i64 start, py_i64 stop, py_i64 step); + /************* random module *************/ PK_API void py_newRandom(py_OutRef out); PK_API void py_Random_seed(py_Ref self, py_i64 seed); @@ -789,6 +763,32 @@ PK_API c11_vec3i py_tovec3i(py_Ref self); PK_API c11_mat3x3* py_tomat3x3(py_Ref self); PK_API c11_color32 py_tocolor32(py_Ref self); +/************* json module *************/ +/// Python equivalent to `json.dumps(val)`. +PK_API bool py_json_dumps(py_Ref val, int indent) PY_RAISE PY_RETURN; +/// Python equivalent to `json.loads(val)`. +PK_API bool py_json_loads(const char* source) PY_RAISE PY_RETURN; + +/************* pickle module *************/ +/// Python equivalent to `pickle.dumps(val)`. +PK_API bool py_pickle_dumps(py_Ref val) PY_RAISE PY_RETURN; +/// Python equivalent to `pickle.loads(val)`. +PK_API bool py_pickle_loads(const unsigned char* data, int size) PY_RAISE PY_RETURN; + +/************* pkpy module *************/ +/// Begin the watchdog with `timeout` in milliseconds. +/// `PK_ENABLE_WATCHDOG` must be defined to `1` to use this feature. +/// You need to call `py_watchdog_end()` later. +/// If `timeout` is reached, `TimeoutError` will be raised. +PK_API void py_watchdog_begin(py_i64 timeout); +/// Reset the watchdog. +PK_API void py_watchdog_end(); + +PK_API void py_profiler_begin(); +PK_API void py_profiler_end(); +PK_API void py_profiler_reset(); +PK_API char* py_profiler_report(); + /************* Others *************/ /// An utility function to read a line from stdin for REPL. diff --git a/src/public/py_array.c b/src/bindings/py_array.c similarity index 100% rename from src/public/py_array.c rename to src/bindings/py_array.c diff --git a/src/public/py_mappingproxy.c b/src/bindings/py_mappingproxy.c similarity index 100% rename from src/public/py_mappingproxy.c rename to src/bindings/py_mappingproxy.c diff --git a/src/public/py_method.c b/src/bindings/py_method.c similarity index 100% rename from src/public/py_method.c rename to src/bindings/py_method.c diff --git a/src/public/py_number.c b/src/bindings/py_number.c similarity index 100% rename from src/public/py_number.c rename to src/bindings/py_number.c diff --git a/src/public/py_object.c b/src/bindings/py_object.c similarity index 100% rename from src/public/py_object.c rename to src/bindings/py_object.c diff --git a/src/public/py_property.c b/src/bindings/py_property.c similarity index 100% rename from src/public/py_property.c rename to src/bindings/py_property.c diff --git a/src/public/py_range.c b/src/bindings/py_range.c similarity index 100% rename from src/public/py_range.c rename to src/bindings/py_range.c diff --git a/src/public/py_str.c b/src/bindings/py_str.c similarity index 88% rename from src/public/py_str.c rename to src/bindings/py_str.c index c01f2c18..d142a521 100644 --- a/src/public/py_str.c +++ b/src/bindings/py_str.c @@ -1,59 +1,10 @@ #include "pocketpy/common/str.h" #include "pocketpy/pocketpy.h" -#include "pocketpy/common/utils.h" #include "pocketpy/objects/object.h" #include "pocketpy/interpreter/vm.h" #include "pocketpy/common/sstream.h" -void py_newstr(py_OutRef out, const char* data) { py_newstrv(out, (c11_sv){data, strlen(data)}); } - -char* py_newstrn(py_OutRef out, int size) { - if(size < 16) { - out->type = tp_str; - out->is_ptr = false; - c11_string* ud = (c11_string*)(&out->extra); - c11_string__ctor3(ud, size); - return ud->data; - } - ManagedHeap* heap = &pk_current_vm->heap; - int total_size = sizeof(c11_string) + size + 1; - PyObject* obj = ManagedHeap__gcnew(heap, tp_str, 0, total_size); - c11_string* ud = PyObject__userdata(obj); - c11_string__ctor3(ud, size); - out->type = tp_str; - out->is_ptr = true; - out->_obj = obj; - return ud->data; -} - -void py_newstrv(py_OutRef out, c11_sv sv) { - char* data = py_newstrn(out, sv.size); - memcpy(data, sv.data, sv.size); -} - -void py_newfstr(py_OutRef out, const char* fmt, ...) { - c11_sbuf buf; - c11_sbuf__ctor(&buf); - va_list args; - va_start(args, fmt); - pk_vsprintf(&buf, fmt, args); - va_end(args); - c11_sbuf__py_submit(&buf, out); -} - -unsigned char* py_newbytes(py_OutRef out, int size) { - ManagedHeap* heap = &pk_current_vm->heap; - // 4 bytes size + data - PyObject* obj = ManagedHeap__gcnew(heap, tp_bytes, 0, sizeof(c11_bytes) + size); - c11_bytes* ud = PyObject__userdata(obj); - ud->size = size; - out->type = tp_bytes; - out->is_ptr = true; - out->_obj = obj; - return ud->data; -} - c11_string* pk_tostr(py_Ref self) { assert(self->type == tp_str); if(!self->is_ptr) { @@ -63,33 +14,6 @@ c11_string* pk_tostr(py_Ref self) { } } -const char* py_tostr(py_Ref self) { return pk_tostr(self)->data; } - -const char* py_tostrn(py_Ref self, int* size) { - c11_string* ud = pk_tostr(self); - *size = ud->size; - return ud->data; -} - -c11_sv py_tosv(py_Ref self) { - c11_string* ud = pk_tostr(self); - return c11_string__sv(ud); -} - -unsigned char* py_tobytes(py_Ref self, int* size) { - assert(self->type == tp_bytes); - c11_bytes* ud = PyObject__userdata(self->_obj); - *size = ud->size; - return ud->data; -} - -void py_bytes_resize(py_Ref self, int size) { - assert(self->type == tp_bytes); - c11_bytes* ud = PyObject__userdata(self->_obj); - if(size > ud->size) c11__abort("bytes can only be resized down: %d > %d", ud->size, size); - ud->size = size; -} - //////////////////////////////// static bool str__new__(int argc, py_Ref argv) { assert(argc >= 1); @@ -673,18 +597,4 @@ py_Type pk_bytes__register() { return type; } -bool py_str(py_Ref val) { - if(val->type == tp_str) { - py_assign(py_retval(), val); - return true; - } - py_Ref tmp = py_tpfindmagic(val->type, __str__); - if(!tmp) return py_repr(val); - return py_call(tmp, 1, val); -} - -bool py_repr(py_Ref val) { return pk_callmagic(__repr__, 1, val); } - -bool py_len(py_Ref val) { return pk_callmagic(__len__, 1, val); } - #undef DEF_STR_CMP_OP \ No newline at end of file diff --git a/src/interpreter/ceval.c b/src/interpreter/ceval.c index 38e55d3d..492ca795 100644 --- a/src/interpreter/ceval.c +++ b/src/interpreter/ceval.c @@ -586,10 +586,10 @@ __NEXT_STEP: case OP_BUILD_SLICE: { // [start, stop, step] py_TValue tmp; - py_newslice(&tmp); - py_setslot(&tmp, 0, THIRD()); - py_setslot(&tmp, 1, SECOND()); - py_setslot(&tmp, 2, TOP()); + py_ObjectRef slots = py_newslice(&tmp); + slots[0] = *THIRD(); + slots[1] = *SECOND(); + slots[2] = *TOP(); STACK_SHRINK(3); PUSH(&tmp); DISPATCH(); @@ -1298,57 +1298,6 @@ bool pk_stack_binaryop(VM* self, py_Name op, py_Name rop) { rhs_t); } -bool py_binaryop(py_Ref lhs, py_Ref rhs, py_Name op, py_Name rop) { - VM* self = pk_current_vm; - PUSH(lhs); - PUSH(rhs); - bool ok = pk_stack_binaryop(self, op, rop); - STACK_SHRINK(2); - return ok; -} - -bool py_binaryadd(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __add__, __radd__); } - -bool py_binarysub(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __sub__, __rsub__); } - -bool py_binarymul(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __mul__, __rmul__); } - -bool py_binarytruediv(py_Ref lhs, py_Ref rhs) { - return py_binaryop(lhs, rhs, __truediv__, __rtruediv__); -} - -bool py_binaryfloordiv(py_Ref lhs, py_Ref rhs) { - return py_binaryop(lhs, rhs, __floordiv__, __rfloordiv__); -} - -bool py_binarymod(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __mod__, __rmod__); } - -bool py_binarypow(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __pow__, __rpow__); } - -bool py_binarylshift(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __lshift__, 0); } - -bool py_binaryrshift(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __rshift__, 0); } - -bool py_binaryand(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __and__, 0); } - -bool py_binaryor(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __or__, 0); } - -bool py_binaryxor(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __xor__, 0); } - -bool py_binarymatmul(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __matmul__, 0); } - -bool py_eq(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __eq__, __eq__); } - -bool py_ne(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __ne__, __ne__); } - -bool py_lt(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __lt__, __gt__); } - -bool py_le(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __le__, __ge__); } - -bool py_gt(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __gt__, __lt__); } - -bool py_ge(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __ge__, __le__); } - static bool stack_format_object(VM* self, c11_sv spec) { // format TOS via `spec` inplace // spec: '!r:.2f', '.2f' @@ -1514,14 +1463,3 @@ static bool stack_format_object(VM* self, c11_sv spec) { #undef INSERT_THIRD #undef vectorcall_opcall #undef RESET_CO_CACHE - -void py_sys_settrace(py_TraceFunc func, bool reset) { - TraceInfo* info = &pk_current_vm->trace_info; - info->func = func; - if(!reset) return; - if(info->prev_loc.src) { - PK_DECREF(info->prev_loc.src); - info->prev_loc.src = NULL; - } - info->prev_loc.lineno = -1; -} \ No newline at end of file diff --git a/src/interpreter/frame.c b/src/interpreter/frame.c index 78a93d79..3f2db232 100644 --- a/src/interpreter/frame.c +++ b/src/interpreter/frame.c @@ -162,44 +162,3 @@ SourceLocation Frame__source_location(py_Frame* self) { loc.src = self->co->src; return loc; } - -const char* py_Frame_sourceloc(py_Frame* self, int* lineno) { - SourceLocation loc = Frame__source_location(self); - *lineno = loc.lineno; - return loc.src->filename->data; -} - -void py_Frame_newglobals(py_Frame* frame, py_OutRef out) { - if(!frame) { - pk_mappingproxy__namedict(out, pk_current_vm->main); - return; - } - if(frame->globals->type == tp_module) { - pk_mappingproxy__namedict(out, frame->globals); - } else { - *out = *frame->globals; // dict - } -} - -void py_Frame_newlocals(py_Frame* frame, py_OutRef out) { - if(!frame) { - py_newdict(out); - return; - } - if(frame->is_locals_special) { - switch(frame->locals->type) { - case tp_locals: frame = frame->locals->_ptr; break; - case tp_dict: *out = *frame->locals; return; - case tp_nil: py_newdict(out); return; - default: c11__unreachable(); - } - } - FastLocals__to_dict(frame->locals, frame->co); - py_assign(out, py_retval()); -} - -py_StackRef py_Frame_function(py_Frame* self) { - if(self->is_locals_special) return NULL; - assert(self->p0->type == tp_function); - return self->p0; -} \ No newline at end of file diff --git a/src/interpreter/typeinfo.c b/src/interpreter/typeinfo.c index 93fba34d..a20b76ce 100644 --- a/src/interpreter/typeinfo.c +++ b/src/interpreter/typeinfo.c @@ -11,45 +11,11 @@ py_ItemRef pk_tpfindname(py_TypeInfo* ti, py_Name name) { return NULL; } -PK_INLINE py_ItemRef py_tpfindname(py_Type type, py_Name name) { - py_TypeInfo* ti = pk_typeinfo(type); - return pk_tpfindname(ti, name); -} - -PK_INLINE py_Ref py_tpfindmagic(py_Type t, py_Name name) { - // assert(py_ismagicname(name)); - return py_tpfindname(t, name); -} - -PK_INLINE py_Type py_tpbase(py_Type t) { - assert(t); - py_TypeInfo* ti = pk_typeinfo(t); - return ti->base; -} - -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(); -} - -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); -} - PK_INLINE py_TypeInfo* pk_typeinfo(py_Type type) { #ifndef NDEBUG int length = pk_current_vm->types.length; - if(type < 0 || type >= length) { - c11__abort("type index %d is out of bounds [0, %d)", type, length); + if(type <= 0 || type >= length) { + c11__abort("type index %d is out of bounds (0, %d)", type, length); } #endif return c11__getitem(TypePointer, &pk_current_vm->types, type).ti; @@ -150,28 +116,3 @@ py_Type pk_newtypewithmode(py_Name name, return pk_newtype(py_name2str(name), base, module, dtor, is_python, is_final); } -py_Type py_newtype(const char* name, py_Type base, const py_GlobalRef module, void (*dtor)(void*)) { - if(strlen(name) == 0) c11__abort("type name cannot be empty"); - py_Type type = pk_newtype(name, base, module, dtor, false, false); - if(module) py_setdict(module, py_name(name), py_tpobject(type)); - return type; -} - -void py_tpsetfinal(py_Type type) { - assert(type); - py_TypeInfo* ti = pk_typeinfo(type); - ti->is_final = true; -} - -void py_tphookattributes(py_Type type, - bool (*getattribute)(py_Ref self, py_Name name), - bool (*setattribute)(py_Ref self, py_Name name, py_Ref val), - bool (*delattribute)(py_Ref self, py_Name name), - bool (*getunboundmethod)(py_Ref self, py_Name name)) { - assert(type); - py_TypeInfo* ti = pk_typeinfo(type); - ti->getattribute = getattribute; - ti->setattribute = setattribute; - ti->delattribute = delattribute; - ti->getunboundmethod = getunboundmethod; -} diff --git a/src/public/modules.c b/src/modules/builtins.c similarity index 67% rename from src/public/modules.c rename to src/modules/builtins.c index ccf740c9..8dc10e31 100644 --- a/src/public/modules.c +++ b/src/modules/builtins.c @@ -8,198 +8,8 @@ #include "pocketpy/interpreter/vm.h" #include "pocketpy/common/_generated.h" -#include #include -py_Ref py_getmodule(const char* path) { - VM* vm = pk_current_vm; - 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_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); } - -static void py_ModuleInfo__dtor(py_ModuleInfo* mi) { - c11_string__delete(mi->name); - c11_string__delete(mi->package); - c11_string__delete(mi->path); -} - -py_Type pk_module__register() { - py_Type type = pk_newtype("module", tp_object, NULL, (py_Dtor)py_ModuleInfo__dtor, false, true); - return type; -} - -py_Ref py_newmodule(const char* path) { - int path_len = strlen(path); - if(path_len > PK_MAX_MODULE_PATH_LEN) c11__abort("module path too long: %s", path); - if(path_len == 0) c11__abort("module path cannot be empty"); - - py_ModuleInfo* mi = py_newobject(py_retval(), tp_module, -1, sizeof(py_ModuleInfo)); - - int last_dot = c11_sv__rindex((c11_sv){path, path_len}, '.'); - if(last_dot == -1) { - mi->name = c11_string__new(path); - mi->package = c11_string__new(""); - } else { - const char* start = path + last_dot + 1; - mi->name = c11_string__new(start); - mi->package = c11_string__new2(path, last_dot); - } - - mi->path = c11_string__new(path); - path = mi->path->data; - - // we do not allow override in order to avoid memory leak - // it is because Module objects are not garbage collected - bool exists = BinTree__contains(&pk_current_vm->modules, (void*)path); - if(exists) c11__abort("module '%s' already exists", path); - - BinTree__set(&pk_current_vm->modules, (void*)path, py_retval()); - py_GlobalRef retval = py_getmodule(path); - mi->self = retval; - - // setup __name__ - py_newstrv(py_emplacedict(retval, __name__), c11_string__sv(mi->name)); - // setup __package__ - py_newstrv(py_emplacedict(retval, __package__), c11_string__sv(mi->package)); - // setup __path__ - py_newstrv(py_emplacedict(retval, __path__), c11_string__sv(mi->path)); - return retval; -} - -int load_module_from_dll_desktop_only(const char* path) PY_RAISE PY_RETURN; - -int py_import(const char* path_cstr) { - VM* vm = pk_current_vm; - c11_sv path = {path_cstr, strlen(path_cstr)}; - if(path.size == 0) return ValueError("empty module name"); - - if(path.data[0] == '.') { - // try relative import - int dot_count = 1; - while(dot_count < path.size && path.data[dot_count] == '.') - dot_count++; - - c11_sv top_filename = c11_string__sv(vm->top_frame->co->src->filename); - int is_init = c11_sv__endswith(top_filename, (c11_sv){"__init__.py", 11}); - - py_ModuleInfo* mi = py_touserdata(vm->top_frame->module); - c11_sv package_sv = c11_string__sv(mi->path); - if(package_sv.size == 0) { - return ImportError("attempted relative import with no known parent package"); - } - - c11_vector /* T=c11_sv */ cpnts = c11_sv__split(package_sv, '.'); - for(int i = is_init; i < dot_count; i++) { - if(cpnts.length == 0) - return ImportError("attempted relative import beyond top-level package"); - c11_vector__pop(&cpnts); - } - - if(dot_count < path.size) { - c11_sv last_cpnt = c11_sv__slice(path, dot_count); - c11_vector__push(c11_sv, &cpnts, last_cpnt); - } - - // join cpnts - c11_sbuf buf; - c11_sbuf__ctor(&buf); - for(int i = 0; i < cpnts.length; i++) { - if(i > 0) c11_sbuf__write_char(&buf, '.'); - c11_sbuf__write_sv(&buf, c11__getitem(c11_sv, &cpnts, i)); - } - - c11_vector__dtor(&cpnts); - c11_string* new_path = c11_sbuf__submit(&buf); - int res = py_import(new_path->data); - c11_string__delete(new_path); - return res; - } - - assert(path.data[0] != '.' && path.data[path.size - 1] != '.'); - - // check existing module - py_GlobalRef ext_mod = py_getmodule(path.data); - if(ext_mod) { - py_assign(py_retval(), ext_mod); - return true; - } - - if(vm->callbacks.lazyimport) { - py_GlobalRef lazymod = vm->callbacks.lazyimport(path_cstr); - if(lazymod) { - c11__rtassert(py_istype(lazymod, tp_module)); - py_assign(py_retval(), lazymod); - return 1; - } - } - - // try import - c11_string* slashed_path = c11_sv__replace(path, '.', PK_PLATFORM_SEP); - c11_string* filename = c11_string__new3("%s.py", slashed_path->data); - - bool need_free = true; - const char* data = load_kPythonLib(path_cstr); - if(data != NULL) { - need_free = false; - goto __SUCCESS; - } - - data = vm->callbacks.importfile(filename->data); - if(data != NULL) goto __SUCCESS; - - c11_string__delete(filename); - filename = c11_string__new3("%s%c__init__.py", slashed_path->data, PK_PLATFORM_SEP); - data = vm->callbacks.importfile(filename->data); - if(data != NULL) goto __SUCCESS; - - c11_string__delete(filename); - c11_string__delete(slashed_path); - // not found - return load_module_from_dll_desktop_only(path_cstr); - -__SUCCESS: - do { - } while(0); - py_GlobalRef mod = py_newmodule(path_cstr); - bool ok = py_exec((const char*)data, filename->data, EXEC_MODE, mod); - py_assign(py_retval(), mod); - - c11_string__delete(filename); - c11_string__delete(slashed_path); - if(need_free) PK_FREE((void*)data); - return ok ? 1 : -1; -} - -bool py_importlib_reload(py_Ref module) { - VM* vm = pk_current_vm; - py_ModuleInfo* mi = py_touserdata(module); - // We should ensure that the module is its original py_GlobalRef - module = mi->self; - c11_sv path = c11_string__sv(mi->path); - c11_string* slashed_path = c11_sv__replace(path, '.', PK_PLATFORM_SEP); - c11_string* filename = c11_string__new3("%s.py", slashed_path->data); - char* data = vm->callbacks.importfile(filename->data); - if(data == NULL) { - c11_string__delete(filename); - filename = c11_string__new3("%s%c__init__.py", slashed_path->data, PK_PLATFORM_SEP); - data = vm->callbacks.importfile(filename->data); - } - c11_string__delete(slashed_path); - if(data == NULL) return ImportError("module '%v' not found", path); - // py_cleardict(module); BUG: removing old classes will cause RELOAD_MODE to fail - bool ok = py_exec(data, filename->data, RELOAD_MODE, module); - c11_string__delete(filename); - PK_FREE(data); - py_assign(py_retval(), module); - return ok; -} - -////////////////////////// static bool builtins_exit(int argc, py_Ref argv) { int code = 0; @@ -399,18 +209,6 @@ static bool builtins_issubclass(int argc, py_Ref argv) { return true; } -bool py_callable(py_Ref val) { - switch(val->type) { - case tp_nativefunc: return true; - case tp_function: return true; - case tp_type: return true; - case tp_boundmethod: return true; - case tp_staticmethod: return true; - case tp_classmethod: return true; - default: return py_tpfindmagic(val->type, __call__); - } -} - static bool builtins_callable(int argc, py_Ref argv) { PY_CHECK_ARGC(1); bool res = py_callable(py_arg(0)); @@ -519,16 +317,6 @@ static bool builtins_locals(int argc, py_Ref argv) { return true; } -void py_newglobals(py_OutRef out) { - py_Frame* frame = pk_current_vm->top_frame; - py_Frame_newglobals(frame, out); -} - -void py_newlocals(py_OutRef out) { - py_Frame* frame = pk_current_vm->top_frame; - py_Frame_newlocals(frame, out); -} - static void pk_push_special_locals() { py_Frame* frame = pk_current_vm->top_frame; if(!frame) { @@ -622,70 +410,6 @@ static bool builtins_eval(int argc, py_Ref argv) { return _builtins_execdyn("eval", argc, argv, EVAL_MODE); } -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; - pk_mappingproxy__namedict(py_pushtmp(), module); // globals - py_newdict(py_pushtmp()); // locals - bool ok = py_compile(source, "", mode, true); - if(!ok) return false; - py_push(py_retval()); - // [globals, locals, code] - CodeObject* co = py_touserdata(py_peek(-1)); - py_StackRef locals = py_peek(-2); - int max_index = -1; - c11__foreach(Bytecode, &co->codes, bc) { - if(bc->op == OP_LOAD_NAME) { - c11_sv name = py_name2sv(c11__getitem(py_Name, &co->names, bc->arg)); - if(name.data[0] != '_') continue; - int index; - if(name.size == 1) { - index = 0; - } else if(name.size == 2 && isdigit(name.data[1])) { - index = name.data[1] - '0'; - } else { - continue; - } - max_index = c11__max(max_index, index); - } - } - - if(max_index == -1) return ValueError("no placeholder found in the source"); - - for(int i = 0; i <= max_index; i++) { - py_Ref val = va_arg(args, py_Ref); - char buf[3]; - buf[0] = '_'; - buf[1] = '0' + i; - buf[2] = '\0'; - py_dict_setitem_by_str(locals, buf, val); - if(i == 0) { - // _ => _0 - py_dict_setitem_by_str(locals, "_", val); - } - } - ok = pk_execdyn(co, module, py_peek(-3), locals); - if(!ok) return false; - py_shrink(3); - return true; -} - -bool py_smartexec(const char* source, py_Ref module, ...) { - va_list args; - va_start(args, module); - bool ok = pk_smartexec(source, module, EXEC_MODE, args); - va_end(args); - return ok; -} - -bool py_smarteval(const char* source, py_Ref module, ...) { - va_list args; - va_start(args, module); - bool ok = pk_smartexec(source, module, EVAL_MODE, args); - va_end(args); - return ok; -} - static bool builtins_compile(int argc, py_Ref argv) { PY_CHECK_ARGC(3); for(int i = 0; i < 3; i++) { diff --git a/src/modules/gc.c b/src/modules/gc.c index 5c5b358f..6bb7fcb9 100644 --- a/src/modules/gc.c +++ b/src/modules/gc.c @@ -40,8 +40,3 @@ void pk__add_module_gc() { 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); -} \ No newline at end of file diff --git a/src/modules/json.c b/src/modules/json.c index e46fdd76..06d8b855 100644 --- a/src/modules/json.c +++ b/src/modules/json.c @@ -189,9 +189,3 @@ bool py_json_loads(const char* source) { return py_exec(source, "", EVAL_MODE, mod); } -bool py_pusheval(const char* expr, py_GlobalRef module) { - bool ok = py_exec(expr, "", EVAL_MODE, module); - if(!ok) return false; - py_push(py_retval()); - return true; -} diff --git a/src/public/Bindings.c b/src/public/Bindings.c new file mode 100644 index 00000000..28ca8950 --- /dev/null +++ b/src/public/Bindings.c @@ -0,0 +1,61 @@ +#include "pocketpy/interpreter/vm.h" + +void py_bind(py_Ref obj, const char* sig, py_CFunction f) { + py_Ref tmp = py_pushtmp(); + py_Name name = py_newfunction(tmp, sig, f, NULL, 0); + py_setdict(obj, name, tmp); + py_pop(); +} + +void py_bindmethod(py_Type type, const char* name, py_CFunction f) { + py_TValue tmp; + py_newnativefunc(&tmp, f); + py_setdict(py_tpobject(type), py_name(name), &tmp); +} + +void py_bindstaticmethod(py_Type type, const char* name, py_CFunction f) { + py_TValue tmp; + py_newnativefunc(&tmp, f); + bool ok = py_tpcall(tp_staticmethod, 1, &tmp); + if(!ok) { + py_printexc(); + c11__abort("py_bindstaticmethod(): failed to create staticmethod"); + } + py_setdict(py_tpobject(type), py_name(name), py_retval()); +} + +void py_bindfunc(py_Ref obj, const char* name, py_CFunction f) { + py_TValue tmp; + py_newnativefunc(&tmp, f); + py_setdict(obj, py_name(name), &tmp); +} + +void py_bindproperty(py_Type type, const char* name, py_CFunction getter, py_CFunction setter) { + py_TValue tmp; + py_newobject(&tmp, tp_property, 2, 0); + py_newnativefunc(py_getslot(&tmp, 0), getter); + if(setter) { + py_newnativefunc(py_getslot(&tmp, 1), setter); + } else { + py_setslot(&tmp, 1, py_None()); + } + py_setdict(py_tpobject(type), py_name(name), &tmp); +} + +void py_bindmagic(py_Type type, py_Name name, py_CFunction f) { + py_Ref tmp = py_emplacedict(py_tpobject(type), name); + py_newnativefunc(tmp, f); +} + +void py_macrobind(const char* sig, py_CFunction f) { + py_Ref tmp = py_pushtmp(); + py_Name name = py_newfunction(tmp, sig, f, NULL, 0); + NameDict__set(&pk_current_vm->compile_time_funcs, name, tmp); + py_pop(); +} + +py_ItemRef py_macroget(py_Name name) { + NameDict* d = &pk_current_vm->compile_time_funcs; + if(d->length == 0) return NULL; + return NameDict__try_get(d, name); +} diff --git a/src/public/exec.c b/src/public/CodeExecution.c similarity index 60% rename from src/public/exec.c rename to src/public/CodeExecution.c index 6a7f9a2a..f2a1a67f 100644 --- a/src/public/exec.c +++ b/src/public/CodeExecution.c @@ -6,13 +6,14 @@ #include "pocketpy/interpreter/vm.h" #include "pocketpy/compiler/compiler.h" #include +#include py_Type pk_code__register() { py_Type type = pk_newtype("code", tp_object, NULL, (py_Dtor)CodeObject__dtor, false, true); return type; } -bool _py_compile(CodeObject* out, +static bool _py_compile(CodeObject* out, const char* source, const char* filename, enum py_CompileMode mode, @@ -33,20 +34,6 @@ bool _py_compile(CodeObject* out, return true; } -bool py_compile(const char* source, - const char* filename, - enum py_CompileMode mode, - bool is_dynamic) { - CodeObject co; - bool ok = _py_compile(&co, source, filename, mode, is_dynamic); - if(ok) { - // compile success - CodeObject* ud = py_newobject(py_retval(), tp_code, 0, sizeof(CodeObject)); - *ud = co; - } - return ok; -} - bool pk_exec(CodeObject* co, py_Ref module) { VM* vm = pk_current_vm; if(!module) module = vm->main; @@ -92,6 +79,68 @@ bool pk_execdyn(CodeObject* co, py_Ref module, py_Ref globals, py_Ref locals) { return true; } +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; + pk_mappingproxy__namedict(py_pushtmp(), module); // globals + py_newdict(py_pushtmp()); // locals + bool ok = py_compile(source, "", mode, true); + if(!ok) return false; + py_push(py_retval()); + // [globals, locals, code] + CodeObject* co = py_touserdata(py_peek(-1)); + py_StackRef locals = py_peek(-2); + int max_index = -1; + c11__foreach(Bytecode, &co->codes, bc) { + if(bc->op == OP_LOAD_NAME) { + c11_sv name = py_name2sv(c11__getitem(py_Name, &co->names, bc->arg)); + if(name.data[0] != '_') continue; + int index; + if(name.size == 1) { + index = 0; + } else if(name.size == 2 && isdigit(name.data[1])) { + index = name.data[1] - '0'; + } else { + continue; + } + max_index = c11__max(max_index, index); + } + } + + if(max_index == -1) return ValueError("no placeholder found in the source"); + + for(int i = 0; i <= max_index; i++) { + py_Ref val = va_arg(args, py_Ref); + char buf[3]; + buf[0] = '_'; + buf[1] = '0' + i; + buf[2] = '\0'; + py_dict_setitem_by_str(locals, buf, val); + if(i == 0) { + // _ => _0 + py_dict_setitem_by_str(locals, "_", val); + } + } + ok = pk_execdyn(co, module, py_peek(-3), locals); + if(!ok) return false; + py_shrink(3); + return true; +} + +bool py_compile(const char* source, + const char* filename, + enum py_CompileMode mode, + bool is_dynamic) { + CodeObject co; + bool ok = _py_compile(&co, source, filename, mode, is_dynamic); + if(ok) { + // compile success + CodeObject* ud = py_newobject(py_retval(), tp_code, 0, sizeof(CodeObject)); + *ud = co; + } + return ok; +} + bool py_exec(const char* source, const char* filename, enum py_CompileMode mode, py_Ref module) { CodeObject co; if(!_py_compile(&co, source, filename, mode, false)) return false; @@ -102,4 +151,20 @@ bool py_exec(const char* source, const char* filename, enum py_CompileMode mode, bool py_eval(const char* source, py_Ref module) { return py_exec(source, "", EVAL_MODE, module); -} \ No newline at end of file +} + +bool py_smartexec(const char* source, py_Ref module, ...) { + va_list args; + va_start(args, module); + bool ok = pk_smartexec(source, module, EXEC_MODE, args); + va_end(args); + return ok; +} + +bool py_smarteval(const char* source, py_Ref module, ...) { + va_list args; + va_start(args, module); + bool ok = pk_smartexec(source, module, EVAL_MODE, args); + va_end(args); + return ok; +} diff --git a/src/public/stack_ops.c b/src/public/DictSlots.c similarity index 56% rename from src/public/stack_ops.c rename to src/public/DictSlots.c index d5a60647..620df4fd 100644 --- a/src/public/stack_ops.c +++ b/src/public/DictSlots.c @@ -1,13 +1,11 @@ -#include "pocketpy/pocketpy.h" - -#include "pocketpy/common/utils.h" -#include "pocketpy/objects/object.h" #include "pocketpy/interpreter/vm.h" py_Ref py_getreg(int i) { return pk_current_vm->reg + i; } void py_setreg(int i, py_Ref val) { pk_current_vm->reg[i] = *val; } +PK_INLINE py_Ref py_retval() { return &pk_current_vm->last_retval; } + PK_INLINE py_Ref py_getdict(py_Ref self, py_Name name) { assert(self && self->is_ptr); return NameDict__try_get(PyObject__dict(self->_obj), name); @@ -18,6 +16,11 @@ PK_INLINE void py_setdict(py_Ref self, py_Name name, py_Ref val) { NameDict__set(PyObject__dict(self->_obj), name, val); } +bool py_deldict(py_Ref self, py_Name name) { + assert(self && self->is_ptr); + return NameDict__del(PyObject__dict(self->_obj), name); +} + py_ItemRef py_emplacedict(py_Ref self, py_Name name) { py_setdict(self, name, py_NIL()); return py_getdict(self, name); @@ -41,11 +44,6 @@ void py_cleardict(py_Ref self) { NameDict__clear(dict); } -bool py_deldict(py_Ref self, py_Name name) { - assert(self && self->is_ptr); - return NameDict__del(PyObject__dict(self->_obj), name); -} - py_Ref py_getslot(py_Ref self, int i) { assert(self && self->is_ptr); assert(i >= 0 && i < self->_obj->slots); @@ -58,59 +56,8 @@ void py_setslot(py_Ref self, int i, py_Ref val) { PyObject__slots(self->_obj)[i] = *val; } -py_StackRef py_inspect_currentfunction() { - VM* vm = pk_current_vm; - if(vm->curr_decl_based_function) return vm->curr_decl_based_function; - py_Frame* frame = vm->top_frame; - if(!frame || frame->is_locals_special) return NULL; - return frame->p0; -} +py_Ref py_getbuiltin(py_Name name) { return py_getdict(pk_current_vm->builtins, name); } -py_GlobalRef py_inspect_currentmodule() { - py_Frame* frame = pk_current_vm->top_frame; - if(!frame) return NULL; - return frame->module; -} +py_Ref py_getglobal(py_Name name) { return py_getdict(pk_current_vm->main, name); } -py_Frame* py_inspect_currentframe() { return pk_current_vm->top_frame; } - -/* Stack References */ -py_Ref py_peek(int i) { - assert(i <= 0); - return pk_current_vm->stack.sp + i; -} - -void py_pop() { - VM* vm = pk_current_vm; - vm->stack.sp--; -} - -void py_shrink(int n) { - VM* vm = pk_current_vm; - vm->stack.sp -= n; -} - -void py_push(py_Ref src) { - VM* vm = pk_current_vm; - *vm->stack.sp++ = *src; -} - -void py_pushnil() { - VM* vm = pk_current_vm; - py_newnil(vm->stack.sp++); -} - -void py_pushnone() { - VM* vm = pk_current_vm; - py_newnone(vm->stack.sp++); -} - -void py_pushname(py_Name name) { - VM* vm = pk_current_vm; - py_newint(vm->stack.sp++, (uintptr_t)name); -} - -py_Ref py_pushtmp() { - VM* vm = pk_current_vm; - return vm->stack.sp++; -} \ No newline at end of file +void py_setglobal(py_Name name, py_Ref val) { py_setdict(pk_current_vm->main, name, val); } diff --git a/src/public/FrameOps.c b/src/public/FrameOps.c new file mode 100644 index 00000000..4fead75f --- /dev/null +++ b/src/public/FrameOps.c @@ -0,0 +1,43 @@ +#include "pocketpy/interpreter/frame.h" +#include "pocketpy/interpreter/vm.h" + +const char* py_Frame_sourceloc(py_Frame* self, int* lineno) { + SourceLocation loc = Frame__source_location(self); + *lineno = loc.lineno; + return loc.src->filename->data; +} + +void py_Frame_newglobals(py_Frame* frame, py_OutRef out) { + if(!frame) { + pk_mappingproxy__namedict(out, pk_current_vm->main); + return; + } + if(frame->globals->type == tp_module) { + pk_mappingproxy__namedict(out, frame->globals); + } else { + *out = *frame->globals; // dict + } +} + +void py_Frame_newlocals(py_Frame* frame, py_OutRef out) { + if(!frame) { + py_newdict(out); + return; + } + if(frame->is_locals_special) { + switch(frame->locals->type) { + case tp_locals: frame = frame->locals->_ptr; break; + case tp_dict: *out = *frame->locals; return; + case tp_nil: py_newdict(out); return; + default: c11__unreachable(); + } + } + FastLocals__to_dict(frame->locals, frame->co); + py_assign(out, py_retval()); +} + +py_StackRef py_Frame_function(py_Frame* self) { + if(self->is_locals_special) return NULL; + assert(self->p0->type == tp_function); + return self->p0; +} diff --git a/src/public/GlobalSetup.c b/src/public/GlobalSetup.c new file mode 100644 index 00000000..60486556 --- /dev/null +++ b/src/public/GlobalSetup.c @@ -0,0 +1,161 @@ +#include "pocketpy/objects/codeobject.h" +#include "pocketpy/pocketpy.h" + +#include "pocketpy/common/utils.h" +#include "pocketpy/common/name.h" +#include "pocketpy/interpreter/vm.h" + +_Thread_local VM* pk_current_vm; + +static bool pk_initialized; +static bool pk_finalized; + +static VM pk_default_vm; +static VM* pk_all_vm[16]; +static py_TValue _True, _False, _None, _NIL; + +void py_initialize() { + c11__rtassert(!pk_finalized); + + if(pk_initialized) { + // c11__abort("py_initialize() can only be called once!"); + return; + } + + pk_names_initialize(); + + // check endianness + int x = 1; + bool is_little_endian = *(char*)&x == 1; + if(!is_little_endian) c11__abort("is_little_endian != true"); + + static_assert(sizeof(py_TValue) == 24, "sizeof(py_TValue) != 24"); + static_assert(offsetof(py_TValue, extra) == 4, "offsetof(py_TValue, extra) != 4"); + + pk_current_vm = pk_all_vm[0] = &pk_default_vm; + + // initialize some convenient references + py_newbool(&_True, true); + py_newbool(&_False, false); + py_newnone(&_None); + py_newnil(&_NIL); + VM__ctor(&pk_default_vm); + + pk_initialized = true; +} + +void py_finalize() { + if(pk_finalized) c11__abort("py_finalize() can only be called once!"); + pk_finalized = true; + + for(int i = 1; i < 16; i++) { + VM* vm = pk_all_vm[i]; + if(vm) { + // temp fix https://github.com/pocketpy/pocketpy/issues/315 + // TODO: refactor VM__ctor and VM__dtor + pk_current_vm = vm; + VM__dtor(vm); + PK_FREE(vm); + } + } + pk_current_vm = &pk_default_vm; + VM__dtor(&pk_default_vm); + pk_current_vm = NULL; + + pk_names_finalize(); +} + +int py_currentvm() { + for(int i = 0; i < 16; i++) { + if(pk_all_vm[i] == pk_current_vm) return i; + } + return -1; +} + +void py_switchvm(int index) { + if(index < 0 || index >= 16) c11__abort("invalid vm index"); + if(!pk_all_vm[index]) { + pk_current_vm = pk_all_vm[index] = PK_MALLOC(sizeof(VM)); + memset(pk_current_vm, 0, sizeof(VM)); + VM__ctor(pk_all_vm[index]); + } else { + pk_current_vm = pk_all_vm[index]; + } +} + +void py_resetvm() { + VM* vm = pk_current_vm; + VM__dtor(vm); + memset(vm, 0, sizeof(VM)); + VM__ctor(vm); +} + +void py_resetallvm() { + for(int i = 0; i < 16; i++) { + py_switchvm(i); + py_resetvm(); + } + py_switchvm(0); +} + +void* py_getvmctx() { return pk_current_vm->ctx; } + +void py_setvmctx(void* ctx) { pk_current_vm->ctx = ctx; } + +py_Callbacks* py_callbacks() { return &pk_current_vm->callbacks; } + +///////////////////////////// + +void py_sys_setargv(int argc, char** argv) { + py_GlobalRef sys = py_getmodule("sys"); + py_Ref argv_list = py_getdict(sys, py_name("argv")); + py_list_clear(argv_list); + for(int i = 0; i < argc; i++) { + py_newstr(py_list_emplace(argv_list), argv[i]); + } +} + +void py_sys_settrace(py_TraceFunc func, bool reset) { + TraceInfo* info = &pk_current_vm->trace_info; + info->func = func; + if(!reset) return; + if(info->prev_loc.src) { + PK_DECREF(info->prev_loc.src); + info->prev_loc.src = NULL; + } + info->prev_loc.lineno = -1; +} + +int py_gc_collect() { + ManagedHeap* heap = &pk_current_vm->heap; + return ManagedHeap__collect(heap); +} + +///////////////////////////// + +void* py_malloc(size_t size) { return PK_MALLOC(size); } + +void* py_realloc(void* ptr, size_t size) { return PK_REALLOC(ptr, size); } + +void py_free(void* ptr) { PK_FREE(ptr); } + +///////////////////////////// + +py_GlobalRef py_True() { return &_True; } + +py_GlobalRef py_False() { return &_False; } + +py_GlobalRef py_None() { return &_None; } + +py_GlobalRef py_NIL() { return &_NIL; } + +///////////////////////////// + +const char* pk_opname(Opcode op) { + const static char* OP_NAMES[] = { +#define OPCODE(name) #name, +#include "pocketpy/xmacros/opcodes.h" +#undef OPCODE + }; + return OP_NAMES[op]; +} diff --git a/src/public/Inspection.c b/src/public/Inspection.c new file mode 100644 index 00000000..adab739c --- /dev/null +++ b/src/public/Inspection.c @@ -0,0 +1,28 @@ +#include "pocketpy/interpreter/frame.h" +#include "pocketpy/interpreter/vm.h" + +py_StackRef py_inspect_currentfunction() { + VM* vm = pk_current_vm; + if(vm->curr_decl_based_function) return vm->curr_decl_based_function; + py_Frame* frame = vm->top_frame; + if(!frame || frame->is_locals_special) return NULL; + return frame->p0; +} + +py_GlobalRef py_inspect_currentmodule() { + py_Frame* frame = pk_current_vm->top_frame; + if(!frame) return NULL; + return frame->module; +} + +py_Frame* py_inspect_currentframe() { return pk_current_vm->top_frame; } + +void py_newglobals(py_OutRef out) { + py_Frame* frame = pk_current_vm->top_frame; + py_Frame_newglobals(frame, out); +} + +void py_newlocals(py_OutRef out) { + py_Frame* frame = pk_current_vm->top_frame; + py_Frame_newlocals(frame, out); +} diff --git a/src/public/ModuleSystem.c b/src/public/ModuleSystem.c new file mode 100644 index 00000000..099e7785 --- /dev/null +++ b/src/public/ModuleSystem.c @@ -0,0 +1,191 @@ +#include "pocketpy/common/str.h" +#include "pocketpy/objects/base.h" +#include "pocketpy/objects/codeobject.h" +#include "pocketpy/pocketpy.h" +#include "pocketpy/common/utils.h" +#include "pocketpy/common/sstream.h" +#include "pocketpy/interpreter/vm.h" +#include "pocketpy/common/_generated.h" + + +py_Ref py_getmodule(const char* path) { + VM* vm = pk_current_vm; + return BinTree__try_get(&vm->modules, (void*)path); +} + +py_Ref py_newmodule(const char* path) { + int path_len = strlen(path); + if(path_len > PK_MAX_MODULE_PATH_LEN) c11__abort("module path too long: %s", path); + if(path_len == 0) c11__abort("module path cannot be empty"); + + py_ModuleInfo* mi = py_newobject(py_retval(), tp_module, -1, sizeof(py_ModuleInfo)); + + int last_dot = c11_sv__rindex((c11_sv){path, path_len}, '.'); + if(last_dot == -1) { + mi->name = c11_string__new(path); + mi->package = c11_string__new(""); + } else { + const char* start = path + last_dot + 1; + mi->name = c11_string__new(start); + mi->package = c11_string__new2(path, last_dot); + } + + mi->path = c11_string__new(path); + path = mi->path->data; + + // we do not allow override in order to avoid memory leak + // it is because Module objects are not garbage collected + bool exists = BinTree__contains(&pk_current_vm->modules, (void*)path); + if(exists) c11__abort("module '%s' already exists", path); + + BinTree__set(&pk_current_vm->modules, (void*)path, py_retval()); + py_GlobalRef retval = py_getmodule(path); + mi->self = retval; + + // setup __name__ + py_newstrv(py_emplacedict(retval, __name__), c11_string__sv(mi->name)); + // setup __package__ + py_newstrv(py_emplacedict(retval, __package__), c11_string__sv(mi->package)); + // setup __path__ + py_newstrv(py_emplacedict(retval, __path__), c11_string__sv(mi->path)); + return retval; +} + +static void py_ModuleInfo__dtor(py_ModuleInfo* mi) { + c11_string__delete(mi->name); + c11_string__delete(mi->package); + c11_string__delete(mi->path); +} + +py_Type pk_module__register() { + py_Type type = pk_newtype("module", tp_object, NULL, (py_Dtor)py_ModuleInfo__dtor, false, true); + return type; +} + +int load_module_from_dll_desktop_only(const char* path) PY_RAISE PY_RETURN; + +int py_import(const char* path_cstr) { + VM* vm = pk_current_vm; + c11_sv path = {path_cstr, strlen(path_cstr)}; + if(path.size == 0) return ValueError("empty module name"); + + if(path.data[0] == '.') { + // try relative import + int dot_count = 1; + while(dot_count < path.size && path.data[dot_count] == '.') + dot_count++; + + c11_sv top_filename = c11_string__sv(vm->top_frame->co->src->filename); + int is_init = c11_sv__endswith(top_filename, (c11_sv){"__init__.py", 11}); + + py_ModuleInfo* mi = py_touserdata(vm->top_frame->module); + c11_sv package_sv = c11_string__sv(mi->path); + if(package_sv.size == 0) { + return ImportError("attempted relative import with no known parent package"); + } + + c11_vector /* T=c11_sv */ cpnts = c11_sv__split(package_sv, '.'); + for(int i = is_init; i < dot_count; i++) { + if(cpnts.length == 0) + return ImportError("attempted relative import beyond top-level package"); + c11_vector__pop(&cpnts); + } + + if(dot_count < path.size) { + c11_sv last_cpnt = c11_sv__slice(path, dot_count); + c11_vector__push(c11_sv, &cpnts, last_cpnt); + } + + // join cpnts + c11_sbuf buf; + c11_sbuf__ctor(&buf); + for(int i = 0; i < cpnts.length; i++) { + if(i > 0) c11_sbuf__write_char(&buf, '.'); + c11_sbuf__write_sv(&buf, c11__getitem(c11_sv, &cpnts, i)); + } + + c11_vector__dtor(&cpnts); + c11_string* new_path = c11_sbuf__submit(&buf); + int res = py_import(new_path->data); + c11_string__delete(new_path); + return res; + } + + assert(path.data[0] != '.' && path.data[path.size - 1] != '.'); + + // check existing module + py_GlobalRef ext_mod = py_getmodule(path.data); + if(ext_mod) { + py_assign(py_retval(), ext_mod); + return true; + } + + if(vm->callbacks.lazyimport) { + py_GlobalRef lazymod = vm->callbacks.lazyimport(path_cstr); + if(lazymod) { + c11__rtassert(py_istype(lazymod, tp_module)); + py_assign(py_retval(), lazymod); + return 1; + } + } + + // try import + c11_string* slashed_path = c11_sv__replace(path, '.', PK_PLATFORM_SEP); + c11_string* filename = c11_string__new3("%s.py", slashed_path->data); + + bool need_free = true; + const char* data = load_kPythonLib(path_cstr); + if(data != NULL) { + need_free = false; + goto __SUCCESS; + } + + data = vm->callbacks.importfile(filename->data); + if(data != NULL) goto __SUCCESS; + + c11_string__delete(filename); + filename = c11_string__new3("%s%c__init__.py", slashed_path->data, PK_PLATFORM_SEP); + data = vm->callbacks.importfile(filename->data); + if(data != NULL) goto __SUCCESS; + + c11_string__delete(filename); + c11_string__delete(slashed_path); + // not found + return load_module_from_dll_desktop_only(path_cstr); + +__SUCCESS: + do { + } while(0); + py_GlobalRef mod = py_newmodule(path_cstr); + bool ok = py_exec((const char*)data, filename->data, EXEC_MODE, mod); + py_assign(py_retval(), mod); + + c11_string__delete(filename); + c11_string__delete(slashed_path); + if(need_free) PK_FREE((void*)data); + return ok ? 1 : -1; +} + +bool py_importlib_reload(py_Ref module) { + VM* vm = pk_current_vm; + py_ModuleInfo* mi = py_touserdata(module); + // We should ensure that the module is its original py_GlobalRef + module = mi->self; + c11_sv path = c11_string__sv(mi->path); + c11_string* slashed_path = c11_sv__replace(path, '.', PK_PLATFORM_SEP); + c11_string* filename = c11_string__new3("%s.py", slashed_path->data); + char* data = vm->callbacks.importfile(filename->data); + if(data == NULL) { + c11_string__delete(filename); + filename = c11_string__new3("%s%c__init__.py", slashed_path->data, PK_PLATFORM_SEP); + data = vm->callbacks.importfile(filename->data); + } + c11_string__delete(slashed_path); + if(data == NULL) return ImportError("module '%v' not found", path); + // py_cleardict(module); BUG: removing old classes will cause RELOAD_MODE to fail + bool ok = py_exec(data, filename->data, RELOAD_MODE, module); + c11_string__delete(filename); + PK_FREE(data); + py_assign(py_retval(), module); + return ok; +} diff --git a/src/public/py_dict.c b/src/public/PyDict.c similarity index 100% rename from src/public/py_dict.c rename to src/public/PyDict.c diff --git a/src/public/py_exception.c b/src/public/PyException.c similarity index 98% rename from src/public/py_exception.c rename to src/public/PyException.c index cd4267ba..18c976cc 100644 --- a/src/public/py_exception.c +++ b/src/public/PyException.c @@ -141,28 +141,6 @@ py_Type pk_StopIteration__register() { return type; } -////////////////////////////////////////////////// -bool py_checkexc() { - VM* vm = pk_current_vm; - return !py_isnil(&vm->unhandled_exc); -} - -bool py_matchexc(py_Type type) { - VM* vm = pk_current_vm; - if(py_isnil(&vm->unhandled_exc)) return false; - bool ok = py_issubclass(vm->unhandled_exc.type, type); - if(ok) vm->last_retval = vm->unhandled_exc; - return ok; -} - -void py_clearexc(py_StackRef p0) { - VM* vm = pk_current_vm; - py_newnil(&vm->unhandled_exc); - if(p0) { - c11__rtassert(p0 >= vm->stack.begin && p0 <= vm->stack.sp); - vm->stack.sp = p0; - } -} static void c11_sbuf__write_exc(c11_sbuf* self, py_Ref exc) { c11_sbuf__write_cstr(self, "Traceback (most recent call last):\n"); @@ -212,6 +190,29 @@ char* safe_stringify_exception(py_Ref exc) { return c11_strdup(message); } +////////////////////////////////////////////////// +bool py_checkexc() { + VM* vm = pk_current_vm; + return !py_isnil(&vm->unhandled_exc); +} + +bool py_matchexc(py_Type type) { + VM* vm = pk_current_vm; + if(py_isnil(&vm->unhandled_exc)) return false; + bool ok = py_issubclass(vm->unhandled_exc.type, type); + if(ok) vm->last_retval = vm->unhandled_exc; + return ok; +} + +void py_clearexc(py_StackRef p0) { + VM* vm = pk_current_vm; + py_newnil(&vm->unhandled_exc); + if(p0) { + c11__rtassert(p0 >= vm->stack.begin && p0 <= vm->stack.sp); + vm->stack.sp = p0; + } +} + void py_printexc() { char* msg = py_formatexc(); if(!msg) return; @@ -299,4 +300,10 @@ bool KeyError(py_Ref key) { bool ok = py_tpcall(tp_KeyError, 1, key); if(!ok) return false; return py_raise(py_retval()); -} \ No newline at end of file +} + +bool StopIteration() { + bool ok = py_tpcall(tp_StopIteration, 0, NULL); + if(!ok) return false; + return py_raise(py_retval()); +} diff --git a/src/public/py_list.c b/src/public/PyList.c similarity index 100% rename from src/public/py_list.c rename to src/public/PyList.c diff --git a/src/public/py_slice.c b/src/public/PySlice.c similarity index 91% rename from src/public/py_slice.c rename to src/public/PySlice.c index 5d0fe627..3e69ab1d 100644 --- a/src/public/py_slice.c +++ b/src/public/PySlice.c @@ -4,12 +4,20 @@ #include "pocketpy/objects/object.h" #include "pocketpy/interpreter/vm.h" -void py_newslice(py_OutRef out) { +py_ObjectRef py_newslice(py_OutRef out) { VM* vm = pk_current_vm; PyObject* obj = ManagedHeap__gcnew(&vm->heap, tp_slice, 3, 0); out->type = tp_slice; out->is_ptr = true; out->_obj = obj; + return PyObject__slots(obj); +} + +void py_newsliceint(py_OutRef out, py_i64 start, py_i64 stop, py_i64 step) { + py_Ref slots = py_newslice(out); + py_newint(&slots[0], start); + py_newint(&slots[1], stop); + py_newint(&slots[2], step); } static bool slice__new__(int argc, py_Ref argv) { diff --git a/src/public/py_tuple.c b/src/public/PyTuple.c similarity index 100% rename from src/public/py_tuple.c rename to src/public/PyTuple.c diff --git a/src/public/py_ops.c b/src/public/PythonOps.c similarity index 78% rename from src/public/py_ops.c rename to src/public/PythonOps.c index 16e63a83..3767b278 100644 --- a/src/public/py_ops.c +++ b/src/public/PythonOps.c @@ -4,6 +4,48 @@ #include "pocketpy/objects/base.h" #include "pocketpy/pocketpy.h" +bool py_binaryadd(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __add__, __radd__); } + +bool py_binarysub(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __sub__, __rsub__); } + +bool py_binarymul(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __mul__, __rmul__); } + +bool py_binarytruediv(py_Ref lhs, py_Ref rhs) { + return py_binaryop(lhs, rhs, __truediv__, __rtruediv__); +} + +bool py_binaryfloordiv(py_Ref lhs, py_Ref rhs) { + return py_binaryop(lhs, rhs, __floordiv__, __rfloordiv__); +} + +bool py_binarymod(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __mod__, __rmod__); } + +bool py_binarypow(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __pow__, __rpow__); } + +bool py_binarylshift(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __lshift__, 0); } + +bool py_binaryrshift(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __rshift__, 0); } + +bool py_binaryand(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __and__, 0); } + +bool py_binaryor(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __or__, 0); } + +bool py_binaryxor(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __xor__, 0); } + +bool py_binarymatmul(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __matmul__, 0); } + +bool py_eq(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __eq__, __eq__); } + +bool py_ne(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __ne__, __ne__); } + +bool py_lt(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __lt__, __gt__); } + +bool py_le(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __le__, __ge__); } + +bool py_gt(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __gt__, __lt__); } + +bool py_ge(py_Ref lhs, py_Ref rhs) { return py_binaryop(lhs, rhs, __ge__, __le__); } + bool py_isidentical(py_Ref lhs, py_Ref rhs) { if(lhs->type != rhs->type) return false; switch(lhs->type) { @@ -52,6 +94,29 @@ int py_bool(py_Ref val) { } } +int py_equal(py_Ref lhs, py_Ref rhs) { + if(py_isidentical(lhs, rhs)) return 1; + if(!py_eq(lhs, rhs)) return -1; + return py_bool(py_retval()); +} + +int py_less(py_Ref lhs, py_Ref rhs) { + if(!py_lt(lhs, rhs)) return -1; + return py_bool(py_retval()); +} + +bool py_callable(py_Ref val) { + switch(val->type) { + case tp_nativefunc: return true; + case tp_function: return true; + case tp_type: return true; + case tp_boundmethod: return true; + case tp_staticmethod: return true; + case tp_classmethod: return true; + default: return py_tpfindmagic(val->type, __call__); + } +} + bool py_hash(py_Ref val, int64_t* out) { py_TypeInfo* ti = pk_typeinfo(val->type); do { @@ -119,6 +184,20 @@ int py_next(py_Ref val) { return -1; } +bool py_str(py_Ref val) { + if(val->type == tp_str) { + py_assign(py_retval(), val); + return true; + } + py_Ref tmp = py_tpfindmagic(val->type, __str__); + if(!tmp) return py_repr(val); + return py_call(tmp, 1, val); +} + +bool py_repr(py_Ref val) { return pk_callmagic(__repr__, 1, val); } + +bool py_len(py_Ref val) { return pk_callmagic(__len__, 1, val); } + bool py_getattr(py_Ref self, py_Name name) { // https://docs.python.org/3/howto/descriptor.html#invocation-from-an-instance py_TypeInfo* ti = pk_typeinfo(self->type); @@ -277,14 +356,3 @@ bool py_delitem(py_Ref self, py_Ref key) { py_shrink(2); return ok; } - -int py_equal(py_Ref lhs, py_Ref rhs) { - if(py_isidentical(lhs, rhs)) return 1; - if(!py_eq(lhs, rhs)) return -1; - return py_bool(py_retval()); -} - -int py_less(py_Ref lhs, py_Ref rhs) { - if(!py_lt(lhs, rhs)) return -1; - return py_bool(py_retval()); -} \ No newline at end of file diff --git a/src/public/internal.c b/src/public/StackOps.c similarity index 54% rename from src/public/internal.c rename to src/public/StackOps.c index 964800d7..4cdd1ee7 100644 --- a/src/public/internal.c +++ b/src/public/StackOps.c @@ -1,140 +1,61 @@ -#include "pocketpy/interpreter/typeinfo.h" -#include "pocketpy/objects/codeobject.h" #include "pocketpy/pocketpy.h" - -#include "pocketpy/common/utils.h" -#include "pocketpy/common/name.h" #include "pocketpy/interpreter/vm.h" -_Thread_local VM* pk_current_vm; - -static bool pk_initialized; -static bool pk_finalized; - -static VM pk_default_vm; -static VM* pk_all_vm[16]; -static py_TValue _True, _False, _None, _NIL; - -void py_initialize() { - c11__rtassert(!pk_finalized); - - if(pk_initialized) { - // c11__abort("py_initialize() can only be called once!"); - return; - } - - pk_names_initialize(); - - // check endianness - int x = 1; - bool is_little_endian = *(char*)&x == 1; - if(!is_little_endian) c11__abort("is_little_endian != true"); - - static_assert(sizeof(py_TValue) == 24, "sizeof(py_TValue) != 24"); - static_assert(offsetof(py_TValue, extra) == 4, "offsetof(py_TValue, extra) != 4"); - - pk_current_vm = pk_all_vm[0] = &pk_default_vm; - - // initialize some convenient references - py_newbool(&_True, true); - py_newbool(&_False, false); - py_newnone(&_None); - py_newnil(&_NIL); - VM__ctor(&pk_default_vm); - - pk_initialized = true; +PK_INLINE py_Ref py_peek(int i) { + assert(i <= 0); + return pk_current_vm->stack.sp + i; } -void* py_malloc(size_t size) { return PK_MALLOC(size); } - -void* py_realloc(void* ptr, size_t size) { return PK_REALLOC(ptr, size); } - -void py_free(void* ptr) { PK_FREE(ptr); } - -py_GlobalRef py_True() { return &_True; } - -py_GlobalRef py_False() { return &_False; } - -py_GlobalRef py_None() { return &_None; } - -py_GlobalRef py_NIL() { return &_NIL; } - -void py_finalize() { - if(pk_finalized) c11__abort("py_finalize() can only be called once!"); - pk_finalized = true; - - for(int i = 1; i < 16; i++) { - VM* vm = pk_all_vm[i]; - if(vm) { - // temp fix https://github.com/pocketpy/pocketpy/issues/315 - // TODO: refactor VM__ctor and VM__dtor - pk_current_vm = vm; - VM__dtor(vm); - PK_FREE(vm); - } - } - pk_current_vm = &pk_default_vm; - VM__dtor(&pk_default_vm); - pk_current_vm = NULL; - - pk_names_finalize(); -} - -void py_switchvm(int index) { - if(index < 0 || index >= 16) c11__abort("invalid vm index"); - if(!pk_all_vm[index]) { - pk_current_vm = pk_all_vm[index] = PK_MALLOC(sizeof(VM)); - memset(pk_current_vm, 0, sizeof(VM)); - VM__ctor(pk_all_vm[index]); - } else { - pk_current_vm = pk_all_vm[index]; - } -} - -void py_resetvm() { +PK_INLINE void py_push(py_Ref src) { VM* vm = pk_current_vm; - VM__dtor(vm); - memset(vm, 0, sizeof(VM)); - VM__ctor(vm); + *vm->stack.sp++ = *src; } -void py_resetallvm() { - for(int i = 0; i < 16; i++) { - py_switchvm(i); - py_resetvm(); - } - py_switchvm(0); +PK_INLINE void py_pushnil() { + VM* vm = pk_current_vm; + py_newnil(vm->stack.sp++); } -int py_currentvm() { - for(int i = 0; i < 16; i++) { - if(pk_all_vm[i] == pk_current_vm) return i; - } - return -1; +PK_INLINE void py_pushnone() { + VM* vm = pk_current_vm; + py_newnone(vm->stack.sp++); } -void* py_getvmctx() { return pk_current_vm->ctx; } - -void py_setvmctx(void* ctx) { pk_current_vm->ctx = ctx; } - -void py_sys_setargv(int argc, char** argv) { - py_GlobalRef sys = py_getmodule("sys"); - py_Ref argv_list = py_getdict(sys, py_name("argv")); - py_list_clear(argv_list); - for(int i = 0; i < argc; i++) { - py_newstr(py_list_emplace(argv_list), argv[i]); - } +PK_INLINE void py_pushname(py_Name name) { + VM* vm = pk_current_vm; + py_newint(vm->stack.sp++, (uintptr_t)name); } -py_Callbacks* py_callbacks() { return &pk_current_vm->callbacks; } +PK_INLINE void py_pop() { + VM* vm = pk_current_vm; + vm->stack.sp--; +} -const char* pk_opname(Opcode op) { - const static char* OP_NAMES[] = { -#define OPCODE(name) #name, -#include "pocketpy/xmacros/opcodes.h" -#undef OPCODE - }; - return OP_NAMES[op]; +PK_INLINE void py_shrink(int n) { + VM* vm = pk_current_vm; + vm->stack.sp -= n; +} + +PK_INLINE py_Ref py_pushtmp() { + VM* vm = pk_current_vm; + return vm->stack.sp++; +} + +PK_INLINE bool py_pushmethod(py_Name name) { + bool ok = pk_loadmethod(py_peek(-1), name); + if(ok) pk_current_vm->stack.sp++; + return ok; +} + +bool py_pusheval(const char* expr, py_GlobalRef module) { + bool ok = py_exec(expr, "", EVAL_MODE, module); + if(!ok) return false; + py_push(py_retval()); + return true; +} + +PK_INLINE bool py_vectorcall(uint16_t argc, uint16_t kwargc) { + return VM__vectorcall(pk_current_vm, argc, kwargc, false) != RES_ERROR; } bool py_call(py_Ref f, int argc, py_Ref argv) { @@ -180,15 +101,15 @@ bool py_callcfunc(py_CFunction f, int argc, py_Ref argv) { } #endif -bool py_vectorcall(uint16_t argc, uint16_t kwargc) { - return VM__vectorcall(pk_current_vm, argc, kwargc, false) != RES_ERROR; +bool py_tpcall(py_Type type, int argc, py_Ref argv) { + return py_call(py_tpobject(type), argc, argv); } -PK_INLINE py_Ref py_retval() { return &pk_current_vm->last_retval; } - -bool py_pushmethod(py_Name name) { - bool ok = pk_loadmethod(py_peek(-1), name); - if(ok) pk_current_vm->stack.sp++; +bool py_binaryop(py_Ref lhs, py_Ref rhs, py_Name op, py_Name rop) { + py_push(lhs); + py_push(rhs); + bool ok = pk_stack_binaryop(pk_current_vm, op, rop); + py_shrink(2); return ok; } @@ -267,10 +188,6 @@ bool pk_loadmethod(py_StackRef self, py_Name name) { return false; } -bool py_tpcall(py_Type type, int argc, py_Ref argv) { - return py_call(py_tpobject(type), argc, argv); -} - bool pk_callmagic(py_Name name, int argc, py_Ref argv) { assert(argc >= 1); // assert(py_ismagicname(name)); @@ -278,9 +195,3 @@ bool pk_callmagic(py_Name name, int argc, py_Ref argv) { if(!tmp) return AttributeError(argv, name); return py_call(tmp, argc, argv); } - -bool StopIteration() { - bool ok = py_tpcall(tp_StopIteration, 0, NULL); - if(!ok) return false; - return py_raise(py_retval()); -} diff --git a/src/public/TypeSystem.c b/src/public/TypeSystem.c new file mode 100644 index 00000000..fde04a5b --- /dev/null +++ b/src/public/TypeSystem.c @@ -0,0 +1,104 @@ +#include "pocketpy/objects/base.h" +#include "pocketpy/pocketpy.h" + +#include "pocketpy/interpreter/vm.h" + +py_Type py_newtype(const char* name, py_Type base, const py_GlobalRef module, void (*dtor)(void*)) { + if(strlen(name) == 0) c11__abort("type name cannot be empty"); + py_Type type = pk_newtype(name, base, module, dtor, false, false); + if(module) py_setdict(module, py_name(name), py_tpobject(type)); + return type; +} + +PK_INLINE bool py_istype(py_Ref self, py_Type type) { return self->type == type; } + +PK_INLINE py_Type py_typeof(py_Ref self) { return 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_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; +} + +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); +} + +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(); +} + +PK_INLINE py_Ref py_tpfindmagic(py_Type t, py_Name name) { + // assert(py_ismagicname(name)); + return py_tpfindname(t, name); +} + +PK_INLINE py_ItemRef py_tpfindname(py_Type type, py_Name name) { + py_TypeInfo* ti = pk_typeinfo(type); + return pk_tpfindname(ti, name); +} + +PK_INLINE py_Type py_tpbase(py_Type t) { + assert(t); + py_TypeInfo* ti = pk_typeinfo(t); + return ti->base; +} + +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); +} + +void py_tpsetfinal(py_Type type) { + assert(type); + py_TypeInfo* ti = pk_typeinfo(type); + ti->is_final = true; +} + +void py_tphookattributes(py_Type type, + bool (*getattribute)(py_Ref self, py_Name name), + bool (*setattribute)(py_Ref self, py_Name name, py_Ref val), + bool (*delattribute)(py_Ref self, py_Name name), + bool (*getunboundmethod)(py_Ref self, py_Name name)) { + assert(type); + py_TypeInfo* ti = pk_typeinfo(type); + ti->getattribute = getattribute; + ti->setattribute = setattribute; + ti->delattribute = delattribute; + ti->getunboundmethod = getunboundmethod; +} diff --git a/src/public/cast.c b/src/public/ValueCast.c similarity index 65% rename from src/public/cast.c rename to src/public/ValueCast.c index 39d8fb66..f2f2cff3 100644 --- a/src/public/cast.c +++ b/src/public/ValueCast.c @@ -4,7 +4,7 @@ #include "pocketpy/objects/object.h" #include "pocketpy/interpreter/vm.h" -int64_t py_toint(py_Ref self) { +py_i64 py_toint(py_Ref self) { assert(self->type == tp_int); return self->_i64; } @@ -55,3 +55,30 @@ void* py_touserdata(py_Ref self) { assert(self && self->is_ptr); return PyObject__userdata(self->_obj); } + +const char* py_tostr(py_Ref self) { return pk_tostr(self)->data; } + +const char* py_tostrn(py_Ref self, int* size) { + c11_string* ud = pk_tostr(self); + *size = ud->size; + return ud->data; +} + +c11_sv py_tosv(py_Ref self) { + c11_string* ud = pk_tostr(self); + return c11_string__sv(ud); +} + +unsigned char* py_tobytes(py_Ref self, int* size) { + assert(self->type == tp_bytes); + c11_bytes* ud = PyObject__userdata(self->_obj); + *size = ud->size; + return ud->data; +} + +void py_bytes_resize(py_Ref self, int size) { + assert(self->type == tp_bytes); + c11_bytes* ud = PyObject__userdata(self->_obj); + if(size > ud->size) c11__abort("bytes can only be resized down: %d > %d", ud->size, size); + ud->size = size; +} diff --git a/src/public/values.c b/src/public/ValueCreation.c similarity index 62% rename from src/public/values.c rename to src/public/ValueCreation.c index 0c35edb0..426cc8e7 100644 --- a/src/public/values.c +++ b/src/public/ValueCreation.c @@ -32,6 +32,54 @@ void py_newbool(py_OutRef out, bool val) { out->_bool = val; } +void py_newstr(py_OutRef out, const char* data) { py_newstrv(out, (c11_sv){data, strlen(data)}); } + +char* py_newstrn(py_OutRef out, int size) { + if(size < 16) { + out->type = tp_str; + out->is_ptr = false; + c11_string* ud = (c11_string*)(&out->extra); + c11_string__ctor3(ud, size); + return ud->data; + } + ManagedHeap* heap = &pk_current_vm->heap; + int total_size = sizeof(c11_string) + size + 1; + PyObject* obj = ManagedHeap__gcnew(heap, tp_str, 0, total_size); + c11_string* ud = PyObject__userdata(obj); + c11_string__ctor3(ud, size); + out->type = tp_str; + out->is_ptr = true; + out->_obj = obj; + return ud->data; +} + +void py_newstrv(py_OutRef out, c11_sv sv) { + char* data = py_newstrn(out, sv.size); + memcpy(data, sv.data, sv.size); +} + +void py_newfstr(py_OutRef out, const char* fmt, ...) { + c11_sbuf buf; + c11_sbuf__ctor(&buf); + va_list args; + va_start(args, fmt); + pk_vsprintf(&buf, fmt, args); + va_end(args); + c11_sbuf__py_submit(&buf, out); +} + +unsigned char* py_newbytes(py_OutRef out, int size) { + ManagedHeap* heap = &pk_current_vm->heap; + // 4 bytes size + data + PyObject* obj = ManagedHeap__gcnew(heap, tp_bytes, 0, sizeof(c11_bytes) + size); + c11_bytes* ud = PyObject__userdata(obj); + ud->size = size; + out->type = tp_bytes; + out->is_ptr = true; + out->_obj = obj; + return ud->data; +} + void py_newnone(py_OutRef out) { out->type = tp_NoneType; out->is_ptr = false; @@ -58,66 +106,6 @@ void py_newnativefunc(py_OutRef out, py_CFunction f) { out->_cfunc = f; } -void py_bindmethod(py_Type type, const char* name, py_CFunction f) { - py_TValue tmp; - py_newnativefunc(&tmp, f); - py_setdict(py_tpobject(type), py_name(name), &tmp); -} - -void py_bindstaticmethod(py_Type type, const char* name, py_CFunction f) { - py_TValue tmp; - py_newnativefunc(&tmp, f); - bool ok = py_tpcall(tp_staticmethod, 1, &tmp); - if(!ok) { - py_printexc(); - c11__abort("py_bindstaticmethod(): failed to create staticmethod"); - } - py_setdict(py_tpobject(type), py_name(name), py_retval()); -} - -void py_bindfunc(py_Ref obj, const char* name, py_CFunction f) { - py_TValue tmp; - py_newnativefunc(&tmp, f); - py_setdict(obj, py_name(name), &tmp); -} - -void py_bindproperty(py_Type type, const char* name, py_CFunction getter, py_CFunction setter) { - py_TValue tmp; - py_newobject(&tmp, tp_property, 2, 0); - py_newnativefunc(py_getslot(&tmp, 0), getter); - if(setter) { - py_newnativefunc(py_getslot(&tmp, 1), setter); - } else { - py_setslot(&tmp, 1, py_None()); - } - py_setdict(py_tpobject(type), py_name(name), &tmp); -} - -void py_bindmagic(py_Type type, py_Name name, py_CFunction f) { - py_Ref tmp = py_emplacedict(py_tpobject(type), name); - py_newnativefunc(tmp, f); -} - -void py_bind(py_Ref obj, const char* sig, py_CFunction f) { - py_Ref tmp = py_pushtmp(); - py_Name name = py_newfunction(tmp, sig, f, NULL, 0); - py_setdict(obj, name, tmp); - py_pop(); -} - -void py_macrobind(const char* sig, py_CFunction f) { - py_Ref tmp = py_pushtmp(); - py_Name name = py_newfunction(tmp, sig, f, NULL, 0); - NameDict__set(&pk_current_vm->compile_time_funcs, name, tmp); - py_pop(); -} - -py_ItemRef py_macroget(py_Name name) { - NameDict* d = &pk_current_vm->compile_time_funcs; - if(d->length == 0) return NULL; - return NameDict__try_get(d, name); -} - py_Name py_newfunction(py_OutRef out, const char* sig, py_CFunction f, diff --git a/src/public/typecast.c b/src/public/typecast.c deleted file mode 100644 index 69dbf690..00000000 --- a/src/public/typecast.c +++ /dev/null @@ -1,45 +0,0 @@ -#include "pocketpy/objects/base.h" -#include "pocketpy/pocketpy.h" - -#include "pocketpy/objects/object.h" -#include "pocketpy/interpreter/vm.h" - -PK_INLINE 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; -} \ No newline at end of file