diff --git a/include/esm/exports.h b/include/esm/exports.h new file mode 100644 index 0000000..ce40008 --- /dev/null +++ b/include/esm/exports.h @@ -0,0 +1,9 @@ +#ifndef ESM_EXPORTS_H +#define ESM_EXPORTS_H + +#include "types.h" +#include "silver/ast.h" + +void esm_predeclare_exports(ant_t *js, sv_ast_t *program, ant_value_t ns); + +#endif diff --git a/src/ant.c b/src/ant.c index 71e0334..fad8090 100644 --- a/src/ant.c +++ b/src/ant.c @@ -24,6 +24,7 @@ #include "esm/remote.h" #include "esm/loader.h" +#include "esm/exports.h" #include "esm/builtin_bundle.h" #include "silver/lexer.h" @@ -13314,6 +13315,11 @@ static ant_value_t js_eval_bytecode_mode(ant_t *js, const char *buf, size_t len, return js_mkerr_typed(js, JS_ERR_INTERNAL | JS_ERR_NO_STACK, "Unexpected parse error"); } + if (mode == SV_COMPILE_MODULE) { + ant_value_t ns = js_module_eval_active_ns(js); + if (is_object_type(ns)) esm_predeclare_exports(js, program, ns); + } + sv_func_t *func = sv_compile(js, program, mode, buf, (ant_offset_t)len); if (!func) { if (js->thrown_exists) return mkval(T_ERR, 0); diff --git a/src/esm/exports.c b/src/esm/exports.c new file mode 100644 index 0000000..c5e9ebe --- /dev/null +++ b/src/esm/exports.c @@ -0,0 +1,86 @@ +#include "esm/exports.h" +#include "internal.h" + +static void collect_binding_names(ant_t *js, sv_ast_t *pat, ant_value_t ns) { + if (!pat) return; + + static const void *dispatch[N__COUNT] = { + [N_IDENT] = &&l_ident, + [N_ASSIGN_PAT] = &&l_left, + [N_REST] = &&l_right, + [N_SPREAD] = &&l_right, + [N_ARRAY] = &&l_list, + [N_ARRAY_PAT] = &&l_list, + [N_OBJECT] = &&l_props, + [N_OBJECT_PAT] = &&l_props, + }; + + if ((unsigned)pat->type < N__COUNT && dispatch[pat->type]) + goto *dispatch[pat->type]; + return; + + l_ident: + setprop_cstr(js, ns, pat->str, pat->len, js_mkundef()); + return; + l_left: + collect_binding_names(js, pat->left, ns); + return; + l_right: + collect_binding_names(js, pat->right, ns); + return; + l_list: + for (int i = 0; i < pat->args.count; i++) + collect_binding_names(js, pat->args.items[i], ns); + return; + l_props: + for (int i = 0; i < pat->args.count; i++) { + sv_ast_t *p = pat->args.items[i]; + if (!p) continue; + collect_binding_names(js, p->type == N_PROPERTY ? p->right : p, ns); + } + return; +} + +static void collect_spec_names(ant_t *js, sv_ast_list_t *specs, ant_value_t ns) { +for (int i = 0; i < specs->count; i++) { + sv_ast_t *spec = specs->items[i]; + if (spec && spec->type == N_IMPORT_SPEC && spec->right && spec->right->type == N_IDENT) + setprop_cstr(js, ns, spec->right->str, spec->right->len, js_mkundef()); +}} + +static void predeclare_decl(ant_t *js, sv_ast_t *decl, ant_value_t ns) { + static const void *dispatch[N__COUNT] = { + [N_VAR] = &&l_var, + [N_FUNC] = &&l_named, + [N_CLASS] = &&l_named, + }; + + if ((unsigned)decl->type < N__COUNT && dispatch[decl->type]) + goto *dispatch[decl->type]; + return; + + l_var: + for (int i = 0; i < decl->args.count; i++) { + sv_ast_t *var = decl->args.items[i]; + if (var && var->type == N_VARDECL) + collect_binding_names(js, var->left, ns); + } + return; + l_named: + if (decl->str && decl->len > 0) + setprop_cstr(js, ns, decl->str, decl->len, js_mkundef()); + return; +} + +void esm_predeclare_exports(ant_t *js, sv_ast_t *program, ant_value_t ns) { + if (!program || !is_object_type(ns)) return; + + for (int i = 0; i < program->args.count; i++) { + sv_ast_t *stmt = program->args.items[i]; + if (!stmt || stmt->type != N_EXPORT) continue; + uint16_t f = stmt->flags; + + if (f & EX_DEFAULT) setprop_cstr(js, ns, "default", 7, js_mkundef()); + else if ((f & EX_DECL) && stmt->left) predeclare_decl(js, stmt->left, ns); + else if ((f & EX_NAMED) || ((f & EX_STAR) && (f & EX_NAMESPACE))) collect_spec_names(js, &stmt->args, ns); +}}