duk_module_node.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. /*
  2. * Node.js-like module loading framework for Duktape
  3. *
  4. * https://nodejs.org/api/modules.html
  5. */
  6. #include "duktape.h"
  7. #include "duk_module_node.h"
  8. #if DUK_VERSION >= 19999
  9. static duk_int_t duk__eval_module_source(duk_context *ctx, void *udata);
  10. #else
  11. static duk_int_t duk__eval_module_source(duk_context *ctx);
  12. #endif
  13. static void duk__push_module_object(duk_context *ctx, const char *id, duk_bool_t main);
  14. static duk_bool_t duk__get_cached_module(duk_context *ctx, const char *id) {
  15. duk_push_global_stash(ctx);
  16. (void) duk_get_prop_string(ctx, -1, "\xff" "requireCache");
  17. if (duk_get_prop_string(ctx, -1, id)) {
  18. duk_remove(ctx, -2);
  19. duk_remove(ctx, -2);
  20. return 1;
  21. } else {
  22. duk_pop_3(ctx);
  23. return 0;
  24. }
  25. }
  26. /* Place a `module` object on the top of the value stack into the require cache
  27. * based on its `.id` property. As a convenience to the caller, leave the
  28. * object on top of the value stack afterwards.
  29. */
  30. static void duk__put_cached_module(duk_context *ctx) {
  31. /* [ ... module ] */
  32. duk_push_global_stash(ctx);
  33. (void) duk_get_prop_string(ctx, -1, "\xff" "requireCache");
  34. duk_dup(ctx, -3);
  35. /* [ ... module stash req_cache module ] */
  36. (void) duk_get_prop_string(ctx, -1, "id");
  37. duk_dup(ctx, -2);
  38. duk_put_prop(ctx, -4);
  39. duk_pop_3(ctx); /* [ ... module ] */
  40. }
  41. static void duk__del_cached_module(duk_context *ctx, const char *id) {
  42. duk_push_global_stash(ctx);
  43. (void) duk_get_prop_string(ctx, -1, "\xff" "requireCache");
  44. duk_del_prop_string(ctx, -1, id);
  45. duk_pop_2(ctx);
  46. }
  47. static duk_ret_t duk__handle_require(duk_context *ctx) {
  48. /*
  49. * Value stack handling here is a bit sloppy but should be correct.
  50. * Call handling will clean up any extra garbage for us.
  51. */
  52. const char *id;
  53. const char *parent_id;
  54. duk_idx_t module_idx;
  55. duk_idx_t stash_idx;
  56. duk_int_t ret;
  57. duk_push_global_stash(ctx);
  58. stash_idx = duk_normalize_index(ctx, -1);
  59. duk_push_current_function(ctx);
  60. (void) duk_get_prop_string(ctx, -1, "\xff" "moduleId");
  61. parent_id = duk_require_string(ctx, -1);
  62. (void) parent_id; /* not used directly; suppress warning */
  63. /* [ id stash require parent_id ] */
  64. id = duk_require_string(ctx, 0);
  65. (void) duk_get_prop_string(ctx, stash_idx, "\xff" "modResolve");
  66. duk_dup(ctx, 0); /* module ID */
  67. duk_dup(ctx, -3); /* parent ID */
  68. duk_call(ctx, 2);
  69. /* [ ... stash ... resolved_id ] */
  70. id = duk_require_string(ctx, -1);
  71. if (duk__get_cached_module(ctx, id)) {
  72. goto have_module; /* use the cached module */
  73. }
  74. duk__push_module_object(ctx, id, 0 /*main*/);
  75. duk__put_cached_module(ctx); /* module remains on stack */
  76. /*
  77. * From here on out, we have to be careful not to throw. If it can't be
  78. * avoided, the error must be caught and the module removed from the
  79. * require cache before rethrowing. This allows the application to
  80. * reattempt loading the module.
  81. */
  82. module_idx = duk_normalize_index(ctx, -1);
  83. /* [ ... stash ... resolved_id module ] */
  84. (void) duk_get_prop_string(ctx, stash_idx, "\xff" "modLoad");
  85. duk_dup(ctx, -3); /* resolved ID */
  86. (void) duk_get_prop_string(ctx, module_idx, "exports");
  87. duk_dup(ctx, module_idx);
  88. ret = duk_pcall(ctx, 3);
  89. if (ret != DUK_EXEC_SUCCESS) {
  90. duk__del_cached_module(ctx, id);
  91. (void) duk_throw(ctx); /* rethrow */
  92. }
  93. if (duk_is_string(ctx, -1)) {
  94. duk_int_t ret;
  95. /* [ ... module source ] */
  96. #if DUK_VERSION >= 19999
  97. ret = duk_safe_call(ctx, duk__eval_module_source, NULL, 2, 1);
  98. #else
  99. ret = duk_safe_call(ctx, duk__eval_module_source, 2, 1);
  100. #endif
  101. if (ret != DUK_EXEC_SUCCESS) {
  102. duk__del_cached_module(ctx, id);
  103. (void) duk_throw(ctx); /* rethrow */
  104. }
  105. } else if (duk_is_undefined(ctx, -1)) {
  106. duk_pop(ctx);
  107. } else {
  108. duk__del_cached_module(ctx, id);
  109. (void) duk_type_error(ctx, "invalid module load callback return value");
  110. }
  111. /* fall through */
  112. have_module:
  113. /* [ ... module ] */
  114. (void) duk_get_prop_string(ctx, -1, "exports");
  115. return 1;
  116. }
  117. static void duk__push_require_function(duk_context *ctx, const char *id) {
  118. duk_push_c_function(ctx, duk__handle_require, 1);
  119. duk_push_string(ctx, "name");
  120. duk_push_string(ctx, "require");
  121. duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE);
  122. duk_push_string(ctx, id);
  123. duk_put_prop_string(ctx, -2, "\xff" "moduleId");
  124. /* require.cache */
  125. duk_push_global_stash(ctx);
  126. (void) duk_get_prop_string(ctx, -1, "\xff" "requireCache");
  127. duk_put_prop_string(ctx, -3, "cache");
  128. duk_pop(ctx);
  129. /* require.main */
  130. duk_push_global_stash(ctx);
  131. (void) duk_get_prop_string(ctx, -1, "\xff" "mainModule");
  132. duk_put_prop_string(ctx, -3, "main");
  133. duk_pop(ctx);
  134. }
  135. static void duk__push_module_object(duk_context *ctx, const char *id, duk_bool_t main) {
  136. duk_push_object(ctx);
  137. /* Set this as the main module, if requested */
  138. if (main) {
  139. duk_push_global_stash(ctx);
  140. duk_dup(ctx, -2);
  141. duk_put_prop_string(ctx, -2, "\xff" "mainModule");
  142. duk_pop(ctx);
  143. }
  144. /* Node.js uses the canonicalized filename of a module for both module.id
  145. * and module.filename. We have no concept of a file system here, so just
  146. * use the module ID for both values.
  147. */
  148. duk_push_string(ctx, id);
  149. duk_dup(ctx, -1);
  150. duk_put_prop_string(ctx, -3, "filename");
  151. duk_put_prop_string(ctx, -2, "id");
  152. /* module.exports = {} */
  153. duk_push_object(ctx);
  154. duk_put_prop_string(ctx, -2, "exports");
  155. /* module.loaded = false */
  156. duk_push_false(ctx);
  157. duk_put_prop_string(ctx, -2, "loaded");
  158. /* module.require */
  159. duk__push_require_function(ctx, id);
  160. duk_put_prop_string(ctx, -2, "require");
  161. }
  162. #if DUK_VERSION >= 19999
  163. static duk_int_t duk__eval_module_source(duk_context *ctx, void *udata) {
  164. #else
  165. static duk_int_t duk__eval_module_source(duk_context *ctx) {
  166. #endif
  167. const char *src;
  168. /*
  169. * Stack: [ ... module source ]
  170. */
  171. #if DUK_VERSION >= 19999
  172. (void) udata;
  173. #endif
  174. /* Wrap the module code in a function expression. This is the simplest
  175. * way to implement CommonJS closure semantics and matches the behavior of
  176. * e.g. Node.js.
  177. */
  178. duk_push_string(ctx, "(function(exports,require,module,__filename,__dirname){");
  179. src = duk_require_string(ctx, -2);
  180. duk_push_string(ctx, (src[0] == '#' && src[1] == '!') ? "//" : ""); /* Shebang support. */
  181. duk_dup(ctx, -3); /* source */
  182. duk_push_string(ctx, "\n})"); /* Newline allows module last line to contain a // comment. */
  183. duk_concat(ctx, 4);
  184. /* [ ... module source func_src ] */
  185. (void) duk_get_prop_string(ctx, -3, "filename");
  186. duk_compile(ctx, DUK_COMPILE_EVAL);
  187. duk_call(ctx, 0);
  188. /* [ ... module source func ] */
  189. /* Set name for the wrapper function. */
  190. duk_push_string(ctx, "name");
  191. duk_push_string(ctx, "main");
  192. duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_FORCE);
  193. /* call the function wrapper */
  194. (void) duk_get_prop_string(ctx, -3, "exports"); /* exports */
  195. (void) duk_get_prop_string(ctx, -4, "require"); /* require */
  196. duk_dup(ctx, -5); /* module */
  197. (void) duk_get_prop_string(ctx, -6, "filename"); /* __filename */
  198. duk_push_undefined(ctx); /* __dirname */
  199. duk_call(ctx, 5);
  200. /* [ ... module source result(ignore) ] */
  201. /* module.loaded = true */
  202. duk_push_true(ctx);
  203. duk_put_prop_string(ctx, -4, "loaded");
  204. /* [ ... module source retval ] */
  205. duk_pop_2(ctx);
  206. /* [ ... module ] */
  207. return 1;
  208. }
  209. /* Load a module as the 'main' module. */
  210. duk_ret_t duk_module_node_peval_main(duk_context *ctx, const char *path) {
  211. /*
  212. * Stack: [ ... source ]
  213. */
  214. duk__push_module_object(ctx, path, 1 /*main*/);
  215. /* [ ... source module ] */
  216. duk_dup(ctx, 0);
  217. /* [ ... source module source ] */
  218. #if DUK_VERSION >= 19999
  219. return duk_safe_call(ctx, duk__eval_module_source, NULL, 2, 1);
  220. #else
  221. return duk_safe_call(ctx, duk__eval_module_source, 2, 1);
  222. #endif
  223. }
  224. void duk_module_node_init(duk_context *ctx) {
  225. /*
  226. * Stack: [ ... options ] => [ ... ]
  227. */
  228. duk_idx_t options_idx;
  229. duk_require_object_coercible(ctx, -1); /* error before setting up requireCache */
  230. options_idx = duk_require_normalize_index(ctx, -1);
  231. /* Initialize the require cache to a fresh object. */
  232. duk_push_global_stash(ctx);
  233. #if DUK_VERSION >= 19999
  234. duk_push_bare_object(ctx);
  235. #else
  236. duk_push_object(ctx);
  237. duk_push_undefined(ctx);
  238. duk_set_prototype(ctx, -2);
  239. #endif
  240. duk_put_prop_string(ctx, -2, "\xff" "requireCache");
  241. duk_pop(ctx);
  242. /* Stash callbacks for later use. User code can overwrite them later
  243. * on directly by accessing the global stash.
  244. */
  245. duk_push_global_stash(ctx);
  246. duk_get_prop_string(ctx, options_idx, "resolve");
  247. duk_require_function(ctx, -1);
  248. duk_put_prop_string(ctx, -2, "\xff" "modResolve");
  249. duk_get_prop_string(ctx, options_idx, "load");
  250. duk_require_function(ctx, -1);
  251. duk_put_prop_string(ctx, -2, "\xff" "modLoad");
  252. duk_pop(ctx);
  253. /* Stash main module. */
  254. duk_push_global_stash(ctx);
  255. duk_push_undefined(ctx);
  256. duk_put_prop_string(ctx, -2, "\xff" "mainModule");
  257. duk_pop(ctx);
  258. /* register `require` as a global function. */
  259. duk_push_global_object(ctx);
  260. duk_push_string(ctx, "require");
  261. duk__push_require_function(ctx, "");
  262. duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE |
  263. DUK_DEFPROP_SET_WRITABLE |
  264. DUK_DEFPROP_SET_CONFIGURABLE);
  265. duk_pop(ctx);
  266. duk_pop(ctx); /* pop argument */
  267. }