ModuleSystem.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. #include "pocketpy/common/str.h"
  2. #include "pocketpy/objects/base.h"
  3. #include "pocketpy/objects/codeobject.h"
  4. #include "pocketpy/pocketpy.h"
  5. #include "pocketpy/common/utils.h"
  6. #include "pocketpy/common/sstream.h"
  7. #include "pocketpy/interpreter/vm.h"
  8. #include "pocketpy/common/_generated.h"
  9. py_Ref py_getmodule(const char* path) {
  10. VM* vm = pk_current_vm;
  11. return BinTree__try_get(&vm->modules, (void*)path);
  12. }
  13. static py_Ref pk_newmodule(const char* path, bool is_init) {
  14. c11_sv pathv = {path, strlen(path)};
  15. if(pathv.size > PK_MAX_MODULE_PATH_LEN) c11__abort("module path too long: %s", path);
  16. if(pathv.size == 0) c11__abort("module path cannot be empty");
  17. py_ModuleInfo* mi = py_newobject(py_retval(), tp_module, -1, sizeof(py_ModuleInfo));
  18. int last_dot = c11_sv__rindex(pathv, '.');
  19. if(last_dot == -1 || is_init) {
  20. mi->package = c11_string__new(path);
  21. } else {
  22. // a.b.c -> a.b
  23. mi->package = c11_string__new2(path, last_dot);
  24. }
  25. mi->path = c11_string__new(path);
  26. path = mi->path->data;
  27. // we do not allow override in order to avoid memory leak
  28. // it is because Module objects are not garbage collected
  29. bool exists = BinTree__contains(&pk_current_vm->modules, (void*)path);
  30. if(exists) c11__abort("module '%s' already exists", path);
  31. BinTree__set(&pk_current_vm->modules, (void*)path, py_retval());
  32. py_GlobalRef retval = py_getmodule(path);
  33. mi->self = retval;
  34. // setup __name__
  35. py_newstrv(py_emplacedict(retval, __name__), c11_string__sv(mi->path));
  36. // setup __package__
  37. py_newstrv(py_emplacedict(retval, __package__), c11_string__sv(mi->package));
  38. return retval;
  39. }
  40. py_Ref py_newmodule(const char* path) {
  41. return pk_newmodule(path, false);
  42. }
  43. static void py_ModuleInfo__dtor(py_ModuleInfo* mi) {
  44. c11_string__delete(mi->package);
  45. c11_string__delete(mi->path);
  46. }
  47. py_Type pk_module__register() {
  48. py_Type type = pk_newtype("module", tp_object, NULL, (py_Dtor)py_ModuleInfo__dtor, false, true);
  49. return type;
  50. }
  51. int load_module_from_dll_desktop_only(const char* path) PY_RAISE PY_RETURN;
  52. int py_import(const char* path_cstr) {
  53. VM* vm = pk_current_vm;
  54. c11_sv path = {path_cstr, strlen(path_cstr)};
  55. if(path.size == 0) {
  56. ValueError("empty module name");
  57. return -1;
  58. }
  59. if(path.size > PK_MAX_MODULE_PATH_LEN) {
  60. ValueError("module name too long: %v", path);
  61. return -1;
  62. }
  63. if(path.data[0] == '.') {
  64. c11__rtassert(vm->top_frame != NULL && vm->top_frame->module != NULL);
  65. // try relative import
  66. int dot_count = 1;
  67. while(dot_count < path.size && path.data[dot_count] == '.')
  68. dot_count++;
  69. py_ModuleInfo* mi = py_touserdata(vm->top_frame->module);
  70. c11_sv package_sv = c11_string__sv(mi->package);
  71. if(package_sv.size == 0) {
  72. ImportError("attempted relative import with no known parent package");
  73. return -1;
  74. }
  75. c11_vector /* T=c11_sv */ cpnts = c11_sv__split(package_sv, '.');
  76. for(int i = 1; i < dot_count; i++) {
  77. if(cpnts.length == 0){
  78. ImportError("attempted relative import beyond top-level package");
  79. return -1;
  80. }
  81. c11_vector__pop(&cpnts);
  82. }
  83. if(dot_count < path.size) {
  84. c11_sv last_cpnt = c11_sv__slice(path, dot_count);
  85. c11_vector__push(c11_sv, &cpnts, last_cpnt);
  86. }
  87. // join cpnts
  88. c11_sbuf buf;
  89. c11_sbuf__ctor(&buf);
  90. for(int i = 0; i < cpnts.length; i++) {
  91. if(i > 0) c11_sbuf__write_char(&buf, '.');
  92. c11_sbuf__write_sv(&buf, c11__getitem(c11_sv, &cpnts, i));
  93. }
  94. c11_vector__dtor(&cpnts);
  95. c11_string* new_path = c11_sbuf__submit(&buf);
  96. int res = py_import(new_path->data);
  97. c11_string__delete(new_path);
  98. return res;
  99. }
  100. c11__rtassert(path.data[0] != '.' && path.data[path.size - 1] != '.');
  101. // import parent module (implicit recursion)
  102. int last_dot_index = c11_sv__rindex(path, '.');
  103. if(last_dot_index >= 0) {
  104. c11_sv ppath = c11_sv__slice2(path, 0, last_dot_index);
  105. py_GlobalRef ext_mod = py_getmodule(ppath.data);
  106. if(!ext_mod) {
  107. char buf[PK_MAX_MODULE_PATH_LEN + 1];
  108. memcpy(buf, ppath.data, ppath.size);
  109. buf[ppath.size] = '\0';
  110. int res = py_import(buf);
  111. if(res != 1) return res;
  112. py_newnil(py_retval());
  113. }
  114. }
  115. // check existing module
  116. py_GlobalRef ext_mod = py_getmodule(path.data);
  117. if(ext_mod) {
  118. py_assign(py_retval(), ext_mod);
  119. return 1;
  120. }
  121. if(vm->callbacks.lazyimport) {
  122. py_GlobalRef lazymod = vm->callbacks.lazyimport(path_cstr);
  123. if(lazymod) {
  124. c11__rtassert(py_istype(lazymod, tp_module));
  125. py_assign(py_retval(), lazymod);
  126. return 1;
  127. }
  128. }
  129. // try import
  130. c11_string* slashed_path = c11_sv__replace(path, '.', PK_PLATFORM_SEP);
  131. c11_string* filename = c11_string__new3("%s.py", slashed_path->data);
  132. bool need_free = true;
  133. bool is_pyc = false;
  134. bool is_init = false;
  135. const char* data = load_kPythonLib(path_cstr);
  136. int data_size = -1;
  137. if(data != NULL) {
  138. need_free = false;
  139. goto __SUCCESS;
  140. }
  141. data = vm->callbacks.importfile(filename->data, &data_size);
  142. if(data != NULL) goto __SUCCESS;
  143. c11_string__delete(filename);
  144. filename = c11_string__new3("%s.pyc", slashed_path->data);
  145. data = vm->callbacks.importfile(filename->data, &data_size);
  146. if(data != NULL) {
  147. is_pyc = true;
  148. goto __SUCCESS;
  149. }
  150. c11_string__delete(filename);
  151. filename = c11_string__new3("%s%c__init__.py", slashed_path->data, PK_PLATFORM_SEP);
  152. data = vm->callbacks.importfile(filename->data, &data_size);
  153. if(data != NULL) {
  154. is_init = true;
  155. goto __SUCCESS;
  156. }
  157. c11_string__delete(filename);
  158. filename = c11_string__new3("%s%c__init__.pyc", slashed_path->data, PK_PLATFORM_SEP);
  159. data = vm->callbacks.importfile(filename->data, &data_size);
  160. if(data != NULL) {
  161. is_pyc = true;
  162. is_init = true;
  163. goto __SUCCESS;
  164. }
  165. c11_string__delete(filename);
  166. c11_string__delete(slashed_path);
  167. // not found
  168. return load_module_from_dll_desktop_only(path_cstr);
  169. __SUCCESS:
  170. do {
  171. } while(0);
  172. py_GlobalRef mod = pk_newmodule(path_cstr, is_init);
  173. bool ok;
  174. if(is_pyc) {
  175. ok = py_execo(data, data_size, filename->data, mod);
  176. } else {
  177. ok = py_exec(data, filename->data, EXEC_MODE, mod);
  178. }
  179. py_assign(py_retval(), mod);
  180. c11_string__delete(filename);
  181. c11_string__delete(slashed_path);
  182. if(need_free) PK_FREE((void*)data);
  183. return ok ? 1 : -1;
  184. }
  185. bool py_importlib_reload(py_Ref module) {
  186. VM* vm = pk_current_vm;
  187. py_ModuleInfo* mi = py_touserdata(module);
  188. // We should ensure that the module is its original py_GlobalRef
  189. module = mi->self;
  190. c11_sv path = c11_string__sv(mi->path);
  191. c11_string* slashed_path = c11_sv__replace(path, '.', PK_PLATFORM_SEP);
  192. c11_string* filename = c11_string__new3("%s.py", slashed_path->data);
  193. // Here we only consider source modules.
  194. // Because compiled modules have no source file (it cannot be reloaded)
  195. char* data = vm->callbacks.importfile(filename->data, NULL);
  196. if(data == NULL) {
  197. c11_string__delete(filename);
  198. filename = c11_string__new3("%s%c__init__.py", slashed_path->data, PK_PLATFORM_SEP);
  199. data = vm->callbacks.importfile(filename->data, NULL);
  200. }
  201. c11_string__delete(slashed_path);
  202. if(data == NULL) return ImportError("module '%v' not found", path);
  203. // py_cleardict(module); BUG: removing old classes will cause RELOAD_MODE to fail
  204. bool ok = py_exec(data, filename->data, RELOAD_MODE, module);
  205. c11_string__delete(filename);
  206. PK_FREE(data);
  207. py_assign(py_retval(), module);
  208. return ok;
  209. }