mirror of
				https://github.com/pocketpy/pocketpy
				synced 2025-10-31 08:50:17 +00:00 
			
		
		
		
	Compare commits
	
		
			4 Commits
		
	
	
		
			f64885f4ca
			...
			8058cee3a9
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 8058cee3a9 | ||
|  | e187a61624 | ||
|  | 68a2186728 | ||
|  | 0b09246a6d | 
| @ -6,7 +6,7 @@ SRC=$(find src/ -name "*.c") | |||||||
| 
 | 
 | ||||||
| FLAGS="-std=c11 -lm -ldl -lpthread -Iinclude -O0 -Wfatal-errors -g -DDEBUG -DPK_ENABLE_OS=1" | FLAGS="-std=c11 -lm -ldl -lpthread -Iinclude -O0 -Wfatal-errors -g -DDEBUG -DPK_ENABLE_OS=1" | ||||||
| 
 | 
 | ||||||
| SANITIZE_FLAGS="-fsanitize=address,leak,undefined" | SANITIZE_FLAGS="-fsanitize=address,leak,undefined -fno-sanitize=function" | ||||||
| 
 | 
 | ||||||
| if [ "$(uname)" == "Darwin" ]; then | if [ "$(uname)" == "Darwin" ]; then | ||||||
|     SANITIZE_FLAGS="-fsanitize=address,undefined" |     SANITIZE_FLAGS="-fsanitize=address,undefined" | ||||||
|  | |||||||
| @ -16,13 +16,26 @@ typedef struct py_TypeInfo { | |||||||
|     bool is_python;  // is it a python class? (not derived from c object)
 |     bool is_python;  // is it a python class? (not derived from c object)
 | ||||||
|     bool is_sealed;  // can it be subclassed?
 |     bool is_sealed;  // can it be subclassed?
 | ||||||
| 
 | 
 | ||||||
|     py_Dtor dtor;  // destructor for this type, NULL if no dtor
 |  | ||||||
| 
 |  | ||||||
|     py_TValue annotations; |     py_TValue annotations; | ||||||
| 
 |     py_Dtor dtor;  // destructor for this type, NULL if no dtor
 | ||||||
|     void (*on_end_subclass)(struct py_TypeInfo*);  // backdoor for enum module
 |     void (*on_end_subclass)(struct py_TypeInfo*);  // backdoor for enum module
 | ||||||
| } py_TypeInfo; | } py_TypeInfo; | ||||||
| 
 | 
 | ||||||
| py_TypeInfo* pk_typeinfo(py_Type type); | py_TypeInfo* pk_typeinfo(py_Type type); | ||||||
| py_ItemRef pk_tpfindname(py_TypeInfo* ti, py_Name name); | py_ItemRef pk_tpfindname(py_TypeInfo* ti, py_Name name); | ||||||
| #define pk_tpfindmagic pk_tpfindname | #define pk_tpfindmagic pk_tpfindname | ||||||
|  | 
 | ||||||
|  | py_Type pk_newtype(const char* name, | ||||||
|  |                    py_Type base, | ||||||
|  |                    const py_GlobalRef module, | ||||||
|  |                    void (*dtor)(void*), | ||||||
|  |                    bool is_python, | ||||||
|  |                    bool is_sealed); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | py_Type pk_newtypewithmode(py_Name name, | ||||||
|  |                    py_Type base, | ||||||
|  |                    const py_GlobalRef module, | ||||||
|  |                    void (*dtor)(void*), | ||||||
|  |                    bool is_python, | ||||||
|  |                    bool is_sealed, enum py_CompileMode mode); | ||||||
| @ -34,6 +34,13 @@ typedef struct TypePointer { | |||||||
|     py_Dtor dtor; |     py_Dtor dtor; | ||||||
| } TypePointer; | } TypePointer; | ||||||
| 
 | 
 | ||||||
|  | typedef struct py_ModuleInfo { | ||||||
|  |     c11_string* name; | ||||||
|  |     c11_string* package; | ||||||
|  |     c11_string* path; | ||||||
|  |     py_GlobalRef self;  // weakref to the original module object
 | ||||||
|  | } py_ModuleInfo; | ||||||
|  | 
 | ||||||
| typedef struct VM { | typedef struct VM { | ||||||
|     py_Frame* top_frame; |     py_Frame* top_frame; | ||||||
| 
 | 
 | ||||||
| @ -100,13 +107,6 @@ typedef enum FrameResult { | |||||||
| 
 | 
 | ||||||
| FrameResult VM__run_top_frame(VM* self); | FrameResult VM__run_top_frame(VM* self); | ||||||
| 
 | 
 | ||||||
| py_Type pk_newtype(const char* name, |  | ||||||
|                    py_Type base, |  | ||||||
|                    const py_GlobalRef module, |  | ||||||
|                    void (*dtor)(void*), |  | ||||||
|                    bool is_python, |  | ||||||
|                    bool is_sealed); |  | ||||||
| 
 |  | ||||||
| FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall); | FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall); | ||||||
| 
 | 
 | ||||||
| const char* pk_opname(Opcode op); | const char* pk_opname(Opcode op); | ||||||
| @ -146,6 +146,7 @@ py_Type pk_nativefunc__register(); | |||||||
| py_Type pk_boundmethod__register(); | py_Type pk_boundmethod__register(); | ||||||
| py_Type pk_range__register(); | py_Type pk_range__register(); | ||||||
| py_Type pk_range_iterator__register(); | py_Type pk_range_iterator__register(); | ||||||
|  | py_Type pk_module__register(); | ||||||
| py_Type pk_BaseException__register(); | py_Type pk_BaseException__register(); | ||||||
| py_Type pk_Exception__register(); | py_Type pk_Exception__register(); | ||||||
| py_Type pk_StopIteration__register(); | py_Type pk_StopIteration__register(); | ||||||
|  | |||||||
| @ -92,7 +92,8 @@ typedef bool (*py_CFunction)(int argc, py_StackRef argv) PY_RAISE PY_RETURN; | |||||||
| /// + `EXEC_MODE`: for statements.
 | /// + `EXEC_MODE`: for statements.
 | ||||||
| /// + `EVAL_MODE`: for expressions.
 | /// + `EVAL_MODE`: for expressions.
 | ||||||
| /// + `SINGLE_MODE`: for REPL or jupyter notebook execution.
 | /// + `SINGLE_MODE`: for REPL or jupyter notebook execution.
 | ||||||
| enum py_CompileMode { EXEC_MODE, EVAL_MODE, SINGLE_MODE }; | /// + `RELOAD_MODE`: for reloading a module without allocating new types if possible.
 | ||||||
|  | enum py_CompileMode { EXEC_MODE, EVAL_MODE, SINGLE_MODE, RELOAD_MODE }; | ||||||
| 
 | 
 | ||||||
| /************* Global Setup *************/ | /************* Global Setup *************/ | ||||||
| 
 | 
 | ||||||
| @ -405,6 +406,8 @@ PK_API py_ItemRef py_emplacedict(py_Ref self, py_Name name); | |||||||
| /// NOTE: Be careful if `f` modifies the object's `__dict__`.
 | /// NOTE: Be careful if `f` modifies the object's `__dict__`.
 | ||||||
| PK_API bool | PK_API bool | ||||||
|     py_applydict(py_Ref self, bool (*f)(py_Name name, py_Ref val, void* ctx), void* ctx) PY_RAISE; |     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.
 | /// Get the i-th slot of the object.
 | ||||||
| /// The object must have slots and `i` must be in valid range.
 | /// The object must have slots and `i` must be in valid range.
 | ||||||
| @ -555,7 +558,7 @@ PK_API py_GlobalRef py_newmodule(const char* path); | |||||||
| /// Get a module by path.
 | /// Get a module by path.
 | ||||||
| PK_API py_GlobalRef py_getmodule(const char* path); | PK_API py_GlobalRef py_getmodule(const char* path); | ||||||
| /// Reload an existing module.
 | /// Reload an existing module.
 | ||||||
| PK_API bool py_importlib_reload(py_GlobalRef module) PY_RAISE PY_RETURN; | PK_API bool py_importlib_reload(py_Ref module) PY_RAISE PY_RETURN; | ||||||
| 
 | 
 | ||||||
| /// Import a module.
 | /// Import a module.
 | ||||||
| /// The result will be set to `py_retval()`.
 | /// The result will be set to `py_retval()`.
 | ||||||
|  | |||||||
| @ -1042,12 +1042,13 @@ FrameResult VM__run_top_frame(VM* self) { | |||||||
|                     goto __ERROR; |                     goto __ERROR; | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 py_Type type = pk_newtype(py_name2str(name), |                 py_Type type = pk_newtypewithmode(name, | ||||||
|                                           base, |                                                   base, | ||||||
|                                           frame->module, |                                                   frame->module, | ||||||
|                                           NULL, |                                                   NULL, | ||||||
|                                           base_ti->is_python, |                                                   base_ti->is_python, | ||||||
|                                           false); |                                                   false, | ||||||
|  |                                                   frame->co->src->mode); | ||||||
|                 PUSH(py_tpobject(type)); |                 PUSH(py_tpobject(type)); | ||||||
|                 self->curr_class = TOP(); |                 self->curr_class = TOP(); | ||||||
|                 DISPATCH(); |                 DISPATCH(); | ||||||
|  | |||||||
| @ -45,5 +45,108 @@ const char* py_tpname(py_Type type) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| py_TypeInfo* pk_typeinfo(py_Type type) { | 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); | ||||||
|  |     } | ||||||
|  | #endif | ||||||
|     return c11__getitem(TypePointer, &pk_current_vm->types, type).ti; |     return c11__getitem(TypePointer, &pk_current_vm->types, type).ti; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void py_TypeInfo__common_init(py_Name name, | ||||||
|  |                                      py_Type base, | ||||||
|  |                                      py_Type index, | ||||||
|  |                                      const py_GlobalRef module, | ||||||
|  |                                      void (*dtor)(void*), | ||||||
|  |                                      bool is_python, | ||||||
|  |                                      bool is_sealed, | ||||||
|  |                                      py_TypeInfo* self, | ||||||
|  |                                      py_TValue* typeobject) { | ||||||
|  |     py_TypeInfo* base_ti = base ? pk_typeinfo(base) : NULL; | ||||||
|  |     if(base_ti && base_ti->is_sealed) { | ||||||
|  |         c11__abort("type '%s' is not an acceptable base type", py_name2str(base_ti->name)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     self->name = name; | ||||||
|  |     self->index = index; | ||||||
|  |     self->base = base; | ||||||
|  |     self->base_ti = base_ti; | ||||||
|  | 
 | ||||||
|  |     py_assign(&self->self, typeobject); | ||||||
|  |     self->module = module ? module : py_NIL(); | ||||||
|  | 
 | ||||||
|  |     if(!dtor && base) dtor = base_ti->dtor; | ||||||
|  |     self->is_python = is_python; | ||||||
|  |     self->is_sealed = is_sealed; | ||||||
|  | 
 | ||||||
|  |     self->annotations = *py_NIL(); | ||||||
|  |     self->dtor = dtor; | ||||||
|  |     self->on_end_subclass = NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | py_Type pk_newtype(const char* name, | ||||||
|  |                    py_Type base, | ||||||
|  |                    const py_GlobalRef module, | ||||||
|  |                    void (*dtor)(void*), | ||||||
|  |                    bool is_python, | ||||||
|  |                    bool is_sealed) { | ||||||
|  |     py_Type index = pk_current_vm->types.length; | ||||||
|  |     py_TypeInfo* self = py_newobject(py_retval(), tp_type, -1, sizeof(py_TypeInfo)); | ||||||
|  |     py_TypeInfo__common_init(py_name(name), | ||||||
|  |                              base, | ||||||
|  |                              index, | ||||||
|  |                              module, | ||||||
|  |                              dtor, | ||||||
|  |                              is_python, | ||||||
|  |                              is_sealed, | ||||||
|  |                              self, | ||||||
|  |                              py_retval()); | ||||||
|  |     TypePointer* pointer = c11_vector__emplace(&pk_current_vm->types); | ||||||
|  |     pointer->ti = self; | ||||||
|  |     pointer->dtor = self->dtor; | ||||||
|  |     return index; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | py_Type pk_newtypewithmode(py_Name name, | ||||||
|  |                            py_Type base, | ||||||
|  |                            const py_GlobalRef module, | ||||||
|  |                            void (*dtor)(void*), | ||||||
|  |                            bool is_python, | ||||||
|  |                            bool is_sealed, | ||||||
|  |                            enum py_CompileMode mode) { | ||||||
|  |     if(mode == RELOAD_MODE && module != NULL) { | ||||||
|  |         py_ItemRef old_class = py_getdict(module, name); | ||||||
|  |         if(old_class != NULL && py_istype(old_class, tp_type)) { | ||||||
|  | #ifndef NDEBUG | ||||||
|  |             const char* name_cstr = py_name2str(name); | ||||||
|  |             (void)name_cstr;  // avoid unused warning
 | ||||||
|  | #endif | ||||||
|  |             py_cleardict(old_class); | ||||||
|  |             py_TypeInfo* self = py_touserdata(old_class); | ||||||
|  |             py_Type index = self->index; | ||||||
|  |             py_TypeInfo__common_init(name, | ||||||
|  |                                      base, | ||||||
|  |                                      index, | ||||||
|  |                                      module, | ||||||
|  |                                      dtor, | ||||||
|  |                                      is_python, | ||||||
|  |                                      is_sealed, | ||||||
|  |                                      self, | ||||||
|  |                                      &self->self); | ||||||
|  |             TypePointer* pointer = c11__at(TypePointer, &pk_current_vm->types, index); | ||||||
|  |             pointer->ti = self; | ||||||
|  |             pointer->dtor = self->dtor; | ||||||
|  |             return index; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return pk_newtype(py_name2str(name), base, module, dtor, is_python, is_sealed); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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; | ||||||
| } | } | ||||||
| @ -50,9 +50,9 @@ void VM__ctor(VM* self) { | |||||||
| 
 | 
 | ||||||
|     const static BinTreeConfig modules_config = { |     const static BinTreeConfig modules_config = { | ||||||
|         .f_cmp = BinTree__cmp_cstr, |         .f_cmp = BinTree__cmp_cstr, | ||||||
|         .need_free_key = true, |         .need_free_key = false, | ||||||
|     }; |     }; | ||||||
|     BinTree__ctor(&self->modules, c11_strdup(""), py_NIL(), &modules_config); |     BinTree__ctor(&self->modules, "", py_NIL(), &modules_config); | ||||||
|     c11_vector__ctor(&self->types, sizeof(TypePointer)); |     c11_vector__ctor(&self->types, sizeof(TypePointer)); | ||||||
| 
 | 
 | ||||||
|     self->builtins = NULL; |     self->builtins = NULL; | ||||||
| @ -114,7 +114,7 @@ void VM__ctor(VM* self) { | |||||||
|     validate(tp_slice, pk_slice__register()); |     validate(tp_slice, pk_slice__register()); | ||||||
|     validate(tp_range, pk_range__register()); |     validate(tp_range, pk_range__register()); | ||||||
|     validate(tp_range_iterator, pk_range_iterator__register()); |     validate(tp_range_iterator, pk_range_iterator__register()); | ||||||
|     validate(tp_module, pk_newtype("module", tp_object, NULL, NULL, false, true)); |     validate(tp_module, pk_module__register()); | ||||||
| 
 | 
 | ||||||
|     validate(tp_function, pk_function__register()); |     validate(tp_function, pk_function__register()); | ||||||
|     validate(tp_nativefunc, pk_nativefunc__register()); |     validate(tp_nativefunc, pk_nativefunc__register()); | ||||||
| @ -364,47 +364,6 @@ bool pk__normalize_index(int* index, int length) { | |||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| py_Type pk_newtype(const char* name, |  | ||||||
|                    py_Type base, |  | ||||||
|                    const py_GlobalRef module, |  | ||||||
|                    void (*dtor)(void*), |  | ||||||
|                    bool is_python, |  | ||||||
|                    bool is_sealed) { |  | ||||||
|     py_Type index = pk_current_vm->types.length; |  | ||||||
|     py_TypeInfo* self = py_newobject(py_retval(), tp_type, -1, sizeof(py_TypeInfo)); |  | ||||||
|     py_TypeInfo* base_ti = base ? pk_typeinfo(base) : NULL; |  | ||||||
|     if(base_ti && base_ti->is_sealed) { |  | ||||||
|         c11__abort("type '%s' is not an acceptable base type", py_name2str(base_ti->name)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     memset(self, 0, sizeof(py_TypeInfo)); |  | ||||||
|     self->name = py_name(name); |  | ||||||
|     self->index = index; |  | ||||||
|     self->base = base; |  | ||||||
|     self->base_ti = base_ti; |  | ||||||
| 
 |  | ||||||
|     self->self = *py_retval(); |  | ||||||
|     self->module = module ? module : py_NIL(); |  | ||||||
|     self->annotations = *py_NIL(); |  | ||||||
| 
 |  | ||||||
|     if(!dtor && base) dtor = base_ti->dtor; |  | ||||||
|     self->is_python = is_python; |  | ||||||
|     self->is_sealed = is_sealed; |  | ||||||
|     self->dtor = dtor; |  | ||||||
| 
 |  | ||||||
|     TypePointer* pointer = c11_vector__emplace(&pk_current_vm->types); |  | ||||||
|     pointer->ti = self; |  | ||||||
|     pointer->dtor = dtor; |  | ||||||
|     return index; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static bool | static bool | ||||||
|     prepare_py_call(py_TValue* buffer, py_Ref argv, py_Ref p1, int kwargc, const FuncDecl* decl) { |     prepare_py_call(py_TValue* buffer, py_Ref argv, py_Ref p1, int kwargc, const FuncDecl* decl) { | ||||||
|     const CodeObject* co = &decl->code; |     const CodeObject* co = &decl->code; | ||||||
|  | |||||||
| @ -37,8 +37,8 @@ void pk_print_stack(VM* self, py_Frame* frame, Bytecode byte) { | |||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|             case tp_module: { |             case tp_module: { | ||||||
|                 py_Ref path = py_getdict(p, __path__); |                 py_ModuleInfo* mi = py_touserdata(p); | ||||||
|                 pk_sprintf(&buf, "<module '%v'>", py_tosv(path)); |                 pk_sprintf(&buf, "<module '%v'>", c11_string__sv(mi->path)); | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|             default: { |             default: { | ||||||
|  | |||||||
| @ -97,6 +97,8 @@ void pk__add_module_os() { | |||||||
|     py_ItemRef path_object = py_emplacedict(mod, py_name("path")); |     py_ItemRef path_object = py_emplacedict(mod, py_name("path")); | ||||||
|     py_newobject(path_object, tp_object, -1, 0); |     py_newobject(path_object, tp_object, -1, 0); | ||||||
|     py_bindfunc(path_object, "exists", os_path_exists); |     py_bindfunc(path_object, "exists", os_path_exists); | ||||||
|  | 
 | ||||||
|  |     py_newdict(py_emplacedict(mod, py_name("environ"))); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|  | |||||||
| @ -66,10 +66,10 @@ static void c11_sbuf__write_type_path(c11_sbuf* path_buf, py_Type type) { | |||||||
|         c11_sbuf__write_cstr(path_buf, py_name2str(ti->name)); |         c11_sbuf__write_cstr(path_buf, py_name2str(ti->name)); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     const char* mod_path = py_tostr(py_getdict(ti->module, __path__)); |     py_ModuleInfo* mi = py_touserdata(ti->module); | ||||||
|     c11_sbuf__write_cstr(path_buf, mod_path); |     c11_sbuf__write_sv(path_buf, c11_string__sv(mi->path)); | ||||||
|     c11_sbuf__write_char(path_buf, '.'); |     c11_sbuf__write_char(path_buf, '.'); | ||||||
|     c11_sbuf__write_cstr(path_buf, py_name2str(ti->name)); |     c11_sbuf__write_sv(path_buf, py_name2sv(ti->name)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void pkl__emit_op(PickleObject* buf, PickleOp op) { | static void pkl__emit_op(PickleObject* buf, PickleOp op) { | ||||||
|  | |||||||
| @ -197,4 +197,5 @@ void Function__dtor(Function* self) { | |||||||
|     // self->decl->code.src->filename->data);
 |     // self->decl->code.src->filename->data);
 | ||||||
|     PK_DECREF(self->decl); |     PK_DECREF(self->decl); | ||||||
|     if(self->closure) NameDict__delete(self->closure); |     if(self->closure) NameDict__delete(self->closure); | ||||||
|  |     memset(self, 0, sizeof(Function)); | ||||||
| } | } | ||||||
| @ -22,50 +22,70 @@ py_Ref py_getglobal(py_Name name) { return py_getdict(pk_current_vm->main, name) | |||||||
| 
 | 
 | ||||||
| void py_setglobal(py_Name name, py_Ref val) { py_setdict(pk_current_vm->main, name, val); } | void py_setglobal(py_Name name, py_Ref val) { py_setdict(pk_current_vm->main, name, val); } | ||||||
| 
 | 
 | ||||||
| py_Ref py_newmodule(const char* path) { | static void py_ModuleInfo__dtor(py_ModuleInfo* mi) { | ||||||
|     ManagedHeap* heap = &pk_current_vm->heap; |     c11_string__delete(mi->name); | ||||||
|  |     c11_string__delete(mi->package); | ||||||
|  |     c11_string__delete(mi->path); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
|  | static bool module__name__(int argc, py_Ref argv) { | ||||||
|  |     PY_CHECK_ARGC(1); | ||||||
|  |     py_ModuleInfo* mi = py_touserdata(argv); | ||||||
|  |     py_newstrv(py_retval(), c11_string__sv(mi->name)); | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool module__package__(int argc, py_Ref argv) { | ||||||
|  |     PY_CHECK_ARGC(1); | ||||||
|  |     py_ModuleInfo* mi = py_touserdata(argv); | ||||||
|  |     py_newstrv(py_retval(), c11_string__sv(mi->package)); | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool module__path__(int argc, py_Ref argv) { | ||||||
|  |     PY_CHECK_ARGC(1); | ||||||
|  |     py_ModuleInfo* mi = py_touserdata(argv); | ||||||
|  |     py_newstrv(py_retval(), c11_string__sv(mi->path)); | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | py_Type pk_module__register() { | ||||||
|  |     py_Type type = pk_newtype("module", tp_object, NULL, (py_Dtor)py_ModuleInfo__dtor, false, true); | ||||||
|  |     py_bindproperty(type, "__name__", module__name__, NULL); | ||||||
|  |     py_bindproperty(type, "__package__", module__package__, NULL); | ||||||
|  |     py_bindproperty(type, "__path__", module__path__, NULL); | ||||||
|  |     return type; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | py_Ref py_newmodule(const char* path) { | ||||||
|     int path_len = strlen(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 > PK_MAX_MODULE_PATH_LEN) c11__abort("module path too long: %s", path); | ||||||
|     if(path_len == 0) c11__abort("module path cannot be empty"); |     if(path_len == 0) c11__abort("module path cannot be empty"); | ||||||
| 
 | 
 | ||||||
|     py_Ref r0 = py_pushtmp(); |     py_ModuleInfo* mi = py_newobject(py_retval(), tp_module, -1, sizeof(py_ModuleInfo)); | ||||||
|     py_Ref r1 = py_pushtmp(); |  | ||||||
| 
 |  | ||||||
|     *r0 = (py_TValue){ |  | ||||||
|         .type = tp_module, |  | ||||||
|         .is_ptr = true, |  | ||||||
|         ._obj = ManagedHeap__new(heap, tp_module, -1, 0), |  | ||||||
|     }; |  | ||||||
| 
 | 
 | ||||||
|     int last_dot = c11_sv__rindex((c11_sv){path, path_len}, '.'); |     int last_dot = c11_sv__rindex((c11_sv){path, path_len}, '.'); | ||||||
|     if(last_dot == -1) { |     if(last_dot == -1) { | ||||||
|         py_newstr(r1, path); |         mi->name = c11_string__new(path); | ||||||
|         py_setdict(r0, __name__, r1); |         mi->package = c11_string__new(""); | ||||||
|         py_newstr(r1, ""); |  | ||||||
|         py_setdict(r0, __package__, r1); |  | ||||||
|     } else { |     } else { | ||||||
|         const char* start = path + last_dot + 1; |         const char* start = path + last_dot + 1; | ||||||
|         py_newstr(r1, start); |         mi->name = c11_string__new(start); | ||||||
|         py_setdict(r0, __name__, r1); |         mi->package = c11_string__new2(path, last_dot); | ||||||
|         py_newstrv(r1, (c11_sv){path, last_dot}); |  | ||||||
|         py_setdict(r0, __package__, r1); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     py_newstr(r1, path); |     mi->path = c11_string__new(path); | ||||||
|     py_setdict(r0, __path__, r1); |     path = mi->path->data; | ||||||
| 
 | 
 | ||||||
|     // we do not allow override in order to avoid memory leak
 |     // we do not allow override in order to avoid memory leak
 | ||||||
|     // it is because Module objects are not garbage collected
 |     // it is because Module objects are not garbage collected
 | ||||||
|     bool exists = BinTree__contains(&pk_current_vm->modules, (void*)path); |     bool exists = BinTree__contains(&pk_current_vm->modules, (void*)path); | ||||||
|     if(exists) c11__abort("module '%s' already exists", path); |     if(exists) c11__abort("module '%s' already exists", path); | ||||||
| 
 | 
 | ||||||
|     // convert to a weak (const char*)
 |     BinTree__set(&pk_current_vm->modules, (void*)path, py_retval()); | ||||||
|     path = py_tostr(py_getdict(r0, __path__)); |     py_GlobalRef retval = py_getmodule(path); | ||||||
|     BinTree__set(&pk_current_vm->modules, c11_strdup(path), r0); |     mi->self = retval; | ||||||
| 
 |     return retval; | ||||||
|     py_shrink(2); |  | ||||||
|     return py_getmodule(path); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int load_module_from_dll_desktop_only(const char* path) PY_RAISE PY_RETURN; | int load_module_from_dll_desktop_only(const char* path) PY_RAISE PY_RETURN; | ||||||
| @ -84,8 +104,8 @@ int py_import(const char* path_cstr) { | |||||||
|         c11_sv top_filename = c11_string__sv(vm->top_frame->co->src->filename); |         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}); |         int is_init = c11_sv__endswith(top_filename, (c11_sv){"__init__.py", 11}); | ||||||
| 
 | 
 | ||||||
|         py_Ref package = py_getdict(vm->top_frame->module, __path__); |         py_ModuleInfo* mi = py_touserdata(vm->top_frame->module); | ||||||
|         c11_sv package_sv = py_tosv(package); |         c11_sv package_sv = c11_string__sv(mi->path); | ||||||
|         if(package_sv.size == 0) { |         if(package_sv.size == 0) { | ||||||
|             return ImportError("attempted relative import with no known parent package"); |             return ImportError("attempted relative import with no known parent package"); | ||||||
|         } |         } | ||||||
| @ -163,9 +183,12 @@ __SUCCESS: | |||||||
|     return ok ? 1 : -1; |     return ok ? 1 : -1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool py_importlib_reload(py_GlobalRef module) { | bool py_importlib_reload(py_Ref module) { | ||||||
|     VM* vm = pk_current_vm; |     VM* vm = pk_current_vm; | ||||||
|     c11_sv path = py_tosv(py_getdict(module, __path__)); |     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* slashed_path = c11_sv__replace(path, '.', PK_PLATFORM_SEP); | ||||||
|     c11_string* filename = c11_string__new3("%s.py", slashed_path->data); |     c11_string* filename = c11_string__new3("%s.py", slashed_path->data); | ||||||
|     char* data = vm->callbacks.importfile(filename->data); |     char* data = vm->callbacks.importfile(filename->data); | ||||||
| @ -176,7 +199,8 @@ bool py_importlib_reload(py_GlobalRef module) { | |||||||
|     } |     } | ||||||
|     c11_string__delete(slashed_path); |     c11_string__delete(slashed_path); | ||||||
|     if(data == NULL) return ImportError("module '%v' not found", path); |     if(data == NULL) return ImportError("module '%v' not found", path); | ||||||
|     bool ok = py_exec(data, filename->data, EXEC_MODE, module); |     // 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); |     c11_string__delete(filename); | ||||||
|     PK_FREE(data); |     PK_FREE(data); | ||||||
|     py_assign(py_retval(), module); |     py_assign(py_retval(), module); | ||||||
|  | |||||||
| @ -20,6 +20,15 @@ static bool namedict__getitem__(int argc, py_Ref argv) { | |||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static bool namedict__get(int argc, py_Ref argv) { | ||||||
|  |     PY_CHECK_ARGC(3); | ||||||
|  |     PY_CHECK_ARG_TYPE(1, tp_str); | ||||||
|  |     py_Name name = py_namev(py_tosv(py_arg(1))); | ||||||
|  |     py_Ref res = py_getdict(py_getslot(argv, 0), name); | ||||||
|  |     py_assign(py_retval(), res ? res : py_arg(2)); | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static bool namedict__setitem__(int argc, py_Ref argv) { | static bool namedict__setitem__(int argc, py_Ref argv) { | ||||||
|     PY_CHECK_ARGC(3); |     PY_CHECK_ARGC(3); | ||||||
|     PY_CHECK_ARG_TYPE(1, tp_str); |     PY_CHECK_ARG_TYPE(1, tp_str); | ||||||
| @ -82,5 +91,6 @@ py_Type pk_namedict__register() { | |||||||
|     py_setdict(py_tpobject(type), __hash__, py_None()); |     py_setdict(py_tpobject(type), __hash__, py_None()); | ||||||
|     py_bindmethod(type, "items", namedict_items); |     py_bindmethod(type, "items", namedict_items); | ||||||
|     py_bindmethod(type, "clear", namedict_clear); |     py_bindmethod(type, "clear", namedict_clear); | ||||||
|  |     py_bindmethod(type, "get", namedict__get); | ||||||
|     return type; |     return type; | ||||||
| } | } | ||||||
|  | |||||||
| @ -104,8 +104,8 @@ static bool type__module__(int argc, py_Ref argv) { | |||||||
|     if(py_isnil(ti->module)) { |     if(py_isnil(ti->module)) { | ||||||
|         py_newnone(py_retval()); |         py_newnone(py_retval()); | ||||||
|     } else { |     } else { | ||||||
|         py_Ref path = py_getdict(ti->module, __path__); |         py_ModuleInfo* mi = py_touserdata(ti->module); | ||||||
|         py_assign(py_retval(), path); |         py_newstrv(py_retval(), c11_string__sv(mi->path)); | ||||||
|     } |     } | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
|  | |||||||
| @ -166,10 +166,10 @@ bool py_getattr(py_Ref self, py_Name name) { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(self->type == tp_module) { |     if(self->type == tp_module) { | ||||||
|         py_Ref path = py_getdict(self, __path__); |         py_ModuleInfo* mi = py_touserdata(self); | ||||||
|         c11_sbuf buf; |         c11_sbuf buf; | ||||||
|         c11_sbuf__ctor(&buf); |         c11_sbuf__ctor(&buf); | ||||||
|         pk_sprintf(&buf, "%v.%n", py_tosv(path), name); |         pk_sprintf(&buf, "%v.%n", c11_string__sv(mi->path), name); | ||||||
|         c11_string* new_path = c11_sbuf__submit(&buf); |         c11_string* new_path = c11_sbuf__submit(&buf); | ||||||
|         int res = py_import(new_path->data); |         int res = py_import(new_path->data); | ||||||
|         c11_string__delete(new_path); |         c11_string__delete(new_path); | ||||||
|  | |||||||
| @ -35,6 +35,12 @@ bool py_applydict(py_Ref self, bool (*f)(py_Name, py_Ref, void*), void* ctx) { | |||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void py_cleardict(py_Ref self) { | ||||||
|  |     assert(self && self->is_ptr); | ||||||
|  |     NameDict* dict = PyObject__dict(self->_obj); | ||||||
|  |     NameDict__clear(dict); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool py_deldict(py_Ref self, py_Name name) { | bool py_deldict(py_Ref self, py_Name name) { | ||||||
|     assert(self && self->is_ptr); |     assert(self && self->is_ptr); | ||||||
|     return NameDict__del(PyObject__dict(self->_obj), name); |     return NameDict__del(PyObject__dict(self->_obj), name); | ||||||
|  | |||||||
| @ -23,6 +23,11 @@ assert get_value_2() == '123' | |||||||
| from test3.a.b import value | from test3.a.b import value | ||||||
| assert value == 1 | assert value == 1 | ||||||
| 
 | 
 | ||||||
|  | from test2.utils import r | ||||||
|  | assert r.__name__ == 'r' | ||||||
|  | assert r.__package__ == 'test2.utils' | ||||||
|  | assert r.__path__ == 'test2.utils.r' | ||||||
|  | 
 | ||||||
| def f(): | def f(): | ||||||
|     import math as m |     import math as m | ||||||
|     assert m.pi > 3 |     assert m.pi > 3 | ||||||
|  | |||||||
							
								
								
									
										43
									
								
								tests/31_modulereload.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								tests/31_modulereload.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | |||||||
|  | try: | ||||||
|  |     import os | ||||||
|  | except ImportError: | ||||||
|  |     exit(0) | ||||||
|  | 
 | ||||||
|  | import importlib | ||||||
|  | 
 | ||||||
|  | os.chdir('tests') | ||||||
|  | assert os.getcwd().endswith('tests') | ||||||
|  | 
 | ||||||
|  | # test | ||||||
|  | os.environ['TEST_RELOAD_VALUE'] = '123' | ||||||
|  | os.environ['SET_X'] = '1' | ||||||
|  | os.environ['SET_Y'] = '0' | ||||||
|  | 
 | ||||||
|  | from testreload import MyClass, a | ||||||
|  | 
 | ||||||
|  | objid = id(MyClass) | ||||||
|  | funcid = id(MyClass.some_func) | ||||||
|  | getxyid = id(MyClass.get_xy) | ||||||
|  | 
 | ||||||
|  | assert MyClass.value == '123' | ||||||
|  | assert MyClass.get_xy() == (1, 0) | ||||||
|  | 
 | ||||||
|  | inst = MyClass() | ||||||
|  | assert inst.some_func() == '123' | ||||||
|  | 
 | ||||||
|  | # reload | ||||||
|  | os.environ['TEST_RELOAD_VALUE'] = '456' | ||||||
|  | os.environ['SET_X'] = '0' | ||||||
|  | os.environ['SET_Y'] = '1' | ||||||
|  | 
 | ||||||
|  | importlib.reload(a) | ||||||
|  | 
 | ||||||
|  | assert id(MyClass) == objid | ||||||
|  | assert id(MyClass.some_func) != funcid | ||||||
|  | assert id(MyClass.get_xy) != getxyid | ||||||
|  | 
 | ||||||
|  | assert MyClass.value == '456' | ||||||
|  | assert inst.some_func() == '456' | ||||||
|  | assert (MyClass.get_xy() == (1, 1)), MyClass.get_xy() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
							
								
								
									
										2
									
								
								tests/testreload/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								tests/testreload/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | |||||||
|  | from .a import MyClass | ||||||
|  | from . import a | ||||||
							
								
								
									
										18
									
								
								tests/testreload/a.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								tests/testreload/a.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | |||||||
|  | import os | ||||||
|  | 
 | ||||||
|  | class MyClass: | ||||||
|  |     value = os.environ['TEST_RELOAD_VALUE'] | ||||||
|  | 
 | ||||||
|  |     def some_func(self): | ||||||
|  |         return self.value | ||||||
|  |      | ||||||
|  |     @staticmethod | ||||||
|  |     def get_xy(): | ||||||
|  |         g = globals() | ||||||
|  |         return g.get('x', 0), g.get('y', 0) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | if os.environ['SET_X'] == '1': | ||||||
|  |     x = 1 | ||||||
|  | elif os.environ['SET_Y'] == '1': | ||||||
|  |     y = 1 | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user