blueloveTH 8 месяцев назад
Родитель
Сommit
68a2186728

+ 8 - 0
include/pocketpy/interpreter/vm.h

@@ -34,6 +34,13 @@ typedef struct TypePointer {
     py_Dtor dtor;
 } TypePointer;
 
+typedef struct py_ModuleInfo {
+    c11_string* name;
+    c11_string* package;
+    c11_string* path;
+} py_ModuleInfo;
+
+
 typedef struct VM {
     py_Frame* top_frame;
 
@@ -139,6 +146,7 @@ py_Type pk_nativefunc__register();
 py_Type pk_boundmethod__register();
 py_Type pk_range__register();
 py_Type pk_range_iterator__register();
+py_Type pk_module__register();
 py_Type pk_BaseException__register();
 py_Type pk_Exception__register();
 py_Type pk_StopIteration__register();

+ 2 - 0
include/pocketpy/pocketpy.h

@@ -406,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__`.
 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.

+ 2 - 2
src/interpreter/vm.c

@@ -50,7 +50,7 @@ void VM__ctor(VM* self) {
 
     const static BinTreeConfig modules_config = {
         .f_cmp = BinTree__cmp_cstr,
-        .need_free_key = true,
+        .need_free_key = false,
     };
     BinTree__ctor(&self->modules, c11_strdup(""), py_NIL(), &modules_config);
     c11_vector__ctor(&self->types, sizeof(TypePointer));
@@ -114,7 +114,7 @@ void VM__ctor(VM* self) {
     validate(tp_slice, pk_slice__register());
     validate(tp_range, pk_range__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_nativefunc, pk_nativefunc__register());

+ 2 - 2
src/interpreter/vmx.c

@@ -37,8 +37,8 @@ void pk_print_stack(VM* self, py_Frame* frame, Bytecode byte) {
                 break;
             }
             case tp_module: {
-                py_Ref path = py_getdict(p, __path__);
-                pk_sprintf(&buf, "<module '%v'>", py_tosv(path));
+                py_ModuleInfo* mi = py_touserdata(p);
+                pk_sprintf(&buf, "<module '%v'>", c11_string__sv(mi->path));
                 break;
             }
             default: {

+ 3 - 3
src/modules/pickle.c

@@ -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));
         return;
     }
-    const char* mod_path = py_tostr(py_getdict(ti->module, __path__));
-    c11_sbuf__write_cstr(path_buf, mod_path);
+    py_ModuleInfo* mi = py_touserdata(ti->module);
+    c11_sbuf__write_sv(path_buf, c11_string__sv(mi->path));
     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) {

+ 48 - 28
src/public/modules.c

@@ -22,49 +22,67 @@ 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); }
 
-py_Ref py_newmodule(const char* path) {
-    ManagedHeap* heap = &pk_current_vm->heap;
+static void py_ModuleInfo__dtor(py_ModuleInfo* mi) {
+    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);
     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_Ref r0 = py_pushtmp();
-    py_Ref r1 = py_pushtmp();
-
-    *r0 = (py_TValue){
-        .type = tp_module,
-        .is_ptr = true,
-        ._obj = ManagedHeap__new(heap, tp_module, -1, 0),
-    };
+    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) {
-        py_newstr(r1, path);
-        py_setdict(r0, __name__, r1);
-        py_newstr(r1, "");
-        py_setdict(r0, __package__, r1);
+        mi->name = c11_string__new(path);
+        mi->package = c11_string__new("");
     } else {
         const char* start = path + last_dot + 1;
-        py_newstr(r1, start);
-        py_setdict(r0, __name__, r1);
-        py_newstrv(r1, (c11_sv){path, last_dot});
-        py_setdict(r0, __package__, r1);
+        mi->name = c11_string__new(start);
+        mi->package = c11_string__new2(path, last_dot);
     }
 
-    py_newstr(r1, path);
-    py_setdict(r0, __path__, r1);
+    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);
 
-    // convert to a weak (const char*)
-    path = py_tostr(py_getdict(r0, __path__));
-    BinTree__set(&pk_current_vm->modules, c11_strdup(path), r0);
-
-    py_shrink(2);
+    BinTree__set(&pk_current_vm->modules, (void*)path, py_retval());
     return py_getmodule(path);
 }
 
@@ -84,8 +102,8 @@ int py_import(const char* path_cstr) {
         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_Ref package = py_getdict(vm->top_frame->module, __path__);
-        c11_sv package_sv = py_tosv(package);
+        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");
         }
@@ -165,7 +183,8 @@ __SUCCESS:
 
 bool py_importlib_reload(py_GlobalRef module) {
     VM* vm = pk_current_vm;
-    c11_sv path = py_tosv(py_getdict(module, __path__));
+    py_ModuleInfo* mi = py_touserdata(module);
+    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);
@@ -176,6 +195,7 @@ bool py_importlib_reload(py_GlobalRef module) {
     }
     c11_string__delete(slashed_path);
     if(data == NULL) return ImportError("module '%v' not found", path);
+    py_cleardict(module);
     bool ok = py_exec(data, filename->data, RELOAD_MODE, module);
     c11_string__delete(filename);
     PK_FREE(data);

+ 2 - 2
src/public/py_object.c

@@ -104,8 +104,8 @@ static bool type__module__(int argc, py_Ref argv) {
     if(py_isnil(ti->module)) {
         py_newnone(py_retval());
     } else {
-        py_Ref path = py_getdict(ti->module, __path__);
-        py_assign(py_retval(), path);
+        py_ModuleInfo* mi = py_touserdata(ti->module);
+        py_newstrv(py_retval(), c11_string__sv(mi->path));
     }
     return true;
 }

+ 2 - 2
src/public/py_ops.c

@@ -166,10 +166,10 @@ bool py_getattr(py_Ref self, py_Name name) {
     }
 
     if(self->type == tp_module) {
-        py_Ref path = py_getdict(self, __path__);
+        py_ModuleInfo* mi = py_touserdata(self);
         c11_sbuf 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);
         int res = py_import(new_path->data);
         c11_string__delete(new_path);

+ 6 - 0
src/public/stack_ops.c

@@ -35,6 +35,12 @@ bool py_applydict(py_Ref self, bool (*f)(py_Name, py_Ref, void*), void* ctx) {
     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) {
     assert(self && self->is_ptr);
     return NameDict__del(PyObject__dict(self->_obj), name);

+ 5 - 0
tests/30_import.py

@@ -23,6 +23,11 @@ assert get_value_2() == '123'
 from test3.a.b import value
 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():
     import math as m
     assert m.pi > 3