MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

migrate ESM module system to use unified module context API

+355 -118
+2 -1
examples/npm/react/index.js
··· 1 1 import { build } from 'esbuild'; 2 + import { join } from 'node:path'; 2 3 3 4 const { outputFiles } = await build({ 4 - entryPoints: ['app.jsx'], 5 + entryPoints: [join(import.meta.dirname, 'app.jsx')], 5 6 bundle: true, 6 7 write: false, 7 8 jsx: 'automatic',
+20 -3
include/esm/loader.h
··· 6 6 #include <stddef.h> 7 7 #include <stdbool.h> 8 8 9 + typedef enum { 10 + MODULE_EVAL_FORMAT_UNKNOWN = 0, 11 + MODULE_EVAL_FORMAT_ESM, 12 + MODULE_EVAL_FORMAT_CJS, 13 + } ant_module_format_t; 14 + 15 + typedef struct ant_module_t { 16 + ant_value_t module_ns; 17 + ant_value_t import_meta; 18 + ant_value_t prev_import_meta_prop; 19 + const char *filename; 20 + const char *parent_path; 21 + ant_module_format_t format; 22 + struct ant_module_t *prev; 23 + } ant_module_t; 24 + 9 25 void js_esm_cleanup_module_cache(void); 10 26 void js_esm_gc_roots(void (*visit)(void *ctx, ant_value_t *val), void *ctx); 11 27 12 28 ant_value_t js_esm_import_sync(ant_t *js, ant_value_t specifier); 13 29 ant_value_t js_esm_make_file_url(ant_t *js, const char *path); 30 + ant_value_t js_esm_import_sync_from(ant_t *js, ant_value_t specifier, const char *base_path); 14 31 15 32 ant_value_t js_esm_eval_module_source( 16 - ant_t *js, 17 - const char *resolved_path, const char *js_code, 18 - size_t js_len, ant_value_t ns 33 + ant_t *js, const char *resolved_path, 34 + const char *js_code, size_t js_len, ant_value_t ns 19 35 ); 20 36 21 37 ant_value_t js_esm_import_sync_cstr(ant_t *js, const char *specifier, size_t spec_len); 22 38 ant_value_t js_esm_resolve_specifier(ant_t *js, ant_value_t specifier, const char *base_path); 39 + ant_value_t js_esm_import_sync_cstr_from(ant_t *js, const char *specifier, size_t spec_len, const char *base_path); 23 40 24 41 #endif
+34 -5
include/internal.h
··· 3 3 4 4 #include "ant.h" 5 5 #include "gc.h" 6 + #include "esm/loader.h" 6 7 7 8 #include <assert.h> 8 9 #include <string.h> ··· 119 120 120 121 struct ant { 121 122 sv_vm_t *vm; 122 - 123 + ant_module_t *module; 124 + 123 125 #ifdef ANT_JIT 124 126 void *jit_ctx; 125 127 #endif ··· 135 137 ant_value_t this_val; 136 138 ant_value_t new_target; 137 139 ant_value_t current_func; 138 - 139 - ant_value_t module_ns; 140 - ant_value_t import_meta; 141 140 ant_value_t length_str; 142 141 143 142 uint8_t *mem; ··· 231 230 ant_value_t mkprop(ant_t *js, ant_value_t obj, ant_value_t k, ant_value_t v, ant_offset_t flags); 232 231 ant_value_t setprop_cstr(ant_t *js, ant_value_t obj, const char *key, size_t len, ant_value_t v); 233 232 ant_value_t setprop_interned(ant_t *js, ant_value_t obj, const char *key, size_t len, ant_value_t v); 233 + 234 234 ant_value_t js_define_own_prop(ant_t *js, ant_value_t obj, const char *key, size_t klen, ant_value_t v); 235 + ant_value_t js_create_import_meta(ant_t *js, const char *filename, bool is_main); 236 + ant_value_t js_instance_proto_from_new_target(ant_t *js, ant_value_t fallback_proto); 235 237 236 238 ant_value_t coerce_to_str(ant_t *js, ant_value_t v); 237 239 ant_value_t coerce_to_str_concat(ant_t *js, ant_value_t v); ··· 243 245 244 246 js_intern_stats_t js_intern_stats(void); 245 247 js_cstr_t js_to_cstr(ant_t *js, ant_value_t value, char *stack_buf, size_t stack_size); 246 - ant_value_t js_instance_proto_from_new_target(ant_t *js, ant_value_t fallback_proto); 247 248 248 249 ant_offset_t lkp(ant_t *js, ant_value_t obj, const char *buf, size_t len); 249 250 ant_offset_t lkp_proto(ant_t *js, ant_value_t obj, const char *buf, size_t len); ··· 277 278 278 279 ant_value_t do_instanceof(ant_t *js, ant_value_t l, ant_value_t r); 279 280 ant_value_t do_in(ant_t *js, ant_value_t l, ant_value_t r); 281 + 282 + void js_module_eval_ctx_push(ant_t *js, ant_module_t *ctx); 283 + void js_module_eval_ctx_pop(ant_t *js, ant_module_t *ctx); 284 + 285 + static inline ant_value_t js_module_eval_active_ns(ant_t *js) { 286 + ant_module_t *ctx = js->module; 287 + return ctx ? ctx->module_ns : js_mkundef(); 288 + } 289 + 290 + static inline ant_value_t js_module_eval_active_import_meta(ant_t *js) { 291 + ant_module_t *ctx = js->module; 292 + return ctx ? ctx->import_meta : js_mkundef(); 293 + } 294 + 295 + static inline const char *js_module_eval_active_filename(ant_t *js) { 296 + ant_module_t *ctx = js->module; 297 + return ctx ? ctx->filename : js->filename; 298 + } 299 + 300 + static inline const char *js_module_eval_active_parent_path(ant_t *js) { 301 + ant_module_t *ctx = js->module; 302 + return ctx ? ctx->parent_path : NULL; 303 + } 304 + 305 + static inline ant_module_format_t js_module_eval_active_format(ant_t *js) { 306 + ant_module_t *ctx = js->module; 307 + return ctx ? ctx->format : MODULE_EVAL_FORMAT_UNKNOWN; 308 + } 280 309 281 310 #endif
+77 -28
src/ant.c
··· 10521 10521 return builtin_Promise_resolve(js, promise_args, 1); 10522 10522 } 10523 10523 10524 + static ant_value_t js_get_import_meta_prop(ant_t *js) { 10525 + ant_value_t glob = js_glob(js); 10526 + ant_offset_t import_off = lkp(js, glob, "import", 6); 10527 + if (import_off == 0) return js_mkundef(); 10528 + 10529 + ant_value_t import_fn = resolveprop(js, mkval(T_PROP, import_off)); 10530 + if (vtype(import_fn) != T_FUNC) return js_mkundef(); 10531 + return js_get(js, js_func_obj(import_fn), "meta"); 10532 + } 10533 + 10534 + static void js_set_import_meta_prop(ant_t *js, ant_value_t import_meta) { 10535 + ant_value_t glob = js_glob(js); 10536 + ant_offset_t import_off = lkp(js, glob, "import", 6); 10537 + if (import_off == 0) return; 10538 + 10539 + ant_value_t import_fn = resolveprop(js, mkval(T_PROP, import_off)); 10540 + if (vtype(import_fn) != T_FUNC) return; 10541 + js_setprop(js, js_func_obj(import_fn), js_mkstr(js, "meta", 4), import_meta); 10542 + } 10543 + 10544 + static ant_value_t js_get_current_import_meta(ant_t *js) { 10545 + ant_value_t import_meta = js_module_eval_active_import_meta(js); 10546 + if (vtype(import_meta) == T_OBJ) return import_meta; 10547 + return js_get_import_meta_prop(js); 10548 + } 10549 + 10524 10550 static ant_value_t builtin_import_meta_resolve(ant_t *js, ant_value_t *args, int nargs) { 10525 10551 if (nargs < 1) return js_mkerr(js, "import.meta.resolve() requires a string specifier"); 10526 - if (vtype(js->import_meta) == T_OBJ) { 10527 - ant_value_t filename = js_get(js, js->import_meta, "filename"); 10552 + 10553 + ant_value_t import_meta = js_get_current_import_meta(js); 10554 + if (vtype(import_meta) == T_OBJ) { 10555 + ant_value_t filename = js_get(js, import_meta, "filename"); 10528 10556 if (vtype(filename) == T_STR) { 10529 10557 ant_offset_t n = 0; ant_offset_t off = vstr(js, filename, &n); 10530 10558 return js_esm_resolve_specifier(js, args[0], (const char *)&js->mem[off]); ··· 10532 10560 } return js_esm_resolve_specifier(js, args[0], NULL); 10533 10561 } 10534 10562 10535 - void js_setup_import_meta(ant_t *js, const char *filename) { 10536 - if (!filename) return; 10537 - 10563 + ant_value_t js_create_import_meta(ant_t *js, const char *filename, bool is_main) { 10564 + if (!filename) return js_mkundef(); 10565 + 10538 10566 ant_value_t import_meta = mkobj(js, 0); 10539 - if (is_err(import_meta)) return; 10567 + if (is_err(import_meta)) return import_meta; 10540 10568 bool is_url = esm_is_url(filename); 10541 - 10569 + 10542 10570 ant_value_t url_val = is_url ? js_mkstr(js, filename, strlen(filename)) : js_esm_make_file_url(js, filename); 10543 10571 if (!is_err(url_val)) js_setprop(js, import_meta, js_mkstr(js, "url", 3), url_val); 10544 - 10572 + 10545 10573 ant_value_t filename_val = js_mkstr(js, filename, strlen(filename)); 10546 10574 if (!is_err(filename_val)) js_setprop(js, import_meta, js_mkstr(js, "filename", 8), filename_val); 10547 - 10575 + 10548 10576 if (is_url) { 10549 10577 char *filename_copy = strdup(filename); 10550 10578 if (filename_copy) { ··· 10568 10596 free(filename_copy); 10569 10597 } 10570 10598 } 10571 - 10572 - js_setprop(js, import_meta, js_mkstr(js, "main", 4), js_true); 10599 + 10600 + js_setprop(js, import_meta, js_mkstr(js, "main", 4), is_main ? js_true : js_false); 10573 10601 ant_value_t resolve_fn = js_mkfun(builtin_import_meta_resolve); 10574 10602 js_setprop(js, import_meta, js_mkstr(js, "resolve", 7), resolve_fn); 10575 - 10576 - ant_value_t glob = js_glob(js); 10577 - ant_offset_t import_off = lkp(js, glob, "import", 6); 10578 - 10579 - if (import_off != 0) { 10580 - ant_value_t import_fn = resolveprop(js, mkval(T_PROP, import_off)); 10581 - if (vtype(import_fn) == T_FUNC) { 10582 - ant_value_t import_obj = js_func_obj(import_fn); 10583 - js_setprop(js, import_obj, js_mkstr(js, "meta", 4), import_meta); 10584 - } 10603 + return import_meta; 10604 + } 10605 + 10606 + void js_setup_import_meta(ant_t *js, const char *filename) { 10607 + if (!filename) return; 10608 + 10609 + ant_value_t import_meta = js_create_import_meta(js, filename, true); 10610 + if (is_err(import_meta)) return; 10611 + js_set_import_meta_prop(js, import_meta); 10612 + } 10613 + 10614 + void js_module_eval_ctx_push(ant_t *js, ant_module_t *ctx) { 10615 + if (!js || !ctx) return; 10616 + 10617 + ctx->prev = js->module; 10618 + ctx->prev_import_meta_prop = js_get_import_meta_prop(js); 10619 + js->module = ctx; 10620 + 10621 + if (vtype(ctx->import_meta) != T_UNDEF) 10622 + js_set_import_meta_prop(js, ctx->import_meta); 10623 + } 10624 + 10625 + void js_module_eval_ctx_pop(ant_t *js, ant_module_t *ctx) { 10626 + if (!js || !ctx) return; 10627 + 10628 + if (js->module == ctx) { 10629 + js_set_import_meta_prop(js, ctx->prev_import_meta_prop); 10630 + js->module = ctx->prev; 10585 10631 } 10586 10632 } 10587 10633 ··· 11431 11477 set_slot(js, import_obj, SLOT_CFUNC, js_mkfun(builtin_import)); 11432 11478 js_setprop(js, glob, js_mkstr(js, "import", 6), js_obj_to_func(import_obj)); 11433 11479 11434 - js->module_ns = js_mkundef(); 11435 - js->import_meta = js_mkundef(); 11436 - 11437 11480 js_setprop(js, object_proto, js_mkstr(js, "constructor", 11), obj_func); 11438 11481 js_set_descriptor(js, object_proto, "constructor", 11, JS_DESC_W | JS_DESC_C); 11439 11482 ··· 11710 11753 } 11711 11754 11712 11755 ant_value_t func_obj = js_func_obj(obj); 11713 - if (key_len == 4 && memcmp(key, "meta", 4) == 0 && vtype(js->import_meta) != T_UNDEF) { 11756 + ant_value_t import_meta = js_get_current_import_meta(js); 11757 + if (key_len == 4 && memcmp(key, "meta", 4) == 0 && vtype(import_meta) != T_UNDEF) { 11714 11758 ant_value_t cfunc = js_get_slot(js, func_obj, SLOT_CFUNC); 11715 11759 if (vtype(cfunc) == T_CFUNC && js_as_cfunc(cfunc) == builtin_import) { 11716 - *out = js->import_meta; 11760 + *out = import_meta; 11717 11761 return true; 11718 11762 } 11719 11763 } ··· 12005 12049 } 12006 12050 12007 12051 js_esm_gc_roots(op_val, c); 12008 - op_val(c, &c->js->import_meta); 12052 + 12053 + for (ant_module_t *ctx = c->js->module; ctx; ctx = ctx->prev) { 12054 + op_val(c, &ctx->module_ns); 12055 + op_val(c, &ctx->import_meta); 12056 + op_val(c, &ctx->prev_import_meta_prop); 12057 + } 12058 + 12009 12059 timer_gc_update_roots(op_val, c); 12010 12060 ffi_gc_update_roots(op_val, c); 12011 12061 fetch_gc_update_roots(op_val, c); ··· 12023 12073 op_val(c, &c->js->object); 12024 12074 op_val(c, &c->js->this_val); 12025 12075 op_val(c, &c->js->new_target); 12026 - op_val(c, &c->js->module_ns); 12027 12076 op_val(c, &c->js->current_func); 12028 12077 op_val(c, &c->js->thrown_value); 12029 12078 op_val(c, &c->js->length_str);
+4 -6
src/esm/commonjs.c
··· 18 18 19 19 ant_value_t fn = js_getcurrentfunc(js); 20 20 ant_value_t data = js_get_slot(js, fn, SLOT_DATA); 21 - const char *prev_filename = js->filename; 21 + const char *base_path = js_module_eval_active_filename(js); 22 22 23 23 if (vtype(data) == T_STR) { 24 24 ant_offset_t path_len = 0; 25 25 ant_offset_t path_off = vstr(js, data, &path_len); 26 - (void)path_len; 27 - js_set_filename(js, (const char *)&js->mem[path_off]); 26 + base_path = (const char *)&js->mem[path_off]; 28 27 } 29 28 30 - ant_value_t ns = js_esm_import_sync(js, args[0]); 31 - js_set_filename(js, prev_filename); 29 + ant_value_t ns = js_esm_import_sync_from(js, args[0], base_path); 32 30 if (is_err(ns)) return ns; 33 31 34 32 if (vtype(ns) == T_OBJ) { ··· 45 43 46 44 ant_value_t fn = js_getcurrentfunc(js); 47 45 ant_value_t data = js_get_slot(js, fn, SLOT_DATA); 48 - const char *base_path = js->filename ? js->filename : "."; 46 + const char *base_path = js_module_eval_active_filename(js); 49 47 50 48 if (vtype(data) == T_STR) { 51 49 ant_offset_t data_len = 0;
+77 -58
src/esm/loader.c
··· 5 5 #include "esm/library.h" 6 6 #include "esm/remote.h" 7 7 8 - #include "silver/engine.h" 9 8 #include "modules/json.h" 10 9 #include "modules/napi.h" 11 10 ··· 34 33 ESM_MODULE_KIND_URL, 35 34 } esm_module_kind_t; 36 35 37 - typedef enum { 38 - ESM_MODULE_FORMAT_UNKNOWN = 0, 39 - ESM_MODULE_FORMAT_ESM, 40 - ESM_MODULE_FORMAT_CJS, 41 - } esm_module_format_t; 42 - 43 36 typedef struct esm_module { 44 37 char *path; 45 38 char *resolved_path; ··· 49 42 ant_value_t default_export; 50 43 UT_hash_handle hh; 51 44 esm_module_kind_t kind; 52 - esm_module_format_t format; 45 + ant_module_format_t format; 53 46 bool is_loaded; 54 47 bool is_loading; 55 48 } esm_module_t; ··· 66 59 67 60 static esm_module_cache_t global_module_cache = {NULL, 0}; 68 61 static char *esm_resolve_node_module(const char *specifier, const char *base_path); 69 - 70 - static ant_value_t esm_get_import_meta_raw(ant_t *js) { 71 - ant_value_t import_fn = js_get(js, js->global, "import"); 72 - if (vtype(import_fn) != T_FUNC) return js_mkundef(); 73 - return js_get(js, js_func_obj(import_fn), "meta"); 74 - } 75 - 76 - static void esm_set_import_meta_main_flag(ant_t *js, bool is_main) { 77 - ant_value_t meta = esm_get_import_meta_raw(js); 78 - if (vtype(meta) != T_OBJ) return; 79 - js_set(js, meta, "main", is_main ? js_true : js_false); 80 - } 81 62 82 63 static char *esm_file_url_to_path(const char *specifier) { 83 64 if (!specifier || strncmp(specifier, "file:", 5) != 0) return NULL; ··· 697 678 return false; 698 679 } 699 680 700 - static esm_module_format_t esm_decide_module_format(const char *resolved_path) { 701 - if (!resolved_path || !resolved_path[0]) return ESM_MODULE_FORMAT_ESM; 702 - if (esm_is_cjs_extension(resolved_path)) return ESM_MODULE_FORMAT_CJS; 703 - if (esm_is_esm_extension(resolved_path)) return ESM_MODULE_FORMAT_ESM; 681 + static ant_module_format_t esm_decide_module_format(const char *resolved_path) { 682 + if (!resolved_path || !resolved_path[0]) return MODULE_EVAL_FORMAT_ESM; 683 + if (esm_is_cjs_extension(resolved_path)) return MODULE_EVAL_FORMAT_CJS; 684 + if (esm_is_esm_extension(resolved_path)) return MODULE_EVAL_FORMAT_ESM; 704 685 705 686 if (esm_has_suffix(resolved_path, ".js") && esm_path_contains_node_modules(resolved_path)) { 706 687 bool pkg_is_module = false; 707 688 bool has_package_json = esm_lookup_package_type_module(resolved_path, &pkg_is_module); 708 - if (!has_package_json) return ESM_MODULE_FORMAT_CJS; 709 - return pkg_is_module ? ESM_MODULE_FORMAT_ESM : ESM_MODULE_FORMAT_CJS; 689 + if (!has_package_json) return MODULE_EVAL_FORMAT_CJS; 690 + return pkg_is_module ? MODULE_EVAL_FORMAT_ESM : MODULE_EVAL_FORMAT_CJS; 710 691 } 711 692 712 - return ESM_MODULE_FORMAT_ESM; 693 + return MODULE_EVAL_FORMAT_ESM; 713 694 } 714 695 715 696 static ant_value_t esm_eval_module_with_format( ··· 718 699 const char *js_code, 719 700 size_t js_len, 720 701 ant_value_t ns, 721 - esm_module_format_t format 702 + ant_module_format_t format 722 703 ) { 723 - if (format == ESM_MODULE_FORMAT_CJS) { 704 + if (format == MODULE_EVAL_FORMAT_CJS) { 724 705 return esm_load_commonjs_module(js, resolved_path, js_code, js_len, ns); 725 706 } 726 707 return js_eval_bytecode_module(js, js_code, js_len); ··· 728 709 729 710 ant_value_t js_esm_eval_module_source( 730 711 ant_t *js, 731 - const char *resolved_path, 732 - const char *js_code, 733 - size_t js_len, 734 - ant_value_t ns 712 + const char *resolved_path, const char *js_code, 713 + size_t js_len, ant_value_t ns 735 714 ) { 736 - esm_module_format_t format = esm_decide_module_format(resolved_path); 737 - ant_value_t prev_module = js->module_ns; 738 - js->module_ns = ns; 715 + ant_module_format_t format = esm_decide_module_format(resolved_path); 716 + ant_module_t *parent_ctx = js->module; 717 + ant_value_t import_meta = js_create_import_meta(js, resolved_path, parent_ctx == NULL); 718 + if (is_err(import_meta)) return import_meta; 719 + 720 + ant_module_t eval_ctx = { 721 + .module_ns = ns, 722 + .import_meta = import_meta, 723 + .prev_import_meta_prop = js_mkundef(), 724 + .filename = resolved_path, 725 + .parent_path = parent_ctx ? parent_ctx->filename : NULL, 726 + .format = format, 727 + .prev = NULL, 728 + }; 739 729 730 + js_module_eval_ctx_push(js, &eval_ctx); 740 731 ant_value_t result = esm_eval_module_with_format( 741 732 js, resolved_path, js_code, js_len, ns, format 742 733 ); 743 - 744 - js->module_ns = prev_module; 734 + 735 + js_module_eval_ctx_pop(js, &eval_ctx); 745 736 return result; 746 737 } 747 738 ··· 828 819 .is_loaded = false, 829 820 .is_loading = false, 830 821 .kind = esm_classify_module_kind(resolved_path), 831 - .format = ESM_MODULE_FORMAT_UNKNOWN, 822 + .format = MODULE_EVAL_FORMAT_UNKNOWN, 832 823 .url_content = NULL, 833 824 .url_content_len = 0, 834 825 }; 835 - 826 + 836 827 HASH_ADD_STR(global_module_cache.modules, resolved_path, mod); 837 828 global_module_cache.count++; 838 829 ··· 1049 1040 char *js_code = content; 1050 1041 ant_value_t ns = js_mkobj(js); 1051 1042 mod->namespace_obj = ns; 1052 - 1053 - ant_value_t prev_module = js->module_ns; 1054 - js->module_ns = ns; 1055 1043 1056 1044 const char *prev_filename = js->filename; 1057 - ant_handle_t prev_import_meta_h = js_root(js, js->import_meta); 1058 - 1045 + ant_module_t *parent_ctx = js->module; 1046 + 1047 + ant_value_t import_meta = js_create_import_meta(js, mod->resolved_path, false); 1048 + if (is_err(import_meta)) { 1049 + free(content); 1050 + mod->is_loading = false; 1051 + return import_meta; 1052 + } 1053 + 1054 + ant_module_t eval_ctx = { 1055 + .module_ns = ns, 1056 + .import_meta = import_meta, 1057 + .prev_import_meta_prop = js_mkundef(), 1058 + .filename = mod->resolved_path, 1059 + .parent_path = parent_ctx ? parent_ctx->filename : prev_filename, 1060 + .format = mod->format, 1061 + .prev = NULL, 1062 + }; 1063 + 1059 1064 js_set_filename(js, mod->resolved_path); 1060 - js_setup_import_meta(js, mod->resolved_path); 1061 - 1062 - esm_set_import_meta_main_flag(js, false); 1063 - js->import_meta = esm_get_import_meta_raw(js); 1065 + js_module_eval_ctx_push(js, &eval_ctx); 1064 1066 1065 - if (mod->format == ESM_MODULE_FORMAT_UNKNOWN) { 1067 + if (mod->format == MODULE_EVAL_FORMAT_UNKNOWN) { 1066 1068 mod->format = esm_decide_module_format(mod->resolved_path); 1069 + eval_ctx.format = mod->format; 1067 1070 } 1068 1071 1069 1072 ant_value_t result = esm_eval_module_with_format( ··· 1073 1076 free(content); 1074 1077 if (vtype(result) == T_PROMISE) js_run_event_loop(js); 1075 1078 1076 - js->import_meta = js_deref(js, prev_import_meta_h); 1077 - js_unroot(js, prev_import_meta_h); 1079 + js_module_eval_ctx_pop(js, &eval_ctx); 1078 1080 js_set_filename(js, prev_filename); 1079 - js->module_ns = prev_module; 1080 1081 1081 1082 if (is_err(result)) { 1082 1083 mod->is_loading = false; ··· 1101 1102 return esm_load_module(js, mod); 1102 1103 } 1103 1104 1104 - ant_value_t js_esm_import_sync_cstr(ant_t *js, const char *specifier, size_t spec_len) { 1105 + static const char *esm_default_base_path(ant_t *js) { 1106 + const char *active = js_module_eval_active_filename(js); 1107 + return (active && active[0]) ? active : "."; 1108 + } 1109 + 1110 + ant_value_t js_esm_import_sync_cstr_from( 1111 + ant_t *js, 1112 + const char *specifier, 1113 + size_t spec_len, 1114 + const char *base_path 1115 + ) { 1105 1116 char *spec_copy = strndup(specifier, spec_len); 1106 1117 if (!spec_copy) return js_mkerr(js, "oom"); 1107 1118 ··· 1118 1129 return lib; 1119 1130 } 1120 1131 1121 - const char *base_path = js->filename ? js->filename : "."; 1132 + if (!base_path || !base_path[0]) base_path = esm_default_base_path(js); 1122 1133 char *resolved_path = esm_resolve(spec_copy, base_path, esm_resolve_path); 1123 1134 if (!resolved_path) { 1124 1135 ant_value_t err = js_mkerr(js, "Cannot resolve module: %s", spec_copy); ··· 1132 1143 return ns; 1133 1144 } 1134 1145 1135 - ant_value_t js_esm_import_sync(ant_t *js, ant_value_t specifier) { 1146 + ant_value_t js_esm_import_sync_cstr(ant_t *js, const char *specifier, size_t spec_len) { 1147 + return js_esm_import_sync_cstr_from(js, specifier, spec_len, NULL); 1148 + } 1149 + 1150 + ant_value_t js_esm_import_sync_from(ant_t *js, ant_value_t specifier, const char *base_path) { 1136 1151 if (vtype(specifier) != T_STR) 1137 1152 return js_mkerr(js, "import() requires a string specifier"); 1138 1153 ··· 1140 1155 ant_offset_t spec_off = vstr(js, specifier, &spec_len); 1141 1156 const char *spec_str = (const char *)&js->mem[spec_off]; 1142 1157 1143 - return js_esm_import_sync_cstr(js, spec_str, (size_t)spec_len); 1158 + return js_esm_import_sync_cstr_from(js, spec_str, (size_t)spec_len, base_path); 1159 + } 1160 + 1161 + ant_value_t js_esm_import_sync(ant_t *js, ant_value_t specifier) { 1162 + return js_esm_import_sync_from(js, specifier, NULL); 1144 1163 } 1145 1164 1146 1165 ant_value_t js_esm_make_file_url(ant_t *js, const char *path) { ··· 1165 1184 char *spec_copy = strndup(spec_str, (size_t)spec_len); 1166 1185 if (!spec_copy) return js_mkerr(js, "oom"); 1167 1186 1168 - if (!base_path) base_path = js->filename ? js->filename : "."; 1187 + if (!base_path || !base_path[0]) base_path = esm_default_base_path(js); 1169 1188 char *resolved_path = esm_resolve(spec_copy, base_path, esm_resolve_path); 1170 1189 free(spec_copy); 1171 1190
+1 -8
src/main.c
··· 336 336 js_set_filename(js, use_path); 337 337 js_setup_import_meta(js, use_path); 338 338 339 - ant_value_t import_fn = js_get(js, js->global, "import"); 340 - ant_value_t module_ns = mkobj(js, 0); 341 - 342 - js->import_meta = (vtype(import_fn) == T_FUNC) 343 - ? js_get(js, import_fn, "meta") 344 - : js_mkundef(); 345 - 346 339 ant_value_t result = js_esm_eval_module_source( 347 340 js, use_path, js_code, 348 - js_len, module_ns 341 + js_len, mkobj(js, 0) 349 342 ); 350 343 351 344 free(js_code);
+5 -6
src/modules/module.c
··· 25 25 26 26 ant_value_t fn = js_getcurrentfunc(js); 27 27 ant_value_t data = js_get_slot(js, fn, SLOT_DATA); 28 - const char *prev_filename = js->filename; 28 + const char *base_path = js_module_eval_active_filename(js); 29 29 30 30 if (vtype(data) == T_STR) { 31 31 ant_offset_t plen = 0; 32 32 ant_offset_t poff = vstr(js, data, &plen); 33 - js_set_filename(js, (const char *)&js->mem[poff]); 33 + base_path = (const char *)&js->mem[poff]; 34 34 } 35 35 36 - ant_value_t ns = js_esm_import_sync(js, args[0]); 37 - js_set_filename(js, prev_filename); 36 + ant_value_t ns = js_esm_import_sync_from(js, args[0], base_path); 38 37 if (is_err(ns)) return ns; 39 38 40 39 if (vtype(ns) == T_OBJ) { ··· 51 50 52 51 ant_value_t fn = js_getcurrentfunc(js); 53 52 ant_value_t data = js_get_slot(js, fn, SLOT_DATA); 54 - const char *base_path = js->filename ? js->filename : "."; 53 + const char *base_path = js_module_eval_active_filename(js); 55 54 56 55 if (vtype(data) == T_STR) { 57 56 ant_offset_t dlen = 0; ··· 114 113 if (nargs < 1 || vtype(args[0]) != T_STR) 115 114 return js_mkerr(js, "Module._resolveFilename() requires a string request"); 116 115 117 - const char *base_path = js->filename ? js->filename : "."; 116 + const char *base_path = js_module_eval_active_filename(js); 118 117 if (nargs >= 2 && vtype(args[1]) == T_OBJ) { 119 118 ant_value_t parent_filename = js_get(js, args[1], "filename"); 120 119 if (vtype(parent_filename) == T_STR) {
+4 -3
src/silver/ops/coercion.h
··· 15 15 const char *name, size_t len, 16 16 ant_value_t value 17 17 ) { 18 - if (vtype(js->module_ns) != T_OBJ) 18 + ant_value_t module_ns = js_module_eval_active_ns(js); 19 + if (vtype(module_ns) != T_OBJ) 19 20 return js_mkerr_typed(js, JS_ERR_SYNTAX, "export used outside module"); 20 21 21 - ant_value_t set_res = setprop_cstr(js, js->module_ns, name, len, value); 22 + ant_value_t set_res = setprop_cstr(js, module_ns, name, len, value); 22 23 if (is_err(set_res)) return set_res; 23 24 24 25 if (len == 7 && memcmp(name, "default", 7) == 0) 25 - js_set_slot(js, js->module_ns, SLOT_DEFAULT, value); 26 + js_set_slot(js, module_ns, SLOT_DEFAULT, value); 26 27 27 28 return tov(0); 28 29 }
+16
tests/cjs_accessor_module.cjs
··· 1 + let getterCalls = 0; 2 + 3 + Object.defineProperty(module.exports, "answer", { 4 + enumerable: true, 5 + get() { 6 + getterCalls += 1; 7 + return 42; 8 + }, 9 + }); 10 + 11 + Object.defineProperty(module.exports, "getterCalls", { 12 + enumerable: true, 13 + get() { 14 + return getterCalls; 15 + }, 16 + });
+13
tests/cjs_esbuild_like_module.cjs
··· 1 + function build(options) { 2 + return { ok: true, options: options ?? null }; 3 + } 4 + 5 + function transform(source) { 6 + return String(source).toUpperCase(); 7 + } 8 + 9 + module.exports = { 10 + build, 11 + transform, 12 + version: "0.0-test", 13 + };
+9
tests/cjs_esm_circular_bridge.cjs
··· 1 + const esm = require("./cjs_esm_circular_entry.mjs"); 2 + 3 + exports.cjsValue = "cjs-value"; 4 + Object.defineProperty(exports, "esmValueSeen", { 5 + enumerable: true, 6 + get() { 7 + return esm.esmValue; 8 + }, 9 + });
+4
tests/cjs_esm_circular_entry.mjs
··· 1 + import bridge from "./cjs_esm_circular_bridge.cjs"; 2 + 3 + export const esmValue = "esm-value"; 4 + export const fromCjs = bridge.cjsValue;
+1
tests/module_eval_reentrant_leaf.mjs
··· 1 + export const leaf = "leaf-ok";
+4
tests/module_eval_reentrant_mid.mjs
··· 1 + import { leaf } from "./module_eval_reentrant_leaf.mjs"; 2 + 3 + export const mid = "mid-ok"; 4 + export const fromLeaf = leaf;
+8
tests/module_eval_reentrant_root.mjs
··· 1 + import { 2 + mid as midValue, 3 + fromLeaf as leafValue, 4 + } from "./module_eval_reentrant_mid.mjs"; 5 + 6 + export const root = "root-ok"; 7 + export const fromMid = midValue; 8 + export const fromLeaf = leafValue;
+14
tests/test_cjs_accessor_exports.mjs
··· 1 + import * as ns from "./cjs_accessor_module.cjs"; 2 + import def from "./cjs_accessor_module.cjs"; 3 + 4 + function assert(condition, message) { 5 + if (!condition) throw new Error(message); 6 + } 7 + 8 + assert(ns.answer === 42, "namespace accessor export should resolve to 42"); 9 + assert(ns.getterCalls >= 1, "accessor getter should have been invoked"); 10 + 11 + assert(def.answer === 42, "default CJS export accessor should resolve to 42"); 12 + assert(ns.default === def, "namespace.default should match default import"); 13 + 14 + console.log("test_cjs_accessor_exports: OK");
+19
tests/test_cjs_esbuild_named_exports.mjs
··· 1 + import * as ns from "./cjs_esbuild_like_module.cjs"; 2 + import def from "./cjs_esbuild_like_module.cjs"; 3 + 4 + function assert(condition, message) { 5 + if (!condition) throw new Error(message); 6 + } 7 + 8 + assert(typeof ns.build === "function", "missing named build export"); 9 + assert(typeof ns.transform === "function", "missing named transform export"); 10 + assert(ns.version === "0.0-test", "missing named version export"); 11 + 12 + const buildResult = ns.build({ minify: false }); 13 + assert(buildResult && buildResult.ok === true, "named build export returned wrong value"); 14 + assert(ns.transform("ab") === "AB", "named transform export returned wrong value"); 15 + 16 + assert(def.build === ns.build, "default and named build exports diverged"); 17 + assert(ns.default === def, "namespace.default should match default import"); 18 + 19 + console.log("test_cjs_esbuild_named_exports: OK");
+21
tests/test_cjs_esm_circular.mjs
··· 1 + import * as esmNs from "./cjs_esm_circular_entry.mjs"; 2 + import cjsDefault from "./cjs_esm_circular_bridge.cjs"; 3 + import * as cjsNs from "./cjs_esm_circular_bridge.cjs"; 4 + 5 + function assert(condition, message) { 6 + if (!condition) throw new Error(message); 7 + } 8 + 9 + assert(esmNs.esmValue === "esm-value", "esm circular export mismatch"); 10 + assert(esmNs.fromCjs === "cjs-value", "esm should read cjs export in circular edge"); 11 + 12 + assert(cjsDefault.cjsValue === "cjs-value", "cjs default export mismatch"); 13 + assert(cjsDefault.esmValueSeen === "esm-value", "cjs getter should see resolved esm export"); 14 + 15 + assert(cjsNs.default === cjsDefault, "namespace.default should match cjs default import"); 16 + assert( 17 + cjsNs.esmValueSeen === "esm-value" || cjsNs.esmValueSeen === undefined, 18 + "cjs named accessor export should be stable under circular loading" 19 + ); 20 + 21 + console.log("test_cjs_esm_circular: OK");
+22
tests/test_module_eval_reentrancy.mjs
··· 1 + import * as root from "./module_eval_reentrant_root.mjs"; 2 + import * as mid from "./module_eval_reentrant_mid.mjs"; 3 + import * as leaf from "./module_eval_reentrant_leaf.mjs"; 4 + 5 + function assert(condition, message) { 6 + if (!condition) throw new Error(message); 7 + } 8 + 9 + assert(root.root === "root-ok", "root export mismatch"); 10 + assert(root.fromMid === "mid-ok", "root->mid export mismatch"); 11 + assert(root.fromLeaf === "leaf-ok", "root->leaf export mismatch"); 12 + 13 + assert(mid.mid === "mid-ok", "mid export mismatch"); 14 + assert(mid.fromLeaf === "leaf-ok", "mid->leaf export mismatch"); 15 + 16 + assert(leaf.leaf === "leaf-ok", "leaf export mismatch"); 17 + 18 + assert(root.mid === undefined, "root namespace was polluted by mid exports"); 19 + assert(mid.root === undefined, "mid namespace was polluted by root exports"); 20 + assert(leaf.root === undefined, "leaf namespace was polluted by root exports"); 21 + 22 + console.log("test_module_eval_reentrancy: OK");